@trebco/treb 30.2.14 → 30.6.0

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.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- /*! API v30.2. Copyright 2018-2024 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
1
+ /*! API v30.6. Copyright 2018-2024 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
2
2
 
3
3
  /**
4
4
  * add our tag to the map
@@ -4,7 +4,7 @@
4
4
 
5
5
  import * as esbuild from 'esbuild';
6
6
 
7
- import { SassPlugin, WorkerPlugin, NotifyPlugin, HTMLPlugin } from './esbuild-utils.mjs';
7
+ import { SassPlugin, WorkerPlugin, NotifyPlugin, HTMLPlugin, RewriteIgnoredImports } from './esbuild-utils.mjs';
8
8
  import { promises as fs } from 'fs';
9
9
 
10
10
  import pkg from './package.json' with { type: 'json' };
@@ -32,7 +32,7 @@ const options = {
32
32
  minify: true,
33
33
  verbose: false,
34
34
  xlsx_support: true,
35
- output_filename: 'treb-spreadsheet.mjs',
35
+ output_filename: 'treb-spreadsheet',
36
36
  };
37
37
 
38
38
  //------------------------------------------------------------------------------
@@ -60,36 +60,19 @@ for (let i = 0; i < process.argv.length; i++) {
60
60
  }
61
61
  }
62
62
 
63
- /**
64
- * thanks to
65
- * https://github.com/evanw/esbuild/issues/3337#issuecomment-2085394950
66
- */
67
- function RewriteIgnoredImports() {
68
- return {
69
- name: 'RewriteIgnoredImports',
70
- setup(build) {
71
- build.onEnd(async (result) => {
72
- if (result.outputFiles) {
73
- for (const file of result.outputFiles) {
74
- const { path, text } = file;
75
- await fs.writeFile(path, text.replace(/esbuild-ignore-import:/, ''));
76
- }
77
- }
78
- });
79
- },
80
- };
81
- }
82
63
 
83
64
  /** @type esbuild.BuildOptions */
84
65
  const build_options = {
85
66
  entryPoints: [
86
- 'treb-embed/src/index.ts',
67
+ { in: 'treb-embed/src/index.ts', out: options.output_filename },
68
+ { in: 'treb-embed/src/export-worker.ts', out: 'treb-export-worker' },
87
69
  ],
88
70
  banner: {
89
71
  js: `/*! TREB v${pkg.version}. Copyright 2018-${new Date().getFullYear()} trebco, llc. All rights reserved. LGPL: https://treb.app/license */`
90
72
  },
91
73
  bundle: true,
92
- outfile: 'dist/' + options.output_filename,
74
+ outdir: 'dist',
75
+ // outfile: 'dist/' + options.output_filename,
93
76
  outExtension: { '.js': '.mjs' },
94
77
  minify: options.minify,
95
78
  metafile: true,
package/esbuild-utils.mjs CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  import * as esbuild from 'esbuild';
7
7
  import { promises as fs } from 'fs';
8
- import { minify } from 'html-minifier';
8
+ import { minify } from 'html-minifier-terser';
9
9
  import path from 'path';
10
10
  import * as sass from 'sass';
11
11
  import cssnano from 'cssnano';
@@ -116,7 +116,7 @@ export const WorkerPlugin = (options) => ({
116
116
  resolveDir: args.resolveDir,
117
117
  });
118
118
  return { path: result.path, namespace: 'worker', };
119
- }),
119
+ });
120
120
 
121
121
  // for some reason I can't get the filter to work here, but using
122
122
  // namespace works. as long as we don't collide with anybody else.
@@ -219,10 +219,14 @@ export const HTMLPlugin = (options) => ({
219
219
  }
220
220
 
221
221
  const text = await fs.readFile(args.path, 'utf8');
222
+
222
223
  return {
223
- contents: options?.minify ? minify(text, html_minifier_options) : text,
224
+ contents: options?.minify ? await minify(text, html_minifier_options) : text,
224
225
  loader: 'text',
225
226
  };
227
+
228
+
229
+
226
230
  });
227
231
  },
228
232
  });
@@ -282,3 +286,24 @@ export const SassPlugin = (options) => ({
282
286
  });
283
287
  },
284
288
  });
