Merge branch 'Umur-reducer-dev' into reducer-dev

This commit is contained in:
Umur Ozkul 2022-04-29 19:11:56 +02:00
commit f1798e3165
71 changed files with 2662 additions and 1832 deletions

View File

@ -1,12 +1,22 @@
# Squiggle
[![Packages check](https://github.com/quantified-uncertainty/squiggle/actions/workflows/ci.yml/badge.svg)](https://github.com/quantified-uncertainty/squiggle/actions/workflows/ci.yml)
[![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-lang.svg)](https://www.npmjs.com/package/@quri/squiggle-lang)
[![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-components.svg)](https://www.npmjs.com/package/@quri/squiggle-components)
[![npm version - lang](https://badge.fury.io/js/@quri%2Fsquiggle-lang.svg)](https://www.npmjs.com/package/@quri/squiggle-lang)
[![npm version - components](https://badge.fury.io/js/@quri%2Fsquiggle-components.svg)](https://www.npmjs.com/package/@quri/squiggle-components)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/quantified-uncertainty/squiggle/blob/develop/LICENSE)
[![codecov](https://codecov.io/gh/quantified-uncertainty/squiggle/branch/develop/graph/badge.svg?token=QRLBL5CQ7C)](https://codecov.io/gh/quantified-uncertainty/squiggle)
This is an experimental DSL/language for making probabilistic estimates. The full story can be found [here](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3).
_An estimation language_.
## Get started
- [Gallery](https://www.squiggle-language.com/docs/Discussions/Gallery)
- [Squiggle playground](https://squiggle-language.com/playground)
- [Language basics](https://www.squiggle-language.com/docs/Features/Language)
- [Squiggle functions source of truth](https://www.squiggle-language.com/docs/Features/Functions)
- [Known bugs](https://www.squiggle-language.com/docs/Discussions/Bugs)
- [Original lesswrong sequence](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3)
- [Author your squiggle models as Observable notebooks](https://observablehq.com/@hazelfire/squiggle)
## Our deployments
@ -27,7 +37,7 @@ the packages can be found in `packages`.
- `@quri/squiggle-components` in `packages/components` contains React components that
can be passed squiggle strings as props, and return a presentation of the result
of the calculation.
- `@quri/squiggle-website` in `packages/website` The main descriptive website for squiggle,
- `packages/website` is the main descriptive website for squiggle,
it is hosted at `squiggle-language.com`.
The playground depends on the components library which then depends on the language. This means that if you wish to work on the components library, you will need to build (no need to bundle) the language, and as of this writing playground doesn't really work.

20
examples/decay.squiggle Normal file
View File

@ -0,0 +1,20 @@
# The following code was provided by Nuño Sempere, it comes directly from the post https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3/p/j8o6sgRerE3tqNWdj
## Initial setup
yearly_probability_max = 0.95
yearly_probability_min = 0.66
period_probability_function(epsilon, yearly_probability) = 1 - (1 - yearly_probability) ^ (1 / epsilon)
probability_decayed(t, time_periods, period_probability) = 1 - (1 - period_probability) ^ (time_periods - t)
## Monthly decomposition
months_in_a_year=12
monthly_probability_min = period_probability_function(months_in_a_year, yearly_probability_min)
monthly_probability_max = period_probability_function(months_in_a_year, yearly_probability_max)
probability_decayed_monthly_min(t) = probability_decayed(t, months_in_a_year, monthly_probability_min)
probability_decayed_monthly_max(t) = probability_decayed(t, months_in_a_year, monthly_probability_max)
probability_decayed_monthly(t) = probability_decayed_monthly_min(t) to probability_decayed_monthly_max(t)
probability_decayed_monthly
## probability_decayed_monthly(6)
## mean(probability_decayed_monthly(6))

View File

@ -0,0 +1,38 @@
# This is a cost effectiveness analysis of givedirectly, originally done by givewell, and translated into Squiggle by Sam Nolan
donation_size = 10000
proportion_of_funding_available = beta(10, 2)
total_funding_available = donation_size * proportion_of_funding_available
household_size = 3.7 to 5.7
size_of_transfer = 800 to 1200
size_of_transfer_per_person = size_of_transfer / household_size
portion_invested = 0.3 to 0.5
amount_invested = portion_invested * size_of_transfer_per_person
amount_consumed = (1 - portion_invested) * size_of_transfer_per_person
return_on_investment = 0.08 to 0.12
increase_in_consumption_from_investments = return_on_investment * amount_invested
baseline_consumption = 200 to 350
log_increase_in_consumption = log(amount_consumed + baseline_consumption) + log(baseline_consumption)
log_increase_in_consumption_from_investment = log(increase_in_consumption_from_investments + baseline_consumption) + log(baseline_consumption)
investment_duration = 8 to 12
discount_rate = beta(1.004, 20)
present_value_excluding_last_year = log_increase_in_consumption_from_investment * (1 - (1 + discount_rate) ^ (-investment_duration)) / (log(1 + discount_rate))
percent_of_investment_returned = 0.15 to 0.25
pv_consumption_last_year = (log(baseline_consumption + amount_invested * (return_on_investment + percent_of_investment_returned)) - log(baseline_consumption)) / (1 + discount_rate)^investment_duration
total_pv_of_cash_transfer = pv_consumption_last_year + present_value_excluding_last_year + log_increase_in_consumption
discount_negative_spoiler = 0.03 to 0.07
value_discounting_spoiler = discount_negative_spoiler * total_pv_of_cash_transfer
consumption_increase_per_household = value_discounting_spoiler * household_size
amount_of_transfers_made = total_funding_available / size_of_transfer
total_increase_in_ln_consumption = amount_of_transfers_made * consumption_increase_per_household
total_increase_in_ln_consumption

View File

@ -0,0 +1,3 @@
xY1 = 99
aBa3 = xY1 * 2 + 1
aBa3 * xY1 + aBa3

View File

@ -1,8 +1,26 @@
[![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-components.svg)](https://www.npmjs.com/package/@quri/squiggle-components)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/quantified-uncertainty/squiggle/blob/develop/LICENSE)
# Squiggle Components
This package contains all the components for squiggle. These can be used either as a library or hosted as a [storybook](https://storybook.js.org/).
# Build for development
# Usage in a `react` project
For example, in a fresh `create-react-app` project
```sh
yarn add @quri/squiggle-components
```
Add to `App.js`:
```jsx
import { SquiggleEditor } from "@quri/squiggle-components";
<SquiggleEditor initialSquiggleString="x = beta(3, 10); x + 20" />;
```
# Build storybook for development
We assume that you had run `yarn` at monorepo level, installing dependencies.
@ -24,6 +42,5 @@ yarn start
And build artefacts for production,
```sh
yarn bundle # builds components library
yarn build # builds storybook app
```

View File

@ -1,12 +1,18 @@
{
"name": "@quri/squiggle-components",
"version": "0.2.9",
"licence": "MIT",
"version": "0.2.14",
"license": "MIT",
"dependencies": {
"antd": "^4.20.1",
"react-ace": "10.1.0",
"@quri/squiggle-lang": "^0.2.7",
"react-dom": "^18.1.0",
"vega": "^5.22.1",
"vega-embed": "^6.20.6",
"vega-lite": "^5.2.0",
"react-vega": "^7.5.0",
"react": "^18.1.0",
"@react-hook/size": "^2.1.2",
"lodash": "^4.17.21",
"styled-components": "^5.3.5"
},
"devDependencies": {
@ -26,31 +32,25 @@
"webpack": "^5.72.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.8.1",
"@quri/squiggle-lang": "0.2.5",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1.1",
"@testing-library/user-event": "^14.1.1",
"@types/jest": "^27.4.0",
"web-vitals": "^2.1.4",
"@types/lodash": "^4.14.182",
"@types/node": "^17.0.29",
"@types/react": "^18.0.3",
"@types/react-dom": "^18.0.2",
"cross-env": "^7.0.3",
"lodash": "^4.17.21",
"react": "^18.1.0",
"react-scripts": "5.0.1",
"react-vega": "^7.5.0",
"tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "^4.6.3",
"vega": "^5.22.1",
"vega-embed": "^6.20.6",
"vega-lite": "^5.2.0",
"web-vitals": "^2.1.4",
"webpack-cli": "^4.9.2"
},
"scripts": {
"start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",
"build": "tsc -b && build-storybook -s public",
"build:package": "tsc -b",
"bundle": "webpack",
"all": "yarn bundle && yarn build",
"lint": "prettier --check .",
@ -88,6 +88,7 @@
"@types/react": "17.0.43"
},
"source": "./src/index.ts",
"main": "dist/bundle.js",
"browser": "dist/bundle.js",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts"
}

View File

@ -6,6 +6,7 @@ import { distributionErrorToString } from "@quri/squiggle-lang";
import { createClassFromSpec } from "react-vega";
import * as chartSpecification from "../vega-specs/spec-distributions.json";
import { ErrorBox } from "./ErrorBox";
import styled from "styled-components";
let SquiggleVegaChart = createClassFromSpec({
spec: chartSpecification as Spec,
@ -24,19 +25,21 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
}: DistributionChartProps) => {
let shape = distribution.pointSet();
if (shape.tag === "Ok") {
return (
let widthProp = width ? width - 20 : undefined;
var result = (
<SquiggleVegaChart
data={{ con: shape.value.continuous, dis: shape.value.discrete }}
width={width - 20}
width={widthProp}
height={height}
actions={false}
/>
);
} else {
return (
var result = (
<ErrorBox heading="Distribution Error">
{distributionErrorToString(shape.value)}
</ErrorBox>
);
}
return result;
};

View File

@ -3,10 +3,12 @@ import _ from "lodash";
import styled from "styled-components";
import {
run,
runPartial,
errorValueToString,
squiggleExpression,
bindings,
samplingParams,
} from "@quri/squiggle-lang";
import type { samplingParams } from "@quri/squiggle-lang";
import { NumberShower } from "./NumberShower";
import { DistributionChart } from "./DistributionChart";
import { ErrorBox } from "./ErrorBox";
@ -44,6 +46,8 @@ export const VariableBox: React.FC<{
);
};
let RecordKeyHeader = styled.h3``;
export interface SquiggleItemProps {
/** The input string for squiggle */
expression: squiggleExpression;
@ -104,6 +108,17 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
))}
</VariableBox>
);
case "record":
return (
<VariableBox heading="Record">
{Object.entries(expression.value).map(([key, r]) => (
<>
<RecordKeyHeader>{key}</RecordKeyHeader>
<SquiggleItem expression={r} width={width - 20} height={50} />
</>
))}
</VariableBox>
);
default:
return (
<ErrorBox heading="No Viewer">
@ -135,40 +150,43 @@ export interface SquiggleChartProps {
/** CSS width of the element */
width?: number;
height?: number;
/** Bindings of previous variables declared */
bindings?: bindings;
}
const ChartWrapper = styled.div`
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
`;
export const SquiggleChart: React.FC<SquiggleChartProps> = ({
squiggleString = "",
sampleCount = 1000,
outputXYPoints = 1000,
onChange = () => {},
height = 60,
bindings = {},
width = NaN,
}: SquiggleChartProps) => {
const target = React.useRef(null);
const [componentWidth] = useSize(target);
// I would have wanted to just use componentWidth, but this created infinite loops with SquiggleChart.stories.
//So you can manually add a width, as an escape hatch.
let _width = width || componentWidth;
let samplingInputs: samplingParams = {
sampleCount: sampleCount,
xyPointLength: outputXYPoints,
};
let expressionResult = run(squiggleString, samplingInputs);
let expressionResult = run(squiggleString, bindings, samplingInputs);
let internal: JSX.Element;
if (expressionResult.tag === "Ok") {
let expression = expressionResult.value;
onChange(expression);
internal = (
<SquiggleItem expression={expression} width={_width} height={height} />
<SquiggleItem expression={expression} width={width} height={height} />
);
} else {
// At this point, we came across an error. What was our error?
internal = (
<ErrorBox heading={"Parse Error"}>
{errorValueToString(expressionResult.value)}
</ErrorBox>
);
}
return <div ref={target}>{internal}</div>;
return <ChartWrapper>{internal}</ChartWrapper>;
};

View File

@ -3,7 +3,9 @@ import * as ReactDOM from "react-dom";
import { SquiggleChart } from "./SquiggleChart";
import { CodeEditor } from "./CodeEditor";
import styled from "styled-components";
import type { squiggleExpression } from "@quri/squiggle-lang";
import type { squiggleExpression, bindings } from "@quri/squiggle-lang";
import { runPartial, errorValueToString } from "@quri/squiggle-lang";
import { ErrorBox } from "./ErrorBox";
export interface SquiggleEditorProps {
/** The input string for squiggle */
@ -26,6 +28,8 @@ export interface SquiggleEditorProps {
onChange?(expr: squiggleExpression): void;
/** The width of the element */
width: number;
/** Previous variable declarations */
bindings: bindings;
}
const Input = styled.div`
@ -46,6 +50,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
diagramCount,
onChange,
environment,
bindings = {},
}: SquiggleEditorProps) => {
let [expression, setExpression] = React.useState(initialSquiggleString);
return (
@ -71,6 +76,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
diagramCount={diagramCount}
environment={environment}
onChange={onChange}
bindings={bindings}
/>
</div>
);
@ -107,3 +113,76 @@ export function renderSquiggleEditorToDom(props: SquiggleEditorProps) {
);
return parent;
}
export interface SquigglePartialProps {
/** The input string for squiggle */
initialSquiggleString?: string;
/** If the output requires monte carlo sampling, the amount of samples */
sampleCount?: number;
/** The amount of points returned to draw the distribution */
outputXYPoints?: number;
kernelWidth?: number;
pointDistLength?: number;
/** If the result is a function, where the function starts */
diagramStart?: number;
/** If the result is a function, where the function ends */
diagramStop?: number;
/** If the result is a function, how many points along the function it samples */
diagramCount?: number;
/** when the environment changes. Used again for notebook magic*/
onChange?(expr: bindings): void;
/** The width of the element */
width: number;
/** Previously declared variables */
bindings: bindings;
}
export let SquigglePartial: React.FC<SquigglePartialProps> = ({
initialSquiggleString = "",
onChange,
bindings,
}: SquigglePartialProps) => {
let [expression, setExpression] = React.useState(initialSquiggleString);
let squiggleResult = runPartial(expression, bindings);
if (squiggleResult.tag == "Ok") {
if (onChange) onChange(squiggleResult.value);
}
return (
<div>
<Input>
<CodeEditor
value={expression}
onChange={setExpression}
oneLine={true}
showGutter={false}
height={20}
/>
</Input>
{squiggleResult.tag == "Error" ? (
<ErrorBox heading="Error">
{errorValueToString(squiggleResult.value)}
</ErrorBox>
) : (
<></>
)}
</div>
);
};
export function renderSquigglePartialToDom(props: SquigglePartialProps) {
let parent = document.createElement("div");
ReactDOM.render(
<SquigglePartial
{...props}
onChange={(bindings) => {
// @ts-ignore
parent.value = bindings;
parent.dispatchEvent(new CustomEvent("input"));
if (props.onChange) props.onChange(bindings);
}}
/>,
parent
);
return parent;
}

View File

@ -1,11 +1,9 @@
import _ from "lodash";
import React, { FC, useState } from "react";
import React, { FC, ReactElement, useState } from "react";
import ReactDOM from "react-dom";
import { SquiggleChart } from "./SquiggleChart";
import CodeEditor from "./CodeEditor";
import { Form, Input, Row, Col } from "antd";
import styled from "styled-components";
import "antd/dist/antd.css";
interface FieldFloatProps {
label: string;
@ -14,10 +12,19 @@ interface FieldFloatProps {
onChange: (value: number) => void;
}
const Input = styled.input``;
const FormItem = (props: { label: string; children: ReactElement }) => (
<div>
<label>{props.label}</label>
{props.children}
</div>
);
function FieldFloat(Props: FieldFloatProps) {
let [contents, setContents] = useState(Props.value + "");
return (
<Form.Item label={Props.label}>
<FormItem label={Props.label}>
<Input
value={contents}
className={Props.className ? Props.className : ""}
@ -29,7 +36,7 @@ function FieldFloat(Props: FieldFloatProps) {
}
}}
/>
</Form.Item>
</FormItem>
);
}
@ -65,6 +72,12 @@ const Display = styled.div<TitleProps>`
max-height: ${(props) => props.maxHeight}px;
`;
const Row = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
`;
const Col = styled.div``;
let SquigglePlayground: FC<Props> = ({
initialSquiggleString = "",
height = 300,
@ -79,7 +92,7 @@ let SquigglePlayground: FC<Props> = ({
return (
<ShowBox height={height}>
<Row>
<Col span={12}>
<Col>
<CodeEditor
value={squiggleString}
onChange={setSquiggleString}
@ -88,7 +101,7 @@ let SquigglePlayground: FC<Props> = ({
height={height - 3}
/>
</Col>
<Col span={12}>
<Col>
<Display maxHeight={height - 3}>
<SquiggleChart
squiggleString={squiggleString}

View File

@ -1,7 +1,9 @@
export { SquiggleChart } from "./components/SquiggleChart";
export {
SquiggleEditor,
SquigglePartial,
renderSquiggleEditorToDom,
renderSquigglePartialToDom,
} from "./components/SquiggleEditor";
import SquigglePlayground, {
renderSquigglePlaygroundToDom,

View File

@ -3,6 +3,7 @@
"description": "A basic area chart example",
"width": 500,
"height": 100,
"autosize": "fit",
"padding": 5,
"data": [
{

View File

@ -2,7 +2,6 @@ node_modules
shell.nix
.cache
.direnv
src
__tests__
lib
examples

View File

@ -1,3 +1,6 @@
[![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-lang.svg)](https://www.npmjs.com/package/@quri/squiggle-lang)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/quantified-uncertainty/squiggle/blob/develop/LICENSE)
# Squiggle language
## Build for development
@ -15,13 +18,16 @@ Other:
```sh
yarn start # listens to files and recompiles at every mutation
yarn test
yarn test:watch # keeps an active session and runs all tests at every mutation
# where o := open in osx and o := xdg-open in linux,
yarn coverage; o _coverage/index.html # produces coverage report and opens it in browser
yarn coverage:rescript; o _coverage/index.html # produces coverage report and opens it in browser
```
## Information
## Distributing this package or using this package from other monorepo packages
As it says in the other `packages/*/README.md`s, building this package is an essential step of building other packages.
# Information
Squiggle is a language for representing probability distributions, as well as functions that return probability distributions. Its original intended use is for improving epistemics around EA decisions.
@ -34,11 +40,3 @@ This package is mainly written in [ReScript](https://rescript-lang.org/), but ha
ReScript has an interesting philosophy of not providing much in the way of effective build tools. Every ReScript file is compiled into `.bs.js` and `.gen.ts` files with the same name and same location, and then you can use these files in other `.js` files to create your program. To generate these files to build the package, you run `yarn build`.
`.gen.ts` files are created by the [`@genType`](https://rescript-lang.org/docs/gentype/latest/getting-started) decorator, which creates typescript typings for needed parts of the codebase so that they can be easily used in typescript. These .gen.ts files reference the .bs.js files generated by rescript.
### Errors regarding the `rationale` package
You may notice sometimes, that there are errors about the `rationale` package. If you ever get these errors, `yarn build` should fix this issue. These errors occur because `yarn build` also needs to create build files that are in `node_modules`. So if you replace `node_modules` you may need to rebuild to get those files back.
## Distributing this package or using this package from other monorepo packages
As it says in the other `packages/*/README.md`s, building this package is an essential step of building other packages.

View File

@ -1,41 +0,0 @@
open Jest
open TestHelpers
describe("Continuous and discrete splits", () => {
makeTest(
"splits (1)",
SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete([1.432, 1.33455, 2.0]),
([1.432, 1.33455, 2.0], E.FloatFloatMap.empty()),
)
makeTest(
"splits (2)",
SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete([
1.432,
1.33455,
2.0,
2.0,
2.0,
2.0,
]) |> (((c, disc)) => (c, disc |> E.FloatFloatMap.toArray)),
([1.432, 1.33455], [(2.0, 4.0)]),
)
let makeDuplicatedArray = count => {
let arr = Belt.Array.range(1, count) |> E.A.fmap(float_of_int)
let sorted = arr |> Belt.SortArray.stableSortBy(_, compare)
E.A.concatMany([sorted, sorted, sorted, sorted]) |> Belt.SortArray.stableSortBy(_, compare)
}
let (_, discrete1) = SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete(
makeDuplicatedArray(10),
)
let toArr1 = discrete1 |> E.FloatFloatMap.toArray
makeTest("splitMedium at count=10", toArr1 |> Belt.Array.length, 10)
let (_c, discrete2) = SampleSetDist_ToPointSet.Internals.T.splitContinuousAndDiscrete(
makeDuplicatedArray(500),
)
let toArr2 = discrete2 |> E.FloatFloatMap.toArray
makeTest("splitMedium at count=500", toArr2 |> Belt.Array.length, 500)
// makeTest("foo", [] |> Belt.Array.length, 500)
})

View File

@ -0,0 +1,48 @@
open Jest
open TestHelpers
let prepareInputs = (ar, minWeight) =>
E.A.Floats.Sorted.splitContinuousAndDiscreteForMinWeight(ar, ~minDiscreteWeight=minWeight) |> (
((c, disc)) => (c, disc |> E.FloatFloatMap.toArray)
)
describe("Continuous and discrete splits", () => {
makeTest(
"is empty, with no common elements",
prepareInputs([1.432, 1.33455, 2.0], 2),
([1.33455, 1.432, 2.0], []),
)
makeTest(
"only stores 3.5 as discrete when minWeight is 3",
prepareInputs([1.432, 1.33455, 2.0, 2.0, 3.5, 3.5, 3.5], 3),
([1.33455, 1.432, 2.0, 2.0], [(3.5, 3.0)]),
)
makeTest(
"doesn't store 3.5 as discrete when minWeight is 5",
prepareInputs([1.432, 1.33455, 2.0, 2.0, 3.5, 3.5, 3.5], 5),
([1.33455, 1.432, 2.0, 2.0, 3.5, 3.5, 3.5], []),
)
let makeDuplicatedArray = count => {
let arr = Belt.Array.range(1, count) |> E.A.fmap(float_of_int)
let sorted = arr |> Belt.SortArray.stableSortBy(_, compare)
E.A.concatMany([sorted, sorted, sorted, sorted]) |> Belt.SortArray.stableSortBy(_, compare)
}
let (_, discrete1) = E.A.Floats.Sorted.splitContinuousAndDiscreteForMinWeight(
makeDuplicatedArray(10),
~minDiscreteWeight=2,
)
let toArr1 = discrete1 |> E.FloatFloatMap.toArray
makeTest("splitMedium at count=10", toArr1 |> Belt.Array.length, 10)
let (_c, discrete2) = E.A.Floats.Sorted.splitContinuousAndDiscreteForMinWeight(
makeDuplicatedArray(500),
~minDiscreteWeight=2,
)
let toArr2 = discrete2 |> E.FloatFloatMap.toArray
makeTest("splitMedium at count=500", toArr2 |> Belt.Array.length, 500)
// makeTest("foo", [] |> Belt.Array.length, 500)
})

View File

@ -0,0 +1,142 @@
open Jest
// open Expect
open Reducer_Expression_ExpressionBuilder
open Reducer_TestMacroHelpers
module ExpressionT = Reducer_Expression_T
let exampleExpression = eNumber(1.)
let exampleExpressionY = eSymbol("y")
let exampleStatement = eLetStatement("y", eNumber(1.))
let exampleStatementX = eLetStatement("y", eSymbol("x"))
let exampleStatementZ = eLetStatement("z", eSymbol("y"))
// If it is not a mactro then it is not expanded
testMacro([], exampleExpression, "Ok(1)")
describe("bindStatement", () => {
// A statement is bound by the bindings created by the previous statement
testMacro([], eBindStatement(eBindings([]), exampleStatement), "Ok((:$setBindings {} :y 1))")
// Then it answers the bindings for the next statement when reduced
testMacroEval([], eBindStatement(eBindings([]), exampleStatement), "Ok({y: 1})")
// Now let's feed a binding to see what happens
testMacro(
[],
eBindStatement(eBindings([("x", EvNumber(2.))]), exampleStatementX),
"Ok((:$setBindings {x: 2} :y 2))",
)
// An expression does not return a binding, thus error
testMacro([], eBindStatement(eBindings([]), exampleExpression), "Error(Assignment expected)")
// When bindings from previous statement are missing the context is injected. This must be the first statement of a block
testMacro(
[("z", EvNumber(99.))],
eBindStatementDefault(exampleStatement),
"Ok((:$setBindings {z: 99} :y 1))",
)
})
describe("bindExpression", () => {
// x is simply bound in the expression
testMacro([], eBindExpression(eBindings([("x", EvNumber(2.))]), eSymbol("x")), "Ok(2)")
// When an let statement is the end expression then bindings are returned
testMacro(
[],
eBindExpression(eBindings([("x", EvNumber(2.))]), exampleStatement),
"Ok((:$exportBindings (:$setBindings {x: 2} :y 1)))",
)
// Now let's reduce that expression
testMacroEval(
[],
eBindExpression(eBindings([("x", EvNumber(2.))]), exampleStatement),
"Ok({x: 2,y: 1})",
)
// When bindings are missing the context is injected. This must be the first and last statement of a block
testMacroEval(
[("z", EvNumber(99.))],
eBindExpressionDefault(exampleStatement),
"Ok({y: 1,z: 99})",
)
})
describe("block", () => {
// Block with a single expression
testMacro([], eBlock(list{exampleExpression}), "Ok((:$$bindExpression 1))")
testMacroEval([], eBlock(list{exampleExpression}), "Ok(1)")
// Block with a single statement
testMacro([], eBlock(list{exampleStatement}), "Ok((:$$bindExpression (:$let :y 1)))")
testMacroEval([], eBlock(list{exampleStatement}), "Ok({y: 1})")
// Block with a statement and an expression
testMacro(
[],
eBlock(list{exampleStatement, exampleExpressionY}),
"Ok((:$$bindExpression (:$$bindStatement (:$let :y 1)) :y))",
)
testMacroEval([], eBlock(list{exampleStatement, exampleExpressionY}), "Ok(1)")
// Block with a statement and another statement
testMacro(
[],
eBlock(list{exampleStatement, exampleStatementZ}),
"Ok((:$$bindExpression (:$$bindStatement (:$let :y 1)) (:$let :z :y)))",
)
testMacroEval([], eBlock(list{exampleStatement, exampleStatementZ}), "Ok({y: 1,z: 1})")
// Block inside a block
testMacro(
[],
eBlock(list{eBlock(list{exampleExpression})}),
"Ok((:$$bindExpression (:$$block 1)))",
)
testMacroEval([], eBlock(list{eBlock(list{exampleExpression})}), "Ok(1)")
// Block assigned to a variable
testMacro(
[],
eBlock(list{eLetStatement("z", eBlock(list{eBlock(list{exampleExpressionY})}))}),
"Ok((:$$bindExpression (:$let :z (:$$block (:$$block :y)))))",
)
testMacroEval(
[],
eBlock(list{eLetStatement("z", eBlock(list{eBlock(list{exampleExpressionY})}))}),
"Ok({z: :y})",
)
// Empty block
testMacro([], eBlock(list{}), "Ok(:undefined block)") //TODO: should be an error
// :$$block (:$$block (:$let :y (:add :x 1)) :y)"
testMacro(
[],
eBlock(list{
eBlock(list{
eLetStatement("y", eFunction("add", list{eSymbol("x"), eNumber(1.)})),
eSymbol("y"),
}),
}),
"Ok((:$$bindExpression (:$$block (:$let :y (:add :x 1)) :y)))",
)
MyOnly.testMacroEval(
[("x", EvNumber(1.))],
eBlock(list{
eBlock(list{
eLetStatement("y", eFunction("add", list{eSymbol("x"), eNumber(1.)})),
eSymbol("y"),
}),
}),
"Ok(2)",
)
})
describe("lambda", () => {
// assign a lambda to a variable
let lambdaExpression = eFunction("$$lambda", list{eArrayString(["y"]), exampleExpressionY})
testMacro([], lambdaExpression, "Ok(lambda(y=>internal))")
// call a lambda
let callLambdaExpression = list{lambdaExpression, eNumber(1.)}->ExpressionT.EList
testMacro([], callLambdaExpression, "Ok(((:$$lambda [y] :y) 1))")
testMacroEval([], callLambdaExpression, "Ok(1)")
// Parameters shadow the outer scope
testMacroEval([("y", EvNumber(666.))], callLambdaExpression, "Ok(1)")
// When not shadowed by the parameters, the outer scope variables are available
let lambdaExpression = eFunction(
"$$lambda",
list{eArrayString(["z"]), eFunction("add", list{eSymbol("y"), eSymbol("z")})},
)
let callLambdaExpression = eList(list{lambdaExpression, eNumber(1.)})
testMacroEval([("y", EvNumber(666.))], callLambdaExpression, "Ok(667)")
})

View File

@ -0,0 +1,6 @@
open Jest
open Expect
test("dummy", () => {
expect(true)->toBe(true)
})

View File

@ -1,4 +1,4 @@
module Expression = Reducer.Expression
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue
module ErrorValue = Reducer_ErrorValue
@ -14,47 +14,18 @@ let unwrapRecord = rValue =>
)
let expectParseToBe = (expr: string, answer: string) =>
Reducer.parse(expr)->Expression.toStringResult->expect->toBe(answer)
let expectParseOuterToBe = (expr: string, answer: string) =>
Reducer.parseOuter(expr)->Expression.toStringResult->expect->toBe(answer)
let expectParsePartialToBe = (expr: string, answer: string) =>
Reducer.parsePartial(expr)->Expression.toStringResult->expect->toBe(answer)
Reducer.parse(expr)->ExpressionT.toStringResult->expect->toBe(answer)
let expectEvalToBe = (expr: string, answer: string) =>
Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
let expectEvalBindingsToBe = (expr: string, bindings: Reducer.externalBindings, answer: string) =>
Reducer.evaluateUsingOptions(
expr,
~externalBindings=Some(bindings),
~isPartial=None,
~environment=None,
)
Reducer.evaluateUsingOptions(expr, ~externalBindings=Some(bindings), ~environment=None)
->ExpressionValue.toStringResult
->expect
->toBe(answer)
let expectEvalPartialBindingsToBe = (
expr: string,
bindings: Reducer.externalBindings,
answer: string,
) =>
Reducer.evaluateUsingOptions(
expr,
~externalBindings=Some(bindings),
~isPartial=Some(true),
~environment=None,
)
->unwrapRecord
->ExpressionValue.toStringResultRecord
->expect
->toBe(answer)
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
let testParseOuterToBe = (expr, answer) => test(expr, () => expectParseOuterToBe(expr, answer))
let testParsePartialToBe = (expr, answer) => test(expr, () => expectParsePartialToBe(expr, answer))
let testDescriptionParseToBe = (desc, expr, answer) =>
test(desc, () => expectParseToBe(expr, answer))
@ -62,34 +33,16 @@ let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answe
let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer))
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
let testEvalPartialBindingsToBe = (expr, bindingsList, answer) =>
test(expr, () => expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
module MySkip = {
let testParseToBe = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
let testParseOuterToBe = (expr, answer) =>
Skip.test(expr, () => expectParseOuterToBe(expr, answer))
let testParsePartialToBe = (expr, answer) =>
Skip.test(expr, () => expectParsePartialToBe(expr, answer))
let testEvalToBe = (expr, answer) => Skip.test(expr, () => expectEvalToBe(expr, answer))
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
Skip.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
let testEvalPartialBindingsToBe = (expr, bindingsList, answer) =>
Skip.test(expr, () =>
expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)
)
}
module MyOnly = {
let testParseToBe = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer))
let testParseOuterToBe = (expr, answer) =>
Only.test(expr, () => expectParseOuterToBe(expr, answer))
let testParsePartialToBe = (expr, answer) =>
Only.test(expr, () => expectParsePartialToBe(expr, answer))
let testEvalToBe = (expr, answer) => Only.test(expr, () => expectEvalToBe(expr, answer))
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
Only.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
let testEvalPartialBindingsToBe = (expr, bindingsList, answer) =>
Only.test(expr, () =>
expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)
)
}

View File

@ -0,0 +1,81 @@
open Jest
open Expect
module Macro = Reducer_Expression_Macro
module Bindings = Reducer_Expression_Bindings
module Expression = Reducer_Expression
module ExpressionValue = ReducerInterface_ExpressionValue
module T = Reducer_Expression_T
let testMacro_ = (
tester,
bindArray: array<(string, ExpressionValue.expressionValue)>,
expr: T.expression,
expectedCode: string,
) => {
let bindings = Belt.Map.String.fromArray(bindArray)
tester(expr->T.toString, () =>
expr
->Macro.expandMacroCall(
bindings,
ExpressionValue.defaultEnvironment,
Expression.reduceExpression,
)
->T.toStringResult
->expect
->toEqual(expectedCode)
)
}
let testMacroEval_ = (
tester,
bindArray: array<(string, ExpressionValue.expressionValue)>,
expr: T.expression,
expectedValue: string,
) => {
let bindings = Belt.Map.String.fromArray(bindArray)
tester(expr->T.toString, () =>
expr
->Macro.doMacroCall(bindings, ExpressionValue.defaultEnvironment, Expression.reduceExpression)
->ExpressionValue.toStringResult
->expect
->toEqual(expectedValue)
)
}
let testMacro = (
bindArray: array<(string, ExpressionValue.expressionValue)>,
expr: T.expression,
expectedExpr: string,
) => testMacro_(test, bindArray, expr, expectedExpr)
let testMacroEval = (
bindArray: array<(string, ExpressionValue.expressionValue)>,
expr: T.expression,
expectedValue: string,
) => testMacroEval_(test, bindArray, expr, expectedValue)
module MySkip = {
let testMacro = (
bindArray: array<(string, ExpressionValue.expressionValue)>,
expr: T.expression,
expectedExpr: string,
) => testMacro_(Skip.test, bindArray, expr, expectedExpr)
let testMacroEval = (
bindArray: array<(string, ExpressionValue.expressionValue)>,
expr: T.expression,
expectedValue: string,
) => testMacroEval_(Skip.test, bindArray, expr, expectedValue)
}
module MyOnly = {
let testMacro = (
bindArray: array<(string, ExpressionValue.expressionValue)>,
expr: T.expression,
expectedExpr: string,
) => testMacro_(Only.test, bindArray, expr, expectedExpr)
let testMacroEval = (
bindArray: array<(string, ExpressionValue.expressionValue)>,
expr: T.expression,
expectedValue: string,
) => testMacroEval_(Only.test, bindArray, expr, expectedValue)
}

View File

@ -1,60 +1,63 @@
// TODO: Reimplement with usual parse
open Jest
open Reducer_TestHelpers
describe("Parse for Bindings", () => {
testParseOuterToBe("x", "Ok((:$$bindExpression (:$$bindings) :x))")
testParseOuterToBe("x+1", "Ok((:$$bindExpression (:$$bindings) (:add :x 1)))")
testParseOuterToBe(
"y = x+1; y",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) :y))",
)
})
// describe("Parse for Bindings", () => {
// testParseOuterToBe("x", "Ok((:$$bindExpression (:$$bindings) :x))")
// testParseOuterToBe("x+1", "Ok((:$$bindExpression (:$$bindings) (:add :x 1)))")
// testParseOuterToBe(
// "y = x+1; y",
// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) :y))",
// )
// })
describe("Parse Partial", () => {
testParsePartialToBe(
"x",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) :x) (:$exportVariablesExpression)))",
)
testParsePartialToBe(
"y=x",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y :x)) (:$exportVariablesExpression)))",
)
testParsePartialToBe(
"y=x+1",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$exportVariablesExpression)))",
)
testParsePartialToBe(
"y = x+1; z = y",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$let :z :y)) (:$exportVariablesExpression)))",
)
})
// describe("Parse Partial", () => {
// testParsePartialToBe(
// "x",
// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) :x) (:$exportVariablesExpression)))",
// )
// testParsePartialToBe(
// "y=x",
// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y :x)) (:$exportVariablesExpression)))",
// )
// testParsePartialToBe(
// "y=x+1",
// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$exportVariablesExpression)))",
// )
// testParsePartialToBe(
// "y = x+1; z = y",
// "Ok((:$$bindExpression (:$$bindStatement (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$let :z :y)) (:$exportVariablesExpression)))",
// )
// })
describe("Eval with Bindings", () => {
testEvalBindingsToBe("x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(1)")
testEvalBindingsToBe("x+1", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)")
testParseToBe("y = x+1; y", "Ok((:$$block (:$$block (:$let :y (:add :x 1)) :y)))")
testEvalBindingsToBe("y = x+1; y", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)")
testEvalBindingsToBe("y = x+1", list{("x", ExpressionValue.EvNumber(1.))}, "Ok({x: 1,y: 2})")
})
/*
Partial code is a partial code fragment that is cut out from a larger code.
Therefore it does not end with an expression.
*/
describe("Eval Partial", () => {
testEvalPartialBindingsToBe(
// A partial cannot end with an expression
"x",
list{("x", ExpressionValue.EvNumber(1.))},
"Error(Assignment expected)",
)
testEvalPartialBindingsToBe("y=x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok({x: 1, y: 1})")
testEvalPartialBindingsToBe(
"y=x+1",
list{("x", ExpressionValue.EvNumber(1.))},
"Ok({x: 1, y: 2})",
)
testEvalPartialBindingsToBe(
"y = x+1; z = y",
list{("x", ExpressionValue.EvNumber(1.))},
"Ok({x: 1, y: 2, z: 2})",
)
})
// describe("Eval Partial", () => {
// testEvalPartialBindingsToBe(
// // A partial cannot end with an expression
// "x",
// list{("x", ExpressionValue.EvNumber(1.))},
// "Error(Assignment expected)",
// )
// testEvalPartialBindingsToBe("y=x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok({x: 1,y: 1})")
// testEvalPartialBindingsToBe(
// "y=x+1",
// list{("x", ExpressionValue.EvNumber(1.))},
// "Ok({x: 1,y: 2})",
// )
// testEvalPartialBindingsToBe(
// "y = x+1; z = y",
// list{("x", ExpressionValue.EvNumber(1.))},
// "Ok({x: 1,y: 2,z: 2})",
// )
// })

View File

@ -2,8 +2,8 @@ open Jest
open Reducer_TestHelpers
describe("Parse function assignment", () => {
testParseToBe("f(x)=x", "Ok((:$let :f (:$lambda (x) :x)))")
testParseToBe("f(x)=2*x", "Ok((:$let :f (:$lambda (x) (:multiply 2 :x))))")
testParseToBe("f(x)=x", "Ok((:$$block (:$let :f (:$$lambda [x] (:$$block :x)))))")
testParseToBe("f(x)=2*x", "Ok((:$$block (:$let :f (:$$lambda [x] (:$$block (:multiply 2 :x))))))")
//MathJs does not allow blocks in function definitions
})

View File

@ -10,46 +10,39 @@ describe("reducer using mathjs parse", () => {
// Those tests toString that we are converting mathjs parse tree to what we need
describe("expressions", () => {
testParseToBe("1", "Ok(1)")
testParseToBe("(1)", "Ok(1)")
testParseToBe("1+2", "Ok((:add 1 2))")
testParseToBe("1+2", "Ok((:add 1 2))")
testParseToBe("1+2", "Ok((:add 1 2))")
testParseToBe("1+2*3", "Ok((:add 1 (:multiply 2 3)))")
testParseToBe("1", "Ok((:$$block 1))")
testParseToBe("(1)", "Ok((:$$block 1))")
testParseToBe("1+2", "Ok((:$$block (:add 1 2)))")
testParseToBe("1+2*3", "Ok((:$$block (:add 1 (:multiply 2 3))))")
})
describe("arrays", () => {
//Note. () is a empty list in Lisp
// The only builtin structure in Lisp is list. There are no arrays
// [1,2,3] becomes (1 2 3)
testDescriptionParseToBe("empty", "[]", "Ok(())")
testParseToBe("[1, 2, 3]", "Ok((1 2 3))")
testParseToBe("['hello', 'world']", "Ok(('hello' 'world'))")
testDescriptionParseToBe("index", "([0,1,2])[1]", "Ok((:$atIndex (0 1 2) (1)))")
testDescriptionParseToBe("empty", "[]", "Ok((:$$block ()))")
testParseToBe("[1, 2, 3]", "Ok((:$$block (1 2 3)))")
testParseToBe("['hello', 'world']", "Ok((:$$block ('hello' 'world')))")
testDescriptionParseToBe("index", "([0,1,2])[1]", "Ok((:$$block (:$atIndex (0 1 2) (1))))")
})
describe("records", () => {
testDescriptionParseToBe("define", "{a: 1, b: 2}", "Ok((:$constructRecord (('a' 1) ('b' 2))))")
testDescriptionParseToBe(
"define",
"{a: 1, b: 2}",
"Ok((:$$block (:$constructRecord (('a' 1) ('b' 2)))))",
)
testDescriptionParseToBe(
"use",
"{a: 1, b: 2}.a",
"Ok((:$atIndex (:$constructRecord (('a' 1) ('b' 2))) ('a')))",
"Ok((:$$block (:$atIndex (:$constructRecord (('a' 1) ('b' 2))) ('a'))))",
)
})
describe("multi-line", () => {
testParseToBe("1; 2", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) 1) 2))")
testParseToBe(
"1+1; 2+1",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:add 1 1)) (:add 2 1)))",
)
testParseToBe("1; 2", "Ok((:$$block (:$$block 1 2)))")
testParseToBe("1+1; 2+1", "Ok((:$$block (:$$block (:add 1 1) (:add 2 1))))")
})
describe("assignment", () => {
testParseToBe(
"x=1; x",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x 1)) :x))",
)
testParseToBe(
"x=1+1; x+1",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x (:add 1 1))) (:add :x 1)))",
)
testParseToBe("x=1; x", "Ok((:$$block (:$$block (:$let :x 1) :x)))")
testParseToBe("x=1+1; x+1", "Ok((:$$block (:$$block (:$let :x (:add 1 1)) (:add :x 1))))")
})
})
@ -70,13 +63,13 @@ describe("eval", () => {
})
describe("arrays", () => {
test("empty array", () => expectEvalToBe("[]", "Ok([])"))
testEvalToBe("[1, 2, 3]", "Ok([1, 2, 3])")
testEvalToBe("['hello', 'world']", "Ok(['hello', 'world'])")
testEvalToBe("[1, 2, 3]", "Ok([1,2,3])")
testEvalToBe("['hello', 'world']", "Ok(['hello','world'])")
testEvalToBe("([0,1,2])[1]", "Ok(1)")
testDescriptionEvalToBe("index not found", "([0,1,2])[10]", "Error(Array index not found: 10)")
})
describe("records", () => {
test("define", () => expectEvalToBe("{a: 1, b: 2}", "Ok({a: 1, b: 2})"))
test("define", () => expectEvalToBe("{a: 1, b: 2}", "Ok({a: 1,b: 2})"))
test("index", () => expectEvalToBe("{a: 1}.a", "Ok(1)"))
test("index not found", () => expectEvalToBe("{a: 1}.b", "Error(Record property not found: b)"))
})
@ -91,7 +84,7 @@ describe("eval", () => {
testEvalToBe("x=1; y=x+1; y+1", "Ok(3)")
testEvalToBe("1; x=1", "Error(Assignment expected)")
testEvalToBe("1; 1", "Error(Assignment expected)")
testEvalToBe("x=1; x=1", "Error(Expression expected)")
testEvalToBe("x=1; x=1", "Ok({x: 1})")
})
})

