@trebco/treb 28.17.4 → 29.1.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.
Files changed (87) hide show
  1. package/dist/treb-spreadsheet-light.mjs +12 -12
  2. package/dist/treb-spreadsheet.mjs +12 -12
  3. package/dist/treb.d.ts +121 -82
  4. package/eslint.config.js +21 -0
  5. package/package.json +6 -6
  6. package/treb-base-types/src/area.ts +4 -2
  7. package/treb-base-types/src/cell.ts +1 -1
  8. package/treb-base-types/src/cells.ts +16 -7
  9. package/treb-base-types/src/dom-utilities.ts +4 -2
  10. package/treb-base-types/src/import.ts +2 -2
  11. package/treb-base-types/src/rectangle.ts +5 -5
  12. package/treb-base-types/src/union.ts +6 -1
  13. package/treb-base-types/src/value-type.ts +1 -1
  14. package/treb-calculator/src/calculator.ts +114 -165
  15. package/treb-calculator/src/dag/calculation_leaf_vertex.ts +1 -2
  16. package/treb-calculator/src/dag/graph.ts +3 -3
  17. package/treb-calculator/src/dag/spreadsheet_vertex.ts +2 -2
  18. package/treb-calculator/src/dag/state_leaf_vertex.ts +2 -4
  19. package/treb-calculator/src/descriptors.ts +28 -2
  20. package/treb-calculator/src/expression-calculator.ts +25 -34
  21. package/treb-calculator/src/function-error.ts +2 -2
  22. package/treb-calculator/src/function-library.ts +16 -0
  23. package/treb-calculator/src/functions/base-functions.ts +185 -211
  24. package/treb-calculator/src/functions/checkbox.ts +0 -1
  25. package/treb-calculator/src/functions/complex-functions.ts +49 -47
  26. package/treb-calculator/src/functions/finance-functions.ts +10 -10
  27. package/treb-calculator/src/functions/function-utilities.ts +26 -0
  28. package/treb-calculator/src/functions/information-functions.ts +21 -41
  29. package/treb-calculator/src/functions/matrix-functions.ts +8 -1
  30. package/treb-calculator/src/functions/sparkline.ts +6 -4
  31. package/treb-calculator/src/functions/statistics-functions.ts +21 -17
  32. package/treb-calculator/src/functions/text-functions.ts +14 -13
  33. package/treb-calculator/src/primitives.ts +48 -37
  34. package/treb-calculator/src/utilities.ts +117 -134
  35. package/treb-charts/src/chart-functions.ts +3 -3
  36. package/treb-charts/src/chart-types.ts +42 -1
  37. package/treb-charts/src/chart-utils.ts +155 -113
  38. package/treb-charts/src/chart.ts +6 -5
  39. package/treb-charts/src/default-chart-renderer.ts +6 -5
  40. package/treb-charts/src/renderer.ts +12 -11
  41. package/treb-charts/src/util.ts +25 -36
  42. package/treb-data-model/package.json +5 -0
  43. package/{treb-grid/src/types → treb-data-model/src}/annotation.ts +2 -2
  44. package/{treb-grid/src/types → treb-data-model/src}/conditional_format.ts +20 -0
  45. package/{treb-grid/src/types → treb-data-model/src}/data_model.ts +231 -133
  46. package/treb-data-model/src/index.ts +45 -0
  47. package/{treb-grid/src/types/named_range.ts → treb-data-model/src/named.ts} +459 -376
  48. package/{treb-grid/src/types → treb-data-model/src}/sheet.ts +13 -5
  49. package/treb-data-model/src/sheet_collection.ts +114 -0
  50. package/{treb-grid/src/types → treb-data-model/src}/sheet_types.ts +6 -3
  51. package/treb-embed/modern.tsconfig.json +1 -0
  52. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +2 -3
  53. package/treb-embed/src/embedded-spreadsheet.ts +125 -270
  54. package/treb-embed/src/selection-state.ts +1 -1
  55. package/treb-embed/src/toolbar-message.ts +1 -1
  56. package/treb-embed/src/types.ts +13 -5
  57. package/treb-export/src/export-worker/export-worker.ts +22 -7
  58. package/treb-export/src/export2.ts +110 -41
  59. package/treb-export/src/import2.ts +6 -5
  60. package/treb-export/src/workbook2.ts +31 -13
  61. package/treb-export/src/xml-utils.ts +5 -1
  62. package/treb-format/src/format.ts +8 -6
  63. package/treb-grid/src/editors/autocomplete.ts +2 -2
  64. package/treb-grid/src/editors/autocomplete_matcher.ts +57 -19
  65. package/treb-grid/src/editors/editor.ts +27 -25
  66. package/treb-grid/src/editors/formula_bar.ts +5 -5
  67. package/treb-grid/src/editors/overlay_editor.ts +1 -2
  68. package/treb-grid/src/index.ts +0 -11
  69. package/treb-grid/src/layout/base_layout.ts +20 -8
  70. package/treb-grid/src/layout/grid_layout.ts +2 -2
  71. package/treb-grid/src/layout/mock-layout.ts +5 -6
  72. package/treb-grid/src/render/selection-renderer.ts +2 -3
  73. package/treb-grid/src/render/tile_renderer.ts +1 -1
  74. package/treb-grid/src/types/grid.ts +95 -66
  75. package/treb-grid/src/types/grid_base.ts +76 -60
  76. package/treb-grid/src/types/grid_command.ts +3 -2
  77. package/treb-grid/src/types/grid_events.ts +12 -6
  78. package/treb-grid/src/types/tab_bar.ts +1 -2
  79. package/treb-parser/src/parser-types.ts +2 -1
  80. package/treb-parser/src/parser.ts +7 -5
  81. package/treb-utils/src/event_source.ts +1 -1
  82. package/treb-utils/src/serialize_html.ts +31 -6
  83. package/.eslintignore +0 -8
  84. package/.eslintrc.cjs +0 -168
  85. package/treb-grid/src/layout/rectangle_cache.ts +0 -86
  86. /package/{treb-grid/src/types → treb-data-model/src}/serialize_options.ts +0 -0
  87. /package/{treb-grid/src/types/grid_selection.ts → treb-data-model/src/sheet_selection.ts} +0 -0
