First refactor to XYShape
This commit is contained in:
parent
f061e9fa01
commit
6f8d06a6d6
|
@ -1,179 +0,0 @@
|
||||||
open Jest;
|
|
||||||
open Expect;
|
|
||||||
|
|
||||||
exception ShapeWrong(string);
|
|
||||||
describe("CDF", () => {
|
|
||||||
test("raise - w/o order", () => {
|
|
||||||
expect(() => {
|
|
||||||
module Cdf =
|
|
||||||
CDF.Make({
|
|
||||||
let shape: DistTypes.xyShape = {
|
|
||||||
xs: [|10., 4., 8.|],
|
|
||||||
ys: [|8., 9., 2.|],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
();
|
|
||||||
})
|
|
||||||
|> toThrow
|
|
||||||
});
|
|
||||||
test("raise - with order", () => {
|
|
||||||
expect(() => {
|
|
||||||
module Cdf =
|
|
||||||
CDF.Make({
|
|
||||||
let shape: DistTypes.xyShape = {
|
|
||||||
xs: [|1., 4., 8.|],
|
|
||||||
ys: [|8., 9., 2.|],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
();
|
|
||||||
})
|
|
||||||
|> not_
|
|
||||||
|> toThrow
|
|
||||||
});
|
|
||||||
test("order#1", () => {
|
|
||||||
let a = CDF.order({xs: [|1., 4., 8.|], ys: [|8., 9., 2.|]});
|
|
||||||
let b: DistTypes.xyShape = {xs: [|1., 4., 8.|], ys: [|8., 9., 2.|]};
|
|
||||||
expect(a) |> toEqual(b);
|
|
||||||
});
|
|
||||||
test("order#2", () => {
|
|
||||||
let a = CDF.order({xs: [|10., 5., 12.|], ys: [|8., 9., 2.|]});
|
|
||||||
let b: DistTypes.xyShape = {xs: [|5., 10., 12.|], ys: [|9., 8., 2.|]};
|
|
||||||
expect(a) |> toEqual(b);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("minX - maxX", () => {
|
|
||||||
module Dist =
|
|
||||||
CDF.Make({
|
|
||||||
let shape = CDF.order({xs: [|20., 4., 8.|], ys: [|8., 9., 2.|]});
|
|
||||||
});
|
|
||||||
test("minX", () => {
|
|
||||||
expect(Dist.minX()) |> toEqual(4.)
|
|
||||||
});
|
|
||||||
test("maxX", () => {
|
|
||||||
expect(Dist.maxX()) |> toEqual(20.)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("findY", () => {
|
|
||||||
module Dist =
|
|
||||||
CDF.Make({
|
|
||||||
let shape = CDF.order({xs: [|1., 2., 3.|], ys: [|5., 6., 7.|]});
|
|
||||||
});
|
|
||||||
test("#1", () => {
|
|
||||||
expect(Dist.findY(1.)) |> toEqual(5.)
|
|
||||||
});
|
|
||||||
test("#2", () => {
|
|
||||||
expect(Dist.findY(1.5)) |> toEqual(5.5)
|
|
||||||
});
|
|
||||||
test("#3", () => {
|
|
||||||
expect(Dist.findY(3.)) |> toEqual(7.)
|
|
||||||
});
|
|
||||||
test("#4", () => {
|
|
||||||
expect(Dist.findY(4.)) |> toEqual(7.)
|
|
||||||
});
|
|
||||||
test("#5", () => {
|
|
||||||
expect(Dist.findY(15.)) |> toEqual(7.)
|
|
||||||
});
|
|
||||||
test("#6", () => {
|
|
||||||
expect(Dist.findY(-1.)) |> toEqual(5.)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("findX", () => {
|
|
||||||
module Dist =
|
|
||||||
CDF.Make({
|
|
||||||
let shape = CDF.order({xs: [|1., 2., 3.|], ys: [|5., 6., 7.|]});
|
|
||||||
});
|
|
||||||
test("#1", () => {
|
|
||||||
expect(Dist.findX(5.)) |> toEqual(1.)
|
|
||||||
});
|
|
||||||
test("#2", () => {
|
|
||||||
expect(Dist.findX(7.)) |> toEqual(3.)
|
|
||||||
});
|
|
||||||
test("#3", () => {
|
|
||||||
expect(Dist.findX(5.5)) |> toEqual(1.5)
|
|
||||||
});
|
|
||||||
test("#4", () => {
|
|
||||||
expect(Dist.findX(8.)) |> toEqual(3.)
|
|
||||||
});
|
|
||||||
test("#5", () => {
|
|
||||||
expect(Dist.findX(4.)) |> toEqual(1.)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("convertWithAlternativeXs", () => {
|
|
||||||
open Functions;
|
|
||||||
let xs = up(1, 9);
|
|
||||||
let ys = up(20, 28);
|
|
||||||
module Dist =
|
|
||||||
CDF.Make({
|
|
||||||
let shape = CDF.order({xs, ys});
|
|
||||||
});
|
|
||||||
|
|
||||||
let xs2 = up(3, 7);
|
|
||||||
module Dist2 =
|
|
||||||
CDF.Make({
|
|
||||||
let shape = Dist.convertWithAlternativeXs(xs2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("#1", () => {
|
|
||||||
expect(Dist2.xs) |> toEqual([|3., 4., 5., 6., 7.|])
|
|
||||||
});
|
|
||||||
test("#2", () => {
|
|
||||||
expect(Dist2.ys) |> toEqual([|22., 23., 24., 25., 26.|])
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("convertToNewLength", () => {
|
|
||||||
open Functions;
|
|
||||||
let xs = up(1, 9);
|
|
||||||
let ys = up(50, 58);
|
|
||||||
module Dist =
|
|
||||||
CDF.Make({
|
|
||||||
let shape = CDF.order({xs, ys});
|
|
||||||
});
|
|
||||||
module Dist2 =
|
|
||||||
CDF.Make({
|
|
||||||
let shape = Dist.convertToNewLength(3);
|
|
||||||
});
|
|
||||||
test("#1", () => {
|
|
||||||
expect(Dist2.xs) |> toEqual([|1., 5., 9.|])
|
|
||||||
});
|
|
||||||
test("#2", () => {
|
|
||||||
expect(Dist2.ys) |> toEqual([|50., 54., 58.|])
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// @todo: Should each test expect 70.?
|
|
||||||
describe("sample", () => {
|
|
||||||
open Functions;
|
|
||||||
let xs = up(1, 9);
|
|
||||||
let ys = up(70, 78);
|
|
||||||
module Dist =
|
|
||||||
CDF.Make({
|
|
||||||
let shape = CDF.order({xs, ys});
|
|
||||||
});
|
|
||||||
|
|
||||||
let xs2 = Dist.sample(3);
|
|
||||||
test("#1", () => {
|
|
||||||
expect(xs2[0]) |> toBe(70.)
|
|
||||||
});
|
|
||||||
test("#2", () => {
|
|
||||||
expect(xs2[1]) |> toBe(70.)
|
|
||||||
});
|
|
||||||
test("#3", () => {
|
|
||||||
expect(xs2[2]) |> toBe(70.)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("integral", () => {
|
|
||||||
module Dist =
|
|
||||||
CDF.Make({
|
|
||||||
let shape =
|
|
||||||
CDF.order({xs: [|0., 1., 2., 4.|], ys: [|0.0, 1.0, 2.0, 2.0|]});
|
|
||||||
});
|
|
||||||
test("with regular inputs", () => {
|
|
||||||
expect(Dist.integral()) |> toBe(6.)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,92 +0,0 @@
|
||||||
open Jest;
|
|
||||||
open Expect;
|
|
||||||
|
|
||||||
exception ShapeWrong(string);
|
|
||||||
describe("Functions", () => {
|
|
||||||
test("interpolate", () => {
|
|
||||||
let a = Functions.interpolate(10., 20., 1., 2., 15.);
|
|
||||||
let b = 1.5;
|
|
||||||
expect(a) |> toEqual(b);
|
|
||||||
});
|
|
||||||
test("range#1", () => {
|
|
||||||
expect(Functions.range(1., 5., 3)) |> toEqual([|1., 3., 5.|])
|
|
||||||
});
|
|
||||||
test("range#2", () => {
|
|
||||||
expect(Functions.range(1., 5., 5)) |> toEqual([|1., 2., 3., 4., 5.|])
|
|
||||||
});
|
|
||||||
test("range#3", () => {
|
|
||||||
expect(Functions.range(-10., 15., 2)) |> toEqual([|(-10.), 15.|])
|
|
||||||
});
|
|
||||||
test("range#4", () => {
|
|
||||||
expect(Functions.range(-10., 15., 3)) |> toEqual([|(-10.), 2.5, 15.|])
|
|
||||||
});
|
|
||||||
test("range#5", () => {
|
|
||||||
expect(Functions.range(-10.3, 17., 3))
|
|
||||||
|> toEqual([|(-10.3), 3.3499999999999996, 17.|])
|
|
||||||
});
|
|
||||||
test("range#6", () => {
|
|
||||||
expect(Functions.range(-10.3, 17., 5))
|
|
||||||
|> toEqual([|
|
|
||||||
(-10.3),
|
|
||||||
(-3.4750000000000005),
|
|
||||||
3.3499999999999996,
|
|
||||||
10.175,
|
|
||||||
17.0,
|
|
||||||
|])
|
|
||||||
});
|
|
||||||
test("range#7", () => {
|
|
||||||
expect(Functions.range(-10.3, 17.31, 3))
|
|
||||||
|> toEqual([|(-10.3), 3.504999999999999, 17.31|])
|
|
||||||
});
|
|
||||||
test("range#8", () => {
|
|
||||||
expect(Functions.range(1., 1., 3)) |> toEqual([|1., 1., 1.|])
|
|
||||||
});
|
|
||||||
test("mean#1", () => {
|
|
||||||
expect(Functions.mean([|1., 2., 3.|])) |> toEqual(2.)
|
|
||||||
});
|
|
||||||
test("mean#2", () => {
|
|
||||||
expect(Functions.mean([|1., 2., 3., (-2.)|])) |> toEqual(1.)
|
|
||||||
});
|
|
||||||
test("mean#3", () => {
|
|
||||||
expect(Functions.mean([|1., 2., 3., (-2.), (-10.)|])) |> toEqual(-1.2)
|
|
||||||
});
|
|
||||||
test("min#1", () => {
|
|
||||||
expect(Functions.min([|1., 2., 3.|])) |> toEqual(1.)
|
|
||||||
});
|
|
||||||
test("min#2", () => {
|
|
||||||
expect(Functions.min([|(-1.), (-2.), 0., 20.|])) |> toEqual(-2.)
|
|
||||||
});
|
|
||||||
test("min#3", () => {
|
|
||||||
expect(Functions.min([|(-1.), (-2.), 0., 20., (-2.2)|]))
|
|
||||||
|> toEqual(-2.2)
|
|
||||||
});
|
|
||||||
test("max#1", () => {
|
|
||||||
expect(Functions.max([|1., 2., 3.|])) |> toEqual(3.)
|
|
||||||
});
|
|
||||||
test("max#2", () => {
|
|
||||||
expect(Functions.max([|(-1.), (-2.), 0., 20.|])) |> toEqual(20.)
|
|
||||||
});
|
|
||||||
test("max#3", () => {
|
|
||||||
expect(Functions.max([|(-1.), (-2.), 0., (-2.2)|])) |> toEqual(0.)
|
|
||||||
});
|
|
||||||
test("random#1", () => {
|
|
||||||
expect(Functions.random(1, 5)) |> toBeLessThanOrEqual(5)
|
|
||||||
});
|
|
||||||
test("random#2", () => {
|
|
||||||
expect(Functions.random(1, 5)) |> toBeGreaterThanOrEqual(1)
|
|
||||||
});
|
|
||||||
test("up#1", () => {
|
|
||||||
expect(Functions.up(1, 5)) |> toEqual([|1., 2., 3., 4., 5.|])
|
|
||||||
});
|
|
||||||
test("up#2", () => {
|
|
||||||
expect(Functions.up(-1, 5))
|
|
||||||
|> toEqual([|(-1.), 0., 1., 2., 3., 4., 5.|])
|
|
||||||
});
|
|
||||||
test("down#1", () => {
|
|
||||||
expect(Functions.down(5, 1)) |> toEqual([|5., 4., 3., 2., 1.|])
|
|
||||||
});
|
|
||||||
test("down#2", () => {
|
|
||||||
expect(Functions.down(5, -1))
|
|
||||||
|> toEqual([|5., 4., 3., 2., 1., 0., (-1.)|])
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -43,7 +43,7 @@ describe("XYShapes", () => {
|
||||||
describe("transverse", () => {
|
describe("transverse", () => {
|
||||||
makeTest(
|
makeTest(
|
||||||
"When very different",
|
"When very different",
|
||||||
XYShape.T._transverse2(
|
XYShape.T.Transversal._transverse(
|
||||||
(aCurrent, aLast) => aCurrent +. aLast,
|
(aCurrent, aLast) => aCurrent +. aLast,
|
||||||
[|1.0, 2.0, 3.0, 4.0|],
|
[|1.0, 2.0, 3.0, 4.0|],
|
||||||
),
|
),
|
||||||
|
|
|
@ -91,7 +91,7 @@ module T = {
|
||||||
// todo: Figure out some way of doing this without having to integrate so many times.
|
// todo: Figure out some way of doing this without having to integrate so many times.
|
||||||
let toShape = (~samples: t, ~outputXYPoints=3000, ~kernelWidth=10, ()) => {
|
let toShape = (~samples: t, ~outputXYPoints=3000, ~kernelWidth=10, ()) => {
|
||||||
Array.fast_sort(compare, samples);
|
Array.fast_sort(compare, samples);
|
||||||
let (continuousPart, discretePart) = E.A.Floats.split(samples);
|
let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples);
|
||||||
let length = samples |> E.A.length;
|
let length = samples |> E.A.length;
|
||||||
let lengthFloat = float_of_int(length);
|
let lengthFloat = float_of_int(length);
|
||||||
let discrete: DistTypes.xyShape =
|
let discrete: DistTypes.xyShape =
|
||||||
|
|
|
@ -87,7 +87,7 @@ module Continuous = {
|
||||||
interpolation,
|
interpolation,
|
||||||
};
|
};
|
||||||
let lastY = (t: t) =>
|
let lastY = (t: t) =>
|
||||||
t |> xyShape |> XYShape.T.unsafeLast |> (((_, y)) => y);
|
t |> xyShape |> XYShape.T.Pairs.unsafeLast |> (((_, y)) => y);
|
||||||
let oShapeMap =
|
let oShapeMap =
|
||||||
(fn, {xyShape, interpolation}: t): option(DistTypes.continuousShape) =>
|
(fn, {xyShape, interpolation}: t): option(DistTypes.continuousShape) =>
|
||||||
fn(xyShape) |> E.O.fmap(make(_, interpolation));
|
fn(xyShape) |> E.O.fmap(make(_, interpolation));
|
||||||
|
@ -146,16 +146,16 @@ module Continuous = {
|
||||||
let truncate = (~cache=None, i, t) =>
|
let truncate = (~cache=None, i, t) =>
|
||||||
t
|
t
|
||||||
|> shapeMap(
|
|> shapeMap(
|
||||||
XYShape.T.convertToNewLengthByProbabilityMass(
|
XYShape.T.XsConversion.proportionByProbabilityMass(
|
||||||
i,
|
i,
|
||||||
integral(~cache, t).xyShape,
|
integral(~cache, t).xyShape,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
let integralEndY = (~cache, t) => t |> integral(~cache) |> lastY;
|
let integralEndY = (~cache, t) => t |> integral(~cache) |> lastY;
|
||||||
let integralXtoY = (~cache, f, t) =>
|
let integralXtoY = (~cache, f, t) =>
|
||||||
t |> integral(~cache) |> shapeFn(XYShape.T.findY(f));
|
t |> integral(~cache) |> shapeFn(XYShape.T.XtoY.linear(f));
|
||||||
let integralYtoX = (~cache, f, t) =>
|
let integralYtoX = (~cache, f, t) =>
|
||||||
t |> integral(~cache) |> shapeFn(XYShape.T.findX(f));
|
t |> integral(~cache) |> shapeFn(XYShape.T.YtoX.linear(f));
|
||||||
let toContinuous = t => Some(t);
|
let toContinuous = t => Some(t);
|
||||||
let toDiscrete = _ => None;
|
let toDiscrete = _ => None;
|
||||||
let toScaledContinuous = t => Some(t);
|
let toScaledContinuous = t => Some(t);
|
||||||
|
@ -208,10 +208,16 @@ module Discrete = {
|
||||||
};
|
};
|
||||||
|
|
||||||
let integralXtoY = (~cache, f, t) =>
|
let integralXtoY = (~cache, f, t) =>
|
||||||
t |> integral(~cache) |> Continuous.getShape |> XYShape.T.findY(f);
|
t
|
||||||
|
|> integral(~cache)
|
||||||
|
|> Continuous.getShape
|
||||||
|
|> XYShape.T.XtoY.linear(f);
|
||||||
|
|
||||||
let integralYtoX = (~cache, f, t) =>
|
let integralYtoX = (~cache, f, t) =>
|
||||||
t |> integral(~cache) |> Continuous.getShape |> XYShape.T.findX(f);
|
t
|
||||||
|
|> integral(~cache)
|
||||||
|
|> Continuous.getShape
|
||||||
|
|> XYShape.T.YtoX.linear(f);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -357,11 +363,17 @@ module Mixed = {
|
||||||
};
|
};
|
||||||
|
|
||||||
let integralXtoY = (~cache, f, t) => {
|
let integralXtoY = (~cache, f, t) => {
|
||||||
t |> integral(~cache) |> Continuous.getShape |> XYShape.T.findY(f);
|
t
|
||||||
|
|> integral(~cache)
|
||||||
|
|> Continuous.getShape
|
||||||
|
|> XYShape.T.XtoY.linear(f);
|
||||||
};
|
};
|
||||||
|
|
||||||
let integralYtoX = (~cache, f, t) => {
|
let integralYtoX = (~cache, f, t) => {
|
||||||
t |> integral(~cache) |> Continuous.getShape |> XYShape.T.findX(f);
|
t
|
||||||
|
|> integral(~cache)
|
||||||
|
|> Continuous.getShape
|
||||||
|
|> XYShape.T.YtoX.linear(f);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: This functionality is kinda weird, because it seems to assume the cdf adds to 1.0 elsewhere, which wouldn't happen here.
|
// TODO: This functionality is kinda weird, because it seems to assume the cdf adds to 1.0 elsewhere, which wouldn't happen here.
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
open DistTypes;
|
open DistTypes;
|
||||||
|
|
||||||
|
let interpolate =
|
||||||
|
(xMin: float, xMax: float, yMin: float, yMax: float, xIntended: float)
|
||||||
|
: float => {
|
||||||
|
let minProportion = (xMax -. xIntended) /. (xMax -. xMin);
|
||||||
|
let maxProportion = (xIntended -. xMin) /. (xMax -. xMin);
|
||||||
|
yMin *. minProportion +. yMax *. maxProportion;
|
||||||
|
};
|
||||||
|
|
||||||
module T = {
|
module T = {
|
||||||
type t = xyShape;
|
type t = xyShape;
|
||||||
type ts = array(xyShape);
|
type ts = array(xyShape);
|
||||||
|
@ -9,134 +17,157 @@ module T = {
|
||||||
};
|
};
|
||||||
let xs = (t: t) => t.xs;
|
let xs = (t: t) => t.xs;
|
||||||
let ys = (t: t) => t.ys;
|
let ys = (t: t) => t.ys;
|
||||||
let minX = (t: t) => t |> xs |> E.A.first;
|
let minX = (t: t) => t |> xs |> E.A.Sorted.min;
|
||||||
let maxX = (t: t) => t |> xs |> E.A.last;
|
let maxX = (t: t) => t |> xs |> E.A.Sorted.max;
|
||||||
let minY = (t: t) => t |> ys |> E.A.first;
|
let minY = (t: t) => t |> ys |> E.A.Sorted.min;
|
||||||
let maxY = (t: t) => t |> ys |> E.A.last;
|
let maxY = (t: t) => t |> ys |> E.A.Sorted.max;
|
||||||
let xTotalRange = (t: t) =>
|
let xTotalRange = (t: t) => t |> xs |> E.A.Sorted.range;
|
||||||
switch (minX(t), maxX(t)) {
|
|
||||||
| (Some(min), Some(max)) => Some(max -. min)
|
|
||||||
| _ => None
|
|
||||||
};
|
|
||||||
let first = ({xs, ys}: t) =>
|
|
||||||
switch (xs |> E.A.first, ys |> E.A.first) {
|
|
||||||
| (Some(x), Some(y)) => Some((x, y))
|
|
||||||
| _ => None
|
|
||||||
};
|
|
||||||
let last = ({xs, ys}: t) =>
|
|
||||||
switch (xs |> E.A.last, ys |> E.A.last) {
|
|
||||||
| (Some(x), Some(y)) => Some((x, y))
|
|
||||||
| _ => None
|
|
||||||
};
|
|
||||||
|
|
||||||
let unsafeFirst = (t: t) => first(t) |> E.O.toExn("Unsafe operation");
|
|
||||||
let unsafeLast = (t: t) => last(t) |> E.O.toExn("Unsafe operation");
|
|
||||||
|
|
||||||
let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys);
|
let zip = ({xs, ys}: t) => Belt.Array.zip(xs, ys);
|
||||||
let getBy = (t: t, fn) => t |> zip |> Belt.Array.getBy(_, fn);
|
|
||||||
|
|
||||||
let firstPairAtOrBeforeValue = (xValue, t: t) => {
|
|
||||||
let zipped = zip(t);
|
|
||||||
let firstIndex =
|
|
||||||
zipped |> Belt.Array.getIndexBy(_, ((x, y)) => x > xValue);
|
|
||||||
let previousIndex =
|
|
||||||
switch (firstIndex) {
|
|
||||||
| None => Some(Array.length(zipped) - 1)
|
|
||||||
| Some(0) => None
|
|
||||||
| Some(n) => Some(n - 1)
|
|
||||||
};
|
|
||||||
previousIndex |> Belt.Option.flatMap(_, Belt.Array.get(zipped));
|
|
||||||
};
|
|
||||||
|
|
||||||
let findY = (x: float, t: t): float => {
|
|
||||||
let firstHigherIndex =
|
|
||||||
E.A.Sorted.binarySearchFirstElementGreaterIndex(xs(t), x);
|
|
||||||
let n =
|
|
||||||
switch (firstHigherIndex) {
|
|
||||||
| `overMax => maxY(t) |> E.O.default(0.0)
|
|
||||||
| `underMin => minY(t) |> E.O.default(0.0)
|
|
||||||
| `firstHigher(firstHigherIndex) =>
|
|
||||||
let lowerOrEqualIndex =
|
|
||||||
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
|
||||||
let needsInterpolation = xs(t)[lowerOrEqualIndex] != x;
|
|
||||||
if (needsInterpolation) {
|
|
||||||
Functions.interpolate(
|
|
||||||
xs(t)[lowerOrEqualIndex],
|
|
||||||
xs(t)[firstHigherIndex],
|
|
||||||
ys(t)[lowerOrEqualIndex],
|
|
||||||
ys(t)[firstHigherIndex],
|
|
||||||
x,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ys(t)[lowerOrEqualIndex];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
n;
|
|
||||||
};
|
|
||||||
|
|
||||||
let findX = (y: float, t: t): float => {
|
|
||||||
let firstHigherIndex =
|
|
||||||
E.A.Sorted.binarySearchFirstElementGreaterIndex(ys(t), y);
|
|
||||||
let foundX =
|
|
||||||
switch (firstHigherIndex) {
|
|
||||||
| `overMax => maxX(t) |> E.O.default(0.0)
|
|
||||||
| `underMin => minX(t) |> E.O.default(0.0)
|
|
||||||
| `firstHigher(firstHigherIndex) =>
|
|
||||||
let lowerOrEqualIndex =
|
|
||||||
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
|
||||||
let needsInterpolation = ys(t)[lowerOrEqualIndex] != y;
|
|
||||||
if (needsInterpolation) {
|
|
||||||
Functions.interpolate(
|
|
||||||
ys(t)[lowerOrEqualIndex],
|
|
||||||
ys(t)[firstHigherIndex],
|
|
||||||
xs(t)[lowerOrEqualIndex],
|
|
||||||
xs(t)[firstHigherIndex],
|
|
||||||
y,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
xs(t)[lowerOrEqualIndex];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
foundX;
|
|
||||||
};
|
|
||||||
|
|
||||||
let convertWithAlternativeXs = (newXs: array(float), t: t): t => {
|
|
||||||
let newYs = Belt.Array.map(newXs, f => findY(f, t));
|
|
||||||
{xs: newXs, ys: newYs};
|
|
||||||
};
|
|
||||||
|
|
||||||
let convertToNewLength = (newLength: int, t: t): DistTypes.xyShape => {
|
|
||||||
Functions.(
|
|
||||||
range(min(xs(t)), max(xs(t)), newLength)
|
|
||||||
|> convertWithAlternativeXs(_, t)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
let convertToNewLengthByProbabilityMass =
|
|
||||||
(newLength: int, integral: t, t: t): DistTypes.xyShape => {
|
|
||||||
Functions.range(0.0, 1.0, newLength)
|
|
||||||
|> E.A.fmap(findX(_, integral))
|
|
||||||
|> convertWithAlternativeXs(_, t);
|
|
||||||
};
|
|
||||||
|
|
||||||
module XtoY = {
|
|
||||||
let stepwiseIncremental = (f, t: t) =>
|
|
||||||
firstPairAtOrBeforeValue(f, t) |> E.O.fmap(((_, y)) => y);
|
|
||||||
|
|
||||||
let stepwiseIfAtX = (f: float, t: t) => {
|
|
||||||
getBy(t, ((x: float, _)) => {x == f}) |> E.O.fmap(((_, y)) => y);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: When Roman's PR comes in, fix this bit. This depends on interpolation, obviously.
|
|
||||||
let linear = (f, t: t) => t |> findY(f);
|
|
||||||
};
|
|
||||||
|
|
||||||
let pointwiseMap = (fn, t: t): t => {xs: t.xs, ys: t.ys |> E.A.fmap(fn)};
|
let pointwiseMap = (fn, t: t): t => {xs: t.xs, ys: t.ys |> E.A.fmap(fn)};
|
||||||
let xMap = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys};
|
let xMap = (fn, t: t): t => {xs: E.A.fmap(fn, t.xs), ys: t.ys};
|
||||||
let fromArray = ((xs, ys)): t => {xs, ys};
|
let fromArray = ((xs, ys)): t => {xs, ys};
|
||||||
let fromArrays = (xs, ys): t => {xs, ys};
|
let fromArrays = (xs, ys): t => {xs, ys};
|
||||||
let fromZippedArray = (is: array((float, float))): t =>
|
let fromZippedArray = (is: array((float, float))): t =>
|
||||||
is |> Belt.Array.unzip |> fromArray;
|
is |> Belt.Array.unzip |> fromArray;
|
||||||
|
let equallyDividedXs = (t: t, newLength) => {
|
||||||
|
E.A.Floats.range(
|
||||||
|
minX(t) |> E.O.toExt("Unsafe"),
|
||||||
|
maxX(t) |> E.O.toExt("Unsafe"),
|
||||||
|
newLength,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
module Ts = {
|
||||||
|
type t = ts;
|
||||||
|
let minX = (t: t) =>
|
||||||
|
t
|
||||||
|
|> E.A.fmap(minX)
|
||||||
|
|> E.A.O.concatSomes
|
||||||
|
|> E.A.min
|
||||||
|
|> E.O.toExt("Unsafe");
|
||||||
|
let maxX = (t: t) =>
|
||||||
|
t
|
||||||
|
|> E.A.fmap(maxX)
|
||||||
|
|> E.A.O.concatSomes
|
||||||
|
|> E.A.max
|
||||||
|
|> E.O.toExt("Unsafe");
|
||||||
|
let equallyDividedXs = (t: t, newLength) => {
|
||||||
|
E.A.Floats.range(minX(t), maxX(t), newLength);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module Pairs = {
|
||||||
|
let first = (t: t) =>
|
||||||
|
switch (minX(t), minY(t)) {
|
||||||
|
| (Some(x), Some(y)) => Some((x, y))
|
||||||
|
| _ => None
|
||||||
|
};
|
||||||
|
let last = (t: t) =>
|
||||||
|
switch (maxX(t), maxY(t)) {
|
||||||
|
| (Some(x), Some(y)) => Some((x, y))
|
||||||
|
| _ => None
|
||||||
|
};
|
||||||
|
let unsafeFirst = (t: t) => first(t) |> E.O.toExn("Unsafe operation");
|
||||||
|
let unsafeLast = (t: t) => last(t) |> E.O.toExn("Unsafe operation");
|
||||||
|
|
||||||
|
let getBy = (t: t, fn) => t |> zip |> Belt.Array.getBy(_, fn);
|
||||||
|
|
||||||
|
let firstAtOrBeforeXValue = (xValue, t: t) => {
|
||||||
|
let zipped = zip(t);
|
||||||
|
let firstIndex =
|
||||||
|
zipped |> Belt.Array.getIndexBy(_, ((x, _)) => x > xValue);
|
||||||
|
let previousIndex =
|
||||||
|
switch (firstIndex) {
|
||||||
|
| None => Some(Array.length(zipped) - 1)
|
||||||
|
| Some(0) => None
|
||||||
|
| Some(n) => Some(n - 1)
|
||||||
|
};
|
||||||
|
previousIndex |> Belt.Option.flatMap(_, Belt.Array.get(zipped));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module YtoX = {
|
||||||
|
let linear = (y: float, t: t): float => {
|
||||||
|
let firstHigherIndex =
|
||||||
|
E.A.Sorted.binarySearchFirstElementGreaterIndex(ys(t), y);
|
||||||
|
let foundX =
|
||||||
|
switch (firstHigherIndex) {
|
||||||
|
| `overMax => maxX(t) |> E.O.default(0.0)
|
||||||
|
| `underMin => minX(t) |> E.O.default(0.0)
|
||||||
|
| `firstHigher(firstHigherIndex) =>
|
||||||
|
let lowerOrEqualIndex =
|
||||||
|
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
||||||
|
let (_xs, _ys) = (xs(t), ys(t));
|
||||||
|
let needsInterpolation = _ys[lowerOrEqualIndex] != y;
|
||||||
|
if (needsInterpolation) {
|
||||||
|
interpolate(
|
||||||
|
_ys[lowerOrEqualIndex],
|
||||||
|
_ys[firstHigherIndex],
|
||||||
|
_xs[lowerOrEqualIndex],
|
||||||
|
_xs[firstHigherIndex],
|
||||||
|
y,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_xs[lowerOrEqualIndex];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
foundX;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module XtoY = {
|
||||||
|
let stepwiseIncremental = (f, t: t) =>
|
||||||
|
Pairs.firstAtOrBeforeXValue(f, t) |> E.O.fmap(((_, y)) => y);
|
||||||
|
|
||||||
|
let stepwiseIfAtX = (f: float, t: t) => {
|
||||||
|
Pairs.getBy(t, ((x: float, _)) => {x == f})
|
||||||
|
|> E.O.fmap(((_, y)) => y);
|
||||||
|
};
|
||||||
|
|
||||||
|
let linear = (x: float, t: t): float => {
|
||||||
|
let firstHigherIndex =
|
||||||
|
E.A.Sorted.binarySearchFirstElementGreaterIndex(xs(t), x);
|
||||||
|
let n =
|
||||||
|
switch (firstHigherIndex) {
|
||||||
|
| `overMax => maxY(t) |> E.O.default(0.0)
|
||||||
|
| `underMin => minY(t) |> E.O.default(0.0)
|
||||||
|
| `firstHigher(firstHigherIndex) =>
|
||||||
|
let lowerOrEqualIndex =
|
||||||
|
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
||||||
|
let (_xs, _ys) = (xs(t), ys(t));
|
||||||
|
let needsInterpolation = _xs[lowerOrEqualIndex] != x;
|
||||||
|
if (needsInterpolation) {
|
||||||
|
interpolate(
|
||||||
|
_xs[lowerOrEqualIndex],
|
||||||
|
_xs[firstHigherIndex],
|
||||||
|
_ys[lowerOrEqualIndex],
|
||||||
|
_ys[firstHigherIndex],
|
||||||
|
x,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_ys[lowerOrEqualIndex];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
n;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module XsConversion = {
|
||||||
|
let replaceWithXs = (newXs: array(float), t: t): t => {
|
||||||
|
let newYs = Belt.Array.map(newXs, f => XtoY.linear(f, t));
|
||||||
|
{xs: newXs, ys: newYs};
|
||||||
|
};
|
||||||
|
|
||||||
|
let proportionEquallyOverX = (newLength: int, t: t): t => {
|
||||||
|
equallyDividedXs(t, newLength) |> replaceWithXs(_, t);
|
||||||
|
};
|
||||||
|
|
||||||
|
let proportionByProbabilityMass = (newLength: int, integral: t, t: t): t => {
|
||||||
|
E.A.Floats.range(0.0, 1.0, newLength)
|
||||||
|
|> E.A.fmap(YtoX.linear(_, integral))
|
||||||
|
|> replaceWithXs(_, t);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
module Zipped = {
|
module Zipped = {
|
||||||
type zipped = array((float, float));
|
type zipped = array((float, float));
|
||||||
|
@ -146,138 +177,57 @@ module T = {
|
||||||
t |> E.A.stableSortBy(_, ((x1, _), (x2, _)) => x1 > x2 ? 1 : 0);
|
t |> E.A.stableSortBy(_, ((x1, _), (x2, _)) => x1 > x2 ? 1 : 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Use faster sort at least, geese.
|
|
||||||
module Combine = {
|
module Combine = {
|
||||||
let combineLinear = (t1: t, t2: t, fn: (float, float) => float) => {
|
let _allXs = (t1: t, t2: t) => E.A.Sorted.concat(xs(t1), xs(t2));
|
||||||
let allXs = Belt.Array.concat(xs(t1), xs(t2));
|
|
||||||
allXs |> Array.fast_sort(compare);
|
let _combineAbstract = (comboAlg, t1: t, t2: t, fn) => {
|
||||||
|
let allXs = _allXs(t1, t2);
|
||||||
let allYs =
|
let allYs =
|
||||||
allXs
|
allXs
|
||||||
|> E.A.fmap(x => {
|
|> E.A.fmap(x => {
|
||||||
let y1 = XtoY.linear(x, t1);
|
let y1 = comboAlg(x, t1);
|
||||||
let y2 = XtoY.linear(x, t2);
|
let y2 = comboAlg(x, t2);
|
||||||
fn(y1, y2);
|
fn(y1, y2);
|
||||||
});
|
});
|
||||||
fromArrays(allXs, allYs);
|
fromArrays(allXs, allYs);
|
||||||
};
|
};
|
||||||
|
|
||||||
let combineStepwise =
|
let combineLinear = _combineAbstract(XtoY.linear);
|
||||||
(t1: t, t2: t, fn: (option(float), option(float)) => float) => {
|
let combineStepwise = _combineAbstract(XtoY.stepwiseIncremental);
|
||||||
let allXs = Belt.Array.concat(xs(t1), xs(t2));
|
let combineIfAtX = _combineAbstract(XtoY.stepwiseIfAtX);
|
||||||
allXs |> Array.fast_sort(compare);
|
|
||||||
let allYs =
|
|
||||||
allXs
|
|
||||||
|> E.A.fmap(x => {
|
|
||||||
let y1 = XtoY.stepwiseIncremental(x, t1);
|
|
||||||
let y2 = XtoY.stepwiseIncremental(x, t2);
|
|
||||||
fn(y1, y2);
|
|
||||||
});
|
|
||||||
fromArrays(allXs, allYs);
|
|
||||||
};
|
|
||||||
|
|
||||||
let combineIfAtX =
|
// TODO: I'd bet this is pretty slow
|
||||||
(t1: t, t2: t, fn: (option(float), option(float)) => float) => {
|
let intersperse = (t1: t, t2: t) => {
|
||||||
let allXs = Belt.Array.concat(xs(t1), xs(t2));
|
E.A.intersperse(zip(t1), zip(t2)) |> fromZippedArray;
|
||||||
allXs |> Array.fast_sort(compare);
|
|
||||||
let allYs =
|
|
||||||
allXs
|
|
||||||
|> E.A.fmap(x => {
|
|
||||||
let y1 = XtoY.stepwiseIfAtX(x, t1);
|
|
||||||
let y2 = XtoY.stepwiseIfAtX(x, t2);
|
|
||||||
fn(y1, y2);
|
|
||||||
});
|
|
||||||
fromArrays(allXs, allYs);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// todo: maybe not needed?
|
module Transversal = {
|
||||||
// let comparePoint = (a: float, b: float) => a > b ? 1 : (-1);
|
let _transverse = (fn, items) => {
|
||||||
|
let length = items |> E.A.length;
|
||||||
let comparePoints = ((x1: float, y1: float), (x2: float, y2: float)) =>
|
let empty = Belt.Array.make(length, items |> E.A.unsafe_get(_, 0));
|
||||||
switch (x1 == x2, y1 == y2) {
|
Belt.Array.forEachWithIndex(
|
||||||
| (false, _) => compare(x1, x2)
|
items,
|
||||||
| (true, false) => compare(y1, y2)
|
(index, element) => {
|
||||||
| (true, true) => (-1)
|
let item =
|
||||||
|
switch (index) {
|
||||||
|
| 0 => element
|
||||||
|
| index => fn(element, E.A.unsafe_get(empty, index - 1))
|
||||||
|
};
|
||||||
|
let _ = Belt.Array.set(empty, index, item);
|
||||||
|
();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
empty;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: I'd bet this is pretty slow
|
let _transverseShape = (fn, p: t) => {
|
||||||
let intersperce = (t1: t, t2: t) => {
|
fromArray((p.xs, _transverse(fn, p.ys)));
|
||||||
let items: ref(array((float, float))) = ref([||]);
|
};
|
||||||
let t1 = zip(t1);
|
|
||||||
let t2 = zip(t2);
|
|
||||||
|
|
||||||
Belt.Array.forEachWithIndex(t1, (i, item) => {
|
|
||||||
switch (Belt.Array.get(t2, i)) {
|
|
||||||
| Some(r) => items := E.A.append(items^, [|item, r|])
|
|
||||||
| None => items := E.A.append(items^, [|item|])
|
|
||||||
}
|
|
||||||
});
|
|
||||||
items^ |> Belt.Array.unzip |> fromArray;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let yFold = (fn, t: t) => {
|
let accumulateYs =
|
||||||
E.A.fold_left(fn, 0., t.ys);
|
Transversal._transverseShape((aCurrent, aLast) => aCurrent +. aLast);
|
||||||
};
|
|
||||||
|
|
||||||
let ySum = yFold((a, b) => a +. b);
|
|
||||||
|
|
||||||
let _transverseSimple = fn =>
|
|
||||||
Belt.Array.reduce(_, [||], (items, y) =>
|
|
||||||
switch (E.A.last(items)) {
|
|
||||||
| Some(yLast) => Belt.Array.concat(items, [|fn(y, yLast)|])
|
|
||||||
| None => [|y|]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let _transverse2 = (fn, items) => {
|
|
||||||
let length = items |> E.A.length;
|
|
||||||
let empty = Belt.Array.make(length, items |> E.A.unsafe_get(_, 0));
|
|
||||||
Belt.Array.forEachWithIndex(
|
|
||||||
items,
|
|
||||||
(index, element) => {
|
|
||||||
let item =
|
|
||||||
switch (index) {
|
|
||||||
| 0 => element
|
|
||||||
| index => fn(element, E.A.unsafe_get(empty, index - 1))
|
|
||||||
};
|
|
||||||
let _ = Belt.Array.set(empty, index, item);
|
|
||||||
();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
empty;
|
|
||||||
};
|
|
||||||
|
|
||||||
let _transverseB = (fn, items) => {
|
|
||||||
let (xs, ys) = items |> Belt.Array.unzip;
|
|
||||||
let newYs = _transverse2(fn, ys);
|
|
||||||
Belt.Array.zip(xs, newYs);
|
|
||||||
};
|
|
||||||
|
|
||||||
let _transverse = fn =>
|
|
||||||
Belt.Array.reduce(_, [||], (items, (x, y)) =>
|
|
||||||
switch (E.A.last(items)) {
|
|
||||||
| Some((_, yLast)) =>
|
|
||||||
Belt.Array.concat(items, [|(x, fn(y, yLast))|])
|
|
||||||
| None => [|(x, y)|]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let _transverseShape2 = (fn, p: t) => {
|
|
||||||
Belt.Array.zip(p.xs, p.ys)
|
|
||||||
|> _transverseB(fn)
|
|
||||||
|> Belt.Array.unzip
|
|
||||||
|> fromArray;
|
|
||||||
};
|
|
||||||
|
|
||||||
let _transverseShape = (fn, p: t) => {
|
|
||||||
fromArray((p.xs, _transverse2(fn, p.ys)));
|
|
||||||
};
|
|
||||||
|
|
||||||
let filter = (fn, t: t) =>
|
|
||||||
t |> zip |> E.A.filter(fn) |> Belt.Array.unzip |> fromArray;
|
|
||||||
|
|
||||||
let accumulateYs = _transverseShape((aCurrent, aLast) => aCurrent +. aLast);
|
|
||||||
let subtractYs = _transverseShape((aCurrent, aLast) => aCurrent -. aLast);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// I'm really not sure this part is actually what we want at this point.
|
// I'm really not sure this part is actually what we want at this point.
|
||||||
|
@ -286,7 +236,7 @@ module Range = {
|
||||||
type zippedRange = ((float, float), (float, float));
|
type zippedRange = ((float, float), (float, float));
|
||||||
|
|
||||||
let floatSum = Belt.Array.reduce(_, 0., (a, b) => a +. b);
|
let floatSum = Belt.Array.reduce(_, 0., (a, b) => a +. b);
|
||||||
let toT = r => r |> Belt.Array.unzip |> T.fromArray;
|
let toT = T.fromZippedArray;
|
||||||
let nextX = ((_, (nextX, _)): zippedRange) => nextX;
|
let nextX = ((_, (nextX, _)): zippedRange) => nextX;
|
||||||
|
|
||||||
let rangePointAssumingSteps =
|
let rangePointAssumingSteps =
|
||||||
|
@ -320,6 +270,7 @@ module Range = {
|
||||||
let integrateWithTriangles = ({xs, ys}) => {
|
let integrateWithTriangles = ({xs, ys}) => {
|
||||||
let length = E.A.length(xs);
|
let length = E.A.length(xs);
|
||||||
let cumulativeY = Belt.Array.make(length, 0.0);
|
let cumulativeY = Belt.Array.make(length, 0.0);
|
||||||
|
//TODO: I don't think this next line is needed, but definitely check.
|
||||||
let _ = Belt.Array.set(cumulativeY, 0, 0.0);
|
let _ = Belt.Array.set(cumulativeY, 0, 0.0);
|
||||||
for (x in 0 to E.A.length(xs) - 2) {
|
for (x in 0 to E.A.length(xs) - 2) {
|
||||||
Belt.Array.set(
|
Belt.Array.set(
|
||||||
|
@ -343,41 +294,24 @@ module Range = {
|
||||||
Some(
|
Some(
|
||||||
items
|
items
|
||||||
|> Belt.Array.map(_, rangePointAssumingSteps)
|
|> Belt.Array.map(_, rangePointAssumingSteps)
|
||||||
|> Belt.Array.unzip
|
|> T.fromZippedArray
|
||||||
|> T.fromArray
|
|> T.Combine.intersperse(t |> T.xMap(e => e +. diff)),
|
||||||
|> T.intersperce(t |> T.xMap(e => e +. diff)),
|
|
||||||
)
|
)
|
||||||
| _ => Some(t)
|
| _ => Some(t)
|
||||||
};
|
};
|
||||||
let bar = items |> E.O.fmap(T.zip) |> E.O.bind(_, E.A.get(_, 0));
|
let first = items |> E.O.fmap(T.zip) |> E.O.bind(_, E.A.get(_, 0));
|
||||||
let items =
|
switch (items, first) {
|
||||||
switch (items, bar) {
|
| (Some(items), Some((0.0, _))) => Some(items)
|
||||||
| (Some(items), Some((0.0, _))) => Some(items)
|
| (Some(items), Some((firstX, _))) =>
|
||||||
| (Some(items), Some((firstX, _))) =>
|
let all = E.A.append([|(firstX, 0.0)|], items |> T.zip);
|
||||||
let all = E.A.append([|(firstX, 0.0)|], items |> T.zip);
|
all |> T.fromZippedArray |> E.O.some;
|
||||||
let foo = all |> Belt.Array.unzip |> T.fromArray;
|
| _ => None
|
||||||
Some(foo);
|
};
|
||||||
| _ => None
|
|
||||||
};
|
|
||||||
items;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module Ts = {
|
|
||||||
type t = T.ts;
|
|
||||||
let minX = (t: t) =>
|
|
||||||
t |> E.A.fmap(T.minX) |> E.A.O.concatSomes |> Functions.min;
|
|
||||||
let maxX = (t: t) =>
|
|
||||||
t |> E.A.fmap(T.maxX) |> E.A.O.concatSomes |> Functions.max;
|
|
||||||
|
|
||||||
// TODO/Warning: This will break if the shapes are empty.
|
|
||||||
let equallyDividedXs = (t: t, newLength) => {
|
|
||||||
Functions.range(minX(t), maxX(t), newLength);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let combinePointwise = (fn, sampleCount, t1: xyShape, t2: xyShape) => {
|
let combinePointwise = (fn, sampleCount, t1: xyShape, t2: xyShape) => {
|
||||||
let xs = Ts.equallyDividedXs([|t1, t2|], sampleCount);
|
let xs = T.Ts.equallyDividedXs([|t1, t2|], sampleCount);
|
||||||
let ys =
|
let ys =
|
||||||
xs |> E.A.fmap(x => fn(T.XtoY.linear(x, t1), T.XtoY.linear(x, t2)));
|
xs |> E.A.fmap(x => fn(T.XtoY.linear(x, t1), T.XtoY.linear(x, t2)));
|
||||||
T.fromArrays(xs, ys);
|
T.fromArrays(xs, ys);
|
||||||
|
@ -396,5 +330,5 @@ let logScorePoint = (sampleCount, t1, t2) =>
|
||||||
logScoreDist(sampleCount, t1, t2)
|
logScoreDist(sampleCount, t1, t2)
|
||||||
|> Range.integrateWithTriangles
|
|> Range.integrateWithTriangles
|
||||||
|> E.O.fmap(T.accumulateYs)
|
|> E.O.fmap(T.accumulateYs)
|
||||||
|> E.O.bind(_, T.last)
|
|> E.O.bind(_, T.Pairs.last)
|
||||||
|> E.O.fmap(((_, y)) => y);
|
|> E.O.fmap(((_, y)) => y);
|
|
@ -94,7 +94,7 @@ module Lognormal = {
|
||||||
let from90PercentCI = (low, high) => {
|
let from90PercentCI = (low, high) => {
|
||||||
let logLow = Js.Math.log(low);
|
let logLow = Js.Math.log(low);
|
||||||
let logHigh = Js.Math.log(high);
|
let logHigh = Js.Math.log(high);
|
||||||
let mu = Functions.mean([|logLow, logHigh|]);
|
let mu = E.A.Floats.mean([|logLow, logHigh|]);
|
||||||
let sigma = (logHigh -. logLow) /. (2.0 *. 1.645);
|
let sigma = (logHigh -. logLow) /. (2.0 *. 1.645);
|
||||||
`Lognormal({mu, sigma});
|
`Lognormal({mu, sigma});
|
||||||
};
|
};
|
||||||
|
@ -189,9 +189,9 @@ module GenericSimple = {
|
||||||
let interpolateXs =
|
let interpolateXs =
|
||||||
(~xSelection: [ | `Linear | `ByWeight]=`Linear, dist: dist, sampleCount) => {
|
(~xSelection: [ | `Linear | `ByWeight]=`Linear, dist: dist, sampleCount) => {
|
||||||
switch (xSelection) {
|
switch (xSelection) {
|
||||||
| `Linear => Functions.range(min(dist), max(dist), sampleCount)
|
| `Linear => E.A.Floats.range(min(dist), max(dist), sampleCount)
|
||||||
| `ByWeight =>
|
| `ByWeight =>
|
||||||
Functions.range(minCdfValue, maxCdfValue, sampleCount)
|
E.A.Floats.range(minCdfValue, maxCdfValue, sampleCount)
|
||||||
|> E.A.fmap(x => inv(x, dist))
|
|> E.A.fmap(x => inv(x, dist))
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -208,20 +208,20 @@ module PointwiseAddDistributionsWeighted = {
|
||||||
type t = pointwiseAdd;
|
type t = pointwiseAdd;
|
||||||
|
|
||||||
let normalizeWeights = (dists: t) => {
|
let normalizeWeights = (dists: t) => {
|
||||||
let total = dists |> E.A.fmap(snd) |> Functions.sum;
|
let total = dists |> E.A.fmap(snd) |> E.A.Floats.sum;
|
||||||
dists |> E.A.fmap(((a, b)) => (a, b /. total));
|
dists |> E.A.fmap(((a, b)) => (a, b /. total));
|
||||||
};
|
};
|
||||||
|
|
||||||
let pdf = (dists: t, x: float) =>
|
let pdf = (dists: t, x: float) =>
|
||||||
dists
|
dists
|
||||||
|> E.A.fmap(((e, w)) => GenericSimple.pdf(x, e) *. w)
|
|> E.A.fmap(((e, w)) => GenericSimple.pdf(x, e) *. w)
|
||||||
|> Functions.sum;
|
|> E.A.Floats.sum;
|
||||||
|
|
||||||
let min = (dists: t) =>
|
let min = (dists: t) =>
|
||||||
dists |> E.A.fmap(d => d |> fst |> GenericSimple.min) |> Functions.min;
|
dists |> E.A.fmap(d => d |> fst |> GenericSimple.min) |> E.A.min;
|
||||||
|
|
||||||
let max = (dists: t) =>
|
let max = (dists: t) =>
|
||||||
dists |> E.A.fmap(d => d |> fst |> GenericSimple.max) |> Functions.max;
|
dists |> E.A.fmap(d => d |> fst |> GenericSimple.max) |> E.A.max;
|
||||||
|
|
||||||
let toShape = (dists: t, sampleCount: int) => {
|
let toShape = (dists: t, sampleCount: int) => {
|
||||||
let xs =
|
let xs =
|
||||||
|
|
120
src/utility/E.re
120
src/utility/E.re
|
@ -249,6 +249,12 @@ module A = {
|
||||||
let fold_right = Array.fold_right;
|
let fold_right = Array.fold_right;
|
||||||
let concatMany = Belt.Array.concatMany;
|
let concatMany = Belt.Array.concatMany;
|
||||||
let keepMap = Belt.Array.keepMap;
|
let keepMap = Belt.Array.keepMap;
|
||||||
|
let min = a =>
|
||||||
|
get(a, 0)
|
||||||
|
|> O.fmap(first => Belt.Array.reduce(a, first, (i, j) => i < j ? i : j));
|
||||||
|
let max = a =>
|
||||||
|
get(a, 0)
|
||||||
|
|> O.fmap(first => Belt.Array.reduce(a, first, (i, j) => i > j ? i : j));
|
||||||
let stableSortBy = Belt.SortArray.stableSortBy;
|
let stableSortBy = Belt.SortArray.stableSortBy;
|
||||||
let toRanges = (a: array('a)) =>
|
let toRanges = (a: array('a)) =>
|
||||||
switch (a |> Belt.Array.length) {
|
switch (a |> Belt.Array.length) {
|
||||||
|
@ -270,6 +276,19 @@ module A = {
|
||||||
/* TODO: Is there a better way of doing this? */
|
/* TODO: Is there a better way of doing this? */
|
||||||
let uniq = r => asList(L.uniq, r);
|
let uniq = r => asList(L.uniq, r);
|
||||||
|
|
||||||
|
//intersperse([1,2,3], [10,11,12]) => [1,10,2,11,3,12]
|
||||||
|
let intersperse = (a: array('a), b: array('a)) => {
|
||||||
|
let items: ref(array('a)) = ref([||]);
|
||||||
|
|
||||||
|
Belt.Array.forEachWithIndex(a, (i, item) => {
|
||||||
|
switch (Belt.Array.get(b, i)) {
|
||||||
|
| Some(r) => items := append(items^, [|item, r|])
|
||||||
|
| None => items := append(items^, [|item|])
|
||||||
|
}
|
||||||
|
});
|
||||||
|
items^;
|
||||||
|
};
|
||||||
|
|
||||||
// @todo: Is -1 still the indicator that this is false (as is true with
|
// @todo: Is -1 still the indicator that this is false (as is true with
|
||||||
// @todo: js findIndex)? Wasn't sure.
|
// @todo: js findIndex)? Wasn't sure.
|
||||||
let findIndex = (e, i) =>
|
let findIndex = (e, i) =>
|
||||||
|
@ -314,6 +333,13 @@ module A = {
|
||||||
};
|
};
|
||||||
|
|
||||||
module Sorted = {
|
module Sorted = {
|
||||||
|
let min = first;
|
||||||
|
let max = last;
|
||||||
|
let range = (~min=min, ~max=max, a) =>
|
||||||
|
switch (min(a), max(a)) {
|
||||||
|
| (Some(min), Some(max)) => Some(max -. min)
|
||||||
|
| _ => None
|
||||||
|
};
|
||||||
let binarySearchFirstElementGreaterIndex = (ar: array('a), el: 'a) => {
|
let binarySearchFirstElementGreaterIndex = (ar: array('a), el: 'a) => {
|
||||||
let el = Belt.SortArray.binarySearchBy(ar, el, compare);
|
let el = Belt.SortArray.binarySearchBy(ar, el, compare);
|
||||||
let el = el < 0 ? el * (-1) - 1 : el;
|
let el = el < 0 ? el * (-1) - 1 : el;
|
||||||
|
@ -323,38 +349,76 @@ module A = {
|
||||||
| e => `firstHigher(e)
|
| e => `firstHigher(e)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let concat = (t1: array('a), t2: array('a)) => {
|
||||||
|
let ts = Belt.Array.concat(t1, t2);
|
||||||
|
ts |> Array.fast_sort(compare);
|
||||||
|
ts;
|
||||||
|
};
|
||||||
|
|
||||||
|
module Floats = {
|
||||||
|
let makeIncrementalUp = (a, b) =>
|
||||||
|
Array.make(b - a + 1, a)
|
||||||
|
|> Array.mapi((i, c) => c + i)
|
||||||
|
|> Belt.Array.map(_, float_of_int);
|
||||||
|
|
||||||
|
let makeIncrementalDown = (a, b) =>
|
||||||
|
Array.make(a - b + 1, a)
|
||||||
|
|> Array.mapi((i, c) => c - i)
|
||||||
|
|> Belt.Array.map(_, float_of_int);
|
||||||
|
|
||||||
|
let split = (sortedArray: array(float)) => {
|
||||||
|
let continuous = [||];
|
||||||
|
let discrete = FloatFloatMap.empty();
|
||||||
|
Belt.Array.forEachWithIndex(
|
||||||
|
sortedArray,
|
||||||
|
(index, element) => {
|
||||||
|
let maxIndex = (sortedArray |> Array.length) - 1;
|
||||||
|
let possiblySimilarElements =
|
||||||
|
(
|
||||||
|
switch (index) {
|
||||||
|
| 0 => [|index + 1|]
|
||||||
|
| n when n == maxIndex => [|index - 1|]
|
||||||
|
| _ => [|index - 1, index + 1|]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> Belt.Array.map(_, r => sortedArray[r]);
|
||||||
|
let hasSimilarElement =
|
||||||
|
Belt.Array.some(possiblySimilarElements, r => r == element);
|
||||||
|
hasSimilarElement
|
||||||
|
? FloatFloatMap.increment(element, discrete)
|
||||||
|
: {
|
||||||
|
let _ = Js.Array.push(element, continuous);
|
||||||
|
();
|
||||||
|
};
|
||||||
|
();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
(continuous, discrete);
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module Floats = {
|
module Floats = {
|
||||||
let split = (sortedArray: array(float)) => {
|
let sum = Belt.Array.reduce(_, 0., (i, j) => i +. j);
|
||||||
let continuous = [||];
|
let mean = a => sum(a) /. (Array.length(a) |> float_of_int);
|
||||||
let discrete = FloatFloatMap.empty();
|
let random = Js.Math.random_int;
|
||||||
Belt.Array.forEachWithIndex(
|
|
||||||
sortedArray,
|
|
||||||
(index, element) => {
|
|
||||||
let maxIndex = (sortedArray |> Array.length) - 1;
|
|
||||||
let possiblySimilarElements =
|
|
||||||
(
|
|
||||||
switch (index) {
|
|
||||||
| 0 => [|index + 1|]
|
|
||||||
| n when n == maxIndex => [|index - 1|]
|
|
||||||
| _ => [|index - 1, index + 1|]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|> Belt.Array.map(_, r => sortedArray[r]);
|
|
||||||
let hasSimilarElement =
|
|
||||||
Belt.Array.some(possiblySimilarElements, r => r == element);
|
|
||||||
hasSimilarElement
|
|
||||||
? FloatFloatMap.increment(element, discrete)
|
|
||||||
: {
|
|
||||||
let _ = Js.Array.push(element, continuous);
|
|
||||||
();
|
|
||||||
};
|
|
||||||
();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
(continuous, discrete);
|
exception RangeError(string);
|
||||||
|
let range = (min: float, max: float, n: int): array(float) => {
|
||||||
|
switch (n) {
|
||||||
|
| 0 => [||]
|
||||||
|
| 1 => [|min|]
|
||||||
|
| 2 => [|min, max|]
|
||||||
|
| _ when min == max => Belt.Array.make(n, min)
|
||||||
|
| _ when n < 0 => raise(RangeError("n must be greater than 0"))
|
||||||
|
| _ when min > max =>
|
||||||
|
raise(RangeError("Min value is less then max value"))
|
||||||
|
| _ =>
|
||||||
|
let diff = (max -. min) /. Belt.Float.fromInt(n - 1);
|
||||||
|
Belt.Array.makeBy(n, i => {min +. Belt.Float.fromInt(i) *. diff});
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
module type Config = {let shape: DistTypes.xyShape;};
|
|
||||||
|
|
||||||
exception ShapeWrong(string);
|
|
||||||
|
|
||||||
let order = (shape: DistTypes.xyShape): DistTypes.xyShape => {
|
|
||||||
let xy =
|
|
||||||
shape.xs
|
|
||||||
|> Array.mapi((i, x) => [x, shape.ys |> Array.get(_, i)])
|
|
||||||
|> Belt.SortArray.stableSortBy(_, ([a, _], [b, _]) => a > b ? 1 : (-1));
|
|
||||||
{
|
|
||||||
xs: xy |> Array.map(([x, _]) => x),
|
|
||||||
ys: xy |> Array.map(([_, y]) => y),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module Make = (Config: Config) => {
|
|
||||||
let xs = Config.shape.xs;
|
|
||||||
let ys = Config.shape.ys;
|
|
||||||
let get = Array.get;
|
|
||||||
let len = Array.length;
|
|
||||||
|
|
||||||
let validateHasLength = (): bool => len(xs) > 0;
|
|
||||||
let validateSize = (): bool => len(xs) == len(ys);
|
|
||||||
if (!validateHasLength()) {
|
|
||||||
raise(ShapeWrong("You need at least one element."));
|
|
||||||
};
|
|
||||||
if (!validateSize()) {
|
|
||||||
raise(ShapeWrong("Arrays of \"xs\" and \"ys\" have different sizes."));
|
|
||||||
};
|
|
||||||
if (!Belt.SortArray.isSorted(xs, (a, b) => a > b ? 1 : (-1))) {
|
|
||||||
raise(ShapeWrong("Arrays of \"xs\" and \"ys\" have different sizes."));
|
|
||||||
};
|
|
||||||
let minX = () => get(xs, 0);
|
|
||||||
let maxX = () => get(xs, len(xs) - 1);
|
|
||||||
let minY = () => get(ys, 0);
|
|
||||||
let maxY = () => get(ys, len(ys) - 1);
|
|
||||||
let findY = (x: float): float => {
|
|
||||||
let firstHigherIndex =
|
|
||||||
E.A.Sorted.binarySearchFirstElementGreaterIndex(xs, x);
|
|
||||||
switch (firstHigherIndex) {
|
|
||||||
| `overMax => maxY()
|
|
||||||
| `underMin => minY()
|
|
||||||
| `firstHigher(firstHigherIndex) =>
|
|
||||||
let lowerOrEqualIndex =
|
|
||||||
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
|
||||||
let needsInterpolation = get(xs, lowerOrEqualIndex) != x;
|
|
||||||
if (needsInterpolation) {
|
|
||||||
Functions.interpolate(
|
|
||||||
get(xs, lowerOrEqualIndex),
|
|
||||||
get(xs, firstHigherIndex),
|
|
||||||
get(ys, lowerOrEqualIndex),
|
|
||||||
get(ys, firstHigherIndex),
|
|
||||||
x,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ys[lowerOrEqualIndex];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
let findX = (y: float): float => {
|
|
||||||
let firstHigherIndex =
|
|
||||||
E.A.Sorted.binarySearchFirstElementGreaterIndex(ys, y);
|
|
||||||
switch (firstHigherIndex) {
|
|
||||||
| `overMax => maxX()
|
|
||||||
| `underMin => minX()
|
|
||||||
| `firstHigher(firstHigherIndex) =>
|
|
||||||
let lowerOrEqualIndex =
|
|
||||||
firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1;
|
|
||||||
let needsInterpolation = get(ys, lowerOrEqualIndex) != y;
|
|
||||||
if (needsInterpolation) {
|
|
||||||
Functions.interpolate(
|
|
||||||
get(ys, lowerOrEqualIndex),
|
|
||||||
get(ys, firstHigherIndex),
|
|
||||||
get(xs, lowerOrEqualIndex),
|
|
||||||
get(xs, firstHigherIndex),
|
|
||||||
y,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
xs[lowerOrEqualIndex];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
let convertWithAlternativeXs = (newXs: array(float)): DistTypes.xyShape => {
|
|
||||||
let newYs = Belt.Array.map(newXs, findY);
|
|
||||||
{xs: newXs, ys: newYs};
|
|
||||||
};
|
|
||||||
let convertToNewLength = (newLength: int): DistTypes.xyShape => {
|
|
||||||
Functions.(
|
|
||||||
range(min(xs), max(xs), newLength) |> convertWithAlternativeXs
|
|
||||||
);
|
|
||||||
};
|
|
||||||
let sampleSingle = (): float => Js.Math.random() |> findY;
|
|
||||||
let sample = (size: int): array(float) =>
|
|
||||||
Belt.Array.makeBy(size, i => sampleSingle());
|
|
||||||
let integral = () => {
|
|
||||||
Belt.Array.reduceWithIndex(ys, 0., (integral, y, i) => {
|
|
||||||
switch (i) {
|
|
||||||
| 0 => integral
|
|
||||||
| _ =>
|
|
||||||
let thisY = y;
|
|
||||||
let lastY = get(ys, i - 1);
|
|
||||||
let thisX = get(xs, i);
|
|
||||||
let lastX = get(xs, i - 1);
|
|
||||||
let sectionInterval = (thisY +. lastY) /. 2. *. (thisX -. lastX);
|
|
||||||
integral +. sectionInterval;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,5 +1,3 @@
|
||||||
exception RangeWrong(string);
|
|
||||||
|
|
||||||
let interpolate =
|
let interpolate =
|
||||||
(xMin: float, xMax: float, yMin: float, yMax: float, xIntended: float)
|
(xMin: float, xMax: float, yMin: float, yMax: float, xIntended: float)
|
||||||
: float => {
|
: float => {
|
||||||
|
@ -7,30 +5,3 @@ let interpolate =
|
||||||
let maxProportion = (xIntended -. xMin) /. (xMax -. xMin);
|
let maxProportion = (xIntended -. xMin) /. (xMax -. xMin);
|
||||||
yMin *. minProportion +. yMax *. maxProportion;
|
yMin *. minProportion +. yMax *. maxProportion;
|
||||||
};
|
};
|
||||||
|
|
||||||
let sum = Belt.Array.reduce(_, 0., (i, j) => i +. j);
|
|
||||||
let mean = a => sum(a) /. (Array.length(a) |> float_of_int);
|
|
||||||
let min = a => Belt.Array.reduce(a, a[0], (i, j) => i < j ? i : j);
|
|
||||||
let max = a => Belt.Array.reduce(a, a[0], (i, j) => i > j ? i : j);
|
|
||||||
let up = (a, b) =>
|
|
||||||
Array.make(b - a + 1, a)
|
|
||||||
|> Array.mapi((i, c) => c + i)
|
|
||||||
|> Belt.Array.map(_, float_of_int);
|
|
||||||
let down = (a, b) =>
|
|
||||||
Array.make(a - b + 1, a)
|
|
||||||
|> Array.mapi((i, c) => c - i)
|
|
||||||
|> Belt.Array.map(_, float_of_int);
|
|
||||||
let range = (min: float, max: float, n: int): array(float) => {
|
|
||||||
switch (n) {
|
|
||||||
| 0 => [||]
|
|
||||||
| 1 => [|min|]
|
|
||||||
| 2 => [|min, max|]
|
|
||||||
| _ when min == max => Belt.Array.make(n, min)
|
|
||||||
| _ when n < 0 => raise(RangeWrong("n is less then zero"))
|
|
||||||
| _ when min > max => raise(RangeWrong("Min values is less then max"))
|
|
||||||
| _ =>
|
|
||||||
let diff = (max -. min) /. Belt.Float.fromInt(n - 1);
|
|
||||||
Belt.Array.makeBy(n, i => {min +. Belt.Float.fromInt(i) *. diff});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
let random = Js.Math.random_int;
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user