From de25eef2f6db0ee7b16390fd8cb66b2ce8a740db Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Wed, 4 Mar 2020 09:11:23 +0300 Subject: [PATCH 1/3] Fixes code style --- .../charts/DistributionPlot/distPlotReact.js | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/components/charts/DistributionPlot/distPlotReact.js b/src/components/charts/DistributionPlot/distPlotReact.js index 21f237cb..2565cf99 100644 --- a/src/components/charts/DistributionPlot/distPlotReact.js +++ b/src/components/charts/DistributionPlot/distPlotReact.js @@ -35,32 +35,31 @@ function CdfChartReact(props) { useEffect(() => { try { - new CdfChartD3() - .set('svgWidth', width) - .set('svgHeight', props.height) - .set('maxX', props.maxX) - .set('minX', props.minX) - .set('onHover', props.onHover) - .set('marginBottom',props.marginBottom || 15) - .set('marginLeft', 30) - .set('marginRight', 30) - .set('marginTop', 5) - .set('showDistributionLines', props.showDistributionLines) - .set('showDistributionYAxis', props.showDistributionYAxis) - .set('verticalLine', props.verticalLine || 110) - .set('showVerticalLine', props.showVerticalLine) - .set('container', containerRef.current) - .set('scale', scale) - .set('timeScale', props.timeScale) - .set('yMaxContinuousDomainFactor', props.yMaxContinuousDomainFactor || 1) - .set('yMaxDiscreteDomainFactor', props.yMaxDiscreteDomainFactor || 1) - .data({ - continuous: props.continuous, - discrete: props.discrete, - }) - .render(); - } - catch(e) { + new CdfChartD3() + .set('svgWidth', width) + .set('svgHeight', props.height) + .set('maxX', props.maxX) + .set('minX', props.minX) + .set('onHover', props.onHover) + .set('marginBottom', props.marginBottom || 15) + .set('marginLeft', 30) + .set('marginRight', 30) + .set('marginTop', 5) + .set('showDistributionLines', props.showDistributionLines) + .set('showDistributionYAxis', props.showDistributionYAxis) + .set('verticalLine', props.verticalLine || 110) + .set('showVerticalLine', props.showVerticalLine) + .set('container', containerRef.current) + .set('scale', scale) + .set('timeScale', props.timeScale) + .set('yMaxContinuousDomainFactor', props.yMaxContinuousDomainFactor || 1) + .set('yMaxDiscreteDomainFactor', props.yMaxDiscreteDomainFactor || 1) + .data({ + continuous: props.continuous, + discrete: props.discrete, + }) + .render(); + } catch (e) { console.error("distPlotD3 Error: ", e) } }); From 5bdb229b56d08051ddac9dec37a329e3a819aca3 Mon Sep 17 00:00:00 2001 From: Roman Galochkin Date: Wed, 4 Mar 2020 10:50:11 +0300 Subject: [PATCH 2/3] Uses strings instead of floats/ints --- package.json | 2 +- src/components/DistBuilder.re | 242 ++++++++---------- .../charts/DistributionPlot/distPlotD3.js | 33 +-- yarn.lock | 8 +- 4 files changed, 135 insertions(+), 150 deletions(-) diff --git a/package.json b/package.json index 12536a55..5ffd925d 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "antd": "3.17.0", "autoprefixer": "9.7.4", "babel-jest": "25.1.0", - "bs-ant-design-alt": "2.0.0-alpha.31", + "bs-ant-design-alt": "2.0.0-alpha.33", "bs-css": "11.0.0", "bs-moment": "0.4.4", "bs-platform": "7.0.1", diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re index a34a71b3..0041b018 100644 --- a/src/components/DistBuilder.re +++ b/src/components/DistBuilder.re @@ -6,21 +6,27 @@ module FormConfig = [%lenses guesstimatorString: string, // domainType: string, // Complete, LeftLimited(...), RightLimited(...), LeftAndRightLimited(..., ...) - xPoint: float, - xPoint2: float, - excludingProbabilityMass: float, - excludingProbabilityMass2: float, + xPoint: string, + xPoint2: string, + excludingProbabilityMass: string, + excludingProbabilityMass2: string, // unitType: string, // UnspecifiedDistribution, TimeDistribution(zero, unit) zero: MomentRe.Moment.t, unit: string, // - sampleCount: int, - outputXYPoints: int, - truncateTo: int, + sampleCount: string, + outputXYPoints: string, + truncateTo: string, } ]; +type options = { + sampleCount: int, + outputXYPoints: int, + truncateTo: option(int), +}; + module Form = ReForm.Make(FormConfig); let schema = Form.Validation.Schema([||]); @@ -43,47 +49,18 @@ module FieldString = { }; }; -module FieldNumber = { - [@react.component] - let make = (~field, ~label, ~min=0) => { - - E.ste}> - validate()} - parser={str => { - let a = str |> Js.Float.fromString |> int_of_float; - a < min ? min : a; - }} - /> - - } - />; - }; -}; - module FieldFloat = { [@react.component] - let make = - (~field, ~label, ~className=Css.style([]), ~min=0., ~precision=2) => { + let make = (~field, ~label, ~className=Css.style([])) => { E.ste}> - validate()} className - parser={str => { - let a = str |> Js.Float.fromString; - Js.Float.isNaN(a) ? min : a; - }} /> } @@ -134,27 +111,23 @@ module Styles = { module DemoDist = { [@react.component] - let make = - ( - ~guesstimatorString, - ~domain, - ~unit, - ~sampleCount, - ~outputXYPoints, - ~truncateTo, - ) => { + let make = (~guesstimatorString, ~domain, ~unit, ~options) => { E.ste}>
-
- {DistPlusIngredients.make(~guesstimatorString, ~domain, ~unit, ()) + {switch (domain, unit, options) { + | (Some(domain), Some(unit), Some(options)) => + DistPlusIngredients.make(~guesstimatorString, ~domain, ~unit, ()) |> DistPlusIngredients.toDistPlus( - ~sampleCount, - ~outputXYPoints, - ~truncateTo, + ~sampleCount=options.sampleCount, + ~outputXYPoints=options.outputXYPoints, + ~truncateTo=options.truncateTo, ) - |> E.O.React.fmapOrNull(distPlus => )} -
+ |> E.O.React.fmapOrNull(distPlus => ) + | _ => + "Nothing to show. Try to change the distribution description." + |> E.ste + }}
; }; @@ -171,16 +144,16 @@ let make = () => { ~initialState={ guesstimatorString: "mm(5 to 20, floor(normal(20,2)), [.5, .5])", domainType: "Complete", - xPoint: 50.0, - xPoint2: 60.0, - excludingProbabilityMass2: 0.5, - excludingProbabilityMass: 0.3, + xPoint: "50.0", + xPoint2: "60.0", + excludingProbabilityMass2: "0.5", + excludingProbabilityMass: "0.3", unitType: "UnspecifiedDistribution", zero: MomentRe.momentNow(), unit: "days", - sampleCount: 1000, - outputXYPoints: 2000, - truncateTo: 500, + sampleCount: "1000", + outputXYPoints: "2000", + truncateTo: "500", }, (), ); @@ -190,78 +163,98 @@ let make = () => { reform.submit(); }; + let xPoint = reform.state.values.xPoint |> Js.Float.fromString; + let xPoint2 = reform.state.values.xPoint2 |> Js.Float.fromString; + let excludingProbabilityMass = + reform.state.values.excludingProbabilityMass |> Js.Float.fromString; + let excludingProbabilityMass2 = + reform.state.values.excludingProbabilityMass2 |> Js.Float.fromString; + + let zero = reform.state.values.zero; + let unit = reform.state.values.unit; + + let domainType = reform.state.values.domainType; + let unitType = reform.state.values.unitType; + + let guesstimatorString = reform.state.values.guesstimatorString; + let sampleCount = reform.state.values.sampleCount |> Js.Float.fromString; + let outputXYPoints = + reform.state.values.outputXYPoints |> Js.Float.fromString; + let truncateTo = reform.state.values.truncateTo |> Js.Float.fromString; + let domain = - switch (reform.state.values.domainType) { - | "Complete" => DistTypes.Complete - | "LeftLimited" => - LeftLimited({ - xPoint: reform.state.values.xPoint, - excludingProbabilityMass: reform.state.values.excludingProbabilityMass, - }) - | "RightLimited" => - RightLimited({ - xPoint: reform.state.values.xPoint2, - excludingProbabilityMass: - reform.state.values.excludingProbabilityMass2, - }) - | "LeftAndRightLimited" => - LeftAndRightLimited( - { - xPoint: reform.state.values.xPoint, - excludingProbabilityMass: - reform.state.values.excludingProbabilityMass, - }, - { - xPoint: reform.state.values.xPoint2, - excludingProbabilityMass: - reform.state.values.excludingProbabilityMass2, - }, + switch (domainType) { + | "Complete" => Some(DistTypes.Complete) + | "LeftLimited" + when + !Js.Float.isNaN(xPoint) + && !Js.Float.isNaN(excludingProbabilityMass) => + Some(LeftLimited({xPoint, excludingProbabilityMass})) + | "RightLimited" + when + !Js.Float.isNaN(xPoint2) + && !Js.Float.isNaN(excludingProbabilityMass2) => + Some(RightLimited({xPoint, excludingProbabilityMass})) + | "LeftAndRightLimited" + when + !Js.Float.isNaN(xPoint) + && !Js.Float.isNaN(excludingProbabilityMass) + && !Js.Float.isNaN(xPoint2) + && !Js.Float.isNaN(excludingProbabilityMass2) => + Some( + LeftAndRightLimited( + {xPoint, excludingProbabilityMass}, + {xPoint, excludingProbabilityMass}, + ), ) - | _ => Js.Exn.raiseError("domain is unknown") + | _ => None }; let unit = - switch (reform.state.values.unitType) { - | "UnspecifiedDistribution" => DistTypes.UnspecifiedDistribution + switch (unitType) { + | "UnspecifiedDistribution" => Some(DistTypes.UnspecifiedDistribution) | "TimeDistribution" => - TimeDistribution({ - zero: reform.state.values.zero, - unit: reform.state.values.unit |> TimeTypes.TimeUnit.ofString, - }) - | _ => Js.Exn.raiseError("unit is unknown") + Some( + TimeDistribution({zero, unit: unit |> TimeTypes.TimeUnit.ofString}), + ) + | _ => None }; - let guesstimatorString = reform.state.values.guesstimatorString; - let sampleCount = reform.state.values.sampleCount; - let outputXYPoints = reform.state.values.outputXYPoints; - let truncateTo = reform.state.values.truncateTo |> E.O.some; + let options = + switch (sampleCount, outputXYPoints, truncateTo) { + | (_, _, _) + when + !Js.Float.isNaN(sampleCount) + && !Js.Float.isNaN(outputXYPoints) + && !Js.Float.isNaN(truncateTo) + && sampleCount > 10. + && outputXYPoints > 10. + && truncateTo > 10. => + Some({ + sampleCount: sampleCount |> int_of_float, + outputXYPoints: outputXYPoints |> int_of_float, + truncateTo: truncateTo |> int_of_float |> E.O.some, + }) + | _ => None + }; let demoDist = React.useMemo1( - () => { - - }, + () => , [| reform.state.values.guesstimatorString, reform.state.values.domainType, - reform.state.values.xPoint |> string_of_float, - reform.state.values.xPoint2 |> string_of_float, - reform.state.values.xPoint2 |> string_of_float, - reform.state.values.excludingProbabilityMass |> string_of_float, - reform.state.values.excludingProbabilityMass2 |> string_of_float, + reform.state.values.xPoint, + reform.state.values.xPoint2, + reform.state.values.xPoint2, + reform.state.values.excludingProbabilityMass, + reform.state.values.excludingProbabilityMass2, reform.state.values.unitType, reform.state.values.zero |> E.M.format(E.M.format_standard), reform.state.values.unit, - reform.state.values.sampleCount |> string_of_int, - reform.state.values.outputXYPoints |> string_of_int, - reform.state.values.truncateTo |> string_of_int, + reform.state.values.sampleCount, + reform.state.values.outputXYPoints, + reform.state.values.truncateTo, reloader |> string_of_int, |], ); @@ -445,25 +438,16 @@ let make = () => { - + - - + Date: Wed, 4 Mar 2020 12:55:01 +0300 Subject: [PATCH 3/3] Fixes a part of issues --- src/components/DistBuilder.re | 20 +++++++++------ .../charts/DistributionPlot/distPlotD3.js | 1 + src/components/editor/main.js | 25 ++++++++++++++++--- src/components/editor/parse.js | 4 ++- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/components/DistBuilder.re b/src/components/DistBuilder.re index 0041b018..3218a6f7 100644 --- a/src/components/DistBuilder.re +++ b/src/components/DistBuilder.re @@ -117,13 +117,19 @@ module DemoDist = {
{switch (domain, unit, options) { | (Some(domain), Some(unit), Some(options)) => - DistPlusIngredients.make(~guesstimatorString, ~domain, ~unit, ()) - |> DistPlusIngredients.toDistPlus( - ~sampleCount=options.sampleCount, - ~outputXYPoints=options.outputXYPoints, - ~truncateTo=options.truncateTo, - ) - |> E.O.React.fmapOrNull(distPlus => ) + let distPlus = + DistPlusIngredients.make(~guesstimatorString, ~domain, ~unit, ()) + |> DistPlusIngredients.toDistPlus( + ~sampleCount=options.sampleCount, + ~outputXYPoints=options.outputXYPoints, + ~truncateTo=options.truncateTo, + ); + switch (distPlus) { + | Some(distPlus) => + | _ => + "Correct Guesstimator string input to show a distribution." + |> E.ste + }; | _ => "Nothing to show. Try to change the distribution description." |> E.ste diff --git a/src/components/charts/DistributionPlot/distPlotD3.js b/src/components/charts/DistributionPlot/distPlotD3.js index a45ad37c..5550a1ed 100644 --- a/src/components/charts/DistributionPlot/distPlotD3.js +++ b/src/components/charts/DistributionPlot/distPlotD3.js @@ -163,6 +163,7 @@ export class CdfChartD3 { this.addLollipopsChart(common); } } catch (e) { + this._container.selectAll("*").remove(); throw e; } return this; diff --git a/src/components/editor/main.js b/src/components/editor/main.js index 0f271d74..7f93cb78 100644 --- a/src/components/editor/main.js +++ b/src/components/editor/main.js @@ -135,6 +135,7 @@ function get_final_pdf(pdf_vals, bst_pts_and_idxs, output_grid) { var active_intervals = new Map(); var active_endpoints = new bst.AVLTree(); var final_pdf_vals = []; + for ( let out_grid_idx = 0; out_grid_idx < output_grid.length; @@ -179,9 +180,19 @@ function get_final_pdf(pdf_vals, bst_pts_and_idxs, output_grid) { active_intervals.delete(interval_idx); } } + return final_pdf_vals; } +/** + * @param {string} str + * @param {string} char + * @returns {number} + */ +function get_count_of_chars(str, char) { + return str.split(char).length - 1; +} + /** * Entrypoint. Pass user input strings to this function, * get the corresponding pdf values and input points back. @@ -194,12 +205,19 @@ function get_final_pdf(pdf_vals, bst_pts_and_idxs, output_grid) { * @returns {([]|*[])[]} */ function get_pdf_from_user_input(user_input_string) { - try{ + try { + const count_opened_bracket = get_count_of_chars(user_input_string, '('); + const count_closed_bracket = get_count_of_chars(user_input_string, ')'); + if (count_opened_bracket !== count_closed_bracket) { + throw new Error('Count of brackets are not equal.'); + } + let parsed = parse.parse_initial_string(user_input_string); let mm_args = parse.separate_mm_args(parsed.mm_args_string); - const is_mm = mm_args.distrs.length > 0; - if (!parsed.outer_string) return [[], [], true]; + if (!parsed.outer_string) { + throw new Error('Parse string is empty.'); + } let tree = new bst.AVLTree(); let possible_start_pts = []; @@ -232,6 +250,7 @@ function get_pdf_from_user_input(user_input_string) { let output_grid = evenly_spaced_grid(start_pt, end_pt, OUTPUT_GRID_NUMEL); let final_pdf_vals = get_final_pdf(all_vals, tree, output_grid); + return [final_pdf_vals, output_grid, false]; } catch (e) { return [[], [], true]; diff --git a/src/components/editor/parse.js b/src/components/editor/parse.js index 11d1ef4b..e19addb3 100644 --- a/src/components/editor/parse.js +++ b/src/components/editor/parse.js @@ -3,6 +3,7 @@ const math = _math.create(_math.all); // Functions for parsing/processing user input strings are here +// @todo: Do not use objects. const DISTR_REGEXS = [ /beta\(/g, /(log)?normal\(/g, @@ -12,7 +13,6 @@ const DISTR_REGEXS = [ ]; /** - * * @param user_input_string * @returns {{mm_args_string: string, outer_string: string}} */ @@ -20,6 +20,7 @@ function parse_initial_string(user_input_string) { let outer_output_string = ""; let mm_args_string = ""; let idx = 0; + while (idx < user_input_string.length) { if ( user_input_string.substring(idx - 11, idx) === "multimodal(" || @@ -42,6 +43,7 @@ function parse_initial_string(user_input_string) { idx += 1; } } + return { outer_string: outer_output_string, mm_args_string: mm_args_string