@trebco/treb 31.9.1 → 32.3.1
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/treb-spreadsheet.mjs +15 -15
- package/i18n/languages/treb-i18n-da.mjs +1 -0
- package/i18n/languages/treb-i18n-de.mjs +1 -0
- package/i18n/languages/treb-i18n-es.mjs +1 -0
- package/i18n/languages/treb-i18n-fr.mjs +1 -0
- package/i18n/languages/treb-i18n-it.mjs +1 -0
- package/i18n/languages/treb-i18n-nl.mjs +1 -0
- package/i18n/languages/treb-i18n-no.mjs +1 -0
- package/i18n/languages/treb-i18n-pl.mjs +1 -0
- package/i18n/languages/treb-i18n-pt.mjs +1 -0
- package/i18n/languages/treb-i18n-sv.mjs +1 -0
- package/package.json +1 -1
- package/treb-base-types/src/style.ts +20 -0
- package/treb-base-types/src/union.ts +6 -0
- package/treb-base-types/src/value-type.ts +13 -0
- package/treb-calculator/src/calculator.ts +20 -1
- package/treb-calculator/src/descriptors.ts +49 -4
- package/treb-calculator/src/expression-calculator.ts +263 -12
- package/treb-calculator/src/functions/base-functions.ts +49 -0
- package/treb-calculator/src/functions/fp.ts +339 -0
- package/treb-calculator/src/functions/gamma.ts +143 -0
- package/treb-calculator/src/functions/lambda-functions.ts +96 -0
- package/treb-calculator/src/functions/statistics-functions.ts +88 -0
- package/treb-data-model/src/data_model.ts +28 -13
- package/treb-data-model/src/named.ts +7 -0
- package/treb-data-model/src/sheet.ts +19 -2
- package/treb-embed/src/embedded-spreadsheet.ts +22 -5
- package/treb-embed/style/theme-defaults.scss +0 -22
- package/treb-grid/src/editors/editor.ts +14 -0
- package/treb-grid/src/types/grid.ts +74 -28
- package/treb-parser/src/parser-types.ts +24 -0
- package/treb-parser/src/parser.ts +157 -223
- package/dist/treb-export-worker.mjs +0 -2
- package/dist/treb.d.ts +0 -2235
- package/treb-calculator/tsconfig.json +0 -7
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* functional programming
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { FunctionMap } from '../descriptors';
|
|
6
|
+
import type { FunctionUnion, UnionValue} from 'treb-base-types';
|
|
7
|
+
import { ValueType } from 'treb-base-types';
|
|
8
|
+
import { ArgumentError, ValueError } from '../function-error';
|
|
9
|
+
|
|
10
|
+
export const FPFunctionLibrary: FunctionMap = {
|
|
11
|
+
|
|
12
|
+
MakeArray: {
|
|
13
|
+
description: 'Create an array using a function',
|
|
14
|
+
arguments: [
|
|
15
|
+
{ name: 'rows' },
|
|
16
|
+
{ name: 'columns' },
|
|
17
|
+
{
|
|
18
|
+
name: 'lambda',
|
|
19
|
+
description: 'Function to apply',
|
|
20
|
+
boxed: true,
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
fp: true,
|
|
24
|
+
fn: function(rows: number, columns: number, lambda: FunctionUnion) {
|
|
25
|
+
if (rows > 0 && columns > 0 && lambda?.type === ValueType.function) {
|
|
26
|
+
const apply = this?.apply;
|
|
27
|
+
if (!apply) {
|
|
28
|
+
return ValueError();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const value: UnionValue[][] = [];
|
|
32
|
+
for (let c = 0; c < columns; c++) {
|
|
33
|
+
const values_col: UnionValue[] = [];
|
|
34
|
+
for (let r = 0; r < rows; r++) {
|
|
35
|
+
values_col.push(apply(lambda, [
|
|
36
|
+
{ type: ValueType.number, value: r + 1 },
|
|
37
|
+
{ type: ValueType.number, value: c + 1 },
|
|
38
|
+
]));
|
|
39
|
+
}
|
|
40
|
+
value.push(values_col);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
type: ValueType.array,
|
|
45
|
+
value,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
return ArgumentError();
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
Reduce: {
|
|
54
|
+
description: 'Accumulates a value by applying a function to a set of values',
|
|
55
|
+
arguments: [
|
|
56
|
+
{
|
|
57
|
+
name: 'initial value',
|
|
58
|
+
boxed: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'data',
|
|
62
|
+
description: 'Input data',
|
|
63
|
+
boxed: true,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'lambda',
|
|
67
|
+
description: 'Function to apply',
|
|
68
|
+
boxed: true,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
fp: true,
|
|
72
|
+
fn: function(initial: UnionValue, data: UnionValue, lambda: FunctionUnion) {
|
|
73
|
+
|
|
74
|
+
if (!this?.apply) {
|
|
75
|
+
return ValueError();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (lambda.type !== ValueType.function) {
|
|
79
|
+
return ArgumentError();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (data.type !== ValueType.array) {
|
|
83
|
+
data = {
|
|
84
|
+
type: ValueType.array,
|
|
85
|
+
value: [[data]],
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (let r = 0; r < data.value.length; r++) {
|
|
90
|
+
const row = data.value[r];
|
|
91
|
+
for (let c = 0; c < row.length; c++) {
|
|
92
|
+
const apply_args: UnionValue[] = [initial, row[c]];
|
|
93
|
+
const result = this.apply(lambda, apply_args)
|
|
94
|
+
initial = result;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return initial;
|
|
99
|
+
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
Scan: {
|
|
104
|
+
description: 'Applies a function to a set of values, iteratively',
|
|
105
|
+
arguments: [
|
|
106
|
+
{
|
|
107
|
+
name: 'initial value',
|
|
108
|
+
boxed: true,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'data',
|
|
112
|
+
description: 'Input data',
|
|
113
|
+
boxed: true,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'lambda',
|
|
117
|
+
description: 'Function to apply',
|
|
118
|
+
boxed: true,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
fp: true,
|
|
122
|
+
fn: function(initial: UnionValue, data: UnionValue, lambda: FunctionUnion) {
|
|
123
|
+
|
|
124
|
+
if (!this?.apply) {
|
|
125
|
+
return ValueError();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (lambda.type !== ValueType.function) {
|
|
129
|
+
return ArgumentError();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (data.type !== ValueType.array) {
|
|
133
|
+
data = {
|
|
134
|
+
type: ValueType.array,
|
|
135
|
+
value: [[data]],
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const results: UnionValue[][] = [];
|
|
140
|
+
for (let r = 0; r < data.value.length; r++) {
|
|
141
|
+
const row = data.value[r];
|
|
142
|
+
const results_row: UnionValue[] = [];
|
|
143
|
+
for (let c = 0; c < row.length; c++) {
|
|
144
|
+
const apply_args: UnionValue[] = [initial, row[c]];
|
|
145
|
+
const result = this.apply(lambda, apply_args)
|
|
146
|
+
results_row.push(result);
|
|
147
|
+
initial = result;
|
|
148
|
+
}
|
|
149
|
+
results.push(results_row);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
type: ValueType.array,
|
|
154
|
+
value: results,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
ByRow: {
|
|
161
|
+
description: 'Apply a function to each row in an array',
|
|
162
|
+
arguments: [
|
|
163
|
+
{
|
|
164
|
+
name: 'data',
|
|
165
|
+
description: 'Input data',
|
|
166
|
+
repeat: true,
|
|
167
|
+
boxed: true,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'lambda',
|
|
171
|
+
description: 'Function to apply',
|
|
172
|
+
boxed: true,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
fp: true,
|
|
176
|
+
fn: function(data: UnionValue, lambda: FunctionUnion) {
|
|
177
|
+
if (!this?.apply) { return ValueError(); }
|
|
178
|
+
|
|
179
|
+
if (lambda.type !== ValueType.function) {
|
|
180
|
+
return ArgumentError();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (data.type !== ValueType.array) {
|
|
184
|
+
data = {
|
|
185
|
+
type: ValueType.array,
|
|
186
|
+
value: [[data]],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const cols = data.value.length;
|
|
191
|
+
const rows = data.value[0].length;
|
|
192
|
+
|
|
193
|
+
const value: UnionValue[][] = [[]];
|
|
194
|
+
for (let r = 0; r < rows; r++) {
|
|
195
|
+
const args: UnionValue[] = [];
|
|
196
|
+
for (let c = 0; c < cols; c++) {
|
|
197
|
+
args.push(data.value[c][r]);
|
|
198
|
+
}
|
|
199
|
+
value[0].push(this.apply(lambda, [{
|
|
200
|
+
type: ValueType.array, value: [args],
|
|
201
|
+
}]));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
type: ValueType.array,
|
|
206
|
+
value,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
ByCol: {
|
|
214
|
+
description: 'Apply a function to each column in an array',
|
|
215
|
+
arguments: [
|
|
216
|
+
{
|
|
217
|
+
name: 'data',
|
|
218
|
+
description: 'Input data',
|
|
219
|
+
repeat: true,
|
|
220
|
+
boxed: true,
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: 'lambda',
|
|
224
|
+
description: 'Function to apply',
|
|
225
|
+
boxed: true,
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
fp: true,
|
|
229
|
+
fn: function(data: UnionValue, lambda: FunctionUnion) {
|
|
230
|
+
if (!this?.apply) { return ValueError(); }
|
|
231
|
+
|
|
232
|
+
if (lambda.type !== ValueType.function) {
|
|
233
|
+
return ArgumentError();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (data.type !== ValueType.array) {
|
|
237
|
+
data = {
|
|
238
|
+
type: ValueType.array,
|
|
239
|
+
value: [[data]],
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const cols = data.value.length;
|
|
244
|
+
const rows = data.value[0].length;
|
|
245
|
+
|
|
246
|
+
const value: UnionValue[][] = [];
|
|
247
|
+
for (let c = 0; c < cols; c++) {
|
|
248
|
+
const args: UnionValue[] = [];
|
|
249
|
+
for (let r = 0; r < rows; r++) {
|
|
250
|
+
args.push(data.value[c][r]);
|
|
251
|
+
}
|
|
252
|
+
value.push([this.apply(lambda, [{
|
|
253
|
+
type: ValueType.array, value: [args],
|
|
254
|
+
}])]);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
type: ValueType.array,
|
|
259
|
+
value,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
Map: {
|
|
266
|
+
description: 'Apply a function to a set of values',
|
|
267
|
+
arguments: [
|
|
268
|
+
{
|
|
269
|
+
name: 'data',
|
|
270
|
+
description: 'Input data',
|
|
271
|
+
repeat: true,
|
|
272
|
+
boxed: true,
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: 'lambda',
|
|
276
|
+
description: 'Function to apply',
|
|
277
|
+
boxed: true,
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
|
|
281
|
+
fp: true,
|
|
282
|
+
fn: function(...args: UnionValue[]) {
|
|
283
|
+
|
|
284
|
+
if (args.length < 2) {
|
|
285
|
+
return ArgumentError();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const lambda = args[args.length - 1];
|
|
289
|
+
if (lambda.type !== ValueType.function) {
|
|
290
|
+
return ArgumentError();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const apply = this?.apply;
|
|
294
|
+
if (!apply) {
|
|
295
|
+
return ValueError();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
for (let i = 0; i < args.length - 1; i++) {
|
|
299
|
+
const arg = args[i];
|
|
300
|
+
if (arg.type !== ValueType.array) {
|
|
301
|
+
args[i] = {
|
|
302
|
+
type: ValueType.array,
|
|
303
|
+
value: [[arg]],
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const key = args[0];
|
|
309
|
+
const results: UnionValue[][] = [];
|
|
310
|
+
if (key.type === ValueType.array) {
|
|
311
|
+
for (let r = 0; r < key.value.length; r++) {
|
|
312
|
+
const row = key.value[r];
|
|
313
|
+
const results_row: UnionValue[] = [];
|
|
314
|
+
for (let c = 0; c < row.length; c++) {
|
|
315
|
+
const apply_args: UnionValue[] = [row[c]];
|
|
316
|
+
|
|
317
|
+
for (let i = 1; i < args.length - 1; i++) {
|
|
318
|
+
const arg = args[i];
|
|
319
|
+
if (arg.type === ValueType.array) {
|
|
320
|
+
apply_args.push(arg.value[r][c])
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
results_row.push(apply(lambda, apply_args));
|
|
325
|
+
}
|
|
326
|
+
results.push(results_row);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
type: ValueType.array,
|
|
332
|
+
value: results,
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* functions and constants for gamma distribution. we're
|
|
3
|
+
* returning false instead of throwing so we can return
|
|
4
|
+
* a spreadsheet-style eror. TODO: optional?
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const max_iterations = 1000;
|
|
8
|
+
const epsilon = 3.0e-9;
|
|
9
|
+
const min_value = 1.0e-30;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* faster approximation for real numbers
|
|
13
|
+
*/
|
|
14
|
+
export const gamma_ln = (z: number): number => {
|
|
15
|
+
|
|
16
|
+
let x = z - 1.0;
|
|
17
|
+
let y = x + 5.5;
|
|
18
|
+
y -= (x + 0.5) * Math.log(y);
|
|
19
|
+
let a = 1.0;
|
|
20
|
+
|
|
21
|
+
const coefficients = [
|
|
22
|
+
76.18009173,
|
|
23
|
+
-86.50532033,
|
|
24
|
+
24.01409822,
|
|
25
|
+
-1.231739516,
|
|
26
|
+
0.120858003e-2,
|
|
27
|
+
-0.536382e-5,
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
for (const coeff of coefficients) {
|
|
31
|
+
a += coeff / (++x);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (-y + Math.log(2.50662827465 * a));
|
|
35
|
+
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* series representation
|
|
40
|
+
*/
|
|
41
|
+
export const gamma_series = (a: number, x: number): number|false => {
|
|
42
|
+
|
|
43
|
+
const gamma_ln_a = gamma_ln(a);
|
|
44
|
+
|
|
45
|
+
if (x === 0) {
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let ap = a;
|
|
50
|
+
let sum = 1.0 / a;
|
|
51
|
+
let del = sum;
|
|
52
|
+
for (let n = 1; n < max_iterations; n++) {
|
|
53
|
+
++ap;
|
|
54
|
+
del *= x / ap;
|
|
55
|
+
sum += del;
|
|
56
|
+
if (Math.abs(del) < Math.abs(sum) * epsilon) {
|
|
57
|
+
return sum * Math.exp(-x + a * Math.log(x) - (gamma_ln_a));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// throw new Error('too many iterations');
|
|
62
|
+
console.error('too many iterations');
|
|
63
|
+
return false;
|
|
64
|
+
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* continued fraction
|
|
69
|
+
*/
|
|
70
|
+
export const gamma_cf = (a: number, x: number): number|false => {
|
|
71
|
+
|
|
72
|
+
const gamma_ln_a = gamma_ln(a);
|
|
73
|
+
|
|
74
|
+
let b = x + 1.0 - a;
|
|
75
|
+
let c = 1.0 / min_value;
|
|
76
|
+
let d = 1.0 / b;
|
|
77
|
+
let h = d;
|
|
78
|
+
|
|
79
|
+
for (let i = 1; i <= max_iterations; i++) {
|
|
80
|
+
|
|
81
|
+
const an = -i * (i - a); b += 2.0; d = an * d + b;
|
|
82
|
+
|
|
83
|
+
if (Math.abs(d) < min_value) d = min_value;
|
|
84
|
+
c = b + an / c;
|
|
85
|
+
|
|
86
|
+
if (Math.abs(c) < min_value) c = min_value;
|
|
87
|
+
|
|
88
|
+
d = 1.0 / d;
|
|
89
|
+
const del = d * c;
|
|
90
|
+
h *= del;
|
|
91
|
+
|
|
92
|
+
if (Math.abs(del - 1.0) < epsilon) {
|
|
93
|
+
return Math.exp(-x + a * Math.log(x) - (gamma_ln_a)) * h;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// throw new Error('too many iterations');
|
|
99
|
+
console.error('too many iterations');
|
|
100
|
+
return false;
|
|
101
|
+
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* regularized gamma function; CDF of gamma distribution with scale 1
|
|
106
|
+
*/
|
|
107
|
+
export const gamma_p = (a: number, x: number) => {
|
|
108
|
+
|
|
109
|
+
if (x < 0.0 || a <= 0.0) {
|
|
110
|
+
// throw new Error('invalid parameter');
|
|
111
|
+
console.error('invalid parameter');
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
if (x < (a + 1.0)) {
|
|
115
|
+
return gamma_series(a, x);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const cf = gamma_cf(a, x);
|
|
119
|
+
if (cf === false) { return false; }
|
|
120
|
+
return 1 - cf;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* regularized gamma function
|
|
127
|
+
*/
|
|
128
|
+
export const gamma_q = (a: number, x: number) => {
|
|
129
|
+
|
|
130
|
+
if (x < 0.0 || a <= 0.0) {
|
|
131
|
+
// throw new Error('invalid parameter');
|
|
132
|
+
console.error('invalid parameter');
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (x < (a + 1.0)) {
|
|
136
|
+
const series = gamma_series(a, x);
|
|
137
|
+
if (series === false) { return false; }
|
|
138
|
+
return 1 - series;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
return gamma_cf(a, x);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
|
|
2
|
+
import type { FunctionMap } from '../descriptors';
|
|
3
|
+
import type { UnionValue} from 'treb-base-types';
|
|
4
|
+
import { ValueType } from 'treb-base-types';
|
|
5
|
+
import type { ExpressionUnit } from 'treb-parser';
|
|
6
|
+
|
|
7
|
+
export const LambdaFunctionLibrary: FunctionMap = {
|
|
8
|
+
|
|
9
|
+
Lambda: {
|
|
10
|
+
|
|
11
|
+
// FIXME: we could use the create_binding_context callback
|
|
12
|
+
// here to validate the original parameters, might be useful
|
|
13
|
+
|
|
14
|
+
description: 'Creates a function',
|
|
15
|
+
arguments: [
|
|
16
|
+
{ name: 'Argument', repeat: true, boxed: true, passthrough: true },
|
|
17
|
+
{ name: 'Function', boxed: true, passthrough: true },
|
|
18
|
+
],
|
|
19
|
+
|
|
20
|
+
fn: (...args: ExpressionUnit[]) => {
|
|
21
|
+
return {
|
|
22
|
+
type: ValueType.function,
|
|
23
|
+
|
|
24
|
+
// FIXME: lock down this type
|
|
25
|
+
|
|
26
|
+
value: {
|
|
27
|
+
|
|
28
|
+
// we should probably clone these
|
|
29
|
+
|
|
30
|
+
bindings: args.slice(0, args.length - 1),
|
|
31
|
+
func: args[args.length - 1],
|
|
32
|
+
|
|
33
|
+
alt: 'LAMBDA', // metadata
|
|
34
|
+
type: 'function', // metadata
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
Let: {
|
|
43
|
+
|
|
44
|
+
// this function has weird semantics we don't normally see --
|
|
45
|
+
// the first two arguments repeat, and the last one is constant.
|
|
46
|
+
// our tooltips aren't prepared for this.
|
|
47
|
+
|
|
48
|
+
// also it creates temporary names, we'll need to create some sort
|
|
49
|
+
// of temporary binding context. complicated!
|
|
50
|
+
|
|
51
|
+
description: 'Binds values to names for a calculation',
|
|
52
|
+
arguments: [
|
|
53
|
+
{ name: 'Name', repeat: true },
|
|
54
|
+
{ name: 'Value', repeat: true },
|
|
55
|
+
{ name: 'Calculation', boxed: true },
|
|
56
|
+
],
|
|
57
|
+
|
|
58
|
+
create_binding_context: ({args, descriptors}) => {
|
|
59
|
+
|
|
60
|
+
if (args.length % 2 !== 1) {
|
|
61
|
+
return undefined; // error
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const context: Record<string, ExpressionUnit> = {};
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < args.length - 1; i += 2) {
|
|
67
|
+
const name = args[i];
|
|
68
|
+
const value = args[i+1];
|
|
69
|
+
|
|
70
|
+
if (name.type === 'identifier') {
|
|
71
|
+
context[name.name] = value;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
return undefined; // error
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
context,
|
|
81
|
+
args: args.slice(-1),
|
|
82
|
+
argument_descriptors: descriptors.slice(-1),
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
fn: (arg?: UnionValue) => {
|
|
87
|
+
return arg ? arg : { type: ValueType.undefined };
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
@@ -26,6 +26,7 @@ import { type Complex, type UnionValue, ValueType, type CellValue, ComplexOrReal
|
|
|
26
26
|
import * as ComplexMath from '../complex-math';
|
|
27
27
|
|
|
28
28
|
import { BetaCDF, BetaPDF, InverseBeta, LnGamma } from './beta';
|
|
29
|
+
import { gamma_p } from './gamma';
|
|
29
30
|
|
|
30
31
|
/** error function (for gaussian distribution) */
|
|
31
32
|
const erf = (x: number): number => {
|
|
@@ -172,6 +173,46 @@ const Gamma = (z: Complex): Complex => {
|
|
|
172
173
|
|
|
173
174
|
};
|
|
174
175
|
|
|
176
|
+
const GammaPDF = (x: number, alpha: number, beta: number): number => {
|
|
177
|
+
const gamma_alpha = Gamma({real: alpha, imaginary: 0}).real;
|
|
178
|
+
return (Math.pow(x, alpha - 1) * Math.exp(-x / beta)) / (gamma_alpha * Math.pow(beta, alpha));
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/** bisection */
|
|
182
|
+
const InverseGamma = (p: number, alpha: number, beta: number): number|false => {
|
|
183
|
+
|
|
184
|
+
let lower = 0;
|
|
185
|
+
let upper = alpha * 10;
|
|
186
|
+
|
|
187
|
+
const tolerance = 1e-6;
|
|
188
|
+
|
|
189
|
+
let iterations = 0;
|
|
190
|
+
|
|
191
|
+
while (upper - lower > tolerance) {
|
|
192
|
+
iterations++;
|
|
193
|
+
|
|
194
|
+
const mid = (upper + lower) / 2;
|
|
195
|
+
|
|
196
|
+
const f_lower = gamma_p(alpha, lower/beta);
|
|
197
|
+
const f_mid = gamma_p(alpha, mid/beta);
|
|
198
|
+
|
|
199
|
+
if (f_lower === false || f_mid === false) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if ((f_mid - p) * (f_lower - p) < 0) {
|
|
204
|
+
upper = mid;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
lower = mid;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return (lower + upper) / 2;
|
|
213
|
+
|
|
214
|
+
};
|
|
215
|
+
|
|
175
216
|
export const Variance = (data: number[], sample = false) => {
|
|
176
217
|
|
|
177
218
|
const len = data.length;
|
|
@@ -743,6 +784,53 @@ export const StatisticsFunctionLibrary: FunctionMap = {
|
|
|
743
784
|
},
|
|
744
785
|
},
|
|
745
786
|
|
|
787
|
+
'Gamma.Inv': {
|
|
788
|
+
description: 'Returns the inverse of the gamma distribution',
|
|
789
|
+
arguments: [
|
|
790
|
+
{name: 'probability', unroll: true},
|
|
791
|
+
{name: 'alpha', },
|
|
792
|
+
{name: 'beta', },
|
|
793
|
+
],
|
|
794
|
+
fn: (p: number, alpha: number, beta: number) => {
|
|
795
|
+
|
|
796
|
+
if (p < 0 || p > 1) {
|
|
797
|
+
return ArgumentError();
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
const value = InverseGamma(p, alpha, beta);
|
|
801
|
+
|
|
802
|
+
if (value === false) {
|
|
803
|
+
return ValueError();
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
return {
|
|
807
|
+
type: ValueType.number,
|
|
808
|
+
value,
|
|
809
|
+
}
|
|
810
|
+
},
|
|
811
|
+
},
|
|
812
|
+
|
|
813
|
+
'Gamma.Dist': {
|
|
814
|
+
fn: (x: number, alpha: number, beta: number, cumulative?: boolean) => {
|
|
815
|
+
|
|
816
|
+
if (x < 0 || alpha <= 0 || beta <= 0) {
|
|
817
|
+
return ArgumentError();
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
const value = cumulative ? gamma_p(alpha, x/beta) : GammaPDF(x, alpha, beta);
|
|
821
|
+
|
|
822
|
+
if (value === false) {
|
|
823
|
+
return ValueError();
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return {
|
|
827
|
+
type: ValueType.number,
|
|
828
|
+
value,
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
}
|
|
832
|
+
},
|
|
833
|
+
|
|
746
834
|
GammaLn: {
|
|
747
835
|
description: 'Returns the natural log of the gamma function',
|
|
748
836
|
arguments: [{ name: 'value', boxed: true, unroll: true }],
|