@explorable-viz/fluid 0.12.1 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fluid/shared/fluid.mjs +478 -438
- package/dist/fluid/shared/load-figure.js +3199 -2975
- package/package.json +12 -26
- package/script/install-website.sh +27 -9
- package/web/lib/CodeMirror.svelte +19 -0
- package/web/lib/DataPane.svelte +46 -0
- package/web/lib/Figure.svelte +38 -0
- package/web/lib/FigureSrc.svelte +35 -0
- package/web/lib/Grid.svelte +32 -0
- package/web/lib/index.js +5 -0
- package/web/lib/view-styles.css +262 -0
- package/website/article/eslint.config.js +45 -0
- package/website/article/package.json +46 -0
- package/website/article/src/app.d.ts +13 -0
- package/website/article/src/app.html +12 -0
- package/website/article/src/lib/assets/css/styles.css +245 -0
- package/website/article/src/lib/assets/image/bristol.png +0 -0
- package/website/article/src/lib/assets/image/fluid.png +0 -0
- package/website/article/src/lib/assets/image/iccs.png +0 -0
- package/website/article/src/lib/assets/image/schmidt.png +0 -0
- package/website/article/src/lib/assets/image/turing.jpg +0 -0
- package/website/article/src/lib/config/nav.ts +7 -0
- package/website/article/src/lib/config/supporters.ts +27 -0
- package/website/article/src/routes/+layout.svelte +15 -0
- package/website/article/src/routes/+layout.ts +1 -0
- package/website/article/{index.html → src/routes/+page.svelte} +8 -17
- package/website/article/src/routes/convolution/+page.svelte +62 -0
- package/website/article/src/routes/energy-scatter/+page.svelte +34 -0
- package/website/article/src/routes/methane/+page.svelte +35 -0
- package/website/article/src/routes/moving-average/+page.svelte +57 -0
- package/website/article/src/routes/non-renewables/+page.svelte +40 -0
- package/website/article/src/routes/renewables-linked/+page.svelte +34 -0
- package/website/article/src/routes/scigen-1805.02474v1-10/+page.svelte +40 -0
- package/website/article/static/fluid/lib/graphics.fld +274 -0
- package/website/article/static/fluid/lib/matrix.fld +40 -0
- package/website/article/static/fluid/lib/prelude.fld +348 -0
- package/website/article/static/fluid/lib/stats.fld +83 -0
- package/website/article/svelte.config.js +13 -0
- package/website/article/test.mjs +66 -11
- package/website/article/tsconfig.json +20 -0
- package/website/article/vite.config.ts +6 -0
- package/.spago/aff/v7.1.0/.editorconfig +0 -13
- package/.spago/aff/v7.1.0/.eslintrc.json +0 -28
- package/.spago/aff/v7.1.0/.gitignore +0 -14
- package/.spago/aff/v7.1.0/.tidyrc.json +0 -10
- package/.spago/affjax/v13.0.0/.editorconfig +0 -13
- package/.spago/affjax/v13.0.0/.eslintrc.json +0 -28
- package/.spago/affjax/v13.0.0/.gitignore +0 -14
- package/.spago/affjax/v13.0.0/.tidyrc.json +0 -10
- package/.spago/affjax-web/v1.0.0/.editorconfig +0 -13
- package/.spago/affjax-web/v1.0.0/.eslintrc.json +0 -30
- package/.spago/affjax-web/v1.0.0/.gitignore +0 -14
- package/.spago/affjax-web/v1.0.0/.tidyrc.json +0 -10
- package/.spago/argonaut-codecs/v9.1.0/.editorconfig +0 -13
- package/.spago/argonaut-codecs/v9.1.0/.gitignore +0 -9
- package/.spago/argonaut-codecs/v9.1.0/.tidyrc.json +0 -10
- package/.spago/argonaut-core/v7.0.0/.editorconfig +0 -13
- package/.spago/argonaut-core/v7.0.0/.eslintrc.json +0 -29
- package/.spago/argonaut-core/v7.0.0/.gitignore +0 -14
- package/.spago/argonaut-core/v7.0.0/.tidyrc.json +0 -10
- package/.spago/arraybuffer-types/v3.0.2/.editorconfig +0 -13
- package/.spago/arraybuffer-types/v3.0.2/.gitignore +0 -9
- package/.spago/arraybuffer-types/v3.0.2/.tidyrc.json +0 -10
- package/.spago/arrays/v7.2.1/.eslintrc.json +0 -26
- package/.spago/arrays/v7.2.1/.gitignore +0 -9
- package/.spago/assert/v6.0.0/.eslintrc.json +0 -26
- package/.spago/assert/v6.0.0/.gitignore +0 -8
- package/.spago/bifunctors/v6.0.0/.gitignore +0 -7
- package/.spago/catenable-lists/v7.0.0/.gitignore +0 -7
- package/.spago/console/v6.1.0/.eslintrc.json +0 -30
- package/.spago/console/v6.1.0/.gitignore +0 -9
- package/.spago/const/v6.0.0/.gitignore +0 -7
- package/.spago/contravariant/v6.0.0/.gitignore +0 -7
- package/.spago/control/v6.0.0/.gitignore +0 -7
- package/.spago/datetime/v6.1.0/.eslintrc.json +0 -26
- package/.spago/datetime/v6.1.0/.gitignore +0 -8
- package/.spago/debug/v6.0.2/.eslintrc.json +0 -36
- package/.spago/debug/v6.0.2/.gitignore +0 -8
- package/.spago/distributive/v6.0.0/.gitignore +0 -7
- package/.spago/effect/v4.0.0/.eslintrc.json +0 -26
- package/.spago/effect/v4.0.0/.gitignore +0 -8
- package/.spago/either/v6.1.0/.gitignore +0 -7
- package/.spago/enums/v6.0.1/.gitignore +0 -7
- package/.spago/exceptions/v6.0.0/.eslintrc.json +0 -26
- package/.spago/exceptions/v6.0.0/.gitignore +0 -8
- package/.spago/exists/v6.0.0/.gitignore +0 -7
- package/.spago/filterable/v5.0.0/.gitignore +0 -11
- package/.spago/foldable-traversable/v6.0.0/.eslintrc.json +0 -26
- package/.spago/foldable-traversable/v6.0.0/.gitignore +0 -8
- package/.spago/foreign/v7.0.0/.eslintrc.json +0 -26
- package/.spago/foreign/v7.0.0/.gitignore +0 -8
- package/.spago/foreign-object/v4.1.0/.eslintrc.json +0 -26
- package/.spago/foreign-object/v4.1.0/.gitignore +0 -8
- package/.spago/form-urlencoded/v7.0.0/.editorconfig +0 -13
- package/.spago/form-urlencoded/v7.0.0/.gitignore +0 -9
- package/.spago/form-urlencoded/v7.0.0/.tidyrc.json +0 -10
- package/.spago/free/v7.1.0/.gitignore +0 -8
- package/.spago/functions/v6.0.0/.eslintrc.json +0 -26
- package/.spago/functions/v6.0.0/.gitignore +0 -8
- package/.spago/functors/v5.0.0/.gitignore +0 -7
- package/.spago/gen/v4.0.0/.gitignore +0 -8
- package/.spago/graphs/v8.1.0/.gitignore +0 -7
- package/.spago/http-methods/v6.0.0/.editorconfig +0 -13
- package/.spago/http-methods/v6.0.0/.gitignore +0 -9
- package/.spago/http-methods/v6.0.0/.tidyrc.json +0 -10
- package/.spago/identity/v6.0.0/.gitignore +0 -7
- package/.spago/integers/v6.0.0/.eslintrc.json +0 -26
- package/.spago/integers/v6.0.0/.gitignore +0 -8
- package/.spago/invariant/v6.0.0/.gitignore +0 -7
- package/.spago/js-date/v8.0.0/.editorconfig +0 -13
- package/.spago/js-date/v8.0.0/.eslintrc.json +0 -29
- package/.spago/js-date/v8.0.0/.gitignore +0 -14
- package/.spago/js-date/v8.0.0/.tidyrc.json +0 -10
- package/.spago/js-uri/v3.1.0/.eslintrc.json +0 -30
- package/.spago/js-uri/v3.1.0/.gitignore +0 -14
- package/.spago/js-uri/v3.1.0/.tidyrc.json +0 -10
- package/.spago/lazy/v6.0.0/.eslintrc.json +0 -26
- package/.spago/lazy/v6.0.0/.gitignore +0 -8
- package/.spago/lcg/v4.0.0/.gitignore +0 -8
- package/.spago/lists/v7.0.0/.gitignore +0 -7
- package/.spago/maybe/v6.0.0/.gitignore +0 -7
- package/.spago/media-types/v6.0.0/.editorconfig +0 -13
- package/.spago/media-types/v6.0.0/.gitignore +0 -9
- package/.spago/media-types/v6.0.0/.tidyrc.json +0 -10
- package/.spago/newtype/v5.0.0/.gitignore +0 -7
- package/.spago/node-buffer/v9.0.0/.eslintrc.json +0 -26
- package/.spago/node-buffer/v9.0.0/.gitignore +0 -8
- package/.spago/node-fs/v9.1.0/.eslintrc.json +0 -29
- package/.spago/node-fs/v9.1.0/.gitignore +0 -9
- package/.spago/node-path/v5.0.0/.eslintrc.json +0 -26
- package/.spago/node-path/v5.0.0/.gitignore +0 -8
- package/.spago/node-process/v11.2.0/.eslintrc.json +0 -29
- package/.spago/node-process/v11.2.0/.gitignore +0 -8
- package/.spago/node-streams/v9.0.0/.eslintrc.json +0 -29
- package/.spago/node-streams/v9.0.0/.gitignore +0 -8
- package/.spago/nonempty/v7.0.0/.gitignore +0 -7
- package/.spago/now/v6.0.0/.editorconfig +0 -13
- package/.spago/now/v6.0.0/.eslintrc.json +0 -29
- package/.spago/now/v6.0.0/.gitignore +0 -14
- package/.spago/now/v6.0.0/.tidyrc.json +0 -10
- package/.spago/nullable/v6.0.0/.editorconfig +0 -13
- package/.spago/nullable/v6.0.0/.eslintrc.json +0 -29
- package/.spago/nullable/v6.0.0/.gitignore +0 -14
- package/.spago/nullable/v6.0.0/.tidyrc.json +0 -10
- package/.spago/optparse/v6.0.0/.gitignore +0 -8
- package/.spago/optparse/v6.0.0/.npmrc +0 -1
- package/.spago/ordered-collections/v3.1.1/.gitignore +0 -8
- package/.spago/orders/v6.0.0/.gitignore +0 -7
- package/.spago/parallel/v7.0.0/.gitignore +0 -7
- package/.spago/parsing/v10.3.1/.editorconfig +0 -13
- package/.spago/parsing/v10.3.1/.gitignore +0 -12
- package/.spago/parsing/v10.3.1/.tidyoperators +0 -231
- package/.spago/parsing/v10.3.1/.tidyrc.json +0 -10
- package/.spago/partial/v4.0.0/.eslintrc.json +0 -26
- package/.spago/partial/v4.0.0/.gitignore +0 -8
- package/.spago/posix-types/v6.0.0/.gitignore +0 -7
- package/.spago/prelude/v6.0.1/.eslintrc.json +0 -26
- package/.spago/prelude/v6.0.1/.gitignore +0 -8
- package/.spago/profunctor/v6.0.0/.gitignore +0 -7
- package/.spago/psci-support/v6.0.0/.eslintrc.json +0 -28
- package/.spago/psci-support/v6.0.0/.gitignore +0 -8
- package/.spago/quickcheck/v8.0.1/.eslintrc.json +0 -26
- package/.spago/quickcheck/v8.0.1/.gitignore +0 -8
- package/.spago/random/v6.0.0/.eslintrc.json +0 -26
- package/.spago/random/v6.0.0/.gitignore +0 -8
- package/.spago/record/v4.0.0/.eslintrc.json +0 -26
- package/.spago/record/v4.0.0/.gitignore +0 -8
- package/.spago/refs/v6.0.0/.eslintrc.json +0 -26
- package/.spago/refs/v6.0.0/.gitignore +0 -8
- package/.spago/st/v6.2.0/.eslintrc.json +0 -26
- package/.spago/st/v6.2.0/.gitignore +0 -8
- package/.spago/strings/v6.0.1/.eslintrc.json +0 -26
- package/.spago/strings/v6.0.1/.gitignore +0 -8
- package/.spago/tailrec/v6.1.0/.gitignore +0 -7
- package/.spago/transformers/v6.0.0/.gitignore +0 -7
- package/.spago/tuples/v7.0.0/.gitignore +0 -7
- package/.spago/type-equality/v4.0.1/.gitignore +0 -7
- package/.spago/typelevel-prelude/v7.0.0/.gitignore +0 -8
- package/.spago/unfoldable/v6.0.0/.eslintrc.json +0 -26
- package/.spago/unfoldable/v6.0.0/.gitignore +0 -8
- package/.spago/unicode/v6.0.0/.editorconfig +0 -13
- package/.spago/unicode/v6.0.0/.gitattributes +0 -2
- package/.spago/unicode/v6.0.0/.gitignore +0 -19
- package/.spago/unicode/v6.0.0/.tidyrc.json +0 -10
- package/.spago/unsafe-coerce/v6.0.0/.eslintrc.json +0 -26
- package/.spago/unsafe-coerce/v6.0.0/.gitignore +0 -8
- package/.spago/web-dom/v6.0.0/.eslintrc.json +0 -29
- package/.spago/web-dom/v6.0.0/.gitignore +0 -9
- package/.spago/web-events/v4.0.0/.eslintrc.json +0 -29
- package/.spago/web-events/v4.0.0/.gitignore +0 -8
- package/.spago/web-file/v4.0.0/.eslintrc.json +0 -29
- package/.spago/web-file/v4.0.0/.gitignore +0 -8
- package/.spago/web-html/v4.1.0/.eslintrc.json +0 -29
- package/.spago/web-html/v4.1.0/.gitignore +0 -8
- package/.spago/web-storage/v5.0.0/.eslintrc.json +0 -29
- package/.spago/web-storage/v5.0.0/.gitignore +0 -8
- package/.spago/web-xhr/v5.0.1/.eslintrc.json +0 -29
- package/.spago/web-xhr/v5.0.1/.gitignore +0 -8
- package/LICENSE +0 -21
- package/README.md +0 -95
- package/dist/fluid/shared/website-test.js +0 -41
- package/dist/fluid/shared/webtest-lib.js +0 -300023
- package/script/bundle-website.sh +0 -52
- package/script/util/clean.sh +0 -5
- package/script/util/lisp-case.sh +0 -10
- package/website/article/convolution/index.html +0 -97
- package/website/article/css/styles.css +0 -325
- package/website/article/css/view-styles.css +0 -205
- package/website/article/energy-scatter/index.html +0 -47
- package/website/article/methane/index.html +0 -46
- package/website/article/moving-average/index.html +0 -64
- package/website/article/non-renewables/index.html +0 -52
- package/website/article/renewables-linked/index.html +0 -46
- package/website/article/scigen-1805.02474v1-10/index.html +0 -54
- package/website/article/shared/util.js +0 -15
- /package/website/article/{favicon.ico → src/lib/assets/image/favicon.ico} +0 -0
- /package/website/article/{dataset → static/dataset}/SciGen/1805.02474v1-10.json +0 -0
- /package/website/article/{dataset → static/dataset}/methane-emissions.json +0 -0
- /package/website/article/{dataset → static/dataset}/non-renewables.json +0 -0
- /package/website/article/{dataset → static/dataset}/renewable-new.json +0 -0
- /package/website/article/{dataset → static/dataset}/renewable.json +0 -0
- /package/website/article/{fluid → static/fluid}/1805.02474v1-10.fld +0 -0
- /package/website/article/{fluid → static/fluid}/bar-chart-line-chart.fld +0 -0
- /package/website/article/{fluid → static/fluid}/convolution/emboss.fld +0 -0
- /package/website/article/{fluid → static/fluid}/convolution/testImage.fld +0 -0
- /package/website/article/{fluid → static/fluid}/convolution.fld +0 -0
- /package/website/article/{fluid → static/fluid}/dataset/scigen/_1805_02474v1_10.fld +0 -0
- /package/website/article/{fluid → static/fluid}/energyscatter.fld +0 -0
- /package/website/article/{fluid → static/fluid}/methane.fld +0 -0
- /package/website/article/{fluid → static/fluid}/methane_data.fld +0 -0
- /package/website/article/{fluid → static/fluid}/moving-average.fld +0 -0
- /package/website/article/{fluid → static/fluid}/non-renewables.fld +0 -0
- /package/website/article/{fluid → static/fluid}/nonRenewables.fld +0 -0
- /package/website/article/{fluid → static/fluid}/renewables.fld +0 -0
- /package/website/article/{fluid → static/fluid}/scigen.fld +0 -0
- /package/website/article/{fluid → static/fluid}/util.fld +0 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# type Colour = Str
|
|
2
|
+
# type Colours = List Colour
|
|
3
|
+
# type Cat = Str
|
|
4
|
+
|
|
5
|
+
# Group has location (0, 0) because it doesn't interfere with positioning of its children.
|
|
6
|
+
# GraphicsElement -> Point
|
|
7
|
+
def coords(Group(gs)): Point(0, 0)
|
|
8
|
+
def coords(Rect(x, y, _, _, _)): Point(x, y)
|
|
9
|
+
def coords(String(x, y, _, _, _)): Point(x, y)
|
|
10
|
+
def coords(Viewport(x, y, _, _, _, _, _, _, _)): Point(x, y)
|
|
11
|
+
|
|
12
|
+
# GraphicsElement -> Float
|
|
13
|
+
def get_x(g):
|
|
14
|
+
def Point(x, _): coords(g)
|
|
15
|
+
x
|
|
16
|
+
|
|
17
|
+
# GraphicsElement -> Float
|
|
18
|
+
def get_y(g):
|
|
19
|
+
def Point(_, y): coords(g)
|
|
20
|
+
y
|
|
21
|
+
|
|
22
|
+
# Want some kind of typeclass mechanism plus record accessors/updaters.
|
|
23
|
+
# Float -> GraphicsElement -> GraphicsElement
|
|
24
|
+
def set_x(x, Group(gs)):
|
|
25
|
+
error("Group has immutable coordinates")
|
|
26
|
+
def set_x(x, Rect(_, y, w, h, fill)):
|
|
27
|
+
Rect(x, y, w, h, fill)
|
|
28
|
+
def set_x(x, String(_, y, str, anchor, baseline)):
|
|
29
|
+
String(x, y, str, anchor, baseline)
|
|
30
|
+
def set_x(x, Viewport(_, y, w, h, fill, margin, scale, translate, g)):
|
|
31
|
+
Viewport(x, y, w, h, fill, margin, scale, translate, g)
|
|
32
|
+
|
|
33
|
+
# (Point, Point) -> Point
|
|
34
|
+
def dimensions2((Point(x1, y1), Point(x2, y2))):
|
|
35
|
+
Point(max(x1, x2), max(y1, y2))
|
|
36
|
+
|
|
37
|
+
# For Group, dimensions are relative to implicit coords of (0, 0), since a Group's children are effectively
|
|
38
|
+
# positioned relative to parent of Group. For Polymarker, will probably have to ignore the markers themselves,
|
|
39
|
+
# since they are scale-invariant.
|
|
40
|
+
# GraphicsElement -> Point
|
|
41
|
+
def dimensions(Group(gs)):
|
|
42
|
+
foldl(curry(dimensions2), Point(0, 0), map(coords_op, gs))
|
|
43
|
+
def dimensions(Polyline(ps, _, _)):
|
|
44
|
+
foldl(curry(dimensions2), Point(0, 0), ps)
|
|
45
|
+
def dimensions(Rect(_, _, w, h, _)): Point(w, h)
|
|
46
|
+
def dimensions(String(_, _, _, _, _)): Point(0, 0)
|
|
47
|
+
def dimensions(Viewport(_, _, w, h, _, _, _, _, _)): Point(w, h)
|
|
48
|
+
|
|
49
|
+
def coords_op(g):
|
|
50
|
+
def (Point(x, y), Point(w, h)):
|
|
51
|
+
prod(coords, dimensions, g)
|
|
52
|
+
Point(x + w, y + h)
|
|
53
|
+
|
|
54
|
+
# GraphicsElement -> Float
|
|
55
|
+
def width(g):
|
|
56
|
+
def Point(w, _): dimensions(g)
|
|
57
|
+
w
|
|
58
|
+
|
|
59
|
+
# GraphicsElement -> Float
|
|
60
|
+
def height(g):
|
|
61
|
+
def Point(_, h): dimensions(g)
|
|
62
|
+
h
|
|
63
|
+
|
|
64
|
+
# Float -> Float -> List GraphicsElement -> List GraphicsElement
|
|
65
|
+
def space_right(z, sep, gs):
|
|
66
|
+
zip_with(set_x, iterate(length(gs), (+)(sep), z), gs)
|
|
67
|
+
|
|
68
|
+
# Bake colour decisions into the library for the time being. Provide two palettes, so we can have two
|
|
69
|
+
# different sets of categorical values (e.g. countries and energy types). Palettes from colorbrewer2.org
|
|
70
|
+
def colours1: [
|
|
71
|
+
"#66c2a5",
|
|
72
|
+
"#a6d854",
|
|
73
|
+
"#ffd92f",
|
|
74
|
+
"#e5c494",
|
|
75
|
+
"#fc8d62",
|
|
76
|
+
"#b3b3b3",
|
|
77
|
+
"#8da0cb",
|
|
78
|
+
"#e78ac3"
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
def colours2: [
|
|
82
|
+
"#e41a1c",
|
|
83
|
+
"#377eb8",
|
|
84
|
+
"#4daf4a",
|
|
85
|
+
"#984ea3",
|
|
86
|
+
"#ff7f00",
|
|
87
|
+
"#ffff33",
|
|
88
|
+
"#a65628",
|
|
89
|
+
"#f781bf"
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
# Compositionality principle: child coords/dimensions are always expressed directly using parent reference
|
|
93
|
+
# frame, to avoid depending on content of child, and so are not themselves scaled. Polyline can't be scaled
|
|
94
|
+
# directly because it inherits its frame of reference from its parent. For Viewport, margin will shrink the
|
|
95
|
+
# available area, possibly to zero, at which point nothing will be rendered.
|
|
96
|
+
# Float -> GraphicsElement -> GraphicsElement
|
|
97
|
+
def scale_to_width(w, Rect(x, y, _, h, fill)):
|
|
98
|
+
Rect(x, y, w, h, fill)
|
|
99
|
+
def scale_to_width(w, Viewport(x, y, w0, h, fill, margin, Scale(x_scale, y_scale), translate, g)):
|
|
100
|
+
def scale: Scale((x_scale * w) / w0, y_scale)
|
|
101
|
+
Viewport(x, y, w, h, fill, margin, scale, translate, g)
|
|
102
|
+
|
|
103
|
+
# Float -> List GraphicsElement -> List GraphicsElement
|
|
104
|
+
def stack_right(sep, gs):
|
|
105
|
+
map(scale_to_width(1 - sep), space_right(sep / 2, 1, gs))
|
|
106
|
+
|
|
107
|
+
# Float -> List GraphicsElement -> GraphicsElement
|
|
108
|
+
def group_right(sep, gs):
|
|
109
|
+
Viewport(0, 0, length(gs), maximum(map(height, gs)), "none", 0, Scale(1, 1), Translate(0, 0), Group(stack_right(sep, gs)))
|
|
110
|
+
|
|
111
|
+
# Heuristic saying how often to place a tick on an axis of length n.
|
|
112
|
+
# Float -> Float
|
|
113
|
+
def tick_every(n):
|
|
114
|
+
def m: floor(log_base(10, n))
|
|
115
|
+
|
|
116
|
+
if n <= 2 * 10 ** m: 2 * 10 ** (m - 1)
|
|
117
|
+
else: 10 ** m
|
|
118
|
+
|
|
119
|
+
def axis_stroke_width: 0.5
|
|
120
|
+
def axis_colour: "black"
|
|
121
|
+
def background_colour: "white"
|
|
122
|
+
def default_margin: 24
|
|
123
|
+
def marker_radius: 3.5
|
|
124
|
+
def tick_length: 4
|
|
125
|
+
|
|
126
|
+
# Helpers for axis functions.
|
|
127
|
+
# Orient -> Colour -> Float -> GraphicsElement
|
|
128
|
+
def tick(Horiz, colour, len):
|
|
129
|
+
Line(Point(0, 0), Point(0, 0 - len), colour, axis_stroke_width)
|
|
130
|
+
def tick(Vert, colour, len):
|
|
131
|
+
Line(Point(0, 0), Point(0 - len, 0), colour, axis_stroke_width)
|
|
132
|
+
|
|
133
|
+
# Orient -> Float -> Float -> Str -> GraphicsElement
|
|
134
|
+
def label(Horiz, x, distance, str):
|
|
135
|
+
String(x, (0 - distance) - 4, str, "middle", "hanging")
|
|
136
|
+
def label(Vert, x, distance, str):
|
|
137
|
+
String(0 - distance, x, str, "end", "central")
|
|
138
|
+
|
|
139
|
+
# Orient -> Colour -> Float -> Str -> GraphicsElement
|
|
140
|
+
def labelled_tick(orient, colour, len, str):
|
|
141
|
+
Group([
|
|
142
|
+
tick(orient, colour, len),
|
|
143
|
+
label(orient, 0, len, str)
|
|
144
|
+
])
|
|
145
|
+
|
|
146
|
+
# Orient -> Float -> Float -> Point
|
|
147
|
+
def mk_point(Horiz, x, y): Point(y, x)
|
|
148
|
+
def mk_point(Vert, x, y): Point(x, y)
|
|
149
|
+
|
|
150
|
+
# x is position of this axis on the other axis. Returns axis and position of last tick.
|
|
151
|
+
# Orient -> Float -> Float -> Float -> GraphicsElement
|
|
152
|
+
def axis(orient, x, start, end):
|
|
153
|
+
def tick_sp: tick_every(end - start)
|
|
154
|
+
def first_tick: ceiling_to_nearest(start, tick_sp)
|
|
155
|
+
def last_tick: ceiling_to_nearest(end, tick_sp)
|
|
156
|
+
def n: floor((end - first_tick) / tick_sp) + 1
|
|
157
|
+
def ys: iterate(n, (+)(tick_sp), first_tick)
|
|
158
|
+
def ys:
|
|
159
|
+
if first_tick > start: start :| ys
|
|
160
|
+
else: ys
|
|
161
|
+
def ys:
|
|
162
|
+
if last_tick > end: concat2(ys, [last_tick])
|
|
163
|
+
else: ys
|
|
164
|
+
def ps: map(mk_point(orient, x), ys)
|
|
165
|
+
def ax:
|
|
166
|
+
Group([
|
|
167
|
+
Line(head(ps), last(ps), axis_colour, axis_stroke_width),
|
|
168
|
+
Polymarkers(ps, flip(map, ys, compose(labelled_tick(orient, axis_colour, tick_length), num_to_str)))
|
|
169
|
+
])
|
|
170
|
+
|
|
171
|
+
(ax, last_tick)
|
|
172
|
+
|
|
173
|
+
# x is position of this axis on the other axis.
|
|
174
|
+
# Orient -> Float -> List Cat -> GraphicsElement
|
|
175
|
+
def cat_axis(orient, x, cat_values):
|
|
176
|
+
def ys: iterate(length(cat_values) + 1, (+)(1), 0)
|
|
177
|
+
def ps: map(mk_point(orient, x), ys)
|
|
178
|
+
|
|
179
|
+
Group([
|
|
180
|
+
Line(head(ps), last(ps), axis_colour, axis_stroke_width),
|
|
181
|
+
Polymarkers(tail(ps), map(const(tick(orient, axis_colour, tick_length)), cat_values)),
|
|
182
|
+
Polymarkers(flip(map, tail(ps), lambda Point(x, y): Point(x - 0.5, y)), map(label(orient, -0.5, 0), cat_values))
|
|
183
|
+
])
|
|
184
|
+
|
|
185
|
+
# Float -> Float -> Float -> Float -> List GraphicsElement -> GraphicsElement
|
|
186
|
+
def viewport(x_start, x_finish, y_finish, margin, gs):
|
|
187
|
+
Viewport(0, 0, x_finish - x_start, y_finish, background_colour, margin, Scale(1, 1), Translate(0 - x_start, 0), Group(gs))
|
|
188
|
+
|
|
189
|
+
# Plot a map of x values to lists of (categorical value, y value) pairs. Importantly, assume all data is uniform
|
|
190
|
+
# (categorical keys are the same for each x value and are ordered the same each time).
|
|
191
|
+
# Bool -> Colours -> Float -> List (Float, List (Cat, Float)) -> GraphicsElement
|
|
192
|
+
def line_chart(with_axes, colours, x_start, data):
|
|
193
|
+
def xs: map(fst, data)
|
|
194
|
+
def n_cat: length(snd(head(data)))
|
|
195
|
+
|
|
196
|
+
# (Int, Colour) -> GraphicsElement
|
|
197
|
+
def plot((n, colour)):
|
|
198
|
+
def ps:
|
|
199
|
+
map(lambda (x, kvs): Point(x, snd(nth(n, kvs))), data)
|
|
200
|
+
|
|
201
|
+
Group([
|
|
202
|
+
Polyline(ps, colour, 1),
|
|
203
|
+
Polymarkers(ps, repeat(length(ps), Circle(0, 0, marker_radius, colour)))
|
|
204
|
+
])
|
|
205
|
+
|
|
206
|
+
# List GraphicsElement
|
|
207
|
+
def lines:
|
|
208
|
+
zip_with(curry(plot), iterate(n_cat, (+)(1), 0), colours)
|
|
209
|
+
def x_finish: last(xs)
|
|
210
|
+
def y_finish:
|
|
211
|
+
maximum(flip(map, data, lambda (_, kvs): maximum(map(snd, kvs))))
|
|
212
|
+
|
|
213
|
+
if with_axes:
|
|
214
|
+
def (x_axis, x_finish): axis(Horiz, 0, x_start, x_finish)
|
|
215
|
+
def (y_axis, y_finish_): axis(Vert, x_start, 0, y_finish)
|
|
216
|
+
viewport(x_start, x_finish, y_finish_, default_margin, x_axis :| y_axis :| lines)
|
|
217
|
+
else:
|
|
218
|
+
viewport(x_start, x_finish, y_finish, 0, lines)
|
|
219
|
+
|
|
220
|
+
# Plot a chart of categorical values on the x-axis and renderings of the corresponding a-value on the y-axis.
|
|
221
|
+
# (Colours -> List a -> GraphicsElement) -> Bool -> Colours -> Float -> List (Cat, a) -> GraphicsElement
|
|
222
|
+
def categorical_chart(plot_value, with_axes, colours, sep, data):
|
|
223
|
+
def gs:
|
|
224
|
+
stack_right(sep, plot_value(colours, map(snd, data)))
|
|
225
|
+
def w: length(gs)
|
|
226
|
+
def h: maximum(map(height, gs))
|
|
227
|
+
|
|
228
|
+
if with_axes:
|
|
229
|
+
def x_axis: cat_axis(Horiz, 0, map(fst, data))
|
|
230
|
+
def (y_axis, h_): axis(Vert, 0, 0, h)
|
|
231
|
+
viewport(0, w, h_, default_margin, concat2(gs, [x_axis, y_axis]))
|
|
232
|
+
else:
|
|
233
|
+
viewport(0, w, h, 0, gs)
|
|
234
|
+
|
|
235
|
+
# Colours -> List a -> GraphicsElement
|
|
236
|
+
def rects(colours, ns):
|
|
237
|
+
zip_with(lambda colour, n: Rect(0, 0, 1, n, colour), colours, ns)
|
|
238
|
+
|
|
239
|
+
# First component of data (categorical value) currently ignored; values just mapped positionally to colors.
|
|
240
|
+
# Can we use Group instead of Viewport here?
|
|
241
|
+
# Colours -> List (a, Num) -> GraphicsElement
|
|
242
|
+
def stacked_bar(colours, ns):
|
|
243
|
+
def heights: map(snd, ns)
|
|
244
|
+
def subtotals: scanl1((+), 0, heights)
|
|
245
|
+
def dims: zip(0 :| subtotals, heights)
|
|
246
|
+
def rects:
|
|
247
|
+
map(lambda ((y, height), colour): Rect(0, y, 1, height, colour), zip(dims, colours))
|
|
248
|
+
|
|
249
|
+
Viewport(0, 0, 1, last(subtotals), "none", 0, Scale(1, 1), Translate(0, 0), Group(rects))
|
|
250
|
+
|
|
251
|
+
# Bool -> Colours -> Float -> List (a, Float) -> GraphicsElement
|
|
252
|
+
def bar_chart:
|
|
253
|
+
categorical_chart(rects)
|
|
254
|
+
|
|
255
|
+
# For each categorical value of type a, plot a bar chart for the corresponding b-indexed data.
|
|
256
|
+
# Bool -> Colours -> Float -> List (a, List (b, Float)) -> GraphicsElement
|
|
257
|
+
def grouped_bar_chart:
|
|
258
|
+
categorical_chart(compose(map, flip(bar_chart(False), 0)))
|
|
259
|
+
|
|
260
|
+
# See stacked_bar for strong (unjustified) assumption about uniformity of data.
|
|
261
|
+
# Bool -> Colours -> Num -> List (a, List (b, Num)) -> GraphicsElement
|
|
262
|
+
def stacked_bar_chart:
|
|
263
|
+
categorical_chart(compose(map, stacked_bar))
|
|
264
|
+
|
|
265
|
+
# Bit of a hack, but how text fits into our model is a bit unclear at the moment.
|
|
266
|
+
# Str -> GraphicsElement -> GraphicsElement
|
|
267
|
+
def caption(str, Viewport(x, y, w, h, fill, margin, scale, translate, g)):
|
|
268
|
+
def g_:
|
|
269
|
+
Group([
|
|
270
|
+
String(x + w / 2, -2, str, "middle", "hanging"),
|
|
271
|
+
Viewport(0, 0, w, h, fill, margin, scale, translate, g)
|
|
272
|
+
])
|
|
273
|
+
|
|
274
|
+
Viewport(x, y, w, h, background_colour, default_margin / 2 + 4, Scale(1, 1), Translate(0, 0), g_)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
def zero(m, n, image):
|
|
2
|
+
def (m_size, n_size): dims(image)
|
|
3
|
+
if m >= 0 and m < m_size and n >= 0 and n < n_size: image ! (m, n)
|
|
4
|
+
else: 0
|
|
5
|
+
|
|
6
|
+
def wrap(m, n, image):
|
|
7
|
+
def (m_size, n_size): dims(image)
|
|
8
|
+
image ! (m % m_size, n % n_size)
|
|
9
|
+
|
|
10
|
+
def extend(m, n, image):
|
|
11
|
+
def (m_size, n_size): dims(image)
|
|
12
|
+
def m_:
|
|
13
|
+
min(max(m, 0), m_size - 1)
|
|
14
|
+
def n_:
|
|
15
|
+
min(max(n, 0), n_size - 1)
|
|
16
|
+
image ! (m_, n_)
|
|
17
|
+
|
|
18
|
+
def matrix_sum(matr):
|
|
19
|
+
def (m, n): dims(matr)
|
|
20
|
+
foldl((+), 0, [matr ! (i, j) for (i, j) in range2d((0, 0), (m, n))])
|
|
21
|
+
|
|
22
|
+
def convolve(image, kernel, lookup):
|
|
23
|
+
def ((m, n), (i, j)):
|
|
24
|
+
(dims(image), dims(kernel))
|
|
25
|
+
def (half_i, half_j):
|
|
26
|
+
(i |quot| 2, j |quot| 2)
|
|
27
|
+
def area: i * j
|
|
28
|
+
def interMatrix(m_, n_):
|
|
29
|
+
[| lookup(m_ + i_ - half_i, n_ + j_ - half_j, image) * kernel ! (i_, j_) for (i_, j_) in (i, j) |]
|
|
30
|
+
|
|
31
|
+
[| matrix_sum(interMatrix(m_, n_)) |quot| area for (m_, n_) in (m, n) |]
|
|
32
|
+
|
|
33
|
+
def mat_mul(a, b):
|
|
34
|
+
def ((m, n), (i, j)): (dims(a), dims(b))
|
|
35
|
+
|
|
36
|
+
if not(n == i):
|
|
37
|
+
error("Dimensions don't line up")
|
|
38
|
+
else:
|
|
39
|
+
@doc(f"""Intermediate matrix""")
|
|
40
|
+
[| sum([a!(i_, k) * b!(k, j_) for k in range(0, n)]) for (i_, j_) in (m, j) |]
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# "Num" throughout means (Int + Float).
|
|
2
|
+
|
|
3
|
+
# To resolve in new semantics: expected dependencies don't arise for and/or unless they make a fresh value.
|
|
4
|
+
# Bool -> Bool
|
|
5
|
+
def and(False, y): False
|
|
6
|
+
def and(True, True): True
|
|
7
|
+
def and(True, False): False
|
|
8
|
+
|
|
9
|
+
# Bool -> Bool
|
|
10
|
+
def or(True, y): True
|
|
11
|
+
def or(False, True): True
|
|
12
|
+
def or(False, False): False
|
|
13
|
+
|
|
14
|
+
# Bool -> Bool
|
|
15
|
+
def not(True): False
|
|
16
|
+
def not(False): True
|
|
17
|
+
|
|
18
|
+
# Int -> Int -> Ordering
|
|
19
|
+
def compare(x, y):
|
|
20
|
+
if x > y: GT
|
|
21
|
+
else:
|
|
22
|
+
if x < y: LT
|
|
23
|
+
else: EQ
|
|
24
|
+
|
|
25
|
+
# (b -> b -> c) -> (a -> b) -> a -> a -> c
|
|
26
|
+
def on(bin_op, prop, x, y):
|
|
27
|
+
bin_op(prop(x), prop(y))
|
|
28
|
+
|
|
29
|
+
# Num -> Num
|
|
30
|
+
def negate: (-)(0)
|
|
31
|
+
|
|
32
|
+
# Log of x in base y.
|
|
33
|
+
# Float -> Float -> Float
|
|
34
|
+
def log_base(x, y): log(y) / log(x)
|
|
35
|
+
|
|
36
|
+
# Float -> Float -> Float
|
|
37
|
+
def ceiling_to_nearest(n, m): ceiling(n / m) * m
|
|
38
|
+
|
|
39
|
+
# (b -> c) -> (a -> b) -> a -> c
|
|
40
|
+
# Want infix <<<
|
|
41
|
+
def compose(f, g, x): f(g(x))
|
|
42
|
+
|
|
43
|
+
# ((a, b) -> c) -> a -> b -> c
|
|
44
|
+
def curry(f, x, y): f((x, y))
|
|
45
|
+
|
|
46
|
+
# (a -> b -> c) -> (a, b) -> c
|
|
47
|
+
def uncurry(f, (x, y)): f(x, y)
|
|
48
|
+
|
|
49
|
+
# a -> b -> a
|
|
50
|
+
def const(x, _): x
|
|
51
|
+
|
|
52
|
+
# (a -> b) -> (a, c) -> (b, c)
|
|
53
|
+
def first(f, (a, c)): (f(a), c)
|
|
54
|
+
|
|
55
|
+
# (a, b) -> b
|
|
56
|
+
def snd((_, y)): y
|
|
57
|
+
|
|
58
|
+
# (a -> b) -> (c, a) -> (c, b)
|
|
59
|
+
def second(f, (c, a)): (c, f(a))
|
|
60
|
+
|
|
61
|
+
# (a -> b -> c) -> b -> a -> c
|
|
62
|
+
def flip(f, x, y): f(y, x)
|
|
63
|
+
|
|
64
|
+
# (a, b) -> a
|
|
65
|
+
def fst((x, _)): x
|
|
66
|
+
|
|
67
|
+
# a -> a
|
|
68
|
+
def id(x): x
|
|
69
|
+
|
|
70
|
+
# (a -> b) -> (a -> c) -> a -> (b, c)
|
|
71
|
+
# Want infix &&&
|
|
72
|
+
def prod(f, g, x): (f(x), g(x))
|
|
73
|
+
|
|
74
|
+
# (a, b) -> (b, a)
|
|
75
|
+
def swap((a, b)): (b, a)
|
|
76
|
+
|
|
77
|
+
# List a -> a
|
|
78
|
+
def head([]):
|
|
79
|
+
error("Can't take head of empty list")
|
|
80
|
+
def head(x :| _): x
|
|
81
|
+
|
|
82
|
+
# List a -> List a
|
|
83
|
+
def tail([]):
|
|
84
|
+
error("Can't take tail of empty list")
|
|
85
|
+
def tail(_ :| xs): xs
|
|
86
|
+
|
|
87
|
+
# Eq a => a -> List a -> Bool
|
|
88
|
+
def elem(x, []): False
|
|
89
|
+
def elem(x, y :| xs):
|
|
90
|
+
x == y or elem(x, xs)
|
|
91
|
+
|
|
92
|
+
# (a -> Bool) -> List a -> Option a
|
|
93
|
+
def find(p, []): None
|
|
94
|
+
def find(p, x :| xs):
|
|
95
|
+
if p(x): Some(x)
|
|
96
|
+
else: find(p, xs)
|
|
97
|
+
|
|
98
|
+
# String -> String -> List Dict -> Option Dict
|
|
99
|
+
def find_with_key(k, v, rs):
|
|
100
|
+
find(lambda y: y[k] == v, rs)
|
|
101
|
+
|
|
102
|
+
# Option a -> a
|
|
103
|
+
def from_some(None): error("Expected Some!")
|
|
104
|
+
def from_some(Some(x)): x
|
|
105
|
+
|
|
106
|
+
# Option a -> a -> a
|
|
107
|
+
def from_option(None, y): y
|
|
108
|
+
def from_option(Some(x), _): x
|
|
109
|
+
|
|
110
|
+
# (a -> Bool) -> List a -> List a
|
|
111
|
+
def filter(p, []): []
|
|
112
|
+
def filter(p, x :| xs):
|
|
113
|
+
def ys: filter(p, xs)
|
|
114
|
+
|
|
115
|
+
if p(x): x :| ys
|
|
116
|
+
else: ys
|
|
117
|
+
|
|
118
|
+
# (a -> Option b) -> List a -> List b
|
|
119
|
+
def filter_map(p, []): []
|
|
120
|
+
def filter_map(p, x :| xs):
|
|
121
|
+
match p(x):
|
|
122
|
+
case None: filter_map(f, xs)
|
|
123
|
+
case Some(y):
|
|
124
|
+
y :| filter_map(f, xs)
|
|
125
|
+
|
|
126
|
+
# Equivalent to but more efficient than length ∘ filter(p)
|
|
127
|
+
def count_if(p):
|
|
128
|
+
def f(n, x):
|
|
129
|
+
if p(x): n + 1
|
|
130
|
+
else: n
|
|
131
|
+
foldl(f, 0)
|
|
132
|
+
|
|
133
|
+
# Split list into longest initial segment whose elements satisfy a predicate, and the rest.
|
|
134
|
+
# (a -> Bool) -> List a -> { init :: List a, rest :: List a }
|
|
135
|
+
def span(p, Nil): { init: Nil, rest: Nil }
|
|
136
|
+
def span(p, x :| xs_):
|
|
137
|
+
if p(x):
|
|
138
|
+
def { init: ys, rest: zs }: span(p, xs_)
|
|
139
|
+
{ init: x :| ys, rest: zs }
|
|
140
|
+
else:
|
|
141
|
+
{ init: Nil, rest: x :| xs_ }
|
|
142
|
+
|
|
143
|
+
# Each returned list is non-empty.
|
|
144
|
+
# (a -> a -> Bool) -> List a -> List (List a)
|
|
145
|
+
def group_by(eq, Nil): Nil
|
|
146
|
+
def group_by(eq, x :| xs):
|
|
147
|
+
def { init: ys, rest: zs }: span(eq(x), xs)
|
|
148
|
+
(x :| ys) :| group_by(eq, zs)
|
|
149
|
+
|
|
150
|
+
# (a -> b -> a) -> a -> List b -> a
|
|
151
|
+
def foldl(op, z, []): z
|
|
152
|
+
def foldl(op, z, x :| xs):
|
|
153
|
+
foldl(op, op(z, x), xs)
|
|
154
|
+
|
|
155
|
+
# (a -> b -> a) -> List b -> a
|
|
156
|
+
def foldl1(op, x :| xs): foldl(op, x, xs)
|
|
157
|
+
|
|
158
|
+
# (a -> b -> b) -> b -> List a -> b
|
|
159
|
+
def foldr(op, z, []): z
|
|
160
|
+
def foldr(op, z, x :| xs):
|
|
161
|
+
op(x, foldr(op, z, xs))
|
|
162
|
+
|
|
163
|
+
# (a -> b -> b) -> List a -> b
|
|
164
|
+
def foldr1(op, [x]): x
|
|
165
|
+
def foldr1(op, x :| y :| xs):
|
|
166
|
+
op(x, foldr1(op, y :| xs))
|
|
167
|
+
|
|
168
|
+
# (a -> b -> a) -> a -> List b -> List a
|
|
169
|
+
def scanl1(op, z, xs):
|
|
170
|
+
def go(x, continue, acc):
|
|
171
|
+
def next: op(acc, x)
|
|
172
|
+
next :| continue(next)
|
|
173
|
+
foldr(go, const([]), xs, z)
|
|
174
|
+
|
|
175
|
+
# (a -> b -> a) -> a -> List b -> List a
|
|
176
|
+
def scanl(op, z, xs):
|
|
177
|
+
z :| scanl1(op, z, xs)
|
|
178
|
+
|
|
179
|
+
# (a -> b) -> List a -> List b
|
|
180
|
+
def map(f, []): []
|
|
181
|
+
def map(f, x :| xs): f(x) :| map(f, xs)
|
|
182
|
+
|
|
183
|
+
# (List a, List a) -> List a
|
|
184
|
+
def append(([], ys)): ys
|
|
185
|
+
def append((x :| xs, ys)):
|
|
186
|
+
x :| append((xs, ys))
|
|
187
|
+
|
|
188
|
+
# List a -> List -> List a
|
|
189
|
+
# Want infix ++
|
|
190
|
+
def concat2([], ys): ys
|
|
191
|
+
def concat2(x :| xs, ys):
|
|
192
|
+
x :| concat2(xs, ys)
|
|
193
|
+
|
|
194
|
+
# List (List a) -> List a
|
|
195
|
+
def concat: foldr(concat2, [])
|
|
196
|
+
|
|
197
|
+
# (a -> List b) -> List a -> List b
|
|
198
|
+
def concat_map(f, xs): concat(map(f, xs))
|
|
199
|
+
|
|
200
|
+
# List String -> String
|
|
201
|
+
def join: foldr((++), "")
|
|
202
|
+
|
|
203
|
+
# List a -> a -> List a
|
|
204
|
+
def intersperse([], _): []
|
|
205
|
+
def intersperse([x], _): [x]
|
|
206
|
+
def intersperse(x :| y :| ys, sep):
|
|
207
|
+
x :| sep :| intersperse(y :| ys, sep)
|
|
208
|
+
|
|
209
|
+
# Int -> (a -> a) -> a -> List a
|
|
210
|
+
def iterate(n, f, z):
|
|
211
|
+
if n == 0: []
|
|
212
|
+
else:
|
|
213
|
+
z :| map(f, iterate(n - 1, f, z))
|
|
214
|
+
|
|
215
|
+
# List Num -> Num
|
|
216
|
+
def sum: foldr((+), 0)
|
|
217
|
+
|
|
218
|
+
# List Num -> Num
|
|
219
|
+
def mean(xs):
|
|
220
|
+
def (s, n): foldl(lambda (s, n), x: (s + x, n + 1), (0, 0), xs)
|
|
221
|
+
s / n
|
|
222
|
+
|
|
223
|
+
# List a -> a
|
|
224
|
+
def last([x]): x
|
|
225
|
+
def last(x :| y :| ys): last(y :| ys)
|
|
226
|
+
|
|
227
|
+
# List a -> Int
|
|
228
|
+
def length([]): 0
|
|
229
|
+
def length(_ :| xs): 1 + length(xs)
|
|
230
|
+
|
|
231
|
+
# List a -> List a
|
|
232
|
+
def reverse([]): []
|
|
233
|
+
def reverse(x :| xs):
|
|
234
|
+
append((reverse(xs), [x]))
|
|
235
|
+
|
|
236
|
+
# Int -> a -> List a
|
|
237
|
+
def repeat: flip(iterate, id)
|
|
238
|
+
|
|
239
|
+
# Int -> List a -> List a
|
|
240
|
+
def take(n, xs):
|
|
241
|
+
if n <= 0: []
|
|
242
|
+
else:
|
|
243
|
+
match xs:
|
|
244
|
+
case []: []
|
|
245
|
+
case x :| xs:
|
|
246
|
+
x :| take(n - 1, xs)
|
|
247
|
+
|
|
248
|
+
# Int -> List a -> List a
|
|
249
|
+
def drop(n, xs):
|
|
250
|
+
if n <= 0: xs
|
|
251
|
+
else:
|
|
252
|
+
match xs:
|
|
253
|
+
case []: []
|
|
254
|
+
case _ :| xs: drop(n - 1, xs)
|
|
255
|
+
|
|
256
|
+
# Int -> List a -> List a
|
|
257
|
+
def last_n(n, xs):
|
|
258
|
+
foldl(compose(const, drop(1)), xs, drop(n, xs))
|
|
259
|
+
|
|
260
|
+
# Expects non-negative integer as first argument and non-empty list as second argument.
|
|
261
|
+
# Int -> List a -> a
|
|
262
|
+
def nth(n, x :| xs):
|
|
263
|
+
if n == 0: x
|
|
264
|
+
else: nth(n - 1, xs)
|
|
265
|
+
|
|
266
|
+
# Int -> Int -> List (List Int) -> Int
|
|
267
|
+
def nth2(i, j, xss):
|
|
268
|
+
nth(j, nth(i, xss))
|
|
269
|
+
|
|
270
|
+
# Partial; requires k to be in the map.
|
|
271
|
+
# Int -> List (Int, b) -> b
|
|
272
|
+
def lookup(k, []):
|
|
273
|
+
error("Key not found in map")
|
|
274
|
+
def lookup(k, (k_, v) :| kvs):
|
|
275
|
+
if k == k_: v
|
|
276
|
+
else: lookup(k, kvs)
|
|
277
|
+
|
|
278
|
+
# Int -> Int -> Int
|
|
279
|
+
def max(n, m):
|
|
280
|
+
if n > m: n
|
|
281
|
+
else: m
|
|
282
|
+
|
|
283
|
+
# Int -> Int -> Int
|
|
284
|
+
def min(n, m):
|
|
285
|
+
if n < m: n
|
|
286
|
+
else: m
|
|
287
|
+
|
|
288
|
+
# List Int -> Int
|
|
289
|
+
def maximum: foldr1(max)
|
|
290
|
+
|
|
291
|
+
# List Int -> Int
|
|
292
|
+
def minimum: foldr1(min)
|
|
293
|
+
|
|
294
|
+
# List (a, b) -> (List a, List b)
|
|
295
|
+
def unzip([]): ([], [])
|
|
296
|
+
def unzip((x, y) :| zs):
|
|
297
|
+
def (xs, ys): unzip(zs)
|
|
298
|
+
(x :| xs, y :| ys)
|
|
299
|
+
|
|
300
|
+
# (a -> b -> c) -> List a -> List b -> List c
|
|
301
|
+
def zip_with(op, [], ys): []
|
|
302
|
+
def zip_with(op, x :| xs, []): []
|
|
303
|
+
def zip_with(op, x :| xs, y :| ys):
|
|
304
|
+
op(x, y) :| zip_with(op, xs, ys)
|
|
305
|
+
|
|
306
|
+
# List a -> List b -> List (a, b)
|
|
307
|
+
def zip: zip_with(curry(id))
|
|
308
|
+
|
|
309
|
+
# Int -> Int -> List Int
|
|
310
|
+
def range(n, m):
|
|
311
|
+
if n < m: n :| range(n + 1, m)
|
|
312
|
+
else: []
|
|
313
|
+
|
|
314
|
+
# (Int, Int) -> (Int, Int) -> List (Int, Int)
|
|
315
|
+
def range2d((m1, n1), (m2, n2)):
|
|
316
|
+
[(i1, i2) for i1 in range(m1, m2) for i2 in range(n1, n2)]
|
|
317
|
+
|
|
318
|
+
# Int -> Int -> Int
|
|
319
|
+
def abs(x, y):
|
|
320
|
+
if x - y < 0: negate(x - y)
|
|
321
|
+
else: x - y
|
|
322
|
+
|
|
323
|
+
# Eq a => [a] -> [a]
|
|
324
|
+
def nub(xs):
|
|
325
|
+
def nub_([], _): []
|
|
326
|
+
def nub_(x :| xs, ys):
|
|
327
|
+
if x |elem| ys:
|
|
328
|
+
nub_(xs, ys)
|
|
329
|
+
else:
|
|
330
|
+
x :| nub_(xs, x :| ys)
|
|
331
|
+
|
|
332
|
+
nub_(xs, [])
|
|
333
|
+
|
|
334
|
+
# Int -> Int -> [a] -> [a]
|
|
335
|
+
def slice(begin, end, xs):
|
|
336
|
+
take(end - begin, drop(begin, xs))
|
|
337
|
+
|
|
338
|
+
# (a -> Boolean) -> List a -> (List a, List a)
|
|
339
|
+
def split_on(p, data):
|
|
340
|
+
def go(fls, trs, []):
|
|
341
|
+
(reverse(fls), reverse(trs))
|
|
342
|
+
def go(fls, trs, x :| xs):
|
|
343
|
+
if p(x):
|
|
344
|
+
go(fls, x :| trs, xs)
|
|
345
|
+
else:
|
|
346
|
+
go(x :| fls, trs, xs)
|
|
347
|
+
|
|
348
|
+
go([], [], data)
|