diff --git a/packages/squiggle-lang/__tests__/Symbolic_test.res b/packages/squiggle-lang/__tests__/Symbolic_test.res index bac4d17b..8ec3be22 100644 --- a/packages/squiggle-lang/__tests__/Symbolic_test.res +++ b/packages/squiggle-lang/__tests__/Symbolic_test.res @@ -22,13 +22,12 @@ describe("Normal distribution with sparklines", () => { let normalDistAtMean5: SymbolicDistTypes.normal = {mean: 5.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,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,] + let range20Float = E.A.rangeFloat(0, 20) // [0.0,1.0,2.0,3.0,4.0,...19.0,] let pdfNormalDistAtMean5 = x => Normal.pdf(x, normalDistAtMean5) let sparklineMean5 = pdfImage(pdfNormalDistAtMean5, range20Float) makeTest("mean=5", Sparklines.create(sparklineMean5, ()), `▁▂▃▅███▅▃▂▁▁▁▁▁▁▁▁▁▁▁`) let sparklineMean15 = normalDistAtMean5 -> parameterWiseAdditionHelper(normalDistAtMean10) -> pdfImage(range20Float) - // let sparklineMean15 = pdfImage(pdfNormalDistAtMean15, range20Float) makeTest("parameter-wise addition of two normal distributions", Sparklines.create(sparklineMean15, ()), `▁▁▁▁▁▁▁▁▁▁▂▃▅▇███▇▅▃▂`) }) diff --git a/packages/squiggle-lang/src/rescript/utility/E.res b/packages/squiggle-lang/src/rescript/utility/E.res index eb366a6d..71c4c1e4 100644 --- a/packages/squiggle-lang/src/rescript/utility/E.res +++ b/packages/squiggle-lang/src/rescript/utility/E.res @@ -1,5 +1,4 @@ open Rationale.Function.Infix - module FloatFloatMap = { module Id = Belt.Id.MakeComparable({ type t = float @@ -87,6 +86,11 @@ module O = { let max = compare(\">") } +module O2 = { + let default = (a, b) => O.default(b, a) + let toExn = (a, b) => O.toExn(b, a) +} + /* Functions */ module F = { let apply = (a, e) => a |> e @@ -269,7 +273,8 @@ module A = { )) |> Rationale.Result.return } - let rangeFloat = (start, stop) => start -> Belt.Array.rangeBy(stop, ~step=1) -> (arr => fmap(Belt.Int.toFloat, arr)) + 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. let zipMaxLength = (array1, array2) => { @@ -323,8 +328,8 @@ module A = { | r => Some(r) } ) - let filter = (o, e) => Js.Array.filter(o, e) - let joinWith = (fill, arr) => Js.Array.joinWith(fill, arr) + let filter = Js.Array.filter + let joinWith = Js.Array.joinWith module O = { let concatSomes = (optionals: array>): array<'a> => @@ -407,6 +412,7 @@ module A = { : { let _ = Js.Array.push(element, continuous) } + () }) @@ -436,6 +442,11 @@ module A = { } } +module A2 = { + let fmap = (a, b) => Array.map(b, a) + let joinWith = (a, b) => A.joinWith(b, a) +} + module JsArray = { let concatSomes = (optionals: Js.Array.t>): Js.Array.t<'a> => optionals diff --git a/packages/squiggle-lang/src/rescript/utility/Sparklines.res b/packages/squiggle-lang/src/rescript/utility/Sparklines.res index 09e17e37..12d509fa 100644 --- a/packages/squiggle-lang/src/rescript/utility/Sparklines.res +++ b/packages/squiggle-lang/src/rescript/utility/Sparklines.res @@ -3,26 +3,24 @@ // Omitting rgb "fire" style, so no `chalk` dependency // Omitting: NaN handling, special consideration for constant data. -let create = ( - numbers: array, - ~minimum=?, - ~maximum=?, - () -) => { - let ticks = [`▁`, `▂`, `▃`, `▄`, `▅`, `▆`, `▇`, `█`] +let ticks = [`▁`, `▂`, `▃`, `▄`, `▅`, `▆`, `▇`, `█`] - let minimum = E.O.default(Js.Math.minMany_float(numbers), minimum) - let maximum = E.O.default(Js.Math.maxMany_float(numbers), maximum) +let _ticksLength = E.A.length(ticks) - let toHeight = (number: float) => { - let tickIndex = Js.Math.ceil_int((number /. maximum) *. (ticks -> Belt.Array.length -> Belt.Int.toFloat)) - 1 - let tickIndex = if maximum == 0.0 || tickIndex < 0 { - 0 - } else { - tickIndex - } - ticks[tickIndex] - } - - toHeight -> E.A.fmap(numbers) -> (arr => E.A.joinWith("", arr)) +let _heightToTickIndex = (maximum: float, v: float) => { + let suggestedTickIndex = Js.Math.ceil_int(v /. maximum *. Belt.Int.toFloat(_ticksLength)) - 1 + max(suggestedTickIndex, 0) +} + +let create = (relativeHeights: array, ~maximum=?, ()) => { + if E.A.length(relativeHeights) === 0 { + "" + } else { + let maximum = maximum->E.O2.default(E.A.max(relativeHeights)->E.O2.toExn("")) + + relativeHeights + ->E.A2.fmap(_heightToTickIndex(maximum)) + ->E.A2.fmap(r => E.A.get(ticks, r)->E.O2.toExn("")) + ->E.A2.joinWith("") + } }