@explorable-viz/fluid 0.11.4 → 0.12.2
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/README.md +22 -9
- package/dist/fluid/fluid/lib/graphics.fld +60 -60
- package/dist/fluid/fluid/lib/matrix.fld +17 -17
- package/dist/fluid/fluid/lib/prelude.fld +36 -35
- package/dist/fluid/fluid/lib/stats.fld +19 -19
- package/dist/fluid/shared/fluid.mjs +1044 -2141
- package/dist/fluid/shared/load-figure.js +1123 -2147
- package/dist/fluid/shared/webtest-lib.js +124 -124
- package/package.json +1 -1
- package/website/article/css/styles.css +3 -3
- package/website/article/fluid/1805.02474v1-10.fld +2 -2
- package/website/article/fluid/bar-chart-line-chart.fld +1 -1
- package/website/article/fluid/convolution.fld +14 -17
- package/website/article/fluid/dataset/scigen/_1805_02474v1_10.fld +1 -1
- package/website/article/fluid/methane_data.fld +1 -1
- package/website/article/fluid/moving-average.fld +1 -1
- package/website/article/fluid/non-renewables.fld +1 -1
- package/website/article/fluid/nonRenewables.fld +2 -2
- package/website/article/fluid/renewables.fld +1 -1
- package/website/article/fluid/scigen.fld +3 -3
- package/website/article/fluid/util.fld +4 -4
- package/.spago/aff/v7.0.0/.editorconfig +0 -13
- package/.spago/aff/v7.0.0/.eslintrc.json +0 -28
- package/.spago/aff/v7.0.0/.gitignore +0 -14
- package/.spago/aff/v7.0.0/.tidyrc.json +0 -10
- package/.spago/argonaut-codecs/v9.0.0/.editorconfig +0 -13
- package/.spago/argonaut-codecs/v9.0.0/.gitignore +0 -9
- package/.spago/argonaut-codecs/v9.0.0/.tidyrc.json +0 -10
- package/.spago/arrays/v7.0.0/.eslintrc.json +0 -26
- package/.spago/arrays/v7.0.0/.gitignore +0 -9
- package/.spago/console/v6.0.0/.eslintrc.json +0 -30
- package/.spago/console/v6.0.0/.gitignore +0 -9
- package/.spago/debug/v6.0.0/.eslintrc.json +0 -36
- package/.spago/debug/v6.0.0/.gitignore +0 -8
- package/.spago/enums/v6.0.0/.gitignore +0 -7
- package/.spago/foreign-object/v4.0.0/.eslintrc.json +0 -26
- package/.spago/foreign-object/v4.0.0/.gitignore +0 -8
- package/.spago/free/v7.0.0/.gitignore +0 -8
- package/.spago/graphs/v8.0.0/.gitignore +0 -7
- package/.spago/js-uri/v3.0.0/.eslintrc.json +0 -30
- package/.spago/js-uri/v3.0.0/.gitignore +0 -14
- package/.spago/js-uri/v3.0.0/.tidyrc.json +0 -10
- package/.spago/node-buffer/v8.0.0/.eslintrc.json +0 -26
- package/.spago/node-buffer/v8.0.0/.gitignore +0 -8
- package/.spago/node-child-process/v9.0.0/.eslintrc.json +0 -29
- package/.spago/node-child-process/v9.0.0/.gitignore +0 -8
- package/.spago/node-process/v10.0.0/.eslintrc.json +0 -29
- package/.spago/node-process/v10.0.0/.gitignore +0 -8
- package/.spago/node-streams/v7.0.0/.eslintrc.json +0 -29
- package/.spago/node-streams/v7.0.0/.gitignore +0 -8
- package/.spago/optparse/v5.0.0/.gitignore +0 -8
- package/.spago/optparse/v5.0.0/.npmrc +0 -1
- package/.spago/ordered-collections/v3.0.0/.gitignore +0 -8
- package/.spago/parallel/v6.0.0/.gitignore +0 -7
- package/.spago/parsing/v10.0.0/.editorconfig +0 -13
- package/.spago/parsing/v10.0.0/.gitignore +0 -10
- package/.spago/parsing/v10.0.0/.tidyoperators +0 -231
- package/.spago/parsing/v10.0.0/.tidyrc.json +0 -10
- package/.spago/prelude/v6.0.0/.eslintrc.json +0 -26
- package/.spago/prelude/v6.0.0/.gitignore +0 -8
- package/.spago/st/v6.0.0/.eslintrc.json +0 -26
- package/.spago/st/v6.0.0/.gitignore +0 -8
- package/.spago/strings/v6.0.0/.eslintrc.json +0 -26
- package/.spago/strings/v6.0.0/.gitignore +0 -8
- package/.spago/tailrec/v6.0.0/.gitignore +0 -7
- package/.spago/web-html/v4.0.0/.eslintrc.json +0 -29
- package/.spago/web-html/v4.0.0/.gitignore +0 -8
- package/.spago/web-xhr/v5.0.0/.eslintrc.json +0 -29
- package/.spago/web-xhr/v5.0.0/.gitignore +0 -8
- package/website/article/.DS_Store +0 -0
- package/website/article/dataset/.DS_Store +0 -0
package/README.md
CHANGED
|
@@ -1,31 +1,44 @@
|
|
|
1
1
|
## Fluid: Language-integrated data provenance
|
|
2
2
|
|
|
3
|
-
Fluid is
|
|
3
|
+
Fluid is a pure functional programming language, with a provenance-tracking runtime and Pythonic syntax. Fluid is implemented in PureScript and runs in the browser.
|
|
4
4
|
|
|
5
5
|
[](https://github.com/explorable-viz/fluid/actions/workflows/develop.yml)
|
|
6
6
|
[](https://github.com/explorable-viz/fluid/actions/workflows/pages/pages-build-deployment)
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## End-user setup
|
|
9
9
|
|
|
10
10
|
### Software required
|
|
11
|
-
- git
|
|
12
11
|
- Node.js >=18.0.0
|
|
13
12
|
- yarn >= 1.22
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
### Initial configuration
|
|
15
|
+
|
|
16
|
+
Building a Fluid website usually involves building a Node application:
|
|
17
|
+
|
|
18
|
+
- `yarn add @explorable-viz/fluid`
|
|
19
|
+
- `yarn install` to install Node dependencies
|
|
20
|
+
- `yarn install-website article` to copy example article website from `@exploreable-viz/fluid`
|
|
21
|
+
- Add `dist/` and `website/` folders to `.gitignore`
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
### Bundling and serving website
|
|
24
|
+
- `yarn bundle-website $WEBSITE_NAME` to bundle website to `dist/$WEBSITE_NAME`
|
|
25
|
+
- `npx http-serve dist/$WEBSITE_NAME -c-1` to serve website at localhost
|
|
26
|
+
|
|
27
|
+
## Development setup
|
|
28
|
+
|
|
29
|
+
### Additional software required
|
|
30
|
+
- git
|
|
31
|
+
- (Windows only) [Ubuntu WSL](https://ubuntu.com/desktop/wsl)
|
|
18
32
|
|
|
19
|
-
###
|
|
33
|
+
### Initial configuration
|
|
20
34
|
|
|
21
|
-
- Clone
|
|
35
|
+
- Clone repository (for Windows users, under Ubuntu WSL)
|
|
22
36
|
- Run `./script/setup/dev-setup.sh` from the top-level directory
|
|
23
37
|
- `yarn install` to install Node dependencies
|
|
24
|
-
- `yarn build`
|
|
25
38
|
|
|
26
39
|
## Use
|
|
27
40
|
|
|
28
|
-
|
|
41
|
+
- `yarn build` to build interpreter
|
|
29
42
|
|
|
30
43
|
### Running programs from the command line
|
|
31
44
|
|
|
@@ -62,8 +62,8 @@ def height(g):
|
|
|
62
62
|
h
|
|
63
63
|
|
|
64
64
|
# Float -> Float -> List GraphicsElement -> List GraphicsElement
|
|
65
|
-
def
|
|
66
|
-
|
|
65
|
+
def space_right(z, sep, gs):
|
|
66
|
+
zip_with(set_x, iterate(length(gs), (+)(sep), z), gs)
|
|
67
67
|
|
|
68
68
|
# Bake colour decisions into the library for the time being. Provide two palettes, so we can have two
|
|
69
69
|
# different sets of categorical values (e.g. countries and energy types). Palettes from colorbrewer2.org
|
|
@@ -94,41 +94,41 @@ def colours2: [
|
|
|
94
94
|
# directly because it inherits its frame of reference from its parent. For Viewport, margin will shrink the
|
|
95
95
|
# available area, possibly to zero, at which point nothing will be rendered.
|
|
96
96
|
# Float -> GraphicsElement -> GraphicsElement
|
|
97
|
-
def
|
|
97
|
+
def scale_to_width(w, Rect(x, y, _, h, fill)):
|
|
98
98
|
Rect(x, y, w, h, fill)
|
|
99
|
-
def
|
|
99
|
+
def scale_to_width(w, Viewport(x, y, w0, h, fill, margin, Scale(x_scale, y_scale), translate, g)):
|
|
100
100
|
def scale: Scale((x_scale * w) / w0, y_scale)
|
|
101
101
|
Viewport(x, y, w, h, fill, margin, scale, translate, g)
|
|
102
102
|
|
|
103
103
|
# Float -> List GraphicsElement -> List GraphicsElement
|
|
104
|
-
def
|
|
105
|
-
map(
|
|
104
|
+
def stack_right(sep, gs):
|
|
105
|
+
map(scale_to_width(1 - sep), space_right(sep / 2, 1, gs))
|
|
106
106
|
|
|
107
107
|
# Float -> List GraphicsElement -> GraphicsElement
|
|
108
|
-
def
|
|
109
|
-
Viewport(0, 0, length(gs), maximum(map(height, gs)), "none", 0, Scale(1, 1), Translate(0, 0), Group(
|
|
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
110
|
|
|
111
111
|
# Heuristic saying how often to place a tick on an axis of length n.
|
|
112
112
|
# Float -> Float
|
|
113
|
-
def
|
|
114
|
-
def m: floor(
|
|
113
|
+
def tick_every(n):
|
|
114
|
+
def m: floor(log_base(10, n))
|
|
115
115
|
|
|
116
116
|
if n <= 2 * 10 ** m: 2 * 10 ** (m - 1)
|
|
117
117
|
else: 10 ** m
|
|
118
118
|
|
|
119
|
-
def
|
|
120
|
-
def
|
|
121
|
-
def
|
|
122
|
-
def
|
|
123
|
-
def
|
|
124
|
-
def
|
|
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
125
|
|
|
126
126
|
# Helpers for axis functions.
|
|
127
127
|
# Orient -> Colour -> Float -> GraphicsElement
|
|
128
128
|
def tick(Horiz, colour, len):
|
|
129
|
-
Line(Point(0, 0), Point(0, 0 - len), colour,
|
|
129
|
+
Line(Point(0, 0), Point(0, 0 - len), colour, axis_stroke_width)
|
|
130
130
|
def tick(Vert, colour, len):
|
|
131
|
-
Line(Point(0, 0), Point(0 - len, 0), colour,
|
|
131
|
+
Line(Point(0, 0), Point(0 - len, 0), colour, axis_stroke_width)
|
|
132
132
|
|
|
133
133
|
# Orient -> Float -> Float -> Str -> GraphicsElement
|
|
134
134
|
def label(Horiz, x, distance, str):
|
|
@@ -137,61 +137,61 @@ def label(Vert, x, distance, str):
|
|
|
137
137
|
String(0 - distance, x, str, "end", "central")
|
|
138
138
|
|
|
139
139
|
# Orient -> Colour -> Float -> Str -> GraphicsElement
|
|
140
|
-
def
|
|
140
|
+
def labelled_tick(orient, colour, len, str):
|
|
141
141
|
Group([
|
|
142
142
|
tick(orient, colour, len),
|
|
143
143
|
label(orient, 0, len, str)
|
|
144
144
|
])
|
|
145
145
|
|
|
146
146
|
# Orient -> Float -> Float -> Point
|
|
147
|
-
def
|
|
148
|
-
def
|
|
147
|
+
def mk_point(Horiz, x, y): Point(y, x)
|
|
148
|
+
def mk_point(Vert, x, y): Point(x, y)
|
|
149
149
|
|
|
150
150
|
# x is position of this axis on the other axis. Returns axis and position of last tick.
|
|
151
151
|
# Orient -> Float -> Float -> Float -> GraphicsElement
|
|
152
152
|
def axis(orient, x, start, end):
|
|
153
|
-
def
|
|
154
|
-
def
|
|
155
|
-
def
|
|
156
|
-
def n: floor((end -
|
|
157
|
-
def ys: iterate(n, (+)(
|
|
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
158
|
def ys:
|
|
159
|
-
if
|
|
159
|
+
if first_tick > start: start :| ys
|
|
160
160
|
else: ys
|
|
161
161
|
def ys:
|
|
162
|
-
if
|
|
162
|
+
if last_tick > end: concat2(ys, [last_tick])
|
|
163
163
|
else: ys
|
|
164
|
-
def ps: map(
|
|
164
|
+
def ps: map(mk_point(orient, x), ys)
|
|
165
165
|
def ax:
|
|
166
166
|
Group([
|
|
167
|
-
Line(head(ps), last(ps),
|
|
168
|
-
Polymarkers(ps, flip(map, ys, compose(
|
|
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
169
|
])
|
|
170
170
|
|
|
171
|
-
(ax,
|
|
171
|
+
(ax, last_tick)
|
|
172
172
|
|
|
173
173
|
# x is position of this axis on the other axis.
|
|
174
174
|
# Orient -> Float -> List Cat -> GraphicsElement
|
|
175
|
-
def
|
|
176
|
-
def ys: iterate(length(
|
|
177
|
-
def ps: map(
|
|
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
178
|
|
|
179
179
|
Group([
|
|
180
|
-
Line(head(ps), last(ps),
|
|
181
|
-
Polymarkers(tail(ps), map(const(tick(orient,
|
|
182
|
-
Polymarkers(flip(map, tail(ps), lambda Point(x, y): Point(x - 0.5, y)), map(label(orient, -0.5, 0),
|
|
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
183
|
])
|
|
184
184
|
|
|
185
185
|
# Float -> Float -> Float -> Float -> List GraphicsElement -> GraphicsElement
|
|
186
186
|
def viewport(x_start, x_finish, y_finish, margin, gs):
|
|
187
|
-
Viewport(0, 0, x_finish - x_start, y_finish,
|
|
187
|
+
Viewport(0, 0, x_finish - x_start, y_finish, background_colour, margin, Scale(1, 1), Translate(0 - x_start, 0), Group(gs))
|
|
188
188
|
|
|
189
189
|
# Plot a map of x values to lists of (categorical value, y value) pairs. Importantly, assume all data is uniform
|
|
190
190
|
# (categorical keys are the same for each x value and are ordered the same each time).
|
|
191
191
|
# Bool -> Colours -> Float -> List (Float, List (Cat, Float)) -> GraphicsElement
|
|
192
|
-
def
|
|
192
|
+
def line_chart(with_axes, colours, x_start, data):
|
|
193
193
|
def xs: map(fst, data)
|
|
194
|
-
def
|
|
194
|
+
def n_cat: length(snd(head(data)))
|
|
195
195
|
|
|
196
196
|
# (Int, Colour) -> GraphicsElement
|
|
197
197
|
def plot((n, colour)):
|
|
@@ -200,46 +200,46 @@ def lineChart(withAxes, colours, x_start, data):
|
|
|
200
200
|
|
|
201
201
|
Group([
|
|
202
202
|
Polyline(ps, colour, 1),
|
|
203
|
-
Polymarkers(ps, repeat(length(ps), Circle(0, 0,
|
|
203
|
+
Polymarkers(ps, repeat(length(ps), Circle(0, 0, marker_radius, colour)))
|
|
204
204
|
])
|
|
205
205
|
|
|
206
206
|
# List GraphicsElement
|
|
207
207
|
def lines:
|
|
208
|
-
|
|
208
|
+
zip_with(curry(plot), iterate(n_cat, (+)(1), 0), colours)
|
|
209
209
|
def x_finish: last(xs)
|
|
210
210
|
def y_finish:
|
|
211
211
|
maximum(flip(map, data, lambda (_, kvs): maximum(map(snd, kvs))))
|
|
212
212
|
|
|
213
|
-
if
|
|
213
|
+
if with_axes:
|
|
214
214
|
def (x_axis, x_finish): axis(Horiz, 0, x_start, x_finish)
|
|
215
215
|
def (y_axis, y_finish_): axis(Vert, x_start, 0, y_finish)
|
|
216
|
-
viewport(x_start, x_finish, y_finish_,
|
|
216
|
+
viewport(x_start, x_finish, y_finish_, default_margin, x_axis :| y_axis :| lines)
|
|
217
217
|
else:
|
|
218
218
|
viewport(x_start, x_finish, y_finish, 0, lines)
|
|
219
219
|
|
|
220
220
|
# Plot a chart of categorical values on the x-axis and renderings of the corresponding a-value on the y-axis.
|
|
221
221
|
# (Colours -> List a -> GraphicsElement) -> Bool -> Colours -> Float -> List (Cat, a) -> GraphicsElement
|
|
222
|
-
def
|
|
222
|
+
def categorical_chart(plot_value, with_axes, colours, sep, data):
|
|
223
223
|
def gs:
|
|
224
|
-
|
|
224
|
+
stack_right(sep, plot_value(colours, map(snd, data)))
|
|
225
225
|
def w: length(gs)
|
|
226
226
|
def h: maximum(map(height, gs))
|
|
227
227
|
|
|
228
|
-
if
|
|
229
|
-
def x_axis:
|
|
228
|
+
if with_axes:
|
|
229
|
+
def x_axis: cat_axis(Horiz, 0, map(fst, data))
|
|
230
230
|
def (y_axis, h_): axis(Vert, 0, 0, h)
|
|
231
|
-
viewport(0, w, h_,
|
|
231
|
+
viewport(0, w, h_, default_margin, concat2(gs, [x_axis, y_axis]))
|
|
232
232
|
else:
|
|
233
233
|
viewport(0, w, h, 0, gs)
|
|
234
234
|
|
|
235
235
|
# Colours -> List a -> GraphicsElement
|
|
236
236
|
def rects(colours, ns):
|
|
237
|
-
|
|
237
|
+
zip_with(lambda colour, n: Rect(0, 0, 1, n, colour), colours, ns)
|
|
238
238
|
|
|
239
239
|
# First component of data (categorical value) currently ignored; values just mapped positionally to colors.
|
|
240
240
|
# Can we use Group instead of Viewport here?
|
|
241
241
|
# Colours -> List (a, Num) -> GraphicsElement
|
|
242
|
-
def
|
|
242
|
+
def stacked_bar(colours, ns):
|
|
243
243
|
def heights: map(snd, ns)
|
|
244
244
|
def subtotals: scanl1((+), 0, heights)
|
|
245
245
|
def dims: zip(0 :| subtotals, heights)
|
|
@@ -249,18 +249,18 @@ def stackedBar(colours, ns):
|
|
|
249
249
|
Viewport(0, 0, 1, last(subtotals), "none", 0, Scale(1, 1), Translate(0, 0), Group(rects))
|
|
250
250
|
|
|
251
251
|
# Bool -> Colours -> Float -> List (a, Float) -> GraphicsElement
|
|
252
|
-
def
|
|
253
|
-
|
|
252
|
+
def bar_chart:
|
|
253
|
+
categorical_chart(rects)
|
|
254
254
|
|
|
255
255
|
# For each categorical value of type a, plot a bar chart for the corresponding b-indexed data.
|
|
256
256
|
# Bool -> Colours -> Float -> List (a, List (b, Float)) -> GraphicsElement
|
|
257
|
-
def
|
|
258
|
-
|
|
257
|
+
def grouped_bar_chart:
|
|
258
|
+
categorical_chart(compose(map, flip(bar_chart(False), 0)))
|
|
259
259
|
|
|
260
|
-
# See
|
|
260
|
+
# See stacked_bar for strong (unjustified) assumption about uniformity of data.
|
|
261
261
|
# Bool -> Colours -> Num -> List (a, List (b, Num)) -> GraphicsElement
|
|
262
|
-
def
|
|
263
|
-
|
|
262
|
+
def stacked_bar_chart:
|
|
263
|
+
categorical_chart(compose(map, stacked_bar))
|
|
264
264
|
|
|
265
265
|
# Bit of a hack, but how text fits into our model is a bit unclear at the moment.
|
|
266
266
|
# Str -> GraphicsElement -> GraphicsElement
|
|
@@ -271,4 +271,4 @@ def caption(str, Viewport(x, y, w, h, fill, margin, scale, translate, g)):
|
|
|
271
271
|
Viewport(0, 0, w, h, fill, margin, scale, translate, g)
|
|
272
272
|
])
|
|
273
273
|
|
|
274
|
-
Viewport(x, y, w, h,
|
|
274
|
+
Viewport(x, y, w, h, background_colour, default_margin / 2 + 4, Scale(1, 1), Translate(0, 0), g_)
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
def zero(m, n, image):
|
|
2
|
-
def (
|
|
3
|
-
if m >=
|
|
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
4
|
else: 0
|
|
5
5
|
|
|
6
6
|
def wrap(m, n, image):
|
|
7
|
-
def (
|
|
8
|
-
image ! (
|
|
7
|
+
def (m_size, n_size): dims(image)
|
|
8
|
+
image ! (m % m_size, n % n_size)
|
|
9
9
|
|
|
10
10
|
def extend(m, n, image):
|
|
11
|
-
def (
|
|
12
|
-
def
|
|
13
|
-
min(max(m,
|
|
14
|
-
def
|
|
15
|
-
min(max(n,
|
|
16
|
-
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
17
|
|
|
18
|
-
def
|
|
18
|
+
def matrix_sum(matr):
|
|
19
19
|
def (m, n): dims(matr)
|
|
20
|
-
foldl((+), 0, [matr ! (i, j) for (i, j) in
|
|
20
|
+
foldl((+), 0, [matr ! (i, j) for (i, j) in range2d((0, 0), (m, n))])
|
|
21
21
|
|
|
22
22
|
def convolve(image, kernel, lookup):
|
|
23
23
|
def ((m, n), (i, j)):
|
|
@@ -25,16 +25,16 @@ def convolve(image, kernel, lookup):
|
|
|
25
25
|
def (half_i, half_j):
|
|
26
26
|
(i |quot| 2, j |quot| 2)
|
|
27
27
|
def area: i * j
|
|
28
|
-
def interMatrix(
|
|
29
|
-
[| lookup(
|
|
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
30
|
|
|
31
|
-
[|
|
|
31
|
+
[| matrix_sum(interMatrix(m_, n_)) |quot| area for (m_, n_) in (m, n) |]
|
|
32
32
|
|
|
33
|
-
def
|
|
33
|
+
def mat_mul(a, b):
|
|
34
34
|
def ((m, n), (i, j)): (dims(a), dims(b))
|
|
35
35
|
|
|
36
36
|
if not(n == i):
|
|
37
37
|
error("Dimensions don't line up")
|
|
38
38
|
else:
|
|
39
39
|
@doc(f"""Intermediate matrix""")
|
|
40
|
-
[| sum([a!(i_, k) * b!(k, j_) for k in
|
|
40
|
+
[| sum([a!(i_, k) * b!(k, j_) for k in range(0, n)]) for (i_, j_) in (m, j) |]
|
|
@@ -23,18 +23,18 @@ def compare(x, y):
|
|
|
23
23
|
else: EQ
|
|
24
24
|
|
|
25
25
|
# (b -> b -> c) -> (a -> b) -> a -> a -> c
|
|
26
|
-
def on(
|
|
27
|
-
|
|
26
|
+
def on(bin_op, prop, x, y):
|
|
27
|
+
bin_op(prop(x), prop(y))
|
|
28
28
|
|
|
29
29
|
# Num -> Num
|
|
30
30
|
def negate: (-)(0)
|
|
31
31
|
|
|
32
32
|
# Log of x in base y.
|
|
33
33
|
# Float -> Float -> Float
|
|
34
|
-
def
|
|
34
|
+
def log_base(x, y): log(y) / log(x)
|
|
35
35
|
|
|
36
36
|
# Float -> Float -> Float
|
|
37
|
-
def
|
|
37
|
+
def ceiling_to_nearest(n, m): ceiling(n / m) * m
|
|
38
38
|
|
|
39
39
|
# (b -> c) -> (a -> b) -> a -> c
|
|
40
40
|
# Want infix <<<
|
|
@@ -87,7 +87,7 @@ def tail(_ :| xs): xs
|
|
|
87
87
|
# Eq a => a -> List a -> Bool
|
|
88
88
|
def elem(x, []): False
|
|
89
89
|
def elem(x, y :| xs):
|
|
90
|
-
x == y
|
|
90
|
+
x == y or elem(x, xs)
|
|
91
91
|
|
|
92
92
|
# (a -> Bool) -> List a -> Option a
|
|
93
93
|
def find(p, []): None
|
|
@@ -96,16 +96,16 @@ def find(p, x :| xs):
|
|
|
96
96
|
else: find(p, xs)
|
|
97
97
|
|
|
98
98
|
# String -> String -> List Dict -> Option Dict
|
|
99
|
-
def
|
|
99
|
+
def find_with_key(k, v, rs):
|
|
100
100
|
find(lambda y: y[k] == v, rs)
|
|
101
101
|
|
|
102
102
|
# Option a -> a
|
|
103
|
-
def
|
|
104
|
-
def
|
|
103
|
+
def from_some(None): error("Expected Some!")
|
|
104
|
+
def from_some(Some(x)): x
|
|
105
105
|
|
|
106
106
|
# Option a -> a -> a
|
|
107
|
-
def
|
|
108
|
-
def
|
|
107
|
+
def from_option(None, y): y
|
|
108
|
+
def from_option(Some(x), _): x
|
|
109
109
|
|
|
110
110
|
# (a -> Bool) -> List a -> List a
|
|
111
111
|
def filter(p, []): []
|
|
@@ -116,12 +116,12 @@ def filter(p, x :| xs):
|
|
|
116
116
|
else: ys
|
|
117
117
|
|
|
118
118
|
# (a -> Option b) -> List a -> List b
|
|
119
|
-
def
|
|
120
|
-
def
|
|
119
|
+
def filter_map(p, []): []
|
|
120
|
+
def filter_map(p, x :| xs):
|
|
121
121
|
match p(x):
|
|
122
|
-
case None:
|
|
122
|
+
case None: filter_map(f, xs)
|
|
123
123
|
case Some(y):
|
|
124
|
-
y :|
|
|
124
|
+
y :| filter_map(f, xs)
|
|
125
125
|
|
|
126
126
|
# Equivalent to but more efficient than length ∘ filter(p)
|
|
127
127
|
def count_if(p):
|
|
@@ -142,10 +142,10 @@ def span(p, x :| xs_):
|
|
|
142
142
|
|
|
143
143
|
# Each returned list is non-empty.
|
|
144
144
|
# (a -> a -> Bool) -> List a -> List (List a)
|
|
145
|
-
def
|
|
146
|
-
def
|
|
145
|
+
def group_by(eq, Nil): Nil
|
|
146
|
+
def group_by(eq, x :| xs):
|
|
147
147
|
def { init: ys, rest: zs }: span(eq(x), xs)
|
|
148
|
-
(x :| ys) :|
|
|
148
|
+
(x :| ys) :| group_by(eq, zs)
|
|
149
149
|
|
|
150
150
|
# (a -> b -> a) -> a -> List b -> a
|
|
151
151
|
def foldl(op, z, []): z
|
|
@@ -192,10 +192,13 @@ def concat2(x :| xs, ys):
|
|
|
192
192
|
x :| concat2(xs, ys)
|
|
193
193
|
|
|
194
194
|
# List (List a) -> List a
|
|
195
|
-
def concat:
|
|
195
|
+
def concat: foldr(concat2, [])
|
|
196
196
|
|
|
197
197
|
# (a -> List b) -> List a -> List b
|
|
198
|
-
def
|
|
198
|
+
def concat_map(f, xs): concat(map(f, xs))
|
|
199
|
+
|
|
200
|
+
# List String -> String
|
|
201
|
+
def join: foldr((++), "")
|
|
199
202
|
|
|
200
203
|
# List a -> a -> List a
|
|
201
204
|
def intersperse([], _): []
|
|
@@ -210,7 +213,7 @@ def iterate(n, f, z):
|
|
|
210
213
|
z :| map(f, iterate(n - 1, f, z))
|
|
211
214
|
|
|
212
215
|
# List Num -> Num
|
|
213
|
-
def sum:
|
|
216
|
+
def sum: foldr((+), 0)
|
|
214
217
|
|
|
215
218
|
# List Num -> Num
|
|
216
219
|
def mean(xs):
|
|
@@ -251,7 +254,7 @@ def drop(n, xs):
|
|
|
251
254
|
case _ :| xs: drop(n - 1, xs)
|
|
252
255
|
|
|
253
256
|
# Int -> List a -> List a
|
|
254
|
-
def
|
|
257
|
+
def last_n(n, xs):
|
|
255
258
|
foldl(compose(const, drop(1)), xs, drop(n, xs))
|
|
256
259
|
|
|
257
260
|
# Expects non-negative integer as first argument and non-empty list as second argument.
|
|
@@ -260,9 +263,9 @@ def nth(n, x :| xs):
|
|
|
260
263
|
if n == 0: x
|
|
261
264
|
else: nth(n - 1, xs)
|
|
262
265
|
|
|
263
|
-
#
|
|
266
|
+
# Int -> Int -> List (List Int) -> Int
|
|
264
267
|
def nth2(i, j, xss):
|
|
265
|
-
nth(j
|
|
268
|
+
nth(j, nth(i, xss))
|
|
266
269
|
|
|
267
270
|
# Partial; requires k to be in the map.
|
|
268
271
|
# Int -> List (Int, b) -> b
|
|
@@ -295,24 +298,22 @@ def unzip((x, y) :| zs):
|
|
|
295
298
|
(x :| xs, y :| ys)
|
|
296
299
|
|
|
297
300
|
# (a -> b -> c) -> List a -> List b -> List c
|
|
298
|
-
def
|
|
299
|
-
def
|
|
300
|
-
def
|
|
301
|
-
op(x, y) :|
|
|
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)
|
|
302
305
|
|
|
303
306
|
# List a -> List b -> List (a, b)
|
|
304
|
-
def zip:
|
|
307
|
+
def zip: zip_with(curry(id))
|
|
305
308
|
|
|
306
|
-
# Rename to 'range'
|
|
307
309
|
# Int -> Int -> List Int
|
|
308
|
-
def
|
|
309
|
-
if n
|
|
310
|
+
def range(n, m):
|
|
311
|
+
if n < m: n :| range(n + 1, m)
|
|
310
312
|
else: []
|
|
311
313
|
|
|
312
|
-
# Rename to 'range2'
|
|
313
314
|
# (Int, Int) -> (Int, Int) -> List (Int, Int)
|
|
314
|
-
def
|
|
315
|
-
[(i1, i2) for i1 in
|
|
315
|
+
def range2d((m1, n1), (m2, n2)):
|
|
316
|
+
[(i1, i2) for i1 in range(m1, m2) for i2 in range(n1, n2)]
|
|
316
317
|
|
|
317
318
|
# Int -> Int -> Int
|
|
318
319
|
def abs(x, y):
|
|
@@ -335,7 +336,7 @@ def slice(begin, end, xs):
|
|
|
335
336
|
take(end - begin, drop(begin, xs))
|
|
336
337
|
|
|
337
338
|
# (a -> Boolean) -> List a -> (List a, List a)
|
|
338
|
-
def
|
|
339
|
+
def split_on(p, data):
|
|
339
340
|
def go(fls, trs, []):
|
|
340
341
|
(reverse(fls), reverse(trs))
|
|
341
342
|
def go(fls, trs, x :| xs):
|
|
@@ -17,7 +17,7 @@ def mergesort(xs):
|
|
|
17
17
|
def (ys, zs): split(xs)
|
|
18
18
|
merge(mergesort(ys), mergesort(zs))
|
|
19
19
|
|
|
20
|
-
def
|
|
20
|
+
def find_quantile(q, p, xs):
|
|
21
21
|
def rank:
|
|
22
22
|
(p / q) * (length(xs) - 1)
|
|
23
23
|
|
|
@@ -30,49 +30,49 @@ def findQuantile(q, p, xs):
|
|
|
30
30
|
def right: nth(x2, xs)
|
|
31
31
|
left + (rank - x1) * (right - left)
|
|
32
32
|
|
|
33
|
-
def
|
|
33
|
+
def find_percentile: find_quantile(100)
|
|
34
34
|
|
|
35
|
-
def
|
|
36
|
-
def
|
|
37
|
-
def
|
|
35
|
+
def accum_bins(data, Nil): []
|
|
36
|
+
def accum_bins(data, [l]): []
|
|
37
|
+
def accum_bins(data, l :| r :| es):
|
|
38
38
|
def (ge, le):
|
|
39
|
-
|
|
40
|
-
(le, r - l) :|
|
|
39
|
+
split_on(lambda x: x <= r, data)
|
|
40
|
+
(le, r - l) :| accum_bins(ge, r :| es)
|
|
41
41
|
|
|
42
42
|
def cut(xs, nbins):
|
|
43
43
|
def low: minimum(xs)
|
|
44
44
|
def binwidth:
|
|
45
45
|
(maximum(xs) - low) / nbins
|
|
46
46
|
def edges:
|
|
47
|
-
[low + x * binwidth for x in
|
|
47
|
+
[low + x * binwidth for x in [0 .. nbins]]
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
accum_bins(xs, edges)
|
|
50
50
|
|
|
51
51
|
def qcut(xs, qs):
|
|
52
52
|
def (low, high):
|
|
53
53
|
(minimum(xs), maximum(xs))
|
|
54
54
|
def edges:
|
|
55
|
-
append((low :| [
|
|
55
|
+
append((low :| [find_percentile(x, xs) for x in qs], [high]))
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
accum_bins(xs, edges)
|
|
58
58
|
|
|
59
|
-
def
|
|
59
|
+
def likelihood_le(xs, target):
|
|
60
60
|
length(filter(lambda x: x <= target, xs)) / length(xs)
|
|
61
61
|
|
|
62
|
-
def
|
|
62
|
+
def likelihood_ge(xs, target):
|
|
63
63
|
length(filter(lambda x: x >= target, xs)) / length(xs)
|
|
64
64
|
|
|
65
|
-
def
|
|
66
|
-
|
|
65
|
+
def likelihood_map(table, prob):
|
|
66
|
+
from_some(find(lambda x: x.prob <= prob, table)).msg
|
|
67
67
|
|
|
68
|
-
def
|
|
69
|
-
|
|
68
|
+
def mk_percent(num):
|
|
69
|
+
num_to_str(num * 100) ++ "%"
|
|
70
70
|
|
|
71
|
-
def
|
|
71
|
+
def leq_p(n, m):
|
|
72
72
|
if n <= m: "less"
|
|
73
73
|
else: "more"
|
|
74
74
|
|
|
75
|
-
def
|
|
75
|
+
def graded_leq_p(n, m):
|
|
76
76
|
def ratio: n / m
|
|
77
77
|
|
|
78
78
|
if ratio <= 1.0:
|