289
+
290
+ /**
291
+ * thanks to
292
+ * https://github.com/evanw/esbuild/issues/3337#issuecomment-2085394950
293
+ */
294
+ export const RewriteIgnoredImports = () => {
295
+ return {
296
+ name: 'RewriteIgnoredImports',
297
+ setup(build) {
298
+ build.onEnd(async (result) => {
299
+ if (result.outputFiles) {
300
+ for (const file of result.outputFiles) {
301
+ const { path, text } = file;
302
+ await fs.writeFile(path, text.replace(/esbuild-ignore-import:/g, ''));
303
+ }
304
+ }
305
+ });
306
+ },
307
+ };
308
+ };
309
+
package/eslint.config.js CHANGED
@@ -23,7 +23,8 @@ export default tseslint.config(
23
23
 
24
24
  "@typescript-eslint/no-unused-vars": [
25
25
  "error", {
26
- "destructuredArrayIgnorePattern": "^_",
26
+ destructuredArrayIgnorePattern: "^_",
27
+ varsIgnorePattern: "^_",
27
28
  }],
28
29
  },
29
30
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trebco/treb",
3
- "version": "30.2.14",
3
+ "version": "30.6.0",
4
4
  "license": "LGPL-3.0-or-later",
5
5
  "homepage": "https://treb.app",
6
6
  "repository": {
@@ -19,7 +19,7 @@
19
19
  "esbuild": "^0.23.0",
20
20
  "eslint": "^9.8.0",
21
21
  "fast-xml-parser": "^4.0.7",
22
- "html-minifier": "^4.0.0",
22
+ "html-minifier-terser": "^7.2.0",
23
23
  "sass": "^1.69.3",
24
24
  "treb-base-types": "file:treb-base-types",
25
25
  "treb-calculator": "file:treb-calculator",
@@ -37,17 +37,16 @@
37
37
  "uzip": "^0.20201231.0"
38
38
  },
39
39
  "scripts": {
40
- "build": "node esbuild-custom-element.mjs",
41
- "build-light": "node esbuild-custom-element.mjs --no-xlsx --output-filename treb-spreadsheet-light.mjs",
42
- "dev": "node esbuild-custom-element.mjs --dev",
40
+ "build": "node esbuild-composite.mjs",
41
+ "dev": "node esbuild-composite.mjs --dev",
43
42
  "clean": "rm -fr build dist declaration",
44
- "watch": "node --watch esbuild-custom-element.mjs --watch --dev",
45
- "watch-production": "node --watch esbuild-custom-element.mjs --watch",
43
+ "watch": "node --watch esbuild-composite.mjs --watch --dev",
44
+ "watch-production": "node --watch esbuild-composite.mjs --watch",
46
45
  "tsc": "tsc -b treb-embed/modern.tsconfig.json",
47
46
  "rebuild-tsc": "tsc -b --force treb-embed/modern.tsconfig.json",
48
47
  "watch-tsc": "tsc -b treb-embed/modern.tsconfig.json -w",
49
48
  "watch-api": "ts-node-dev --respawn api-generator/api-generator.ts --config api-config.json",
50
49
  "generate-api": "ts-node-dev api-generator/api-generator.ts --config api-config.json",
51
- "release": "npm run rebuild-tsc && npm run build && npm run generate-api && npm run build-light"
50
+ "release": "npm run rebuild-tsc && npm run build && npm run generate-api"
52
51
  }
53
52
  }
