Merge branch 'develop' into Umur-reducer-dev
This commit is contained in:
		
						commit
						8d92941736
					
				| 
						 | 
				
			
			@ -9,3 +9,4 @@ packages/website/.docusaurus
 | 
			
		|||
packages/squiggle-lang/lib
 | 
			
		||||
packages/squiggle-lang/.nyc_output/
 | 
			
		||||
packages/squiggle-lang/coverage/
 | 
			
		||||
packages/squiggle-lang/.cache/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ Squiggle is currently pre-alpha.
 | 
			
		|||
 | 
			
		||||
# Bug reports
 | 
			
		||||
 | 
			
		||||
Anyone (with a github account) can file an issue at any time. Please allow Quinn, Sam, and Ozzie to triage, but otherwise just follow the suggestions in the issue templates. 
 | 
			
		||||
Anyone (with a github account) can file an issue at any time. Please allow Quinn, Sam, and Ozzie to triage, but otherwise just follow the suggestions in the issue templates.
 | 
			
		||||
 | 
			
		||||
# Project structure
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,11 +46,11 @@ You can't run `yarn` outside of a FHS shell. Additionally, you need to `patchelf
 | 
			
		|||
./nixos.sh
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Reasons for this are comments in the script. Then, you should be able to do all the package-level `yarn run` commands/scripts. 
 | 
			
		||||
Reasons for this are comments in the script. Then, you should be able to do all the package-level `yarn run` commands/scripts.
 | 
			
		||||
 | 
			
		||||
# Try not to push directly to develop
 | 
			
		||||
 | 
			
		||||
If you absolutely must, please prefix your commit message with `hotfix: `. 
 | 
			
		||||
If you absolutely must, please prefix your commit message with `hotfix: `.
 | 
			
		||||
 | 
			
		||||
# Pull request protocol
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								packages/components/.prettierignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								packages/components/.prettierignore
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
dist/
 | 
			
		||||
storybook-static
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +131,7 @@ export interface SquiggleChartProps {
 | 
			
		|||
  /** variables declared before this expression */
 | 
			
		||||
  environment?: unknown;
 | 
			
		||||
  /** When the environment changes */
 | 
			
		||||
  onEnvChange?(env: unknown): void;
 | 
			
		||||
  onChange?(expr: squiggleExpression): void;
 | 
			
		||||
  /** CSS width of the element */
 | 
			
		||||
  width?: number;
 | 
			
		||||
  height?: number;
 | 
			
		||||
| 
						 | 
				
			
			@ -141,8 +141,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
 | 
			
		|||
  squiggleString = "",
 | 
			
		||||
  sampleCount = 1000,
 | 
			
		||||
  outputXYPoints = 1000,
 | 
			
		||||
  environment = [],
 | 
			
		||||
  onEnvChange = () => {},
 | 
			
		||||
  onChange = () => {},
 | 
			
		||||
  height = 60,
 | 
			
		||||
  width = NaN,
 | 
			
		||||
}: SquiggleChartProps) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -158,8 +157,8 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
 | 
			
		|||
  let expressionResult = run(squiggleString, samplingInputs);
 | 
			
		||||
  let internal: JSX.Element;
 | 
			
		||||
  if (expressionResult.tag === "Ok") {
 | 
			
		||||
    onEnvChange(environment);
 | 
			
		||||
    let expression = expressionResult.value;
 | 
			
		||||
    onChange(expression);
 | 
			
		||||
    internal = (
 | 
			
		||||
      <SquiggleItem expression={expression} width={_width} height={height} />
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ import * as ReactDOM from "react-dom";
 | 
			
		|||
import { SquiggleChart } from "./SquiggleChart";
 | 
			
		||||
import { CodeEditor } from "./CodeEditor";
 | 
			
		||||
import styled from "styled-components";
 | 
			
		||||
import type { squiggleExpression } from "@quri/squiggle-lang";
 | 
			
		||||
 | 
			
		||||
export interface SquiggleEditorProps {
 | 
			
		||||
  /** The input string for squiggle */
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +23,7 @@ export interface SquiggleEditorProps {
 | 
			
		|||
  /** The environment, other variables that were already declared */
 | 
			
		||||
  environment?: unknown;
 | 
			
		||||
  /** when the environment changes. Used again for notebook magic*/
 | 
			
		||||
  onEnvChange?(env: unknown): void;
 | 
			
		||||
  onChange?(expr: squiggleExpression): void;
 | 
			
		||||
  /** The width of the element */
 | 
			
		||||
  width: number;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +44,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
 | 
			
		|||
  diagramStart,
 | 
			
		||||
  diagramStop,
 | 
			
		||||
  diagramCount,
 | 
			
		||||
  onEnvChange,
 | 
			
		||||
  onChange,
 | 
			
		||||
  environment,
 | 
			
		||||
}: SquiggleEditorProps) => {
 | 
			
		||||
  let [expression, setExpression] = React.useState(initialSquiggleString);
 | 
			
		||||
| 
						 | 
				
			
			@ -69,7 +70,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
 | 
			
		|||
        diagramStop={diagramStop}
 | 
			
		||||
        diagramCount={diagramCount}
 | 
			
		||||
        environment={environment}
 | 
			
		||||
        onEnvChange={onEnvChange}
 | 
			
		||||
        onChange={onChange}
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +81,7 @@ export function renderSquiggleEditorToDom(props: SquiggleEditorProps) {
 | 
			
		|||
  ReactDOM.render(
 | 
			
		||||
    <SquiggleEditor
 | 
			
		||||
      {...props}
 | 
			
		||||
      onEnvChange={(env) => {
 | 
			
		||||
      onChange={(expr) => {
 | 
			
		||||
        // Typescript complains on two levels here.
 | 
			
		||||
        //  - Div elements don't have a value property
 | 
			
		||||
        //  - Even if it did (like it was an input element), it would have to
 | 
			
		||||
| 
						 | 
				
			
			@ -96,10 +97,10 @@ export function renderSquiggleEditorToDom(props: SquiggleEditorProps) {
 | 
			
		|||
        //  viewof env = cell('normal(0,1)')
 | 
			
		||||
        //  to work
 | 
			
		||||
        // @ts-ignore
 | 
			
		||||
        parent.value = env;
 | 
			
		||||
        parent.value = expr;
 | 
			
		||||
 | 
			
		||||
        parent.dispatchEvent(new CustomEvent("input"));
 | 
			
		||||
        if (props.onEnvChange) props.onEnvChange(env);
 | 
			
		||||
        if (props.onChange) props.onChange(expr);
 | 
			
		||||
      }}
 | 
			
		||||
    />,
 | 
			
		||||
    parent
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,3 +4,4 @@ lib
 | 
			
		|||
*.gen.tsx
 | 
			
		||||
.nyc_output/
 | 
			
		||||
coverage/
 | 
			
		||||
.cache/
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +30,7 @@ let toExt: option<'a> => 'a = E.O.toExt(
 | 
			
		|||
describe("sparkline", () => {
 | 
			
		||||
  let runTest = (
 | 
			
		||||
    name: string,
 | 
			
		||||
    dist: GenericDist_Types.genericDist,
 | 
			
		||||
    dist: DistributionTypes.genericDist,
 | 
			
		||||
    expected: DistributionOperation.outputType,
 | 
			
		||||
  ) => {
 | 
			
		||||
    test(name, () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,14 @@
 | 
			
		|||
let normalDist5: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0}))
 | 
			
		||||
let normalDist10: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0}))
 | 
			
		||||
let normalDist20: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0}))
 | 
			
		||||
let normalDist: GenericDist_Types.genericDist = normalDist5
 | 
			
		||||
let normalDist5: DistributionTypes.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0}))
 | 
			
		||||
let normalDist10: DistributionTypes.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0}))
 | 
			
		||||
let normalDist20: DistributionTypes.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0}))
 | 
			
		||||
let normalDist: DistributionTypes.genericDist = normalDist5
 | 
			
		||||
 | 
			
		||||
let betaDist: GenericDist_Types.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0}))
 | 
			
		||||
let lognormalDist: GenericDist_Types.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0}))
 | 
			
		||||
let cauchyDist: GenericDist_Types.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0}))
 | 
			
		||||
let triangularDist: GenericDist_Types.genericDist = Symbolic(
 | 
			
		||||
let betaDist: DistributionTypes.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0}))
 | 
			
		||||
let lognormalDist: DistributionTypes.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0}))
 | 
			
		||||
let cauchyDist: DistributionTypes.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0}))
 | 
			
		||||
let triangularDist: DistributionTypes.genericDist = Symbolic(
 | 
			
		||||
  #Triangular({low: 1.0, medium: 2.0, high: 3.0}),
 | 
			
		||||
)
 | 
			
		||||
let exponentialDist: GenericDist_Types.genericDist = Symbolic(#Exponential({rate: 2.0}))
 | 
			
		||||
let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
 | 
			
		||||
let floatDist: GenericDist_Types.genericDist = Symbolic(#Float(1e1))
 | 
			
		||||
let exponentialDist: DistributionTypes.genericDist = Symbolic(#Exponential({rate: 2.0}))
 | 
			
		||||
let uniformDist: DistributionTypes.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
 | 
			
		||||
let floatDist: DistributionTypes.genericDist = Symbolic(#Float(1e1))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,10 +43,10 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
    test("normal(mean=5) + normal(mean=20)", () => {
 | 
			
		||||
      normalDist5
 | 
			
		||||
      ->algebraicAdd(normalDist20)
 | 
			
		||||
      ->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean)
 | 
			
		||||
      ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean)
 | 
			
		||||
      ->E.R2.fmap(run)
 | 
			
		||||
      ->E.R2.fmap(toFloat)
 | 
			
		||||
      ->E.R.toExn
 | 
			
		||||
      ->E.R.toExn("Expected float", _)
 | 
			
		||||
      ->expect
 | 
			
		||||
      ->toBe(Some(2.5e1))
 | 
			
		||||
    })
 | 
			
		||||
| 
						 | 
				
			
			@ -57,10 +57,10 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        uniformDist
 | 
			
		||||
        ->algebraicAdd(betaDist)
 | 
			
		||||
        ->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean)
 | 
			
		||||
        ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean)
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toExn
 | 
			
		||||
        ->E.R.toExn("Expected float", _)
 | 
			
		||||
      switch received {
 | 
			
		||||
      | None => "algebraicAdd has"->expect->toBe("failed")
 | 
			
		||||
      // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
 | 
			
		||||
| 
						 | 
				
			
			@ -74,10 +74,10 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        betaDist
 | 
			
		||||
        ->algebraicAdd(uniformDist)
 | 
			
		||||
        ->E.R2.fmap(GenericDist_Types.Constructors.UsingDists.mean)
 | 
			
		||||
        ->E.R2.fmap(DistributionTypes.Constructors.UsingDists.mean)
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toExn
 | 
			
		||||
        ->E.R.toExn("Expected float", _)
 | 
			
		||||
      switch received {
 | 
			
		||||
      | None => "algebraicAdd has"->expect->toBe("failed")
 | 
			
		||||
      // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +95,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
        let received =
 | 
			
		||||
          normalDist10 // this should be normal(10, sqrt(8))
 | 
			
		||||
          ->Ok
 | 
			
		||||
          ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, x))
 | 
			
		||||
          ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, x))
 | 
			
		||||
          ->E.R2.fmap(run)
 | 
			
		||||
          ->E.R2.fmap(toFloat)
 | 
			
		||||
          ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +103,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
        let calculated =
 | 
			
		||||
          normalDist5
 | 
			
		||||
          ->algebraicAdd(normalDist5)
 | 
			
		||||
          ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, x))
 | 
			
		||||
          ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, x))
 | 
			
		||||
          ->E.R2.fmap(run)
 | 
			
		||||
          ->E.R2.fmap(toFloat)
 | 
			
		||||
          ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +126,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        normalDist20
 | 
			
		||||
        ->Ok
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1.9e1))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1.9e1))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -134,7 +134,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let calculated =
 | 
			
		||||
        normalDist10
 | 
			
		||||
        ->algebraicAdd(normalDist10)
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1.9e1))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1.9e1))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -155,10 +155,10 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        uniformDist
 | 
			
		||||
        ->algebraicAdd(betaDist)
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1e1))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1e1))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toExn
 | 
			
		||||
        ->E.R.toExn("Expected float", _)
 | 
			
		||||
      switch received {
 | 
			
		||||
      | None => "algebraicAdd has"->expect->toBe("failed")
 | 
			
		||||
      // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
 | 
			
		||||
| 
						 | 
				
			
			@ -170,10 +170,10 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        betaDist
 | 
			
		||||
        ->algebraicAdd(uniformDist)
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.pdf(d, 1e1))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.pdf(d, 1e1))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toExn
 | 
			
		||||
        ->E.R.toExn("Expected float", _)
 | 
			
		||||
      switch received {
 | 
			
		||||
      | None => "algebraicAdd has"->expect->toBe("failed")
 | 
			
		||||
      // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
 | 
			
		||||
| 
						 | 
				
			
			@ -187,7 +187,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        normalDist10
 | 
			
		||||
        ->Ok
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, x))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, x))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -195,7 +195,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let calculated =
 | 
			
		||||
        normalDist5
 | 
			
		||||
        ->algebraicAdd(normalDist5)
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, x))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, x))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -217,7 +217,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        normalDist20
 | 
			
		||||
        ->Ok
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1.25e1))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1.25e1))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -225,7 +225,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let calculated =
 | 
			
		||||
        normalDist10
 | 
			
		||||
        ->algebraicAdd(normalDist10)
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1.25e1))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1.25e1))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -246,10 +246,10 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        uniformDist
 | 
			
		||||
        ->algebraicAdd(betaDist)
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1e1))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1e1))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toExn
 | 
			
		||||
        ->E.R.toExn("Expected float", _)
 | 
			
		||||
      switch received {
 | 
			
		||||
      | None => "algebraicAdd has"->expect->toBe("failed")
 | 
			
		||||
      // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
 | 
			
		||||
| 
						 | 
				
			
			@ -261,10 +261,10 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        betaDist
 | 
			
		||||
        ->algebraicAdd(uniformDist)
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.cdf(d, 1e1))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.cdf(d, 1e1))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toExn
 | 
			
		||||
        ->E.R.toExn("Expected float", _)
 | 
			
		||||
      switch received {
 | 
			
		||||
      | None => "algebraicAdd has"->expect->toBe("failed")
 | 
			
		||||
      // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
 | 
			
		||||
| 
						 | 
				
			
			@ -279,7 +279,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        normalDist10
 | 
			
		||||
        ->Ok
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, x))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, x))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -287,7 +287,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let calculated =
 | 
			
		||||
        normalDist5
 | 
			
		||||
        ->algebraicAdd(normalDist5)
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, x))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, x))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -309,7 +309,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        normalDist20
 | 
			
		||||
        ->Ok
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 1e-1))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 1e-1))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -317,7 +317,7 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let calculated =
 | 
			
		||||
        normalDist10
 | 
			
		||||
        ->algebraicAdd(normalDist10)
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 1e-1))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 1e-1))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toOption
 | 
			
		||||