@@ -19,11 +19,11 @@
19
19
  *
20
20
  */
21
21
 
22
- import type { FunctionMap } from '../descriptors';
22
+ import type { FunctionMap, IntrinsicValue } from '../descriptors';
23
23
  import * as Utils from '../utilities';
24
- import { ReferenceError, NotImplError, NAError, ArgumentError, DivideByZeroError, ValueError, NameError } from '../function-error';
24
+ import { ReferenceError, NotImplError, NAError, ArgumentError, DivideByZeroError, ValueError } from '../function-error';
25
25
  import type { UnionValue,
26
- RenderFunctionResult, RenderFunctionOptions, Complex } from 'treb-base-types';
26
+ RenderFunctionResult, RenderFunctionOptions, Complex, CellValue } from 'treb-base-types';
27
27
  import { Box, ValueType, GetValueType, ComplexOrReal, IsComplex } from 'treb-base-types';
28
28
  import { Sparkline } from './sparkline';
29
29
  import { LotusDate, UnlotusDate } from 'treb-format';
@@ -32,6 +32,7 @@ import { ClickCheckbox, RenderCheckbox } from './checkbox';
32
32
  import { UnionIsMetadata } from '../expression-calculator';
33
33
 
34
34
  import { Exp as ComplexExp, Power as ComplexPower, Multiply as ComplexMultply } from '../complex-math';
35
+ import { CoerceComplex } from './function-utilities';
35
36
 
36
37
  /**
37
38
  * BaseFunctionLibrary is a static object that has basic spreadsheet
@@ -108,7 +109,7 @@ const zlookup_arguments = [
108
109
  * unified VLOOKUP/HLOOKUP. ordinarily we'd call it XLOOKUP but that's taken.
109
110
  * FIXME: can't use use that function for this?
110
111
  */
