Merge pull request #198 from QURIresearch/generic-sparklines

Implement generic sparklines with tests
This commit is contained in:
Ozzie Gooen 2022-04-08 10:06:44 -04:00 committed by GitHub
commit d699c32072
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 155 additions and 48 deletions

View File

@ -6,9 +6,19 @@ let env: DistributionOperation.env = {
xyPointLength: 100, xyPointLength: 100,
} }
let {
normalDist5,
normalDist10,
normalDist20,
normalDist,
uniformDist,
betaDist,
lognormalDist,
cauchyDist,
triangularDist,
exponentialDist,
} = module(GenericDist_Fixtures)
let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev})) let mkNormal = (mean, stdev) => GenericDist_Types.Symbolic(#Normal({mean: mean, stdev: stdev}))
let normalDist5: GenericDist_Types.genericDist = mkNormal(5.0, 2.0)
let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))
let {toFloat, toDist, toString, toError} = module(DistributionOperation.Output) let {toFloat, toDist, toString, toError} = module(DistributionOperation.Output)
let {run} = module(DistributionOperation) let {run} = module(DistributionOperation)
@ -19,6 +29,57 @@ let toExt: option<'a> => 'a = E.O.toExt(
"Should be impossible to reach (This error is in test file)", "Should be impossible to reach (This error is in test file)",
) )
describe("sparkline", () => {
let runTest = (
name: string,
dist: GenericDist_Types.genericDist,
expected: DistributionOperation.outputType,
) => {
test(name, () => {
let result = DistributionOperation.run(~env, FromDist(ToString(ToSparkline(20)), dist))
expect(result)->toEqual(expected)
})
}
runTest(
"normal",
normalDist,
String(`▁▁▁▁▁▂▄▆▇██▇▆▄▂▁▁▁▁▁`),
)
runTest(
"uniform",
uniformDist,
String(`████████████████████`),
)
runTest("beta", betaDist, String(`▁▄▇████▇▆▅▄▃▃▂▁▁▁▁▁▁`))
runTest(
"lognormal",
lognormalDist,
String(`▁█▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁`),
)
runTest(
"cauchy",
cauchyDist,
String(`▁▁▁▁▁▁▁▁▁██▁▁▁▁▁▁▁▁▁`),
)
runTest(
"triangular",
triangularDist,
String(`▁▁▂▃▄▅▆▇████▇▆▅▄▃▂▁▁`),
)
runTest(
"exponential",
exponentialDist,
String(`█▅▄▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁`),
)
})
describe("toPointSet", () => { describe("toPointSet", () => {
test("on symbolic normal distribution", () => { test("on symbolic normal distribution", () => {
let result = let result =

View File

@ -0,0 +1,11 @@
let normalDist5: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 5.0, stdev: 2.0}))
let normalDist10: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 10.0, stdev: 2.0}))
let normalDist20: GenericDist_Types.genericDist = Symbolic(#Normal({mean: 20.0, stdev: 2.0}))
let normalDist: GenericDist_Types.genericDist = normalDist5
let betaDist: GenericDist_Types.genericDist = Symbolic(#Beta({alpha: 2.0, beta: 5.0}))
let lognormalDist: GenericDist_Types.genericDist = Symbolic(#Lognormal({mu: 0.0, sigma: 1.0}))
let cauchyDist: GenericDist_Types.genericDist = Symbolic(#Cauchy({local: 1.0, scale: 1.0}))
let triangularDist: GenericDist_Types.genericDist = Symbolic(#Triangular({low: 1.0, medium: 2.0, high: 3.0}))
let exponentialDist: GenericDist_Types.genericDist = Symbolic(#Exponential({rate: 2.0}))
let uniformDist: GenericDist_Types.genericDist = Symbolic(#Uniform({low: 9.0, high: 10.0}))

View File

@ -134,21 +134,21 @@ describe("Normal distribution with sparklines", () => {
let normalDistAtMean5: SymbolicDistTypes.normal = {mean: 5.0, stdev: 2.0} let normalDistAtMean5: SymbolicDistTypes.normal = {mean: 5.0, stdev: 2.0}
let normalDistAtMean10: SymbolicDistTypes.normal = {mean: 10.0, stdev: 2.0} let normalDistAtMean10: SymbolicDistTypes.normal = {mean: 10.0, stdev: 2.0}
let range20Float = E.A.rangeFloat(0, 20) // [0.0,1.0,2.0,3.0,4.0,...19.0,] let range20Float = E.A.Floats.range(0.0, 20.0, 20) // [0.0,1.0,2.0,3.0,4.0,...19.0,]
test("mean=5 pdf", () => { test("mean=5 pdf", () => {
let pdfNormalDistAtMean5 = x => SymbolicDist.Normal.pdf(x, normalDistAtMean5) let pdfNormalDistAtMean5 = x => SymbolicDist.Normal.pdf(x, normalDistAtMean5)
let sparklineMean5 = fnImage(pdfNormalDistAtMean5, range20Float) let sparklineMean5 = fnImage(pdfNormalDistAtMean5, range20Float)
Sparklines.create(sparklineMean5, ()) Sparklines.create(sparklineMean5, ())
-> expect -> expect
-> toEqual(`▁▂▃▅███▅▃▂▁▁▁▁▁▁▁▁▁▁▁`) -> toEqual(`▁▂▃▆██▇▅▂▁▁▁▁▁▁▁▁▁▁▁`)
}) })
test("parameter-wise addition of two normal distributions", () => { test("parameter-wise addition of two normal distributions", () => {
let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionPdf(normalDistAtMean10) -> fnImage(range20Float) let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionPdf(normalDistAtMean10) -> fnImage(range20Float)
Sparklines.create(sparklineMean15, ()) Sparklines.create(sparklineMean15, ())
-> expect -> expect
-> toEqual(`▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃▂`) -> toEqual(`▁▁▁▁▁▁▁▁▁▂▃▄▆███▇▅▄▂`)
}) })
test("mean=10 cdf", () => { test("mean=10 cdf", () => {
@ -156,6 +156,6 @@ describe("Normal distribution with sparklines", () => {
let sparklineMean10 = fnImage(cdfNormalDistAtMean10, range20Float) let sparklineMean10 = fnImage(cdfNormalDistAtMean10, range20Float)
Sparklines.create(sparklineMean10, ()) Sparklines.create(sparklineMean10, ())
-> expect -> expect
-> toEqual(`▁▁▁▁▁▁▁▁▂▃▅▆▇████████`) -> toEqual(`▁▁▁▁▁▁▁▁▂▄▅▇████████`)
}) })
}) })

View File

@ -9,6 +9,3 @@ let expectParseToBe = (expr: string, answer: string) =>
let expectEvalToBe = (expr: string, answer: string) => let expectEvalToBe = (expr: string, answer: string) =>
Reducer.eval(expr)->ExpressionValue.toStringResult->expect->toBe(answer) Reducer.eval(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
// Current configuration does not ignore this file so we have to have a test
test("test helpers", () => expect(1)->toBe(1))

View File

@ -30,6 +30,9 @@ describe("eval on distribution functions", () => {
testEval("mean(normal(5,2))", "Ok(5)") testEval("mean(normal(5,2))", "Ok(5)")
testEval("mean(lognormal(1,2))", "Ok(20.085536923187668)") testEval("mean(lognormal(1,2))", "Ok(20.085536923187668)")
}) })
describe("toString", () => {
testEval("toString(normal(5,2))", "Ok('Normal(5,2)')")
})
describe("normalize", () => { describe("normalize", () => {
testEval("normalize(normal(5,2))", "Ok(Normal(5,2))") testEval("normalize(normal(5,2))", "Ok(Normal(5,2))")
}) })

View File

@ -1,11 +1,13 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = { module.exports = {
preset: 'ts-jest', preset: "ts-jest",
testEnvironment: 'node', testEnvironment: "node",
setupFilesAfterEnv: [ setupFilesAfterEnv: [
"<rootdir>/../../node_modules/bisect_ppx/src/runtime/js/jest.bs.js" "<rootdir>/../../node_modules/bisect_ppx/src/runtime/js/jest.bs.js",
], ],
testPathIgnorePatterns: [ testPathIgnorePatterns: [
"__tests__/TestHelpers.bs.js" ".*Fixtures.bs.js",
"/node_modules/",
".*Helpers.bs.js",
], ],
}; };

View File

@ -113,7 +113,11 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation) GenericDist.toFloatOperation(dist, ~toPointSetFn, ~distToFloatOperation)
->E.R2.fmap(r => Float(r)) ->E.R2.fmap(r => Float(r))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToString => dist->GenericDist.toString->String | ToString(ToString) => dist->GenericDist.toString->String
| ToString(ToSparkline(buckets)) =>
GenericDist.toSparkline(dist, ~sampleCount, ~buckets, ())
->E.R2.fmap(r => String(r))
->OutputLocal.fromResult
| ToDist(Inspect) => { | ToDist(Inspect) => {
Js.log2("Console log requested: ", dist) Js.log2("Console log requested: ", dist)
Dist(dist) Dist(dist)
@ -127,7 +131,7 @@ let rec run = (~env, functionCallInfo: functionCallInfo): outputType => {
dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(SampleSet(r)))->OutputLocal.fromResult dist->GenericDist.sampleN(n)->E.R2.fmap(r => Dist(SampleSet(r)))->OutputLocal.fromResult
| ToDist(ToPointSet) => | ToDist(ToPointSet) =>
dist dist
->GenericDist.toPointSet(~xyPointLength, ~sampleCount) ->GenericDist.toPointSet(~xyPointLength, ~sampleCount, ())
->E.R2.fmap(r => Dist(PointSet(r))) ->E.R2.fmap(r => Dist(PointSet(r)))
->OutputLocal.fromResult ->OutputLocal.fromResult
| ToDistCombination(Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented) | ToDistCombination(Algebraic, _, #Float(_)) => GenDistError(NotYetImplemented)

View File

@ -49,13 +49,19 @@ let toFloatOperation = (
} }
} }
//Todo: If it's a pointSet, but the xyPointLenght is different from what it has, it should change. //Todo: If it's a pointSet, but the xyPointLength is different from what it has, it should change.
// This is tricky because the case of discrete distributions. // This is tricky because the case of discrete distributions.
// Also, change the outputXYPoints/pointSetDistLength details // Also, change the outputXYPoints/pointSetDistLength details
let toPointSet = (~xyPointLength, ~sampleCount, t): result<PointSetTypes.pointSetDist, error> => { let toPointSet = (
t,
~xyPointLength,
~sampleCount,
~xSelection: GenericDist_Types.Operation.pointsetXSelection=#ByWeight,
unit,
): result<PointSetTypes.pointSetDist, error> => {
switch (t: t) { switch (t: t) {
| PointSet(pointSet) => Ok(pointSet) | PointSet(pointSet) => Ok(pointSet)
| Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(xyPointLength, r)) | Symbolic(r) => Ok(SymbolicDist.T.toPointSetDist(~xSelection, xyPointLength, r))
| SampleSet(r) => { | SampleSet(r) => {
let response = SampleSet.toPointSetDist( let response = SampleSet.toPointSetDist(
~samples=r, ~samples=r,
@ -75,6 +81,11 @@ let toPointSet = (~xyPointLength, ~sampleCount, t): result<PointSetTypes.pointSe
} }
} }
let toSparkline = (t: t, ~sampleCount: int, ~buckets: int=20, unit): result<string, error> =>
t
->toPointSet(~xSelection=#Linear, ~xyPointLength=buckets, ~sampleCount, ())
->E.R.bind(r => r->PointSetDist.toSparkline->E.R2.errMap(r => Error(GenericDist_Types.Other(r))))
module Truncate = { module Truncate = {
let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option<t> => let trySymbolicSimplification = (leftCutoff, rightCutoff, t: t): option<t> =>
switch (leftCutoff, rightCutoff, t) { switch (leftCutoff, rightCutoff, t) {

View File

@ -20,17 +20,20 @@ let toFloatOperation: (
) => result<float, error> ) => result<float, error>
let toPointSet: ( let toPointSet: (
t,
~xyPointLength: int, ~xyPointLength: int,
~sampleCount: int, ~sampleCount: int,
t, ~xSelection: GenericDist_Types.Operation.pointsetXSelection=?,
unit,
) => result<PointSetTypes.pointSetDist, error> ) => result<PointSetTypes.pointSetDist, error>
let toSparkline: (t, ~sampleCount: int, ~buckets: int=?, unit) => result<string, error>
let truncate: ( let truncate: (
t, t,
~toPointSetFn: toPointSetFn, ~toPointSetFn: toPointSetFn,
~leftCutoff: option<float>=?, ~leftCutoff: option<float>=?,
~rightCutoff: option<float>=?, ~rightCutoff: option<float>=?,
unit unit,
) => result<t, error> ) => result<t, error>
let algebraicCombination: ( let algebraicCombination: (

View File

@ -41,6 +41,8 @@ module Operation = {
| #Sample | #Sample
] ]
type pointsetXSelection = [#Linear | #ByWeight]
type toDist = type toDist =
| Normalize | Normalize
| ToPointSet | ToPointSet
@ -50,11 +52,15 @@ module Operation = {
type toFloatArray = Sample(int) type toFloatArray = Sample(int)
type toString =
| ToString
| ToSparkline(int)
type fromDist = type fromDist =
| ToFloat(toFloat) | ToFloat(toFloat)
| ToDist(toDist) | ToDist(toDist)
| ToDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)]) | ToDistCombination(direction, arithmeticOperation, [#Dist(genericDist) | #Float(float)])
| ToString | ToString(toString)
type singleParamaterFunction = type singleParamaterFunction =
| FromDist(fromDist) | FromDist(fromDist)
@ -77,7 +83,8 @@ module Operation = {
| ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` | ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})`
| ToDist(Truncate(_, _)) => `truncate` | ToDist(Truncate(_, _)) => `truncate`
| ToDist(Inspect) => `inspect` | ToDist(Inspect) => `inspect`
| ToString => `toString` | ToString(ToString) => `toString`
| ToString(ToSparkline(n)) => `toSparkline(${E.I.toString(n)})`
| ToDistCombination(Algebraic, _, _) => `algebraic` | ToDistCombination(Algebraic, _, _) => `algebraic`
| ToDistCombination(Pointwise, _, _) => `pointwise` | ToDistCombination(Pointwise, _, _) => `pointwise`
} }

View File

@ -191,7 +191,6 @@ let isFloat = (t: t) =>
let sampleNRendered = (n, dist) => { let sampleNRendered = (n, dist) => {
let integralCache = T.Integral.get(dist) let integralCache = T.Integral.get(dist)
let distWithUpdatedIntegralCache = T.updateIntegralCache(Some(integralCache), dist) let distWithUpdatedIntegralCache = T.updateIntegralCache(Some(integralCache), dist)
doN(n, () => sample(distWithUpdatedIntegralCache)) doN(n, () => sample(distWithUpdatedIntegralCache))
} }
@ -203,3 +202,8 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float =>
| #Sample => sample(s) | #Sample => sample(s)
| #Mean => T.mean(s) | #Mean => T.mean(s)
} }
let toSparkline = (t: t) =>
T.toContinuous(t)
->E.O2.toResult("toContinous Error: Could not convert into continuous distribution")
->E.R2.fmap(r => Continuous.getShape(r).ys->Sparklines.create())

View File

@ -346,11 +346,11 @@ module T = {
| _ => #NoSolution | _ => #NoSolution
} }
let toPointSetDist = (sampleCount, d: symbolicDist): PointSetTypes.pointSetDist => let toPointSetDist = (~xSelection=#ByWeight, sampleCount, d: symbolicDist): PointSetTypes.pointSetDist =>
switch d { switch d {
| #Float(v) => Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [v], ys: [1.0]})) | #Float(v) => Discrete(Discrete.make(~integralSumCache=Some(1.0), {xs: [v], ys: [1.0]}))
| _ => | _ =>
let xs = interpolateXs(~xSelection=#ByWeight, d, sampleCount) let xs = interpolateXs(~xSelection, d, sampleCount)
let ys = xs |> E.A.fmap(x => pdf(x, d)) let ys = xs |> E.A.fmap(x => pdf(x, d))
Continuous(Continuous.make(~integralSumCache=Some(1.0), {xs: xs, ys: ys})) Continuous(Continuous.make(~integralSumCache=Some(1.0), {xs: xs, ys: ys}))
} }

View File

@ -45,6 +45,13 @@ module Helpers = {
FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some FromDist(GenericDist_Types.Operation.ToFloat(fnCall), dist)->runGenericOperation->Some
} }
let toStringFn = (
fnCall: GenericDist_Types.Operation.toString,
dist: GenericDist_Types.genericDist,
) => {
FromDist(GenericDist_Types.Operation.ToString(fnCall), dist)->runGenericOperation->Some
}
let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => { let toDistFn = (fnCall: GenericDist_Types.Operation.toDist, dist) => {
FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some FromDist(GenericDist_Types.Operation.ToDist(fnCall), dist)->runGenericOperation->Some
} }
@ -119,6 +126,9 @@ let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
->SymbolicConstructors.symbolicResultToOutput ->SymbolicConstructors.symbolicResultToOutput
| ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist) | ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist)
| ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist) | ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist)
| ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist)
| ("toSparkline", [EvDistribution(dist)]) => Helpers.toStringFn(ToSparkline(20), dist)
| ("toSparkline", [EvDistribution(dist), EvNumber(n)]) => Helpers.toStringFn(ToSparkline(Belt.Float.toInt(n)), dist)
| ("exp", [EvDistribution(a)]) => | ("exp", [EvDistribution(a)]) =>
// https://mathjs.org/docs/reference/functions/exp.html // https://mathjs.org/docs/reference/functions/exp.html
Helpers.twoDiststoDistFn(Algebraic, "pow", GenericDist.fromFloat(Math.e), a)->Some Helpers.twoDiststoDistFn(Algebraic, "pow", GenericDist.fromFloat(Math.e), a)->Some

View File

@ -101,6 +101,7 @@ module O2 = {
let default = (a, b) => O.default(b, a) let default = (a, b) => O.default(b, a)
let toExn = (a, b) => O.toExn(b, a) let toExn = (a, b) => O.toExn(b, a)
let fmap = (a, b) => O.fmap(b, a) let fmap = (a, b) => O.fmap(b, a)
let toResult = (a, b) => O.toResult(b, a)
} }
/* Functions */ /* Functions */
@ -178,6 +179,13 @@ module R = {
module R2 = { module R2 = {
let fmap = (a,b) => R.fmap(b,a) let fmap = (a,b) => R.fmap(b,a)
let bind = (a, b) => R.bind(b, a)
//Converts result type to change error type only
let errMap = (a, map) => switch(a){
| Ok(r) => Ok(r)
| Error(e) => map(e)
}
} }
let safe_fn_of_string = (fn, s: string): option<'a> => let safe_fn_of_string = (fn, s: string): option<'a> =>
@ -289,8 +297,7 @@ module A = {
)) ))
|> Rationale.Result.return |> Rationale.Result.return
} }
let rangeFloat = (~step=1, start, stop) =>
Belt.Array.rangeBy(start, stop, ~step) |> fmap(Belt.Int.toFloat)
// This zips while taking the longest elements of each array. // This zips while taking the longest elements of each array.
let zipMaxLength = (array1, array2) => { let zipMaxLength = (array1, array2) => {
@ -442,6 +449,12 @@ module A = {
let mean = a => sum(a) /. (Array.length(a) |> float_of_int) let mean = a => sum(a) /. (Array.length(a) |> float_of_int)
let random = Js.Math.random_int let random = Js.Math.random_int
// Gives an array with all the differences between values
// diff([1,5,3,7]) = [4,-2,4]
let diff = (arr: array<float>): array<float> =>
Belt.Array.zipBy(arr, Belt.Array.sliceToEnd(arr, 1), (left, right) => right -. left)
exception RangeError(string) exception RangeError(string)
let range = (min: float, max: float, n: int): array<float> => let range = (min: float, max: float, n: int): array<float> =>
switch n { switch n {

View File

@ -8434,13 +8434,6 @@ extglob@^2.0.4:
snapdragon "^0.8.1" snapdragon "^0.8.1"
to-regex "^3.0.1" to-regex "^3.0.1"
fast-check@^2.17.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-2.24.0.tgz#39f85586862108a4de6394c5196ebcf8b76b6c8b"
integrity sha512-iNXbN90lbabaCUfnW5jyXYPwMJLFYl09eJDkXA9ZoidFlBK63gNRvcKxv+8D1OJ1kIYjwBef4bO/K3qesUeWLQ==
dependencies:
pure-rand "^5.0.1"
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3: fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3:
version "3.1.3" version "3.1.3"
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
@ -13573,11 +13566,6 @@ pure-color@^1.2.0:
resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e" resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e"
integrity sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4= integrity sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=
pure-rand@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.1.tgz#97a287b4b4960b2a3448c0932bf28f2405cac51d"
integrity sha512-ksWccjmXOHU2gJBnH0cK1lSYdvSZ0zLoCMSz/nTGh6hDvCSgcRxDyIcOBD6KNxFz3xhMPm/T267Tbe2JRymKEQ==
q@^1.1.2: q@^1.1.2:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@ -14766,13 +14754,6 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
rescript-fast-check@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/rescript-fast-check/-/rescript-fast-check-1.1.1.tgz#ef153cb01254b2f01a738faf85b73327d423d5e1"
integrity sha512-wxeW0TsL/prkRvEYGbhEiLaLKmYJaECyDcKEWh65hDqP2i76lARzVW3QmYujSYK4OnjAC70dln3X6UC/2m2Huw==
dependencies:
fast-check "^2.17.0"
rescript@^9.1.4: rescript@^9.1.4:
version "9.1.4" version "9.1.4"
resolved "https://registry.npmjs.org/rescript/-/rescript-9.1.4.tgz" resolved "https://registry.npmjs.org/rescript/-/rescript-9.1.4.tgz"