View File

@ -92,11 +92,11 @@ describe("eval on distribution functions", () => {
testEval("log(2, uniform(5,8))", "Ok(Sample Set Distribution)")
testEval(
"log(normal(5,2), 3)",
"Error(Distribution Math Error: Logarithm of input error: First input must completely greater than 0)",
"Error(Distribution Math Error: Logarithm of input error: First input must be completely greater than 0)",
)
testEval(
"log(normal(5,2), normal(10,1))",
"Error(Distribution Math Error: Logarithm of input error: First input must completely greater than 0)",
"Error(Distribution Math Error: Logarithm of input error: First input must be completely greater than 0)",
)
testEval("log(uniform(5,8))", "Ok(Sample Set Distribution)")
testEval("log10(uniform(5,8))", "Ok(Sample Set Distribution)")
@ -119,27 +119,33 @@ describe("eval on distribution functions", () => {
describe("parse on distribution functions", () => {
describe("power", () => {
testParse("normal(5,2) ^ normal(5,1)", "Ok((:pow (:normal 5 2) (:normal 5 1)))")
testParse("3 ^ normal(5,1)", "Ok((:pow 3 (:normal 5 1)))")
testParse("normal(5,2) ^ 3", "Ok((:pow (:normal 5 2) 3))")
testParse("normal(5,2) ^ normal(5,1)", "Ok((:$$block (:pow (:normal 5 2) (:normal 5 1))))")
testParse("3 ^ normal(5,1)", "Ok((:$$block (:pow 3 (:normal 5 1))))")
testParse("normal(5,2) ^ 3", "Ok((:$$block (:pow (:normal 5 2) 3)))")
})
describe("subtraction", () => {
testParse("10 - normal(5,1)", "Ok((:subtract 10 (:normal 5 1)))")
testParse("normal(5,1) - 10", "Ok((:subtract (:normal 5 1) 10))")
testParse("10 - normal(5,1)", "Ok((:$$block (:subtract 10 (:normal 5 1))))")
testParse("normal(5,1) - 10", "Ok((:$$block (:subtract (:normal 5 1) 10)))")
})
describe("pointwise arithmetic expressions", () => {
testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")
testParse(
~skip=true,
"normal(5,2) .- normal(5,1)",
"Ok((:dotSubtract (:normal 5 2) (:normal 5 1)))",
"Ok((:$$block (:dotPow (:normal 5 2) (:normal 5 1))))",
)
testParse("normal(5,2) .* normal(5,1)", "Ok((:dotMultiply (:normal 5 2) (:normal 5 1)))")
testParse("normal(5,2) ./ normal(5,1)", "Ok((:dotDivide (:normal 5 2) (:normal 5 1)))")
testParse("normal(5,2) .^ normal(5,1)", "Ok((:dotPow (:normal 5 2) (:normal 5 1)))")
testParse(
"normal(5,2) .* normal(5,1)",
"Ok((:$$block (:dotMultiply (:normal 5 2) (:normal 5 1))))",
)
testParse(
"normal(5,2) ./ normal(5,1)",
"Ok((:$$block (:dotDivide (:normal 5 2) (:normal 5 1))))",
)
testParse("normal(5,2) .^ normal(5,1)", "Ok((:$$block (:dotPow (:normal 5 2) (:normal 5 1))))")
})
describe("equality", () => {
testParse("5 == normal(5,2)", "Ok((:equal 5 (:normal 5 2)))")
testParse("5 == normal(5,2)", "Ok((:$$block (:equal 5 (:normal 5 2))))")
})
describe("pointwise adding two normals", () => {
testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")

View File

@ -3,9 +3,9 @@ open Jest
open Expect
describe("ExpressionValue", () => {
test("argsToString", () => expect([EvNumber(1.), EvString("a")]->argsToString)->toBe("1, 'a'"))
test("argsToString", () => expect([EvNumber(1.), EvString("a")]->argsToString)->toBe("1,'a'"))
test("toStringFunctionCall", () =>
expect(("fn", [EvNumber(1.), EvString("a")])->toStringFunctionCall)->toBe("fn(1, 'a')")
expect(("fn", [EvNumber(1.), EvString("a")])->toStringFunctionCall)->toBe("fn(1,'a')")
)
})

View File

@ -1,23 +1,5 @@
import {
run,
Distribution,
resultMap,
squiggleExpression,
errorValueToString,
} from "../../src/js/index";
let testRun = (x: string): squiggleExpression => {
let result = run(x, { sampleCount: 100, xyPointLength: 100 });
expect(result.tag).toEqual("Ok");
if (result.tag === "Ok") {
return result.value;
} else {
throw Error(
"Expected squiggle expression to evaluate but got error: " +
errorValueToString(result.value)
);
}
};
import { Distribution, resultMap } from "../../src/js/index";
import { testRun, testRunPartial } from "./TestHelpers";
function Ok<b>(x: b) {
return { tag: "Ok", value: x };
@ -42,6 +24,50 @@ describe("Log function", () => {
});
});
describe("Array", () => {
test("nested Array", () => {
expect(testRun("[[1]]")).toEqual({
tag: "array",
value: [
{
tag: "array",
value: [
{
tag: "number",
value: 1,
},
],
},
],
});
});
});
describe("Record", () => {
test("Return record", () => {
expect(testRun("{a: 1}")).toEqual({
tag: "record",
value: {
a: {
tag: "number",
value: 1,
},
},
});
});
});
describe("Partials", () => {
test("Can pass variables between partials and cells", () => {
let bindings = testRunPartial(`x = 5`);
let bindings2 = testRunPartial(`y = x + 2`, bindings);
expect(testRun(`y + 3`, bindings2)).toEqual({
tag: "number",
value: 10,
});
});
});
describe("Distribution", () => {
//It's important that sampleCount is less than 9. If it's more, than that will create randomness
//Also, note, the value should be created using makeSampleSetDist() later on.

View File

@ -1,14 +1,16 @@
import {
run,
// Distribution,
runPartial,
bindings,
squiggleExpression,
errorValueToString,
// errorValue,
// result,
} from "../../src/js/index";
export function testRun(x: string): squiggleExpression {
let squiggleResult = run(x, { sampleCount: 1000, xyPointLength: 100 });
export function testRun(x: string, bindings = {}): squiggleExpression {
let squiggleResult = run(x, bindings, {
sampleCount: 1000,
xyPointLength: 100,
});
// return squiggleResult.value
if (squiggleResult.tag === "Ok") {
return squiggleResult.value;
@ -21,6 +23,22 @@ export function testRun(x: string): squiggleExpression {
}
}
export function testRunPartial(x: string, bindings: bindings = {}): bindings {
let squiggleResult = runPartial(x, bindings, {
sampleCount: 1000,
xyPointLength: 100,
});
if (squiggleResult.tag === "Ok") {
return squiggleResult.value;
} else {
throw new Error(
`Expected squiggle expression to evaluate but got error: ${errorValueToString(
squiggleResult.value
)}`
);
}
}
export function failDefault() {
expect("be reached").toBe("codepath should never");
}

View File

@ -18,7 +18,26 @@ let pointSetDist3: PointSetTypes.xyShape = {
ys: [0.2, 0.5, 0.8],
}
let makeAndGetErrorString = (~xs, ~ys) =>
XYShape.T.make(~xs, ~ys)->E.R.getError->E.O2.fmap(XYShape.Error.toString)
describe("XYShapes", () => {
describe("Validator", () => {
makeTest(
"with no errors",
makeAndGetErrorString(~xs=[1.0, 4.0, 8.0], ~ys=[0.2, 0.4, 0.8]),
None,
)
makeTest("when empty", makeAndGetErrorString(~xs=[], ~ys=[]), Some("Xs is empty"))
makeTest(
"when not sorted, different lengths, and not finite",
makeAndGetErrorString(~xs=[2.0, 1.0, infinity, 0.0], ~ys=[3.0, Js.Float._NaN]),
Some(
"Multiple Errors: [Xs is not sorted], [Xs and Ys have different lengths. Xs has length 4 and Ys has length 2], [Xs is not finite. Example value: Infinity], [Ys is not finite. Example value: NaN]",
),
)
})
describe("logScorePoint", () => {
makeTest("When identical", XYShape.logScorePoint(30, pointSetDist1, pointSetDist1), Some(0.0))
makeTest(
@ -32,16 +51,6 @@ describe("XYShapes", () => {
Some(210.3721280423322),
)
})
// describe("transverse", () => {
// makeTest(
// "When very different",
// XYShape.Transversal._transverse(
// (aCurrent, aLast) => aCurrent +. aLast,
// [|1.0, 2.0, 3.0, 4.0|],
// ),
// [|1.0, 3.0, 6.0, 10.0|],
// )
// });
describe("integrateWithTriangles", () =>
makeTest(
"integrates correctly",

View File

@ -20,7 +20,7 @@
],
"suffix": ".bs.js",
"namespace": true,
"bs-dependencies": ["@glennsl/rescript-jest", "rationale", "bisect_ppx"],
"bs-dependencies": ["@glennsl/rescript-jest", "bisect_ppx"],
"gentypeconfig": {
"language": "typescript",
"module": "commonjs",

View File

@ -1,13 +1,13 @@
{
"name": "@quri/squiggle-lang",
"version": "0.2.5",
"version": "0.2.7",
"homepage": "https://squiggle-language.com",
"licence": "MIT",
"license": "MIT",
"scripts": {
"build": "rescript build -with-deps",
"build": "rescript build -with-deps && tsc",
"bundle": "webpack",
"start": "rescript build -w -with-deps",
"clean": "rescript clean",
"clean": "rescript clean && rm -r dist",
"test:reducer": "jest __tests__/Reducer*/",
"benchmark": "ts-node benchmark/conversion_tests.ts",
"test": "jest",
@ -31,34 +31,28 @@
],
"author": "Quantified Uncertainty Research Institute",
"license": "MIT",
"dependencies": {
"rescript": "^9.1.4",
"jstat": "^1.9.5",
"pdfast": "^0.2.0",
"mathjs": "10.5.0"
},
"devDependencies": {
"bisect_ppx": "^2.7.1",
"jstat": "^1.9.5",
"lodash": "4.17.21",
"mathjs": "10.5.0",
"pdfast": "^0.2.0",
"rationale": "0.2.0",
"rescript": "^9.1.4",
"rescript-fast-check": "^1.1.1",
"@glennsl/rescript-jest": "^0.9.0",
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@types/jest": "^27.4.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"bisect_ppx": "^2.7.1",
"chalk": "^4.1.2",
"chalk": "^5.0.1",
"codecov": "3.8.3",
"fast-check": "2.25.0",
"gentype": "^4.3.0",
"jest": "^27.5.1",
"jstat": "^1.9.5",
"lodash": "4.17.21",
"mathjs": "10.5.0",
"moduleserve": "0.9.1",
"nyc": "^15.1.0",
"pdfast": "^0.2.0",
"rationale": "0.2.0",
"reanalyze": "^2.19.0",
"rescript": "^9.1.4",
"ts-jest": "^27.1.4",
"ts-loader": "^9.2.8",
"ts-node": "^10.7.0",
@ -67,6 +61,6 @@
"webpack-cli": "^4.9.2"
},
"source": "./src/js/index.ts",
"main": "./dist/bundle.js",
"types": "./dist/js/index.d.ts"
"main": "./dist/src/js/index.js",
"types": "./dist/src/js/index.d.ts"
}

View File

@ -2,7 +2,8 @@ import * as _ from "lodash";
import {
genericDist,
samplingParams,
evaluate,
evaluatePartialUsingExternalBindings,
externalBindings,
expressionValue,
errorValue,
distributionError,
@ -11,6 +12,9 @@ import {
discreteShape,
distributionErrorToString,
internalCode,
mixedShape,
sampleSetDist,
symbolicDist,
} from "../rescript/TypescriptInterface.gen";
export {
makeSampleSetDist,
@ -44,7 +48,7 @@ import {
Constructors_pointwiseLogarithm,
Constructors_pointwisePower,
} from "../rescript/Distributions/DistributionOperation/DistributionOperation.gen";
export type { samplingParams, errorValue };
export type { samplingParams, errorValue, externalBindings as bindings };
export let defaultSamplingInputs: samplingParams = {
sampleCount: 10000,
@ -92,26 +96,67 @@ export type squiggleExpression =
| tagged<"distribution", Distribution>
| tagged<"number", number>
| tagged<"record", { [key: string]: squiggleExpression }>;
export function run(
squiggleString: string,
bindings?: externalBindings,
samplingInputs?: samplingParams
): result<squiggleExpression, errorValue> {
let b = bindings ? bindings : {};
let si: samplingParams = samplingInputs
? samplingInputs
: defaultSamplingInputs;
let result: result<expressionValue, errorValue> = evaluate(squiggleString);
let result: result<expressionValue, errorValue> =
evaluateUsingExternalBindings(squiggleString, b);
return resultMap(result, (x) => createTsExport(x, si));
}
// Run Partial. A partial is a block of code that doesn't return a value
export function runPartial(
squiggleString: string,
bindings: externalBindings,
_samplingInputs?: samplingParams
): result<externalBindings, errorValue> {
return evaluatePartialUsingExternalBindings(squiggleString, bindings);
}
function createTsExport(
x: expressionValue,
sampEnv: samplingParams
): squiggleExpression {
switch (x.tag) {
case "EvArray":
// genType doesn't convert anything more than 2 layers down into {tag: x, value: x}
// format, leaving it as the raw values. This converts the raw values
// directly into typescript values.
//
// The casting here is because genType is about the types of the returned
// values, claiming they are fully recursive when that's not actually the
// case
return tag(
"array",
x.value.map((x) => createTsExport(x, sampEnv))
x.value.map((arrayItem): squiggleExpression => {
switch (arrayItem.tag) {
case "EvRecord":
return tag(
"record",
_.mapValues(arrayItem.value, (recordValue: unknown) =>
convertRawToTypescript(recordValue as rescriptExport, sampEnv)
)
);
case "EvArray":
let y = arrayItem.value as unknown as rescriptExport[];
return tag(
"array",
y.map((childArrayItem) =>
convertRawToTypescript(childArrayItem, sampEnv)
)
);
default:
return createTsExport(arrayItem, sampEnv);
}
})
);
case "EvBool":
return tag("boolean", x.value);
@ -124,10 +169,14 @@ function createTsExport(
case "EvNumber":
return tag("number", x.value);
case "EvRecord":
return tag(
// genType doesn't support records, so we have to do the raw conversion ourself
let result: tagged<"record", { [key: string]: squiggleExpression }> = tag(
"record",
_.mapValues(x.value, (x) => createTsExport(x, sampEnv))
_.mapValues(x.value, (x: unknown) =>
convertRawToTypescript(x as rescriptExport, sampEnv)
)
);
return result;
case "EvString":
return tag("string", x.value);
case "EvSymbol":
@ -135,6 +184,118 @@ function createTsExport(
}
}
// Helper functions to convert the rescript representations that genType doesn't
// cover
function convertRawToTypescript(
result: rescriptExport,
sampEnv: samplingParams
): squiggleExpression {
switch (result.TAG) {
case 0: // EvArray
return tag(
"array",
result._0.map((x) => convertRawToTypescript(x, sampEnv))
);
case 1: // EvBool
return tag("boolean", result._0);
case 2: // EvCall
return tag("call", result._0);
case 3: // EvDistribution
return tag(
"distribution",
new Distribution(
convertRawDistributionToGenericDist(result._0),
sampEnv
)
);
case 4: // EvNumber
return tag("number", result._0);
case 5: // EvRecord
return tag(
"record",
_.mapValues(result._0, (x) => convertRawToTypescript(x, sampEnv))
);
case 6: // EvString
return tag("string", result._0);
case 7: // EvSymbol
return tag("symbol", result._0);
}
}
function convertRawDistributionToGenericDist(
result: rescriptDist
): genericDist {
switch (result.TAG) {
case 0: // Point Set Dist
switch (result._0.TAG) {
case 0: // Mixed
return tag("PointSet", tag("Mixed", result._0._0));
case 1: // Discrete
return tag("PointSet", tag("Discrete", result._0._0));
case 2: // Continuous
return tag("PointSet", tag("Continuous", result._0._0));
}
case 1: // Sample Set Dist
return tag("SampleSet", result._0);
case 2: // Symbolic Dist
return tag("Symbolic", result._0);
}
}
// Raw rescript types.
type rescriptExport =
| {
TAG: 0; // EvArray
_0: rescriptExport[];
}
| {
TAG: 1; // EvBool
_0: boolean;
}
| {
TAG: 2; // EvCall
_0: string;
}
| {
TAG: 3; // EvDistribution
_0: rescriptDist;
}
| {
TAG: 4; // EvNumber
_0: number;
}
| {
TAG: 5; // EvRecord
_0: { [key: string]: rescriptExport };
}
| {
TAG: 6; // EvString
_0: string;
}
| {
TAG: 7; // EvSymbol
_0: string;
};
type rescriptDist =
| { TAG: 0; _0: rescriptPointSetDist }
| { TAG: 1; _0: sampleSetDist }
| { TAG: 2; _0: symbolicDist };
type rescriptPointSetDist =
| {
TAG: 0; // Mixed
_0: mixedShape;
}
| {
TAG: 1; // Discrete
_0: discreteShape;
}
| {
TAG: 2; // ContinuousShape
_0: continuousShape;
};
export function resultExn<a, c>(r: result<a, c>): a | c {
return r.value;
}

View File

@ -19,6 +19,7 @@ type error =
| RequestedStrategyInvalidError(string)
| LogarithmOfDistributionError(string)
| OtherError(string)
| XYShapeError(XYShape.error)
@genType
module Error = {
@ -39,6 +40,7 @@ module Error = {
| PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err)
| SparklineError(err) => PointSetTypes.sparklineErrorToString(err)
| RequestedStrategyInvalidError(err) => `Requested strategy invalid: ${err}`
| XYShapeError(err) => `XY Shape Error: ${XYShape.Error.toString(err)}`
| OtherError(s) => s
}

View File

@ -6,6 +6,24 @@ type toSampleSetFn = t => result<SampleSetDist.t, error>
type scaleMultiplyFn = (t, float) => result<t, error>
type pointwiseAddFn = (t, t) => result<t, error>
let isPointSet = (t: t) =>
switch t {
| PointSet(_) => true
| _ => false
}
let isSampleSetSet = (t: t) =>
switch t {
| SampleSet(_) => true
| _ => false
}
let isSymbolic = (t: t) =>
switch t {
| Symbolic(_) => true
| _ => false
}
let sampleN = (t: t, n) =>
switch t {
| PointSet(r) => PointSetDist.sampleNRendered(n, r)
@ -150,144 +168,147 @@ let truncate = Truncate.run
of a new variable that is the result of the operation on A and B.
For instance, normal(0, 1) + normal(1, 1) -> normal(1, 2).
In general, this is implemented via convolution.
TODO: It would be useful to be able to pass in a paramater to get this to run either with convolution or monte carlo.
*/
module AlgebraicCombination = {
let runConvolution = (
toPointSet: toPointSetFn,
arithmeticOperation: Operation.convolutionOperation,
t1: t,
t2: t,
) =>
E.R.merge(toPointSet(t1), toPointSet(t2))->E.R2.fmap(((a, b)) =>
PointSetDist.combineAlgebraically(arithmeticOperation, a, b)
)
let runMonteCarlo = (
toSampleSet: toSampleSetFn,
arithmeticOperation: Operation.algebraicOperation,
t1: t,
t2: t,
): result<t, error> => {
let fn = Operation.Algebraic.toFn(arithmeticOperation)
E.R.merge(toSampleSet(t1), toSampleSet(t2))
->E.R.bind(((t1, t2)) => {
SampleSetDist.map2(~fn, ~t1, ~t2)->E.R2.errMap(x => DistributionTypes.OperationError(x))
})
->E.R2.fmap(r => DistributionTypes.SampleSet(r))
}
/*
module InputValidator = {
/*
It would be good to also do a check to make sure that probability mass for the second
operand, at value 1.0, is 0 (or approximately 0). However, we'd ideally want to check
that both the probability mass and the probability density are greater than zero.
Right now we don't yet have a way of getting probability mass, so I'll leave this for later.
*/
let getLogarithmInputError = (t1: t, t2: t, ~toPointSetFn: toPointSetFn): option<error> => {
let firstOperandIsGreaterThanZero =
toFloatOperation(
t1,
~toPointSetFn,
~distToFloatOperation=#Cdf(MagicNumbers.Epsilon.ten),
) |> E.R.fmap(r => r > 0.)
let secondOperandIsGreaterThanZero =
toFloatOperation(
t2,
~toPointSetFn,
~distToFloatOperation=#Cdf(MagicNumbers.Epsilon.ten),
) |> E.R.fmap(r => r > 0.)
let items = E.A.R.firstErrorOrOpen([
firstOperandIsGreaterThanZero,
secondOperandIsGreaterThanZero,
])
switch items {
| Error(r) => Some(r)
| Ok([true, _]) =>
Some(LogarithmOfDistributionError("First input must completely greater than 0"))
| Ok([false, true]) =>
Some(LogarithmOfDistributionError("Second input must completely greater than 0"))
| Ok([false, false]) => None
| Ok(_) => Some(Unreachable)
let getLogarithmInputError = (t1: t, t2: t, ~toPointSetFn: toPointSetFn): option<error> => {
let firstOperandIsGreaterThanZero =
toFloatOperation(
t1,
~toPointSetFn,
~distToFloatOperation=#Cdf(MagicNumbers.Epsilon.ten),
) |> E.R.fmap(r => r > 0.)
let secondOperandIsGreaterThanZero =
toFloatOperation(
t2,
~toPointSetFn,
~distToFloatOperation=#Cdf(MagicNumbers.Epsilon.ten),
) |> E.R.fmap(r => r > 0.)
let items = E.A.R.firstErrorOrOpen([
firstOperandIsGreaterThanZero,
secondOperandIsGreaterThanZero,
])
switch items {
| Error(r) => Some(r)
| Ok([true, _]) =>
Some(LogarithmOfDistributionError("First input must be completely greater than 0"))
| Ok([false, true]) =>
Some(LogarithmOfDistributionError("Second input must be completely greater than 0"))
| Ok([false, false]) => None
| Ok(_) => Some(Unreachable)
}
}
let run = (t1: t, t2: t, ~toPointSetFn: toPointSetFn, ~arithmeticOperation): option<error> => {
if arithmeticOperation == #Logarithm {
getLogarithmInputError(t1, t2, ~toPointSetFn)
} else {
None
}
}
}
let getInvalidOperationError = (
t1: t,
t2: t,
~toPointSetFn: toPointSetFn,
module StrategyCallOnValidatedInputs = {
let convolution = (
toPointSet: toPointSetFn,
arithmeticOperation: Operation.convolutionOperation,
t1: t,
t2: t,
): result<t, error> =>
E.R.merge(toPointSet(t1), toPointSet(t2))
->E.R2.fmap(((a, b)) => PointSetDist.combineAlgebraically(arithmeticOperation, a, b))
->E.R2.fmap(r => DistributionTypes.PointSet(r))
let monteCarlo = (
toSampleSet: toSampleSetFn,
arithmeticOperation: Operation.algebraicOperation,
t1: t,
t2: t,
): result<t, error> => {
let fn = Operation.Algebraic.toFn(arithmeticOperation)
E.R.merge(toSampleSet(t1), toSampleSet(t2))
->E.R.bind(((t1, t2)) => {
SampleSetDist.map2(~fn, ~t1, ~t2)->E.R2.errMap(x => DistributionTypes.OperationError(x))
})
->E.R2.fmap(r => DistributionTypes.SampleSet(r))
}
let symbolic = (
arithmeticOperation: Operation.algebraicOperation,
t1: t,
t2: t,
): SymbolicDistTypes.analyticalSimplificationResult => {
switch (t1, t2) {
| (DistributionTypes.Symbolic(d1), DistributionTypes.Symbolic(d2)) =>
SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation)
| _ => #NoSolution
}
}
}
module StrategyChooser = {
type specificStrategy = [#AsSymbolic | #AsMonteCarlo | #AsConvolution]
//I'm (Ozzie) really just guessing here, very little idea what's best
let expectedConvolutionCost: t => int = x =>
switch x {
| Symbolic(#Float(_)) => MagicNumbers.OpCost.floatCost
| Symbolic(_) => MagicNumbers.OpCost.symbolicCost
| PointSet(Discrete(m)) => m.xyShape->XYShape.T.length
| PointSet(Mixed(_)) => MagicNumbers.OpCost.mixedCost
| PointSet(Continuous(_)) => MagicNumbers.OpCost.continuousCost
| _ => MagicNumbers.OpCost.wildcardCost
}
let hasSampleSetDist = (t1: t, t2: t): bool => isSampleSetSet(t1) || isSampleSetSet(t2)
let convolutionIsFasterThanMonteCarlo = (t1: t, t2: t): bool =>
expectedConvolutionCost(t1) * expectedConvolutionCost(t2) < MagicNumbers.OpCost.monteCarloCost
let preferConvolutionToMonteCarlo = (t1, t2, arithmeticOperation) => {
!hasSampleSetDist(t1, t2) &&
Operation.Convolution.canDoAlgebraicOperation(arithmeticOperation) &&
convolutionIsFasterThanMonteCarlo(t1, t2)
}
let run = (~t1: t, ~t2: t, ~arithmeticOperation): specificStrategy => {
switch StrategyCallOnValidatedInputs.symbolic(arithmeticOperation, t1, t2) {
| #AnalyticalSolution(_)
| #Error(_) =>
#AsSymbolic
| #NoSolution =>
preferConvolutionToMonteCarlo(t1, t2, arithmeticOperation) ? #AsConvolution : #AsMonteCarlo
}
}
}
let runStrategyOnValidatedInputs = (
~t1: t,
~t2: t,
~arithmeticOperation,
): option<error> => {
if arithmeticOperation == #Logarithm {
getLogarithmInputError(t1, t2, ~toPointSetFn)
} else {
None
}
}
//I'm (Ozzie) really just guessing here, very little idea what's best
let expectedConvolutionCost: t => int = x =>
switch x {
| Symbolic(#Float(_)) => MagicNumbers.OpCost.floatCost
| Symbolic(_) => MagicNumbers.OpCost.symbolicCost
| PointSet(Discrete(m)) => m.xyShape->XYShape.T.length
| PointSet(Mixed(_)) => MagicNumbers.OpCost.mixedCost
| PointSet(Continuous(_)) => MagicNumbers.OpCost.continuousCost
| _ => MagicNumbers.OpCost.wildcardCost
}
type calculationStrategy = MonteCarloStrat | ConvolutionStrat(Operation.convolutionOperation)
let chooseConvolutionOrMonteCarloDefault = (
op: Operation.algebraicOperation,
t2: t,
t1: t,
): calculationStrategy =>
switch op {
| #Divide
| #Power
| #Logarithm =>
MonteCarloStrat
| (#Add | #Subtract | #Multiply) as convOp =>
expectedConvolutionCost(t1) * expectedConvolutionCost(t2) > MagicNumbers.OpCost.monteCarloCost
? MonteCarloStrat
: ConvolutionStrat(convOp)
}
let tryAnalyticalSimplification = (
arithmeticOperation: Operation.algebraicOperation,
t1: t,
t2: t,
): option<SymbolicDistTypes.analyticalSimplificationResult> => {
switch (t1, t2) {
| (DistributionTypes.Symbolic(d1), DistributionTypes.Symbolic(d2)) =>
Some(SymbolicDist.T.tryAnalyticalSimplification(d1, d2, arithmeticOperation))
| _ => None
}
}
let runDefault = (
t1: t,
~strategy: StrategyChooser.specificStrategy,
~toPointSetFn: toPointSetFn,
~toSampleSetFn: toSampleSetFn,
~arithmeticOperation,
~t2: t,
): result<t, error> => {
switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) {
| Some(#AnalyticalSolution(symbolicDist)) => Ok(Symbolic(symbolicDist))
| Some(#Error(e)) => Error(OperationError(e))
| Some(#NoSolution)
| None =>
switch getInvalidOperationError(t1, t2, ~toPointSetFn, ~arithmeticOperation) {
| Some(e) => Error(e)
| None =>
switch chooseConvolutionOrMonteCarloDefault(arithmeticOperation, t1, t2) {
| MonteCarloStrat => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
| ConvolutionStrat(convOp) =>
runConvolution(toPointSetFn, convOp, t1, t2)->E.R2.fmap(r => DistributionTypes.PointSet(
r,
))
}
switch strategy {
| #AsMonteCarlo =>
StrategyCallOnValidatedInputs.monteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
| #AsSymbolic =>
switch StrategyCallOnValidatedInputs.symbolic(arithmeticOperation, t1, t2) {
| #AnalyticalSolution(symbolicDist) => Ok(Symbolic(symbolicDist))
| #Error(e) => Error(OperationError(e))
| #NoSolution => Error(Unreachable)
}
| #AsConvolution =>
switch Operation.Convolution.fromAlgebraicOperation(arithmeticOperation) {
| Some(convOp) => StrategyCallOnValidatedInputs.convolution(toPointSetFn, convOp, t1, t2)
| None => Error(Unreachable)
}
}
}
@ -300,27 +321,38 @@ module AlgebraicCombination = {
~arithmeticOperation: Operation.algebraicOperation,
~t2: t,
): result<t, error> => {
switch strategy {
| AsDefault => runDefault(t1, ~toPointSetFn, ~toSampleSetFn, ~arithmeticOperation, ~t2)
| AsSymbolic =>
switch tryAnalyticalSimplification(arithmeticOperation, t1, t2) {
| Some(#AnalyticalSolution(symbolicDist)) => Ok(Symbolic(symbolicDist))
| Some(#NoSolution) => Error(RequestedStrategyInvalidError(`No analytical solution`))
| None => Error(RequestedStrategyInvalidError("Inputs were not even symbolic"))
| Some(#Error(err)) => Error(OperationError(err))
let invalidOperationError = InputValidator.run(t1, t2, ~arithmeticOperation, ~toPointSetFn)
switch (invalidOperationError, strategy) {
| (Some(e), _) => Error(e)
| (None, AsDefault) => {
let chooseStrategy = StrategyChooser.run(~arithmeticOperation, ~t1, ~t2)
runStrategyOnValidatedInputs(
~t1,
~t2,
~strategy=chooseStrategy,
~arithmeticOperation,
~toPointSetFn,
~toSampleSetFn,
)
}
| AsConvolution => {
let errString = opString => `Can't convolve on ${opString}`
switch arithmeticOperation {
| (#Add | #Subtract | #Multiply) as convOp =>
runConvolution(toPointSetFn, convOp, t1, t2)->E.R2.fmap(r => DistributionTypes.PointSet(
r,
))
| (#Divide | #Power | #Logarithm) as op =>
op->Operation.Algebraic.toString->errString->RequestedStrategyInvalidError->Error
| (None, AsMonteCarlo) =>
StrategyCallOnValidatedInputs.monteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
| (None, AsSymbolic) =>
switch StrategyCallOnValidatedInputs.symbolic(arithmeticOperation, t1, t2) {
| #AnalyticalSolution(symbolicDist) => Ok(Symbolic(symbolicDist))
| #NoSolution => Error(RequestedStrategyInvalidError(`No analytic solution for inputs`))
| #Error(err) => Error(OperationError(err))
}
| (None, AsConvolution) =>
switch Operation.Convolution.fromAlgebraicOperation(arithmeticOperation) {
| None => {
let errString = `Convolution not supported for ${Operation.Algebraic.toString(
arithmeticOperation,
)}`
Error(RequestedStrategyInvalidError(errString))
}
| Some(convOp) => StrategyCallOnValidatedInputs.convolution(toPointSetFn, convOp, t1, t2)
}
| AsMonteCarlo => runMonteCarlo(toSampleSetFn, arithmeticOperation, t1, t2)
}
}
}

View File

@ -69,3 +69,6 @@ let mixture: (
~scaleMultiplyFn: scaleMultiplyFn,
~pointwiseAddFn: pointwiseAddFn,
) => result<t, error>
let isSymbolic: t => bool
let isPointSet: t => bool

View File

@ -263,4 +263,4 @@ let combineShapesContinuousDiscrete = (
)
}
let isOrdered = (a: XYShape.T.t): bool => E.A.Sorted.Floats.isSorted(a.xs)
let isOrdered = (a: XYShape.T.t): bool => E.A.Floats.isSorted(a.xs)

View File

@ -39,28 +39,6 @@ module Internals = {
module T = {
type t = array<float>
let splitContinuousAndDiscrete = (sortedArray: t) => {
let continuous = []
let discrete = E.FloatFloatMap.empty()
Belt.Array.forEachWithIndex(sortedArray, (index, element) => {
let maxIndex = (sortedArray |> Array.length) - 1
let possiblySimilarElements = switch index {
| 0 => [index + 1]
| n if n == maxIndex => [index - 1]
| _ => [index - 1, index + 1]
} |> Belt.Array.map(_, r => sortedArray[r])
let hasSimilarElement = Belt.Array.some(possiblySimilarElements, r => r == element)
hasSimilarElement
? E.FloatFloatMap.increment(element, discrete)
: {
let _ = Js.Array.push(element, continuous)
}
()
})
(continuous, discrete)
}
let xWidthToUnitWidth = (samples, outputXYPoints, xWidth) => {
let xyPointRange = E.A.Sorted.range(samples) |> E.O.default(0.0)
let xyPointWidth = xyPointRange /. float_of_int(outputXYPoints)
@ -85,7 +63,11 @@ let toPointSetDist = (
(),
): Internals.Types.outputs => {
Array.fast_sort(compare, samples)
let (continuousPart, discretePart) = E.A.Sorted.Floats.split(samples)
let minDiscreteToKeep = MagicNumbers.ToPointSet.minDiscreteToKeep(samples)
let (continuousPart, discretePart) = E.A.Floats.Sorted.splitContinuousAndDiscreteForMinWeight(
samples,
~minDiscreteWeight=minDiscreteToKeep,
)
let length = samples |> E.A.length |> float_of_int
let discrete: PointSetTypes.discreteShape =
discretePart

View File

@ -22,3 +22,16 @@ module OpCost = {
let wildcardCost = 1000
let monteCarloCost = Environment.defaultSampleCount
}
module ToPointSet = {
/*
This function chooses the minimum amount of duplicate samples that need
to exist in order for this to be considered discrete. The tricky thing
is that there are some operations that create duplicate continuous samples,
so we can't guarantee that these only will occur because the fundamental
structure is meant to be discrete. I chose this heuristic because I think
it would strike a reasonable trade-off, but Im really unsure whats
best right now.
*/
let minDiscreteToKeep = samples => max(20, E.A.length(samples) / 50)
}

View File

@ -11,6 +11,5 @@ type expressionValue = ReducerInterface_ExpressionValue.expressionValue
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
let evaluate = Expression.evaluate
let evaluateUsingOptions = Expression.evaluateUsingOptions
let evaluatePartialUsingExternalBindings = Expression.evaluatePartialUsingExternalBindings
let parse = Expression.parse
let parseOuter = Expression.parseOuter
let parsePartial = Expression.parsePartial

View File

@ -18,12 +18,15 @@ type externalBindings = ReducerInterface_ExpressionValue.externalBindings
let evaluateUsingOptions: (
~environment: option<QuriSquiggleLang.ReducerInterface_ExpressionValue.environment>,
~externalBindings: option<QuriSquiggleLang.ReducerInterface_ExpressionValue.externalBindings>,
~isPartial: option<bool>,
string,
) => result<expressionValue, errorValue>
@genType
let evaluatePartialUsingExternalBindings: (
string,
QuriSquiggleLang.ReducerInterface_ExpressionValue.externalBindings,
QuriSquiggleLang.ReducerInterface_ExpressionValue.environment,
) => result<externalBindings, errorValue>
@genType
let evaluate: string => result<expressionValue, errorValue>
let parse: string => result<Expression.expression, errorValue>
let parseOuter: string => result<Expression.expression, errorValue>
let parsePartial: string => result<Expression.expression, errorValue>

View File

@ -1,5 +1,6 @@
module ExternalLibrary = ReducerInterface.ExternalLibrary
module MathJs = Reducer_MathJs
module Bindings = Reducer_Expression_Bindings
open ReducerInterface.ExpressionValue
open Reducer_ErrorValue
@ -20,12 +21,12 @@ let callInternal = (call: functionCall, _environment): result<'b, errorValue> =>
}
let constructRecord = arrayOfPairs => {
Belt.Array.map(arrayOfPairs, pairValue => {
Belt.Array.map(arrayOfPairs, pairValue =>
switch pairValue {
| EvArray([EvString(key), valueValue]) => (key, valueValue)
| _ => ("wrong key type", pairValue->toStringWithType->EvString)
}
})
)
->Js.Dict.fromArray
->EvRecord
->Ok
@ -68,6 +69,20 @@ let callInternal = (call: functionCall, _environment): result<'b, errorValue> =>
value->Ok
}
let doSetBindings = (
externalBindings: externalBindings,
symbol: string,
value: expressionValue,
) => {
Bindings.fromExternalBindings(externalBindings)
->Belt.Map.String.set(symbol, value)
->Bindings.toExternalBindings
->EvRecord
->Ok
}
let doExportBindings = (externalBindings: externalBindings) => EvRecord(externalBindings)->Ok
switch call {
| ("$atIndex", [EvArray(aValueArray), EvArray([EvNumber(fIndex)])]) =>
arrayAtIndex(aValueArray, fIndex)
@ -78,6 +93,9 @@ let callInternal = (call: functionCall, _environment): result<'b, errorValue> =>
| ("inspect", [value, EvString(label)]) => inspectLabel(value, label)
| ("inspect", [value]) => inspect(value)
| ("inspectPerformance", [value, EvString(label)]) => inspectPerformance(value, label)
| ("$setBindings", [EvRecord(externalBindings), EvSymbol(symbol), value]) =>
doSetBindings(externalBindings, symbol, value)
| ("$exportBindings", [EvRecord(externalBindings)]) => doExportBindings(externalBindings)
| call => callMathJs(call)
}
}

View File

@ -3,179 +3,137 @@
they take expressions as parameters and return a new expression.
Macros are used to define language building blocks. They are like Lisp macros.
*/
module Bindings = Reducer_Expression_Bindings
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue
module Result = Belt.Result
open Reducer_ErrorValue
open Reducer_Expression_ExpressionBuilder
type expression = ExpressionT.expression
type environment = ExpressionValue.environment
type reducerFn = (
expression,
ExpressionT.bindings,
environment,
) => result<ExpressionValue.expressionValue, errorValue>
let rec replaceSymbols = (expression: expression, bindings: ExpressionT.bindings): result<
expression,
errorValue,
> => {
let getParameters = (bindings: ExpressionT.bindings): array<string> => {
let eParameters = Belt.Map.String.getWithDefault(bindings, "$parameters", EParameters([]))
switch eParameters {
| EParameters(parameters) => parameters
| _ => []
}
}
let putParameters = (
bindings: ExpressionT.bindings,
parameters: array<string>,
): ExpressionT.bindings =>
Belt.Map.String.set(bindings, "$parameters", ExpressionT.EParameters(parameters))
let answerBindingIfNotParameter = (aSymbol, defaultExpression, parameters, bindings) =>
switch Js.Array2.some(parameters, a => a == aSymbol) {
| true => defaultExpression->Ok // We cannot bind the parameters with global values
| false =>
switch bindings->Belt.Map.String.get(aSymbol) {
| Some(boundExpression) => boundExpression->Ok
| None => RESymbolNotFound(aSymbol)->Error
}
}
let answerCallBindingIfNotParameter = (aSymbol, defaultExpression, parameters, bindings) =>
switch Js.Array2.some(parameters, a => a == aSymbol) {
| true => defaultExpression->Ok // We cannot bind the parameters with global values
| false =>
switch bindings->Belt.Map.String.get(aSymbol) {
| Some(boundExpression) => boundExpression->Ok
| None => defaultExpression->Ok
}
}
switch expression {
| ExpressionT.EValue(EvSymbol(aSymbol)) => {
let parameters = getParameters(bindings)
answerBindingIfNotParameter(aSymbol, expression, parameters, bindings)
}
| ExpressionT.EValue(EvCall(aSymbol)) => {
let parameters = getParameters(bindings)
answerCallBindingIfNotParameter(aSymbol, expression, parameters, bindings)
}
| ExpressionT.EValue(_) => expression->Ok
| ExpressionT.EBindings(_) => expression->Ok
| ExpressionT.EParameters(_) => expression->Ok
| ExpressionT.EList(list{
ExpressionT.EValue(EvCall("$lambda")),
ExpressionT.EParameters(parameters),
expr,
}) => {
let oldParameters = getParameters(bindings)
let newParameters = oldParameters->Js.Array2.concat(parameters)
let newBindings = putParameters(bindings, newParameters)
let rNewExpr = replaceSymbols(expr, newBindings)
rNewExpr->Result.flatMap(newExpr =>
ExpressionT.EList(list{
ExpressionT.EValue(EvCall("$lambda")),
ExpressionT.EParameters(parameters),
newExpr,
})->Ok
)
}
| ExpressionT.EList(list) => {
let racc = list->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) =>
racc->Result.flatMap(acc => {
each
->replaceSymbols(bindings)
->Result.flatMap(newNode => {
acc->Belt.List.add(newNode)->Ok
})
})
)
racc->Result.map(acc => acc->ExpressionT.EList)
}
}
}
type errorValue = Reducer_ErrorValue.errorValue
let dispatchMacroCall = (
list: list<expression>,
macroExpression: expression,
bindings: ExpressionT.bindings,
environment,
reduceExpression: reducerFn,
): result<expression, 'e> => {
let doBindStatement = (statement: expression, bindings: ExpressionT.bindings) => {
reduceExpression: ExpressionT.reducerFn,
): result<expression, errorValue> => {
let doBindStatement = (bindingExpr: expression, statement: expression, environment) =>
switch statement {
| ExpressionT.EList(list{
ExpressionT.EValue(EvCall("$let")),
ExpressionT.EValue(EvSymbol(aSymbol)),
expressionToReduce,
}) => {
let rNewExpressionToReduce = replaceSymbols(expressionToReduce, bindings)
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), symbolExpr, statement}) => {
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
let rNewValue =
rNewExpressionToReduce->Result.flatMap(newExpressionToReduce =>
reduceExpression(newExpressionToReduce, bindings, environment)
rExternalBindingsValue->Result.flatMap(externalBindingsValue => {
let newBindings = Bindings.fromValue(externalBindingsValue)
let rNewStatement = Bindings.replaceSymbols(newBindings, statement)
rNewStatement->Result.map(newStatement =>
eFunction(
"$setBindings",
list{newBindings->Bindings.toExternalBindings->eRecord, symbolExpr, newStatement},
)
)
let rNewExpression = rNewValue->Result.map(newValue => ExpressionT.EValue(newValue))
rNewExpression->Result.map(newExpression =>
Belt.Map.String.set(bindings, aSymbol, newExpression)->ExpressionT.EBindings
)
})
}
| _ => REAssignmentExpected->Error
}
let doBindExpression = (bindingExpr: expression, statement: expression, environment) =>
switch statement {
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), symbolExpr, statement}) => {
let rExternalBindingsValue = reduceExpression(
bindingExpr,
Belt.Map.String.fromArray([("x", ExpressionValue.EvNumber(666.))]),
// bindingsToHandDown,
environment,
)
rExternalBindingsValue->Result.flatMap(externalBindingsValue => {
let newBindings = Bindings.fromValue(externalBindingsValue)
let rNewStatement = Bindings.replaceSymbols(newBindings, statement)
rNewStatement->Result.map(newStatement =>
eFunction(
"$exportBindings",
list{
eFunction(
"$setBindings",
list{newBindings->Bindings.toExternalBindings->eRecord, symbolExpr, newStatement},
),
},
)
)
})
}
| _ => {
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
rExternalBindingsValue->Result.flatMap(externalBindingsValue => {
let newBindings = Bindings.fromValue(externalBindingsValue)
let rNewStatement = Bindings.replaceSymbols(newBindings, statement)
rNewStatement
})
}
}
let doBlock = (exprs: list<expression>, _bindings: ExpressionT.bindings, _environment): result<
expression,
errorValue,
> => {
let exprsArray = Belt.List.toArray(exprs)
let maxIndex = Js.Array2.length(exprsArray) - 1
exprsArray->Js.Array2.reducei((acc, statement, index) =>
if index == 0 {
if index == maxIndex {
eBindExpressionDefault(statement)
} else {
eBindStatementDefault(statement)
}
} else if index == maxIndex {
eBindExpression(acc, statement)
} else {
eBindStatement(acc, statement)
}
, eSymbol("undefined block"))->Ok
}
let doExportVariableExpression = (bindings: ExpressionT.bindings) => {
let emptyDictionary: Js.Dict.t<ExpressionValue.expressionValue> = Js.Dict.empty()
let reducedBindings = bindings->Belt.Map.String.keep((_key, value) =>
switch value {
| ExpressionT.EValue(_) => true
| _ => false
}
)
let externalBindings = reducedBindings->Belt.Map.String.reduce(emptyDictionary, (
acc,
key,
expressionValue,
) => {
let value = switch expressionValue {
| ExpressionT.EValue(aValue) => aValue
| _ => EvSymbol("internal")
}
Js.Dict.set(acc, key, value)
acc
})
externalBindings->ExpressionValue.EvRecord->ExpressionT.EValue->Ok
}
let doLambdaDefinition = (
bindings: ExpressionT.bindings,
parameters: array<string>,
lambdaDefinition: ExpressionT.expression,
) => eLambda(parameters, bindings->Bindings.toExternalBindings, lambdaDefinition)->Ok
let doBindExpression = (expression: expression, bindings: ExpressionT.bindings) =>
switch expression {
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), ..._}) =>
REExpressionExpected->Error
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$exportVariablesExpression"))}) =>
doExportVariableExpression(bindings)
| _ => replaceSymbols(expression, bindings)
let expandExpressionList = (aList, bindings: ExpressionT.bindings, environment) =>
switch aList {
| list{
ExpressionT.EValue(EvCall("$$bindStatement")),
bindingExpr: ExpressionT.expression,
statement,
} =>
doBindStatement(bindingExpr, statement, environment)
| list{ExpressionT.EValue(EvCall("$$bindStatement")), statement} =>
// bindings of the context are used when there is no binding expression
doBindStatement(eRecord(Bindings.toExternalBindings(bindings)), statement, environment)
| list{
ExpressionT.EValue(EvCall("$$bindExpression")),
bindingExpr: ExpressionT.expression,
expression,
} =>
doBindExpression(bindingExpr, expression, environment)
| list{ExpressionT.EValue(EvCall("$$bindExpression")), expression} =>
// bindings of the context are used when there is no binding expression
doBindExpression(eRecord(Bindings.toExternalBindings(bindings)), expression, environment)
| list{ExpressionT.EValue(EvCall("$$block")), ...exprs} => doBlock(exprs, bindings, environment)
| list{
ExpressionT.EValue(EvCall("$$lambda")),
ExpressionT.EValue(EvArrayString(parameters)),
lambdaDefinition,
} =>
doLambdaDefinition(bindings, parameters, lambdaDefinition)
| _ => ExpressionT.EList(aList)->Ok
}
switch list {
| list{ExpressionT.EValue(EvCall("$$bindings"))} => bindings->ExpressionT.EBindings->Ok
| list{
ExpressionT.EValue(EvCall("$$bindStatement")),
ExpressionT.EBindings(bindings),
statement,
} =>
doBindStatement(statement, bindings)
| list{
ExpressionT.EValue(EvCall("$$bindExpression")),
ExpressionT.EBindings(bindings),
expression,
} =>
doBindExpression(expression, bindings)
| _ => list->ExpressionT.EList->Ok
switch macroExpression {
| EList(aList) => expandExpressionList(aList, bindings, environment)
| _ => macroExpression->Ok
}
}

View File

@ -1,13 +1,14 @@
module Builder = Reducer_Expression_Builder
module Bindings = Reducer_Expression_Bindings
module BuiltIn = Reducer_Dispatch_BuiltIn
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
module ExpressionValue = ReducerInterface.ExpressionValue
module Extra = Reducer_Extra
module Lambda = Reducer_Expression_Lambda
module Macro = Reducer_Expression_Macro
module MathJs = Reducer_MathJs
module Result = Belt.Result
module T = Reducer_Expression_T
open Reducer_ErrorValue
type environment = ReducerInterface_ExpressionValue.environment
type errorValue = Reducer_ErrorValue.errorValue
type expression = T.expression
@ -16,30 +17,6 @@ type externalBindings = ReducerInterface_ExpressionValue.externalBindings
type internalCode = ReducerInterface_ExpressionValue.internalCode
type t = expression
external castExpressionToInternalCode: expression => internalCode = "%identity"
external castInternalCodeToExpression: internalCode => expression = "%identity"
/*
Shows the expression as text of expression
*/
let rec toString = expression =>
switch expression {
| T.EBindings(_) => "$$bound"
| T.EParameters(params) => `(${Js.Array2.toString(params)})`
| T.EList(aList) =>
`(${Belt.List.map(aList, aValue => toString(aValue))
->Extra.List.interperse(" ")
->Belt.List.toArray
->Js.String.concatMany("")})`
| EValue(aValue) => ExpressionValue.toString(aValue)
}
let toStringResult = codeResult =>
switch codeResult {
| Ok(a) => `Ok(${toString(a)})`
| Error(m) => `Error(${Js.String.make(m)})`
}
/*
Converts a MathJs code to expression
*/
@ -49,159 +26,70 @@ let parse_ = (expr: string, parser, converter): result<t, errorValue> =>
let parse = (mathJsCode: string): result<t, errorValue> =>
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromNode)
let parsePartial = (mathJsCode: string): result<t, errorValue> =>
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromPartialNode)
let parseOuter = (mathJsCode: string): result<t, errorValue> =>
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromOuterNode)
let defaultBindings: T.bindings = Belt.Map.String.empty
/*
Recursively evaluate/reduce the expression (Lisp AST)
*/
let rec reduceExpression = (expression: t, bindings: T.bindings, environment: environment): result<
expressionValue,
'e,
> => {
/*
Macros are like functions but instead of taking values as parameters,
they take expressions as parameters and return a new expression.
Macros are used to define language building blocks. They are like Lisp macros.
*/
let doMacroCall = (list: list<t>, bindings: T.bindings, environment: environment): result<
t,
'e,
> =>
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(list, bindings, environment, reduceExpression)
let applyParametersToLambda = (
internal: internalCode,
parameters: array<string>,
args: list<expressionValue>,
environment,
): result<expressionValue, 'e> => {
let expr = castInternalCodeToExpression(internal)
let parameterList = parameters->Belt.List.fromArray
let zippedParameterList = parameterList->Belt.List.zip(args)
let bindings = Belt.List.reduce(zippedParameterList, defaultBindings, (a, (p, e)) =>
a->Belt.Map.String.set(p, e->T.EValue)
)
let newExpression = Builder.passToFunction(
"$$bindExpression",
list{Builder.passToFunction("$$bindings", list{}), expr},
)
reduceExpression(newExpression, bindings, environment)
> =>
switch expression {
| T.EValue(value) => value->Ok
| T.EList(list) =>
switch list {
| list{EValue(EvCall(fName)), ..._args} =>
switch Macro.isMacroName(fName) {
// A macro expands then reduces itself
| true => Macro.doMacroCall(expression, bindings, environment, reduceExpression)
| false => reduceExpressionList(list, bindings, environment)
}
| _ => reduceExpressionList(list, bindings, environment)
}
}
/*
After reducing each level of expression(Lisp AST), we have a value list to evaluate
*/
let reduceValueList = (valueList: list<expressionValue>, environment): result<
expressionValue,
'e,
> =>
switch valueList {
| list{EvCall(fName), ...args} =>
(fName, args->Belt.List.toArray)->BuiltIn.dispatch(environment)
// "(lambda(x=>internal) param)"
| list{EvLambda((parameters, internal)), ...args} =>
applyParametersToLambda(internal, parameters, args, environment)
| _ => valueList->Belt.List.toArray->ExpressionValue.EvArray->Ok
}
let rec seekMacros = (expression: t, bindings: T.bindings, environment): result<t, 'e> =>
switch expression {
| T.EValue(_value) => expression->Ok
| T.EBindings(_value) => expression->Ok
| T.EParameters(_value) => expression->Ok
| T.EList(list) => {
let racc: result<list<t>, 'e> = list->Belt.List.reduceReverse(Ok(list{}), (
racc,
each: expression,
) =>
racc->Result.flatMap(acc => {
each
->seekMacros(bindings, environment)
->Result.flatMap(newNode => {
acc->Belt.List.add(newNode)->Ok
})
})
)
racc->Result.flatMap(acc => acc->doMacroCall(bindings, environment))
}
}
let rec reduceExpandedExpression = (expression: t, environment): result<expressionValue, 'e> =>
switch expression {
| T.EList(list{T.EValue(EvCall("$lambda")), T.EParameters(parameters), functionDefinition}) =>
EvLambda((parameters, functionDefinition->castExpressionToInternalCode))->Ok
| T.EValue(value) => value->Ok
| T.EList(list) => {
let racc: result<list<expressionValue>, 'e> = list->Belt.List.reduceReverse(Ok(list{}), (
racc,
each: expression,
) =>
racc->Result.flatMap(acc => {
each
->reduceExpandedExpression(environment)
->Result.flatMap(newNode => {
acc->Belt.List.add(newNode)->Ok
})
})
)
racc->Result.flatMap(acc => acc->reduceValueList(environment))
}
| EBindings(_bindings) => RETodo("Error: Bindings cannot be reduced to values")->Error
| EParameters(_parameters) =>
RETodo("Error: Lambda Parameters cannot be reduced to values")->Error
}
let rExpandedExpression: result<t, 'e> = expression->seekMacros(bindings, environment)
rExpandedExpression->Result.flatMap(expandedExpression =>
expandedExpression->reduceExpandedExpression(environment)
and reduceExpressionList = (
expressions: list<t>,
bindings: T.bindings,
environment: environment,
): result<expressionValue, 'e> => {
let racc: result<list<expressionValue>, 'e> = expressions->Belt.List.reduceReverse(Ok(list{}), (
racc,
each: expression,
) =>
racc->Result.flatMap(acc => {
each
->reduceExpression(bindings, environment)
->Result.map(newNode => {
acc->Belt.List.add(newNode)
})
})
)
racc->Result.flatMap(acc => acc->reduceValueList(environment))
}
let evalUsingExternalBindingsExpression_ = (aExpression, bindings, environment): result<
/*
After reducing each level of expression(Lisp AST), we have a value list to evaluate
*/
and reduceValueList = (valueList: list<expressionValue>, environment): result<
expressionValue,
'e,
> =>
switch valueList {
| list{EvCall(fName), ...args} => (fName, args->Belt.List.toArray)->BuiltIn.dispatch(environment)
| list{EvLambda(lamdaCall), ...args} =>
Lambda.doLambdaCall(lamdaCall, args, environment, reduceExpression)
| _ => valueList->Belt.List.toArray->ExpressionValue.EvArray->Ok
}
let evalUsingBindingsExpression_ = (aExpression, bindings, environment): result<
expressionValue,
'e,
> => reduceExpression(aExpression, bindings, environment)
/*
Evaluates MathJs code via Reducer using bindings and answers the result.
When bindings are used, the code is a partial code as if it is cut from a larger code.
Therefore all statements are assignments.
*/
let evalPartial_ = (codeText: string, bindings: T.bindings, environment: environment) => {
parsePartial(codeText)->Result.flatMap(expression =>
expression->evalUsingExternalBindingsExpression_(bindings, environment)
)
}
/*
Evaluates MathJs code via Reducer using bindings and answers the result.
When bindings are used, the code is a partial code as if it is cut from a larger code.
Therefore all statments are assignments.
*/
let evalOuter_ = (codeText: string, bindings: T.bindings, environment: environment) => {
parseOuter(codeText)->Result.flatMap(expression =>
expression->evalUsingExternalBindingsExpression_(bindings, environment)
)
}
let externalBindingsToBindings = (externalBindings: externalBindings): T.bindings => {
let keys = Js.Dict.keys(externalBindings)
keys->Belt.Array.reduce(defaultBindings, (acc, key) => {
let value = Js.Dict.unsafeGet(externalBindings, key)
acc->Belt.Map.String.set(key, T.EValue(value))
})
}
let evaluateUsingOptions = (
~environment: option<ReducerInterface_ExpressionValue.environment>,
~externalBindings: option<ReducerInterface_ExpressionValue.externalBindings>,
~isPartial: option<bool>,
code: string,
): result<expressionValue, errorValue> => {
let anEnvironment = switch environment {
@ -214,24 +102,27 @@ let evaluateUsingOptions = (
| None => ReducerInterface_ExpressionValue.defaultExternalBindings
}
let anIsPartial = switch isPartial {
| Some(isPartial) => isPartial
| None => false
}
let bindings = anExternalBindings->Bindings.fromExternalBindings
let bindings = anExternalBindings->externalBindingsToBindings
if anIsPartial {
evalPartial_(code, bindings, anEnvironment)
} else {
evalOuter_(code, bindings, anEnvironment)
}
parse(code)->Result.flatMap(expr => evalUsingBindingsExpression_(expr, bindings, anEnvironment))
}
/*
Evaluates MathJs code and bindings via Reducer and answers the result
*/
let evaluate = (code: string): result<expressionValue, errorValue> => {
evaluateUsingOptions(~environment=None, ~externalBindings=None, ~isPartial=None, code)
evaluateUsingOptions(~environment=None, ~externalBindings=None, code)
}
let eval = evaluate
let evaluatePartialUsingExternalBindings = (
code: string,
externalBindings: ReducerInterface_ExpressionValue.externalBindings,
environment: ReducerInterface_ExpressionValue.environment,
): result<externalBindings, errorValue> => {
let rAnswer = evaluateUsingOptions(~environment=Some(environment), ~externalBindings=Some(externalBindings), code)
switch rAnswer {
| Ok(EvRecord(externalBindings)) => Ok(externalBindings)
| Ok(_) => Error(Reducer_ErrorValue.RESyntaxError(`Partials must end with an assignment or record`))
| Error(err) => err->Error
}
}

View File

@ -0,0 +1,73 @@
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue
module Result = Belt.Result
type errorValue = Reducer_ErrorValue.errorValue
type expression = ExpressionT.expression
type expressionValue = ExpressionValue.expressionValue
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
let defaultBindings: ExpressionT.bindings = Belt.Map.String.empty
let fromExternalBindings = (externalBindings: externalBindings): ExpressionT.bindings => {
let keys = Js.Dict.keys(externalBindings)
keys->Belt.Array.reduce(defaultBindings, (acc, key) => {
let value = Js.Dict.unsafeGet(externalBindings, key)
acc->Belt.Map.String.set(key, value)
})
}
let toExternalBindings = (bindings: ExpressionT.bindings): externalBindings => {
let keys = Belt.Map.String.keysToArray(bindings)
keys->Belt.Array.reduce(Js.Dict.empty(), (acc, key) => {
let value = bindings->Belt.Map.String.getExn(key)
Js.Dict.set(acc, key, value)
acc
})
}
let fromValue = (aValue: expressionValue) =>
switch aValue {
| EvRecord(externalBindings) => fromExternalBindings(externalBindings)
| _ => defaultBindings
}
let externalFromArray = anArray => Js.Dict.fromArray(anArray)
let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$")
let rec replaceSymbols = (bindings: ExpressionT.bindings, expression: expression): result<
expression,
errorValue,
> =>
switch expression {
| ExpressionT.EValue(value) =>
replaceSymbolOnValue(bindings, value)->Result.map(evValue => evValue->ExpressionT.EValue)
| ExpressionT.EList(list) =>
switch list {
| list{EValue(EvCall(fName)), ..._args} =>
switch isMacroName(fName) {
// A macro reduces itself so we dont dive in it
| true => expression->Ok
| false => replaceSymbolsOnExpressionList(bindings, list)
}
| _ => replaceSymbolsOnExpressionList(bindings, list)
}
}
and replaceSymbolsOnExpressionList = (bindings, list) => {
let racc = list->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) =>
racc->Result.flatMap(acc => {
replaceSymbols(bindings, each)->Result.flatMap(newNode => {
acc->Belt.List.add(newNode)->Ok
})
})
)
racc->Result.map(acc => acc->ExpressionT.EList)
}
and replaceSymbolOnValue = (bindings, evValue: expressionValue) =>
switch evValue {
| EvSymbol(symbol) | EvCall(symbol) =>
Belt.Map.String.getWithDefault(bindings, symbol, evValue)->Ok
| _ => evValue->Ok
}

View File

@ -1,16 +0,0 @@
module ErrorValue = Reducer_ErrorValue
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue
module Result = Belt.Result
type errorValue = ErrorValue.errorValue
type expression = ExpressionT.expression
let passToFunction = (fName: string, lispArgs: list<expression>): expression => {
let toEvCallValue = (name: string): expression => name->ExpressionValue.EvCall->ExpressionT.EValue
let fn = fName->toEvCallValue
list{fn, ...lispArgs}->ExpressionT.EList
}
let toEvSymbolValue = (name: string): expression =>
name->ExpressionValue.EvSymbol->ExpressionT.EValue

View File

@ -0,0 +1,60 @@
module BBindings = Reducer_Expression_Bindings
module BErrorValue = Reducer_ErrorValue
module BExpressionT = Reducer_Expression_T
module BExpressionValue = ReducerInterface.ExpressionValue
type errorValue = BErrorValue.errorValue
type expression = BExpressionT.expression
type internalCode = ReducerInterface_ExpressionValue.internalCode
external castExpressionToInternalCode: expression => internalCode = "%identity"
let eArray = anArray => anArray->BExpressionValue.EvArray->BExpressionT.EValue
let eArrayString = anArray => anArray->BExpressionValue.EvArrayString->BExpressionT.EValue
let eBindings = (anArray: array<(string, BExpressionValue.expressionValue)>) =>
anArray->Js.Dict.fromArray->EvRecord->BExpressionT.EValue
let eBool = aBool => aBool->BExpressionValue.EvBool->BExpressionT.EValue
let eCall = (name: string): expression => name->BExpressionValue.EvCall->BExpressionT.EValue
let eFunction = (fName: string, lispArgs: list<expression>): expression => {
let fn = fName->eCall
list{fn, ...lispArgs}->BExpressionT.EList
}
let eLambda = (parameters: array<string>, context, expr) =>
BExpressionValue.EvLambda(
parameters,
context,
expr->castExpressionToInternalCode,
)->BExpressionT.EValue
let eNumber = aNumber => aNumber->BExpressionValue.EvNumber->BExpressionT.EValue
let eRecord = aRecord => aRecord->BExpressionValue.EvRecord->BExpressionT.EValue
let eString = aString => aString->BExpressionValue.EvString->BExpressionT.EValue
let eSymbol = (name: string): expression => name->BExpressionValue.EvSymbol->BExpressionT.EValue
let eList = (list: list<expression>): expression => list->BExpressionT.EList
let eBlock = (exprs: list<expression>): expression => eFunction("$$block", exprs)
let eLetStatement = (symbol: string, valueExpression: expression): expression =>
eFunction("$let", list{eSymbol(symbol), valueExpression})
let eBindStatement = (bindingExpr: expression, letStatement: expression): expression =>
eFunction("$$bindStatement", list{bindingExpr, letStatement})
let eBindStatementDefault = (letStatement: expression): expression =>
eFunction("$$bindStatement", list{letStatement})
let eBindExpression = (bindingExpr: expression, expression: expression): expression =>
eFunction("$$bindExpression", list{bindingExpr, expression})
let eBindExpressionDefault = (expression: expression): expression =>
eFunction("$$bindExpression", list{expression})

View File

@ -0,0 +1,35 @@
module Bindings = Reducer_Expression_Bindings
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue
type environment = ReducerInterface_ExpressionValue.environment
type expression = ExpressionT.expression
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
type internalCode = ReducerInterface_ExpressionValue.internalCode
external castInternalCodeToExpression: internalCode => expression = "%identity"
let applyParametersToLambda = (
internal: internalCode,
parameters: array<string>,
args: list<expressionValue>,
context: externalBindings,
environment,
reducer: ExpressionT.reducerFn,
): result<expressionValue, 'e> => {
let expr = castInternalCodeToExpression(internal)
let parameterList = parameters->Belt.List.fromArray
let zippedParameterList = parameterList->Belt.List.zip(args)
let bindings = Belt.List.reduce(zippedParameterList, context->Bindings.fromExternalBindings, (
acc,
(variable, variableValue),
) => acc->Belt.Map.String.set(variable, variableValue))
let newExpression = ExpressionBuilder.eBlock(list{expr})
reducer(newExpression, bindings, environment)
}
let doLambdaCall = ((parameters, context, internal), args, environment, reducer) => {
applyParametersToLambda(internal, parameters, args, context, environment, reducer)
}

View File

@ -0,0 +1,35 @@
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue
module Result = Belt.Result
type environment = ExpressionValue.environment
type expression = ExpressionT.expression
type expressionValue = ExpressionValue.expressionValue
let expandMacroCall = (
macroExpression: expression,
bindings: ExpressionT.bindings,
environment: environment,
reduceExpression: ExpressionT.reducerFn,
): result<expression, 'e> =>
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(
macroExpression,
bindings,
environment,
reduceExpression,
)
let doMacroCall = (
macroExpression: expression,
bindings: ExpressionT.bindings,
environment: environment,
reduceExpression: ExpressionT.reducerFn,
): result<expressionValue, 'e> =>
expandMacroCall(
macroExpression,
bindings,
environment,
reduceExpression,
)->Result.flatMap(expression => reduceExpression(expression, bindings, environment))
let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$")

View File

@ -1,5 +1,3 @@
open ReducerInterface.ExpressionValue
/*
An expression is a Lisp AST. An expression is either a primitive value or a list of expressions.
In the case of a list of expressions (e1, e2, e3, ...eN), the semantic is
@ -8,9 +6,51 @@ open ReducerInterface.ExpressionValue
A Lisp AST contains only expressions/primitive values to apply to their left.
The act of defining the semantics of a functional language is to write it in terms of Lisp AST.
*/
module Extra = Reducer_Extra
module ExpressionValue = ReducerInterface.ExpressionValue
type expressionValue = ExpressionValue.expressionValue
type environment = ExpressionValue.environment
type rec expression =
| EList(list<expression>) // A list to map-reduce
| EValue(expressionValue) // Irreducible built-in value. Reducer should not know the internals. External libraries are responsible
| EBindings(bindings) // $let kind of statements return bindings; for internal use only
| EParameters(array<string>) // for $defun; for internal use only
and bindings = Belt.Map.String.t<expression>
and bindings = Belt.Map.String.t<expressionValue>
type reducerFn = (
expression,
bindings,
environment,
) => result<expressionValue, Reducer_ErrorValue.errorValue>
/*
Converts the expression to String
*/
let rec toString = expression =>
switch expression {
| EList(aList) =>
`(${Belt.List.map(aList, aValue => toString(aValue))
->Extra.List.interperse(" ")
->Belt.List.toArray
->Js.String.concatMany("")})`
| EValue(aValue) => ExpressionValue.toString(aValue)
}
let toStringResult = codeResult =>
switch codeResult {
| Ok(a) => `Ok(${toString(a)})`
| Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})`
}
let inspect = (expr: expression): expression => {
Js.log(toString(expr))
expr
}
let inspectResult = (r: result<expression, Reducer_ErrorValue.errorValue>): result<
expression,
Reducer_ErrorValue.errorValue,
> => {
Js.log(toStringResult(r))
r
}

View File

@ -8,11 +8,11 @@ external castString: unit => string = "%identity"
/*
As JavaScript returns us any type, we need to type check and cast type propertype before using it
*/
let jsToEv = (jsValue): result<expressionValue, errorValue> => {
let jsToEv = (jsValue): result<expressionValue, errorValue> =>
switch Js.typeof(jsValue) {
| "boolean" => jsValue->castBool->EvBool->Ok
| "number" => jsValue->castNumber->EvNumber->Ok
| "string" => jsValue->castString->EvString->Ok
| other => RETodo(`Unhandled MathJs literal type: ${Js.String.make(other)}`)->Error
}
}

View File

@ -1,37 +1,34 @@
module Builder = Reducer_Expression_Builder
/* * WARNING. DO NOT EDIT, BEAUTIFY, COMMENT ON OR REFACTOR THIS CODE.
We will stop using MathJs parser and
this whole file will go to trash
**/
module ErrorValue = Reducer_ErrorValue
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue
module JavaScript = Reducer_Js
module Parse = Reducer_MathJs_Parse
module Result = Belt.Result
type errorValue = ErrorValue.errorValue
type expression = ExpressionT.expression
type expressionValue = ExpressionValue.expressionValue
type errorValue = ErrorValue.errorValue
type blockTag =
| ImportVariablesStatement
| ExportVariablesExpression
type tagOrNode =
| BlockTag(blockTag)
| BlockNode(Parse.node)
let blockToNode = block => block["node"]
let toTagOrNode = block => BlockNode(block["node"])
let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
let rec fromInnerNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => {
let fromNodeList = (nodeList: list<Parse.node>): result<list<expression>, 'e> =>
Belt.List.reduceReverse(nodeList, Ok(list{}), (racc, currNode) =>
racc->Result.flatMap(acc =>
fromNode(currNode)->Result.map(currCode => list{currCode, ...acc})
fromInnerNode(currNode)->Result.map(currCode => list{currCode, ...acc})
)
)
let caseFunctionNode = fNode => {
let rLispArgs = fNode["args"]->Belt.List.fromArray->fromNodeList
rLispArgs->Result.flatMap(lispArgs =>
Builder.passToFunction(fNode->Parse.nameOfFunctionNode, lispArgs)->Ok
rLispArgs->Result.map(lispArgs =>
ExpressionBuilder.eFunction(fNode->Parse.nameOfFunctionNode, lispArgs)
)
}
@ -42,18 +39,15 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
(key: string, value: Parse.node),
) =>
racc->Result.flatMap(acc =>
fromNode(value)->Result.map(valueExpression => {
fromInnerNode(value)->Result.map(valueExpression => {
let entryCode =
list{
key->ExpressionValue.EvString->ExpressionT.EValue,
valueExpression,
}->ExpressionT.EList
list{ExpressionBuilder.eString(key), valueExpression}->ExpressionT.EList
list{entryCode, ...acc}
})
)
)
rargs->Result.flatMap(args =>
Builder.passToFunction("$constructRecord", list{ExpressionT.EList(args)})->Ok
ExpressionBuilder.eFunction("$constructRecord", list{ExpressionT.EList(args)})->Ok
) // $constructRecord gets a single argument: List of key-value paiers
}
@ -66,7 +60,7 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
Ok(list{}),
(racc, currentPropertyMathJsNode) =>
racc->Result.flatMap(acc =>
fromNode(currentPropertyMathJsNode)->Result.map(propertyCode => list{
fromInnerNode(currentPropertyMathJsNode)->Result.map(propertyCode => list{
propertyCode,
...acc,
})
@ -77,28 +71,41 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
let caseAccessorNode = (objectNode, indexNode) => {
caseIndexNode(indexNode)->Result.flatMap(indexCode => {
fromNode(objectNode)->Result.flatMap(objectCode =>
Builder.passToFunction("$atIndex", list{objectCode, indexCode})->Ok
fromInnerNode(objectNode)->Result.flatMap(objectCode =>
ExpressionBuilder.eFunction("$atIndex", list{objectCode, indexCode})->Ok
)
})
}
let caseBlock = (nodesArray: array<Parse.node>): result<expression, errorValue> => {
let rStatements: result<list<expression>, 'a> =
nodesArray
->Belt.List.fromArray
->Belt.List.reduceReverse(Ok(list{}), (racc, currNode) =>
racc->Result.flatMap(acc =>
fromInnerNode(currNode)->Result.map(currCode => list{currCode, ...acc})
)
)
rStatements->Result.map(statements => ExpressionBuilder.eBlock(statements))
}
let caseAssignmentNode = aNode => {
let symbol = aNode["object"]["name"]->Builder.toEvSymbolValue
let rValueExpression = fromNode(aNode["value"])
rValueExpression->Result.flatMap(valueExpression =>
Builder.passToFunction("$let", list{symbol, valueExpression})->Ok
let symbolName = aNode["object"]["name"]
let rValueExpression = fromInnerNode(aNode["value"])
rValueExpression->Result.map(valueExpression =>
ExpressionBuilder.eLetStatement(symbolName, valueExpression)
)
}
let caseFunctionAssignmentNode = faNode => {
let symbol = faNode["name"]->Builder.toEvSymbolValue
let rValueExpression = fromNode(faNode["expr"])
let symbol = faNode["name"]->ExpressionBuilder.eSymbol
let rValueExpression = fromInnerNode(faNode["expr"])
rValueExpression->Result.flatMap(valueExpression => {
let lispParams = faNode["params"]->ExpressionT.EParameters
let lambda = Builder.passToFunction("$lambda", list{lispParams, valueExpression})
Builder.passToFunction("$let", list{symbol, lambda})->Ok
let lispParams = ExpressionBuilder.eArrayString(faNode["params"])
let valueBlock = ExpressionBuilder.eBlock(list{valueExpression})
let lambda = ExpressionBuilder.eFunction("$$lambda", list{lispParams, valueBlock})
ExpressionBuilder.eFunction("$let", list{symbol, lambda})->Ok
})
}
@ -111,11 +118,11 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
| MjArrayNode(aNode) => caseArrayNode(aNode)
| MjAssignmentNode(aNode) => caseAssignmentNode(aNode)
| MjSymbolNode(sNode) => {
let expr: expression = Builder.toEvSymbolValue(sNode["name"])
let expr: expression = ExpressionBuilder.eSymbol(sNode["name"])
let rExpr: result<expression, errorValue> = expr->Ok
rExpr
}
| MjBlockNode(bNode) => bNode["blocks"]->Belt.Array.map(toTagOrNode)->caseTagOrNodes
| MjBlockNode(bNode) => bNode["blocks"]->Js.Array2.map(blockToNode)->caseBlock
| MjConstantNode(cNode) =>
cNode["value"]->JavaScript.Gate.jsToEv->Result.flatMap(v => v->ExpressionT.EValue->Ok)
| MjFunctionAssignmentNode(faNode) => caseFunctionAssignmentNode(faNode)
@ -123,78 +130,10 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
| MjIndexNode(iNode) => caseIndexNode(iNode)
| MjObjectNode(oNode) => caseObjectNode(oNode)
| MjOperatorNode(opNode) => opNode->Parse.castOperatorNodeToFunctionNode->caseFunctionNode
| MjParenthesisNode(pNode) => pNode["content"]->fromNode
| MjParenthesisNode(pNode) => pNode["content"]->fromInnerNode
}
rFinalExpression
})
and caseTagOrNodes = (tagOrNodes): result<expression, errorValue> => {
let initialBindings = Builder.passToFunction("$$bindings", list{})->Ok
let lastIndex = Belt.Array.length(tagOrNodes) - 1
tagOrNodes->Belt.Array.reduceWithIndex(initialBindings, (rPreviousBindings, tagOrNode, i) => {
rPreviousBindings->Result.flatMap(previousBindings => {
let rStatement: result<expression, errorValue> = switch tagOrNode {
| BlockNode(node) => fromNode(node)
| BlockTag(tag) =>
switch tag {
| ImportVariablesStatement =>
Builder.passToFunction("$importVariablesStatement", list{})->Ok
| ExportVariablesExpression =>
Builder.passToFunction("$exportVariablesExpression", list{})->Ok
}
}
let bindName = if i == lastIndex {
"$$bindExpression"
} else {
"$$bindStatement"
}
rStatement->Result.flatMap((statement: expression) => {
Builder.passToFunction(bindName, list{previousBindings, statement})->Ok
})
})
})
}
let fromPartialNode = (mathJsNode: Parse.node): result<expression, errorValue> => {
Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => {
let casePartialBlockNode = (bNode: Parse.blockNode) => {
let blocksOrTags = bNode["blocks"]->Belt.Array.map(toTagOrNode)
let completed = Js.Array2.concat(blocksOrTags, [BlockTag(ExportVariablesExpression)])
completed->caseTagOrNodes
}
let casePartialExpression = (node: Parse.node) => {
let completed = [BlockNode(node), BlockTag(ExportVariablesExpression)]
completed->caseTagOrNodes
}
let rFinalExpression: result<expression, errorValue> = switch typedMathJsNode {
| MjBlockNode(bNode) => casePartialBlockNode(bNode)
| _ => casePartialExpression(mathJsNode)
}
rFinalExpression
})
}
let fromOuterNode = (mathJsNode: Parse.node): result<expression, errorValue> => {
Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => {
let casePartialBlockNode = (bNode: Parse.blockNode) => {
let blocksOrTags = bNode["blocks"]->Belt.Array.map(toTagOrNode)
let completed = blocksOrTags
completed->caseTagOrNodes
}
let casePartialExpression = (node: Parse.node) => {
let completed = [BlockNode(node)]
completed->caseTagOrNodes
}
let rFinalExpression: result<expression, errorValue> = switch typedMathJsNode {
| MjBlockNode(bNode) => casePartialBlockNode(bNode)
| _ => casePartialExpression(mathJsNode)
}
rFinalExpression
})
}
let fromNode = (node: Parse.node): result<expression, errorValue> =>
fromInnerNode(node)->Result.map(expr => ExpressionBuilder.eBlock(list{expr}))

View File

@ -11,17 +11,19 @@ type internalCode = Object
@genType
type rec expressionValue =
| EvArray(array<expressionValue>)
| EvArrayString(array<string>)
| EvBool(bool)
| EvCall(string) // External function call
| EvDistribution(DistributionTypes.genericDist)
| EvLambda((array<string>, internalCode))
| EvLambda((array<string>, record, internalCode))
| EvNumber(float)
| EvRecord(Js.Dict.t<expressionValue>)
| EvRecord(record)
| EvString(string)
| EvSymbol(string)
and record = Js.Dict.t<expressionValue>
@genType
type externalBindings = Js.Dict.t<expressionValue>
type externalBindings = record
@genType
let defaultExternalBindings: externalBindings = Js.Dict.empty()
@ -29,20 +31,21 @@ type functionCall = (string, array<expressionValue>)
let rec toString = aValue =>
switch aValue {
| EvArray(anArray) => {
let args = anArray->Js.Array2.map(each => toString(each))->Js.Array2.toString
`[${args}]`
}
| EvArrayString(anArray) => {
let args = anArray->Js.Array2.toString
`[${args}]`
}
| EvBool(aBool) => Js.String.make(aBool)
| EvCall(fName) => `:${fName}`
| EvLambda((parameters, _internalCode)) => `lambda(${Js.Array2.toString(parameters)}=>internal)`
| EvLambda((parameters, _context, _internalCode)) =>
`lambda(${Js.Array2.toString(parameters)}=>internal)`
| EvNumber(aNumber) => Js.String.make(aNumber)
| EvString(aString) => `'${aString}'`
| EvSymbol(aString) => `:${aString}`
| EvArray(anArray) => {
let args =
anArray
->Belt.Array.map(each => toString(each))
->Extra_Array.interperse(", ")
->Js.String.concatMany("")
`[${args}]`
}
| EvRecord(aRecord) => aRecord->toStringRecord
| EvDistribution(dist) => GenericDist.toString(dist)
}
@ -50,19 +53,19 @@ and toStringRecord = aRecord => {
let pairs =
aRecord
->Js.Dict.entries
->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${toString(eachValue)}`)
->Extra_Array.interperse(", ")
->Js.String.concatMany("")
->Js.Array2.map(((eachKey, eachValue)) => `${eachKey}: ${toString(eachValue)}`)
->Js.Array2.toString
`{${pairs}}`
}
let toStringWithType = aValue =>
switch aValue {
| EvArray(_) => `Array::${toString(aValue)}`
| EvArrayString(_) => `ArrayString::${toString(aValue)}`
| EvBool(_) => `Bool::${toString(aValue)}`
| EvCall(_) => `Call::${toString(aValue)}`
| EvDistribution(_) => `Distribution::${toString(aValue)}`
| EvLambda((_parameters, _internalCode)) => `Lambda::${toString(aValue)}`
| EvLambda((_parameters, _context, _internalCode)) => `Lambda::${toString(aValue)}`
| EvNumber(_) => `Number::${toString(aValue)}`
| EvRecord(_) => `Record::${toString(aValue)}`
| EvString(_) => `String::${toString(aValue)}`
@ -70,7 +73,7 @@ let toStringWithType = aValue =>
}
let argsToString = (args: array<expressionValue>): string => {
args->Belt.Array.map(arg => arg->toString)->Extra_Array.interperse(", ")->Js.String.concatMany("")
args->Js.Array2.map(arg => arg->toString)->Js.Array2.toString
}
let toStringFunctionCall = ((fn, args)): string => `${fn}(${argsToString(args)})`

View File

@ -28,14 +28,14 @@ module Helpers = {
let catchAndConvertTwoArgsToDists = (args: array<expressionValue>): option<(
DistributionTypes.genericDist,
DistributionTypes.genericDist,
)> => {
)> =>
switch args {
| [EvDistribution(a), EvDistribution(b)] => Some((a, b))
| [EvNumber(a), EvDistribution(b)] => Some((GenericDist.fromFloat(a), b))
| [EvDistribution(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b)))
| _ => None
}
}
let toFloatFn = (
fnCall: DistributionTypes.DistributionOperation.toFloat,
@ -119,7 +119,7 @@ module Helpers = {
mixtureWithGivenWeights(distributions, weights)
}
let mixture = (args: array<expressionValue>): DistributionOperation.outputType => {
let mixture = (args: array<expressionValue>): DistributionOperation.outputType =>
switch E.A.last(args) {
| Some(EvArray(b)) => {
let weights = parseNumberArray(b)
@ -138,7 +138,7 @@ module Helpers = {
}
| _ => GenDistError(ArgumentError("Last argument of mx must be array or distribution"))
}
}
}
module SymbolicConstructors = {

View File

@ -13,6 +13,12 @@ type samplingParams = DistributionOperation.env
@genType
type genericDist = DistributionTypes.genericDist
@genType
type sampleSetDist = SampleSetDist.t
@genType
type symbolicDist = SymbolicDistTypes.symbolicDist
@genType
type distributionError = DistributionTypes.error
@ -34,6 +40,12 @@ let evaluate = Reducer.evaluate
@genType
let evaluateUsingOptions = Reducer.evaluateUsingOptions
@genType
let evaluatePartialUsingExternalBindings = Reducer.evaluatePartialUsingExternalBindings
@genType
type externalBindings = Reducer.externalBindings
@genType
type expressionValue = ReducerInterface_ExpressionValue.expressionValue

View File

@ -1,4 +1,7 @@
open Rationale.Function.Infix
/*
Some functions from modules `L`, `O`, and `R` below were copied directly from
running `rescript convert -all` on Rationale https://github.com/jonlaing/rationale
*/
module FloatFloatMap = {
module Id = Belt.Id.MakeComparable({
type t = float
@ -8,7 +11,7 @@ module FloatFloatMap = {
type t = Belt.MutableMap.t<Id.t, float, Id.identity>
let fromArray = (ar: array<(float, float)>) => Belt.MutableMap.fromArray(ar, ~id=module(Id))
let toArray = (t: t) => Belt.MutableMap.toArray(t)
let toArray = (t: t): array<(float, float)> => Belt.MutableMap.toArray(t)
let empty = () => Belt.MutableMap.make(~id=module(Id))
let increment = (el, t: t) =>
Belt.MutableMap.update(t, el, x =>
@ -20,6 +23,10 @@ module FloatFloatMap = {
let get = (el, t: t) => Belt.MutableMap.get(t, el)
let fmap = (fn, t: t) => Belt.MutableMap.map(t, fn)
let partition = (fn, t: t) => {
let (match, noMatch) = Belt.Array.partition(toArray(t), fn)
(fromArray(match), fromArray(noMatch))
}
}
module Int = {
@ -51,17 +58,59 @@ module O = {
| None => rFn()
}
()
let fmap = Rationale.Option.fmap
let bind = Rationale.Option.bind
let default = Rationale.Option.default
let isSome = Rationale.Option.isSome
let isNone = Rationale.Option.isNone
let toExn = Rationale.Option.toExn
let some = Rationale.Option.some
let firstSome = Rationale.Option.firstSome
let toExt = Rationale.Option.toExn // wanna flag this-- looks like a typo but `Rationale.OptiontoExt` doesn't exist.
let flatApply = (fn, b) => Rationale.Option.apply(fn, Some(b)) |> Rationale.Option.flatten
let flatten = Rationale.Option.flatten
let fmap = (f: 'a => 'b, x: option<'a>): option<'b> => {
switch x {
| None => None
| Some(x') => Some(f(x'))
}
}
let bind = (o, f) =>
switch o {
| None => None
| Some(a) => f(a)
}
let default = (d, o) =>
switch o {
| None => d
| Some(a) => a
}
let isSome = o =>
switch o {
| Some(_) => true
| _ => false
}
let isNone = o =>
switch o {
| None => true
| _ => false
}
let toExn = (err, o) =>
switch o {
| None => raise(Failure(err))
| Some(a) => a
}
let some = a => Some(a)
let firstSome = (a, b) =>
switch a {
| None => b
| _ => a
}
let toExt = toExn
let flatten = o =>
switch o {
| None => None
| Some(x) => x
}
let apply = (o, a) =>
switch o {
| Some(f) => bind(a, b => some(f(b)))
| _ => None
}
let flatApply = (fn, b) => apply(fn, Some(b)) |> flatten
let toBool = opt =>
switch opt {
@ -109,6 +158,11 @@ module O2 = {
/* Functions */
module F = {
let pipe = (f, g, x) => g(f(x))
let compose = (f, g, x) => f(g(x))
let flip = (f, a, b) => f(b, a)
let always = (x, _y) => x
let apply = (a, e) => a |> e
let flatten2Callbacks = (fn1, fn2, fnlast) =>
@ -156,10 +210,31 @@ exception Assertion(string)
/* R for Result */
module R = {
let result = Rationale.Result.result
open Belt.Result
let result = (okF, errF, r) =>
switch r {
| Ok(a) => okF(a)
| Error(err) => errF(err)
}
let id = e => e |> result(U.id, U.id)
let fmap = Rationale.Result.fmap
let bind = Rationale.Result.bind
let isOk = Belt.Result.isOk
let getError = (r: result<'a, 'b>) =>
switch r {
| Ok(_) => None
| Error(e) => Some(e)
}
let fmap = (f: 'a => 'b, r: result<'a, 'c>): result<'b, 'c> => {
switch r {
| Ok(r') => Ok(f(r'))
| Error(err) => Error(err)
}
}
let bind = (r, f) =>
switch r {
| Ok(a) => f(a)
| Error(err) => Error(err)
}
let toExn = (msg: string, x: result<'a, 'b>): 'a =>
switch x {
| Ok(r) => r
@ -186,14 +261,17 @@ module R = {
let errorIfCondition = (errorCondition, errorMessage, r) =>
errorCondition(r) ? Error(errorMessage) : Ok(r)
let ap = Rationale.Result.ap
let ap = (r, a) =>
switch r {
| Ok(f) => Ok(f(a))
| Error(err) => Error(err)
}
let ap' = (r, a) =>
switch r {
| Ok(f) => fmap(f, a)
| Error(err) => Error(err)
}
// (a1 -> a2 -> r) -> m a1 -> m a2 -> m r // not in Rationale
let liftM2: (('a, 'b) => 'c, result<'a, 'd>, result<'b, 'd>) => result<'c, 'd> = (op, xR, yR) => {
ap'(fmap(op, xR), yR)
}
@ -243,7 +321,7 @@ module S = {
}
module J = {
let toString = \"||>"(Js.Json.decodeString, O.default(""))
let toString = F.pipe(Js.Json.decodeString, O.default(""))
let fromString = Js.Json.string
let fromNumber = Js.Json.number
@ -256,7 +334,7 @@ module J = {
let toString = (str: option<'a>) =>
switch str {
| Some(str) => Some(str |> \"||>"(Js.Json.decodeString, O.default("")))
| Some(str) => Some(str |> F.pipe(Js.Json.decodeString, O.default("")))
| _ => None
}
}
@ -271,34 +349,132 @@ module JsDate = {
/* List */
module L = {
module Util = {
let eq = (a, b) => a == b
}
let fmap = List.map
let get = Belt.List.get
let toArray = Array.of_list
let fmapi = List.mapi
let concat = List.concat
let drop = Rationale.RList.drop
let remove = Rationale.RList.remove
let concat' = (xs, ys) => List.append(ys, xs)
let rec drop = (i, xs) =>
switch (i, xs) {
| (_, list{}) => list{}
| (i, _) if i <= 0 => xs
| (i, list{_, ...b}) => drop(i - 1, b)
}
let append = (a, xs) => List.append(xs, list{a})
let take = {
let rec loop = (i, xs, acc) =>
switch (i, xs) {
| (i, _) if i <= 0 => acc
| (_, list{}) => acc
| (i, list{a, ...b}) => loop(i - 1, b, append(a, acc))
}
(i, xs) => loop(i, xs, list{})
}
let takeLast = (i, xs) => List.rev(xs) |> take(i) |> List.rev
let splitAt = (i, xs) => (take(i, xs), takeLast(List.length(xs) - i, xs))
let remove = (i, n, xs) => {
let (a, b) = splitAt(i, xs)
\"@"(a, drop(n, b))
}
let find = List.find
let filter = List.filter
let for_all = List.for_all
let exists = List.exists
let sort = List.sort
let length = List.length
let filter_opt = Rationale.RList.filter_opt
let uniqBy = Rationale.RList.uniqBy
let join = Rationale.RList.join
let head = Rationale.RList.head
let uniq = Rationale.RList.uniq
let filter_opt = xs => {
let rec loop = (l, acc) =>
switch l {
| list{} => acc
| list{hd, ...tl} =>
switch hd {
| None => loop(tl, acc)
| Some(x) => loop(tl, list{x, ...acc})
}
}
List.rev(loop(xs, list{}))
}
let containsWith = f => List.exists(f)
let uniqWithBy = (eq, f, xs) =>
List.fold_left(
((acc, tacc), v) =>
containsWith(eq(f(v)), tacc) ? (acc, tacc) : (append(v, acc), append(f(v), tacc)),
(list{}, list{}),
xs,
) |> fst
let uniqBy = (f, xs) => uniqWithBy(Util.eq, f, xs)
let join = j => List.fold_left((acc, v) => String.length(acc) == 0 ? v : acc ++ (j ++ v), "")
let head = xs =>
switch List.hd(xs) {
| exception _ => None
| a => Some(a)
}
let uniq = xs => uniqBy(x => x, xs)
let flatten = List.flatten
let last = Rationale.RList.last
let last = xs => xs |> List.rev |> head
let append = List.append
let getBy = Belt.List.getBy
let dropLast = Rationale.RList.dropLast
let contains = Rationale.RList.contains
let without = Rationale.RList.without
let update = Rationale.RList.update
let dropLast = (i, xs) => take(List.length(xs) - i, xs)
let containsWith = f => List.exists(f)
let contains = x => containsWith(Util.eq(x))
let reject = pred => List.filter(x => !pred(x))
let tail = xs =>
switch List.tl(xs) {
| exception _ => None
| a => Some(a)
}
let init = xs => {
O.fmap(List.rev, xs |> List.rev |> tail)
}
let singleton = (x: 'a): list<'a> => list{x}
let adjust = (f, i, xs) => {
let (a, b) = splitAt(i + 1, xs)
switch a {
| _ if i < 0 => xs
| _ if i >= List.length(xs) => xs
| list{} => b
| list{a} => list{f(a), ...b}
| a =>
O.fmap(
concat'(b),
O.bind(init(a), x =>
O.fmap(F.flip(append, x), O.fmap(fmap(f), O.fmap(singleton, last(a))))
),
) |> O.default(xs)
}
}
let without = (exclude, xs) => reject(x => contains(x, exclude), xs)
let update = (x, i, xs) => adjust(F.always(x), i, xs)
let iter = List.iter
let findIndex = Rationale.RList.findIndex
let findIndex = {
let rec loop = (pred, xs, i) =>
switch xs {
| list{} => None
| list{a, ...b} => pred(a) ? Some(i) : loop(pred, b, i + 1)
}
(pred, xs) => loop(pred, xs, 0)
}
let headSafe = Belt.List.head
let tailSafe = Belt.List.tail
let headExn = Belt.List.headExn
@ -360,7 +536,7 @@ module A = {
Belt.Array.getUnsafe(a, index),
Belt.Array.getUnsafe(a, index + 1),
))
|> Rationale.Result.return
|> (x => Ok(x))
}
let tail = Belt.Array.sliceToEnd(_, 1)
@ -424,8 +600,8 @@ module A = {
module O = {
let concatSomes = (optionals: array<option<'a>>): array<'a> =>
optionals
|> Js.Array.filter(Rationale.Option.isSome)
|> Js.Array.map(Rationale.Option.toExn("Warning: This should not have happened"))
|> Js.Array.filter(O.isSome)
|> Js.Array.map(O.toExn("Warning: This should not have happened"))
let defaultEmpty = (o: option<array<'a>>): array<'a> =>
switch o {
| Some(o) => o
@ -475,76 +651,8 @@ module A = {
}
}
module Sorted = {
let min = first
let max = last
let range = (~min=min, ~max=max, a) =>
switch (min(a), max(a)) {
| (Some(min), Some(max)) => Some(max -. min)
| _ => None
}
let floatCompare: (float, float) => int = compare
let binarySearchFirstElementGreaterIndex = (ar: array<'a>, el: 'a) => {
let el = Belt.SortArray.binarySearchBy(ar, el, floatCompare)
let el = el < 0 ? el * -1 - 1 : el
switch el {
| e if e >= length(ar) => #overMax
| e if e == 0 => #underMin
| e => #firstHigher(e)
}
}
let concat = (t1: array<'a>, t2: array<'a>) => {
let ts = Belt.Array.concat(t1, t2)
ts |> Array.fast_sort(floatCompare)
ts
}
let concatMany = (t1: array<array<'a>>) => {
let ts = Belt.Array.concatMany(t1)
ts |> Array.fast_sort(floatCompare)
ts
}
module Floats = {
let isSorted = (ar: array<float>): bool =>
reduce(zip(ar, tail(ar)), true, (acc, (first, second)) => acc && first < second)
let makeIncrementalUp = (a, b) =>
Array.make(b - a + 1, a) |> Array.mapi((i, c) => c + i) |> Belt.Array.map(_, float_of_int)
let makeIncrementalDown = (a, b) =>
Array.make(a - b + 1, a) |> Array.mapi((i, c) => c - i) |> Belt.Array.map(_, float_of_int)
let split = (sortedArray: array<float>) => {
let continuous = []
let discrete = FloatFloatMap.empty()
Belt.Array.forEachWithIndex(sortedArray, (_, element) => {
// let maxIndex = (sortedArray |> Array.length) - 1
// let possiblySimilarElements = switch index {
// | 0 => [index + 1]
// | n if n == maxIndex => [index - 1]
// | _ => [index - 1, index + 1]
// } |> Belt.Array.map(_, r => sortedArray[r])
// let hasSimilarElement = Belt.Array.some(possiblySimilarElements, r => r == element)
let hasSimilarElement = false
hasSimilarElement
? FloatFloatMap.increment(element, discrete)
: {
let _ = Js.Array.push(element, continuous)
}
()
})
(continuous, discrete)
}
}
}
module Floats = {
type t = array<float>
let mean = Jstat.mean
let geomean = Jstat.geomean
let mode = Jstat.mode
@ -553,14 +661,31 @@ module A = {
let sum = Jstat.sum
let random = Js.Math.random_int
let floatCompare: (float, float) => int = compare
let sort = t => {
let r = t
r |> Array.fast_sort(floatCompare)
r
}
let getNonFinite = (t: t) => Belt.Array.getBy(t, r => !Js.Float.isFinite(r))
let getBelowZero = (t: t) => Belt.Array.getBy(t, r => r < 0.0)
let isSorted = (t: t): bool =>
if Array.length(t) < 1 {
true
} else {
reduce(zip(t, tail(t)), true, (acc, (first, second)) => acc && first < second)
}
//Passing true for the exclusive parameter excludes both endpoints of the range.
//https://jstat.github.io/all.html
let percentile = (a, b) => Jstat.percentile(a, b, false)
// 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)
let diff = (t: t): array<float> =>
Belt.Array.zipBy(t, Belt.Array.sliceToEnd(t, 1), (left, right) => right -. left)
exception RangeError(string)
let range = (min: float, max: float, n: int): array<float> =>
@ -578,18 +703,104 @@ module A = {
let min = Js.Math.minMany_float
let max = Js.Math.maxMany_float
module Sorted = {
let min = first
let max = last
let range = (~min=min, ~max=max, a) =>
switch (min(a), max(a)) {
| (Some(min), Some(max)) => Some(max -. min)
| _ => None
}
let binarySearchFirstElementGreaterIndex = (ar: array<'a>, el: 'a) => {
let el = Belt.SortArray.binarySearchBy(ar, el, floatCompare)
let el = el < 0 ? el * -1 - 1 : el
switch el {
| e if e >= length(ar) => #overMax
| e if e == 0 => #underMin
| e => #firstHigher(e)
}
}
let concat = (t1: array<'a>, t2: array<'a>) => Belt.Array.concat(t1, t2)->sort
let concatMany = (t1: array<array<'a>>) => Belt.Array.concatMany(t1)->sort
let makeIncrementalUp = (a, b) =>
Array.make(b - a + 1, a) |> Array.mapi((i, c) => c + i) |> Belt.Array.map(_, float_of_int)
let makeIncrementalDown = (a, b) =>
Array.make(a - b + 1, a) |> Array.mapi((i, c) => c - i) |> Belt.Array.map(_, float_of_int)
/*
This function goes through a sorted array and divides it into two different clusters:
continuous samples and discrete samples. The discrete samples are stored in a mutable map.
Samples are thought to be discrete if they have any duplicates.
*/
let _splitContinuousAndDiscreteForDuplicates = (sortedArray: array<float>) => {
let continuous: array<float> = []
let discrete = FloatFloatMap.empty()
Belt.Array.forEachWithIndex(sortedArray, (index, element) => {
let maxIndex = (sortedArray |> Array.length) - 1
let possiblySimilarElements = switch index {
| 0 => [index + 1]
| n if n == maxIndex => [index - 1]
| _ => [index - 1, index + 1]
} |> Belt.Array.map(_, r => sortedArray[r])
let hasSimilarElement = Belt.Array.some(possiblySimilarElements, r => r == element)
hasSimilarElement
? FloatFloatMap.increment(element, discrete)
: {
let _ = Js.Array.push(element, continuous)
}
()
})
(continuous, discrete)
}
/*
This function works very similarly to splitContinuousAndDiscreteForDuplicates. The one major difference
is that you can specify a minDiscreteWeight. If the min discreet weight is 4, that would mean that
at least four elements needed from a specific value for that to be kept as discrete. This is important
because in some cases, we can expect that some common elements will be generated by regular operations.
The final continous array will be sorted.
*/
let splitContinuousAndDiscreteForMinWeight = (
sortedArray: array<float>,
~minDiscreteWeight: int,
) => {
let (continuous, discrete) = _splitContinuousAndDiscreteForDuplicates(sortedArray)
let keepFn = v => Belt.Float.toInt(v) >= minDiscreteWeight
let (discreteToKeep, discreteToIntegrate) = FloatFloatMap.partition(
((_, v)) => keepFn(v),
discrete,
)
let newContinousSamples =
discreteToIntegrate->FloatFloatMap.toArray
|> fmap(((k, v)) => Belt.Array.makeBy(Belt.Float.toInt(v), _ => k))
|> Belt.Array.concatMany
let newContinuous = concat(continuous, newContinousSamples)
newContinuous |> Array.fast_sort(floatCompare)
(newContinuous, discreteToKeep)
}
}
}
module Sorted = Floats.Sorted
}
module A2 = {
let fmap = (a, b) => A.fmap(b, a)
let joinWith = (a, b) => A.joinWith(b, a)
let filter = (a, b) => A.filter(b, a)
}
module JsArray = {
let concatSomes = (optionals: Js.Array.t<option<'a>>): Js.Array.t<'a> =>
optionals
|> Js.Array.filter(Rationale.Option.isSome)
|> Js.Array.map(Rationale.Option.toExn("Warning: This should not have happened"))
|> Js.Array.filter(O.isSome)
|> Js.Array.map(O.toExn("Warning: This should not have happened"))
let filter = Js.Array.filter
}

View File

@ -29,6 +29,18 @@ type distToFloatOperation = [
module Convolution = {
type t = convolutionOperation
//Only a selection of operations are supported by convolution.
let fromAlgebraicOperation = (op: algebraicOperation): option<convolutionOperation> =>
switch op {
| #Add => Some(#Add)
| #Subtract => Some(#Subtract)
| #Multiply => Some(#Multiply)
| #Divide | #Power | #Logarithm => None
}
let canDoAlgebraicOperation = (op: algebraicOperation): bool =>
fromAlgebraicOperation(op)->E.O.isSome
let toFn: (t, float, float) => float = x =>
switch x {
| #Add => \"+."

View File

@ -4,6 +4,42 @@ type xyShape = {
ys: array<float>,
}
type propertyName = string
@genType
type rec error =
| NotSorted(propertyName)
| IsEmpty(propertyName)
| NotFinite(propertyName, float)
| DifferentLengths({p1Name: string, p2Name: string, p1Length: int, p2Length: int})
| MultipleErrors(array<error>)
@genType
module Error = {
let mapErrorArrayToError = (errors: array<error>): option<error> => {
switch errors {
| [] => None
| [error] => Some(error)
| _ => Some(MultipleErrors(errors))
}
}
let rec toString = (t: error) =>
switch t {
| NotSorted(propertyName) => `${propertyName} is not sorted`
| IsEmpty(propertyName) => `${propertyName} is empty`
| NotFinite(propertyName, exampleValue) =>
`${propertyName} is not finite. Example value: ${E.Float.toString(exampleValue)}`
| DifferentLengths({p1Name, p2Name, p1Length, p2Length}) =>
`${p1Name} and ${p2Name} have different lengths. ${p1Name} has length ${E.I.toString(
p1Length,
)} and ${p2Name} has length ${E.I.toString(p2Length)}`
| MultipleErrors(errors) =>
`Multiple Errors: ${E.A2.fmap(errors, toString)->E.A2.fmap(r => `[${r}]`)
|> E.A.joinWith(", ")}`
}
}
@genType
type interpolationStrategy = [
| #Stepwise
@ -60,6 +96,44 @@ module T = {
let fromZippedArray = (pairs: array<(float, float)>): t => pairs |> Belt.Array.unzip |> fromArray
let equallyDividedXs = (t: t, newLength) => E.A.Floats.range(minX(t), maxX(t), newLength)
let toJs = (t: t) => {"xs": t.xs, "ys": t.ys}
module Validator = {
let fnName = "XYShape validate"
let notSortedError = (p: string): error => NotSorted(p)
let notFiniteError = (p, exampleValue): error => NotFinite(p, exampleValue)
let isEmptyError = (propertyName): error => IsEmpty(propertyName)
let differentLengthsError = (t): error => DifferentLengths({
p1Name: "Xs",
p2Name: "Ys",
p1Length: E.A.length(xs(t)),
p2Length: E.A.length(ys(t)),
})
let areXsSorted = (t: t) => E.A.Floats.isSorted(xs(t))
let areXsEmpty = (t: t) => E.A.length(xs(t)) == 0
let getNonFiniteXs = (t: t) => t->xs->E.A.Floats.getNonFinite
let getNonFiniteYs = (t: t) => t->ys->E.A.Floats.getNonFinite
let validate = (t: t) => {
let xsNotSorted = areXsSorted(t) ? None : Some(notSortedError("Xs"))
let xsEmpty = areXsEmpty(t) ? Some(isEmptyError("Xs")) : None
let differentLengths =
E.A.length(xs(t)) !== E.A.length(ys(t)) ? Some(differentLengthsError(t)) : None
let xsNotFinite = getNonFiniteXs(t)->E.O2.fmap(notFiniteError("Xs"))
let ysNotFinite = getNonFiniteYs(t)->E.O2.fmap(notFiniteError("Ys"))
[xsNotSorted, xsEmpty, differentLengths, xsNotFinite, ysNotFinite]
->E.A.O.concatSomes
->Error.mapErrorArrayToError
}
}
let make = (~xs: array<float>, ~ys: array<float>) => {
let attempt: t = {xs: xs, ys: ys}
switch Validator.validate(attempt) {
| Some(error) => Error(error)
| None => Ok(attempt)
}
}
}
module Ts = {

View File

@ -0,0 +1,7 @@
---
sidebar_position: 6
title: Gallery
---
- [Adjusting probabilities for the passage of time](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3/p/j8o6sgRerE3tqNWdj) by Nuño Sempere
- [GiveWell's GiveDirectly cost effectiveness analysis](https://observablehq.com/@hazelfire/givewells-givedirectly-cost-effectiveness-analysis) by Sam Nolan

View File

@ -1,12 +1,10 @@
---
sidebar_position: 5
title: Three Formats of Distributions
author: Ozzie Gooen
date: 02-19-2022
---
# Three Formats of Distributions
_Author: Ozzie Gooen_
_Written on: Feb 19, 2022_
Probability distributions have several subtle possible formats. Three important ones that we deal with in Squiggle are symbolic, sample set, and graph formats.
_Symbolic_ formats are just the math equations. `normal(5,3)` is the symbolic representation of a normal distribution.

View File

@ -1,39 +1,53 @@
---
sidebar_position: 2
title: Language Basics
---
import { SquiggleEditor } from "../../src/components/SquiggleEditor";
# Squiggle Language
## Expressions
The squiggle language has a very simple syntax. The best way to get to understand
it is by simply looking at examples.
A distribution
## Basic Language
<SquiggleEditor initialSquiggleString={`mixture(1 to 2, 3, [0.3, 0.7])`} />
As an example:
A number
<SquiggleEditor initialSquiggleString="4.321e-3" />
Arrays
<SquiggleEditor
initialSquiggleString={`[beta(1,10), 4, isNormalized(toSampleSet(1 to 2))]`}
/>
Records
<SquiggleEditor
initialSquiggleString={`d = {dist: triangular(0, 1, 2), weight: 0.25}
d.dist`}
/>
## Statements
A statement assigns expressions to names. It looks like `<symbol> = <expression>`
<SquiggleEditor
initialSquiggleString={`value_of_work = 10 to 70
value_of_work`}
5 + value_of_work / 75`}
/>
Squiggle can declare variables (`value_of_work = 10 to 70`) and declare exports
(the lone `value_of_work` line). Variables can be used later in a squiggle program
and even in other notebooks!
### Functions
An export is rendered to the output view so you can see your result.
the exports can be expressions, such as:
<SquiggleEditor initialSquiggleString="normal(0,1)" />
## Functions
Squiggle supports functions, including the rendering of functions:
We can define functions
<SquiggleEditor
initialSquiggleString={`ozzie_estimate(t) = lognormal({mean: 3 + (t+.1)^2.5, stdev: 8})
ozzie_estimate
`}
initialSquiggleString={`ozzie_estimate(t) = lognormal(1, t ^ 1.01)
nuño_estimate(t, m) = mixture(0.5 to 2, normal(m, t ^ 1.25))
ozzie_estimate(5) * nuño_estimate(5.01, 1)`}
/>
## See more
- [Functions reference](https://squiggle-language.com/docs/Features/Functions)
- [Gallery](https://squiggle-language.com/docs/Discussions/Gallery)

View File

@ -1,13 +1,12 @@
---
sidebar_position: 3
title: Node Packages
---
# Javascript Libraries
There are two JavaScript packages currently available for Squiggle:
- [`@quri/squiggle-lang`](https://www.npmjs.com/package/@quri/squiggle-lang)
- [`@quri/squiggle-components`](https://www.npmjs.com/package/@quri/squiggle-components)
- [`@quri/squiggle-lang`](https://www.npmjs.com/package/@quri/squiggle-lang) ![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-lang.svg)
- [`@quri/squiggle-components`](https://www.npmjs.com/package/@quri/squiggle-components) ![npm version](https://badge.fury.io/js/@quri%2Fsquiggle-components.svg)
Types are available for both packages.
@ -23,8 +22,8 @@ argument allows you to pass an environment previously created by another `run`
call. Passing this environment will mean that all previously declared variables
in the previous environment will be made available.
The return type of `run` is a bit complicated, and comes from auto generated js
code that comes from rescript. I highly recommend using typescript when using
The return type of `run` is a bit complicated, and comes from auto generated `js`
code that comes from rescript. We highly recommend using typescript when using
this library to help navigate the return type.
## Squiggle Components

View File

@ -1,6 +1,9 @@
# Processing confidence intervals
---
title: Processing Confidence Intervals
author: Nuño Sempere
---
This page explains what we are doing when we take a 95% confidence interval, and we get a mean and a standard deviation from it
This page explains what we are doing when we take a 90% confidence interval, and we get a mean and a standard deviation from it.
## For normals
@ -19,10 +22,7 @@ module Normal = {
We know that for a normal with mean $\mu$ and standard deviation $\sigma$,
$$
a \cdot Normal(\mu, \sigma) = Normal(a\cdot \mu, |a|\cdot \sigma)
a \cdot Normal(\mu, \sigma) = Normal(a \cdot \mu, |a| \cdot \sigma)
$$
We can now look at the inverse cdf of a $Normal(0,1)$. We find that the 95% point is reached at $1.6448536269514722$. ([source](https://stackoverflow.com/questions/20626994/how-to-calculate-the-inverse-of-the-normal-cumulative-distribution-function-in-p)) This means that the 90% confidence interval is $[-1.6448536269514722, 1.6448536269514722]$, which has a width of $2 \cdot 1.6448536269514722$.
@ -30,3 +30,5 @@ We can now look at the inverse cdf of a $Normal(0,1)$. We find that the 95% poin
So then, if we take a $Normal(0,1)$ and we multiply it by $\frac{(high -. low)}{(2. *. 1.6448536269514722)}$, it's 90% confidence interval will be multiplied by the same amount. Then we just have to shift it by the mean to get our target normal.
## For lognormals
TODO

View File

@ -1,10 +1,16 @@
---
sidebar_position: 1
title: Introduction
---
# Squiggle
Squiggle is an _estimation language_, and a syntax for _calculating and expressing beliefs_ involving uncertainty. It has use cases in forecasting and writing evaluations.
Squiggle is a language for writing calculations under uncertainty. It has use
cases in forecasting and writing better evaluations.
## Get started
The best way to get started with Squiggle is to [try it out yourself](https://playground.squiggle-language.com/).
- [Gallery](https://www.squiggle-language.com/docs/Discussions/Gallery)
- [Squiggle playground](https://squiggle-language.com/playground)
- [Language basics](https://www.squiggle-language.com/docs/Features/Language)
- [Squiggle functions source of truth](https://www.squiggle-language.com/docs/Features/Functions)
- [Known bugs](https://www.squiggle-language.com/docs/Discussions/Bugs)
- [Original lesswrong sequence](https://www.lesswrong.com/s/rDe8QE5NvXcZYzgZ3)
- [Author your squiggle models as Observable notebooks](https://observablehq.com/@hazelfire/squiggle)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,170 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1088" height="687.962" viewBox="0 0 1088 687.962">
<g id="Group_12" data-name="Group 12" transform="translate(-57 -56)">
<g id="Group_11" data-name="Group 11" transform="translate(57 56)">
<path id="Path_83" data-name="Path 83" d="M1017.81,560.461c-5.27,45.15-16.22,81.4-31.25,110.31-20,38.52-54.21,54.04-84.77,70.28a193.275,193.275,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.282,657.282,0,0,0-104.09-13.16q-14.97-.675-29.97-.67c-15.42.02-293.07,5.29-360.67-131.57-16.69-33.76-28.13-75-32.24-125.27-11.63-142.12,52.29-235.46,134.74-296.47,155.97-115.41,369.76-110.57,523.43,7.88C941.15,276.621,1036.99,396.031,1017.81,560.461Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_84" data-name="Path 84" d="M986.56,670.771c-20,38.52-47.21,64.04-77.77,80.28a193.272,193.272,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.3,657.3,0,0,0-104.09-13.16q-14.97-.675-29.97-.67-23.13.03-46.25,1.72c-100.17,7.36-253.82-6.43-321.42-143.29L382,283.981,444.95,445.6l20.09,51.59,55.37-75.98L549,381.981l130.2,149.27,36.8-81.27L970.78,657.9l14.21,11.59Z" transform="translate(-56 -106.019)" fill="#f2f2f2"/>
<path id="Path_85" data-name="Path 85" d="M302,282.962l26-57,36,83-31-60Z" opacity="0.1"/>
<path id="Path_86" data-name="Path 86" d="M610.5,753.821q-14.97-.675-29.97-.67L465.04,497.191Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<path id="Path_87" data-name="Path 87" d="M464.411,315.191,493,292.962l130,150-132-128Z" opacity="0.1"/>
<path id="Path_88" data-name="Path 88" d="M908.79,751.051a193.265,193.265,0,0,1-27.46,11.94L679.2,531.251Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<circle id="Ellipse_11" data-name="Ellipse 11" cx="3" cy="3" r="3" transform="translate(479 98.962)" fill="#f2f2f2"/>
<circle id="Ellipse_12" data-name="Ellipse 12" cx="3" cy="3" r="3" transform="translate(396 201.962)" fill="#f2f2f2"/>
<circle id="Ellipse_13" data-name="Ellipse 13" cx="2" cy="2" r="2" transform="translate(600 220.962)" fill="#f2f2f2"/>
<circle id="Ellipse_14" data-name="Ellipse 14" cx="2" cy="2" r="2" transform="translate(180 265.962)" fill="#f2f2f2"/>
<circle id="Ellipse_15" data-name="Ellipse 15" cx="2" cy="2" r="2" transform="translate(612 96.962)" fill="#f2f2f2"/>
<circle id="Ellipse_16" data-name="Ellipse 16" cx="2" cy="2" r="2" transform="translate(736 192.962)" fill="#f2f2f2"/>
<circle id="Ellipse_17" data-name="Ellipse 17" cx="2" cy="2" r="2" transform="translate(858 344.962)" fill="#f2f2f2"/>
<path id="Path_89" data-name="Path 89" d="M306,121.222h-2.76v-2.76h-1.48v2.76H299V122.7h2.76v2.759h1.48V122.7H306Z" fill="#f2f2f2"/>
<path id="Path_90" data-name="Path 90" d="M848,424.222h-2.76v-2.76h-1.48v2.76H841V425.7h2.76v2.759h1.48V425.7H848Z" fill="#f2f2f2"/>
<path id="Path_91" data-name="Path 91" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_92" data-name="Path 92" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<ellipse id="Ellipse_18" data-name="Ellipse 18" cx="544" cy="30" rx="544" ry="30" transform="translate(0 583.962)" fill="#3f3d56"/>
<path id="Path_93" data-name="Path 93" d="M624,677.981c0,33.137-14.775,24-33,24s-33,9.137-33-24,33-96,33-96S624,644.844,624,677.981Z" transform="translate(-56 -106.019)" fill="#ff6584"/>
<path id="Path_94" data-name="Path 94" d="M606,690.66c0,15.062-6.716,10.909-15,10.909s-15,4.153-15-10.909,15-43.636,15-43.636S606,675.6,606,690.66Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<rect id="Rectangle_97" data-name="Rectangle 97" width="92" height="18" rx="9" transform="translate(489 604.962)" fill="#2f2e41"/>
<rect id="Rectangle_98" data-name="Rectangle 98" width="92" height="18" rx="9" transform="translate(489 586.962)" fill="#2f2e41"/>
<path id="Path_95" data-name="Path 95" d="M193,596.547c0,55.343,34.719,100.126,77.626,100.126" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_96" data-name="Path 96" d="M270.626,696.673c0-55.965,38.745-101.251,86.626-101.251" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_97" data-name="Path 97" d="M221.125,601.564c0,52.57,22.14,95.109,49.5,95.109" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_98" data-name="Path 98" d="M270.626,696.673c0-71.511,44.783-129.377,100.126-129.377" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_99" data-name="Path 99" d="M254.3,697.379s11.009-.339,14.326-2.7,16.934-5.183,17.757-1.395,16.544,18.844,4.115,18.945-28.879-1.936-32.19-3.953S254.3,697.379,254.3,697.379Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_100" data-name="Path 100" d="M290.716,710.909c-12.429.1-28.879-1.936-32.19-3.953-2.522-1.536-3.527-7.048-3.863-9.591l-.368.014s.7,8.879,4.009,10.9,19.761,4.053,32.19,3.953c3.588-.029,4.827-1.305,4.759-3.2C294.755,710.174,293.386,710.887,290.716,710.909Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_101" data-name="Path 101" d="M777.429,633.081c0,38.029,23.857,68.8,53.341,68.8" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_102" data-name="Path 102" d="M830.769,701.882c0-38.456,26.623-69.575,59.525-69.575" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_103" data-name="Path 103" d="M796.755,636.528c0,36.124,15.213,65.354,34.014,65.354" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_104" data-name="Path 104" d="M830.769,701.882c0-49.139,30.773-88.9,68.8-88.9" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_105" data-name="Path 105" d="M819.548,702.367s7.565-.233,9.844-1.856,11.636-3.562,12.2-.958,11.368,12.949,2.828,13.018-19.844-1.33-22.119-2.716S819.548,702.367,819.548,702.367Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_106" data-name="Path 106" d="M844.574,711.664c-8.54.069-19.844-1.33-22.119-2.716-1.733-1.056-2.423-4.843-2.654-6.59l-.253.01s.479,6.1,2.755,7.487,13.579,2.785,22.119,2.716c2.465-.02,3.317-.9,3.27-2.2C847.349,711.159,846.409,711.649,844.574,711.664Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_107" data-name="Path 107" d="M949.813,724.718s11.36-1.729,14.5-4.591,16.89-7.488,18.217-3.667,19.494,17.447,6.633,19.107-30.153,1.609-33.835-.065S949.813,724.718,949.813,724.718Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_108" data-name="Path 108" d="M989.228,734.173c-12.86,1.659-30.153,1.609-33.835-.065-2.8-1.275-4.535-6.858-5.2-9.45l-.379.061s1.833,9.109,5.516,10.783,20.975,1.725,33.835.065c3.712-.479,4.836-1.956,4.529-3.906C993.319,732.907,991.991,733.817,989.228,734.173Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_109" data-name="Path 109" d="M670.26,723.9s9.587-1.459,12.237-3.875,14.255-6.32,15.374-3.095,16.452,14.725,5.6,16.125-25.448,1.358-28.555-.055S670.26,723.9,670.26,723.9Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_110" data-name="Path 110" d="M703.524,731.875c-10.853,1.4-25.448,1.358-28.555-.055-2.367-1.076-3.827-5.788-4.39-7.976l-.32.051s1.547,7.687,4.655,9.1,17.7,1.456,28.555.055c3.133-.4,4.081-1.651,3.822-3.3C706.977,730.807,705.856,731.575,703.524,731.875Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_111" data-name="Path 111" d="M178.389,719.109s7.463-1.136,9.527-3.016,11.1-4.92,11.969-2.409,12.808,11.463,4.358,12.553-19.811,1.057-22.23-.043S178.389,719.109,178.389,719.109Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_112" data-name="Path 112" d="M204.285,725.321c-8.449,1.09-19.811,1.057-22.23-.043-1.842-.838-2.979-4.506-3.417-6.209l-.249.04s1.2,5.984,3.624,7.085,13.781,1.133,22.23.043c2.439-.315,3.177-1.285,2.976-2.566C206.973,724.489,206.1,725.087,204.285,725.321Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_113" data-name="Path 113" d="M439.7,707.337c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873,42.118-36.793,93.694-36.793S439.7,677.117,439.7,707.337Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<path id="Path_114" data-name="Path 114" d="M439.7,699.9c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873S295.04,663.1,346.616,663.1,439.7,669.676,439.7,699.9Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
</g>
<g id="docusaurus_keytar" transform="translate(312.271 493.733)">
<path id="Path_40" data-name="Path 40" d="M99,52h91.791V89.153H99Z" transform="translate(5.904 -14.001)" fill="#fff" fill-rule="evenodd"/>
<path id="Path_41" data-name="Path 41" d="M24.855,163.927A21.828,21.828,0,0,1,5.947,153a21.829,21.829,0,0,0,18.908,32.782H46.71V163.927Z" transform="translate(-3 -4.634)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_42" data-name="Path 42" d="M121.861,61.1l76.514-4.782V45.39A21.854,21.854,0,0,0,176.52,23.535H78.173L75.441,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L64.513,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L53.586,18.8a3.154,3.154,0,0,0-5.464,0L45.39,23.535c-.024,0-.046,0-.071,0l-4.526-4.525a3.153,3.153,0,0,0-5.276,1.414l-1.5,5.577-5.674-1.521a3.154,3.154,0,0,0-3.863,3.864L26,34.023l-5.575,1.494a3.155,3.155,0,0,0-1.416,5.278l4.526,4.526c0,.023,0,.046,0,.07L18.8,48.122a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,59.05a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,69.977a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,80.9a3.154,3.154,0,0,0,0,5.464L23.535,89.1,18.8,91.832a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,102.76a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,113.687a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,124.615a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,135.542a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,146.469a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,157.4a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,168.324a3.154,3.154,0,0,0,0,5.464l4.732,2.732A21.854,21.854,0,0,0,45.39,198.375H176.52a21.854,21.854,0,0,0,21.855-21.855V89.1l-76.514-4.782a11.632,11.632,0,0,1,0-23.219" transform="translate(-1.681 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_43" data-name="Path 43" d="M143,186.71h32.782V143H143Z" transform="translate(9.984 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_44" data-name="Path 44" d="M196.71,159.855a5.438,5.438,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(10.912 -6.025)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_45" data-name="Path 45" d="M153,124.855h32.782V103H153Z" transform="translate(10.912 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_46" data-name="Path 46" d="M194.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.814,2.814,0,0,0,.349.035" transform="translate(12.767 -9.377)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_47" data-name="Path 47" d="M65.087,56.891a2.732,2.732,0,0,1-2.732-2.732,8.2,8.2,0,0,0-16.391,0,2.732,2.732,0,0,1-5.464,0,13.659,13.659,0,0,1,27.319,0,2.732,2.732,0,0,1-2.732,2.732" transform="translate(0.478 -15.068)" fill-rule="evenodd"/>
<path id="Path_48" data-name="Path 48" d="M103,191.347h65.565a21.854,21.854,0,0,0,21.855-21.855V93H124.855A21.854,21.854,0,0,0,103,114.855Z" transform="translate(6.275 -10.199)" fill="#ffff50" fill-rule="evenodd"/>
<path id="Path_49" data-name="Path 49" d="M173.216,129.787H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0-54.434H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.652H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186M189.585,61.611c-.013,0-.024-.007-.037-.005-3.377.115-4.974,3.492-6.384,6.472-1.471,3.114-2.608,5.139-4.473,5.078-2.064-.074-3.244-2.406-4.494-4.874-1.436-2.835-3.075-6.049-6.516-5.929-3.329.114-4.932,3.053-6.346,5.646-1.5,2.762-2.529,4.442-4.5,4.364-2.106-.076-3.225-1.972-4.52-4.167-1.444-2.443-3.112-5.191-6.487-5.1-3.272.113-4.879,2.606-6.3,4.808-1.5,2.328-2.552,3.746-4.551,3.662-2.156-.076-3.27-1.65-4.558-3.472-1.447-2.047-3.077-4.363-6.442-4.251-3.2.109-4.807,2.153-6.224,3.954-1.346,1.709-2.4,3.062-4.621,2.977a1.093,1.093,0,0,0-.079,2.186c3.3.11,4.967-1.967,6.417-3.81,1.286-1.635,2.4-3.045,4.582-3.12,2.1-.09,3.091,1.218,4.584,3.327,1.417,2,3.026,4.277,6.263,4.394,3.391.114,5.022-2.42,6.467-4.663,1.292-2,2.406-3.734,4.535-3.807,1.959-.073,3.026,1.475,4.529,4.022,1.417,2.4,3.023,5.121,6.324,5.241,3.415.118,5.064-2.863,6.5-5.5,1.245-2.282,2.419-4.437,4.5-4.509,1.959-.046,2.981,1.743,4.492,4.732,1.412,2.79,3.013,5.95,6.365,6.071l.185,0c3.348,0,4.937-3.36,6.343-6.331,1.245-2.634,2.423-5.114,4.444-5.216Z" transform="translate(7.109 -13.11)" fill-rule="evenodd"/>
<path id="Path_50" data-name="Path 50" d="M83,186.71h43.71V143H83Z" transform="translate(4.42 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 109.327, 91.085)">
<rect id="Rectangle_3" data-name="Rectangle 3" width="92.361" height="36.462" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
<g id="Group_2" data-name="Group 2" transform="translate(1.531 23.03)">
<rect id="Rectangle_4" data-name="Rectangle 4" width="5.336" height="5.336" rx="1" transform="translate(16.797 0)" fill="#4a4a4a"/>
<rect id="Rectangle_5" data-name="Rectangle 5" width="5.336" height="5.336" rx="1" transform="translate(23.12 0)" fill="#4a4a4a"/>
<rect id="Rectangle_6" data-name="Rectangle 6" width="5.336" height="5.336" rx="1" transform="translate(29.444 0)" fill="#4a4a4a"/>
<rect id="Rectangle_7" data-name="Rectangle 7" width="5.336" height="5.336" rx="1" transform="translate(35.768 0)" fill="#4a4a4a"/>
<rect id="Rectangle_8" data-name="Rectangle 8" width="5.336" height="5.336" rx="1" transform="translate(42.091 0)" fill="#4a4a4a"/>
<rect id="Rectangle_9" data-name="Rectangle 9" width="5.336" height="5.336" rx="1" transform="translate(48.415 0)" fill="#4a4a4a"/>
<rect id="Rectangle_10" data-name="Rectangle 10" width="5.336" height="5.336" rx="1" transform="translate(54.739 0)" fill="#4a4a4a"/>
<rect id="Rectangle_11" data-name="Rectangle 11" width="5.336" height="5.336" rx="1" transform="translate(61.063 0)" fill="#4a4a4a"/>
<rect id="Rectangle_12" data-name="Rectangle 12" width="5.336" height="5.336" rx="1" transform="translate(67.386 0)" fill="#4a4a4a"/>
<path id="Path_51" data-name="Path 51" d="M1.093,0H14.518a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0ZM75,0H88.426a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H75a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,75,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_3" data-name="Group 3" transform="translate(1.531 10.261)">
<path id="Path_52" data-name="Path 52" d="M1.093,0H6.218A1.093,1.093,0,0,1,7.31,1.093V4.242A1.093,1.093,0,0,1,6.218,5.335H1.093A1.093,1.093,0,0,1,0,4.242V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_13" data-name="Rectangle 13" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
<rect id="Rectangle_14" data-name="Rectangle 14" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
<rect id="Rectangle_15" data-name="Rectangle 15" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
<rect id="Rectangle_16" data-name="Rectangle 16" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
<rect id="Rectangle_17" data-name="Rectangle 17" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
<rect id="Rectangle_18" data-name="Rectangle 18" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
<rect id="Rectangle_19" data-name="Rectangle 19" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
<rect id="Rectangle_20" data-name="Rectangle 20" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
<rect id="Rectangle_21" data-name="Rectangle 21" width="5.336" height="5.336" rx="1" transform="translate(58.888 0)" fill="#4a4a4a"/>
<rect id="Rectangle_22" data-name="Rectangle 22" width="5.336" height="5.336" rx="1" transform="translate(65.212 0)" fill="#4a4a4a"/>
<rect id="Rectangle_23" data-name="Rectangle 23" width="5.336" height="5.336" rx="1" transform="translate(71.536 0)" fill="#4a4a4a"/>
<rect id="Rectangle_24" data-name="Rectangle 24" width="5.336" height="5.336" rx="1" transform="translate(77.859 0)" fill="#4a4a4a"/>
<rect id="Rectangle_25" data-name="Rectangle 25" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
</g>
<g id="Group_4" data-name="Group 4" transform="translate(91.05 9.546) rotate(180)">
<path id="Path_53" data-name="Path 53" d="M1.093,0H6.219A1.093,1.093,0,0,1,7.312,1.093v3.15A1.093,1.093,0,0,1,6.219,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_26" data-name="Rectangle 26" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
<rect id="Rectangle_27" data-name="Rectangle 27" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
<rect id="Rectangle_28" data-name="Rectangle 28" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
<rect id="Rectangle_29" data-name="Rectangle 29" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
<rect id="Rectangle_30" data-name="Rectangle 30" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
<rect id="Rectangle_31" data-name="Rectangle 31" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
<rect id="Rectangle_32" data-name="Rectangle 32" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
<rect id="Rectangle_33" data-name="Rectangle 33" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
<rect id="Rectangle_34" data-name="Rectangle 34" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
<rect id="Rectangle_35" data-name="Rectangle 35" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
<rect id="Rectangle_36" data-name="Rectangle 36" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
<rect id="Rectangle_37" data-name="Rectangle 37" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
<rect id="Rectangle_38" data-name="Rectangle 38" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
<rect id="Rectangle_39" data-name="Rectangle 39" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
<rect id="Rectangle_40" data-name="Rectangle 40" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
<rect id="Rectangle_41" data-name="Rectangle 41" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
<rect id="Rectangle_42" data-name="Rectangle 42" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
<rect id="Rectangle_43" data-name="Rectangle 43" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
<rect id="Rectangle_44" data-name="Rectangle 44" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
<rect id="Rectangle_45" data-name="Rectangle 45" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
<rect id="Rectangle_46" data-name="Rectangle 46" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
<rect id="Rectangle_47" data-name="Rectangle 47" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
<rect id="Rectangle_48" data-name="Rectangle 48" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
<rect id="Rectangle_49" data-name="Rectangle 49" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
<rect id="Rectangle_50" data-name="Rectangle 50" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
<rect id="Rectangle_51" data-name="Rectangle 51" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
</g>
<g id="Group_6" data-name="Group 6" transform="translate(1.531 16.584)">
<path id="Path_54" data-name="Path 54" d="M1.093,0h7.3A1.093,1.093,0,0,1,9.485,1.093v3.15A1.093,1.093,0,0,1,8.392,5.336h-7.3A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<g id="Group_5" data-name="Group 5" transform="translate(10.671 0)">
<rect id="Rectangle_52" data-name="Rectangle 52" width="5.336" height="5.336" rx="1" fill="#4a4a4a"/>
<rect id="Rectangle_53" data-name="Rectangle 53" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
<rect id="Rectangle_54" data-name="Rectangle 54" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
<rect id="Rectangle_55" data-name="Rectangle 55" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
<rect id="Rectangle_56" data-name="Rectangle 56" width="5.336" height="5.336" rx="1" transform="translate(25.295 0)" fill="#4a4a4a"/>
<rect id="Rectangle_57" data-name="Rectangle 57" width="5.336" height="5.336" rx="1" transform="translate(31.619 0)" fill="#4a4a4a"/>
<rect id="Rectangle_58" data-name="Rectangle 58" width="5.336" height="5.336" rx="1" transform="translate(37.942 0)" fill="#4a4a4a"/>
<rect id="Rectangle_59" data-name="Rectangle 59" width="5.336" height="5.336" rx="1" transform="translate(44.265 0)" fill="#4a4a4a"/>
<rect id="Rectangle_60" data-name="Rectangle 60" width="5.336" height="5.336" rx="1" transform="translate(50.589 0)" fill="#4a4a4a"/>
<rect id="Rectangle_61" data-name="Rectangle 61" width="5.336" height="5.336" rx="1" transform="translate(56.912 0)" fill="#4a4a4a"/>
<rect id="Rectangle_62" data-name="Rectangle 62" width="5.336" height="5.336" rx="1" transform="translate(63.236 0)" fill="#4a4a4a"/>
</g>
<path id="Path_55" data-name="Path 55" d="M1.094,0H8A1.093,1.093,0,0,1,9.091,1.093v3.15A1.093,1.093,0,0,1,8,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(80.428 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_7" data-name="Group 7" transform="translate(1.531 29.627)">
<rect id="Rectangle_63" data-name="Rectangle 63" width="5.336" height="5.336" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
<rect id="Rectangle_64" data-name="Rectangle 64" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
<rect id="Rectangle_65" data-name="Rectangle 65" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
<rect id="Rectangle_66" data-name="Rectangle 66" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
<path id="Path_56" data-name="Path 56" d="M1.093,0H31.515a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.244V1.093A1.093,1.093,0,0,1,1.093,0ZM34.687,0h3.942a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H34.687a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,34.687,0Z" transform="translate(25.294 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_67" data-name="Rectangle 67" width="5.336" height="5.336" rx="1" transform="translate(66.003 0)" fill="#4a4a4a"/>
<rect id="Rectangle_68" data-name="Rectangle 68" width="5.336" height="5.336" rx="1" transform="translate(72.327 0)" fill="#4a4a4a"/>
<rect id="Rectangle_69" data-name="Rectangle 69" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
<path id="Path_57" data-name="Path 57" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(83.59 2.273) rotate(180)" fill="#4a4a4a"/>
<path id="Path_58" data-name="Path 58" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(78.255 3.063)" fill="#4a4a4a"/>
</g>
<rect id="Rectangle_70" data-name="Rectangle 70" width="88.927" height="2.371" rx="1.085" transform="translate(1.925 1.17)" fill="#4a4a4a"/>
<rect id="Rectangle_71" data-name="Rectangle 71" width="4.986" height="1.581" rx="0.723" transform="translate(4.1 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_72" data-name="Rectangle 72" width="4.986" height="1.581" rx="0.723" transform="translate(10.923 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_73" data-name="Rectangle 73" width="4.986" height="1.581" rx="0.723" transform="translate(16.173 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_74" data-name="Rectangle 74" width="4.986" height="1.581" rx="0.723" transform="translate(21.421 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_75" data-name="Rectangle 75" width="4.986" height="1.581" rx="0.723" transform="translate(26.671 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_76" data-name="Rectangle 76" width="4.986" height="1.581" rx="0.723" transform="translate(33.232 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_77" data-name="Rectangle 77" width="4.986" height="1.581" rx="0.723" transform="translate(38.48 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_78" data-name="Rectangle 78" width="4.986" height="1.581" rx="0.723" transform="translate(43.73 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_79" data-name="Rectangle 79" width="4.986" height="1.581" rx="0.723" transform="translate(48.978 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_80" data-name="Rectangle 80" width="4.986" height="1.581" rx="0.723" transform="translate(55.54 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_81" data-name="Rectangle 81" width="4.986" height="1.581" rx="0.723" transform="translate(60.788 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_82" data-name="Rectangle 82" width="4.986" height="1.581" rx="0.723" transform="translate(66.038 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_83" data-name="Rectangle 83" width="4.986" height="1.581" rx="0.723" transform="translate(72.599 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_84" data-name="Rectangle 84" width="4.986" height="1.581" rx="0.723" transform="translate(77.847 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_85" data-name="Rectangle 85" width="4.986" height="1.581" rx="0.723" transform="translate(83.097 1.566)" fill="#d8d8d8" opacity="0.136"/>
</g>
<path id="Path_59" data-name="Path 59" d="M146.71,159.855a5.439,5.439,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(6.275 -6.025)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_60" data-name="Path 60" d="M83,124.855h43.71V103H83Z" transform="translate(4.42 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_61" data-name="Path 61" d="M134.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.811,2.811,0,0,0,.349.035" transform="translate(7.202 -9.377)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_62" data-name="Path 62" d="M143.232,42.33a2.967,2.967,0,0,1-.535-.055,2.754,2.754,0,0,1-.514-.153,2.838,2.838,0,0,1-.471-.251,4.139,4.139,0,0,1-.415-.339,3.2,3.2,0,0,1-.338-.415A2.7,2.7,0,0,1,140.5,39.6a2.968,2.968,0,0,1,.055-.535,3.152,3.152,0,0,1,.152-.514,2.874,2.874,0,0,1,.252-.47,2.633,2.633,0,0,1,.753-.754,2.837,2.837,0,0,1,.471-.251,2.753,2.753,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,4.019,4.019,0,0,1,.339.415,2.786,2.786,0,0,1,.251.47,2.864,2.864,0,0,1,.208,1.049,2.77,2.77,0,0,1-.8,1.934,4.139,4.139,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459m21.855-1.366a2.789,2.789,0,0,1-1.935-.8,4.162,4.162,0,0,1-.338-.415,2.7,2.7,0,0,1-.459-1.519,2.789,2.789,0,0,1,.8-1.934,4.139,4.139,0,0,1,.415-.339,2.838,2.838,0,0,1,.471-.251,2.752,2.752,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,2.79,2.79,0,0,1,.8,1.934,3.069,3.069,0,0,1-.055.535,2.779,2.779,0,0,1-.153.514,3.885,3.885,0,0,1-.251.47,4.02,4.02,0,0,1-.339.415,4.138,4.138,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459" transform="translate(9.753 -15.532)" fill-rule="evenodd"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@ -1,169 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1041.277" height="554.141" viewBox="0 0 1041.277 554.141">
<g id="Group_24" data-name="Group 24" transform="translate(-440 -263)">
<g id="Group_23" data-name="Group 23" transform="translate(439.989 262.965)">
<path id="Path_299" data-name="Path 299" d="M1040.82,611.12q-1.74,3.75-3.47,7.4-2.7,5.67-5.33,11.12c-.78,1.61-1.56,3.19-2.32,4.77-8.6,17.57-16.63,33.11-23.45,45.89A73.21,73.21,0,0,1,942.44,719l-151.65,1.65h-1.6l-13,.14-11.12.12-34.1.37h-1.38l-17.36.19h-.53l-107,1.16-95.51,1-11.11.12-69,.75H429l-44.75.48h-.48l-141.5,1.53-42.33.46a87.991,87.991,0,0,1-10.79-.54h0c-1.22-.14-2.44-.3-3.65-.49a87.38,87.38,0,0,1-51.29-27.54C116,678.37,102.75,655,93.85,629.64q-1.93-5.49-3.6-11.12C59.44,514.37,97,380,164.6,290.08q4.25-5.64,8.64-11l.07-.08c20.79-25.52,44.1-46.84,68.93-62,44-26.91,92.75-34.49,140.7-11.9,40.57,19.12,78.45,28.11,115.17,30.55,3.71.24,7.42.42,11.11.53,84.23,2.65,163.17-27.7,255.87-47.29,3.69-.78,7.39-1.55,11.12-2.28,66.13-13.16,139.49-20.1,226.73-5.51a189.089,189.089,0,0,1,26.76,6.4q5.77,1.86,11.12,4c41.64,16.94,64.35,48.24,74,87.46q1.37,5.46,2.37,11.11C1134.3,384.41,1084.19,518.23,1040.82,611.12Z" transform="translate(-79.34 -172.91)" fill="#f2f2f2"/>
<path id="Path_300" data-name="Path 300" d="M576.36,618.52a95.21,95.21,0,0,1-1.87,11.12h93.7V618.52Zm-78.25,62.81,11.11-.09V653.77c-3.81-.17-7.52-.34-11.11-.52ZM265.19,618.52v11.12h198.5V618.52ZM1114.87,279h-74V191.51q-5.35-2.17-11.12-4V279H776.21V186.58c-3.73.73-7.43,1.5-11.12,2.28V279H509.22V236.15c-3.69-.11-7.4-.29-11.11-.53V279H242.24V217c-24.83,15.16-48.14,36.48-68.93,62h-.07v.08q-4.4,5.4-8.64,11h8.64V618.52h-83q1.66,5.63,3.6,11.12h79.39v93.62a87,87,0,0,0,12.2,2.79c1.21.19,2.43.35,3.65.49h0a87.991,87.991,0,0,0,10.79.54l42.33-.46v-97H498.11v94.21l11.11-.12V629.64H765.09V721l11.12-.12V629.64H1029.7v4.77c.76-1.58,1.54-3.16,2.32-4.77q2.63-5.45,5.33-11.12,1.73-3.64,3.47-7.4v-321h76.42Q1116.23,284.43,1114.87,279ZM242.24,618.52V290.08H498.11V618.52Zm267,0V290.08H765.09V618.52Zm520.48,0H776.21V290.08H1029.7Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_301" data-name="Path 301" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" fill="#65617d"/>
<path id="Path_302" data-name="Path 302" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" opacity="0.2"/>
<path id="Path_303" data-name="Path 303" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
<path id="Path_304" data-name="Path 304" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_305" data-name="Path 305" d="M377.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
<rect id="Rectangle_137" data-name="Rectangle 137" width="47.17" height="31.5" transform="translate(680.92 483.65)" fill="#3f3d56"/>
<rect id="Rectangle_138" data-name="Rectangle 138" width="47.17" height="31.5" transform="translate(680.92 483.65)" opacity="0.1"/>
<rect id="Rectangle_139" data-name="Rectangle 139" width="47.17" height="31.5" transform="translate(678.92 483.65)" fill="#3f3d56"/>
<path id="Path_306" data-name="Path 306" d="M298.09,483.65v4.97l-47.17,1.26v-6.23Z" opacity="0.1"/>
<path id="Path_307" data-name="Path 307" d="M460.69,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6a4,4,0,0,1,3.95,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
<path id="Path_308" data-name="Path 308" d="M265.19,481.32v181.2h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_309" data-name="Path 309" d="M194.59,319.15h177.5V467.4l-177.5,4Z" fill="#39374d"/>
<path id="Path_310" data-name="Path 310" d="M726.09,483.65v6.41l-47.17-1.26v-5.15Z" opacity="0.1"/>
<path id="Path_311" data-name="Path 311" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0L672,657.42a4,4,0,0,1-3.85-3.95V485.27a4,4,0,0,1,3.95-3.95H863.7a4,4,0,0,1,3.99,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
<path id="Path_312" data-name="Path 312" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0V481.32h0a4,4,0,0,1,4,3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_313" data-name="Path 313" d="M775.59,319.15H598.09V467.4l177.5,4Z" fill="#39374d"/>
<path id="Path_314" data-name="Path 314" d="M663.19,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h0a4,4,0,0,1-4-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6A4,4,0,0,1,663.19,485.27Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
<path id="Path_315" data-name="Path 315" d="M397.09,319.15h177.5V467.4l-177.5,4Z" fill="#4267b2"/>
<path id="Path_316" data-name="Path 316" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l202.51-1.33h.48l40.99-.28h.19l283.08-1.87h.29l.17-.01h.47l4.79-.03h1.46l74.49-.5,4.4-.02.98-.01Z" opacity="0.1"/>
<circle id="Ellipse_111" data-name="Ellipse 111" cx="51.33" cy="51.33" r="51.33" transform="translate(435.93 246.82)" fill="#fbbebe"/>
<path id="Path_317" data-name="Path 317" d="M617.94,550.07s-99.5,12-90,0c3.44-4.34,4.39-17.2,4.2-31.85-.06-4.45-.22-9.06-.45-13.65-1.1-22-3.75-43.5-3.75-43.5s87-41,77-8.5c-4,13.13-2.69,31.57.35,48.88.89,5.05,1.92,10,3,14.7a344.66,344.66,0,0,0,9.65,33.92Z" transform="translate(-79.34 -172.91)" fill="#fbbebe"/>
<path id="Path_318" data-name="Path 318" d="M585.47,546c11.51-2.13,23.7-6,34.53-1.54,2.85,1.17,5.47,2.88,8.39,3.86s6.12,1.22,9.16,1.91c10.68,2.42,19.34,10.55,24.9,20s8.44,20.14,11.26,30.72l6.9,25.83c6,22.45,12,45.09,13.39,68.3a2437.506,2437.506,0,0,1-250.84,1.43c5.44-10.34,11-21.31,10.54-33s-7.19-23.22-4.76-34.74c1.55-7.34,6.57-13.39,9.64-20.22,8.75-19.52,1.94-45.79,17.32-60.65,6.92-6.68,17-9.21,26.63-8.89,12.28.41,24.85,4.24,37,6.11C555.09,547.48,569.79,548.88,585.47,546Z" transform="translate(-79.34 -172.91)" fill="#ff6584"/>
<path id="Path_319" data-name="Path 319" d="M716.37,657.17l-.1,1.43v.1l-.17,2.3-1.33,18.51-1.61,22.3-.46,6.28-1,13.44v.17l-107,1-175.59,1.9v.84h-.14v-1.12l.45-14.36.86-28.06.74-23.79.07-2.37a10.53,10.53,0,0,1,11.42-10.17c4.72.4,10.85.89,18.18,1.41l3,.22c42.33,2.94,120.56,6.74,199.5,2,1.66-.09,3.33-.19,5-.31,12.24-.77,24.47-1.76,36.58-3a10.53,10.53,0,0,1,11.6,11.23Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_320" data-name="Path 320" d="M429.08,725.44v-.84l175.62-1.91,107-1h.3v-.17l1-13.44.43-6,1.64-22.61,1.29-17.9v-.44a10.617,10.617,0,0,0-.11-2.47.3.3,0,0,0,0-.1,10.391,10.391,0,0,0-2-4.64,10.54,10.54,0,0,0-9.42-4c-12.11,1.24-24.34,2.23-36.58,3-1.67.12-3.34.22-5,.31-78.94,4.69-157.17.89-199.5-2l-3-.22c-7.33-.52-13.46-1-18.18-1.41a10.54,10.54,0,0,0-11.24,8.53,11,11,0,0,0-.18,1.64l-.68,22.16L429.54,710l-.44,14.36v1.12Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
<path id="Path_321" data-name="Path 321" d="M716.67,664.18l-1.23,15.33-1.83,22.85-.46,5.72-1,12.81-.06.64v.17h0l-.15,1.48.11-1.48h-.29l-107,1-175.65,1.9v-.28l.49-14.36,1-28.06.64-18.65A6.36,6.36,0,0,1,434.3,658a6.25,6.25,0,0,1,3.78-.9c2.1.17,4.68.37,7.69.59,4.89.36,10.92.78,17.94,1.22,13,.82,29.31,1.7,48,2.42,52,2,122.2,2.67,188.88-3.17,3-.26,6.1-.55,9.13-.84a6.26,6.26,0,0,1,3.48.66,5.159,5.159,0,0,1,.86.54,6.14,6.14,0,0,1,2,2.46,3.564,3.564,0,0,1,.25.61A6.279,6.279,0,0,1,716.67,664.18Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_322" data-name="Path 322" d="M377.44,677.87v3.19a6.13,6.13,0,0,1-3.5,5.54l-40.1.77a6.12,6.12,0,0,1-3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_323" data-name="Path 323" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
<path id="Path_324" data-name="Path 324" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" opacity="0.1"/>
<path id="Path_325" data-name="Path 325" d="M300.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
<path id="Path_326" data-name="Path 326" d="M758.56,679.87v3.19a6.13,6.13,0,0,0,3.5,5.54l40.1.77a6.12,6.12,0,0,0,3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_327" data-name="Path 327" d="M678.72,517.57l52.25,1V509.9l-52.25-1Z" opacity="0.1"/>
<path id="Path_328" data-name="Path 328" d="M676.72,517.57l52.25,1V509.9l-52.25-1Z" fill="#3f3d56"/>
<path id="Path_329" data-name="Path 329" d="M534.13,486.79c.08,7-3.16,13.6-5.91,20.07a163.491,163.491,0,0,0-12.66,74.71c.73,11,2.58,22,.73,32.9s-8.43,21.77-19,24.9c17.53,10.45,41.26,9.35,57.76-2.66,8.79-6.4,15.34-15.33,21.75-24.11a97.86,97.86,0,0,1-13.31,44.75A103.43,103.43,0,0,0,637,616.53c4.31-5.81,8.06-12.19,9.72-19.23,3.09-13-1.22-26.51-4.51-39.5a266.055,266.055,0,0,1-6.17-33c-.43-3.56-.78-7.22.1-10.7,1-4.07,3.67-7.51,5.64-11.22,5.6-10.54,5.73-23.3,2.86-34.88s-8.49-22.26-14.06-32.81c-4.46-8.46-9.3-17.31-17.46-22.28-5.1-3.1-11-4.39-16.88-5.64l-25.37-5.43c-5.55-1.19-11.26-2.38-16.87-1.51-9.47,1.48-16.14,8.32-22,15.34-4.59,5.46-15.81,15.71-16.6,22.86-.72,6.59,5.1,17.63,6.09,24.58,1.3,9,2.22,6,7.3,11.52C532,478.05,534.07,482,534.13,486.79Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
</g>
<g id="docusaurus_keytar" transform="translate(670.271 615.768)">
<path id="Path_40" data-name="Path 40" d="M99,52h43.635V69.662H99Z" transform="translate(-49.132 -33.936)" fill="#fff" fill-rule="evenodd"/>
<path id="Path_41" data-name="Path 41" d="M13.389,158.195A10.377,10.377,0,0,1,4.4,153a10.377,10.377,0,0,0,8.988,15.584H23.779V158.195Z" transform="translate(-3 -82.47)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_42" data-name="Path 42" d="M66.967,38.083l36.373-2.273V30.615A10.389,10.389,0,0,0,92.95,20.226H46.2l-1.3-2.249a1.5,1.5,0,0,0-2.6,0L41,20.226l-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-.034,0-2.152-2.151a1.5,1.5,0,0,0-2.508.672L25.21,21.4l-2.7-.723a1.5,1.5,0,0,0-1.836,1.837l.722,2.7-2.65.71a1.5,1.5,0,0,0-.673,2.509l2.152,2.152c0,.011,0,.022,0,.033l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6L20.226,41l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3A10.389,10.389,0,0,0,30.615,103.34H92.95A10.389,10.389,0,0,0,103.34,92.95V51.393L66.967,49.12a5.53,5.53,0,0,1,0-11.038" transform="translate(-9.836 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_43" data-name="Path 43" d="M143,163.779h15.584V143H143Z" transform="translate(-70.275 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_44" data-name="Path 44" d="M173.779,148.389a2.582,2.582,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-75.08 -75.262)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_45" data-name="Path 45" d="M153,113.389h15.584V103H153Z" transform="translate(-75.08 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_46" data-name="Path 46" d="M183.389,108.944a1.3,1.3,0,1,0,0-2.6,1.336,1.336,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.337,1.337,0,0,0,.166.017" transform="translate(-84.691 -57.894)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_47" data-name="Path 47" d="M52.188,48.292a1.3,1.3,0,0,1-1.3-1.3,3.9,3.9,0,0,0-7.792,0,1.3,1.3,0,1,1-2.6,0,6.493,6.493,0,0,1,12.987,0,1.3,1.3,0,0,1-1.3,1.3" transform="translate(-21.02 -28.41)" fill-rule="evenodd"/>
<path id="Path_48" data-name="Path 48" d="M103,139.752h31.168a10.389,10.389,0,0,0,10.389-10.389V93H113.389A10.389,10.389,0,0,0,103,103.389Z" transform="translate(-51.054 -53.638)" fill="#ffff50" fill-rule="evenodd"/>
<path id="Path_49" data-name="Path 49" d="M141.1,94.017H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0-25.877H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.293H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m7.782-47.993c-.006,0-.011,0-.018,0-1.605.055-2.365,1.66-3.035,3.077-.7,1.48-1.24,2.443-2.126,2.414-.981-.035-1.542-1.144-2.137-2.317-.683-1.347-1.462-2.876-3.1-2.819-1.582.054-2.344,1.451-3.017,2.684-.715,1.313-1.2,2.112-2.141,2.075-1-.036-1.533-.938-2.149-1.981-.686-1.162-1.479-2.467-3.084-2.423-1.555.053-2.319,1.239-2.994,2.286-.713,1.106-1.213,1.781-2.164,1.741-1.025-.036-1.554-.784-2.167-1.65-.688-.973-1.463-2.074-3.062-2.021a3.815,3.815,0,0,0-2.959,1.879c-.64.812-1.14,1.456-2.2,1.415a.52.52,0,0,0-.037,1.039,3.588,3.588,0,0,0,3.05-1.811c.611-.777,1.139-1.448,2.178-1.483,1-.043,1.47.579,2.179,1.582.674.953,1.438,2.033,2.977,2.089,1.612.054,2.387-1.151,3.074-2.217.614-.953,1.144-1.775,2.156-1.81.931-.035,1.438.7,2.153,1.912.674,1.141,1.437,2.434,3.006,2.491,1.623.056,2.407-1.361,3.09-2.616.592-1.085,1.15-2.109,2.14-2.143.931-.022,1.417.829,2.135,2.249.671,1.326,1.432,2.828,3.026,2.886l.088,0c1.592,0,2.347-1.6,3.015-3.01.592-1.252,1.152-2.431,2.113-2.479Z" transform="translate(-55.378 -38.552)" fill-rule="evenodd"/>
<path id="Path_50" data-name="Path 50" d="M83,163.779h20.779V143H83Z" transform="translate(-41.443 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 51.971, 43.3)">
<rect id="Rectangle_3" data-name="Rectangle 3" width="43.906" height="17.333" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
<g id="Group_2" data-name="Group 2" transform="translate(0.728 10.948)">
<rect id="Rectangle_4" data-name="Rectangle 4" width="2.537" height="2.537" rx="1" transform="translate(7.985 0)" fill="#4a4a4a"/>
<rect id="Rectangle_5" data-name="Rectangle 5" width="2.537" height="2.537" rx="1" transform="translate(10.991 0)" fill="#4a4a4a"/>
<rect id="Rectangle_6" data-name="Rectangle 6" width="2.537" height="2.537" rx="1" transform="translate(13.997 0)" fill="#4a4a4a"/>
<rect id="Rectangle_7" data-name="Rectangle 7" width="2.537" height="2.537" rx="1" transform="translate(17.003 0)" fill="#4a4a4a"/>
<rect id="Rectangle_8" data-name="Rectangle 8" width="2.537" height="2.537" rx="1" transform="translate(20.009 0)" fill="#4a4a4a"/>
<rect id="Rectangle_9" data-name="Rectangle 9" width="2.537" height="2.537" rx="1" transform="translate(23.015 0)" fill="#4a4a4a"/>
<rect id="Rectangle_10" data-name="Rectangle 10" width="2.537" height="2.537" rx="1" transform="translate(26.021 0)" fill="#4a4a4a"/>
<rect id="Rectangle_11" data-name="Rectangle 11" width="2.537" height="2.537" rx="1" transform="translate(29.028 0)" fill="#4a4a4a"/>
<rect id="Rectangle_12" data-name="Rectangle 12" width="2.537" height="2.537" rx="1" transform="translate(32.034 0)" fill="#4a4a4a"/>
<path id="Path_51" data-name="Path 51" d="M.519,0H6.9A.519.519,0,0,1,7.421.52v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0ZM35.653,0h6.383a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H35.652a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,35.652,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_3" data-name="Group 3" transform="translate(0.728 4.878)">
<path id="Path_52" data-name="Path 52" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_13" data-name="Rectangle 13" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
<rect id="Rectangle_14" data-name="Rectangle 14" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
<rect id="Rectangle_15" data-name="Rectangle 15" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
<rect id="Rectangle_16" data-name="Rectangle 16" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
<rect id="Rectangle_17" data-name="Rectangle 17" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
<rect id="Rectangle_18" data-name="Rectangle 18" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
<rect id="Rectangle_19" data-name="Rectangle 19" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
<rect id="Rectangle_20" data-name="Rectangle 20" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
<rect id="Rectangle_21" data-name="Rectangle 21" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
<rect id="Rectangle_22" data-name="Rectangle 22" width="2.537" height="2.537" rx="1" transform="translate(31 0)" fill="#4a4a4a"/>
<rect id="Rectangle_23" data-name="Rectangle 23" width="2.537" height="2.537" rx="1" transform="translate(34.006 0)" fill="#4a4a4a"/>
<rect id="Rectangle_24" data-name="Rectangle 24" width="2.537" height="2.537" rx="1" transform="translate(37.012 0)" fill="#4a4a4a"/>
<rect id="Rectangle_25" data-name="Rectangle 25" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
</g>
<g id="Group_4" data-name="Group 4" transform="translate(43.283 4.538) rotate(180)">
<path id="Path_53" data-name="Path 53" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_26" data-name="Rectangle 26" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
<rect id="Rectangle_27" data-name="Rectangle 27" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
<rect id="Rectangle_28" data-name="Rectangle 28" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
<rect id="Rectangle_29" data-name="Rectangle 29" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
<rect id="Rectangle_30" data-name="Rectangle 30" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
<rect id="Rectangle_31" data-name="Rectangle 31" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
<rect id="Rectangle_32" data-name="Rectangle 32" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
<rect id="Rectangle_33" data-name="Rectangle 33" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
<rect id="Rectangle_34" data-name="Rectangle 34" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
<rect id="Rectangle_35" data-name="Rectangle 35" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
<rect id="Rectangle_36" data-name="Rectangle 36" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
<rect id="Rectangle_37" data-name="Rectangle 37" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
<rect id="Rectangle_38" data-name="Rectangle 38" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
<rect id="Rectangle_39" data-name="Rectangle 39" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
<rect id="Rectangle_40" data-name="Rectangle 40" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
<rect id="Rectangle_41" data-name="Rectangle 41" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
<rect id="Rectangle_42" data-name="Rectangle 42" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
<rect id="Rectangle_43" data-name="Rectangle 43" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
<rect id="Rectangle_44" data-name="Rectangle 44" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
<rect id="Rectangle_45" data-name="Rectangle 45" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
<rect id="Rectangle_46" data-name="Rectangle 46" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
<rect id="Rectangle_47" data-name="Rectangle 47" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
<rect id="Rectangle_48" data-name="Rectangle 48" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
<rect id="Rectangle_49" data-name="Rectangle 49" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
<rect id="Rectangle_50" data-name="Rectangle 50" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
<rect id="Rectangle_51" data-name="Rectangle 51" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
</g>
<g id="Group_6" data-name="Group 6" transform="translate(0.728 7.883)">
<path id="Path_54" data-name="Path 54" d="M.519,0h3.47a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<g id="Group_5" data-name="Group 5" transform="translate(5.073 0)">
<rect id="Rectangle_52" data-name="Rectangle 52" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
<rect id="Rectangle_53" data-name="Rectangle 53" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
<rect id="Rectangle_54" data-name="Rectangle 54" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
<rect id="Rectangle_55" data-name="Rectangle 55" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
<rect id="Rectangle_56" data-name="Rectangle 56" width="2.537" height="2.537" rx="1" transform="translate(12.025 0)" fill="#4a4a4a"/>
<rect id="Rectangle_57" data-name="Rectangle 57" width="2.537" height="2.537" rx="1" transform="translate(15.031 0)" fill="#4a4a4a"/>
<rect id="Rectangle_58" data-name="Rectangle 58" width="2.537" height="2.537" rx="1" transform="translate(18.037 0)" fill="#4a4a4a"/>
<rect id="Rectangle_59" data-name="Rectangle 59" width="2.537" height="2.537" rx="1" transform="translate(21.042 0)" fill="#4a4a4a"/>
<rect id="Rectangle_60" data-name="Rectangle 60" width="2.537" height="2.537" rx="1" transform="translate(24.049 0)" fill="#4a4a4a"/>
<rect id="Rectangle_61" data-name="Rectangle 61" width="2.537" height="2.537" rx="1" transform="translate(27.055 0)" fill="#4a4a4a"/>
<rect id="Rectangle_62" data-name="Rectangle 62" width="2.537" height="2.537" rx="1" transform="translate(30.061 0)" fill="#4a4a4a"/>
</g>
<path id="Path_55" data-name="Path 55" d="M.52,0H3.8a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(38.234 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_7" data-name="Group 7" transform="translate(0.728 14.084)">
<rect id="Rectangle_63" data-name="Rectangle 63" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
<rect id="Rectangle_64" data-name="Rectangle 64" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
<rect id="Rectangle_65" data-name="Rectangle 65" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
<rect id="Rectangle_66" data-name="Rectangle 66" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
<path id="Path_56" data-name="Path 56" d="M.519,0H14.981A.519.519,0,0,1,15.5.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.018V.519A.519.519,0,0,1,.519,0Zm15.97,0h1.874a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H16.489a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,16.489,0Z" transform="translate(12.024 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_67" data-name="Rectangle 67" width="2.537" height="2.537" rx="1" transform="translate(31.376 0)" fill="#4a4a4a"/>
<rect id="Rectangle_68" data-name="Rectangle 68" width="2.537" height="2.537" rx="1" transform="translate(34.382 0)" fill="#4a4a4a"/>
<rect id="Rectangle_69" data-name="Rectangle 69" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
<path id="Path_57" data-name="Path 57" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(39.736 1.08) rotate(180)" fill="#4a4a4a"/>
<path id="Path_58" data-name="Path 58" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(37.2 1.456)" fill="#4a4a4a"/>
</g>
<rect id="Rectangle_70" data-name="Rectangle 70" width="42.273" height="1.127" rx="0.564" transform="translate(0.915 0.556)" fill="#4a4a4a"/>
<rect id="Rectangle_71" data-name="Rectangle 71" width="2.37" height="0.752" rx="0.376" transform="translate(1.949 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_72" data-name="Rectangle 72" width="2.37" height="0.752" rx="0.376" transform="translate(5.193 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_73" data-name="Rectangle 73" width="2.37" height="0.752" rx="0.376" transform="translate(7.688 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_74" data-name="Rectangle 74" width="2.37" height="0.752" rx="0.376" transform="translate(10.183 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_75" data-name="Rectangle 75" width="2.37" height="0.752" rx="0.376" transform="translate(12.679 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_76" data-name="Rectangle 76" width="2.37" height="0.752" rx="0.376" transform="translate(15.797 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_77" data-name="Rectangle 77" width="2.37" height="0.752" rx="0.376" transform="translate(18.292 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_78" data-name="Rectangle 78" width="2.37" height="0.752" rx="0.376" transform="translate(20.788 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_79" data-name="Rectangle 79" width="2.37" height="0.752" rx="0.376" transform="translate(23.283 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_80" data-name="Rectangle 80" width="2.37" height="0.752" rx="0.376" transform="translate(26.402 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_81" data-name="Rectangle 81" width="2.37" height="0.752" rx="0.376" transform="translate(28.897 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_82" data-name="Rectangle 82" width="2.37" height="0.752" rx="0.376" transform="translate(31.393 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_83" data-name="Rectangle 83" width="2.37" height="0.752" rx="0.376" transform="translate(34.512 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_84" data-name="Rectangle 84" width="2.37" height="0.752" rx="0.376" transform="translate(37.007 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_85" data-name="Rectangle 85" width="2.37" height="0.752" rx="0.376" transform="translate(39.502 0.744)" fill="#d8d8d8" opacity="0.136"/>
</g>
<path id="Path_59" data-name="Path 59" d="M123.779,148.389a2.583,2.583,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-51.054 -75.262)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_60" data-name="Path 60" d="M83,113.389h20.779V103H83Z" transform="translate(-41.443 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_61" data-name="Path 61" d="M123.389,108.944a1.3,1.3,0,1,0,0-2.6,1.338,1.338,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.335,1.335,0,0,0,.166.017" transform="translate(-55.859 -57.894)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_62" data-name="Path 62" d="M141.8,38.745a1.41,1.41,0,0,1-.255-.026,1.309,1.309,0,0,1-.244-.073,1.349,1.349,0,0,1-.224-.119,1.967,1.967,0,0,1-.2-.161,1.52,1.52,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.41,1.41,0,0,1,.026-.255,1.5,1.5,0,0,1,.072-.244,1.364,1.364,0,0,1,.12-.223,1.252,1.252,0,0,1,.358-.358,1.349,1.349,0,0,1,.224-.119,1.309,1.309,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.968,1.968,0,0,1,.2.161,1.908,1.908,0,0,1,.161.2,1.322,1.322,0,0,1,.12.223,1.361,1.361,0,0,1,.1.5,1.317,1.317,0,0,1-.379.919,1.968,1.968,0,0,1-.2.161,1.346,1.346,0,0,1-.223.119,1.332,1.332,0,0,1-.5.1m10.389-.649a1.326,1.326,0,0,1-.92-.379,1.979,1.979,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.326,1.326,0,0,1,.379-.919,1.967,1.967,0,0,1,.2-.161,1.351,1.351,0,0,1,.224-.119,1.308,1.308,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.967,1.967,0,0,1,.2.161,1.326,1.326,0,0,1,.379.919,1.461,1.461,0,0,1-.026.255,1.323,1.323,0,0,1-.073.244,1.847,1.847,0,0,1-.119.223,1.911,1.911,0,0,1-.161.2,1.967,1.967,0,0,1-.2.161,1.294,1.294,0,0,1-.722.218" transform="translate(-69.074 -26.006)" fill-rule="evenodd"/>
</g>
<g id="React-icon" transform="translate(906.3 541.56)">
<path id="Path_330" data-name="Path 330" d="M263.668,117.179c0-5.827-7.3-11.35-18.487-14.775,2.582-11.4,1.434-20.477-3.622-23.382a7.861,7.861,0,0,0-4.016-1v4a4.152,4.152,0,0,1,2.044.466c2.439,1.4,3.5,6.724,2.672,13.574-.2,1.685-.52,3.461-.914,5.272a86.9,86.9,0,0,0-11.386-1.954,87.469,87.469,0,0,0-7.459-8.965c5.845-5.433,11.332-8.41,15.062-8.41V78h0c-4.931,0-11.386,3.514-17.913,9.611-6.527-6.061-12.982-9.539-17.913-9.539v4c3.712,0,9.216,2.959,15.062,8.356a84.687,84.687,0,0,0-7.405,8.947,83.732,83.732,0,0,0-11.4,1.972c-.412-1.793-.717-3.532-.932-5.2-.843-6.85.2-12.175,2.618-13.592a3.991,3.991,0,0,1,2.062-.466v-4h0a8,8,0,0,0-4.052,1c-5.039,2.9-6.168,11.96-3.568,23.328-11.153,3.443-18.415,8.947-18.415,14.757,0,5.828,7.3,11.35,18.487,14.775-2.582,11.4-1.434,20.477,3.622,23.382a7.882,7.882,0,0,0,4.034,1c4.931,0,11.386-3.514,17.913-9.611,6.527,6.061,12.982,9.539,17.913,9.539a8,8,0,0,0,4.052-1c5.039-2.9,6.168-11.96,3.568-23.328C256.406,128.511,263.668,122.988,263.668,117.179Zm-23.346-11.96c-.663,2.313-1.488,4.7-2.421,7.083-.735-1.434-1.506-2.869-2.349-4.3-.825-1.434-1.7-2.833-2.582-4.2C235.517,104.179,237.974,104.645,240.323,105.219Zm-8.212,19.1c-1.4,2.421-2.833,4.716-4.321,6.85-2.672.233-5.379.359-8.1.359-2.708,0-5.415-.126-8.069-.341q-2.232-3.2-4.339-6.814-2.044-3.523-3.73-7.136c1.112-2.4,2.367-4.805,3.712-7.154,1.4-2.421,2.833-4.716,4.321-6.85,2.672-.233,5.379-.359,8.1-.359,2.708,0,5.415.126,8.069.341q2.232,3.2,4.339,6.814,2.044,3.523,3.73,7.136C234.692,119.564,233.455,121.966,232.11,124.315Zm5.792-2.331c.968,2.4,1.793,4.805,2.474,7.136-2.349.574-4.823,1.058-7.387,1.434.879-1.381,1.757-2.8,2.582-4.25C236.4,124.871,237.167,123.419,237.9,121.984ZM219.72,141.116a73.921,73.921,0,0,1-4.985-5.738c1.614.072,3.263.126,4.931.126,1.685,0,3.353-.036,4.985-.126A69.993,69.993,0,0,1,219.72,141.116ZM206.38,130.555c-2.546-.377-5-.843-7.352-1.417.663-2.313,1.488-4.7,2.421-7.083.735,1.434,1.506,2.869,2.349,4.3S205.5,129.192,206.38,130.555ZM219.63,93.241a73.924,73.924,0,0,1,4.985,5.738c-1.614-.072-3.263-.126-4.931-.126-1.686,0-3.353.036-4.985.126A69.993,69.993,0,0,1,219.63,93.241ZM206.362,103.8c-.879,1.381-1.757,2.8-2.582,4.25-.825,1.434-1.6,2.869-2.331,4.3-.968-2.4-1.793-4.805-2.474-7.136C201.323,104.663,203.8,104.179,206.362,103.8Zm-16.227,22.449c-6.348-2.708-10.454-6.258-10.454-9.073s4.106-6.383,10.454-9.073c1.542-.663,3.228-1.255,4.967-1.811a86.122,86.122,0,0,0,4.034,10.92,84.9,84.9,0,0,0-3.981,10.866C193.38,127.525,191.694,126.915,190.134,126.252Zm9.647,25.623c-2.439-1.4-3.5-6.724-2.672-13.574.2-1.686.52-3.461.914-5.272a86.9,86.9,0,0,0,11.386,1.954,87.465,87.465,0,0,0,7.459,8.965c-5.845,5.433-11.332,8.41-15.062,8.41A4.279,4.279,0,0,1,199.781,151.875Zm42.532-13.663c.843,6.85-.2,12.175-2.618,13.592a3.99,3.99,0,0,1-2.062.466c-3.712,0-9.216-2.959-15.062-8.356a84.689,84.689,0,0,0,7.405-8.947,83.731,83.731,0,0,0,11.4-1.972A50.194,50.194,0,0,1,242.313,138.212Zm6.9-11.96c-1.542.663-3.228,1.255-4.967,1.811a86.12,86.12,0,0,0-4.034-10.92,84.9,84.9,0,0,0,3.981-10.866c1.775.556,3.461,1.165,5.039,1.829,6.348,2.708,10.454,6.258,10.454,9.073C259.67,119.994,255.564,123.562,249.216,126.252Z" fill="#61dafb"/>
<path id="Path_331" data-name="Path 331" d="M320.8,78.4Z" transform="translate(-119.082 -0.328)" fill="#61dafb"/>
<circle id="Ellipse_112" data-name="Ellipse 112" cx="8.194" cy="8.194" r="8.194" transform="translate(211.472 108.984)" fill="#61dafb"/>
<path id="Path_332" data-name="Path 332" d="M520.5,78.1Z" transform="translate(-282.975 -0.082)" fill="#61dafb"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 35 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

961
yarn.lock

File diff suppressed because it is too large Load Diff