111
- const ZLookup = (value: any, table: any[][], col: number, inexact = true, transpose = false): UnionValue => {
112
+ const ZLookup = (value: number|string|boolean|undefined, table: (number|string|boolean|undefined)[][], col: number, inexact = true, transpose = false): UnionValue => {
112
113
 
113
114
  if (transpose) {
114
115
  table = Utils.TransposeArray(table);
@@ -128,7 +129,7 @@ const ZLookup = (value: any, table: any[][], col: number, inexact = true, transp
128
129
 
129
130
  if (inexact) {
130
131
 
131
- let result: any = table[col][0];
132
+ let result: number|string|boolean|undefined = table[col][0];
132
133
 
133
134
  if (typeof value === 'number') {
134
135
 
@@ -149,7 +150,7 @@ const ZLookup = (value: any, table: any[][], col: number, inexact = true, transp
149
150
  }
150
151
  else {
151
152
 
152
- value = value.toLowerCase(); // ?
153
+ value = (value||'').toString().toLowerCase(); // ?
153
154
  let compare: string = (table[0][0] || '').toString().toLowerCase();
154
155
  if (compare.localeCompare(value) > 0) {
155
156
  return NAError();
@@ -189,9 +190,9 @@ export const AltFunctionLibrary: FunctionMap = {
189
190
  Sqrt: {
190
191
  description: 'Returns the square root of the argument',
191
192
  arguments: [
192
- {boxed: true},
193
+ { boxed: true, unroll: true },
193
194
  ],
194
- fn: Utils.ApplyAsArray((ref: UnionValue): UnionValue => {
195
+ fn: (ref: UnionValue): UnionValue => {
195
196
 
196
197
  if (ref.type === ValueType.complex) {
197
198
  const value = ComplexPower(ref.value, {real: 0.5, imaginary: 0});
@@ -209,23 +210,33 @@ export const AltFunctionLibrary: FunctionMap = {
209
210
  value,
210
211
  }
211
212
  }
213
+ else if (ref.type === ValueType.number) {
214
+ const value = Math.sqrt(ref.value);
215
+ if (isNaN(value)) {
216
+ return ValueError();
217
+ }
218
+ return { type: ValueType.number, value };
219
+ }
212
220
  else {
221
+ /*
213
222
  const value = Math.sqrt(ref.value);
214
223
  if (isNaN(value)) {
215
224
  return ValueError();
216
225
  }
217
226
  return { type: ValueType.number, value };
227
+ */
228
+ return ValueError();
218
229
  }
219
- }),
230
+ },
220
231
  },
221
232
 
222
233
  Power: {
223
234
  description: 'Returns base raised to the given power',
224
235
  arguments: [
225
- { name: 'base', boxed: true, },
226
- { name: 'exponent', boxed: true, }
236
+ { name: 'base', boxed: true, unroll: true, },
237
+ { name: 'exponent', boxed: true, unroll: true, }
227
238
  ],
228
- fn: Utils.ApplyAsArray2((base: UnionValue, exponent: UnionValue): UnionValue => {
239
+ fn: (base: UnionValue, exponent: UnionValue): UnionValue => {
229
240
 
230
241
  // we're leaking complex numbers here because our functions are
231
242
  // very slightly imprecise. I would like to stop doing that. try to
@@ -247,25 +258,18 @@ export const AltFunctionLibrary: FunctionMap = {
247
258
  }
248
259
  }
249
260
 
250
- /*
251
- if (base.type === ValueType.number) {
252
- base = {
253
- type: ValueType.complex,
254
- value: { imaginary: 0, real: base.value },
255
- };
256
- }
257
- */
261
+ const a = CoerceComplex(base);
262
+ const b = CoerceComplex(exponent);
258
263
 
259
- const a = base.type === ValueType.complex ? base.value :
260
- { real: base.value || 0, imaginary: 0, };
264
+ if (a && b) {
265
+ const value = ComplexPower(a, b);
266
+ return ComplexOrReal(value);
267
+ }
261
268
 
262
- const b = exponent.type === ValueType.complex ? exponent.value :
263
- { real: exponent.value || 0, imaginary: 0, };
269
+ return ValueError();
264
270
 
265
- const value = ComplexPower(a, b);
266
- return ComplexOrReal(value);
267
271
 
268
- }),
272
+ },
269
273
  },
270
274
 
271
275
  };
@@ -381,9 +385,9 @@ export const BaseFunctionLibrary: FunctionMap = {
381
385
  Date: {
382
386
  description: 'Constructs a Lotus date from parts',
383
387
  arguments: [
384
- { name: 'year' },
385
- { name: 'month' },
386
- { name: 'day' },
388
+ { name: 'year', unroll: true },
389
+ { name: 'month', unroll: true },
390
+ { name: 'day', unroll: true },
387
391
  ],
388
392
  fn: (year: number, month: number, day: number) => {
389
393
  const date = new Date();
@@ -490,8 +494,8 @@ export const BaseFunctionLibrary: FunctionMap = {
490
494
  Cell: {
491
495
  description: 'Returns data about a cell',
492
496
  arguments: [
493
- { name: 'type', description: 'Type of data to return' },
494
- { name: 'reference', description: 'Cell reference', metadata: true },
497
+ { name: 'type', description: 'Type of data to return', unroll: true, },
498
+ { name: 'reference', description: 'Cell reference', metadata: true, unroll: true, },
495
499
  ],
496
500
 
497
501
  // there's no concept of "structure volatile", and structure events
@@ -500,7 +504,7 @@ export const BaseFunctionLibrary: FunctionMap = {
500
504
 
501
505
  // volatile: true,
502
506
 
503
- fn: Utils.ApplyAsArray2((type: string, reference: UnionValue): UnionValue => {
507
+ fn: (type: string, reference: UnionValue): UnionValue => {
504
508
 
505
509
  if (!UnionIsMetadata(reference)) {
506
510
  return ReferenceError();
@@ -518,13 +522,14 @@ export const BaseFunctionLibrary: FunctionMap = {
518
522
 
519
523
  return { type: ValueType.error, value: NotImplError.error };
520
524
 
521
- }),
525
+ },
526
+
522
527
  },
523
528
 
524
529
  Year: {
525
530
  description: 'Returns year from date',
526
531
  arguments: [{
527
- name: 'date',
532
+ name: 'date', unroll: true,
528
533
  }],
529
534
  fn: (source: number): UnionValue => {
530
535
  return Box(new Date(LotusDate(source)).getUTCFullYear());
@@ -535,7 +540,7 @@ export const BaseFunctionLibrary: FunctionMap = {
535
540
  Month: {
536
541
  description: 'Returns month from date',
537
542
  arguments: [{
538
- name: 'date',
543
+ name: 'date', unroll: true,
539
544
  }],
540
545
  fn: (source: number): UnionValue => {
541
546
  return Box(new Date(LotusDate(source)).getUTCMonth() + 1); // 0-based
@@ -546,7 +551,7 @@ export const BaseFunctionLibrary: FunctionMap = {
546
551
  Day: {
547
552
  description: 'Returns day of month from date',
548
553
  arguments: [{
549
- name: 'date',
554
+ name: 'date', unroll: true,
550
555
  }],
551
556
  fn: (source: number): UnionValue => {
552
557
  return Box(new Date(LotusDate(source)).getUTCDate());
@@ -555,24 +560,24 @@ export const BaseFunctionLibrary: FunctionMap = {
555
560
 
556
561
  Radians: {
557
562
  description: 'Converts degrees to radians',
558
- arguments: [{ name: 'Degrees', description: 'Angle in degrees' }],
559
- fn: Utils.ApplyAsArray((degrees: number): UnionValue => {
563
+ arguments: [{ name: 'Degrees', description: 'Angle in degrees', unroll: true }],
564
+ fn: (degrees: number): UnionValue => {
560
565
  return Box(degrees * Math.PI / 180);
561
- }),
566
+ },
562
567
  },
563
568
 
564
569
  Degrees: {
565
570
  description: 'Converts radians to degrees',
566
- arguments: [{ name: 'Radians', description: 'Angle in radians' }],
567
- fn: Utils.ApplyAsArray((radians: number): UnionValue => {
571
+ arguments: [{ name: 'Radians', description: 'Angle in radians', unroll: true }],
572
+ fn: (radians: number): UnionValue => {
568
573
  return Box(radians / Math.PI * 180);
569
- }),
574
+ },
570
575
  },
571
576
 
572
577
  CountA: {
573
578
  description: 'Counts cells that are not empty',
574
- fn: (...args: unknown[]): UnionValue => {
575
- return Box(Utils.FlattenUnboxed(args).reduce((a: number, b: unknown) => {
579
+ fn: (...args: CellValue[]): UnionValue => {
580
+ return Box(Utils.FlattenCellValues(args).reduce((a: number, b: unknown) => {
576
581
  if (typeof b === 'undefined') return a;
577
582
  return a + 1;
578
583
  }, 0));
@@ -581,8 +586,8 @@ export const BaseFunctionLibrary: FunctionMap = {
581
586
 
582
587
  Count: {
583
588
  description: 'Counts cells that contain numbers',
584
- fn: (...args: unknown[]): UnionValue => {
585
- return Box(Utils.FlattenUnboxed(args).reduce((a: number, b: unknown) => {
589
+ fn: (...args: CellValue[]): UnionValue => {
590
+ return Box(Utils.FlattenCellValues(args).reduce((a: number, b: unknown) => {
586
591
  if (typeof b === 'number' || IsComplex(b)) return a + 1;
587
592
  return a;
588
593
  }, 0));
@@ -590,9 +595,9 @@ export const BaseFunctionLibrary: FunctionMap = {
590
595
  },
591
596
 
592
597
  Or: {
593
- fn: (...args: unknown[]): UnionValue => {
598
+ fn: (...args: CellValue[]): UnionValue => {
594
599
  let result = false;
595
- args = Utils.FlattenUnboxed(args);
600
+ args = Utils.FlattenCellValues(args);
596
601
  for (const arg of args) {
597
602
  result = result || !!arg;
598
603
  }
@@ -601,9 +606,9 @@ export const BaseFunctionLibrary: FunctionMap = {
601
606
  },
602
607
 
603
608
  And: {
604
- fn: (...args: unknown[]): UnionValue => {
609
+ fn: (...args: CellValue[]): UnionValue => {
605
610
  let result = true;
606
- args = Utils.FlattenUnboxed(args);
611
+ args = Utils.FlattenCellValues(args);
607
612
  for (const arg of args) {
608
613
  result = result && !!arg;
609
614
  }
@@ -612,7 +617,8 @@ export const BaseFunctionLibrary: FunctionMap = {
612
617
  },
613
618
 
614
619
  Not: {
615
- fn: Utils.ApplyAsArray((...args: unknown[]): UnionValue => {
620
+ arguments: [{ unroll: true }],
621
+ fn: (...args: unknown[]): UnionValue => {
616
622
  if (args.length === 0) {
617
623
  return ArgumentError();
618
624
  }
@@ -620,7 +626,7 @@ export const BaseFunctionLibrary: FunctionMap = {
620
626
  return Box(!args[0]);
621
627
  }
622
628
  return Box(true);
623
- })
629
+ },
624
630
  },
625
631
 
626
632
  If: {
@@ -663,10 +669,11 @@ export const BaseFunctionLibrary: FunctionMap = {
663
669
  Fact: {
664
670
  description: 'Returns the factorial of a number',
665
671
  arguments: [
666
- { name: 'number' },
672
+ { name: 'number', unroll: true },
667
673
  ],
668
- fn: Utils.ApplyAsArray((number: number): UnionValue => {
669
- number = Math.floor(number);
674
+ fn: (number: number): UnionValue => {
675
+ number = Math.round(number);
676
+
670
677
  let value = 1;
671
678
  while (number > 1) {
672
679
  value *= number;
@@ -676,40 +683,29 @@ export const BaseFunctionLibrary: FunctionMap = {
676
683
  type: ValueType.number,
677
684
  value,
678
685
  }
679
- }),
686
+ },
680
687
  },
681
688
 
682
689
  Power: {
683
690
  description: 'Returns base raised to the given power',
684
691
  arguments: [
685
- { name: 'base', boxed: true, },
686
- { name: 'exponent', boxed: true, }
692
+ { name: 'base', boxed: true, unroll: true, },
693
+ { name: 'exponent', boxed: true, unroll: true, }
687
694
  ],
688
- fn: Utils.ApplyAsArray2((base: UnionValue, exponent: UnionValue): UnionValue => {
695
+ fn: (base: UnionValue, exponent: UnionValue): UnionValue => {
696
+
697
+ const a = CoerceComplex(base);
698
+ const b = CoerceComplex(exponent);
689
699
 
690
- /*
691
- if (base.type === ValueType.number) {
692
- base = {
693
- type: ValueType.complex,
694
- value: { imaginary: 0, real: base.value },
695
- };
700
+ if (!a || !b) {
701
+ return ValueError();
696
702
  }
697
- */
698
703
 
699
704
  if (base.type === ValueType.complex || exponent.type === ValueType.complex) {
700
-
701
- const a = base.type === ValueType.complex ? base.value :
702
- { real: base.value || 0, imaginary: 0, };
703
- const b = exponent.type === ValueType.complex ? exponent.value :
704
- { real: exponent.value || 0, imaginary: 0, };
705
-
706
- const value = ComplexPower(a, b);
707
-
708
- return ComplexOrReal(value);
709
-
705
+ return ComplexOrReal(ComplexPower(a, b));
710
706
  }
711
707
  else {
712
- const value = Math.pow(base.value, exponent.value);
708
+ const value = Math.pow(a.real, b.real);
713
709
  if (isNaN(value)) {
714
710
  return ValueError();
715
711
  }
@@ -717,16 +713,20 @@ export const BaseFunctionLibrary: FunctionMap = {
717
713
  // return Box(Math.pow(base.value, exponent.value))
718
714
  }
719
715
 
720
- }),
716
+ },
721
717
  },
722
718
 
723
719
  Mod: {
724
- fn: Utils.ApplyAsArray2((num: number, divisor: number): UnionValue => {
720
+ arguments: [
721
+ { unroll: true },
722
+ { unroll: true },
723
+ ],
724
+ fn: (num: number, divisor: number): UnionValue => {
725
725
  if (!divisor) {
726
726
  return DivideByZeroError();
727
727
  }
728
728
  return Box(num % divisor);
729
- })
729
+ },
730
730
  },
731
731
 
732
732
  Large: {
@@ -736,22 +736,19 @@ export const BaseFunctionLibrary: FunctionMap = {
736
736
  name: 'values',
737
737
  },
738
738
  {
739
- name: 'index',
739
+ name: 'index', unroll: true,
740
740
  }
741
741
  ],
742
742
 
743
- // OK a little tricky here -- we're going to reverse the arguments
744
- // so we can call apply as array, but applying against the trailing
745
- // arguments.
746
-
747
- fn: Utils.ApplyAsArraySwap((data: any, index: number) => {
743
+ fn: (data: CellValue[], index: number) => {
748
744
 
749
745
  if (index <= 0) {
750
746
  return ArgumentError();
751
747
  }
752
748
 
753
- const flat = Utils.FlattenUnboxed(data);
754
- const numeric: number[] = flat.filter(test => typeof test === 'number');
749
+ // const flat = Utils.FlattenCellValues(data);
750
+ // const numeric: number[] = flat.filter((test): test is number => typeof test === 'number');
751
+ const numeric = Utils.FlattenNumbers(data);
755
752
  numeric.sort((a, b) => b - a);
756
753
 
757
754
  if (index <= numeric.length) {
@@ -762,7 +759,8 @@ export const BaseFunctionLibrary: FunctionMap = {
762
759
  }
763
760
 
764
761
  return ArgumentError();
765
- }),
762
+ },
763
+
766
764
  },
767
765
 
768
766
  Small: {
@@ -772,20 +770,19 @@ export const BaseFunctionLibrary: FunctionMap = {
772
770
  name: 'values',
773
771
  },
774
772
  {
775
- name: 'index',
773
+ name: 'index', unroll: true,
776
774
  }
777
775
  ],
778
776
 
779
- // see large, above
780
-
781
- fn: Utils.ApplyAsArraySwap((data: any, index: number) => {
777
+ fn: (data: CellValue[], index: number) => {
782
778
 
783
779
  if (index <= 0) {
784
780
  return ArgumentError();
785
781
  }
786
782
 
787
- const flat = Utils.FlattenUnboxed(data);
788
- const numeric: number[] = flat.filter(test => typeof test === 'number');
783
+ // const flat = Utils.FlattenCellValues(data);
784
+ // const numeric: number[] = flat.filter((test): test is number => typeof test === 'number');
785
+ const numeric = Utils.FlattenNumbers(data);
789
786
  numeric.sort((a, b) => a - b);
790
787
 
791
788
  if (index <= numeric.length) {
@@ -797,7 +794,8 @@ export const BaseFunctionLibrary: FunctionMap = {
797
794
 
798
795
  return ArgumentError();
799
796
 
800
- }),
797
+ },
798
+
801
799
  },
802
800
 
803
801
  /**
@@ -811,12 +809,12 @@ export const BaseFunctionLibrary: FunctionMap = {
811
809
  arguments: [
812
810
  { name: 'values' }
813
811
  ],
814
- fn: (...args: any[]): UnionValue => {
812
+ fn: (...args: CellValue[]): UnionValue => {
815
813
 
816
- args = Utils.FlattenUnboxed(args);
814
+ args = Utils.FlattenCellValues(args);
817
815
 
818
816
  if(args.every(test => typeof test === 'number')) {
819
- args.sort((a, b) => a - b);
817
+ (args as number[]).sort((a, b) => a - b);
820
818
  }
821
819
  else {
822
820
  args.sort(); // lexical
@@ -850,19 +848,21 @@ export const BaseFunctionLibrary: FunctionMap = {
850
848
  },
851
849
 
852
850
  Max: {
853
- fn: (...args: any[]): UnionValue => {
851
+ fn: (...args: number[]): UnionValue => {
854
852
  return {
855
853
  type: ValueType.number,
856
- value: Math.max.apply(0, Utils.FlattenUnboxed(args).filter(x => typeof x === 'number')),
854
+ // value: Math.max.apply(0, Utils.FlattenCellValues(args).filter((x): x is number => typeof x === 'number')),
855
+ value: Math.max.apply(0, Utils.FlattenNumbers(args)),
857
856
  };
858
857
  },
859
858
  },
860
859
 
861
860
  Min: {
862
- fn: (...args: any[]): UnionValue => {
861
+ fn: (...args: number[]): UnionValue => {
863
862
  return {
864
863
  type: ValueType.number,
865
- value: Math.min.apply(0, Utils.FlattenUnboxed(args).filter(x => typeof x === 'number')),
864
+ // value: Math.min.apply(0, Utils.FlattenCellValues(args).filter((x): x is number => typeof x === 'number')),
865
+ value: Math.min.apply(0, Utils.FlattenNumbers(args)),
866
866
  };
867
867
  },
868
868
  },
@@ -905,15 +905,19 @@ export const BaseFunctionLibrary: FunctionMap = {
905
905
 
906
906
  SumProduct: {
907
907
  description: 'Returns the sum of pairwise products of two or more ranges',
908
- fn: (...args: any[]): UnionValue => {
908
+ fn: (...args: CellValue[][]): UnionValue => {
909
909
 
910
- const flattened = args.map(arg => Utils.FlattenUnboxed(arg));
910
+ const flattened = args.map(arg => Utils.FlattenCellValues(arg));
911
911
  const len = Math.max.apply(0, flattened.map(x => x.length));
912
912
 
913
913
  let sum = 0;
914
914
  for (let i = 0; i < len; i++) {
915
915
  sum += flattened.reduce((a, arg) => {
916
- return a * (arg[i] || 0);
916
+ let ai: CellValue = arg[i];
917
+ if (ai === true) {
918
+ ai = 1;
919
+ }
920
+ return (typeof ai === 'number') ? a * ai : 0;
917
921
  }, 1);
918
922
  }
919
923
 
@@ -986,9 +990,9 @@ export const BaseFunctionLibrary: FunctionMap = {
986
990
  ],
987
991
  xlfn: true,
988
992
  fn: (
989
- lookup_value: any,
990
- lookup_array: any[][],
991
- return_array: any[][],
993
+ lookup_value: IntrinsicValue,
994
+ lookup_array: IntrinsicValue[][],
995
+ return_array: IntrinsicValue[][],
992
996
  not_found?: UnionValue,
993
997
  match_mode = 0,
994
998
  search_mode = 1,
@@ -1021,7 +1025,7 @@ export const BaseFunctionLibrary: FunctionMap = {
1021
1025
  return ValueError();
1022
1026
  }
1023
1027
 
1024
- let transpose = (lookup_array.length === 1);
1028
+ const transpose = (lookup_array.length === 1);
1025
1029
  if (transpose) {
1026
1030
  lookup_array = Utils.TransposeArray(lookup_array);
1027
1031
  return_array = Utils.TransposeArray(return_array);
@@ -1115,11 +1119,11 @@ export const BaseFunctionLibrary: FunctionMap = {
1115
1119
  // wildcard string match. we only handle strings for
1116
1120
  // this case (see above).
1117
1121
 
1118
- const pattern = Utils.ParseWildcards(lookup_value);
1122
+ const pattern = Utils.ParseWildcards(lookup_value?.toString() || '');
1119
1123
  const regex = new RegExp('^' + pattern + '$', 'i'); //.exec(lookup_value);
1120
1124
 
1121
1125
  for (let i = 0; i < lookup_array.length; i++) {
1122
- let value = lookup_array[i][0];
1126
+ const value = lookup_array[i][0];
1123
1127
  if (typeof value === 'string' && regex.exec(value)) {
1124
1128
  return ReturnIndex(i);
1125
1129
  }
@@ -1146,68 +1150,6 @@ export const BaseFunctionLibrary: FunctionMap = {
1146
1150
  break;
1147
1151
  }
1148
1152
 
1149
- /*
1150
- const flat_lookup = Utils.FlattenUnboxed(lookup_array);
1151
- const flat_return = Utils.FlattenUnboxed(return_array);
1152
-
1153
- // maybe reverse...
1154
-
1155
- if (search_mode < 0) {
1156
- flat_lookup.reverse();
1157
- flat_return.reverse();
1158
- }
1159
-
1160
- // if value is not a string, then we can ignore wildcards.
1161
- // in that case convert to exact match.
1162
-
1163
- if (match_mode === 2 && typeof lookup_value !== 'string') {
1164
- match_mode = 0;
1165
- }
1166
-
1167
- switch (match_mode) {
1168
- case 2:
1169
-
1170
- {
1171
-
1172
- // wildcard string match. we only handle strings
1173
- // for wildcard matching (handled above).
1174
-
1175
- const pattern = Utils.ParseWildcards(lookup_value);
1176
- const regex = new RegExp('^' + pattern + '$', 'i'); //.exec(lookup_value);
1177
-
1178
- for (let i = 0; i < flat_lookup.length; i++) {
1179
- let value = flat_lookup[i];
1180
- if (typeof value === 'string' && regex.exec(value)) {
1181
- return Box(flat_return[i]);
1182
- }
1183
- }
1184
-
1185
-
1186
- }
1187
-
1188
- break;
1189
-
1190
- case 0:
1191
-
1192
- // return exact match or NA/default. in this case
1193
- // "exact" means icase (but not wildcard)
1194
-
1195
- if (typeof lookup_value === 'string') {
1196
- lookup_value = lookup_value.toLowerCase();
1197
- }
1198
- for (let i = 0; i < flat_lookup.length; i++) {
1199
- let value = flat_lookup[i];
1200
- if (typeof value === 'string') {
1201
- value = value.toLowerCase();
1202
- }
1203
- if (value === lookup_value) {
1204
- return Box(flat_return[i]);
1205
- }
1206
- }
1207
-
1208
- break;
1209
- }
1210
- */
1211
1153
 
1212
1154
  // FIXME: if we're expecting to return an array maybe we should
1213
1155
  // pack it up as an array? if it's not already an array? (...)
@@ -1222,7 +1164,7 @@ export const BaseFunctionLibrary: FunctionMap = {
1222
1164
  */
1223
1165
  HLookup: {
1224
1166
  arguments: [...zlookup_arguments],
1225
- fn: (value: any, table: any[][], col: number, inexact = true): UnionValue => {
1167
+ fn: (value: number|boolean|string|undefined, table: (number|boolean|string|undefined)[][], col: number, inexact = true): UnionValue => {
1226
1168
  return ZLookup(value, table, col, inexact, true);
1227
1169
  },
1228
1170
  },
@@ -1233,14 +1175,14 @@ export const BaseFunctionLibrary: FunctionMap = {
1233
1175
  */
1234
1176
  VLookup: {
1235
1177
  arguments: [...zlookup_arguments],
1236
- fn: (value: any, table: any[][], col: number, inexact = true): UnionValue => {
1178
+ fn: (value: number|boolean|string|undefined, table: (number|boolean|string|undefined)[][], col: number, inexact = true): UnionValue => {
1237
1179
  return ZLookup(value, table, col, inexact, false);
1238
1180
  },
1239
1181
  },
1240
1182
 
1241
1183
  Product: {
1242
1184
  arguments: [{boxed: true}],
1243
- fn: (...args: any[]): UnionValue => {
1185
+ fn: (...args: UnionValue[]): UnionValue => {
1244
1186
 
1245
1187
  let product: Complex = { real: 1, imaginary: 0 };
1246
1188
 
@@ -1269,54 +1211,64 @@ export const BaseFunctionLibrary: FunctionMap = {
1269
1211
  },
1270
1212
 
1271
1213
  Log: {
1214
+ arguments: [ { unroll: true }, { unroll: true } ],
1215
+
1272
1216
  /** default is base 10; allow specific base */
1273
- fn: Utils.ApplyAsArray2((a: number, base = 10): UnionValue => {
1217
+ fn: (a: number, base = 10): UnionValue => {
1274
1218
  return { type: ValueType.number, value: Math.log(a) / Math.log(base) };
1275
- }),
1219
+ },
1276
1220
  },
1277
1221
 
1278
1222
  Log10: {
1279
- fn: Utils.ApplyAsArray((a: number): UnionValue => {
1223
+ arguments: [{ unroll: true }],
1224
+ fn: (a: number): UnionValue => {
1280
1225
  return { type: ValueType.number, value: Math.log(a) / Math.log(10) };
1281
- }),
1226
+ },
1282
1227
  },
1283
1228
 
1284
1229
  Ln: {
1285
- fn: Utils.ApplyAsArray((a: number): UnionValue => {
1230
+ arguments: [{ unroll: true }],
1231
+ fn: (a: number): UnionValue => {
1286
1232
  return { type: ValueType.number, value: Math.log(a) };
1287
- }),
1233
+ },
1288
1234
  },
1289
1235
 
1290
1236
  Round: {
1291
- fn: Utils.ApplyAsArray2((a, digits = 0) => {
1237
+ arguments: [ { unroll: true }, { unroll: true } ], // FIXME: lazy
1238
+
1239
+ fn: (a, digits = 0) => {
1292
1240
  const m = Math.pow(10, digits);
1293
1241
  return {
1294
1242
  type: ValueType.number,
1295
1243
  value: Math.round(m * a) / m,
1296
1244
  };
1297
- }),
1245
+ },
1298
1246
  },
1299
1247
 
1300
1248
  RoundDown: {
1301
- fn: Utils.ApplyAsArray2((a, digits = 0) => {
1249
+ arguments: [ { unroll: true }, { unroll: true } ], // FIXME: lazy
1250
+
1251
+ fn: (a, digits = 0) => {
1302
1252
  const m = Math.pow(10, digits);
1303
1253
  const positive = a >= 0;
1304
1254
  return {
1305
1255
  type: ValueType.number,
1306
1256
  value: positive ? Math.floor(m * a) / m : Math.ceil(m * a) / m,
1307
1257
  };
1308
- }),
1258
+ },
1309
1259
  },
1310
1260
 
1311
1261
  RoundUp: {
1312
- fn: Utils.ApplyAsArray2((a, digits = 0) => {
1262
+ arguments: [ { unroll: true }, { unroll: true } ], // FIXME: lazy
1263
+
1264
+ fn: (a, digits = 0) => {
1313
1265
  const m = Math.pow(10, digits);
1314
1266
  const positive = a >= 0;
1315
1267
  return {
1316
1268
  type: ValueType.number,
1317
1269
  value: positive ? Math.ceil(m * a) / m : Math.floor(m * a) / m,
1318
1270
  };
1319
- }),
1271
+ },
1320
1272
  },
1321
1273
 
1322
1274
  /*
@@ -1373,7 +1325,7 @@ export const BaseFunctionLibrary: FunctionMap = {
1373
1325
 
1374
1326
  return {
1375
1327
  type: ValueType.string,
1376
- value: a.value.toString().split('').reverse().join(''),
1328
+ value: (a.value??'').toString().split('').reverse().join(''),
1377
1329
  };
1378
1330
  },
1379
1331
  },
@@ -1383,15 +1335,21 @@ export const BaseFunctionLibrary: FunctionMap = {
1383
1335
  */
1384
1336
  Exp: {
1385
1337
  arguments: [
1386
- { boxed: true },
1338
+ { boxed: true, unroll: true },
1387
1339
  ],
1388
- fn: Utils.ApplyAsArray((x: UnionValue) => {
1340
+ fn: (x: UnionValue) => {
1341
+
1389
1342
  if (x.type === ValueType.complex) {
1390
1343
  const value = ComplexExp(x.value);
1391
1344
  return ComplexOrReal(value);
1392
1345
  }
1393
- return { type: ValueType.number, value: Math.exp(x.value || 0) };
1394
- }),
1346
+
1347
+ if (x.type !== ValueType.number) {
1348
+ return ValueError();
1349
+ }
1350
+
1351
+ return { type: ValueType.number, value: Math.exp(x.value) };
1352
+ },
1395
1353
  },
1396
1354
 
1397
1355
  /**
@@ -1400,25 +1358,30 @@ export const BaseFunctionLibrary: FunctionMap = {
1400
1358
  */
1401
1359
  Abs: {
1402
1360
  arguments: [
1403
- { boxed: true },
1361
+ { boxed: true, unroll: true },
1404
1362
  ],
1405
- fn: Utils.ApplyAsArray((a: UnionValue) => {
1363
+ fn: (a: UnionValue) => {
1406
1364
  if (a.type === ValueType.complex) {
1407
1365
  return {
1408
1366
  type: ValueType.number,
1409
1367
  value: Math.sqrt(a.value.real * a.value.real + a.value.imaginary * a.value.imaginary),
1410
1368
  };
1411
1369
  }
1370
+
1371
+ if (a.type !== ValueType.number) {
1372
+ return ValueError();
1373
+ }
1374
+
1412
1375
  return { type: ValueType.number, value: Math.abs(a.value || 0) };
1413
- }),
1376
+ },
1414
1377
  },
1415
1378
 
1416
1379
  Simplify: {
1417
1380
  arguments: [
1418
- { name: 'value' },
1419
- { name: 'significant digits' },
1381
+ { name: 'value', unroll: true, },
1382
+ { name: 'significant digits', unroll: true, },
1420
1383
  ],
1421
- fn: Utils.ApplyAsArray2((value: number, significant_digits = 2): UnionValue => {
1384
+ fn: (value: number, significant_digits = 2): UnionValue => {
1422
1385
  significant_digits = significant_digits || 2;
1423
1386
  if (value === 0) {
1424
1387
  return { type: ValueType.number, value };
@@ -1430,7 +1393,7 @@ export const BaseFunctionLibrary: FunctionMap = {
1430
1393
  type: ValueType.number,
1431
1394
  value: Math.round(value / x) * x * negative
1432
1395
  };
1433
- }),
1396
+ },
1434
1397
  },
1435
1398
 
1436
1399
  Erf: {
@@ -1508,9 +1471,9 @@ export const BaseFunctionLibrary: FunctionMap = {
1508
1471
  Sqrt: {
1509
1472
  description: 'Returns the square root of the argument',
1510
1473
  arguments: [
1511
- {boxed: true},
1474
+ { boxed: true, unroll: true },
1512
1475
  ],
1513
- fn: Utils.ApplyAsArray((ref: UnionValue): UnionValue => {
1476
+ fn: (ref: UnionValue): UnionValue => {
1514
1477
 
1515
1478
  // little bit torn on this. what should sqrt(-1) return? a complex
1516
1479
  // number, or NaN? or should we control that with a flag?
@@ -1535,6 +1498,15 @@ export const BaseFunctionLibrary: FunctionMap = {
1535
1498
  }
1536
1499
  }
1537
1500
  */
1501
+ else if (ref.type === ValueType.number) {
1502
+ return {
1503
+ type: ValueType.number, value: Math.sqrt(ref.value),
1504
+ };
1505
+ }
1506
+
1507
+ return ValueError();
1508
+
1509
+ /*
1538
1510
  else {
1539
1511
  const value = Math.sqrt(ref.value);
1540
1512
  if (isNaN(value)) {
@@ -1542,18 +1514,19 @@ export const BaseFunctionLibrary: FunctionMap = {
1542
1514
  }
1543
1515
  return { type: ValueType.number, value };
1544
1516
  }
1545
- }),
1517
+ */
1518
+ },
1546
1519
  },
1547
1520
 
1548
1521
  HexToDec: {
1549
- arguments: [{ description: 'hexadecimal string' }],
1522
+ arguments: [{ description: 'hexadecimal string', unroll: true }],
1550
1523
  fn: (hex: string): UnionValue => {
1551
1524
  return { type: ValueType.number, value: parseInt(hex, 16) };
1552
1525
  },
1553
1526
  },
1554
1527
 
1555
1528
  DecToHex: {
1556
- arguments: [{ description: 'number' }],
1529
+ arguments: [{ description: 'number', unroll: true }],
1557
1530
  fn: (num: number): UnionValue => {
1558
1531
  return { type: ValueType.string, value: num.toString(16) };
1559
1532
  },
@@ -1608,8 +1581,8 @@ export const BaseFunctionLibrary: FunctionMap = {
1608
1581
 
1609
1582
  if (area.type === ValueType.array) {
1610
1583
 
1611
- const cols = area.value.length;
1612
- const rows = area.value[0]?.length;
1584
+ // const cols = area.value.length;
1585
+ // const rows = area.value[0]?.length;
1613
1586
 
1614
1587
  // how is uniqueness defined in this context? (...)
1615
1588
 
@@ -1690,7 +1663,7 @@ export const BaseFunctionLibrary: FunctionMap = {
1690
1663
 
1691
1664
  const tmp = Utils.FlattenBoxed([area]);
1692
1665
 
1693
- let sum = 0;
1666
+ // let sum = 0;
1694
1667
  let count = 0;
1695
1668
  let min = 0;
1696
1669
  let max = 0;
@@ -1719,7 +1692,7 @@ export const BaseFunctionLibrary: FunctionMap = {
1719
1692
  min = static_min;
1720
1693
  }
1721
1694
 
1722
- let range = max - min;
1695
+ const range = max - min;
1723
1696
 
1724
1697
  let rows = 1;
1725
1698
  let columns = 1;
@@ -1838,6 +1811,7 @@ for (const name of Object.getOwnPropertyNames(Math)) {
1838
1811
  case 'function':
1839
1812
  // console.info("MATH FUNC", name);
1840
1813
  BaseFunctionLibrary[name] = {
1814
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1841
1815
  fn: (...args: any) => {
1842
1816
  return Box(value(...args));
1843
1817
  },