@@ -709,7 +709,7 @@ export class Cells {
709
709
  typeof cell.calculated !== 'undefined') { // && cell.calculated_type !== ValueType.error) {
710
710
  obj.calculated = cell.calculated;
711
711
  if (cell.spill) {
712
- obj.spill = cell.spill;
712
+ obj.spill = cell.spill.toJSON();
713
713
  }
714
714
 
715
715
  // always preserve error type, because we can't infer
@@ -253,6 +253,7 @@ export class Calculator extends Graph {
253
253
  // special functions... need reference to the graph (this)
254
254
  // moving countif here so we can reference it in COUNTIFS...
255
255
 
256
+ /*
256
257
  const FlattenBooleans = (value: ArrayUnion) => {
257
258
  const result: boolean[] = [];
258
259
  for (const col of value.value) {
@@ -262,6 +263,58 @@ export class Calculator extends Graph {
262
263
  }
263
264
  return result;
264
265
  };
266
+ */
267
+
268
+ /**
269
+ * this is a function that does sumif/averageif/countif.
270
+ * args is one or more sets of [criteria_range, criteria]
271
+ */
272
+ const XIf = (type: 'sum'|'count'|'average', value_range: CellValue[][], ...args: unknown[]): UnionValue => {
273
+
274
+ const filter: boolean[] = [];
275
+
276
+ for (let i = 0; i < args.length; i += 2) {
277
+ if (Array.isArray(args[i])) {
278
+ const step = CountIfInternal(args[i] as CellValue[][], args[i+1] as CellValue);
279
+ if (step.type !== ValueType.array) {
280
+ return step;
281
+ }
282
+ for (const [r, cell] of step.value[0].entries()) {
283
+ filter[r] = (!!cell.value && (filter[r] !== false));
284
+ }
285
+ }
286
+ }
287
+
288
+ const values = Utilities.FlattenCellValues(value_range, true); // keep undefineds
289
+
290
+ let count = 0;
291
+ let sum = 0;
292
+ for (const [index, test] of filter.entries()) {
293
+ if (test) {
294
+ count++;
295
+ const value = values[index];
296
+ if (typeof value === 'number') {
297
+ sum += value;
298
+ }
299
+ }
300
+ }
301
+
302
+ switch (type) {
303
+ case 'count':
304
+ return { type: ValueType.number, value: count };
305
+
306
+ case 'sum':
307
+ return { type: ValueType.number, value: sum };
308
+
309
+ case 'average':
310
+ if (count === 0) {
311
+ return DivideByZeroError();
312
+ }
313
+ return { type: ValueType.number, value: sum/count };
314
+ }
315
+
316
+
317
+ }
265
318
 
266
319
  const CountIfInternal = (range: CellValue[][], criteria: CellValue): UnionValue => {
267
320
 
@@ -271,7 +324,7 @@ export class Calculator extends Graph {
271
324
  // in any event there are no dynamic dependencies with this
272
325
  // function.
273
326
 
274
- const data = Utilities.FlattenCellValues(range);
327
+ const data = Utilities.FlattenCellValues(range, true); // keep undefineds, important for mapping
275
328
 
276
329
  let parse_result: ParseResult|undefined;
277
330
  let expression: ExpressionUnit|undefined;
@@ -326,7 +379,7 @@ export class Calculator extends Graph {
326
379
  }
327
380
 
328
381
  if (expression?.type === 'call' && expression.args[0]?.type === 'array') {
329
- expression.args[0].values = [Utilities.FilterIntrinsics(data)];
382
+ expression.args[0].values = [Utilities.FilterIntrinsics(data, true)];
330
383
  }
331
384
 
332
385
  }
@@ -377,7 +430,7 @@ export class Calculator extends Graph {
377
430
  }
378
431
  }
379
432
 
380
- expression.left.values = [Utilities.FilterIntrinsics(data)];
433
+ expression.left.values = [Utilities.FilterIntrinsics(data, true)];
381
434
 
382
435
  }
383
436
 
@@ -388,22 +441,6 @@ export class Calculator extends Graph {
388
441
  const result = this.CalculateExpression(expression);
389
442
  return result;
390
443
 
391
- /*
392
- // console.info({expression, result});
393
-
394
- if (result.type === ValueType.array) {
395
- let count = 0;
396
- for (const column of (result as ArrayUnion).value) {
397
- for (const cell of column) {
398
- if (cell.value) { count++; }
399
- }
400
- }
401
- return { type: ValueType.number, value: count };
402
- }
403
-
404
- return result; // error?
405
- */
406
-
407
444
  };
408
445
 
409
446
  this.library.Register({
@@ -615,47 +652,63 @@ export class Calculator extends Graph {
615
652
  */
616
653
  CountIfs: {
617
654
  arguments: [
618
- { name: 'range1', },
619
- { name: 'criteria1', },
620
- { name: 'range2', },
621
- { name: 'criteria2', }
655
+ { name: 'range', },
656
+ { name: 'criteria', },
657
+ { name: 'range', },
658
+ { name: 'criteria', }
622
659
  ],
623
660
  fn: (...args): UnionValue => {
661
+ return XIf('count', args[0], ...args);
662
+ },
663
+ },
624
664
 
625
- let count = 0;
626
-
627
- const result = CountIfInternal(args[0], args[1]);
628
- if (result.type !== ValueType.array) {
629
- return result; // error
630
- }
631
-
632
- const base = FlattenBooleans(result);
633
-
634
- for (let i = 2; i < args.length; i += 2) {
635
- if (args[i] && args[i + 1]) {
636
-
637
- const result = CountIfInternal(args[i], args[i+1]);
638
- if (result.type !== ValueType.array) {
639
- return result;
640
- }
665
+ /** @see CountIf */
666
+ AverageIf: {
667
+ arguments: [
668
+ { name: 'range', },
669
+ { name: 'criteria', },
670
+ ],
671
+ fn: (range: CellValue[][], criteria: CellValue, average_range?: CellValue[][]) => {
672
+ return XIf('average', average_range||range, range, criteria);
673
+ },
674
+ },
641
675
 
642
- const step = FlattenBooleans(result);
643
- for (const [index, value] of base.entries()) {
644
- base[index] = value && step[index];
645
- }
646
-
647
- }
648
- }
676
+ /** @see CountIf */
677
+ AverageIfs: {
678
+ arguments: [
679
+ { name: 'value range', },
680
+ { name: 'criteria range', },
681
+ { name: 'criteria', },
682
+ { name: 'criteria range', },
683
+ { name: 'criteria', },
684
+ ],
685
+ fn: (range: CellValue[][], ...args) => {
686
+ return XIf('average', range, ...args);
687
+ },
688
+ },
649
689
 
650
- for (const element of base) {
651
- if (element) { count++; }
652
- }
653
-
654
- return {
655
- type: ValueType.number,
656
- value: count,
657
- }
690
+ /** @see CountIf */
691
+ SumIf: {
692
+ arguments: [
693
+ { name: 'range', },
694
+ { name: 'criteria', },
695
+ ],
696
+ fn: (range: CellValue[][], criteria: CellValue, sum_range?: CellValue[][]) => {
697
+ return XIf('sum', sum_range||range, range, criteria);
698
+ },
699
+ },
658
700
 
701
+ /** @see CountIf */
702
+ SumIfs: {
703
+ arguments: [
704
+ { name: 'value range', },
705
+ { name: 'criteria range', },
706
+ { name: 'criteria', },
707
+ { name: 'criteria range', },
708
+ { name: 'criteria', },
709
+ ],
710
+ fn: (range: CellValue[][], ...args) => {
711
+ return XIf('sum', range, ...args);
659
712
  },
660
713
  },
661
714
 
@@ -680,21 +733,7 @@ export class Calculator extends Graph {
680
733
  { name: 'criteria', }
681
734
  ],
682
735
  fn: (range, criteria): UnionValue => {
683
-
684
- const result = CountIfInternal(range, criteria);
685
-
686
- if (result.type === ValueType.array) {
687
- let count = 0;
688
- for (const column of (result as ArrayUnion).value) {
689
- for (const cell of column) {
690
- if (cell.value) { count++; }
691
- }
692
- }
693
- return { type: ValueType.number, value: count };
694
- }
695
-
696
- return result; // error
697
-
736
+ return XIf('count', range, range, criteria);
698
737
  },
699
738
  },
700
739
 
@@ -99,6 +99,54 @@ export const RectangularToPolar = (value: Complex): { r: number, theta: number }
99
99
  return { r, theta };
100
100
  };
101
101
 
102
+ export const Tan = (z: Complex) => {
103
+
104
+ // tan(a+bi) = (tan(a) + i tanh(b)) / (1 - i tan(a) tanh(b))
105
+
106
+ return Divide({
107
+ real: Math.tan(z.real),
108
+ imaginary: Math.tanh(z.imaginary),
109
+ }, {
110
+ real: 1,
111
+ imaginary: -(Math.tan(z.real) * Math.tanh(z.imaginary)),
112
+ });
113
+
114
+ };
115
+
116
+ export const Cos = (z: Complex) => {
117
+
118
+ // sin(a+bi) = cos(a) cosh(b) + i sin(a) sinh(b)
119
+
120
+ return {
121
+ real: Math.cos(z.real) * Math.cosh(z.imaginary),
122
+ imaginary: Math.sin(z.real) * Math.sinh(z.imaginary),
123
+ };
124
+
125
+ };
126
+
127
+
128
+ export const Sin = (z: Complex) => {
129
+
130
+ // sin(a+bi) = sin(a) cosh(b) + i cos(a) sinh(b)
131
+
132
+ return {
133
+ real: Math.sin(z.real) * Math.cosh(z.imaginary),
134
+ imaginary: Math.cos(z.real) * Math.sinh(z.imaginary),
135
+ };
136
+
137
+ };
138
+
139
+ export const Product = (...args: Complex[]): Complex => {
140
+ let base = args.shift();
141
+ if (!base) {
142
+ return { real: 0, imaginary: 0 };
143
+ }
144
+ for (const arg of args) {
145
+ base = Multiply(base, arg);
146
+ }
147
+ return base;
148
+ };
149
+
102
150
  export const Multiply = (a: Complex, b: Complex): Complex => {
103
151
  return {
104
152
  real: (a.real * b.real) - (a.imaginary * b.imaginary),
@@ -179,7 +179,10 @@ export abstract class Graph implements GraphCallbacks {
179
179
  public GetVertex(address: ICellAddress, create?: boolean): SpreadsheetVertex | undefined {
180
180
 
181
181
  if (!address.sheet_id) {
182
- console.info({address, create});
182
+ console.info(JSON.stringify({address, create}));
183
+ console.trace();
184
+
185
+
183
186
  throw new Error('getvertex with no sheet id');
184
187
  }
185
188
 
@@ -35,8 +35,11 @@ import { ClickCheckbox, RenderCheckbox } from './checkbox';
35
35
  import { UnionIsMetadata } from '../expression-calculator';
36
36
 
37
37
  import { Exp as ComplexExp, Power as ComplexPower, Multiply as ComplexMultply } from '../complex-math';
38
+ import * as ComplexMath from '../complex-math';
39
+
38
40
  import { CoerceComplex } from './function-utilities';
39
41
  import type { UnitAddress, UnitRange } from 'treb-parser';
42
+ import { ConstructDate } from './date-utils';
40
43
 
41
44
  /**
42
45
  * BaseFunctionLibrary is a static object that has basic spreadsheet
@@ -77,6 +80,22 @@ const erf = (x: number): number => {
77
80
 
78
81
  const sqrt2pi = Math.sqrt(2 * Math.PI);
79
82
 
83
+ const norm_dist = (x: number, mean: number, stdev: number, cumulative: boolean) => {
84
+
85
+ let value = 0;
86
+
87
+ if (cumulative) {
88
+ const sign = (x < mean) ? -1 : 1;
89
+ value = 0.5 * (1.0 + sign * erf((Math.abs(x - mean)) / (stdev * Math.sqrt(2))));
90
+ }
91
+ else {
92
+ value = Math.exp(-1/2 * Math.pow((x - mean) / stdev, 2)) / (stdev * sqrt2pi);
93
+ }
94
+
95
+ return value;
96
+
97
+ }
98
+
80
99
  /** imprecise but reasonably fast normsinv function */
81
100
  const inverse_normal = (q: number): number => {
82
101
 
@@ -93,6 +112,7 @@ const inverse_normal = (q: number): number => {
93
112
 
94
113
  };
95
114
 
115
+
96
116
  const edate_calc = (start: number, months: number) => {
97
117
 
98
118
  let date = new Date(LotusDate(start));
@@ -519,36 +539,18 @@ export const BaseFunctionLibrary: FunctionMap = {
519
539
  },
520
540
 
521
541
  Date: {
522
- description: 'Constructs a Lotus date from parts',
542
+ description: 'Constructs a date from year/month/day',
523
543
  arguments: [
524
544
  { name: 'year', unroll: true },
525
545
  { name: 'month', unroll: true },
526
546
  { name: 'day', unroll: true },
527
547
  ],
528
548
  fn: (year: number, month: number, day: number) => {
529
- const date = new Date();
530
- date.setMilliseconds(0);
531
- date.setSeconds(0);
532
- date.setMinutes(0);
533
- date.setHours(0);
534
-
535
- if (year < 0 || year > 10000) {
549
+ const date = ConstructDate(year, month, day);
550
+ if (date === false) {
536
551
  return ArgumentError();
537
552
  }
538
- if (year < 1899) { year += 1900; }
539
- date.setFullYear(year);
540
-
541
- if (month < 1 || month > 12) {
542
- return ArgumentError();
543
- }
544
- date.setMonth(month - 1);
545
-
546
- if (day < 1 || day > 31) {
547
- return ArgumentError();
548
- }
549
- date.setDate(day);
550
-
551
- return { type: ValueType.number, value: UnlotusDate(date.getTime()) };
553
+ return { type: ValueType.number, value: date };
552
554
  },
553
555
  },
554
556
 
@@ -2122,6 +2124,22 @@ export const BaseFunctionLibrary: FunctionMap = {
2122
2124
  }
2123
2125
  },
2124
2126
 
2127
+ 'Norm.S.Inv': {
2128
+ description: 'Inverse of the normal cumulative distribution',
2129
+ arguments: [
2130
+ {name: 'probability'},
2131
+ {name: 'mean', default: 0},
2132
+ {name: 'standard deviation', default: 1},
2133
+ ],
2134
+ xlfn: true,
2135
+ fn: (q: number, mean = 0, stdev = 1): UnionValue => {
2136
+ return {
2137
+ type: ValueType.number,
2138
+ value: inverse_normal(q) * stdev + mean,
2139
+ }
2140
+ }
2141
+ },
2142
+
2125
2143
  'Norm.Dist': {
2126
2144
 
2127
2145
  description: 'Cumulative normal distribution',
@@ -2138,22 +2156,22 @@ export const BaseFunctionLibrary: FunctionMap = {
2138
2156
  xlfn: true,
2139
2157
 
2140
2158
  fn: (x: number, mean = 0, stdev = 1, cumulative = true): UnionValue => {
2159
+ return { type: ValueType.number, value: norm_dist(x, mean, stdev, cumulative) };
2160
+ },
2161
+ },
2141
2162
 
2142
- let value = 0;
2163
+ 'Norm.S.Dist': {
2143
2164
 
2144
- if (cumulative) {
2145
- const sign = (x < mean) ? -1 : 1;
2146
- value = 0.5 * (1.0 + sign * erf((Math.abs(x - mean)) / (stdev * Math.sqrt(2))));
2147
- }
2148
- else {
2149
- value = Math.exp(-1/2 * Math.pow((x - mean) / stdev, 2)) / (stdev * sqrt2pi);
2150
- }
2165
+ description: 'Cumulative normal distribution',
2166
+ arguments: [
2167
+ {name: 'value'},
2168
+ {name: 'cumulative', default: true},
2169
+ ],
2151
2170
 
2152
- return {
2153
- type: ValueType.number,
2154
- value,
2155
- };
2171
+ xlfn: true,
2156
2172
 
2173
+ fn: (x: number, cumulative = true): UnionValue => {
2174
+ return { type: ValueType.number, value: norm_dist(x, 0, 1, cumulative) };
2157
2175
  },
2158
2176
  },
2159
2177
 
@@ -2437,6 +2455,60 @@ export const BaseFunctionLibrary: FunctionMap = {
2437
2455
  return ArgumentError();
2438
2456
  }
2439
2457
 
2458
+ },
2459
+ },
2460
+
2461
+ Sin: {
2462
+ arguments: [
2463
+ { name: 'angle in radians', boxed: true, unroll: true, }
2464
+ ],
2465
+ fn: (a: UnionValue) => {
2466
+
2467
+ if (a.type === ValueType.number) {
2468
+ return { type: ValueType.number, value: Math.sin(a.value) };
2469
+ }
2470
+ if (a.type === ValueType.complex) {
2471
+ return { type: ValueType.complex, value: ComplexMath.Sin(a.value) };
2472
+ }
2473
+
2474
+ return ArgumentError();
2475
+
2476
+ },
2477
+ },
2478
+
2479
+ Cos: {
2480
+ arguments: [
2481
+ { name: 'angle in radians', boxed: true, unroll: true, }
2482
+ ],
2483
+ fn: (a: UnionValue) => {
2484
+
2485
+ if (a.type === ValueType.number) {
2486
+ return { type: ValueType.number, value: Math.cos(a.value) };
2487
+ }
2488
+ if (a.type === ValueType.complex) {
2489
+ return { type: ValueType.complex, value: ComplexMath.Cos(a.value) };
2490
+ }
2491
+
2492
+ return ArgumentError();
2493
+
2494
+ },
2495
+ },
2496
+
2497
+ Tan: {
2498
+ arguments: [
2499
+ { name: 'angle in radians', boxed: true, unroll: true, }
2500
+ ],
2501
+ fn: (a: UnionValue) => {
2502
+
2503
+ if (a.type === ValueType.number) {
2504
+ return { type: ValueType.number, value: Math.tan(a.value) };
2505
+ }
2506
+ if (a.type === ValueType.complex) {
2507
+ return { type: ValueType.complex, value: ComplexMath.Tan(a.value) };
2508
+ }
2509
+
2510
+ return ArgumentError();
2511
+
2440
2512
  },
2441
2513
  }
2442
2514