From 8aafb2bb6e7e816e9eb967cd421c289dfa23a85b Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 15:06:19 +0300 Subject: [PATCH 01/32] Adds required files --- src/utility/lib/continuousDistribution.js | 204 ++++++++++++++++++ .../lib/continuousDistribution.spec.js | 111 ++++++++++ src/utility/lib/functions.js | 136 ++++++++++++ src/utility/lib/functions.spec.js | 55 +++++ 4 files changed, 506 insertions(+) create mode 100644 src/utility/lib/continuousDistribution.js create mode 100644 src/utility/lib/continuousDistribution.spec.js create mode 100644 src/utility/lib/functions.js create mode 100644 src/utility/lib/functions.spec.js diff --git a/src/utility/lib/continuousDistribution.js b/src/utility/lib/continuousDistribution.js new file mode 100644 index 00000000..9c80d19d --- /dev/null +++ b/src/utility/lib/continuousDistribution.js @@ -0,0 +1,204 @@ +const _ = require('lodash'); +const { interpolate, range, min, max } = require('./functions'); + +class ContinuousDistribution { + /** + * @param {number[]} xs + * @param {number[]} ys + */ + constructor(xs, ys) { + if (!_.isArray(xs)) { + throw new Error('XS should be an array.'); + } + if (!_.isArray(ys)) { + throw new Error('YS should be an array.'); + } + if (!this.validateHasLength(xs)) { + throw new Error('You need at least one element.'); + } + if (!this.validateSize(xs, ys)) { + throw new Error('Arrays of "xs" and "ys" have different sizes.'); + } + + const sorted = this.order(xs, ys); + this.xs = sorted.xs; + this.ys = sorted.ys; + } + + /** + * Order them to make sure that xs are increasing + * @param {number[]} xs + * @param {number[]} ys + * @return {{ys: number[], xs: number[]}} + */ + order(xs, ys) { + const xsYs = xs.map((v, i) => ({ ys: ys[i], xs: v })); + const sorted = xsYs.sort((a, b) => { + if (a.xs > b.xs) return 1; + if (a.xs < b.xs) return -1; + return 0; + }); + + const XS = sorted.map(v => v.xs); + const YS = sorted.map(v => v.ys); + + return { xs: XS, ys: YS }; + } + + /** + * @param {number[]} xs + * @param {number[]} ys + * @return {boolean} + */ + validateSize(xs, ys) { + return xs.length === ys.length; + } + + /** + * @param xs + * @returns {boolean} + */ + validateHasLength(xs) { + return xs.length > 0; + } + + /** + * @returns {number} + */ + minX() { + return this.xs[0] + } + + /** + * @returns {number} + */ + maxX() { + return this.xs[this.xs.length - 1] + } + + /** + * If xs=[1,2,3], and ys=[5,6,7], + * then findY(1) = 5, findY(3) = 7, findY(1.5) = 5.5 + * @param {number} x + * @return {number} + */ + findY(x) { + let firstHigherIndex = this.xs.findIndex(X => X >= x); + if (firstHigherIndex < 0) return this.ys[this.ys.length - 1]; + if (firstHigherIndex === 0) return this.ys[0]; + let lowerOrEqualIndex = firstHigherIndex - 1; + if (lowerOrEqualIndex < 0) lowerOrEqualIndex = 0; + let needsInterpolation = this.xs[lowerOrEqualIndex] !== x; + if (needsInterpolation) { + return interpolate( + this.xs[lowerOrEqualIndex], + this.xs[firstHigherIndex], + this.ys[lowerOrEqualIndex], + this.ys[firstHigherIndex], + x + ); + } else { + return this.ys[lowerOrEqualIndex]; + } + } + + /** + * If xs=[1,2,3], and ys=[5,6,7], + * then findX(5) = 1, findX(7) = 3, findY(5.5) = 1.5 + * This should do the same thing as `findY`, but for Y. + * @param {number} y + * @return {number} + */ + findX(y) { + let firstHigherIndex = this.ys.findIndex(Y => Y >= y); + if (firstHigherIndex < 0) return this.xs[this.xs.length - 1]; + if (firstHigherIndex === 0) return this.xs[0]; + let lowerOrEqualIndex = firstHigherIndex - 1; + if (lowerOrEqualIndex < 0) lowerOrEqualIndex = 0; + let needsInterpolation = this.ys[lowerOrEqualIndex] !== y; + if (needsInterpolation) { + return interpolate( + this.ys[lowerOrEqualIndex], + this.ys[firstHigherIndex], + this.xs[lowerOrEqualIndex], + this.xs[firstHigherIndex], + y + ); + } else { + return this.xs[lowerOrEqualIndex]; + } + } + + /** + * @param {number[]} xs + * @return {ContinuousDistribution} + */ + convertWithAlternativeXs(xs) { + const ys = xs.map(x => this.findY(x)); + return new ContinuousDistribution(xs, ys); + } + + /** + * @param {number} newLength + * @return {ContinuousDistribution} + */ + convertToNewLength(newLength) { + const _range = range(min(this.xs), max(this.xs), newLength); + return this.convertWithAlternativeXs(_range); + } + + /** + * @return {number} + */ + sampleSingle() { + const y = Math.random(); + return this.findX(y); + } + + /** + * Poduce n samples, using ``sampleSingle`` for each. + * @param size + * @return {number[]} + */ + sample(size) { + return Array.from(Array(size), () => this.sampleSingle()); + } + + /** + * Finds the integral. Takes the average Y value between points, + * treating them like a triangle. + * @return {number[]} + */ + integral(params = { filterOutNaNs: false }) { + let integral = 0; + if (!params.filterOutNaNs && _.includes(this.ys, NaN)) { + return NaN; + } else if (_.includes(this.ys, Infinity) && _.includes(this.ys, -Infinity)) { + return NaN; + } else if (_.includes(this.ys, Infinity)) { + return Infinity; + } else if (_.includes(this.ys, -Infinity)) { + return -Infinity; + } + for (let i = 1; i < this.ys.length; i++) { + let thisY = this.ys[i]; + let lastY = this.ys[i - 1]; + let thisX = this.xs[i]; + let lastX = this.xs[i - 1]; + + if ( + _.isFinite(thisY) && _.isFinite(lastY) && + _.isFinite(thisX) && _.isFinite(lastX) + ) { + let sectionInterval = ((thisY + lastY) / 2) * (thisX - lastX); + integral = integral + sectionInterval; + } + + } + return integral; + } +} + +module.exports = { + ContinuousDistribution, +}; diff --git a/src/utility/lib/continuousDistribution.spec.js b/src/utility/lib/continuousDistribution.spec.js new file mode 100644 index 00000000..75831fb5 --- /dev/null +++ b/src/utility/lib/continuousDistribution.spec.js @@ -0,0 +1,111 @@ +const { ContinuousDistribution } = require('./continuousDistribution'); +const { up, down } = require('./functions'); + +describe('ContinuousDistribution Class', () => { + it('constructor()', () => { + const xs = up(1, 9); + const ys = up(1, 8); + expect(() => { + new ContinuousDistribution(xs, ys); + }).toThrow(/^Arrays of "xs" and "ys" have different sizes.$/); + }); + it('order()', () => { + const xs = down(9, 1); + const ys = down(9, 1); + const cdf = new ContinuousDistribution(xs, ys); + expect(cdf.xs).toEqual(up(1, 9)); + expect(cdf.ys).toEqual(up(1, 9)); + }); + it('findY()', () => { + const xs = [1, 2, 3]; + const ys = [5, 6, 7]; + const cdf = new ContinuousDistribution(xs, ys); + expect(cdf.findY(1)).toEqual(5); + expect(cdf.findY(1.5)).toEqual(5.5); + expect(cdf.findY(3)).toEqual(7); + expect(cdf.findY(4)).toEqual(7); + expect(cdf.findY(15)).toEqual(7); + expect(cdf.findY(-1)).toEqual(5); + }); + it('findX()', () => { + const xs = [1, 2, 3]; + const ys = [5, 6, 7]; + const cdf = new ContinuousDistribution(xs, ys); + expect(cdf.findX(5)).toEqual(1); + expect(cdf.findX(7)).toEqual(3); + expect(cdf.findX(5.5)).toEqual(1.5); + expect(cdf.findX(8)).toEqual(3); + expect(cdf.findX(4)).toEqual(1); + }); + it('convertWithAlternativeXs() when "XS" within "xs"', () => { + const xs = up(1, 9); + const ys = up(20, 28); + const cdf = new ContinuousDistribution(xs, ys); + const XS = up(3, 7); + const CDF = cdf.convertWithAlternativeXs(XS); + expect(CDF.xs).toEqual([3, 4, 5, 6, 7]); + expect(CDF.ys).toEqual([22, 23, 24, 25, 26]); + }); + it('convertToNewLength()', () => { + const xs = up(1, 9); + const ys = up(50, 58); + const cdf = new ContinuousDistribution(xs, ys); + const CDF = cdf.convertToNewLength(3); + expect(CDF.xs).toEqual([1, 5, 9]); + expect(CDF.ys).toEqual([50, 54, 58]); + }); + it('sample()', () => { + const xs = up(1, 9); + const ys = up(70, 78); + const cdf = new ContinuousDistribution(xs, ys); + const XS = cdf.sample(3); + expect(Number.isInteger(XS[0])).toBe(true); + expect(Number.isInteger(XS[1])).toBe(true); + expect(Number.isInteger(XS[2])).toBe(true); + }); + + describe('integral()', () => { + it('with regular inputs', () => { + const xs = [0,1,2,4]; + const ys = [0.0, 1.0, 2.0, 2.0]; + const cdf = new ContinuousDistribution(xs, ys); + const integral = cdf.integral(); + expect(integral).toEqual(6); + }); + it('with an infinity', () => { + const xs = [0,1,2,4]; + const ys = [0.0, 1.0, Infinity, 2.0]; + const cdf = new ContinuousDistribution(xs, ys); + const integral = cdf.integral(); + expect(integral).toEqual(Infinity); + }); + it('with negative infinity', () => { + const xs = [0,1,2,4]; + const ys = [0.0, 1.0, -Infinity, 2.0]; + const cdf = new ContinuousDistribution(xs, ys); + const integral = cdf.integral(); + expect(integral).toEqual(-Infinity); + }); + it('with both positive and negative infinities', () => { + const xs = [0,1,2,4]; + const ys = [0.0, 1.0, -Infinity, Infinity]; + const cdf = new ContinuousDistribution(xs, ys); + const integral = cdf.integral(); + expect(integral).toEqual(NaN); + }); + it('with a NaN and filterOutNaNs set to false', () => { + const xs = [0,1,2,4]; + const ys = [0.0, 1.0, 2.0, NaN]; + const cdf = new ContinuousDistribution(xs, ys); + const integral = cdf.integral({filterOutNaNs: false}); + expect(integral).toEqual(NaN); + }); + it('with a NaN and filterOutNaNs set to true', () => { + const xs = [0,1,2,4]; + const ys = [0.0, 1.0, 2.0, NaN]; + const cdf = new ContinuousDistribution(xs, ys); + const integral = cdf.integral({filterOutNaNs: true}); + expect(integral).toEqual(2); + }); + }) +}); diff --git a/src/utility/lib/functions.js b/src/utility/lib/functions.js new file mode 100644 index 00000000..0943e530 --- /dev/null +++ b/src/utility/lib/functions.js @@ -0,0 +1,136 @@ +/** + * @param {number} xMin + * @param {number} xMax + * @param {number} yMin + * @param {number} yMax + * @param {number} xIntended + * @return {number} + */ +function interpolate(xMin, xMax, yMin, yMax, xIntended) { + const minProportion = (xMax - xIntended) / (xMax - xMin); + const maxProportion = (xIntended - xMin) / (xMax - xMin); + return (yMin * minProportion) + (yMax * maxProportion); +} + +/** + * This should return an array of n evenly-spaced items + * between min and max, including min and max. + * range(1,5,3) = [1, 3, 5]; + * range(1,5,5) = [1, 2, 3, 4, 5]; + * @param {number} min + * @param {number} max + * @param {number} n + * @return {number[]} + */ +function range(min, max, n) { + if (n <= 0) throw new RangeError('n is less then zero'); + if (n === Infinity) throw new RangeError('n is Infinity'); + if (n === 0) return []; + if (n === 1) return [min]; + if (n === 2) return [min, max]; + if (min === max) return Array(n).fill(min); + n -= 1; + const diff = min - max; + const interval = Math.abs(diff / n); + + const result = []; + + let item = min; + do { + result.push(item); + item += interval; + } while (item <= max); + + // corrects results because of math errors + if ((n + 1) - result.length === 1) { + result.push(max); + } + + return result; +} + +/** + * @param {number[]} arr + * @return {number} + */ +function sum(arr) { + return arr.reduce((acc, val) => acc + val, 0); +} + +/** + * @param {number[]} arr + * @return {number} + */ +function mean(arr) { + return sum(arr) / arr.length; +} + +/** + * @param {number[]} arr + * @return {number} + */ +function min(arr) { + let val = arr[0]; + for (let i = 1; i < arr.length; i++) { + if (arr[i] < val) { + val = arr[i]; + } + } + return val; +} + +/** + * @param {number[]} arr + * @return {number} + */ +function max(arr) { + let val = arr[0]; + for (let i = 1; i < arr.length; i++) { + if (arr[i] > val) { + val = arr[i]; + } + } + return val; +} + +/** + * @param {number} min + * @param {number} max + * @return {number} + */ +function random(min, max) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +/** + * @param {number} from + * @param {number} to + * @return {number[]} + */ +function up(from, to) { + const arr = []; + for (let i = from; i <= to; i++) arr.push(i); + return arr; +} + +/** + * @param {number} from + * @param {number} to + * @return {number[]} + */ +function down(from, to) { + const arr = []; + for (let i = from; i >= to; i--) arr.push(i); + return arr; +} + +module.exports = { + interpolate, + min, + max, + range, + mean, + random, + up, + down, +}; \ No newline at end of file diff --git a/src/utility/lib/functions.spec.js b/src/utility/lib/functions.spec.js new file mode 100644 index 00000000..948f1f20 --- /dev/null +++ b/src/utility/lib/functions.spec.js @@ -0,0 +1,55 @@ +const { interpolate } = require('./functions'); +const { range } = require('./functions'); +const { mean } = require('./functions'); +const { min } = require('./functions'); +const { max } = require('./functions'); +const { random } = require('./functions'); +const { up, down } = require('./functions'); + +describe('Functions', () => { + it('interpolate()', () => { + expect(interpolate( + 10, 20, + 1, 2, + 15 + )).toBe(1.5); + }); + it('range()', () => { + expect(range(1, 5, 3)).toEqual([1, 3, 5]); + expect(range(1, 5, 5)).toEqual([1, 2, 3, 4, 5]); + expect(range(-10, 15, 2)).toEqual([-10, 15]); + expect(range(-10, 15, 3)).toEqual([-10, 2.5, 15]); + expect(range(-10.3, 17, 3)).toEqual([-10.3, 3.3499999999999996, 17]); + expect(range(-10.3, 17, 5)).toEqual([-10.3, -3.4750000000000005, 3.3499999999999996, 10.175, 17]); + expect(range(-10.3, 17.31, 3)).toEqual([-10.3, 3.504999999999999, 17.31]); + expect(range(1, 1, 3)).toEqual([1, 1, 1]); + }); + it('mean()', () => { + expect(mean([1, 2, 3])).toBe(2); + expect(mean([1, 2, 3, -2])).toBe(1); + expect(mean([1, 2, 3, -2, -10])).toBe(-1.2); + }); + it('min()', () => { + expect(min([1, 2, 3])).toBe(1); + expect(min([-1, -2, 0, 20])).toBe(-2); + expect(min([-1, -2, 0, 20, -2.2])).toBe(-2.2); + }); + it('max()', () => { + expect(max([1, 2, 3])).toBe(3); + expect(max([-1, -2, 0, 20])).toBe(20); + expect(max([-1, -2, 0, -2.2])).toBe(0); + }); + it('random()', () => { + const num = random(1, 5); + expect(num).toBeLessThanOrEqual(5); + expect(num).toBeGreaterThanOrEqual(1); + }); + it('up()', () => { + expect(up(1, 5)).toEqual([1, 2, 3, 4, 5]); + expect(up(-1, 5)).toEqual([-1, 0, 1, 2, 3, 4, 5]); + }); + it('down()', () => { + expect(down(5, 1)).toEqual([5, 4, 3, 2, 1]); + expect(down(5, -1)).toEqual([5, 4, 3, 2, 1, 0, -1]); + }); +}); From 961db72adf9d973629c6fc54df8676e5f2f39946 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 15:09:43 +0300 Subject: [PATCH 02/32] Removes future obsolete lines --- src/utility/lib/continuousDistribution.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/utility/lib/continuousDistribution.js b/src/utility/lib/continuousDistribution.js index 9c80d19d..8ae8a813 100644 --- a/src/utility/lib/continuousDistribution.js +++ b/src/utility/lib/continuousDistribution.js @@ -7,12 +7,6 @@ class ContinuousDistribution { * @param {number[]} ys */ constructor(xs, ys) { - if (!_.isArray(xs)) { - throw new Error('XS should be an array.'); - } - if (!_.isArray(ys)) { - throw new Error('YS should be an array.'); - } if (!this.validateHasLength(xs)) { throw new Error('You need at least one element.'); } From ff13b144e602ff0d335c71d6cc0430d2bcb26117 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 15:25:08 +0300 Subject: [PATCH 03/32] Step 1 --- src/utility/lib/CDFunctor.re | 7 +++++++ src/utility/lib/CDFunctor__Test.re | 8 ++++++++ 2 files changed, 15 insertions(+) create mode 100644 src/utility/lib/CDFunctor.re create mode 100644 src/utility/lib/CDFunctor__Test.re diff --git a/src/utility/lib/CDFunctor.re b/src/utility/lib/CDFunctor.re new file mode 100644 index 00000000..c6b823dd --- /dev/null +++ b/src/utility/lib/CDFunctor.re @@ -0,0 +1,7 @@ +module type Config = {let shape: DistributionTypes.xyShape;}; + +module Make = (Config: Config) => { + let validateHasLength = (): bool => Array.length(Config.shape.xs) > 0; + let validateSize = (): bool => + Array.length(Config.shape.xs) == Array.length(Config.shape.ys); +}; diff --git a/src/utility/lib/CDFunctor__Test.re b/src/utility/lib/CDFunctor__Test.re new file mode 100644 index 00000000..e6399a8f --- /dev/null +++ b/src/utility/lib/CDFunctor__Test.re @@ -0,0 +1,8 @@ +module CDFConfig = { + let shape: DistributionTypes.xyShape = { + xs: [|1., 4., 8.|], + ys: [|8., 9., 2.|], + }; +}; + +module CDF = CDFunctor.Make(CDFConfig); From 36ad96d129b3ae46f745208d0070349035436cff Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 15:29:25 +0300 Subject: [PATCH 04/32] Step 2 --- src/utility/lib/CDFunctor.re | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utility/lib/CDFunctor.re b/src/utility/lib/CDFunctor.re index c6b823dd..6e1a3719 100644 --- a/src/utility/lib/CDFunctor.re +++ b/src/utility/lib/CDFunctor.re @@ -1,7 +1,15 @@ module type Config = {let shape: DistributionTypes.xyShape;}; +exception ShapeWrong(string); + module Make = (Config: Config) => { let validateHasLength = (): bool => Array.length(Config.shape.xs) > 0; let validateSize = (): bool => Array.length(Config.shape.xs) == Array.length(Config.shape.ys); + if (!validateHasLength()) { + raise(ShapeWrong("You need at least one element.")); + }; + if (!validateSize()) { + raise(ShapeWrong("Arrays of \"xs\" and \"ys\" have different sizes.")); + }; }; From 8448b388faea015333d2863f3924dec85c9fa029 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 16:03:27 +0300 Subject: [PATCH 05/32] Step 3 --- src/utility/lib/CDFunctor.re | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utility/lib/CDFunctor.re b/src/utility/lib/CDFunctor.re index 6e1a3719..90d5120b 100644 --- a/src/utility/lib/CDFunctor.re +++ b/src/utility/lib/CDFunctor.re @@ -12,4 +12,12 @@ module Make = (Config: Config) => { if (!validateSize()) { raise(ShapeWrong("Arrays of \"xs\" and \"ys\" have different sizes.")); }; + + let order = (): DistributionTypes.xyShape => { + let xy = + Config.shape.xs + |> Array.mapi((i, x) => [x, Config.shape.ys[i]]) + |> Belt.SortArray.stableSortBy(_, ([a], [b]) => a > b ? (-1) : 1); + {xs: xy |> Array.map(([x]) => x), ys: xy |> Array.map(([_, y]) => y)}; + }; }; From f929ccd9428b50b28feb3fa34fc6a670a06dbdae Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 16:17:32 +0300 Subject: [PATCH 06/32] Step 4 - first step --- __tests__/CDFunctor__Test.re | 15 +++++++++++++++ src/utility/lib/CDFunctor.re | 19 +++++++++++-------- src/utility/lib/CDFunctor__Test.re | 8 -------- 3 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 __tests__/CDFunctor__Test.re delete mode 100644 src/utility/lib/CDFunctor__Test.re diff --git a/__tests__/CDFunctor__Test.re b/__tests__/CDFunctor__Test.re new file mode 100644 index 00000000..fbd319a0 --- /dev/null +++ b/__tests__/CDFunctor__Test.re @@ -0,0 +1,15 @@ +open Jest; +open Expect; + +describe("CDF", () => { + module CDF = + CDFunctor.Make({ + let shape: DistributionTypes.xyShape = + CDFunctor.order({xs: [|1., 4., 8.|], ys: [|8., 9., 2.|]}); + }); + test("order", () => { + Js.log(CDFunctor.order({xs: [|1., 4., 8.|], ys: [|8., 9., 2.|]})); + Js.log(CDFunctor.order({xs: [|10., 5., 12.|], ys: [|8., 9., 2.|]})); + expect(19.0) |> toEqual(19.0); + }); +}); diff --git a/src/utility/lib/CDFunctor.re b/src/utility/lib/CDFunctor.re index 90d5120b..93003321 100644 --- a/src/utility/lib/CDFunctor.re +++ b/src/utility/lib/CDFunctor.re @@ -2,6 +2,17 @@ module type Config = {let shape: DistributionTypes.xyShape;}; exception ShapeWrong(string); +let order = (shape: DistributionTypes.xyShape): DistributionTypes.xyShape => { + let xy = + shape.xs + |> Array.mapi((i, x) => [x, shape.ys[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 validateHasLength = (): bool => Array.length(Config.shape.xs) > 0; let validateSize = (): bool => @@ -12,12 +23,4 @@ module Make = (Config: Config) => { if (!validateSize()) { raise(ShapeWrong("Arrays of \"xs\" and \"ys\" have different sizes.")); }; - - let order = (): DistributionTypes.xyShape => { - let xy = - Config.shape.xs - |> Array.mapi((i, x) => [x, Config.shape.ys[i]]) - |> Belt.SortArray.stableSortBy(_, ([a], [b]) => a > b ? (-1) : 1); - {xs: xy |> Array.map(([x]) => x), ys: xy |> Array.map(([_, y]) => y)}; - }; }; diff --git a/src/utility/lib/CDFunctor__Test.re b/src/utility/lib/CDFunctor__Test.re deleted file mode 100644 index e6399a8f..00000000 --- a/src/utility/lib/CDFunctor__Test.re +++ /dev/null @@ -1,8 +0,0 @@ -module CDFConfig = { - let shape: DistributionTypes.xyShape = { - xs: [|1., 4., 8.|], - ys: [|8., 9., 2.|], - }; -}; - -module CDF = CDFunctor.Make(CDFConfig); From 7ed722047fdc515e774b768d670a19594911c769 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 16:21:43 +0300 Subject: [PATCH 07/32] Step 5 - test --- __tests__/CDFunctor__Test.re | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/__tests__/CDFunctor__Test.re b/__tests__/CDFunctor__Test.re index fbd319a0..f271e67d 100644 --- a/__tests__/CDFunctor__Test.re +++ b/__tests__/CDFunctor__Test.re @@ -7,9 +7,20 @@ describe("CDF", () => { let shape: DistributionTypes.xyShape = CDFunctor.order({xs: [|1., 4., 8.|], ys: [|8., 9., 2.|]}); }); - test("order", () => { - Js.log(CDFunctor.order({xs: [|1., 4., 8.|], ys: [|8., 9., 2.|]})); - Js.log(CDFunctor.order({xs: [|10., 5., 12.|], ys: [|8., 9., 2.|]})); - expect(19.0) |> toEqual(19.0); + test("order#1", () => { + let a = CDFunctor.order({xs: [|1., 4., 8.|], ys: [|8., 9., 2.|]}); + let b: DistributionTypes.xyShape = { + xs: [|1., 4., 8.|], + ys: [|8., 9., 2.|], + }; + expect(a) |> toEqual(b); + }); + test("order#2", () => { + let a = CDFunctor.order({xs: [|10., 5., 12.|], ys: [|8., 9., 2.|]}); + let b: DistributionTypes.xyShape = { + xs: [|5., 10., 12.|], + ys: [|9., 8., 2.|], + }; + expect(a) |> toEqual(b); }); }); From da15a2b2a584d6a6453362d123708740175cb5c2 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 16:39:55 +0300 Subject: [PATCH 08/32] Step 6 - test --- __tests__/CDFunctor__Test.re | 33 +++++++++++++++++++---- src/utility/lib/CDFunctor.re | 5 ++++ src/utility/lib/continuousDistribution.js | 2 ++ 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/__tests__/CDFunctor__Test.re b/__tests__/CDFunctor__Test.re index f271e67d..031b55fc 100644 --- a/__tests__/CDFunctor__Test.re +++ b/__tests__/CDFunctor__Test.re @@ -1,12 +1,35 @@ open Jest; open Expect; +exception ShapeWrong(string); describe("CDF", () => { - module CDF = - CDFunctor.Make({ - let shape: DistributionTypes.xyShape = - CDFunctor.order({xs: [|1., 4., 8.|], ys: [|8., 9., 2.|]}); - }); + test("raise - w/o order", () => { + expect(() => { + module CDF = + CDFunctor.Make({ + let shape: DistributionTypes.xyShape = { + xs: [|10., 4., 8.|], + ys: [|8., 9., 2.|], + }; + }); + (); + }) + |> toThrow + }); + test("raise - with order", () => { + expect(() => { + module CDF = + CDFunctor.Make({ + let shape: DistributionTypes.xyShape = { + xs: [|1., 4., 8.|], + ys: [|8., 9., 2.|], + }; + }); + (); + }) + |> not_ + |> toThrow + }); test("order#1", () => { let a = CDFunctor.order({xs: [|1., 4., 8.|], ys: [|8., 9., 2.|]}); let b: DistributionTypes.xyShape = { diff --git a/src/utility/lib/CDFunctor.re b/src/utility/lib/CDFunctor.re index 93003321..09848aaf 100644 --- a/src/utility/lib/CDFunctor.re +++ b/src/utility/lib/CDFunctor.re @@ -23,4 +23,9 @@ module Make = (Config: Config) => { if (!validateSize()) { raise(ShapeWrong("Arrays of \"xs\" and \"ys\" have different sizes.")); }; + if (!Belt.SortArray.isSorted(Config.shape.xs, (a, b) => a > b ? 1 : (-1))) { + raise(ShapeWrong("Arrays of \"xs\" and \"ys\" have different sizes.")); + }; + + 1; }; diff --git a/src/utility/lib/continuousDistribution.js b/src/utility/lib/continuousDistribution.js index 8ae8a813..65f6e85d 100644 --- a/src/utility/lib/continuousDistribution.js +++ b/src/utility/lib/continuousDistribution.js @@ -3,6 +3,7 @@ const { interpolate, range, min, max } = require('./functions'); class ContinuousDistribution { /** + * @Done * @param {number[]} xs * @param {number[]} ys */ @@ -20,6 +21,7 @@ class ContinuousDistribution { } /** + * @Done * Order them to make sure that xs are increasing * @param {number[]} xs * @param {number[]} ys From fc66ac008210da5bb35be9db52b3418ecf98aaea Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 16:50:23 +0300 Subject: [PATCH 09/32] Step 7 - test --- __tests__/CDFunctor__Test.re | 16 ++++++++++++++++ src/utility/lib/CDFunctor.re | 6 ++++-- src/utility/lib/continuousDistribution.js | 4 ++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/__tests__/CDFunctor__Test.re b/__tests__/CDFunctor__Test.re index 031b55fc..69e483e3 100644 --- a/__tests__/CDFunctor__Test.re +++ b/__tests__/CDFunctor__Test.re @@ -46,4 +46,20 @@ describe("CDF", () => { }; expect(a) |> toEqual(b); }); + test("minX", () => { + module CDF = + CDFunctor.Make({ + let shape: DistributionTypes.xyShape = + CDFunctor.order({xs: [|20., 4., 8.|], ys: [|8., 9., 2.|]}); + }); + expect(CDF.minX()) |> toEqual(4.); + }); + test("maxX", () => { + module CDF = + CDFunctor.Make({ + let shape: DistributionTypes.xyShape = + CDFunctor.order({xs: [|20., 4., 8.|], ys: [|8., 9., 2.|]}); + }); + expect(CDF.maxX()) |> toEqual(20.); + }); }); diff --git a/src/utility/lib/CDFunctor.re b/src/utility/lib/CDFunctor.re index 09848aaf..3db4f574 100644 --- a/src/utility/lib/CDFunctor.re +++ b/src/utility/lib/CDFunctor.re @@ -5,7 +5,7 @@ exception ShapeWrong(string); let order = (shape: DistributionTypes.xyShape): DistributionTypes.xyShape => { let xy = shape.xs - |> Array.mapi((i, x) => [x, shape.ys[i]]) + |> 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), @@ -26,6 +26,8 @@ module Make = (Config: Config) => { if (!Belt.SortArray.isSorted(Config.shape.xs, (a, b) => a > b ? 1 : (-1))) { raise(ShapeWrong("Arrays of \"xs\" and \"ys\" have different sizes.")); }; - + let minX = () => Config.shape.xs |> Array.get(_, 0); + let maxX = () => + Config.shape.xs |> Array.get(_, Array.length(Config.shape.xs) - 1); 1; }; diff --git a/src/utility/lib/continuousDistribution.js b/src/utility/lib/continuousDistribution.js index 65f6e85d..a1eb3117 100644 --- a/src/utility/lib/continuousDistribution.js +++ b/src/utility/lib/continuousDistribution.js @@ -42,6 +42,7 @@ class ContinuousDistribution { } /** + * @Done * @param {number[]} xs * @param {number[]} ys * @return {boolean} @@ -51,6 +52,7 @@ class ContinuousDistribution { } /** + * @Done * @param xs * @returns {boolean} */ @@ -59,6 +61,7 @@ class ContinuousDistribution { } /** + * @Done * @returns {number} */ minX() { @@ -66,6 +69,7 @@ class ContinuousDistribution { } /** + * @Done * @returns {number} */ maxX() { From 3de54bee23f5810c219299b6572d7afc06eb335f Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 16:54:39 +0300 Subject: [PATCH 10/32] Step 8 --- src/utility/lib/Functions.re | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/utility/lib/Functions.re diff --git a/src/utility/lib/Functions.re b/src/utility/lib/Functions.re new file mode 100644 index 00000000..3cf4537a --- /dev/null +++ b/src/utility/lib/Functions.re @@ -0,0 +1,7 @@ +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; +}; From b0cc498b07e6e6f6fc4c545223896982ddd068d1 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 17:05:42 +0300 Subject: [PATCH 11/32] Step 9 --- src/utility/lib/Functions.re | 6 ++++++ src/utility/lib/functions.js | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/utility/lib/Functions.re b/src/utility/lib/Functions.re index 3cf4537a..94da83b1 100644 --- a/src/utility/lib/Functions.re +++ b/src/utility/lib/Functions.re @@ -5,3 +5,9 @@ let interpolate = let maxProportion = (xIntended -. xMin) /. (xMax -. xMin); yMin *. minProportion +. yMax *. maxProportion; }; + +/* https://bucklescript.github.io/bucklescript/api/Belt.html */ +let sum = Belt.Array.reduce(_, 0., (i, j) => i +. j); +let mean = a => sum(a) /. (Array.length(a) |> float_of_int); +let min = Belt.Array.reduce(_, 0., (i, j) => i < j ? i : j); +let max = Belt.Array.reduce(_, 0., (i, j) => i > j ? i : j); diff --git a/src/utility/lib/functions.js b/src/utility/lib/functions.js index 0943e530..0a934402 100644 --- a/src/utility/lib/functions.js +++ b/src/utility/lib/functions.js @@ -1,4 +1,5 @@ /** + * @Done * @param {number} xMin * @param {number} xMax * @param {number} yMin @@ -50,6 +51,7 @@ function range(min, max, n) { } /** + * @Done * @param {number[]} arr * @return {number} */ @@ -58,6 +60,7 @@ function sum(arr) { } /** + * @Done * @param {number[]} arr * @return {number} */ @@ -66,6 +69,7 @@ function mean(arr) { } /** + * @Done * @param {number[]} arr * @return {number} */ @@ -80,6 +84,7 @@ function min(arr) { } /** + * @Done * @param {number[]} arr * @return {number} */ @@ -133,4 +138,4 @@ module.exports = { random, up, down, -}; \ No newline at end of file +}; From 43746571c9a2529b16593c69f51a51dd6c1a7bed Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Fri, 21 Feb 2020 17:13:04 +0300 Subject: [PATCH 12/32] Step 10 --- src/utility/lib/Functions.re | 3 +++ src/utility/lib/functions.js | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/utility/lib/Functions.re b/src/utility/lib/Functions.re index 94da83b1..d6db7a03 100644 --- a/src/utility/lib/Functions.re +++ b/src/utility/lib/Functions.re @@ -6,8 +6,11 @@ let interpolate = yMin *. minProportion +. yMax *. maxProportion; }; +// @todo: To test! /* https://bucklescript.github.io/bucklescript/api/Belt.html */ let sum = Belt.Array.reduce(_, 0., (i, j) => i +. j); let mean = a => sum(a) /. (Array.length(a) |> float_of_int); let min = Belt.Array.reduce(_, 0., (i, j) => i < j ? i : j); let max = Belt.Array.reduce(_, 0., (i, j) => i > j ? i : j); +let up = (a, b) => Array.make(b - a, a) |> Array.mapi((i, c) => c + i); +let down = (a, b) => Array.make(a - b, a) |> Array.mapi((i, c) => c - i); diff --git a/src/utility/lib/functions.js b/src/utility/lib/functions.js index 0a934402..a234c799 100644 --- a/src/utility/lib/functions.js +++ b/src/utility/lib/functions.js @@ -108,6 +108,7 @@ function random(min, max) { } /** + * @Done * @param {number} from * @param {number} to * @return {number[]} @@ -119,6 +120,7 @@ function up(from, to) { } /** + * @Done * @param {number} from * @param {number} to * @return {number[]} From e55993527ef978aad001c983feea3fea94dc2d73 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 08:20:11 +0300 Subject: [PATCH 13/32] Fixes file link --- __tests__/CDFunctor__Test.re | 18 ++++++------------ src/Index.bs.js | 11 ----------- src/utility/lib/CDFunctor.re | 4 ++-- 3 files changed, 8 insertions(+), 25 deletions(-) delete mode 100644 src/Index.bs.js diff --git a/__tests__/CDFunctor__Test.re b/__tests__/CDFunctor__Test.re index 69e483e3..fef9affb 100644 --- a/__tests__/CDFunctor__Test.re +++ b/__tests__/CDFunctor__Test.re @@ -7,7 +7,7 @@ describe("CDF", () => { expect(() => { module CDF = CDFunctor.Make({ - let shape: DistributionTypes.xyShape = { + let shape: DistTypes.xyShape = { xs: [|10., 4., 8.|], ys: [|8., 9., 2.|], }; @@ -20,7 +20,7 @@ describe("CDF", () => { expect(() => { module CDF = CDFunctor.Make({ - let shape: DistributionTypes.xyShape = { + let shape: DistTypes.xyShape = { xs: [|1., 4., 8.|], ys: [|8., 9., 2.|], }; @@ -32,24 +32,18 @@ describe("CDF", () => { }); test("order#1", () => { let a = CDFunctor.order({xs: [|1., 4., 8.|], ys: [|8., 9., 2.|]}); - let b: DistributionTypes.xyShape = { - 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 = CDFunctor.order({xs: [|10., 5., 12.|], ys: [|8., 9., 2.|]}); - let b: DistributionTypes.xyShape = { - xs: [|5., 10., 12.|], - ys: [|9., 8., 2.|], - }; + let b: DistTypes.xyShape = {xs: [|5., 10., 12.|], ys: [|9., 8., 2.|]}; expect(a) |> toEqual(b); }); test("minX", () => { module CDF = CDFunctor.Make({ - let shape: DistributionTypes.xyShape = + let shape: DistTypes.xyShape = CDFunctor.order({xs: [|20., 4., 8.|], ys: [|8., 9., 2.|]}); }); expect(CDF.minX()) |> toEqual(4.); @@ -57,7 +51,7 @@ describe("CDF", () => { test("maxX", () => { module CDF = CDFunctor.Make({ - let shape: DistributionTypes.xyShape = + let shape: DistTypes.xyShape = CDFunctor.order({xs: [|20., 4., 8.|], ys: [|8., 9., 2.|]}); }); expect(CDF.maxX()) |> toEqual(20.); diff --git a/src/Index.bs.js b/src/Index.bs.js deleted file mode 100644 index d28cff64..00000000 --- a/src/Index.bs.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -var React = require("react"); -var ReactDOMRe = require("reason-react/src/ReactDOMRe.js"); -var App$ProbExample = require("./App.bs.js"); - -((import('./styles/index.css'))); - -ReactDOMRe.renderToElementWithId(React.createElement(App$ProbExample.make, { }), "app"); - -/* Not a pure module */ diff --git a/src/utility/lib/CDFunctor.re b/src/utility/lib/CDFunctor.re index 3db4f574..c9e0a19e 100644 --- a/src/utility/lib/CDFunctor.re +++ b/src/utility/lib/CDFunctor.re @@ -1,8 +1,8 @@ -module type Config = {let shape: DistributionTypes.xyShape;}; +module type Config = {let shape: DistTypes.xyShape;}; exception ShapeWrong(string); -let order = (shape: DistributionTypes.xyShape): DistributionTypes.xyShape => { +let order = (shape: DistTypes.xyShape): DistTypes.xyShape => { let xy = shape.xs |> Array.mapi((i, x) => [x, shape.ys |> Array.get(_, i)]) From 43ef31e7723e41601f4c9c5c6996515a2f83143b Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 09:16:14 +0300 Subject: [PATCH 14/32] Adds a "range" function --- src/utility/lib/Functions.re | 16 +++++++++++++++- src/utility/lib/functions.js | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/utility/lib/Functions.re b/src/utility/lib/Functions.re index d6db7a03..a887be60 100644 --- a/src/utility/lib/Functions.re +++ b/src/utility/lib/Functions.re @@ -1,3 +1,5 @@ +exception RangeWrong(string); + let interpolate = (xMin: float, xMax: float, yMin: float, yMax: float, xIntended: float) : float => { @@ -7,10 +9,22 @@ let interpolate = }; // @todo: To test! -/* https://bucklescript.github.io/bucklescript/api/Belt.html */ let sum = Belt.Array.reduce(_, 0., (i, j) => i +. j); let mean = a => sum(a) /. (Array.length(a) |> float_of_int); let min = Belt.Array.reduce(_, 0., (i, j) => i < j ? i : j); let max = Belt.Array.reduce(_, 0., (i, j) => i > j ? i : j); let up = (a, b) => Array.make(b - a, a) |> Array.mapi((i, c) => c + i); let down = (a, b) => Array.make(a - b, a) |> Array.mapi((i, c) => c - i); +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.Array.makeBy(n, i => float_of_int(i) *. diff); + }; +}; diff --git a/src/utility/lib/functions.js b/src/utility/lib/functions.js index a234c799..a4353e52 100644 --- a/src/utility/lib/functions.js +++ b/src/utility/lib/functions.js @@ -14,6 +14,7 @@ function interpolate(xMin, xMax, yMin, yMax, xIntended) { } /** + * @Done * This should return an array of n evenly-spaced items * between min and max, including min and max. * range(1,5,3) = [1, 3, 5]; From d3bc12ddd0947843671d415ae042054292a01554 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 10:01:40 +0300 Subject: [PATCH 15/32] Adds tests --- __tests__/Functions__Test.re | 44 ++++++++++++++++++++++++++++++++++++ src/utility/lib/Functions.re | 5 ++-- src/utility/lib/functions.js | 1 + 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 __tests__/Functions__Test.re diff --git a/__tests__/Functions__Test.re b/__tests__/Functions__Test.re new file mode 100644 index 00000000..874a143d --- /dev/null +++ b/__tests__/Functions__Test.re @@ -0,0 +1,44 @@ +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.|]) + }); +}); diff --git a/src/utility/lib/Functions.re b/src/utility/lib/Functions.re index a887be60..a2e4ba66 100644 --- a/src/utility/lib/Functions.re +++ b/src/utility/lib/Functions.re @@ -24,7 +24,8 @@ let range = (min: float, max: float, n: int): array(float) => { | _ 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.Array.makeBy(n, i => float_of_int(i) *. diff); + 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; diff --git a/src/utility/lib/functions.js b/src/utility/lib/functions.js index a4353e52..c8c6fbfa 100644 --- a/src/utility/lib/functions.js +++ b/src/utility/lib/functions.js @@ -100,6 +100,7 @@ function max(arr) { } /** + * @Done * @param {number} min * @param {number} max * @return {number} From fb4bc523247ec5dbb74a3e5d8c6122427b5f3ffb Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 10:04:12 +0300 Subject: [PATCH 16/32] Adds tests (2) --- __tests__/Functions__Test.re | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/__tests__/Functions__Test.re b/__tests__/Functions__Test.re index 874a143d..80c42fc7 100644 --- a/__tests__/Functions__Test.re +++ b/__tests__/Functions__Test.re @@ -41,4 +41,13 @@ describe("Functions", () => { 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) + }); }); From f553fb742e920f60b13c68aaf5327864e2741c25 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 10:10:21 +0300 Subject: [PATCH 17/32] Adds tests (3) --- __tests__/Functions__Test.re | 10 ++++++++++ src/utility/lib/Functions.re | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/__tests__/Functions__Test.re b/__tests__/Functions__Test.re index 80c42fc7..a0500e00 100644 --- a/__tests__/Functions__Test.re +++ b/__tests__/Functions__Test.re @@ -50,4 +50,14 @@ describe("Functions", () => { 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) + }); }); diff --git a/src/utility/lib/Functions.re b/src/utility/lib/Functions.re index a2e4ba66..0f310966 100644 --- a/src/utility/lib/Functions.re +++ b/src/utility/lib/Functions.re @@ -11,7 +11,7 @@ let interpolate = // @todo: To test! let sum = Belt.Array.reduce(_, 0., (i, j) => i +. j); let mean = a => sum(a) /. (Array.length(a) |> float_of_int); -let min = Belt.Array.reduce(_, 0., (i, j) => i < j ? i : j); +let min = a => Belt.Array.reduce(a, a[0], (i, j) => i < j ? i : j); let max = Belt.Array.reduce(_, 0., (i, j) => i > j ? i : j); let up = (a, b) => Array.make(b - a, a) |> Array.mapi((i, c) => c + i); let down = (a, b) => Array.make(a - b, a) |> Array.mapi((i, c) => c - i); From 822a803d01f4cf6e2a24cb6d0f02fe368afc5bda Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 10:38:08 +0300 Subject: [PATCH 18/32] Adds tests (4) --- __tests__/Functions__Test.re | 9 +++++++++ src/utility/lib/Functions.re | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/__tests__/Functions__Test.re b/__tests__/Functions__Test.re index a0500e00..d5cc0d03 100644 --- a/__tests__/Functions__Test.re +++ b/__tests__/Functions__Test.re @@ -60,4 +60,13 @@ describe("Functions", () => { 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.) + }); }); diff --git a/src/utility/lib/Functions.re b/src/utility/lib/Functions.re index 0f310966..4600f237 100644 --- a/src/utility/lib/Functions.re +++ b/src/utility/lib/Functions.re @@ -12,7 +12,7 @@ let interpolate = 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 = Belt.Array.reduce(_, 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, a) |> Array.mapi((i, c) => c + i); let down = (a, b) => Array.make(a - b, a) |> Array.mapi((i, c) => c - i); let range = (min: float, max: float, n: int): array(float) => { From 790791eab1dc1b22e9f26003e48b003ee39d5736 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 10:59:01 +0300 Subject: [PATCH 19/32] Adds tests (5) --- __tests__/Functions__Test.re | 18 ++++++++++++++++++ src/utility/lib/Functions.re | 5 +++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/__tests__/Functions__Test.re b/__tests__/Functions__Test.re index d5cc0d03..d32f44e8 100644 --- a/__tests__/Functions__Test.re +++ b/__tests__/Functions__Test.re @@ -69,4 +69,22 @@ describe("Functions", () => { 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)|]) + }); }); diff --git a/src/utility/lib/Functions.re b/src/utility/lib/Functions.re index 4600f237..26a8bf20 100644 --- a/src/utility/lib/Functions.re +++ b/src/utility/lib/Functions.re @@ -13,8 +13,9 @@ 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, a) |> Array.mapi((i, c) => c + i); -let down = (a, b) => Array.make(a - b, a) |> Array.mapi((i, c) => c - i); +let up = (a, b) => Array.make(b - a + 1, a) |> Array.mapi((i, c) => c + i); +let down = (a, b) => + Array.make(a - b + 1, a) |> Array.mapi((i, c) => c - i); let range = (min: float, max: float, n: int): array(float) => { switch (n) { | 0 => [||] From f9b04a1ca688de893993877d2452659ac94016de Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 11:29:28 +0300 Subject: [PATCH 20/32] FindY --- src/utility/lib/CDFunctor.re | 40 +++++++++++++++++++---- src/utility/lib/continuousDistribution.js | 2 ++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/utility/lib/CDFunctor.re b/src/utility/lib/CDFunctor.re index c9e0a19e..588fa8e4 100644 --- a/src/utility/lib/CDFunctor.re +++ b/src/utility/lib/CDFunctor.re @@ -14,20 +14,46 @@ let order = (shape: DistTypes.xyShape): DistTypes.xyShape => { }; module Make = (Config: Config) => { - let validateHasLength = (): bool => Array.length(Config.shape.xs) > 0; - let validateSize = (): bool => - Array.length(Config.shape.xs) == Array.length(Config.shape.ys); + let xs = Config.shape.xs; + let ys = Config.shape.ys; + let get = Array.get; + + let validateHasLength = (): bool => Array.length(xs) > 0; + let validateSize = (): bool => Array.length(xs) == Array.length(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(Config.shape.xs, (a, b) => a > b ? 1 : (-1))) { + if (!Belt.SortArray.isSorted(xs, (a, b) => a > b ? 1 : (-1))) { raise(ShapeWrong("Arrays of \"xs\" and \"ys\" have different sizes.")); }; - let minX = () => Config.shape.xs |> Array.get(_, 0); - let maxX = () => - Config.shape.xs |> Array.get(_, Array.length(Config.shape.xs) - 1); + let minX = () => xs |> get(_, 0); + let maxX = () => xs |> get(_, Array.length(xs) - 1); + let minY = () => ys |> get(_, 0); + let maxY = () => ys |> get(_, Array.length(ys) - 1); + let findY = x => { + let firstHigherIndex = Belt.Array.getIndexBy(xs, e => e > x); + switch (firstHigherIndex) { + | None => maxY() + | Some(1) => minY() + | Some(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]; + }; + }; + }; 1; }; diff --git a/src/utility/lib/continuousDistribution.js b/src/utility/lib/continuousDistribution.js index a1eb3117..11495891 100644 --- a/src/utility/lib/continuousDistribution.js +++ b/src/utility/lib/continuousDistribution.js @@ -86,8 +86,10 @@ class ContinuousDistribution { let firstHigherIndex = this.xs.findIndex(X => X >= x); if (firstHigherIndex < 0) return this.ys[this.ys.length - 1]; if (firstHigherIndex === 0) return this.ys[0]; + let lowerOrEqualIndex = firstHigherIndex - 1; if (lowerOrEqualIndex < 0) lowerOrEqualIndex = 0; + let needsInterpolation = this.xs[lowerOrEqualIndex] !== x; if (needsInterpolation) { return interpolate( From aafccda8a16868e350e10feaa4ed3cc3dfcb164e Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 11:30:45 +0300 Subject: [PATCH 21/32] FindY --- src/utility/lib/CDFunctor.re | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/utility/lib/CDFunctor.re b/src/utility/lib/CDFunctor.re index 588fa8e4..94a34331 100644 --- a/src/utility/lib/CDFunctor.re +++ b/src/utility/lib/CDFunctor.re @@ -17,9 +17,10 @@ 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 => Array.length(xs) > 0; - let validateSize = (): bool => Array.length(xs) == Array.length(ys); + let validateHasLength = (): bool => len(xs) > 0; + let validateSize = (): bool => len(xs) == len(ys); if (!validateHasLength()) { raise(ShapeWrong("You need at least one element.")); }; @@ -29,10 +30,10 @@ module Make = (Config: Config) => { if (!Belt.SortArray.isSorted(xs, (a, b) => a > b ? 1 : (-1))) { raise(ShapeWrong("Arrays of \"xs\" and \"ys\" have different sizes.")); }; - let minX = () => xs |> get(_, 0); - let maxX = () => xs |> get(_, Array.length(xs) - 1); - let minY = () => ys |> get(_, 0); - let maxY = () => ys |> get(_, Array.length(ys) - 1); + 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 => { let firstHigherIndex = Belt.Array.getIndexBy(xs, e => e > x); switch (firstHigherIndex) { From ee3816fae4976c26afadbfb090251549b2549272 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 11:36:13 +0300 Subject: [PATCH 22/32] Findx --- src/utility/lib/CDFunctor.re | 26 +++++++++++++++++++++-- src/utility/lib/continuousDistribution.js | 2 ++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/utility/lib/CDFunctor.re b/src/utility/lib/CDFunctor.re index 94a34331..11bd6c77 100644 --- a/src/utility/lib/CDFunctor.re +++ b/src/utility/lib/CDFunctor.re @@ -34,8 +34,8 @@ module Make = (Config: Config) => { let maxX = () => get(xs, len(xs) - 1); let minY = () => get(ys, 0); let maxY = () => get(ys, len(ys) - 1); - let findY = x => { - let firstHigherIndex = Belt.Array.getIndexBy(xs, e => e > x); + let findY = (x: float): float => { + let firstHigherIndex = Belt.Array.getIndexBy(xs, e => e >= x); switch (firstHigherIndex) { | None => maxY() | Some(1) => minY() @@ -56,5 +56,27 @@ module Make = (Config: Config) => { }; }; }; + let findX = (y: float): float => { + let firstHigherIndex = Belt.Array.getIndexBy(ys, e => e >= y); + switch (firstHigherIndex) { + | None => maxX() + | Some(1) => minX() + | Some(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]; + }; + }; + }; 1; }; diff --git a/src/utility/lib/continuousDistribution.js b/src/utility/lib/continuousDistribution.js index 11495891..75355a36 100644 --- a/src/utility/lib/continuousDistribution.js +++ b/src/utility/lib/continuousDistribution.js @@ -77,6 +77,7 @@ class ContinuousDistribution { } /** + * @Done * If xs=[1,2,3], and ys=[5,6,7], * then findY(1) = 5, findY(3) = 7, findY(1.5) = 5.5 * @param {number} x @@ -105,6 +106,7 @@ class ContinuousDistribution { } /** + * @Done * If xs=[1,2,3], and ys=[5,6,7], * then findX(5) = 1, findX(7) = 3, findY(5.5) = 1.5 * This should do the same thing as `findY`, but for Y. From efe3c67a2518236483b812eac891a16ba6767df5 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 11:39:42 +0300 Subject: [PATCH 23/32] Renames CDFunctor --- .../{CDFunctor__Test.re => CDF__Test.re} | 30 +++++++++---------- src/utility/lib/{CDFunctor.re => CDF.re} | 0 2 files changed, 14 insertions(+), 16 deletions(-) rename __tests__/{CDFunctor__Test.re => CDF__Test.re} (59%) rename src/utility/lib/{CDFunctor.re => CDF.re} (100%) diff --git a/__tests__/CDFunctor__Test.re b/__tests__/CDF__Test.re similarity index 59% rename from __tests__/CDFunctor__Test.re rename to __tests__/CDF__Test.re index fef9affb..4dd926cf 100644 --- a/__tests__/CDFunctor__Test.re +++ b/__tests__/CDF__Test.re @@ -5,8 +5,8 @@ exception ShapeWrong(string); describe("CDF", () => { test("raise - w/o order", () => { expect(() => { - module CDF = - CDFunctor.Make({ + module Cdf = + CDF.Make({ let shape: DistTypes.xyShape = { xs: [|10., 4., 8.|], ys: [|8., 9., 2.|], @@ -18,8 +18,8 @@ describe("CDF", () => { }); test("raise - with order", () => { expect(() => { - module CDF = - CDFunctor.Make({ + module Cdf = + CDF.Make({ let shape: DistTypes.xyShape = { xs: [|1., 4., 8.|], ys: [|8., 9., 2.|], @@ -31,29 +31,27 @@ describe("CDF", () => { |> toThrow }); test("order#1", () => { - let a = CDFunctor.order({xs: [|1., 4., 8.|], ys: [|8., 9., 2.|]}); + 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 = CDFunctor.order({xs: [|10., 5., 12.|], ys: [|8., 9., 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); }); test("minX", () => { - module CDF = - CDFunctor.Make({ - let shape: DistTypes.xyShape = - CDFunctor.order({xs: [|20., 4., 8.|], ys: [|8., 9., 2.|]}); + module Dist = + CDF.Make({ + let shape = CDF.order({xs: [|20., 4., 8.|], ys: [|8., 9., 2.|]}); }); - expect(CDF.minX()) |> toEqual(4.); + expect(Dist.minX()) |> toEqual(4.); }); test("maxX", () => { - module CDF = - CDFunctor.Make({ - let shape: DistTypes.xyShape = - CDFunctor.order({xs: [|20., 4., 8.|], ys: [|8., 9., 2.|]}); + module Dist = + CDF.Make({ + let shape = CDF.order({xs: [|20., 4., 8.|], ys: [|8., 9., 2.|]}); }); - expect(CDF.maxX()) |> toEqual(20.); + expect(Dist.maxX()) |> toEqual(20.); }); }); diff --git a/src/utility/lib/CDFunctor.re b/src/utility/lib/CDF.re similarity index 100% rename from src/utility/lib/CDFunctor.re rename to src/utility/lib/CDF.re From 80a52a2a7b56ab0d572c423ecd89338e2719317e Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 11:45:44 +0300 Subject: [PATCH 24/32] FindY tests --- __tests__/CDF__Test.re | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/__tests__/CDF__Test.re b/__tests__/CDF__Test.re index 4dd926cf..1d318118 100644 --- a/__tests__/CDF__Test.re +++ b/__tests__/CDF__Test.re @@ -54,4 +54,18 @@ describe("CDF", () => { }); expect(Dist.maxX()) |> toEqual(20.); }); + test("findY#1", () => { + module Dist = + CDF.Make({ + let shape = CDF.order({xs: [|1., 2., 3.|], ys: [|5., 6., 7.|]}); + }); + expect(Dist.findY(1.)) |> toEqual(5.); + }); + test("findY#2", () => { + module Dist = + CDF.Make({ + let shape = CDF.order({xs: [|1., 2., 3.|], ys: [|5., 6., 7.|]}); + }); + expect(Dist.findY(1.5)) |> toEqual(5.5); + }); }); From 94a9712788ba1f4f13bc1518d8f8aa20631e0456 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 11:57:14 +0300 Subject: [PATCH 25/32] FindY tests (2) --- __tests__/CDF__Test.re | 46 +++++++++++++++++++++++++----------------- src/utility/lib/CDF.re | 4 ++-- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/__tests__/CDF__Test.re b/__tests__/CDF__Test.re index 1d318118..cf271f94 100644 --- a/__tests__/CDF__Test.re +++ b/__tests__/CDF__Test.re @@ -40,32 +40,42 @@ describe("CDF", () => { let b: DistTypes.xyShape = {xs: [|5., 10., 12.|], ys: [|9., 8., 2.|]}; expect(a) |> toEqual(b); }); - test("minX", () => { + + describe("minX - maxX", () => { module Dist = CDF.Make({ let shape = CDF.order({xs: [|20., 4., 8.|], ys: [|8., 9., 2.|]}); }); - expect(Dist.minX()) |> toEqual(4.); + test("minX", () => { + expect(Dist.minX()) |> toEqual(4.) + }); + test("maxX", () => { + expect(Dist.maxX()) |> toEqual(20.) + }); }); - test("maxX", () => { - module Dist = - CDF.Make({ - let shape = CDF.order({xs: [|20., 4., 8.|], ys: [|8., 9., 2.|]}); - }); - expect(Dist.maxX()) |> toEqual(20.); - }); - test("findY#1", () => { + + describe("findY", () => { module Dist = CDF.Make({ let shape = CDF.order({xs: [|1., 2., 3.|], ys: [|5., 6., 7.|]}); }); - expect(Dist.findY(1.)) |> toEqual(5.); - }); - test("findY#2", () => { - module Dist = - CDF.Make({ - let shape = CDF.order({xs: [|1., 2., 3.|], ys: [|5., 6., 7.|]}); - }); - expect(Dist.findY(1.5)) |> toEqual(5.5); + test("findY#1", () => { + expect(Dist.findY(1.)) |> toEqual(5.) + }); + test("findY#2", () => { + expect(Dist.findY(1.5)) |> toEqual(5.5) + }); + test("findY#3", () => { + expect(Dist.findY(3.)) |> toEqual(7.) + }); + test("findY#4", () => { + expect(Dist.findY(4.)) |> toEqual(7.) + }); + test("findY#5", () => { + expect(Dist.findY(15.)) |> toEqual(7.) + }); + test("findY#6", () => { + expect(Dist.findY(-1.)) |> toEqual(5.) + }); }); }); diff --git a/src/utility/lib/CDF.re b/src/utility/lib/CDF.re index 11bd6c77..09095ae2 100644 --- a/src/utility/lib/CDF.re +++ b/src/utility/lib/CDF.re @@ -38,7 +38,7 @@ module Make = (Config: Config) => { let firstHigherIndex = Belt.Array.getIndexBy(xs, e => e >= x); switch (firstHigherIndex) { | None => maxY() - | Some(1) => minY() + | Some(0) => minY() | Some(firstHigherIndex) => let lowerOrEqualIndex = firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1; @@ -60,7 +60,7 @@ module Make = (Config: Config) => { let firstHigherIndex = Belt.Array.getIndexBy(ys, e => e >= y); switch (firstHigherIndex) { | None => maxX() - | Some(1) => minX() + | Some(0) => minX() | Some(firstHigherIndex) => let lowerOrEqualIndex = firstHigherIndex - 1 < 0 ? 0 : firstHigherIndex - 1; From 188266546d26f0df1a0f58dda33f0ced3620c18a Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 12:04:26 +0300 Subject: [PATCH 26/32] FindX tests --- __tests__/CDF__Test.re | 34 +++++++++++++++++++---- src/utility/lib/continuousDistribution.js | 2 ++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/__tests__/CDF__Test.re b/__tests__/CDF__Test.re index cf271f94..66c35fdf 100644 --- a/__tests__/CDF__Test.re +++ b/__tests__/CDF__Test.re @@ -59,23 +59,45 @@ describe("CDF", () => { CDF.Make({ let shape = CDF.order({xs: [|1., 2., 3.|], ys: [|5., 6., 7.|]}); }); - test("findY#1", () => { + test("#1", () => { expect(Dist.findY(1.)) |> toEqual(5.) }); - test("findY#2", () => { + test("#2", () => { expect(Dist.findY(1.5)) |> toEqual(5.5) }); - test("findY#3", () => { + test("#3", () => { expect(Dist.findY(3.)) |> toEqual(7.) }); - test("findY#4", () => { + test("#4", () => { expect(Dist.findY(4.)) |> toEqual(7.) }); - test("findY#5", () => { + test("#5", () => { expect(Dist.findY(15.)) |> toEqual(7.) }); - test("findY#6", () => { + 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.) + }); + }); }); diff --git a/src/utility/lib/continuousDistribution.js b/src/utility/lib/continuousDistribution.js index 75355a36..1e639398 100644 --- a/src/utility/lib/continuousDistribution.js +++ b/src/utility/lib/continuousDistribution.js @@ -117,8 +117,10 @@ class ContinuousDistribution { let firstHigherIndex = this.ys.findIndex(Y => Y >= y); if (firstHigherIndex < 0) return this.xs[this.xs.length - 1]; if (firstHigherIndex === 0) return this.xs[0]; + let lowerOrEqualIndex = firstHigherIndex - 1; if (lowerOrEqualIndex < 0) lowerOrEqualIndex = 0; + let needsInterpolation = this.ys[lowerOrEqualIndex] !== y; if (needsInterpolation) { return interpolate( From 946e2dad7eeb5017c1f5dcae08d7e6bf24a38030 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 12:45:41 +0300 Subject: [PATCH 27/32] Adds tests --- __tests__/CDF__Test.re | 43 +++++++++++++++++++++++ __tests__/Functions__Test.re | 10 +++--- src/utility/lib/CDF.re | 12 +++++++ src/utility/lib/Functions.re | 9 +++-- src/utility/lib/continuousDistribution.js | 4 +++ 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/__tests__/CDF__Test.re b/__tests__/CDF__Test.re index 66c35fdf..33ad8700 100644 --- a/__tests__/CDF__Test.re +++ b/__tests__/CDF__Test.re @@ -100,4 +100,47 @@ describe("CDF", () => { 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.|]) + }); + }); }); diff --git a/__tests__/Functions__Test.re b/__tests__/Functions__Test.re index d32f44e8..5dda5e3d 100644 --- a/__tests__/Functions__Test.re +++ b/__tests__/Functions__Test.re @@ -76,15 +76,17 @@ describe("Functions", () => { expect(Functions.random(1, 5)) |> toBeGreaterThanOrEqual(1) }); test("up#1", () => { - expect(Functions.up(1, 5)) |> toEqual([|1, 2, 3, 4, 5|]) + 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|]) + 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|]) + 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)|]) + expect(Functions.down(5, -1)) + |> toEqual([|5., 4., 3., 2., 1., 0., (-1.)|]) }); }); diff --git a/src/utility/lib/CDF.re b/src/utility/lib/CDF.re index 09095ae2..b5f9221e 100644 --- a/src/utility/lib/CDF.re +++ b/src/utility/lib/CDF.re @@ -78,5 +78,17 @@ module Make = (Config: Config) => { }; }; }; + 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()); 1; }; diff --git a/src/utility/lib/Functions.re b/src/utility/lib/Functions.re index 26a8bf20..998084cc 100644 --- a/src/utility/lib/Functions.re +++ b/src/utility/lib/Functions.re @@ -13,9 +13,14 @@ 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); +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); + 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 => [||] diff --git a/src/utility/lib/continuousDistribution.js b/src/utility/lib/continuousDistribution.js index 1e639398..bd777908 100644 --- a/src/utility/lib/continuousDistribution.js +++ b/src/utility/lib/continuousDistribution.js @@ -136,6 +136,7 @@ class ContinuousDistribution { } /** + * @Done * @param {number[]} xs * @return {ContinuousDistribution} */ @@ -145,6 +146,7 @@ class ContinuousDistribution { } /** + * Done * @param {number} newLength * @return {ContinuousDistribution} */ @@ -154,6 +156,7 @@ class ContinuousDistribution { } /** + * @Done * @return {number} */ sampleSingle() { @@ -162,6 +165,7 @@ class ContinuousDistribution { } /** + * @Done * Poduce n samples, using ``sampleSingle`` for each. * @param size * @return {number[]} From c97c6756428ba3bd75a3618ad83a80d74e929f75 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 12:52:36 +0300 Subject: [PATCH 28/32] Adds tests --- __tests__/CDF__Test.re | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/__tests__/CDF__Test.re b/__tests__/CDF__Test.re index 33ad8700..f5605b68 100644 --- a/__tests__/CDF__Test.re +++ b/__tests__/CDF__Test.re @@ -143,4 +143,26 @@ describe("CDF", () => { expect(Dist2.ys) |> toEqual([|50., 54., 58.|]) }); }); + + // @todo + 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.) + }); + }); }); From 4a88acae66c8e4dd45ede015d90a782698473dc5 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 13:08:16 +0300 Subject: [PATCH 29/32] Adds integral function --- __tests__/CDF__Test.re | 11 +++++++++++ src/utility/lib/CDF.re | 15 ++++++++++++++- src/utility/lib/continuousDistribution.spec.js | 16 ++++++++-------- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/__tests__/CDF__Test.re b/__tests__/CDF__Test.re index f5605b68..cfe74694 100644 --- a/__tests__/CDF__Test.re +++ b/__tests__/CDF__Test.re @@ -165,4 +165,15 @@ describe("CDF", () => { 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.) + }); + }); }); diff --git a/src/utility/lib/CDF.re b/src/utility/lib/CDF.re index b5f9221e..cbfefa6c 100644 --- a/src/utility/lib/CDF.re +++ b/src/utility/lib/CDF.re @@ -90,5 +90,18 @@ module Make = (Config: Config) => { let sampleSingle = (): float => Js.Math.random() |> findY; let sample = (size: int): array(float) => Belt.Array.makeBy(size, i => sampleSingle()); - 1; + 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; + } + }); + }; }; diff --git a/src/utility/lib/continuousDistribution.spec.js b/src/utility/lib/continuousDistribution.spec.js index 75831fb5..114caf58 100644 --- a/src/utility/lib/continuousDistribution.spec.js +++ b/src/utility/lib/continuousDistribution.spec.js @@ -66,45 +66,45 @@ describe('ContinuousDistribution Class', () => { describe('integral()', () => { it('with regular inputs', () => { - const xs = [0,1,2,4]; + const xs = [0, 1, 2, 4]; const ys = [0.0, 1.0, 2.0, 2.0]; const cdf = new ContinuousDistribution(xs, ys); const integral = cdf.integral(); expect(integral).toEqual(6); }); it('with an infinity', () => { - const xs = [0,1,2,4]; + const xs = [0, 1, 2, 4]; const ys = [0.0, 1.0, Infinity, 2.0]; const cdf = new ContinuousDistribution(xs, ys); const integral = cdf.integral(); expect(integral).toEqual(Infinity); }); it('with negative infinity', () => { - const xs = [0,1,2,4]; + const xs = [0, 1, 2, 4]; const ys = [0.0, 1.0, -Infinity, 2.0]; const cdf = new ContinuousDistribution(xs, ys); const integral = cdf.integral(); expect(integral).toEqual(-Infinity); }); it('with both positive and negative infinities', () => { - const xs = [0,1,2,4]; + const xs = [0, 1, 2, 4]; const ys = [0.0, 1.0, -Infinity, Infinity]; const cdf = new ContinuousDistribution(xs, ys); const integral = cdf.integral(); expect(integral).toEqual(NaN); }); it('with a NaN and filterOutNaNs set to false', () => { - const xs = [0,1,2,4]; + const xs = [0, 1, 2, 4]; const ys = [0.0, 1.0, 2.0, NaN]; const cdf = new ContinuousDistribution(xs, ys); - const integral = cdf.integral({filterOutNaNs: false}); + const integral = cdf.integral({ filterOutNaNs: false }); expect(integral).toEqual(NaN); }); it('with a NaN and filterOutNaNs set to true', () => { - const xs = [0,1,2,4]; + const xs = [0, 1, 2, 4]; const ys = [0.0, 1.0, 2.0, NaN]; const cdf = new ContinuousDistribution(xs, ys); - const integral = cdf.integral({filterOutNaNs: true}); + const integral = cdf.integral({ filterOutNaNs: true }); expect(integral).toEqual(2); }); }) From 4fc7723e32d73ab98592d8569b4354d142280436 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 13:10:32 +0300 Subject: [PATCH 30/32] Adds integral function --- __tests__/CDF__Test.re | 2 +- src/utility/lib/continuousDistribution.js | 214 ------------------ .../lib/continuousDistribution.spec.js | 111 --------- src/utility/lib/functions.js | 145 ------------ src/utility/lib/functions.spec.js | 55 ----- 5 files changed, 1 insertion(+), 526 deletions(-) delete mode 100644 src/utility/lib/continuousDistribution.js delete mode 100644 src/utility/lib/continuousDistribution.spec.js delete mode 100644 src/utility/lib/functions.js delete mode 100644 src/utility/lib/functions.spec.js diff --git a/__tests__/CDF__Test.re b/__tests__/CDF__Test.re index cfe74694..68d0bd37 100644 --- a/__tests__/CDF__Test.re +++ b/__tests__/CDF__Test.re @@ -144,7 +144,7 @@ describe("CDF", () => { }); }); - // @todo + // @todo: Should each test expect 70.? describe("sample", () => { open Functions; let xs = up(1, 9); diff --git a/src/utility/lib/continuousDistribution.js b/src/utility/lib/continuousDistribution.js deleted file mode 100644 index bd777908..00000000 --- a/src/utility/lib/continuousDistribution.js +++ /dev/null @@ -1,214 +0,0 @@ -const _ = require('lodash'); -const { interpolate, range, min, max } = require('./functions'); - -class ContinuousDistribution { - /** - * @Done - * @param {number[]} xs - * @param {number[]} ys - */ - constructor(xs, ys) { - if (!this.validateHasLength(xs)) { - throw new Error('You need at least one element.'); - } - if (!this.validateSize(xs, ys)) { - throw new Error('Arrays of "xs" and "ys" have different sizes.'); - } - - const sorted = this.order(xs, ys); - this.xs = sorted.xs; - this.ys = sorted.ys; - } - - /** - * @Done - * Order them to make sure that xs are increasing - * @param {number[]} xs - * @param {number[]} ys - * @return {{ys: number[], xs: number[]}} - */ - order(xs, ys) { - const xsYs = xs.map((v, i) => ({ ys: ys[i], xs: v })); - const sorted = xsYs.sort((a, b) => { - if (a.xs > b.xs) return 1; - if (a.xs < b.xs) return -1; - return 0; - }); - - const XS = sorted.map(v => v.xs); - const YS = sorted.map(v => v.ys); - - return { xs: XS, ys: YS }; - } - - /** - * @Done - * @param {number[]} xs - * @param {number[]} ys - * @return {boolean} - */ - validateSize(xs, ys) { - return xs.length === ys.length; - } - - /** - * @Done - * @param xs - * @returns {boolean} - */ - validateHasLength(xs) { - return xs.length > 0; - } - - /** - * @Done - * @returns {number} - */ - minX() { - return this.xs[0] - } - - /** - * @Done - * @returns {number} - */ - maxX() { - return this.xs[this.xs.length - 1] - } - - /** - * @Done - * If xs=[1,2,3], and ys=[5,6,7], - * then findY(1) = 5, findY(3) = 7, findY(1.5) = 5.5 - * @param {number} x - * @return {number} - */ - findY(x) { - let firstHigherIndex = this.xs.findIndex(X => X >= x); - if (firstHigherIndex < 0) return this.ys[this.ys.length - 1]; - if (firstHigherIndex === 0) return this.ys[0]; - - let lowerOrEqualIndex = firstHigherIndex - 1; - if (lowerOrEqualIndex < 0) lowerOrEqualIndex = 0; - - let needsInterpolation = this.xs[lowerOrEqualIndex] !== x; - if (needsInterpolation) { - return interpolate( - this.xs[lowerOrEqualIndex], - this.xs[firstHigherIndex], - this.ys[lowerOrEqualIndex], - this.ys[firstHigherIndex], - x - ); - } else { - return this.ys[lowerOrEqualIndex]; - } - } - - /** - * @Done - * If xs=[1,2,3], and ys=[5,6,7], - * then findX(5) = 1, findX(7) = 3, findY(5.5) = 1.5 - * This should do the same thing as `findY`, but for Y. - * @param {number} y - * @return {number} - */ - findX(y) { - let firstHigherIndex = this.ys.findIndex(Y => Y >= y); - if (firstHigherIndex < 0) return this.xs[this.xs.length - 1]; - if (firstHigherIndex === 0) return this.xs[0]; - - let lowerOrEqualIndex = firstHigherIndex - 1; - if (lowerOrEqualIndex < 0) lowerOrEqualIndex = 0; - - let needsInterpolation = this.ys[lowerOrEqualIndex] !== y; - if (needsInterpolation) { - return interpolate( - this.ys[lowerOrEqualIndex], - this.ys[firstHigherIndex], - this.xs[lowerOrEqualIndex], - this.xs[firstHigherIndex], - y - ); - } else { - return this.xs[lowerOrEqualIndex]; - } - } - - /** - * @Done - * @param {number[]} xs - * @return {ContinuousDistribution} - */ - convertWithAlternativeXs(xs) { - const ys = xs.map(x => this.findY(x)); - return new ContinuousDistribution(xs, ys); - } - - /** - * Done - * @param {number} newLength - * @return {ContinuousDistribution} - */ - convertToNewLength(newLength) { - const _range = range(min(this.xs), max(this.xs), newLength); - return this.convertWithAlternativeXs(_range); - } - - /** - * @Done - * @return {number} - */ - sampleSingle() { - const y = Math.random(); - return this.findX(y); - } - - /** - * @Done - * Poduce n samples, using ``sampleSingle`` for each. - * @param size - * @return {number[]} - */ - sample(size) { - return Array.from(Array(size), () => this.sampleSingle()); - } - - /** - * Finds the integral. Takes the average Y value between points, - * treating them like a triangle. - * @return {number[]} - */ - integral(params = { filterOutNaNs: false }) { - let integral = 0; - if (!params.filterOutNaNs && _.includes(this.ys, NaN)) { - return NaN; - } else if (_.includes(this.ys, Infinity) && _.includes(this.ys, -Infinity)) { - return NaN; - } else if (_.includes(this.ys, Infinity)) { - return Infinity; - } else if (_.includes(this.ys, -Infinity)) { - return -Infinity; - } - for (let i = 1; i < this.ys.length; i++) { - let thisY = this.ys[i]; - let lastY = this.ys[i - 1]; - let thisX = this.xs[i]; - let lastX = this.xs[i - 1]; - - if ( - _.isFinite(thisY) && _.isFinite(lastY) && - _.isFinite(thisX) && _.isFinite(lastX) - ) { - let sectionInterval = ((thisY + lastY) / 2) * (thisX - lastX); - integral = integral + sectionInterval; - } - - } - return integral; - } -} - -module.exports = { - ContinuousDistribution, -}; diff --git a/src/utility/lib/continuousDistribution.spec.js b/src/utility/lib/continuousDistribution.spec.js deleted file mode 100644 index 114caf58..00000000 --- a/src/utility/lib/continuousDistribution.spec.js +++ /dev/null @@ -1,111 +0,0 @@ -const { ContinuousDistribution } = require('./continuousDistribution'); -const { up, down } = require('./functions'); - -describe('ContinuousDistribution Class', () => { - it('constructor()', () => { - const xs = up(1, 9); - const ys = up(1, 8); - expect(() => { - new ContinuousDistribution(xs, ys); - }).toThrow(/^Arrays of "xs" and "ys" have different sizes.$/); - }); - it('order()', () => { - const xs = down(9, 1); - const ys = down(9, 1); - const cdf = new ContinuousDistribution(xs, ys); - expect(cdf.xs).toEqual(up(1, 9)); - expect(cdf.ys).toEqual(up(1, 9)); - }); - it('findY()', () => { - const xs = [1, 2, 3]; - const ys = [5, 6, 7]; - const cdf = new ContinuousDistribution(xs, ys); - expect(cdf.findY(1)).toEqual(5); - expect(cdf.findY(1.5)).toEqual(5.5); - expect(cdf.findY(3)).toEqual(7); - expect(cdf.findY(4)).toEqual(7); - expect(cdf.findY(15)).toEqual(7); - expect(cdf.findY(-1)).toEqual(5); - }); - it('findX()', () => { - const xs = [1, 2, 3]; - const ys = [5, 6, 7]; - const cdf = new ContinuousDistribution(xs, ys); - expect(cdf.findX(5)).toEqual(1); - expect(cdf.findX(7)).toEqual(3); - expect(cdf.findX(5.5)).toEqual(1.5); - expect(cdf.findX(8)).toEqual(3); - expect(cdf.findX(4)).toEqual(1); - }); - it('convertWithAlternativeXs() when "XS" within "xs"', () => { - const xs = up(1, 9); - const ys = up(20, 28); - const cdf = new ContinuousDistribution(xs, ys); - const XS = up(3, 7); - const CDF = cdf.convertWithAlternativeXs(XS); - expect(CDF.xs).toEqual([3, 4, 5, 6, 7]); - expect(CDF.ys).toEqual([22, 23, 24, 25, 26]); - }); - it('convertToNewLength()', () => { - const xs = up(1, 9); - const ys = up(50, 58); - const cdf = new ContinuousDistribution(xs, ys); - const CDF = cdf.convertToNewLength(3); - expect(CDF.xs).toEqual([1, 5, 9]); - expect(CDF.ys).toEqual([50, 54, 58]); - }); - it('sample()', () => { - const xs = up(1, 9); - const ys = up(70, 78); - const cdf = new ContinuousDistribution(xs, ys); - const XS = cdf.sample(3); - expect(Number.isInteger(XS[0])).toBe(true); - expect(Number.isInteger(XS[1])).toBe(true); - expect(Number.isInteger(XS[2])).toBe(true); - }); - - describe('integral()', () => { - it('with regular inputs', () => { - const xs = [0, 1, 2, 4]; - const ys = [0.0, 1.0, 2.0, 2.0]; - const cdf = new ContinuousDistribution(xs, ys); - const integral = cdf.integral(); - expect(integral).toEqual(6); - }); - it('with an infinity', () => { - const xs = [0, 1, 2, 4]; - const ys = [0.0, 1.0, Infinity, 2.0]; - const cdf = new ContinuousDistribution(xs, ys); - const integral = cdf.integral(); - expect(integral).toEqual(Infinity); - }); - it('with negative infinity', () => { - const xs = [0, 1, 2, 4]; - const ys = [0.0, 1.0, -Infinity, 2.0]; - const cdf = new ContinuousDistribution(xs, ys); - const integral = cdf.integral(); - expect(integral).toEqual(-Infinity); - }); - it('with both positive and negative infinities', () => { - const xs = [0, 1, 2, 4]; - const ys = [0.0, 1.0, -Infinity, Infinity]; - const cdf = new ContinuousDistribution(xs, ys); - const integral = cdf.integral(); - expect(integral).toEqual(NaN); - }); - it('with a NaN and filterOutNaNs set to false', () => { - const xs = [0, 1, 2, 4]; - const ys = [0.0, 1.0, 2.0, NaN]; - const cdf = new ContinuousDistribution(xs, ys); - const integral = cdf.integral({ filterOutNaNs: false }); - expect(integral).toEqual(NaN); - }); - it('with a NaN and filterOutNaNs set to true', () => { - const xs = [0, 1, 2, 4]; - const ys = [0.0, 1.0, 2.0, NaN]; - const cdf = new ContinuousDistribution(xs, ys); - const integral = cdf.integral({ filterOutNaNs: true }); - expect(integral).toEqual(2); - }); - }) -}); diff --git a/src/utility/lib/functions.js b/src/utility/lib/functions.js deleted file mode 100644 index c8c6fbfa..00000000 --- a/src/utility/lib/functions.js +++ /dev/null @@ -1,145 +0,0 @@ -/** - * @Done - * @param {number} xMin - * @param {number} xMax - * @param {number} yMin - * @param {number} yMax - * @param {number} xIntended - * @return {number} - */ -function interpolate(xMin, xMax, yMin, yMax, xIntended) { - const minProportion = (xMax - xIntended) / (xMax - xMin); - const maxProportion = (xIntended - xMin) / (xMax - xMin); - return (yMin * minProportion) + (yMax * maxProportion); -} - -/** - * @Done - * This should return an array of n evenly-spaced items - * between min and max, including min and max. - * range(1,5,3) = [1, 3, 5]; - * range(1,5,5) = [1, 2, 3, 4, 5]; - * @param {number} min - * @param {number} max - * @param {number} n - * @return {number[]} - */ -function range(min, max, n) { - if (n <= 0) throw new RangeError('n is less then zero'); - if (n === Infinity) throw new RangeError('n is Infinity'); - if (n === 0) return []; - if (n === 1) return [min]; - if (n === 2) return [min, max]; - if (min === max) return Array(n).fill(min); - n -= 1; - const diff = min - max; - const interval = Math.abs(diff / n); - - const result = []; - - let item = min; - do { - result.push(item); - item += interval; - } while (item <= max); - - // corrects results because of math errors - if ((n + 1) - result.length === 1) { - result.push(max); - } - - return result; -} - -/** - * @Done - * @param {number[]} arr - * @return {number} - */ -function sum(arr) { - return arr.reduce((acc, val) => acc + val, 0); -} - -/** - * @Done - * @param {number[]} arr - * @return {number} - */ -function mean(arr) { - return sum(arr) / arr.length; -} - -/** - * @Done - * @param {number[]} arr - * @return {number} - */ -function min(arr) { - let val = arr[0]; - for (let i = 1; i < arr.length; i++) { - if (arr[i] < val) { - val = arr[i]; - } - } - return val; -} - -/** - * @Done - * @param {number[]} arr - * @return {number} - */ -function max(arr) { - let val = arr[0]; - for (let i = 1; i < arr.length; i++) { - if (arr[i] > val) { - val = arr[i]; - } - } - return val; -} - -/** - * @Done - * @param {number} min - * @param {number} max - * @return {number} - */ -function random(min, max) { - return Math.floor(Math.random() * (max - min + 1) + min); -} - -/** - * @Done - * @param {number} from - * @param {number} to - * @return {number[]} - */ -function up(from, to) { - const arr = []; - for (let i = from; i <= to; i++) arr.push(i); - return arr; -} - -/** - * @Done - * @param {number} from - * @param {number} to - * @return {number[]} - */ -function down(from, to) { - const arr = []; - for (let i = from; i >= to; i--) arr.push(i); - return arr; -} - -module.exports = { - interpolate, - min, - max, - range, - mean, - random, - up, - down, -}; diff --git a/src/utility/lib/functions.spec.js b/src/utility/lib/functions.spec.js deleted file mode 100644 index 948f1f20..00000000 --- a/src/utility/lib/functions.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -const { interpolate } = require('./functions'); -const { range } = require('./functions'); -const { mean } = require('./functions'); -const { min } = require('./functions'); -const { max } = require('./functions'); -const { random } = require('./functions'); -const { up, down } = require('./functions'); - -describe('Functions', () => { - it('interpolate()', () => { - expect(interpolate( - 10, 20, - 1, 2, - 15 - )).toBe(1.5); - }); - it('range()', () => { - expect(range(1, 5, 3)).toEqual([1, 3, 5]); - expect(range(1, 5, 5)).toEqual([1, 2, 3, 4, 5]); - expect(range(-10, 15, 2)).toEqual([-10, 15]); - expect(range(-10, 15, 3)).toEqual([-10, 2.5, 15]); - expect(range(-10.3, 17, 3)).toEqual([-10.3, 3.3499999999999996, 17]); - expect(range(-10.3, 17, 5)).toEqual([-10.3, -3.4750000000000005, 3.3499999999999996, 10.175, 17]); - expect(range(-10.3, 17.31, 3)).toEqual([-10.3, 3.504999999999999, 17.31]); - expect(range(1, 1, 3)).toEqual([1, 1, 1]); - }); - it('mean()', () => { - expect(mean([1, 2, 3])).toBe(2); - expect(mean([1, 2, 3, -2])).toBe(1); - expect(mean([1, 2, 3, -2, -10])).toBe(-1.2); - }); - it('min()', () => { - expect(min([1, 2, 3])).toBe(1); - expect(min([-1, -2, 0, 20])).toBe(-2); - expect(min([-1, -2, 0, 20, -2.2])).toBe(-2.2); - }); - it('max()', () => { - expect(max([1, 2, 3])).toBe(3); - expect(max([-1, -2, 0, 20])).toBe(20); - expect(max([-1, -2, 0, -2.2])).toBe(0); - }); - it('random()', () => { - const num = random(1, 5); - expect(num).toBeLessThanOrEqual(5); - expect(num).toBeGreaterThanOrEqual(1); - }); - it('up()', () => { - expect(up(1, 5)).toEqual([1, 2, 3, 4, 5]); - expect(up(-1, 5)).toEqual([-1, 0, 1, 2, 3, 4, 5]); - }); - it('down()', () => { - expect(down(5, 1)).toEqual([5, 4, 3, 2, 1]); - expect(down(5, -1)).toEqual([5, 4, 3, 2, 1, 0, -1]); - }); -}); From 9349930ad89f5a45deb6556bac4323ec41ef0d0e Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 13:13:17 +0300 Subject: [PATCH 31/32] Fixes CdfLibrary.js --- src/utility/CdfLibrary.js | 334 +++++++++++++++++++------------------- 1 file changed, 165 insertions(+), 169 deletions(-) diff --git a/src/utility/CdfLibrary.js b/src/utility/CdfLibrary.js index ffe63322..2e23da7e 100644 --- a/src/utility/CdfLibrary.js +++ b/src/utility/CdfLibrary.js @@ -1,174 +1,170 @@ const { - Cdf, - Pdf, - ContinuousDistribution, - ContinuousDistributionCombination, - scoringFunctions, - } = require("@foretold/cdf/lib"); - const _ = require("lodash"); - - /** - * - * @param xs - * @param ys - * @returns {{ys: *, xs: *}} - */ - function cdfToPdf({ xs, ys }) { - let cdf = new Cdf(xs, ys); - let pdf = cdf.toPdf(); - return { xs: pdf.xs, ys: pdf.ys }; - } + Cdf, + Pdf, + ContinuousDistribution, + ContinuousDistributionCombination, + scoringFunctions, +} = require("@foretold/cdf/lib"); +const _ = require("lodash"); - /** - * - * @param xs - * @param ys - * @returns {{ys: *, xs: *}} - */ - function pdfToCdf({ xs, ys }) { - let cdf = new Pdf(xs, ys); - let pdf = cdf.toCdf(); - return { xs: pdf.xs, ys: pdf.ys }; - } - - /** - * - * @param sampleCount - * @param vars - * @returns {{ys: *, xs: *}} - */ - function mean(sampleCount, vars) { - let cdfs = vars.map(r => new Cdf(r.xs, r.ys)); - let comb = new ContinuousDistributionCombination(cdfs); - let newCdf = comb.combineYsWithMean(sampleCount); - - return { xs: newCdf.xs, ys: newCdf.ys }; - } - - /** - * - * @param sampleCount - * @param predictionCdf - * @param resolutionCdf - */ - function scoreNonMarketCdfCdf(sampleCount, predictionCdf, resolutionCdf, resolutionUniformAdditionWeight=0) { - let toCdf = (r) => (new Cdf(r.xs, r.ys)); - let prediction = toCdf(predictionCdf); - if (_.isFinite(resolutionUniformAdditionWeight)){ - prediction = prediction.combineWithUniformOfCdf( - { - cdf: toCdf(resolutionCdf), - uniformWeight: resolutionUniformAdditionWeight, - sampleCount - } - ); - } - - return scoringFunctions.distributionInputDistributionOutputMarketless({ - predictionCdf: prediction, - resultCdf: toCdf(resolutionCdf), - sampleCount, - }); - } - - /** - * - * @param sampleCount - * @param cdf - */ - function differentialEntropy(sampleCount, cdf) { - let toCdf = (r) => (new Cdf(r.xs, r.ys)); - - return scoringFunctions.differentialEntropy({ - cdf: toCdf(cdf), - sampleCount: sampleCount - }); - } - - /** - * - * @param x - * @param xs - * @param ys - * @returns {number} - */ - function findY(x, { xs, ys }) { - let cdf = new Cdf(xs, ys); - return cdf.findY(x); - } +/** + * + * @param xs + * @param ys + * @returns {{ys: *, xs: *}} + */ +function cdfToPdf({ xs, ys }) { + let cdf = new Cdf(xs, ys); + let pdf = cdf.toPdf(); + return { xs: pdf.xs, ys: pdf.ys }; +} - /** - * - * @param x - * @param xs - * @param ys - * @returns {number[]} - */ - function convertToNewLength(n, { xs, ys }) { - let dist = new ContinuousDistribution(xs, ys); - return dist.convertToNewLength(n); - } - - /** - * - * @param y - * @param xs - * @param ys - * @returns {number} - */ - function findX(y, { xs, ys }) { - let cdf = new Cdf(xs, ys); - return cdf.findX(y); - } - - /** - * - * @param xs - * @param ys - * @returns {number[]} - */ - function integral({ xs, ys }) { - if (_.includes(ys, NaN)){ - return NaN; - } - else if (_.includes(ys, Infinity) && _.includes(ys, -Infinity)){ - return NaN; - } - else if (_.includes(ys, Infinity)){ - return Infinity; - } - else if (_.includes(ys, -Infinity)){ - return -Infinity; - } - - let integral = 0; - for (let i = 1; i < ys.length; i++) { - let thisY = ys[i]; - let lastY = ys[i - 1]; - let thisX = xs[i]; - let lastX = xs[i - 1]; - - if ( - _.isFinite(thisY) && _.isFinite(lastY) && - _.isFinite(thisX) && _.isFinite(lastX) - ) { - let sectionInterval = ((thisY + lastY) / 2) * (thisX - lastX); - integral = integral + sectionInterval; +/** + * + * @param xs + * @param ys + * @returns {{ys: *, xs: *}} + */ +function pdfToCdf({ xs, ys }) { + let cdf = new Pdf(xs, ys); + let pdf = cdf.toCdf(); + return { xs: pdf.xs, ys: pdf.ys }; +} + +/** + * + * @param sampleCount + * @param vars + * @returns {{ys: *, xs: *}} + */ +function mean(sampleCount, vars) { + let cdfs = vars.map(r => new Cdf(r.xs, r.ys)); + let comb = new ContinuousDistributionCombination(cdfs); + let newCdf = comb.combineYsWithMean(sampleCount); + + return { xs: newCdf.xs, ys: newCdf.ys }; +} + +/** + * + * @param sampleCount + * @param predictionCdf + * @param resolutionCdf + */ +function scoreNonMarketCdfCdf(sampleCount, predictionCdf, resolutionCdf, resolutionUniformAdditionWeight = 0) { + let toCdf = (r) => (new Cdf(r.xs, r.ys)); + let prediction = toCdf(predictionCdf); + if (_.isFinite(resolutionUniformAdditionWeight)) { + prediction = prediction.combineWithUniformOfCdf( + { + cdf: toCdf(resolutionCdf), + uniformWeight: resolutionUniformAdditionWeight, + sampleCount } - - } - return integral; + ); } - - module.exports = { - cdfToPdf, - pdfToCdf, - findY, - findX, - convertToNewLength, - mean, - scoreNonMarketCdfCdf, - differentialEntropy, - integral, - }; - \ No newline at end of file + + return scoringFunctions.distributionInputDistributionOutputMarketless({ + predictionCdf: prediction, + resultCdf: toCdf(resolutionCdf), + sampleCount, + }); +} + +/** + * + * @param sampleCount + * @param cdf + */ +function differentialEntropy(sampleCount, cdf) { + let toCdf = (r) => (new Cdf(r.xs, r.ys)); + + return scoringFunctions.differentialEntropy({ + cdf: toCdf(cdf), + sampleCount: sampleCount + }); +} + +/** + * + * @param x + * @param xs + * @param ys + * @returns {number} + */ +function findY(x, { xs, ys }) { + let cdf = new Cdf(xs, ys); + return cdf.findY(x); +} + +/** + * + * @param x + * @param xs + * @param ys + * @returns {number[]} + */ +function convertToNewLength(n, { xs, ys }) { + let dist = new ContinuousDistribution(xs, ys); + return dist.convertToNewLength(n); +} + +/** + * + * @param y + * @param xs + * @param ys + * @returns {number} + */ +function findX(y, { xs, ys }) { + let cdf = new Cdf(xs, ys); + return cdf.findX(y); +} + +/** + * + * @param xs + * @param ys + * @returns {number[]} + */ +function integral({ xs, ys }) { + if (_.includes(ys, NaN)) { + return NaN; + } else if (_.includes(ys, Infinity) && _.includes(ys, -Infinity)) { + return NaN; + } else if (_.includes(ys, Infinity)) { + return Infinity; + } else if (_.includes(ys, -Infinity)) { + return -Infinity; + } + + let integral = 0; + for (let i = 1; i < ys.length; i++) { + let thisY = ys[i]; + let lastY = ys[i - 1]; + let thisX = xs[i]; + let lastX = xs[i - 1]; + + if ( + _.isFinite(thisY) && _.isFinite(lastY) && + _.isFinite(thisX) && _.isFinite(lastX) + ) { + let sectionInterval = ((thisY + lastY) / 2) * (thisX - lastX); + integral = integral + sectionInterval; + } + + } + return integral; +} + +module.exports = { + cdfToPdf, + pdfToCdf, + findY, + findX, + convertToNewLength, + mean, + scoreNonMarketCdfCdf, + differentialEntropy, + integral, +}; From 51aa8d82d9cf90881b07e6c2369b1797548a7b6b Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Tue, 25 Feb 2020 13:16:27 +0300 Subject: [PATCH 32/32] Fixes CdfLibrary.js --- src/utility/lib/Functions.re | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utility/lib/Functions.re b/src/utility/lib/Functions.re index 998084cc..3124c873 100644 --- a/src/utility/lib/Functions.re +++ b/src/utility/lib/Functions.re @@ -8,7 +8,6 @@ let interpolate = yMin *. minProportion +. yMax *. maxProportion; }; -// @todo: To test! 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);