@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.
Files changed (35) hide show
  1. package/dist/treb-spreadsheet.mjs +15 -15
  2. package/i18n/languages/treb-i18n-da.mjs +1 -0
  3. package/i18n/languages/treb-i18n-de.mjs +1 -0
  4. package/i18n/languages/treb-i18n-es.mjs +1 -0
  5. package/i18n/languages/treb-i18n-fr.mjs +1 -0
  6. package/i18n/languages/treb-i18n-it.mjs +1 -0
  7. package/i18n/languages/treb-i18n-nl.mjs +1 -0
  8. package/i18n/languages/treb-i18n-no.mjs +1 -0
  9. package/i18n/languages/treb-i18n-pl.mjs +1 -0
  10. package/i18n/languages/treb-i18n-pt.mjs +1 -0
  11. package/i18n/languages/treb-i18n-sv.mjs +1 -0
  12. package/package.json +1 -1
  13. package/treb-base-types/src/style.ts +20 -0
  14. package/treb-base-types/src/union.ts +6 -0
  15. package/treb-base-types/src/value-type.ts +13 -0
  16. package/treb-calculator/src/calculator.ts +20 -1
  17. package/treb-calculator/src/descriptors.ts +49 -4
  18. package/treb-calculator/src/expression-calculator.ts +263 -12
  19. package/treb-calculator/src/functions/base-functions.ts +49 -0
  20. package/treb-calculator/src/functions/fp.ts +339 -0
  21. package/treb-calculator/src/functions/gamma.ts +143 -0
  22. package/treb-calculator/src/functions/lambda-functions.ts +96 -0
  23. package/treb-calculator/src/functions/statistics-functions.ts +88 -0
  24. package/treb-data-model/src/data_model.ts +28 -13
  25. package/treb-data-model/src/named.ts +7 -0
  26. package/treb-data-model/src/sheet.ts +19 -2
  27. package/treb-embed/src/embedded-spreadsheet.ts +22 -5
  28. package/treb-embed/style/theme-defaults.scss +0 -22
  29. package/treb-grid/src/editors/editor.ts +14 -0
  30. package/treb-grid/src/types/grid.ts +74 -28
  31. package/treb-parser/src/parser-types.ts +24 -0
  32. package/treb-parser/src/parser.ts +157 -223
  33. package/dist/treb-export-worker.mjs +0 -2
  34. package/dist/treb.d.ts +0 -2235
  35. 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 }],