| 
						 | 
				
			
			@ -338,10 +338,10 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        uniformDist
 | 
			
		||||
        ->algebraicAdd(betaDist)
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 2e-2))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 2e-2))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toExn
 | 
			
		||||
        ->E.R.toExn("Expected float", _)
 | 
			
		||||
      switch received {
 | 
			
		||||
      | None => "algebraicAdd has"->expect->toBe("failed")
 | 
			
		||||
      // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
 | 
			
		||||
| 
						 | 
				
			
			@ -353,10 +353,10 @@ describe("(Algebraic) addition of distributions", () => {
 | 
			
		|||
      let received =
 | 
			
		||||
        betaDist
 | 
			
		||||
        ->algebraicAdd(uniformDist)
 | 
			
		||||
        ->E.R2.fmap(d => GenericDist_Types.Constructors.UsingDists.inv(d, 2e-2))
 | 
			
		||||
        ->E.R2.fmap(d => DistributionTypes.Constructors.UsingDists.inv(d, 2e-2))
 | 
			
		||||
        ->E.R2.fmap(run)
 | 
			
		||||
        ->E.R2.fmap(toFloat)
 | 
			
		||||
        ->E.R.toExn
 | 
			
		||||
        ->E.R.toExn("Expected float", _)
 | 
			
		||||
      switch received {
 | 
			
		||||
      | None => "algebraicAdd has"->expect->toBe("failed")
 | 
			
		||||
      // This is nondeterministic, we could be in a situation where ci fails but you click rerun and it passes, which is bad.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ open TestHelpers
 | 
			
		|||
module Internals = {
 | 
			
		||||
  let epsilon = 5e1
 | 
			
		||||
 | 
			
		||||
  let mean = GenericDist_Types.Constructors.UsingDists.mean
 | 
			
		||||
  let mean = DistributionTypes.Constructors.UsingDists.mean
 | 
			
		||||
 | 
			
		||||
  let expectImpossiblePath: string => assertion = algebraicOp =>
 | 
			
		||||
    `${algebraicOp} has`->expect->toEqual("failed")
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +50,11 @@ module Internals = {
 | 
			
		|||
    let dist1 = dist1'->DistributionTypes.Symbolic
 | 
			
		||||
    let dist2 = dist2'->DistributionTypes.Symbolic
 | 
			
		||||
    let received =
 | 
			
		||||
      distOp(dist1, dist2)->E.R2.fmap(mean)->E.R2.fmap(run)->E.R2.fmap(toFloat)->E.R.toExn
 | 
			
		||||
      distOp(dist1, dist2)
 | 
			
		||||
      ->E.R2.fmap(mean)
 | 
			
		||||
      ->E.R2.fmap(run)
 | 
			
		||||
      ->E.R2.fmap(toFloat)
 | 
			
		||||
      ->E.R.toExn("Expected float", _)
 | 
			
		||||
    let expected = floatOp(runMean(dist1), runMean(dist2))
 | 
			
		||||
    switch received {
 | 
			
		||||
    | None => expectImpossiblePath(description)
 | 
			
		||||
| 
						 | 
				
			
			@ -80,14 +84,16 @@ let {testOperationMean, distributions, pairsOfDifferentDistributions, epsilon} =
 | 
			
		|||
describe("Means are invariant", () => {
 | 
			
		||||
  describe("for addition", () => {
 | 
			
		||||
    let testAdditionMean = testOperationMean(algebraicAdd, "algebraicAdd", \"+.", ~epsilon)
 | 
			
		||||
    let testAddInvariant = (t1, t2) =>
 | 
			
		||||
      E.R.liftM2(testAdditionMean, t1, t2)->E.R.toExn("Means were not invariant", _)
 | 
			
		||||
 | 
			
		||||
    testAll("with two of the same distribution", distributions, dist => {
 | 
			
		||||
      E.R.liftM2(testAdditionMean, dist, dist)->E.R.toExn
 | 
			
		||||
      testAddInvariant(dist, dist)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    testAll("with two different distributions", pairsOfDifferentDistributions, dists => {
 | 
			
		||||
      let (dist1, dist2) = dists
 | 
			
		||||
      E.R.liftM2(testAdditionMean, dist1, dist2)->E.R.toExn
 | 
			
		||||
      testAddInvariant(dist1, dist2)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    testAll(
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +101,7 @@ describe("Means are invariant", () => {
 | 
			
		|||
      pairsOfDifferentDistributions,
 | 
			
		||||
      dists => {
 | 
			
		||||
        let (dist1, dist2) = dists
 | 
			
		||||
        E.R.liftM2(testAdditionMean, dist2, dist1)->E.R.toExn
 | 
			
		||||
        testAddInvariant(dist1, dist2)
 | 
			
		||||
      },
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
| 
						 | 
				
			
			@ -107,14 +113,16 @@ describe("Means are invariant", () => {
 | 
			
		|||
      \"-.",
 | 
			
		||||
      ~epsilon,
 | 
			
		||||
    )
 | 
			
		||||
    let testSubtractInvariant = (t1, t2) =>
 | 
			
		||||
      E.R.liftM2(testSubtractionMean, t1, t2)->E.R.toExn("Means were not invariant", _)
 | 
			
		||||
 | 
			
		||||
    testAll("with two of the same distribution", distributions, dist => {
 | 
			
		||||
      E.R.liftM2(testSubtractionMean, dist, dist)->E.R.toExn
 | 
			
		||||
      testSubtractInvariant(dist, dist)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    testAll("with two different distributions", pairsOfDifferentDistributions, dists => {
 | 
			
		||||
      let (dist1, dist2) = dists
 | 
			
		||||
      E.R.liftM2(testSubtractionMean, dist1, dist2)->E.R.toExn
 | 
			
		||||
      testSubtractInvariant(dist1, dist2)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    testAll(
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +130,7 @@ describe("Means are invariant", () => {
 | 
			
		|||
      pairsOfDifferentDistributions,
 | 
			
		||||
      dists => {
 | 
			
		||||
        let (dist1, dist2) = dists
 | 
			
		||||
        E.R.liftM2(testSubtractionMean, dist2, dist1)->E.R.toExn
 | 
			
		||||
        testSubtractInvariant(dist1, dist2)
 | 
			
		||||
      },
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
| 
						 | 
				
			
			@ -134,14 +142,16 @@ describe("Means are invariant", () => {
 | 
			
		|||
      \"*.",
 | 
			
		||||
      ~epsilon,
 | 
			
		||||
    )
 | 
			
		||||
    let testMultiplicationInvariant = (t1, t2) =>
 | 
			
		||||
      E.R.liftM2(testMultiplicationMean, t1, t2)->E.R.toExn("Means were not invariant", _)
 | 
			
		||||
 | 
			
		||||
    testAll("with two of the same distribution", distributions, dist => {
 | 
			
		||||
      E.R.liftM2(testMultiplicationMean, dist, dist)->E.R.toExn
 | 
			
		||||
      testMultiplicationInvariant(dist, dist)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    testAll("with two different distributions", pairsOfDifferentDistributions, dists => {
 | 
			
		||||
      let (dist1, dist2) = dists
 | 
			
		||||
      E.R.liftM2(testMultiplicationMean, dist1, dist2)->E.R.toExn
 | 
			
		||||
      testMultiplicationInvariant(dist1, dist2)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    testAll(
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +159,7 @@ describe("Means are invariant", () => {
 | 
			
		|||
      pairsOfDifferentDistributions,
 | 
			
		||||
      dists => {
 | 
			
		||||
        let (dist1, dist2) = dists
 | 
			
		||||
        E.R.liftM2(testMultiplicationMean, dist2, dist1)->E.R.toExn
 | 
			
		||||
        testMultiplicationInvariant(dist1, dist2)
 | 
			
		||||
      },
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,9 +20,10 @@ describe("eval on distribution functions", () => {
 | 
			
		|||
  })
 | 
			
		||||
  describe("unaryMinus", () => {
 | 
			
		||||
    testEval("mean(-normal(5,2))", "Ok(-5)")
 | 
			
		||||
    testEval("-normal(5,2)", "Ok(Normal(-5,2))")
 | 
			
		||||
  })
 | 
			
		||||
  describe("to", () => {
 | 
			
		||||
    testEval("5 to 2", "Error(TODO: Low value must be less than high value.)")
 | 
			
		||||
    testEval("5 to 2", "Error(Math Error: Low value must be less than high value.)")
 | 
			
		||||
    testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.27853260523016377))")
 | 
			
		||||
    testEval("to(-2,2)", "Ok(Normal(0,1.2159136638235384))")
 | 
			
		||||
  })
 | 
			
		||||
| 
						 | 
				
			
			@ -88,18 +89,15 @@ describe("eval on distribution functions", () => {
 | 
			
		|||
 | 
			
		||||
  describe("log", () => {
 | 
			
		||||
    testEval("log(2, uniform(5,8))", "Ok(Sample Set Distribution)")
 | 
			
		||||
    testEval("log(normal(5,2), 3)", "Ok(Sample Set Distribution)")
 | 
			
		||||
    testEval("log(normal(5,2), normal(10,1))", "Ok(Sample Set Distribution)")
 | 
			
		||||
    testEval("log(normal(5,2), 3)", "Error(Math Error: Operation returned complex result)")
 | 
			
		||||
    testEval(
 | 
			
		||||
      "log(normal(5,2), normal(10,1))",
 | 
			
		||||
      "Error(Math Error: Operation returned complex result)",
 | 
			
		||||
    )
 | 
			
		||||
    testEval("log(uniform(5,8))", "Ok(Sample Set Distribution)")
 | 
			
		||||
    testEval("log10(uniform(5,8))", "Ok(Sample Set Distribution)")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("dotLog", () => {
 | 
			
		||||
    testEval("dotLog(normal(5,2), 3)", "Ok(Point Set Distribution)")
 | 
			
		||||
    testEval("dotLog(normal(5,2), 3)", "Ok(Point Set Distribution)")
 | 
			
		||||
    testEval("dotLog(normal(5,2), normal(10,1))", "Ok(Point Set Distribution)")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("dotAdd", () => {
 | 
			
		||||
    testEval("dotAdd(normal(5,2), lognormal(10,2))", "Ok(Point Set Distribution)")
 | 
			
		||||
    testEval("dotAdd(normal(5,2), 3)", "Ok(Point Set Distribution)")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,7 @@ describe("Symbolic mean", () => {
 | 
			
		|||
            expect(squiggleResult.value).toBeCloseTo((x + y + z) / 3);
 | 
			
		||||
          } catch (err) {
 | 
			
		||||
            expect((err as Error).message).toEqual(
 | 
			
		||||
              "Expected squiggle expression to evaluate but got error: TODO: Triangular values must be increasing order."
 | 
			
		||||
              "Expected squiggle expression to evaluate but got error: Math Error: Triangular values must be increasing order."
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@
 | 
			
		|||
module.exports = {
 | 
			
		||||
  preset: "ts-jest",
 | 
			
		||||
  testEnvironment: "node",
 | 
			
		||||
  bail: true,
 | 
			
		||||
  setupFilesAfterEnv: [
 | 
			
		||||
    "<rootdir>/../../node_modules/bisect_ppx/src/runtime/js/jest.bs.js",
 | 
			
		||||
  ],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
type functionCallInfo = GenericDist_Types.Operation.genericFunctionCallInfo
 | 
			
		||||
type functionCallInfo = DistributionTypes.DistributionOperation.genericFunctionCallInfo
 | 
			
		||||
type genericDist = DistributionTypes.genericDist
 | 
			
		||||
type error = DistributionTypes.error
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +120,10 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
 | 
			
		|||
      (),
 | 
			
		||||
    )->OutputLocal.toDistR
 | 
			
		||||
 | 
			
		||||
  let fromDistFn = (subFnName: GenericDist_Types.Operation.fromDist, dist: genericDist) => {
 | 
			
		||||
  let fromDistFn = (
 | 
			
		||||
    subFnName: DistributionTypes.DistributionOperation.fromDist,
 | 
			
		||||
    dist: genericDist,
 | 
			
		||||
  ) => {
 | 
			
		||||
    let response = switch subFnName {
 | 
			
		||||
    | ToFloat(distToFloatOperation) =>
 | 
			
		||||
      GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation)
 | 
			
		||||
| 
						 | 
				
			
			@ -157,14 +160,14 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
 | 
			
		|||
      ->GenericDist.algebraicCombination(~toPointSetFn, ~toSampleSetFn, ~arithmeticOperation, ~t2)
 | 
			
		||||
      ->E.R2.fmap(r => Dist(r))
 | 
			
		||||
      ->OutputLocal.fromResult
 | 
			
		||||
    | ToDistCombination(Pointwise, arithmeticOperation, #Dist(t2)) =>
 | 
			
		||||
    | ToDistCombination(Pointwise, algebraicCombination, #Dist(t2)) =>
 | 
			
		||||
      dist
 | 
			
		||||
      ->GenericDist.pointwiseCombination(~toPointSetFn, ~arithmeticOperation, ~t2)
 | 
			
		||||
      ->GenericDist.pointwiseCombination(~toPointSetFn, ~algebraicCombination, ~t2)
 | 
			
		||||
      ->E.R2.fmap(r => Dist(r))
 | 
			
		||||
      ->OutputLocal.fromResult
 | 
			
		||||
    | ToDistCombination(Pointwise, arithmeticOperation, #Float(float)) =>
 | 
			
		||||
    | ToDistCombination(Pointwise, algebraicCombination, #Float(f)) =>
 | 
			
		||||
      dist
 | 
			
		||||
      ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~arithmeticOperation, ~float)
 | 
			
		||||
      ->GenericDist.pointwiseCombinationFloat(~toPointSetFn, ~algebraicCombination, ~f)
 | 
			
		||||
      ->E.R2.fmap(r => Dist(r))
 | 
			
		||||
      ->OutputLocal.fromResult
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -192,24 +195,24 @@ module Output = {
 | 
			
		|||
  let fmap = (
 | 
			
		||||
    ~env,
 | 
			
		||||
    input: outputType,
 | 
			
		||||
    functionCallInfo: GenericDist_Types.Operation.singleParamaterFunction,
 | 
			
		||||
    functionCallInfo: DistributionTypes.DistributionOperation.singleParamaterFunction,
 | 
			
		||||
  ): outputType => {
 | 
			
		||||
    let newFnCall: result<functionCallInfo, error> = switch (functionCallInfo, input) {
 | 
			
		||||
    | (FromDist(fromDist), Dist(o)) => Ok(FromDist(fromDist, o))
 | 
			
		||||
    | (FromFloat(fromDist), Float(o)) => Ok(FromFloat(fromDist, o))
 | 
			
		||||
    | (_, GenDistError(r)) => Error(r)
 | 
			
		||||
    | (FromDist(_), _) => Error(Other("Expected dist, got something else"))
 | 
			
		||||
    | (FromFloat(_), _) => Error(Other("Expected float, got something else"))
 | 
			
		||||
    | (FromDist(_), _) => Error(OtherError("Expected dist, got something else"))
 | 
			
		||||
    | (FromFloat(_), _) => Error(OtherError("Expected float, got something else"))
 | 
			
		||||
    }
 | 
			
		||||
    newFnCall->E.R2.fmap(run(~env))->OutputLocal.fromResult
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See comment above GenericDist_Types.Constructors to explain the purpose of this module.
 | 
			
		||||
// See comment above DistributionTypes.Constructors to explain the purpose of this module.
 | 
			
		||||
// I tried having another internal module called UsingDists, similar to how its done in
 | 
			
		||||
// GenericDist_Types.Constructors. However, this broke GenType for me, so beware.
 | 
			
		||||
// DistributionTypes.Constructors. However, this broke GenType for me, so beware.
 | 
			
		||||
module Constructors = {
 | 
			
		||||
  module C = GenericDist_Types.Constructors.UsingDists
 | 
			
		||||
  module C = DistributionTypes.Constructors.UsingDists
 | 
			
		||||
  open OutputLocal
 | 
			
		||||
  let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR
 | 
			
		||||
  let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ type env = {
 | 
			
		|||
  xyPointLength: int,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
open GenericDist_Types
 | 
			
		||||
open DistributionTypes
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
type outputType =
 | 
			
		||||
| 
						 | 
				
			
			@ -15,15 +15,15 @@ type outputType =
 | 
			
		|||
  | GenDistError(error)
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let run: (~env: env, GenericDist_Types.Operation.genericFunctionCallInfo) => outputType
 | 
			
		||||
let run: (~env: env, DistributionTypes.DistributionOperation.genericFunctionCallInfo) => outputType
 | 
			
		||||
let runFromDist: (
 | 
			
		||||
  ~env: env,
 | 
			
		||||
  ~functionCallInfo: GenericDist_Types.Operation.fromDist,
 | 
			
		||||
  ~functionCallInfo: DistributionTypes.DistributionOperation.fromDist,
 | 
			
		||||
  genericDist,
 | 
			
		||||
) => outputType
 | 
			
		||||
let runFromFloat: (
 | 
			
		||||
  ~env: env,
 | 
			
		||||
  ~functionCallInfo: GenericDist_Types.Operation.fromDist,
 | 
			
		||||
  ~functionCallInfo: DistributionTypes.DistributionOperation.fromDist,
 | 
			
		||||
  float,
 | 
			
		||||
) => outputType
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ module Output: {
 | 
			
		|||
  let toBool: t => option<bool>
 | 
			
		||||
  let toBoolR: t => result<bool, error>
 | 
			
		||||
  let toError: t => option<error>
 | 
			
		||||
  let fmap: (~env: env, t, GenericDist_Types.Operation.singleParamaterFunction) => t
 | 
			
		||||
  let fmap: (~env: env, t, DistributionTypes.DistributionOperation.singleParamaterFunction) => t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module Constructors: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,33 +9,51 @@ type error =
 | 
			
		|||
  | NotYetImplemented
 | 
			
		||||
  | Unreachable
 | 
			
		||||
  | DistributionVerticalShiftIsInvalid
 | 
			
		||||
  | TooFewSamples
 | 
			
		||||
  | ArgumentError(string)
 | 
			
		||||
  | Other(string)
 | 
			
		||||
  | OperationError(Operation.Error.t)
 | 
			
		||||
  | PointSetConversionError(SampleSetDist.pointsetConversionError)
 | 
			
		||||
  | SparklineError(PointSetTypes.sparklineError) // This type of error is for when we find a sparkline of a discrete distribution. This should probably at some point be actually implemented
 | 
			
		||||
  | OtherError(string)
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
module Error = {
 | 
			
		||||
  type t = error
 | 
			
		||||
 | 
			
		||||
  let fromString = (s: string): t => OtherError(s)
 | 
			
		||||
 | 
			
		||||
  @genType
 | 
			
		||||
  let toString = (err: error): string =>
 | 
			
		||||
    switch err {
 | 
			
		||||
    | NotYetImplemented => "Function Not Yet Implemented"
 | 
			
		||||
    | Unreachable => "Unreachable"
 | 
			
		||||
    | DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid"
 | 
			
		||||
    | ArgumentError(s) => `Argument Error ${s}`
 | 
			
		||||
    | TooFewSamples => "Too Few Samples"
 | 
			
		||||
    | OperationError(err) => Operation.Error.toString(err)
 | 
			
		||||
    | PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err)
 | 
			
		||||
    | SparklineError(err) => PointSetTypes.sparklineErrorToString(err)
 | 
			
		||||
    | OtherError(s) => s
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  let resultStringToResultError: result<'a, string> => result<'a, error> = n =>
 | 
			
		||||
    n->E.R2.errMap(r => r->fromString)
 | 
			
		||||
 | 
			
		||||
  let sampleErrorToDistErr = (err: SampleSetDist.sampleSetError): error =>
 | 
			
		||||
    switch err {
 | 
			
		||||
    | TooFewSamples => TooFewSamples
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
module DistributionOperation = {
 | 
			
		||||
  @genType
 | 
			
		||||
  type pointsetXSelection = [#Linear | #ByWeight]
 | 
			
		||||
 | 
			
		||||
module Operation = {
 | 
			
		||||
  type direction =
 | 
			
		||||
    | Algebraic
 | 
			
		||||
    | Pointwise
 | 
			
		||||
 | 
			
		||||
  type arithmeticOperation = [
 | 
			
		||||
    | #Add
 | 
			
		||||
    | #Multiply
 | 
			
		||||
    | #Subtract
 | 
			
		||||
    | #Divide
 | 
			
		||||
    | #Power
 | 
			
		||||
    | #Logarithm
 | 
			
		||||
  ]
 | 
			
		||||
 | 
			
		||||
  let arithmeticToFn = (arithmetic: arithmeticOperation) =>
 | 
			
		||||
    switch arithmetic {
 | 
			
		||||
    | #Add => \"+."
 | 
			
		||||
    | #Multiply => \"*."
 | 
			
		||||
    | #Subtract => \"-."
 | 
			
		||||
    | #Power => \"**"
 | 
			
		||||
    | #Divide => \"/."
 | 
			
		||||
    | #Logarithm => (a, b) => log(a) /. log(b)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  type toFloat = [
 | 
			
		||||
    | #Cdf(float)
 | 
			
		||||
    | #Inv(float)
 | 
			
		||||
| 
						 | 
				
			
			@ -43,9 +61,7 @@ module Operation = {
 | 
			
		|||
    | #Mean
 | 
			
		||||
    | #Sample
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module DistributionOperation = {
 | 
			
		||||
  type toDist =
 | 
			
		||||
    | Normalize
 | 
			
		||||
    | ToPointSet
 | 
			
		||||
| 
						 | 
				
			
			@ -55,15 +71,18 @@ module DistributionOperation = {
 | 
			
		|||
 | 
			
		||||
  type toFloatArray = Sample(int)
 | 
			
		||||
 | 
			
		||||
  type fromDist =
 | 
			
		||||
    | ToFloat(Operation.toFloat)
 | 
			
		||||
    | ToDist(toDist)
 | 
			
		||||
    | ToDistCombination(
 | 
			
		||||
        Operation.direction,
 | 
			
		||||
        Operation.arithmeticOperation,
 | 
			
		||||
        [#Dist(genericDist) | #Float(float)],
 | 
			
		||||
      )
 | 
			
		||||
  type toBool = IsNormalized
 | 
			
		||||
 | 
			
		||||
  type toString =
 | 
			
		||||
    | ToString
 | 
			
		||||
    | ToSparkline(int)
 | 
			
		||||
 | 
			
		||||
  type fromDist =
 | 
			
		||||
    | ToFloat(toFloat)
 | 
			
		||||
    | ToDist(toDist)
 | 
			
		||||
    | ToDistCombination(direction, Operation.Algebraic.t, [#Dist(genericDist) | #Float(float)])
 | 
			
		||||
    | ToString(toString)
 | 
			
		||||
    | ToBool(toBool)
 | 
			
		||||
 | 
			
		||||
  type singleParamaterFunction =
 | 
			
		||||
    | FromDist(fromDist)
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +105,9 @@ module DistributionOperation = {
 | 
			
		|||
    | ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
 | 
			
		||||
    | ToDist(Truncate(_, _)) => `truncate`
 | 
			
		||||
    | ToDist(Inspect) => `inspect`
 | 
			
		||||
    | ToString => `toString`
 | 
			
		||||
    | ToString(ToString) => `toString`
 | 
			
		||||
    | ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})`
 | 
			
		||||
    | ToBool(IsNormalized) => `isNormalized`
 | 
			
		||||
    | ToDistCombination(Algebraic, _, _) => `algebraic`
 | 
			
		||||
    | ToDistCombination(Pointwise, _, _) => `pointwise`
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -97,3 +118,71 @@ module DistributionOperation = {
 | 
			
		|||
    | Mixture(_) => `mixture`
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
module Constructors = {
 | 
			
		||||
  type t = DistributionOperation.genericFunctionCallInfo
 | 
			
		||||
 | 
			
		||||
  module UsingDists = {
 | 
			
		||||
    @genType
 | 
			
		||||
    let mean = (dist): t => FromDist(ToFloat(#Mean), dist)
 | 
			
		||||
    let sample = (dist): t => FromDist(ToFloat(#Sample), dist)
 | 
			
		||||
    let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist)
 | 
			
		||||
    let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist)
 | 
			
		||||
    let pdf = (dist, x): t => FromDist(ToFloat(#Pdf(x)), dist)
 | 
			
		||||
    let normalize = (dist): t => FromDist(ToDist(Normalize), dist)
 | 
			
		||||
    let isNormalized = (dist): t => FromDist(ToBool(IsNormalized), dist)
 | 
			
		||||
    let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist)
 | 
			
		||||
    let toSampleSet = (dist, r): t => FromDist(ToDist(ToSampleSet(r)), dist)
 | 
			
		||||
    let truncate = (dist, left, right): t => FromDist(ToDist(Truncate(left, right)), dist)
 | 
			
		||||
    let inspect = (dist): t => FromDist(ToDist(Inspect), dist)
 | 
			
		||||
    let toString = (dist): t => FromDist(ToString(ToString), dist)
 | 
			
		||||
    let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist)
 | 
			
		||||
    let algebraicAdd = (dist1, dist2: genericDist): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Add, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let algebraicMultiply = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Multiply, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let algebraicDivide = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Divide, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let algebraicSubtract = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Subtract, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let algebraicLogarithm = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Logarithm, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let algebraicPower = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Power, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwiseAdd = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Add, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwiseMultiply = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Multiply, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwiseDivide = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Divide, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwiseSubtract = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Subtract, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwiseLogarithm = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwisePower = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Power, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ let sampleN = (t: t, n) =>
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
let toSampleSetDist = (t: t, n) =>
 | 
			
		||||
  SampleSetDist.make(sampleN(t, n))->GenericDist_Types.Error.resultStringToResultError
 | 
			
		||||
  SampleSetDist.make(sampleN(t, n))->E.R2.errMap(DistributionTypes.Error.sampleErrorToDistErr)
 | 
			
		||||
 | 
			
		||||
let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +68,7 @@ let toPointSet = (
 | 
			
		|||
  t,
 | 
			
		||||
  ~xyPointLength,
 | 
			
		||||
  ~sampleCount,
 | 
			
		||||
  ~xSelection: GenericDist_Types.Operation.pointsetXSelection=#ByWeight,
 | 
			
		||||
  ~xSelection: DistributionTypes.DistributionOperation.pointsetXSelection=#ByWeight,
 | 
			
		||||
  (),
 | 
			
		||||
): result<PointSetTypes.pointSetDist, error> => {
 | 
			
		||||
  switch (t: t) {
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +83,7 @@ let toPointSet = (
 | 
			
		|||
        pointSetDistLength: xyPointLength,
 | 
			
		||||
        kernelWidth: None,
 | 
			
		||||
      },
 | 
			
		||||
    )->GenericDist_Types.Error.resultStringToResultError
 | 
			
		||||
    )->E.R2.errMap(x => DistributionTypes.PointSetConversionError(x))
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +97,7 @@ let toSparkline = (t: t, ~sampleCount: int, ~bucketCount: int=20, ()): result<st
 | 
			
		|||
  t
 | 
			
		||||
  ->toPointSet(~xSelection=#Linear, ~xyPointLength=bucketCount * 3, ~sampleCount, ())
 | 
			
		||||
  ->E.R.bind(r =>
 | 
			
		||||
    r->PointSetDist.toSparkline(bucketCount)->GenericDist_Types.Error.resultStringToResultError
 | 
			
		||||
    r->PointSetDist.toSparkline(bucketCount)->E.R2.errMap(x => DistributionTypes.SparklineError(x))
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
module Truncate = {
 | 
			
		||||
| 
						 | 
				
			
			@ -148,10 +148,10 @@ let truncate = Truncate.run
 | 
			
		|||
*/
 | 
			
		||||
module AlgebraicCombination = {
 | 
			
		||||
  let tryAnalyticalSimplification = (
 | 
			
		||||
    arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
 | 
			
		||||
    arithmeticOperation: Operation.algebraicOperation,
 | 
			
		||||
    t1: t,
 | 
			
		||||
    t2: t,
 | 
			
		||||
  ): option<result<SymbolicDistTypes.symbolicDist, string>> =>
 | 
			
		||||
  ): option<result<SymbolicDistTypes.symbolicDist, Operation.Error.t>> =>
 | 
			
		||||
    switch (arithmeticOperation, t1, t2) {
 | 
			
		||||
    | (arithmeticOperation, Symbolic(d1), Symbolic(d2)) =>
 | 
			
		||||
      switch SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation) {
 | 
			
		||||
| 
						 | 
				
			
			@ -174,14 +174,14 @@ module AlgebraicCombination = {
 | 
			
		|||
 | 
			
		||||
  let runMonteCarlo = (
 | 
			
		||||
    toSampleSet: toSampleSetFn,
 | 
			
		||||
    arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
 | 
			
		||||
    arithmeticOperation: Operation.algebraicOperation,
 | 
			
		||||
    t1: t,
 | 
			
		||||
    t2: t,
 | 
			
		||||
  ) => {
 | 
			
		||||
  ): result<t, error> => {
 | 
			
		||||
    let fn = Operation.Algebraic.toFn(arithmeticOperation)
 | 
			
		||||
    E.R.merge(toSampleSet(t1), toSampleSet(t2))
 | 
			
		||||
    ->E.R.bind(((t1, t2)) => {
 | 
			
		||||
      SampleSetDist.map2(~fn, ~t1, ~t2)->GenericDist_Types.Error.resultStringToResultError
 | 
			
		||||
      SampleSetDist.map2(~fn, ~t1, ~t2)->E.R2.errMap(x => DistributionTypes.OperationError(x))
 | 
			
		||||
    })
 | 
			
		||||
    ->E.R2.fmap(r => DistributionTypes.SampleSet(r))
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -224,7 +224,7 @@ module AlgebraicCombination = {
 | 
			
		|||
  ): result<t, error> => {
 | 
			
		||||
    switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) {
 | 
			
		||||
    | Some(Ok(symbolicDist)) => Ok(Symbolic(symbolicDist))
 | 
			
		||||
    | Some(Error(e)) => Error(Other(e))
 | 
			
		||||
    | Some(Error(e)) => Error(OperationError(e))
 | 
			
		||||
    | None =>
 | 
			
		||||
      switch chooseConvolutionOrMonteCarlo(arithmeticOperation, t1, t2) {
 | 
			
		||||
      | MonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
 | 
			
		||||
| 
						 | 
				
			
			@ -241,40 +241,36 @@ let algebraicCombination = AlgebraicCombination.run
 | 
			
		|||
let pointwiseCombination = (
 | 
			
		||||
  t1: t,
 | 
			
		||||
  ~toPointSetFn: toPointSetFn,
 | 
			
		||||
  ~arithmeticOperation,
 | 
			
		||||
  ~algebraicCombination: Operation.algebraicOperation,
 | 
			
		||||
  ~t2: t,
 | 
			
		||||
): result<t, error> => {
 | 
			
		||||
  E.R.merge(toPointSetFn(t1), toPointSetFn(t2))
 | 
			
		||||
  ->E.R2.fmap(((t1, t2)) =>
 | 
			
		||||
    PointSetDist.combinePointwise(
 | 
			
		||||
      GenericDist_Types.Operation.arithmeticToFn(arithmeticOperation),
 | 
			
		||||
      t1,
 | 
			
		||||
      t2,
 | 
			
		||||
    )
 | 
			
		||||
  E.R.merge(toPointSetFn(t1), toPointSetFn(t2))->E.R.bind(((t1, t2)) =>
 | 
			
		||||
    PointSetDist.combinePointwise(Operation.Algebraic.toFn(algebraicCombination), t1, t2)
 | 
			
		||||
    ->E.R2.fmap(r => DistributionTypes.PointSet(r))
 | 
			
		||||
    ->E.R2.errMap(err => DistributionTypes.OperationError(err))
 | 
			
		||||
  )
 | 
			
		||||
  ->E.R2.fmap(r => DistributionTypes.PointSet(r))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let pointwiseCombinationFloat = (
 | 
			
		||||
  t: t,
 | 
			
		||||
  ~toPointSetFn: toPointSetFn,
 | 
			
		||||
  ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
 | 
			
		||||
  ~float: float,
 | 
			
		||||
  ~algebraicCombination: Operation.algebraicOperation,
 | 
			
		||||
  ~f: float,
 | 
			
		||||
): result<t, error> => {
 | 
			
		||||
  let m = switch arithmeticOperation {
 | 
			
		||||
  let m = switch algebraicCombination {
 | 
			
		||||
  | #Add | #Subtract => Error(DistributionTypes.DistributionVerticalShiftIsInvalid)
 | 
			
		||||
  | (#Multiply | #Divide | #Power | #Logarithm) as arithmeticOperation =>
 | 
			
		||||
    toPointSetFn(t)->E.R2.fmap(t => {
 | 
			
		||||
    toPointSetFn(t)->E.R.bind(t => {
 | 
			
		||||
      //TODO: Move to PointSet codebase
 | 
			
		||||
      let fn = (secondary, main) => Operation.Scale.toFn(arithmeticOperation, main, secondary)
 | 
			
		||||
      let integralSumCacheFn = Operation.Scale.toIntegralSumCacheFn(arithmeticOperation)
 | 
			
		||||
      let integralCacheFn = Operation.Scale.toIntegralCacheFn(arithmeticOperation)
 | 
			
		||||
      PointSetDist.T.mapY(
 | 
			
		||||
        ~integralSumCacheFn=integralSumCacheFn(float),
 | 
			
		||||
        ~integralCacheFn=integralCacheFn(float),
 | 
			
		||||
        ~fn=fn(float),
 | 
			
		||||
      PointSetDist.T.mapYResult(
 | 
			
		||||
        ~integralSumCacheFn=integralSumCacheFn(f),
 | 
			
		||||
        ~integralCacheFn=integralCacheFn(f),
 | 
			
		||||
        ~fn=fn(f),
 | 
			
		||||
        t,
 | 
			
		||||
      )
 | 
			
		||||
      )->E.R2.errMap(x => DistributionTypes.OperationError(x))
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
  m->E.R2.fmap(r => DistributionTypes.PointSet(r))
 | 
			
		||||
| 
						 | 
				
			
			@ -288,7 +284,7 @@ let mixture = (
 | 
			
		|||
  ~pointwiseAddFn: pointwiseAddFn,
 | 
			
		||||
) => {
 | 
			
		||||
  if E.A.length(values) == 0 {
 | 
			
		||||
    Error(DistributionTypes.Other("Mixture error: mixture must have at least 1 element"))
 | 
			
		||||
    Error(DistributionTypes.OtherError("Mixture error: mixture must have at least 1 element"))
 | 
			
		||||
  } else {
 | 
			
		||||
    let totalWeight = values->E.A2.fmap(E.Tuple2.second)->E.A.Floats.sum
 | 
			
		||||
    let properlyWeightedValues =
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
type t = GenericDist_Types.genericDist
 | 
			
		||||
type error = GenericDist_Types.error
 | 
			
		||||
type t = DistributionTypes.genericDist
 | 
			
		||||
type error = DistributionTypes.error
 | 
			
		||||
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
 | 
			
		||||
type toSampleSetFn = t => result<SampleSetDist.t, error>
 | 
			
		||||
type scaleMultiplyFn = (t, float) => result<t, error>
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ let toPointSet: (
 | 
			
		|||
  t,
 | 
			
		||||
  ~xyPointLength: int,
 | 
			
		||||
  ~sampleCount: int,
 | 
			
		||||
  ~xSelection: GenericDist_Types.Operation.pointsetXSelection=?,
 | 
			
		||||
  ~xSelection: DistributionTypes.DistributionOperation.pointsetXSelection=?,
 | 
			
		||||
  unit,
 | 
			
		||||
) => result<PointSetTypes.pointSetDist, error>
 | 
			
		||||
let toSparkline: (t, ~sampleCount: int, ~bucketCount: int=?, unit) => result<string, error>
 | 
			
		||||
| 
						 | 
				
			
			@ -45,22 +45,22 @@ let algebraicCombination: (
 | 
			
		|||
  t,
 | 
			
		||||
  ~toPointSetFn: toPointSetFn,
 | 
			
		||||
  ~toSampleSetFn: toSampleSetFn,
 | 
			
		||||
  ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
 | 
			
		||||
  ~arithmeticOperation: Operation.algebraicOperation,
 | 
			
		||||
  ~t2: t,
 | 
			
		||||
) => result<t, error>
 | 
			
		||||
 | 
			
		||||
let pointwiseCombination: (
 | 
			
		||||
  t,
 | 
			
		||||
  ~toPointSetFn: toPointSetFn,
 | 
			
		||||
  ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
 | 
			
		||||
  ~algebraicCombination: Operation.algebraicOperation,
 | 
			
		||||
  ~t2: t,
 | 
			
		||||
) => result<t, error>
 | 
			
		||||
 | 
			
		||||
let pointwiseCombinationFloat: (
 | 
			
		||||
  t,
 | 
			
		||||
  ~toPointSetFn: toPointSetFn,
 | 
			
		||||
  ~arithmeticOperation: GenericDist_Types.Operation.arithmeticOperation,
 | 
			
		||||
  ~float: float,
 | 
			
		||||
  ~algebraicCombination: Operation.algebraicOperation,
 | 
			
		||||
  ~f: float,
 | 
			
		||||
) => result<t, error>
 | 
			
		||||
 | 
			
		||||
let mixture: (
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,194 +0,0 @@
 | 
			
		|||
type genericDist = DistributionTypes.genericDist
 | 
			
		||||
@genType
 | 
			
		||||
type error = DistributionTypes.error
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
module Error = {
 | 
			
		||||
  type t = error
 | 
			
		||||
 | 
			
		||||
  let fromString = (s: string): t => Other(s)
 | 
			
		||||
 | 
			
		||||
  @genType
 | 
			
		||||
  let toString = (x: t) => {
 | 
			
		||||
    switch x {
 | 
			
		||||
    | NotYetImplemented => "Not Yet Implemented"
 | 
			
		||||
    | Unreachable => "Unreachable"
 | 
			
		||||
    | DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift Is Invalid"
 | 
			
		||||
    | ArgumentError(x) => `Argument Error: ${x}`
 | 
			
		||||
    | Other(s) => s
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let resultStringToResultError: result<'a, string> => result<'a, error> = n =>
 | 
			
		||||
    n->E.R2.errMap(r => r->fromString->Error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module Operation = {
 | 
			
		||||
  type direction =
 | 
			
		||||
    | Algebraic
 | 
			
		||||
    | Pointwise
 | 
			
		||||
 | 
			
		||||
  type arithmeticOperation = [
 | 
			
		||||
    | #Add
 | 
			
		||||
    | #Multiply
 | 
			
		||||
    | #Subtract
 | 
			
		||||
    | #Divide
 | 
			
		||||
    | #Power
 | 
			
		||||
    | #Logarithm
 | 
			
		||||
  ]
 | 
			
		||||
 | 
			
		||||
  let arithmeticToFn = (arithmetic: arithmeticOperation) =>
 | 
			
		||||
    switch arithmetic {
 | 
			
		||||
    | #Add => \"+."
 | 
			
		||||
    | #Multiply => \"*."
 | 
			
		||||
    | #Subtract => \"-."
 | 
			
		||||
    | #Power => \"**"
 | 
			
		||||
    | #Divide => \"/."
 | 
			
		||||
    | #Logarithm => (a, b) => log(a) /. log(b)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  type toFloat = [
 | 
			
		||||
    | #Cdf(float)
 | 
			
		||||
    | #Inv(float)
 | 
			
		||||
    | #Mean
 | 
			
		||||
    | #Pdf(float)
 | 
			
		||||
    | #Sample
 | 
			
		||||
  ]
 | 
			
		||||
 | 
			
		||||
  @genType
 | 
			
		||||
  type pointsetXSelection = [#Linear | #ByWeight]
 | 
			
		||||
 | 
			
		||||
  type toDist =
 | 
			
		||||
    | Normalize
 | 
			
		||||
    | ToPointSet
 | 
			
		||||
    | ToSampleSet(int)
 | 
			
		||||
    | Truncate(option<float>, option<float>)
 | 
			
		||||
    | Inspect
 | 
			
		||||
 | 
			
		||||
  type toFloatArray = Sample(int)
 | 
			
		||||
 | 
			
		||||
  type toString =
 | 
			
		||||
    | ToString
 | 
			
		||||
    | ToSparkline(int)
 | 
			
		||||
 | 
			
		||||
  type toBool = IsNormalized
 | 
			
		||||
 | 
			
		||||
  type fromDist =
 | 
			
		||||
    | ToFloat(toFloat)
 | 
			
		||||
    | ToDist(toDist)
 | 
			
		||||
    | ToDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)])
 | 
			
		||||
    | ToString(toString)
 | 
			
		||||
    | ToBool(toBool)
 | 
			
		||||
 | 
			
		||||
  type singleParamaterFunction =
 | 
			
		||||
    | FromDist(fromDist)
 | 
			
		||||
    | FromFloat(fromDist)
 | 
			
		||||
 | 
			
		||||
  @genType
 | 
			
		||||
  type genericFunctionCallInfo =
 | 
			
		||||
    | FromDist(fromDist, genericDist)
 | 
			
		||||
    | FromFloat(fromDist, float)
 | 
			
		||||
    | Mixture(array<(genericDist, float)>)
 | 
			
		||||
 | 
			
		||||
  let distCallToString = (distFunction: fromDist): string =>
 | 
			
		||||
    switch distFunction {
 | 
			
		||||
    | ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})`
 | 
			
		||||
    | ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})`
 | 
			
		||||
    | ToFloat(#Mean) => `mean`
 | 
			
		||||
    | ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})`
 | 
			
		||||
    | ToFloat(#Sample) => `sample`
 | 
			
		||||
    | ToDist(Normalize) => `normalize`
 | 
			
		||||
    | ToDist(ToPointSet) => `toPointSet`
 | 
			
		||||
    | ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
 | 
			
		||||
    | ToDist(Truncate(_, _)) => `truncate`
 | 
			
		||||
    | ToDist(Inspect) => `inspect`
 | 
			
		||||
    | ToString(ToString) => `toString`
 | 
			
		||||
    | ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})`
 | 
			
		||||
    | ToBool(IsNormalized) => `isNormalized`
 | 
			
		||||
    | ToDistCombination(Algebraic, _, _) => `algebraic`
 | 
			
		||||
    | ToDistCombination(Pointwise, _, _) => `pointwise`
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  let toString = (d: genericFunctionCallInfo): string =>
 | 
			
		||||
    switch d {
 | 
			
		||||
    | FromDist(f, _) | FromFloat(f, _) => distCallToString(f)
 | 
			
		||||
    | Mixture(_) => `mixture`
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
It can be a pain to write out the genericFunctionCallInfo. The constructors help with this. 
 | 
			
		||||
This code only covers some of genericFunctionCallInfo: many arguments could be called with either a
 | 
			
		||||
float or a distribution. The "UsingDists" module assumes that everything is a distribution.
 | 
			
		||||
This is a tradeoff of some generality in order to get a bit more simplicity.
 | 
			
		||||
I could see having a longer interface in the future, but it could be messy.
 | 
			
		||||
Like, algebraicAddDistFloat vs. algebraicAddDistDist
 | 
			
		||||
*/
 | 
			
		||||
module Constructors = {
 | 
			
		||||
  type t = Operation.genericFunctionCallInfo
 | 
			
		||||
 | 
			
		||||
  module UsingDists = {
 | 
			
		||||
    @genType
 | 
			
		||||
    let mean = (dist): t => FromDist(ToFloat(#Mean), dist)
 | 
			
		||||
    let sample = (dist): t => FromDist(ToFloat(#Sample), dist)
 | 
			
		||||
    let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist)
 | 
			
		||||
    let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist)
 | 
			
		||||
    let pdf = (dist, x): t => FromDist(ToFloat(#Pdf(x)), dist)
 | 
			
		||||
    let normalize = (dist): t => FromDist(ToDist(Normalize), dist)
 | 
			
		||||
    let isNormalized = (dist): t => FromDist(ToBool(IsNormalized), dist)
 | 
			
		||||
    let toPointSet = (dist): t => FromDist(ToDist(ToPointSet), dist)
 | 
			
		||||
    let toSampleSet = (dist, r): t => FromDist(ToDist(ToSampleSet(r)), dist)
 | 
			
		||||
    let truncate = (dist, left, right): t => FromDist(ToDist(Truncate(left, right)), dist)
 | 
			
		||||
    let inspect = (dist): t => FromDist(ToDist(Inspect), dist)
 | 
			
		||||
    let toString = (dist): t => FromDist(ToString(ToString), dist)
 | 
			
		||||
    let toSparkline = (dist, n): t => FromDist(ToString(ToSparkline(n)), dist)
 | 
			
		||||
    let algebraicAdd = (dist1, dist2: genericDist): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Add, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let algebraicMultiply = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Multiply, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let algebraicDivide = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Divide, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let algebraicSubtract = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Subtract, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let algebraicLogarithm = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Logarithm, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let algebraicPower = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Algebraic, #Power, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwiseAdd = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Add, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwiseMultiply = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Multiply, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwiseDivide = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Divide, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwiseSubtract = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Subtract, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwiseLogarithm = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Logarithm, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
    let pointwisePower = (dist1, dist2): t => FromDist(
 | 
			
		||||
      ToDistCombination(Pointwise, #Power, #Dist(dist2)),
 | 
			
		||||
      dist1,
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -230,23 +230,24 @@ let combineShapesContinuousDiscrete = (
 | 
			
		|||
          i,
 | 
			
		||||
          (
 | 
			
		||||
            fn(continuousShape.xs[i], discreteShape.xs[j]),
 | 
			
		||||
            continuousShape.ys[i] *. discreteShape.ys[j] /. discreteShape.xs[j],
 | 
			
		||||
            continuousShape.ys[i] *. discreteShape.ys[j] /. Js.Math.abs_float(discreteShape.xs[j]),
 | 
			
		||||
          ),
 | 
			
		||||
        ) |> ignore
 | 
			
		||||
        ()
 | 
			
		||||
      }
 | 
			
		||||
      Belt.Array.set(outXYShapes, j, dxyShape) |> ignore
 | 
			
		||||
      ()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  outXYShapes
 | 
			
		||||
  |> E.A.fmap(XYShape.T.fromZippedArray)
 | 
			
		||||
  |> E.A.fold_left(
 | 
			
		||||
    XYShape.PointwiseCombination.combine(
 | 
			
		||||
      \"+.",
 | 
			
		||||
      XYShape.XtoY.continuousInterpolator(#Linear, #UseZero),
 | 
			
		||||
    ),
 | 
			
		||||
    (acc, x) =>
 | 
			
		||||
      XYShape.PointwiseCombination.addCombine(
 | 
			
		||||
        XYShape.XtoY.continuousInterpolator(#Linear, #UseZero),
 | 
			
		||||
        acc,
 | 
			
		||||
        x,
 | 
			
		||||
      ),
 | 
			
		||||
    XYShape.T.empty,
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -88,10 +88,10 @@ let stepwiseToLinear = (t: t): t =>
 | 
			
		|||
let combinePointwise = (
 | 
			
		||||
  ~integralSumCachesFn=(_, _) => None,
 | 
			
		||||
  ~distributionType: PointSetTypes.distributionType=#PDF,
 | 
			
		||||
  fn: (float, float) => float,
 | 
			
		||||
  fn: (float, float) => result<float, Operation.Error.t>,
 | 
			
		||||
  t1: PointSetTypes.continuousShape,
 | 
			
		||||
  t2: PointSetTypes.continuousShape,
 | 
			
		||||
): PointSetTypes.continuousShape => {
 | 
			
		||||
): result<PointSetTypes.continuousShape, 'e> => {
 | 
			
		||||
  // If we're adding the distributions, and we know the total of each, then we
 | 
			
		||||
  // can just sum them up. Otherwise, all bets are off.
 | 
			
		||||
  let combinedIntegralSum = Common.combineIntegralSums(
 | 
			
		||||
| 
						 | 
				
			
			@ -119,9 +119,8 @@ let combinePointwise = (
 | 
			
		|||
 | 
			
		||||
  let interpolator = XYShape.XtoY.continuousInterpolator(t1.interpolation, extrapolation)
 | 
			
		||||
 | 
			
		||||
  make(
 | 
			
		||||
    ~integralSumCache=combinedIntegralSum,
 | 
			
		||||
    XYShape.PointwiseCombination.combine(fn, interpolator, t1.xyShape, t2.xyShape),
 | 
			
		||||
  XYShape.PointwiseCombination.combine(fn, interpolator, t1.xyShape, t2.xyShape)->E.R2.fmap(x =>
 | 
			
		||||
    make(~integralSumCache=combinedIntegralSum, x)
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -140,13 +139,47 @@ let updateIntegralSumCache = (integralSumCache, t: t): t => {
 | 
			
		|||
 | 
			
		||||
let updateIntegralCache = (integralCache, t: t): t => {...t, integralCache: integralCache}
 | 
			
		||||
 | 
			
		||||
let sum = (
 | 
			
		||||
  ~integralSumCachesFn: (float, float) => option<float>=(_, _) => None,
 | 
			
		||||
  continuousShapes,
 | 
			
		||||
): t =>
 | 
			
		||||
  continuousShapes |> E.A.fold_left(
 | 
			
		||||
    (x, y) =>
 | 
			
		||||
      combinePointwise(~integralSumCachesFn, (a, b) => Ok(a +. b), x, y)->E.R.toExn(
 | 
			
		||||
        "Addition should never fail",
 | 
			
		||||
        _,
 | 
			
		||||
      ),
 | 
			
		||||
    empty,
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
let reduce = (
 | 
			
		||||
  ~integralSumCachesFn: (float, float) => option<float>=(_, _) => None,
 | 
			
		||||
  fn,
 | 
			
		||||
  fn: (float, float) => result<float, 'e>,
 | 
			
		||||
  continuousShapes,
 | 
			
		||||
) => continuousShapes |> E.A.fold_left(combinePointwise(~integralSumCachesFn, fn), empty)
 | 
			
		||||
): result<t, 'e> =>
 | 
			
		||||
  continuousShapes |> E.A.R.foldM(combinePointwise(~integralSumCachesFn, fn), empty)
 | 
			
		||||
 | 
			
		||||
let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t) =>
 | 
			
		||||
let mapYResult = (
 | 
			
		||||
  ~integralSumCacheFn=_ => None,
 | 
			
		||||
  ~integralCacheFn=_ => None,
 | 
			
		||||
  ~fn: float => result<float, 'e>,
 | 
			
		||||
  t: t,
 | 
			
		||||
): result<t, 'e> =>
 | 
			
		||||
  XYShape.T.mapYResult(fn, getShape(t))->E.R2.fmap(x =>
 | 
			
		||||
    make(
 | 
			
		||||
      ~interpolation=t.interpolation,
 | 
			
		||||
      ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
 | 
			
		||||
      ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn),
 | 
			
		||||
      x,
 | 
			
		||||
    )
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
let mapY = (
 | 
			
		||||
  ~integralSumCacheFn=_ => None,
 | 
			
		||||
  ~integralCacheFn=_ => None,
 | 
			
		||||
  ~fn: float => float,
 | 
			
		||||
  t: t,
 | 
			
		||||
): t =>
 | 
			
		||||
  make(
 | 
			
		||||
    ~interpolation=t.interpolation,
 | 
			
		||||
    ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
 | 
			
		||||
| 
						 | 
				
			
			@ -170,6 +203,7 @@ module T = Dist({
 | 
			
		|||
  let minX = shapeFn(XYShape.T.minX)
 | 
			
		||||
  let maxX = shapeFn(XYShape.T.maxX)
 | 
			
		||||
  let mapY = mapY
 | 
			
		||||
  let mapYResult = mapYResult
 | 
			
		||||
  let updateIntegralCache = updateIntegralCache
 | 
			
		||||
  let toDiscreteProbabilityMassFraction = _ => 0.0
 | 
			
		||||
  let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Continuous(t)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,11 +49,11 @@ let combinePointwise = (
 | 
			
		|||
  make(
 | 
			
		||||
    ~integralSumCache=combinedIntegralSum,
 | 
			
		||||
    XYShape.PointwiseCombination.combine(
 | 
			
		||||
      \"+.",
 | 
			
		||||
      (a, b) => Ok(a +. b),
 | 
			
		||||
      XYShape.XtoY.discreteInterpolator,
 | 
			
		||||
      t1.xyShape,
 | 
			
		||||
      t2.xyShape,
 | 
			
		||||
    ),
 | 
			
		||||
    )->E.R.toExn("Addition operation should never fail", _),
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +103,26 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t
 | 
			
		|||
  make(~integralSumCache=combinedIntegralSum, combinedShape)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t) =>
 | 
			
		||||
let mapYResult = (
 | 
			
		||||
  ~integralSumCacheFn=_ => None,
 | 
			
		||||
  ~integralCacheFn=_ => None,
 | 
			
		||||
  ~fn: float => result<float, 'e>,
 | 
			
		||||
  t: t,
 | 
			
		||||
): result<t, 'e> =>
 | 
			
		||||
  XYShape.T.mapYResult(fn, getShape(t))->E.R2.fmap(x =>
 | 
			
		||||
    make(
 | 
			
		||||
      ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
 | 
			
		||||
      ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn),
 | 
			
		||||
      x,
 | 
			
		||||
    )
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
let mapY = (
 | 
			
		||||
  ~integralSumCacheFn=_ => None,
 | 
			
		||||
  ~integralCacheFn=_ => None,
 | 
			
		||||
  ~fn: float => float,
 | 
			
		||||
  t: t,
 | 
			
		||||
): t =>
 | 
			
		||||
  make(
 | 
			
		||||
    ~integralSumCache=t.integralSumCache |> E.O.bind(_, integralSumCacheFn),
 | 
			
		||||
    ~integralCache=t.integralCache |> E.O.bind(_, integralCacheFn),
 | 
			
		||||
| 
						 | 
				
			
			@ -143,6 +162,7 @@ module T = Dist({
 | 
			
		|||
  let maxX = shapeFn(XYShape.T.maxX)
 | 
			
		||||
  let toDiscreteProbabilityMassFraction = _ => 1.0
 | 
			
		||||
  let mapY = mapY
 | 
			
		||||
  let mapYResult = mapYResult
 | 
			
		||||
  let updateIntegralCache = updateIntegralCache
 | 
			
		||||
  let toPointSetDist = (t: t): PointSetTypes.pointSetDist => Discrete(t)
 | 
			
		||||
  let toContinuous = _ => None
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,12 @@ module type dist = {
 | 
			
		|||
    ~fn: float => float,
 | 
			
		||||
    t,
 | 
			
		||||
  ) => t
 | 
			
		||||
  let mapYResult: (
 | 
			
		||||
    ~integralSumCacheFn: float => option<float>=?,
 | 
			
		||||
    ~integralCacheFn: PointSetTypes.continuousShape => option<PointSetTypes.continuousShape>=?,
 | 
			
		||||
    ~fn: float => result<float, 'e>,
 | 
			
		||||
    t,
 | 
			
		||||
  ) => result<t, 'e>
 | 
			
		||||
  let xToY: (float, t) => PointSetTypes.mixedPoint
 | 
			
		||||
  let toPointSetDist: t => PointSetTypes.pointSetDist
 | 
			
		||||
  let toContinuous: t => option<PointSetTypes.continuousShape>
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +43,7 @@ module Dist = (T: dist) => {
 | 
			
		|||
  let integral = T.integral
 | 
			
		||||
  let xTotalRange = (t: t) => maxX(t) -. minX(t)
 | 
			
		||||
  let mapY = T.mapY
 | 
			
		||||
  let mapYResult = T.mapYResult
 | 
			
		||||
  let xToY = T.xToY
 | 
			
		||||
  let downsample = T.downsample
 | 
			
		||||
  let toPointSetDist = T.toPointSetDist
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -146,8 +146,7 @@ module T = Dist({
 | 
			
		|||
      let discreteIntegral = Continuous.stepwiseToLinear(Discrete.T.Integral.get(t.discrete))
 | 
			
		||||
 | 
			
		||||
      Continuous.make(
 | 
			
		||||
        XYShape.PointwiseCombination.combine(
 | 
			
		||||
          \"+.",
 | 
			
		||||
        XYShape.PointwiseCombination.addCombine(
 | 
			
		||||
          XYShape.XtoY.continuousInterpolator(#Linear, #UseOutermostPoints),
 | 
			
		||||
          Continuous.getShape(continuousIntegral),
 | 
			
		||||
          Continuous.getShape(discreteIntegral),
 | 
			
		||||
| 
						 | 
				
			
			@ -161,19 +160,20 @@ module T = Dist({
 | 
			
		|||
 | 
			
		||||
  let integralYtoX = (f, t) => t |> integral |> Continuous.getShape |> XYShape.YtoX.linear(f)
 | 
			
		||||
 | 
			
		||||
  // This pipes all ys (continuous and discrete) through fn.
 | 
			
		||||
  // If mapY is a linear operation, we might be able to update the integralSumCaches as well;
 | 
			
		||||
  // if not, they'll be set to None.
 | 
			
		||||
  let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn, t: t): t => {
 | 
			
		||||
  let createMixedFromContinuousDiscrete = (
 | 
			
		||||
    ~integralSumCacheFn=_ => None,
 | 
			
		||||
    ~integralCacheFn=_ => None,
 | 
			
		||||
    t: t,
 | 
			
		||||
    discrete: PointSetTypes.discreteShape,
 | 
			
		||||
    continuous: PointSetTypes.continuousShape,
 | 
			
		||||
  ): t => {
 | 
			
		||||
    let yMappedDiscrete: PointSetTypes.discreteShape =
 | 
			
		||||
      t.discrete
 | 
			
		||||
      |> Discrete.T.mapY(~fn)
 | 
			
		||||
      discrete
 | 
			
		||||
      |> Discrete.updateIntegralSumCache(E.O.bind(t.discrete.integralSumCache, integralSumCacheFn))
 | 
			
		||||
      |> Discrete.updateIntegralCache(E.O.bind(t.discrete.integralCache, integralCacheFn))
 | 
			
		||||
 | 
			
		||||
    let yMappedContinuous: PointSetTypes.continuousShape =
 | 
			
		||||
      t.continuous
 | 
			
		||||
      |> Continuous.T.mapY(~fn)
 | 
			
		||||
      continuous
 | 
			
		||||
      |> Continuous.updateIntegralSumCache(
 | 
			
		||||
        E.O.bind(t.continuous.integralSumCache, integralSumCacheFn),
 | 
			
		||||
      )
 | 
			
		||||
| 
						 | 
				
			
			@ -187,6 +187,46 @@ module T = Dist({
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // This pipes all ys (continuous and discrete) through fn.
 | 
			
		||||
  // If mapY is a linear operation, we might be able to update the integralSumCaches as well;
 | 
			
		||||
  // if not, they'll be set to None.
 | 
			
		||||
  let mapY = (
 | 
			
		||||
    ~integralSumCacheFn=_ => None,
 | 
			
		||||
    ~integralCacheFn=_ => None,
 | 
			
		||||
    ~fn: float => float,
 | 
			
		||||
    t: t,
 | 
			
		||||
  ): t => {
 | 
			
		||||
    let discrete = t.discrete |> Discrete.T.mapY(~fn)
 | 
			
		||||
    let continuous = t.continuous |> Continuous.T.mapY(~fn)
 | 
			
		||||
    createMixedFromContinuousDiscrete(
 | 
			
		||||
      ~integralCacheFn,
 | 
			
		||||
      ~integralSumCacheFn,
 | 
			
		||||
      t,
 | 
			
		||||
      discrete,
 | 
			
		||||
      continuous,
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let mapYResult = (
 | 
			
		||||
    ~integralSumCacheFn=_ => None,
 | 
			
		||||
    ~integralCacheFn=_ => None,
 | 
			
		||||
    ~fn: float => result<float, 'e>,
 | 
			
		||||
    t: t,
 | 
			
		||||
  ): result<t, 'e> => {
 | 
			
		||||
    E.R.merge(
 | 
			
		||||
      Discrete.T.mapYResult(~fn, t.discrete),
 | 
			
		||||
      Continuous.T.mapYResult(~fn, t.continuous),
 | 
			
		||||
    )->E.R2.fmap(((discreteMapped, continuousMapped)) => {
 | 
			
		||||
      createMixedFromContinuousDiscrete(
 | 
			
		||||
        ~integralCacheFn,
 | 
			
		||||
        ~integralSumCacheFn,
 | 
			
		||||
        t,
 | 
			
		||||
        discreteMapped,
 | 
			
		||||
        continuousMapped,
 | 
			
		||||
      )
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let mean = ({discrete, continuous}: t): float => {
 | 
			
		||||
    let discreteMean = Discrete.T.mean(discrete)
 | 
			
		||||
    let continuousMean = Continuous.T.mean(continuous)
 | 
			
		||||
| 
						 | 
				
			
			@ -239,7 +279,7 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t
 | 
			
		|||
  let ccConvResult = Continuous.combineAlgebraically(op, t1.continuous, t2.continuous)
 | 
			
		||||
  let dcConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t2.continuous, t1.discrete)
 | 
			
		||||
  let cdConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t1.continuous, t2.discrete)
 | 
			
		||||
  let continuousConvResult = Continuous.reduce(\"+.", [ccConvResult, dcConvResult, cdConvResult])
 | 
			
		||||
  let continuousConvResult = Continuous.sum([ccConvResult, dcConvResult, cdConvResult])
 | 
			
		||||
 | 
			
		||||
  // ... finally, discrete (*) discrete => discrete, obviously:
 | 
			
		||||
  let discreteConvResult = Discrete.combineAlgebraically(op, t1.discrete, t2.discrete)
 | 
			
		||||
| 
						 | 
				
			
			@ -261,10 +301,10 @@ let combineAlgebraically = (op: Operation.convolutionOperation, t1: t, t2: t): t
 | 
			
		|||
let combinePointwise = (
 | 
			
		||||
  ~integralSumCachesFn=(_, _) => None,
 | 
			
		||||
  ~integralCachesFn=(_, _) => None,
 | 
			
		||||
  fn,
 | 
			
		||||
  fn: (float, float) => result<float, 'e>,
 | 
			
		||||
  t1: t,
 | 
			
		||||
  t2: t,
 | 
			
		||||
): t => {
 | 
			
		||||
): result<t, 'e> => {
 | 
			
		||||
  let reducedDiscrete =
 | 
			
		||||
    [t1, t2] |> E.A.fmap(toDiscrete) |> E.A.O.concatSomes |> Discrete.reduce(~integralSumCachesFn)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -285,11 +325,12 @@ let combinePointwise = (
 | 
			
		|||
    t1.integralCache,
 | 
			
		||||
    t2.integralCache,
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  make(
 | 
			
		||||
    ~integralSumCache=combinedIntegralSum,
 | 
			
		||||
    ~integralCache=combinedIntegral,
 | 
			
		||||
    ~discrete=reducedDiscrete,
 | 
			
		||||
    ~continuous=reducedContinuous,
 | 
			
		||||
  reducedContinuous->E.R2.fmap(continuous =>
 | 
			
		||||
    make(
 | 
			
		||||
      ~integralSumCache=combinedIntegralSum,
 | 
			
		||||
      ~integralCache=combinedIntegral,
 | 
			
		||||
      ~discrete=reducedDiscrete,
 | 
			
		||||
      ~continuous,
 | 
			
		||||
    )
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,13 @@ let fmap = ((fn1, fn2, fn3), t: t): t =>
 | 
			
		|||
  | Continuous(m) => Continuous(fn3(m))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
let fmapResult = ((fn1, fn2, fn3), t: t): result<t, 'e> =>
 | 
			
		||||
  switch t {
 | 
			
		||||
  | Mixed(m) => fn1(m)->E.R2.fmap(x => PointSetTypes.Mixed(x))
 | 
			
		||||
  | Discrete(m) => fn2(m)->E.R2.fmap(x => PointSetTypes.Discrete(x))
 | 
			
		||||
  | Continuous(m) => fn3(m)->E.R2.fmap(x => PointSetTypes.Continuous(x))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
let toMixed = mapToAll((
 | 
			
		||||
  m => m,
 | 
			
		||||
  d =>
 | 
			
		||||
| 
						 | 
				
			
			@ -53,19 +60,28 @@ let combinePointwise = (
 | 
			
		|||
    PointSetTypes.continuousShape,
 | 
			
		||||
    PointSetTypes.continuousShape,
 | 
			
		||||
  ) => option<PointSetTypes.continuousShape>=(_, _) => None,
 | 
			
		||||
  fn,
 | 
			
		||||
  fn: (float, float) => result<float, Operation.Error.t>,
 | 
			
		||||
  t1: t,
 | 
			
		||||
  t2: t,
 | 
			
		||||
) =>
 | 
			
		||||
): result<PointSetTypes.pointSetDist, Operation.Error.t> =>
 | 
			
		||||
  switch (t1, t2) {
 | 
			
		||||
  | (Continuous(m1), Continuous(m2)) =>
 | 
			
		||||
    PointSetTypes.Continuous(Continuous.combinePointwise(~integralSumCachesFn, fn, m1, m2))
 | 
			
		||||
    Continuous.combinePointwise(
 | 
			
		||||
      ~integralSumCachesFn,
 | 
			
		||||
      fn,
 | 
			
		||||
      m1,
 | 
			
		||||
      m2,
 | 
			
		||||
    )->E.R2.fmap(x => PointSetTypes.Continuous(x))
 | 
			
		||||
  | (Discrete(m1), Discrete(m2)) =>
 | 
			
		||||
    PointSetTypes.Discrete(Discrete.combinePointwise(~integralSumCachesFn, m1, m2))
 | 
			
		||||
    Ok(PointSetTypes.Discrete(Discrete.combinePointwise(~integralSumCachesFn, m1, m2)))
 | 
			
		||||
  | (m1, m2) =>
 | 
			
		||||
    PointSetTypes.Mixed(
 | 
			
		||||
      Mixed.combinePointwise(~integralSumCachesFn, ~integralCachesFn, fn, toMixed(m1), toMixed(m2)),
 | 
			
		||||
    )
 | 
			
		||||
    Mixed.combinePointwise(
 | 
			
		||||
      ~integralSumCachesFn,
 | 
			
		||||
      ~integralCachesFn,
 | 
			
		||||
      fn,
 | 
			
		||||
      toMixed(m1),
 | 
			
		||||
      toMixed(m2),
 | 
			
		||||
    )->E.R2.fmap(x => PointSetTypes.Mixed(x))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
module T = Dist({
 | 
			
		||||
| 
						 | 
				
			
			@ -130,13 +146,26 @@ module T = Dist({
 | 
			
		|||
  let integralYtoX = f =>
 | 
			
		||||
    mapToAll((Mixed.T.Integral.yToX(f), Discrete.T.Integral.yToX(f), Continuous.T.Integral.yToX(f)))
 | 
			
		||||
  let maxX = mapToAll((Mixed.T.maxX, Discrete.T.maxX, Continuous.T.maxX))
 | 
			
		||||
  let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn) =>
 | 
			
		||||
  let mapY = (~integralSumCacheFn=_ => None, ~integralCacheFn=_ => None, ~fn: float => float): (
 | 
			
		||||
    t => t
 | 
			
		||||
  ) =>
 | 
			
		||||
    fmap((
 | 
			
		||||
      Mixed.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
 | 
			
		||||
      Discrete.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
 | 
			
		||||
      Continuous.T.mapY(~integralSumCacheFn, ~integralCacheFn, ~fn),
 | 
			
		||||
    ))
 | 
			
		||||
 | 
			
		||||
  let mapYResult = (
 | 
			
		||||
    ~integralSumCacheFn=_ => None,
 | 
			
		||||
    ~integralCacheFn=_ => None,
 | 
			
		||||
    ~fn: float => result<float, 'e>,
 | 
			
		||||
  ): (t => result<t, 'e>) =>
 | 
			
		||||
    fmapResult((
 | 
			
		||||
      Mixed.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn),
 | 
			
		||||
      Discrete.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn),
 | 
			
		||||
      Continuous.T.mapYResult(~integralSumCacheFn, ~integralCacheFn, ~fn),
 | 
			
		||||
    ))
 | 
			
		||||
 | 
			
		||||
  let mean = (t: t): float =>
 | 
			
		||||
    switch t {
 | 
			
		||||
    | Mixed(m) => Mixed.T.mean(m)
 | 
			
		||||
| 
						 | 
				
			
			@ -195,8 +224,8 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float =>
 | 
			
		|||
  | #Mean => T.mean(s)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
let toSparkline = (t: t, bucketCount) =>
 | 
			
		||||
let toSparkline = (t: t, bucketCount): result<string, PointSetTypes.sparklineError> =>
 | 
			
		||||
  T.toContinuous(t)
 | 
			
		||||
  ->E.O2.fmap(Continuous.downsampleEquallyOverX(bucketCount))
 | 
			
		||||
  ->E.O2.toResult("toContinous Error: Could not convert into continuous distribution")
 | 
			
		||||
  ->E.O2.toResult(PointSetTypes.CannotSparklineDiscrete)
 | 
			
		||||
  ->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -94,3 +94,11 @@ module MixedPoint = {
 | 
			
		|||
 | 
			
		||||
  let add = combine2((a, b) => a +. b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
type sparklineError = CannotSparklineDiscrete
 | 
			
		||||
 | 
			
		||||
let sparklineErrorToString = (err: sparklineError): string =>
 | 
			
		||||
  switch err {
 | 
			
		||||
  | CannotSparklineDiscrete => "Cannot find the sparkline of a discrete distribution"
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,24 @@
 | 
			
		|||
@genType
 | 
			
		||||
module Error = {
 | 
			
		||||
  @genType
 | 
			
		||||
  type sampleSetError = TooFewSamples
 | 
			
		||||
 | 
			
		||||
  let sampleSetErrorToString = (err: sampleSetError): string =>
 | 
			
		||||
    switch err {
 | 
			
		||||
    | TooFewSamples => "Too few samples when constructing sample set"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  @genType
 | 
			
		||||
  type pointsetConversionError = TooFewSamplesForConversionToPointSet
 | 
			
		||||
 | 
			
		||||
  let pointsetConversionErrorToString = (err: pointsetConversionError) =>
 | 
			
		||||
    switch err {
 | 
			
		||||
    | TooFewSamplesForConversionToPointSet => "Too Few Samples to convert to point set"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
include Error
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
This is used as a smart constructor. The only way to create a SampleSetDist.t is to call
 | 
			
		||||
this constructor.
 | 
			
		||||
| 
						 | 
				
			
			@ -8,7 +29,7 @@ module T: {
 | 
			
		|||
  //When we get a good functional library in TS, we could refactor that out.
 | 
			
		||||
  @genType
 | 
			
		||||
  type t = array<float>
 | 
			
		||||
  let make: array<float> => result<t, string>
 | 
			
		||||
  let make: array<float> => result<t, sampleSetError>
 | 
			
		||||
  let get: t => array<float>
 | 
			
		||||
} = {
 | 
			
		||||
  type t = array<float>
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +37,7 @@ module T: {
 | 
			
		|||
    if E.A.length(a) > 5 {
 | 
			
		||||
      Ok(a)
 | 
			
		||||
    } else {
 | 
			
		||||
      Error("too small")
 | 
			
		||||
      Error(TooFewSamples)
 | 
			
		||||
    }
 | 
			
		||||
  let get = (a: t) => a
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -31,13 +52,13 @@ some refactoring.
 | 
			
		|||
*/
 | 
			
		||||
let toPointSetDist = (~samples: t, ~samplingInputs: SamplingInputs.samplingInputs): result<
 | 
			
		||||
  PointSetTypes.pointSetDist,
 | 
			
		||||
  string,
 | 
			
		||||
  pointsetConversionError,
 | 
			
		||||
> =>
 | 
			
		||||
  SampleSetDist_ToPointSet.toPointSetDist(
 | 
			
		||||
    ~samples=get(samples),
 | 
			
		||||
    ~samplingInputs,
 | 
			
		||||
    (),
 | 
			
		||||
  ).pointSetDist->E.O2.toResult("Failed to convert to PointSetDist")
 | 
			
		||||
  ).pointSetDist->E.O2.toResult(TooFewSamplesForConversionToPointSet)
 | 
			
		||||
 | 
			
		||||
//Randomly get one sample from the distribution
 | 
			
		||||
let sample = (t: t): float => {
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +83,18 @@ let sampleN = (t: t, n) => {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
//TODO: Figure out what to do if distributions are different lengths. ``zip`` is kind of inelegant for this.
 | 
			
		||||
let map2 = (~fn: (float, float) => float, ~t1: t, ~t2: t) => {
 | 
			
		||||
let map2 = (~fn: (float, float) => result<float, Operation.Error.t>, ~t1: t, ~t2: t): result<
 | 
			
		||||
  t,
 | 
			
		||||
  Operation.Error.t,
 | 
			
		||||
> => {
 | 
			
		||||
  let samples = Belt.Array.zip(get(t1), get(t2))->E.A2.fmap(((a, b)) => fn(a, b))
 | 
			
		||||
  make(samples)
 | 
			
		||||
 | 
			
		||||
  // This assertion should never be reached. In order for it to be reached, one
 | 
			
		||||
  // of the input parameters would need to be a sample set distribution with less
 | 
			
		||||
  // than 6 samples. Which should be impossible due to the smart constructor.
 | 
			
		||||
  // I could prove this to the type system (say, creating a {first: float, second: float, ..., fifth: float, rest: array<float>}
 | 
			
		||||
  // But doing so would take too much time, so I'll leave it as an assertion
 | 
			
		||||
  E.A.R.firstErrorOrOpen(samples)->E.R2.fmap(x =>
 | 
			
		||||
    E.R.toExn("Input of samples should be larger than 5", make(x))
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,7 +83,7 @@ let toPointSetDist = (
 | 
			
		|||
  ~samples: Internals.T.t,
 | 
			
		||||
  ~samplingInputs: SamplingInputs.samplingInputs,
 | 
			
		||||
  (),
 | 
			
		||||
) => {
 | 
			
		||||
): Internals.Types.outputs => {
 | 
			
		||||
  Array.fast_sort(compare, samples)
 | 
			
		||||
  let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples)
 | 
			
		||||
  let length = samples |> E.A.length |> float_of_int
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ module Normal = {
 | 
			
		|||
    switch operation {
 | 
			
		||||
    | #Add => Some(#Normal({mean: n1 +. n2.mean, stdev: n2.stdev}))
 | 
			
		||||
    | #Subtract => Some(#Normal({mean: n1 -. n2.mean, stdev: n2.stdev}))
 | 
			
		||||
    | #Multiply => Some(#Normal({mean: n1 *. n2.mean, stdev: n1 *. n2.stdev}))
 | 
			
		||||
    | #Multiply => Some(#Normal({mean: n1 *. n2.mean, stdev: Js.Math.abs_float(n1) *. n2.stdev}))
 | 
			
		||||
    | _ => None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -60,8 +60,8 @@ module Normal = {
 | 
			
		|||
    switch operation {
 | 
			
		||||
    | #Add => Some(#Normal({mean: n1.mean +. n2, stdev: n1.stdev}))
 | 
			
		||||
    | #Subtract => Some(#Normal({mean: n1.mean -. n2, stdev: n1.stdev}))
 | 
			
		||||
    | #Multiply => Some(#Normal({mean: n1.mean *. n2, stdev: n1.stdev *. n2}))
 | 
			
		||||
    | #Divide => Some(#Normal({mean: n1.mean /. n2, stdev: n1.stdev /. n2}))
 | 
			
		||||
    | #Multiply => Some(#Normal({mean: n1.mean *. n2, stdev: n1.stdev *. Js.Math.abs_float(n2)}))
 | 
			
		||||
    | #Divide => Some(#Normal({mean: n1.mean /. n2, stdev: n1.stdev /. Js.Math.abs_float(n2)}))
 | 
			
		||||
    | _ => None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -379,7 +379,7 @@ module T = {
 | 
			
		|||
  ): analyticalSimplificationResult =>
 | 
			
		||||
    switch (d1, d2) {
 | 
			
		||||
    | (#Float(v1), #Float(v2)) =>
 | 
			
		||||
      switch Operation.Algebraic.applyFn(op, v1, v2) {
 | 
			
		||||
      switch Operation.Algebraic.toFn(op, v1, v2) {
 | 
			
		||||
      | Ok(r) => #AnalyticalSolution(#Float(r))
 | 
			
		||||
      | Error(n) => #Error(n)
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,6 +45,6 @@ type symbolicDist = [
 | 
			
		|||
 | 
			
		||||
type analyticalSimplificationResult = [
 | 
			
		||||
  | #AnalyticalSolution(symbolicDist)
 | 
			
		||||
  | #Error(string)
 | 
			
		||||
  | #Error(Operation.Error.t)
 | 
			
		||||
  | #NoSolution
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ type errorValue =
 | 
			
		|||
  | RERecordPropertyNotFound(string, string)
 | 
			
		||||
  | RESymbolNotFound(string)
 | 
			
		||||
  | RESyntaxError(string)
 | 
			
		||||
  | REDistributionError(DistributionTypes.error)
 | 
			
		||||
  | RETodo(string) // To do
 | 
			
		||||
 | 
			
		||||
type t = errorValue
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +21,7 @@ let errorToString = err =>
 | 
			
		|||
  | REAssignmentExpected => "Assignment expected"
 | 
			
		||||
  | REExpressionExpected => "Expression expected"
 | 
			
		||||
  | REFunctionExpected(msg) => `Function expected: ${msg}`
 | 
			
		||||
  | REDistributionError(err) => `Math Error: ${DistributionTypes.Error.toString(err)}`
 | 
			
		||||
  | REJavaScriptExn(omsg, oname) => {
 | 
			
		||||
      let answer = "JS Exception:"
 | 
			
		||||
      let answer = switch oname {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ type rec expressionValue =
 | 
			
		|||
  | EvArray(array<expressionValue>)
 | 
			
		||||
  | EvBool(bool)
 | 
			
		||||
  | EvCall(string) // External function call
 | 
			
		||||
  | EvDistribution(GenericDist_Types.genericDist)
 | 
			
		||||
  | EvDistribution(DistributionTypes.genericDist)
 | 
			
		||||
  | EvLambda((array<string>, internalCode))
 | 
			
		||||
  | EvNumber(float)
 | 
			
		||||
  | EvRecord(Js.Dict.t<expressionValue>)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,13 +24,12 @@ module Helpers = {
 | 
			
		|||
    | "dotPow" => #Power
 | 
			
		||||
    | "multiply" => #Multiply
 | 
			
		||||
    | "dotMultiply" => #Multiply
 | 
			
		||||
    | "dotLog" => #Logarithm
 | 
			
		||||
    | _ => #Multiply
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  let catchAndConvertTwoArgsToDists = (args: array<expressionValue>): option<(
 | 
			
		||||
    GenericDist_Types.genericDist,
 | 
			
		||||
    GenericDist_Types.genericDist,
 | 
			
		||||
    DistributionTypes.genericDist,
 | 
			
		||||
    DistributionTypes.genericDist,
 | 
			
		||||
  )> => {
 | 
			
		||||
    switch args {
 | 
			
		||||
    | [EvDistribution(a), EvDistribution(b)] => Some((a, b))
 | 
			
		||||
| 
						 | 
				
			
			@ -41,33 +40,41 @@ module Helpers = {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  let toFloatFn = (
 | 
			
		||||
    fnCall: GenericDist_Types.Operation.toFloat,
 | 
			
		||||
    dist: GenericDist_Types.genericDist,
 | 
			
		||||
    fnCall: DistributionTypes.DistributionOperation.toFloat,
 | 
			
		||||
    dist: DistributionTypes.genericDist,
 | 
			
		||||
  ) => {
 | 
			
		||||
    FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some
 | 
			
		||||
    FromDist(DistributionTypes.DistributionOperation.ToFloat(fnCall), dist)
 | 
			
		||||
    ->runGenericOperation
 | 
			
		||||
    ->Some
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let toStringFn = (
 | 
			
		||||
    fnCall: GenericDist_Types.Operation.toString,
 | 
			
		||||
    dist: GenericDist_Types.genericDist,
 | 
			
		||||
    fnCall: DistributionTypes.DistributionOperation.toString,
 | 
			
		||||
    dist: DistributionTypes.genericDist,
 | 
			
		||||
  ) => {
 | 
			
		||||
    FromDist(GenericDist_Types.Operation.ToString(fnCall), dist)->runGenericOperation->Some
 | 
			
		||||
    FromDist(DistributionTypes.DistributionOperation.ToString(fnCall), dist)
 | 
			
		||||
    ->runGenericOperation
 | 
			
		||||
    ->Some
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let toBoolFn = (
 | 
			
		||||
    fnCall: GenericDist_Types.Operation.toBool,
 | 
			
		||||
    dist: GenericDist_Types.genericDist,
 | 
			
		||||
    fnCall: DistributionTypes.DistributionOperation.toBool,
 | 
			
		||||
    dist: DistributionTypes.genericDist,
 | 
			
		||||
  ) => {
 | 
			
		||||
    FromDist(GenericDist_Types.Operation.ToBool(fnCall), dist)->runGenericOperation->Some
 | 
			
		||||
    FromDist(DistributionTypes.DistributionOperation.ToBool(fnCall), dist)
 | 
			
		||||
    ->runGenericOperation
 | 
			
		||||
    ->Some
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => {
 | 
			
		||||
    FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some
 | 
			
		||||
  let toDistFn = (fnCall: DistributionTypes.DistributionOperation.toDist, dist) => {
 | 
			
		||||
    FromDist(DistributionTypes.DistributionOperation.ToDist(fnCall), dist)
 | 
			
		||||
    ->runGenericOperation
 | 
			
		||||
    ->Some
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let twoDiststoDistFn = (direction, arithmetic, dist1, dist2) => {
 | 
			
		||||
    FromDist(
 | 
			
		||||
      GenericDist_Types.Operation.ToDistCombination(
 | 
			
		||||
      DistributionTypes.DistributionOperation.ToDistCombination(
 | 
			
		||||
        direction,
 | 
			
		||||
        arithmeticMap(arithmetic),
 | 
			
		||||
        #Dist(dist2),
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +91,7 @@ module Helpers = {
 | 
			
		|||
  let parseNumberArray = (ags: array<expressionValue>): Belt.Result.t<array<float>, string> =>
 | 
			
		||||
    E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen
 | 
			
		||||
 | 
			
		||||
  let parseDist = (args: expressionValue): Belt.Result.t<GenericDist_Types.genericDist, string> =>
 | 
			
		||||
  let parseDist = (args: expressionValue): Belt.Result.t<DistributionTypes.genericDist, string> =>
 | 
			
		||||
    switch args {
 | 
			
		||||
    | EvDistribution(x) => Ok(x)
 | 
			
		||||
    | EvNumber(x) => Ok(GenericDist.fromFloat(x))
 | 
			
		||||
| 
						 | 
				
			
			@ -92,12 +99,12 @@ module Helpers = {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
  let parseDistributionArray = (ags: array<expressionValue>): Belt.Result.t<
 | 
			
		||||
    array<GenericDist_Types.genericDist>,
 | 
			
		||||
    array<DistributionTypes.genericDist>,
 | 
			
		||||
    string,
 | 
			
		||||
  > => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen
 | 
			
		||||
 | 
			
		||||
  let mixtureWithGivenWeights = (
 | 
			
		||||
    distributions: array<GenericDist_Types.genericDist>,
 | 
			
		||||
    distributions: array<DistributionTypes.genericDist>,
 | 
			
		||||
    weights: array<float>,
 | 
			
		||||
  ): DistributionOperation.outputType =>
 | 
			
		||||
    E.A.length(distributions) == E.A.length(weights)
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +114,7 @@ module Helpers = {
 | 
			
		|||
        )
 | 
			
		||||
 | 
			
		||||
  let mixtureWithDefaultWeights = (
 | 
			
		||||
    distributions: array<GenericDist_Types.genericDist>,
 | 
			
		||||
    distributions: array<DistributionTypes.genericDist>,
 | 
			
		||||
  ): DistributionOperation.outputType => {
 | 
			
		||||
    let length = E.A.length(distributions)
 | 
			
		||||
    let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length))
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +172,7 @@ module SymbolicConstructors = {
 | 
			
		|||
  ): option<DistributionOperation.outputType> =>
 | 
			
		||||
    switch symbolicResult {
 | 
			
		||||
    | Ok(r) => Some(Dist(Symbolic(r)))
 | 
			
		||||
    | Error(r) => Some(GenDistError(Other(r)))
 | 
			
		||||
    | Error(r) => Some(GenDistError(OtherError(r)))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -235,15 +242,12 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
 | 
			
		|||
      | "dotMultiply"
 | 
			
		||||
      | "dotSubtract"
 | 
			
		||||
      | "dotDivide"
 | 
			
		||||
      | "dotPow"
 | 
			
		||||
      | "dotLog") as arithmetic,
 | 
			
		||||
      | "dotPow") as arithmetic,
 | 
			
		||||
      [_, _] as args,
 | 
			
		||||
    ) =>
 | 
			
		||||
    Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
 | 
			
		||||
      Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd)
 | 
			
		||||
    )
 | 
			
		||||
  | ("dotLog", [EvDistribution(a)]) =>
 | 
			
		||||
    Helpers.twoDiststoDistFn(Pointwise, "dotLog", a, GenericDist.fromFloat(Math.e))->Some
 | 
			
		||||
  | ("dotExp", [EvDistribution(a)]) =>
 | 
			
		||||
    Helpers.twoDiststoDistFn(Pointwise, "dotPow", GenericDist.fromFloat(Math.e), a)->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
| 
						 | 
				
			
			@ -259,12 +263,7 @@ let genericOutputToReducerValue = (o: DistributionOperation.outputType): result<
 | 
			
		|||
  | Float(d) => Ok(EvNumber(d))
 | 
			
		||||
  | String(d) => Ok(EvString(d))
 | 
			
		||||
  | Bool(d) => Ok(EvBool(d))
 | 
			
		||||
  | GenDistError(NotYetImplemented) => Error(RETodo("Function not yet implemented"))
 | 
			
		||||
  | GenDistError(Unreachable) => Error(RETodo("Unreachable"))
 | 
			
		||||
  | GenDistError(DistributionVerticalShiftIsInvalid) =>
 | 
			
		||||
    Error(RETodo("Distribution Vertical Shift Is Invalid"))
 | 
			
		||||
  | GenDistError(ArgumentError(err)) => Error(RETodo("Argument Error: " ++ err))
 | 
			
		||||
  | GenDistError(Other(s)) => Error(RETodo(s))
 | 
			
		||||
  | GenDistError(err) => Error(REDistributionError(err))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
let dispatch = call => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,4 +53,4 @@ type continuousShape = PointSetTypes.continuousShape
 | 
			
		|||
let errorValueToString = Reducer_ErrorValue.errorToString
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let distributionErrorToString = GenericDist_Types.Error.toString
 | 
			
		||||
let distributionErrorToString = DistributionTypes.Error.toString
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -152,13 +152,20 @@ module I = {
 | 
			
		|||
  let toString = Js.Int.toString
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exception Assertion(string)
 | 
			
		||||
 | 
			
		||||
/* R for Result */
 | 
			
		||||
module R = {
 | 
			
		||||
  let result = Rationale.Result.result
 | 
			
		||||
  let id = e => e |> result(U.id, U.id)
 | 
			
		||||
  let fmap = Rationale.Result.fmap
 | 
			
		||||
  let bind = Rationale.Result.bind
 | 
			
		||||
  let toExn = Belt.Result.getExn
 | 
			
		||||
  let toExn = (msg: string, x: result<'a, 'b>): 'a =>
 | 
			
		||||
    switch x {
 | 
			
		||||
    | Ok(r) => r
 | 
			
		||||
    | Error(_) => raise(Assertion(msg))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  let default = (default, res: Belt.Result.t<'a, 'b>) =>
 | 
			
		||||
    switch res {
 | 
			
		||||
    | Ok(r) => r
 | 
			
		||||
| 
						 | 
				
			
			@ -185,6 +192,7 @@ module R = {
 | 
			
		|||
    | Ok(f) => fmap(f, a)
 | 
			
		||||
    | Error(err) => Error(err)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  // (a1 -> a2 -> r) -> m a1 -> m a2 -> m r  // not in Rationale
 | 
			
		||||
  let liftM2: (('a, 'b) => 'c, result<'a, 'd>, result<'b, 'd>) => result<'c, 'd> = (op, xR, yR) => {
 | 
			
		||||
    ap'(fmap(op, xR), yR)
 | 
			
		||||
| 
						 | 
				
			
			@ -210,10 +218,10 @@ module R2 = {
 | 
			
		|||
  let bind = (a, b) => R.bind(b, a)
 | 
			
		||||
 | 
			
		||||
  //Converts result type to change error type only
 | 
			
		||||
  let errMap = (a, map) =>
 | 
			
		||||
  let errMap = (a: result<'a, 'b>, map: 'b => 'c): result<'a, 'c> =>
 | 
			
		||||
    switch a {
 | 
			
		||||
    | Ok(r) => Ok(r)
 | 
			
		||||
    | Error(e) => map(e)
 | 
			
		||||
    | Error(e) => Error(map(e))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  let fmap2 = (xR, f) =>
 | 
			
		||||
| 
						 | 
				
			
			@ -436,6 +444,32 @@ module A = {
 | 
			
		|||
        r |> Belt.Array.map(_, r => Belt.Result.getExn(r))
 | 
			
		||||
      bringErrorUp |> Belt.Result.map(_, forceOpen)
 | 
			
		||||
    }
 | 
			
		||||
    let filterOk = (x: array<result<'a, 'b>>): array<'a> => fmap(R.toOption, x)->O.concatSomes
 | 
			
		||||
 | 
			
		||||
    let forM = (x: array<'a>, fn: 'a => result<'b, 'c>): result<array<'b>, 'c> =>
 | 
			
		||||
      firstErrorOrOpen(fmap(fn, x))
 | 
			
		||||
 | 
			
		||||
    let foldM = (fn: ('c, 'a) => result<'b, 'e>, init: 'c, x: array<'a>): result<'c, 'e> => {
 | 
			
		||||
      let acc = ref(init)
 | 
			
		||||
      let final = ref(Ok())
 | 
			
		||||
      let break = ref(false)
 | 
			
		||||
      let i = ref(0)
 | 
			
		||||
 | 
			
		||||
      while break.contents != true && i.contents < length(x) {
 | 
			
		||||
        switch fn(acc.contents, x[i.contents]) {
 | 
			
		||||
        | Ok(r) => acc := r
 | 
			
		||||
        | Error(err) => {
 | 
			
		||||
            final := Error(err)
 | 
			
		||||
            break := true
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        i := i.contents + 1
 | 
			
		||||
      }
 | 
			
		||||
      switch final.contents {
 | 
			
		||||
      | Ok(_) => Ok(acc.contents)
 | 
			
		||||
      | Error(err) => Error(err)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  module Sorted = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,22 +37,59 @@ module Convolution = {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module Algebraic = {
 | 
			
		||||
  type t = algebraicOperation
 | 
			
		||||
  let toFn: (t, float, float) => float = x =>
 | 
			
		||||
    switch x {
 | 
			
		||||
    | #Add => \"+."
 | 
			
		||||
    | #Subtract => \"-."
 | 
			
		||||
    | #Multiply => \"*."
 | 
			
		||||
    | #Power => \"**"
 | 
			
		||||
    | #Divide => \"/."
 | 
			
		||||
    | #Logarithm => (a, b) => log(a) /. log(b)
 | 
			
		||||
    }
 | 
			
		||||
type operationError =
 | 
			
		||||
  | DivisionByZeroError
 | 
			
		||||
  | ComplexNumberError
 | 
			
		||||
 | 
			
		||||
  let applyFn = (t, f1, f2) =>
 | 
			
		||||
    switch (t, f1, f2) {
 | 
			
		||||
    | (#Divide, _, 0.) => Error("Cannot divide $v1 by zero.")
 | 
			
		||||
    | _ => Ok(toFn(t, f1, f2))
 | 
			
		||||
@genType
 | 
			
		||||
module Error = {
 | 
			
		||||
  @genType
 | 
			
		||||
  type t = operationError
 | 
			
		||||
 | 
			
		||||
  let toString = (err: t): string =>
 | 
			
		||||
    switch err {
 | 
			
		||||
    | DivisionByZeroError => "Cannot divide by zero"
 | 
			
		||||
    | ComplexNumberError => "Operation returned complex result"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let power = (a: float, b: float): result<float, Error.t> =>
 | 
			
		||||
  if a >= 0.0 {
 | 
			
		||||
    Ok(a ** b)
 | 
			
		||||
  } else {
 | 
			
		||||
    Error(ComplexNumberError)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
let divide = (a: float, b: float): result<float, Error.t> =>
 | 
			
		||||
  if b != 0.0 {
 | 
			
		||||
    Ok(a /. b)
 | 
			
		||||
  } else {
 | 
			
		||||
    Error(DivisionByZeroError)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
let logarithm = (a: float, b: float): result<float, Error.t> =>
 | 
			
		||||
  if b == 1. {
 | 
			
		||||
    Error(DivisionByZeroError)
 | 
			
		||||
  } else if b == 0. {
 | 
			
		||||
    Ok(0.)
 | 
			
		||||
  } else if a > 0.0 && b > 0.0 {
 | 
			
		||||
    Ok(log(a) /. log(b))
 | 
			
		||||
  } else {
 | 
			
		||||
    Error(ComplexNumberError)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
module Algebraic = {
 | 
			
		||||
  @genType
 | 
			
		||||
  type t = algebraicOperation
 | 
			
		||||
  let toFn: (t, float, float) => result<float, Error.t> = (x, a, b) =>
 | 
			
		||||
    switch x {
 | 
			
		||||
    | #Add => Ok(a +. b)
 | 
			
		||||
    | #Subtract => Ok(a -. b)
 | 
			
		||||
    | #Multiply => Ok(a *. b)
 | 
			
		||||
    | #Power => power(a, b)
 | 
			
		||||
    | #Divide => divide(a, b)
 | 
			
		||||
    | #Logarithm => logarithm(a, b)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  let toString = x =>
 | 
			
		||||
| 
						 | 
				
			
			@ -96,12 +133,12 @@ module DistToFloat = {
 | 
			
		|||
// Note that different logarithms don't really do anything.
 | 
			
		||||
module Scale = {
 | 
			
		||||
  type t = scaleOperation
 | 
			
		||||
  let toFn = x =>
 | 
			
		||||
  let toFn = (x: t, a: float, b: float): result<float, Error.t> =>
 | 
			
		||||
    switch x {
 | 
			
		||||
    | #Multiply => \"*."
 | 
			
		||||
    | #Divide => \"/."
 | 
			
		||||
    | #Power => \"**"
 | 
			
		||||
    | #Logarithm => (a, b) => log(a) /. log(b)
 | 
			
		||||
    | #Multiply => Ok(a *. b)
 | 
			
		||||
    | #Divide => divide(a, b)
 | 
			
		||||
    | #Power => power(a, b)
 | 
			
		||||
    | #Logarithm => logarithm(a, b)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  let format = (operation: t, value, scaleBy) =>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,10 @@ module T = {
 | 
			
		|||
  let xTotalRange = (t: t) => maxX(t) -. minX(t)
 | 
			
		||||
  let mapX = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys}
 | 
			
		||||
  let mapY = (fn, t: t): t => {xs: t.xs, ys: E.A.fmap(fn, t.ys)}
 | 
			
		||||
  let mapYResult = (fn: float => result<float, 'e>, t: t): result<t, 'e> => {
 | 
			
		||||
    let mappedYs = E.A.fmap(fn, t.ys)
 | 
			
		||||
    E.A.R.firstErrorOrOpen(mappedYs)->E.R2.fmap(y => {xs: t.xs, ys: y})
 | 
			
		||||
  }
 | 
			
		||||
  let square = mapX(x => x ** 2.0)
 | 
			
		||||
  let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys)
 | 
			
		||||
  let fromArray = ((xs, ys)): t => {xs: xs, ys: ys}
 | 
			
		||||
| 
						 | 
				
			
			@ -229,7 +233,12 @@ module Zipped = {
 | 
			
		|||
 | 
			
		||||
module PointwiseCombination = {
 | 
			
		||||
  // t1Interpolator and t2Interpolator are functions from XYShape.XtoY, e.g. linearBetweenPointsExtrapolateFlat.
 | 
			
		||||
  let combine: ((float, float) => float, interpolator, T.t, T.t) => T.t = %raw(`
 | 
			
		||||
  let combine: (
 | 
			
		||||
    (float, float) => result<float, Operation.Error.t>,
 | 
			
		||||
    interpolator,
 | 
			
		||||
    T.t,
 | 
			
		||||
    T.t,
 | 
			
		||||
  ) => result<T.t, Operation.Error.t> = %raw(`
 | 
			
		||||
      // This function combines two xyShapes by looping through both of them simultaneously.
 | 
			
		||||
      // It always moves on to the next smallest x, whether that's in the first or second input's xs,
 | 
			
		||||
      // and interpolates the value on the other side, thus accumulating xs and ys.
 | 
			
		||||
| 
						 | 
				
			
			@ -277,13 +286,28 @@ module PointwiseCombination = {
 | 
			
		|||
          }
 | 
			
		||||
 | 
			
		||||
          outX.push(x);
 | 
			
		||||
          outY.push(fn(ya, yb));
 | 
			
		||||
 | 
			
		||||
          // Here I check whether the operation was a success. If it was
 | 
			
		||||
          // keep going. Otherwise, stop and throw the error back to user
 | 
			
		||||
          let newY = fn(ya, yb);
 | 
			
		||||
          if(newY.TAG === 0){
 | 
			
		||||
            outY.push(newY._0);
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            return newY;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return {xs: outX, ys: outY};
 | 
			
		||||
        return {TAG: 0, _0: {xs: outX, ys: outY}, [Symbol.for("name")]: "Ok"};
 | 
			
		||||
      }
 | 
			
		||||
    `)
 | 
			
		||||
 | 
			
		||||
  let addCombine = (interpolator: interpolator, t1: T.t, t2: T.t): T.t =>
 | 
			
		||||
    combine((a, b) => Ok(a +. b), interpolator, t1, t2)->E.R.toExn(
 | 
			
		||||
      "Add operation should never fail",
 | 
			
		||||
      _,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  let combineEvenXs = (~fn, ~xToYSelection, sampleCount, t1: T.t, t2: T.t) =>
 | 
			
		||||
    switch (E.A.length(t1.xs), E.A.length(t2.xs)) {
 | 
			
		||||
    | (0, 0) => T.empty
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -255,16 +255,6 @@ dist2 = triangular(1,2,3)
 | 
			
		|||
dist1 .^ dist2`}
 | 
			
		||||
/>
 | 
			
		||||
 | 
			
		||||
### Pointwise logarithm
 | 
			
		||||
 | 
			
		||||
TODO: write about the semantics and the case handling re scalar vs. dist and log base.
 | 
			
		||||
 | 
			
		||||
<SquiggleEditor
 | 
			
		||||
  initialSquiggleString={`dist1 = 1 to 10
 | 
			
		||||
dist2 = triangular(1,2,3)
 | 
			
		||||
dotLog(dist1, dist2)`}
 | 
			
		||||
/>
 | 
			
		||||
 | 
			
		||||
## Standard functions on distributions
 | 
			
		||||
 | 
			
		||||
### Probability density function
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user