@trebco/treb 23.6.2 → 25.0.0-rc1

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 (217) hide show
  1. package/.eslintignore +8 -0
  2. package/.eslintrc.js +164 -0
  3. package/README-shadow-DOM.md +88 -0
  4. package/README.md +37 -130
  5. package/api-config.json +29 -0
  6. package/api-generator/api-generator-types.ts +82 -0
  7. package/api-generator/api-generator.ts +1172 -0
  8. package/api-generator/package.json +3 -0
  9. package/build/treb-spreadsheet.mjs +14 -0
  10. package/{treb.d.ts → build/treb.d.ts} +293 -299
  11. package/esbuild-custom-element.mjs +336 -0
  12. package/esbuild.js +305 -0
  13. package/package.json +43 -14
  14. package/treb-base-types/package.json +5 -0
  15. package/treb-base-types/src/api_types.ts +36 -0
  16. package/treb-base-types/src/area.ts +583 -0
  17. package/treb-base-types/src/basic_types.ts +45 -0
  18. package/treb-base-types/src/cell.ts +612 -0
  19. package/treb-base-types/src/cells.ts +1066 -0
  20. package/treb-base-types/src/color.ts +124 -0
  21. package/treb-base-types/src/import.ts +71 -0
  22. package/treb-base-types/src/index-standalone.ts +29 -0
  23. package/treb-base-types/src/index.ts +42 -0
  24. package/treb-base-types/src/layout.ts +47 -0
  25. package/treb-base-types/src/localization.ts +187 -0
  26. package/treb-base-types/src/rectangle.ts +145 -0
  27. package/treb-base-types/src/render_text.ts +72 -0
  28. package/treb-base-types/src/style.ts +545 -0
  29. package/treb-base-types/src/table.ts +109 -0
  30. package/treb-base-types/src/text_part.ts +54 -0
  31. package/treb-base-types/src/theme.ts +608 -0
  32. package/treb-base-types/src/union.ts +152 -0
  33. package/treb-base-types/src/value-type.ts +164 -0
  34. package/treb-base-types/style/resizable.css +59 -0
  35. package/treb-calculator/modern.tsconfig.json +11 -0
  36. package/treb-calculator/package.json +5 -0
  37. package/treb-calculator/src/calculator.ts +2546 -0
  38. package/treb-calculator/src/complex-math.ts +558 -0
  39. package/treb-calculator/src/dag/array-vertex.ts +198 -0
  40. package/treb-calculator/src/dag/graph.ts +951 -0
  41. package/treb-calculator/src/dag/leaf_vertex.ts +118 -0
  42. package/treb-calculator/src/dag/spreadsheet_vertex.ts +327 -0
  43. package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +44 -0
  44. package/treb-calculator/src/dag/vertex.ts +352 -0
  45. package/treb-calculator/src/descriptors.ts +162 -0
  46. package/treb-calculator/src/expression-calculator.ts +1069 -0
  47. package/treb-calculator/src/function-error.ts +103 -0
  48. package/treb-calculator/src/function-library.ts +103 -0
  49. package/treb-calculator/src/functions/base-functions.ts +1214 -0
  50. package/treb-calculator/src/functions/checkbox.ts +164 -0
  51. package/treb-calculator/src/functions/complex-functions.ts +253 -0
  52. package/treb-calculator/src/functions/finance-functions.ts +399 -0
  53. package/treb-calculator/src/functions/information-functions.ts +102 -0
  54. package/treb-calculator/src/functions/matrix-functions.ts +182 -0
  55. package/treb-calculator/src/functions/sparkline.ts +335 -0
  56. package/treb-calculator/src/functions/statistics-functions.ts +350 -0
  57. package/treb-calculator/src/functions/text-functions.ts +298 -0
  58. package/treb-calculator/src/index.ts +27 -0
  59. package/treb-calculator/src/notifier-types.ts +59 -0
  60. package/treb-calculator/src/primitives.ts +428 -0
  61. package/treb-calculator/src/utilities.ts +305 -0
  62. package/treb-charts/package.json +5 -0
  63. package/treb-charts/src/chart-functions.ts +156 -0
  64. package/treb-charts/src/chart-types.ts +230 -0
  65. package/treb-charts/src/chart.ts +1288 -0
  66. package/treb-charts/src/index.ts +24 -0
  67. package/treb-charts/src/main.ts +37 -0
  68. package/treb-charts/src/rectangle.ts +52 -0
  69. package/treb-charts/src/renderer.ts +1841 -0
  70. package/treb-charts/src/util.ts +122 -0
  71. package/treb-charts/style/charts.scss +221 -0
  72. package/treb-charts/style/old-charts.scss +250 -0
  73. package/treb-embed/markup/layout.html +137 -0
  74. package/treb-embed/markup/toolbar.html +175 -0
  75. package/treb-embed/modern.tsconfig.json +25 -0
  76. package/treb-embed/src/custom-element/content-types.d.ts +18 -0
  77. package/treb-embed/src/custom-element/global.d.ts +11 -0
  78. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +1227 -0
  79. package/treb-embed/src/custom-element/treb-global.ts +44 -0
  80. package/treb-embed/src/custom-element/treb-spreadsheet-element.ts +52 -0
  81. package/treb-embed/src/embedded-spreadsheet.ts +5362 -0
  82. package/treb-embed/src/index.ts +16 -0
  83. package/treb-embed/src/language-model.ts +41 -0
  84. package/treb-embed/src/options.ts +320 -0
  85. package/treb-embed/src/progress-dialog.ts +228 -0
  86. package/treb-embed/src/selection-state.ts +16 -0
  87. package/treb-embed/src/spinner.ts +42 -0
  88. package/treb-embed/src/toolbar-message.ts +96 -0
  89. package/treb-embed/src/types.ts +167 -0
  90. package/treb-embed/style/autocomplete.scss +103 -0
  91. package/treb-embed/style/dark-theme.scss +114 -0
  92. package/treb-embed/style/defaults.scss +36 -0
  93. package/treb-embed/style/dialog.scss +181 -0
  94. package/treb-embed/style/dropdown-select.scss +101 -0
  95. package/treb-embed/style/formula-bar.scss +193 -0
  96. package/treb-embed/style/grid.scss +374 -0
  97. package/treb-embed/style/layout.scss +424 -0
  98. package/treb-embed/style/mouse-mask.scss +67 -0
  99. package/treb-embed/style/note.scss +92 -0
  100. package/treb-embed/style/overlay-editor.scss +102 -0
  101. package/treb-embed/style/spinner.scss +92 -0
  102. package/treb-embed/style/tab-bar.scss +228 -0
  103. package/treb-embed/style/table.scss +80 -0
  104. package/treb-embed/style/theme-defaults.scss +444 -0
  105. package/treb-embed/style/toolbar.scss +416 -0
  106. package/treb-embed/style/tooltip.scss +68 -0
  107. package/treb-embed/style/treb-icons.scss +130 -0
  108. package/treb-embed/style/treb-spreadsheet-element.scss +20 -0
  109. package/treb-embed/style/z-index.scss +43 -0
  110. package/treb-export/docs/charts.md +68 -0
  111. package/treb-export/modern.tsconfig.json +19 -0
  112. package/treb-export/package.json +4 -0
  113. package/treb-export/src/address-type.ts +77 -0
  114. package/treb-export/src/base-template.ts +22 -0
  115. package/treb-export/src/column-width.ts +85 -0
  116. package/treb-export/src/drawing2/chart-template-components2.ts +389 -0
  117. package/treb-export/src/drawing2/chart2.ts +282 -0
  118. package/treb-export/src/drawing2/column-chart-template2.ts +521 -0
  119. package/treb-export/src/drawing2/donut-chart-template2.ts +296 -0
  120. package/treb-export/src/drawing2/drawing2.ts +355 -0
  121. package/treb-export/src/drawing2/embedded-image.ts +71 -0
  122. package/treb-export/src/drawing2/scatter-chart-template2.ts +555 -0
  123. package/treb-export/src/export-worker/export-worker.ts +99 -0
  124. package/treb-export/src/export-worker/index-modern.ts +22 -0
  125. package/treb-export/src/export2.ts +2204 -0
  126. package/treb-export/src/import2.ts +882 -0
  127. package/treb-export/src/relationship.ts +36 -0
  128. package/treb-export/src/shared-strings2.ts +128 -0
  129. package/treb-export/src/template-2.ts +22 -0
  130. package/treb-export/src/unescape_xml.ts +47 -0
  131. package/treb-export/src/workbook-sheet2.ts +182 -0
  132. package/treb-export/src/workbook-style2.ts +1285 -0
  133. package/treb-export/src/workbook-theme2.ts +88 -0
  134. package/treb-export/src/workbook2.ts +491 -0
  135. package/treb-export/src/xml-utils.ts +201 -0
  136. package/treb-export/template/base/[Content_Types].xml +2 -0
  137. package/treb-export/template/base/_rels/.rels +2 -0
  138. package/treb-export/template/base/docProps/app.xml +2 -0
  139. package/treb-export/template/base/docProps/core.xml +12 -0
  140. package/treb-export/template/base/xl/_rels/workbook.xml.rels +2 -0
  141. package/treb-export/template/base/xl/sharedStrings.xml +2 -0
  142. package/treb-export/template/base/xl/styles.xml +2 -0
  143. package/treb-export/template/base/xl/theme/theme1.xml +2 -0
  144. package/treb-export/template/base/xl/workbook.xml +2 -0
  145. package/treb-export/template/base/xl/worksheets/sheet1.xml +2 -0
  146. package/treb-export/template/base.xlsx +0 -0
  147. package/treb-format/package.json +8 -0
  148. package/treb-format/src/format.test.ts +213 -0
  149. package/treb-format/src/format.ts +942 -0
  150. package/treb-format/src/format_cache.ts +199 -0
  151. package/treb-format/src/format_parser.ts +723 -0
  152. package/treb-format/src/index.ts +25 -0
  153. package/treb-format/src/number_format_section.ts +100 -0
  154. package/treb-format/src/value_parser.ts +337 -0
  155. package/treb-grid/package.json +5 -0
  156. package/treb-grid/src/editors/autocomplete.ts +394 -0
  157. package/treb-grid/src/editors/autocomplete_matcher.ts +260 -0
  158. package/treb-grid/src/editors/formula_bar.ts +473 -0
  159. package/treb-grid/src/editors/formula_editor_base.ts +910 -0
  160. package/treb-grid/src/editors/overlay_editor.ts +511 -0
  161. package/treb-grid/src/index.ts +37 -0
  162. package/treb-grid/src/layout/base_layout.ts +2618 -0
  163. package/treb-grid/src/layout/grid_layout.ts +299 -0
  164. package/treb-grid/src/layout/rectangle_cache.ts +86 -0
  165. package/treb-grid/src/render/selection-renderer.ts +414 -0
  166. package/treb-grid/src/render/svg_header_overlay.ts +93 -0
  167. package/treb-grid/src/render/svg_selection_block.ts +187 -0
  168. package/treb-grid/src/render/tile_renderer.ts +2122 -0
  169. package/treb-grid/src/types/annotation.ts +216 -0
  170. package/treb-grid/src/types/border_constants.ts +34 -0
  171. package/treb-grid/src/types/clipboard_data.ts +31 -0
  172. package/treb-grid/src/types/data_model.ts +334 -0
  173. package/treb-grid/src/types/drag_mask.ts +81 -0
  174. package/treb-grid/src/types/grid.ts +7743 -0
  175. package/treb-grid/src/types/grid_base.ts +3644 -0
  176. package/treb-grid/src/types/grid_command.ts +470 -0
  177. package/treb-grid/src/types/grid_events.ts +124 -0
  178. package/treb-grid/src/types/grid_options.ts +97 -0
  179. package/treb-grid/src/types/grid_selection.ts +60 -0
  180. package/treb-grid/src/types/named_range.ts +369 -0
  181. package/treb-grid/src/types/scale-control.ts +202 -0
  182. package/treb-grid/src/types/serialize_options.ts +72 -0
  183. package/treb-grid/src/types/set_range_options.ts +52 -0
  184. package/treb-grid/src/types/sheet.ts +3099 -0
  185. package/treb-grid/src/types/sheet_types.ts +95 -0
  186. package/treb-grid/src/types/tab_bar.ts +464 -0
  187. package/treb-grid/src/types/tile.ts +59 -0
  188. package/treb-grid/src/types/update_flags.ts +75 -0
  189. package/treb-grid/src/util/dom_utilities.ts +44 -0
  190. package/treb-grid/src/util/fontmetrics2.ts +179 -0
  191. package/treb-grid/src/util/ua.ts +104 -0
  192. package/treb-logo.svg +18 -0
  193. package/treb-parser/package.json +5 -0
  194. package/treb-parser/src/csv-parser.ts +122 -0
  195. package/treb-parser/src/index.ts +25 -0
  196. package/treb-parser/src/md-parser.ts +526 -0
  197. package/treb-parser/src/parser-types.ts +397 -0
  198. package/treb-parser/src/parser.test.ts +298 -0
  199. package/treb-parser/src/parser.ts +2673 -0
  200. package/treb-utils/package.json +5 -0
  201. package/treb-utils/src/dispatch.ts +57 -0
  202. package/treb-utils/src/event_source.ts +147 -0
  203. package/treb-utils/src/ievent_source.ts +33 -0
  204. package/treb-utils/src/index.ts +31 -0
  205. package/treb-utils/src/measurement.ts +174 -0
  206. package/treb-utils/src/resizable.ts +160 -0
  207. package/treb-utils/src/scale.ts +137 -0
  208. package/treb-utils/src/serialize_html.ts +124 -0
  209. package/treb-utils/src/template.ts +70 -0
  210. package/treb-utils/src/validate_uri.ts +61 -0
  211. package/tsconfig.json +10 -0
  212. package/tsproject.json +30 -0
  213. package/util/license-plugin-esbuild.js +86 -0
  214. package/util/list-css-vars.sh +46 -0
  215. package/README-esm.md +0 -37
  216. package/treb-bundle.css +0 -2
  217. package/treb-bundle.mjs +0 -15
@@ -0,0 +1,1214 @@
1
+ /*
2
+ * This file is part of TREB.
3
+ *
4
+ * TREB is free software: you can redistribute it and/or modify it under the
5
+ * terms of the GNU General Public License as published by the Free Software
6
+ * Foundation, either version 3 of the License, or (at your option) any
7
+ * later version.
8
+ *
9
+ * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ * details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License along
15
+ * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
+ *
17
+ * Copyright 2022-2023 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+
22
+ import type { FunctionMap } from '../descriptors';
23
+ import * as Utils from '../utilities';
24
+ import { ReferenceError, NotImplError, NAError, ArgumentError, DivideByZeroError, ValueError } from '../function-error';
25
+ import { Box, UnionValue, ValueType, GetValueType,
26
+ RenderFunctionResult, RenderFunctionOptions, ComplexOrReal, Complex } from 'treb-base-types';
27
+ import { Sparkline } from './sparkline';
28
+ import { LotusDate, UnlotusDate } from 'treb-format';
29
+
30
+ import { ClickCheckbox, RenderCheckbox } from './checkbox';
31
+ import { UnionIsMetadata } from '../expression-calculator';
32
+
33
+ import { Exp as ComplexExp, Power as ComplexPower, Multiply as ComplexMultply } from '../complex-math';
34
+
35
+ /**
36
+ * BaseFunctionLibrary is a static object that has basic spreadsheet
37
+ * functions and associated metadata (there's also a list of aliases).
38
+ *
39
+ * Calculator should register this one first, followed by any other
40
+ * application-specific libraries.
41
+ *
42
+ * FIXME: there's no reason this has to be a single, monolithic library.
43
+ * we could split up by category or something.
44
+ *
45
+ * ALSO: add category to descriptor.
46
+ */
47
+
48
+ /** milliseconds in one day, used in time functions */
49
+ // const DAY_MS = 1000 * 60 * 60 * 24;
50
+
51
+ // some functions have semantics that can't be represented inline,
52
+ // or we may want to refer to them from other functions.
53
+
54
+ // OK, just one.
55
+
56
+ /** error function (for gaussian distribution) */
57
+ const erf = (x: number): number => {
58
+
59
+ const a1 = 0.254829592;
60
+ const a2 = -0.284496736;
61
+ const a3 = 1.421413741;
62
+ const a4 = -1.453152027;
63
+ const a5 = 1.061405429;
64
+ const p = 0.3275911;
65
+
66
+ x = Math.abs(x);
67
+ const t = 1 / (1 + p * x);
68
+ return 1 - ((((((a5 * t + a4) * t) + a3) * t + a2) * t) + a1) * t * Math.exp(-1 * x * x);
69
+
70
+ };
71
+
72
+ const sqrt2pi = Math.sqrt(2 * Math.PI);
73
+
74
+ /** imprecise but reasonably fast normsinv function */
75
+ const inverse_normal = (q: number): number => {
76
+
77
+ if (q === 0.50) {
78
+ return 0;
79
+ }
80
+
81
+ const p = (q < 1.0 && q > 0.5) ? (1 - q) : q;
82
+ const t = Math.sqrt(Math.log(1.0 / Math.pow(p, 2.0)));
83
+ const x = t - (2.515517 + 0.802853 * t + 0.010328 * Math.pow(t, 2.0)) /
84
+ (1.0 + 1.432788 * t + 0.189269 * Math.pow(t, 2.0) + 0.001308 * Math.pow(t, 3.0));
85
+
86
+ return (q > 0.5 ? x : -x);
87
+
88
+ };
89
+
90
+ /**
91
+ * alternate functions. these are used (atm) only for changing complex
92
+ * behavior.
93
+ */
94
+ export const AltFunctionLibrary: FunctionMap = {
95
+
96
+ Sqrt: {
97
+ description: 'Returns the square root of the argument',
98
+ arguments: [
99
+ {boxed: true},
100
+ ],
101
+ fn: Utils.ApplyAsArray((ref: UnionValue): UnionValue => {
102
+
103
+ if (ref.type === ValueType.complex) {
104
+ const value = ComplexPower(ref.value, {real: 0.5, imaginary: 0});
105
+ return ComplexOrReal(value);
106
+ }
107
+ else if (ref.type === ValueType.undefined || !ref.value) {
108
+ return {
109
+ type: ValueType.number, value: 0,
110
+ }
111
+ }
112
+ else if (ref.type === ValueType.number && ref.value < 0) {
113
+ const value = ComplexPower({real: ref.value, imaginary: 0}, {real: 0.5, imaginary: 0});
114
+ return {
115
+ type: ValueType.complex,
116
+ value,
117
+ }
118
+ }
119
+ else {
120
+ const value = Math.sqrt(ref.value);
121
+ if (isNaN(value)) {
122
+ return ValueError();
123
+ }
124
+ return { type: ValueType.number, value };
125
+ }
126
+ }),
127
+ },
128
+
129
+ Power: {
130
+ description: 'Returns base raised to the given power',
131
+ arguments: [
132
+ { name: 'base', boxed: true, },
133
+ { name: 'exponent', boxed: true, }
134
+ ],
135
+ fn: Utils.ApplyAsArray2((base: UnionValue, exponent: UnionValue): UnionValue => {
136
+
137
+ // we're leaking complex numbers here because our functions are
138
+ // very slightly imprecise. I would like to stop doing that. try to
139
+ // use real math unless absolutely necessary.
140
+
141
+ // in the alternative we could update the epsilon on our ComplexOrReal
142
+ // function, but I would prefer not to do that if we don't have to.
143
+
144
+ // so: if both arguments are real, and base is >= 0 we can use real math.
145
+ // also if exponent is either 0 or >= 1 we can use real math.
146
+
147
+ if (base.type === ValueType.number && exponent.type === ValueType.number) {
148
+ if (base.value >= 0 || exponent.value === 0 || Math.abs(exponent.value) >= 1) {
149
+ const value = Math.pow(base.value, exponent.value);
150
+ if (isNaN(value)) {
151
+ return ValueError();
152
+ }
153
+ return { type: ValueType.number, value };
154
+ }
155
+ }
156
+
157
+ /*
158
+ if (base.type === ValueType.number) {
159
+ base = {
160
+ type: ValueType.complex,
161
+ value: { imaginary: 0, real: base.value },
162
+ };
163
+ }
164
+ */
165
+
166
+ const a = base.type === ValueType.complex ? base.value :
167
+ { real: base.value || 0, imaginary: 0, };
168
+
169
+ const b = exponent.type === ValueType.complex ? exponent.value :
170
+ { real: exponent.value || 0, imaginary: 0, };
171
+
172
+ const value = ComplexPower(a, b);
173
+ return ComplexOrReal(value);
174
+
175
+ }),
176
+ },
177
+
178
+ };
179
+
180
+ // use a single, static object for base functions
181
+
182
+ export const BaseFunctionLibrary: FunctionMap = {
183
+
184
+ Int: {
185
+ fn: (value: number) => {
186
+ return {type: ValueType.number, value: Math.floor(value) };
187
+ },
188
+ },
189
+
190
+ Rand: {
191
+ volatile: true,
192
+ fn: () => { return { type: ValueType.number, value: Math.random() }},
193
+ },
194
+
195
+ RandBetween: {
196
+ arguments: [{name: 'min'}, {name: 'max'}],
197
+ volatile: true,
198
+ fn: (min = 0, max = 1) => {
199
+ if (min > max) {
200
+ const tmp = min;
201
+ min = max;
202
+ max = tmp;
203
+ }
204
+ return { type: ValueType.number, value: Math.floor(Math.random() * (max + 1 - min) + min) }
205
+ },
206
+ },
207
+
208
+ Sum: {
209
+ description: 'Adds arguments and ranges',
210
+ arguments: [{ boxed: true, name: 'values or ranges' }],
211
+ fn: (...args: UnionValue[]) => {
212
+
213
+ const sum = { real: 0, imaginary: 0 };
214
+
215
+ const values = Utils.FlattenBoxed(args); // as UnionValue[];
216
+
217
+ for (const value of values) {
218
+
219
+ switch (value.type) {
220
+ case ValueType.number: sum.real += value.value; break;
221
+ case ValueType.boolean: sum.real += (value.value ? 1 : 0); break;
222
+ case ValueType.complex:
223
+ sum.real += value.value.real;
224
+ sum.imaginary += value.value.imaginary;
225
+ break;
226
+ case ValueType.error: return value;
227
+ }
228
+ }
229
+
230
+ return ComplexOrReal(sum);
231
+
232
+ },
233
+ },
234
+
235
+ Now: {
236
+ description: 'Returns current time',
237
+ volatile: true,
238
+ fn: () => {
239
+ return { type: ValueType.number, value: UnlotusDate(new Date().getTime()) };
240
+ },
241
+ },
242
+
243
+ Date: {
244
+ description: 'Constructs a Lotus date from parts',
245
+ arguments: [
246
+ { name: 'year' },
247
+ { name: 'month' },
248
+ { name: 'day' },
249
+ ],
250
+ fn: (year: number, month: number, day: number) => {
251
+ const date = new Date();
252
+ date.setMilliseconds(0);
253
+ date.setSeconds(0);
254
+ date.setMinutes(0);
255
+ date.setHours(0);
256
+
257
+ if (year < 0 || year > 10000) {
258
+ return ArgumentError();
259
+ }
260
+ if (year < 1899) { year += 1900; }
261
+ date.setFullYear(year);
262
+
263
+ if (month < 1 || month > 12) {
264
+ return ArgumentError();
265
+ }
266
+ date.setMonth(month - 1);
267
+
268
+ if (day < 1 || day > 31) {
269
+ return ArgumentError();
270
+ }
271
+ date.setDate(day);
272
+
273
+ return { type: ValueType.number, value: UnlotusDate(date.getTime()) };
274
+ },
275
+ },
276
+
277
+ Today: {
278
+ description: 'Returns current day',
279
+ volatile: true,
280
+ fn: () => {
281
+ const date = new Date();
282
+ date.setMilliseconds(0);
283
+ date.setSeconds(0);
284
+ date.setMinutes(0);
285
+ date.setHours(12);
286
+ return { type: ValueType.number, value: UnlotusDate(date.getTime()) };
287
+ },
288
+ },
289
+
290
+ IfError: {
291
+ description: 'Returns the original value, or the alternate value if the original value contains an error',
292
+ arguments: [{ name: 'original value', allow_error: true, boxed: true }, { name: 'alternate value' }],
293
+ fn: (ref: UnionValue, value_if_error: unknown = 0): UnionValue => {
294
+ if (ref && ref.type === ValueType.error) {
295
+ return { value: value_if_error, type: GetValueType(value_if_error) } as UnionValue;
296
+ }
297
+ return ref;
298
+ },
299
+ },
300
+
301
+ IsError: {
302
+ description: 'Checks if another cell contains an error',
303
+ arguments: [{ name: 'reference', allow_error: true, boxed: true }],
304
+ fn: (...args: UnionValue[]): UnionValue => {
305
+
306
+ const values = Utils.FlattenBoxed(args);
307
+ for (const value of values) {
308
+ if (value.type === ValueType.error) {
309
+ return { type: ValueType.boolean, value: true };
310
+ }
311
+ }
312
+
313
+ /*
314
+ if (Array.isArray(ref)) {
315
+ const values = Utils.Flatten(ref) as UnionValue[];
316
+ for (const value of values) {
317
+ if (value.type === ValueType.error) {
318
+ return { type: ValueType.boolean, value: true };
319
+ }
320
+ }
321
+ }
322
+ else if (ref) {
323
+ return { type: ValueType.boolean, value: ref.type === ValueType.error };
324
+ }
325
+ */
326
+
327
+ return { type: ValueType.boolean, value: false };
328
+
329
+ },
330
+ },
331
+
332
+
333
+ Cell: {
334
+ description: 'Returns data about a cell',
335
+ arguments: [
336
+ { name: 'type', description: 'Type of data to return' },
337
+ { name: 'reference', description: 'Cell reference', metadata: true },
338
+ ],
339
+
340
+ // there's no concept of "structure volatile", and structure events
341
+ // don't trigger recalc, so this is not helpful -- we may need to
342
+ // think about both of those things
343
+
344
+ // volatile: true,
345
+
346
+ fn: Utils.ApplyAsArray2((type: string, reference: UnionValue): UnionValue => {
347
+
348
+ if (!UnionIsMetadata(reference)) {
349
+ return ReferenceError();
350
+ }
351
+
352
+ if (type) {
353
+ switch (type.toString().toLowerCase()) {
354
+ case 'format':
355
+ return reference.value.format ? // || ReferenceError;
356
+ { type: ValueType.string, value: reference.value.format } : ReferenceError();
357
+ case 'address':
358
+ return { type: ValueType.string, value: reference.value.address.label.replace(/\$/g, '') };
359
+ }
360
+ }
361
+
362
+ return { type: ValueType.error, value: NotImplError.error };
363
+
364
+ }),
365
+ },
366
+
367
+ Year: {
368
+ description: 'Returns year from date',
369
+ arguments: [{
370
+ name: 'date',
371
+ }],
372
+ fn: (source: number): UnionValue => {
373
+ return Box(new Date(LotusDate(source)).getUTCFullYear());
374
+ },
375
+ },
376
+
377
+
378
+ Month: {
379
+ description: 'Returns month from date',
380
+ arguments: [{
381
+ name: 'date',
382
+ }],
383
+ fn: (source: number): UnionValue => {
384
+ return Box(new Date(LotusDate(source)).getUTCMonth() + 1); // 0-based
385
+ },
386
+ },
387
+
388
+
389
+ Day: {
390
+ description: 'Returns day of month from date',
391
+ arguments: [{
392
+ name: 'date',
393
+ }],
394
+ fn: (source: number): UnionValue => {
395
+ return Box(new Date(LotusDate(source)).getUTCDate());
396
+ },
397
+ },
398
+
399
+ Radians: {
400
+ description: 'Converts degrees to radians',
401
+ arguments: [{ name: 'Degrees', description: 'Angle in degrees' }],
402
+ fn: Utils.ApplyAsArray((degrees: number): UnionValue => {
403
+ return Box(degrees * Math.PI / 180);
404
+ }),
405
+ },
406
+
407
+ Degrees: {
408
+ description: 'Converts radians to degrees',
409
+ arguments: [{ name: 'Radians', description: 'Angle in radians' }],
410
+ fn: Utils.ApplyAsArray((radians: number): UnionValue => {
411
+ return Box(radians / Math.PI * 180);
412
+ }),
413
+ },
414
+
415
+ CountA: {
416
+ description: 'Counts cells that are not empty',
417
+ fn: (...args: unknown[]): UnionValue => {
418
+ return Box(Utils.FlattenUnboxed(args).reduce((a: number, b: unknown) => {
419
+ if (typeof b === 'undefined') return a;
420
+ return a + 1;
421
+ }, 0));
422
+ },
423
+ },
424
+
425
+ Count: {
426
+ description: 'Counts cells that contain numbers',
427
+ fn: (...args: unknown[]): UnionValue => {
428
+ return Box(Utils.FlattenUnboxed(args).reduce((a: number, b: unknown) => {
429
+ if (typeof b === 'number') return a + 1;
430
+ return a;
431
+ }, 0));
432
+ },
433
+ },
434
+
435
+ Or: {
436
+ fn: (...args: unknown[]): UnionValue => {
437
+ let result = false;
438
+ args = Utils.FlattenUnboxed(args);
439
+ for (const arg of args) {
440
+ result = result || !!arg;
441
+ }
442
+ return Box(result);
443
+ },
444
+ },
445
+
446
+ And: {
447
+ fn: (...args: unknown[]): UnionValue => {
448
+ let result = true;
449
+ args = Utils.FlattenUnboxed(args);
450
+ for (const arg of args) {
451
+ result = result && !!arg;
452
+ }
453
+ return Box(result);
454
+ },
455
+ },
456
+
457
+ Not: {
458
+ fn: (...args: unknown[]): UnionValue => {
459
+ if (args.length === 0) {
460
+ return ArgumentError();
461
+ }
462
+ if (args.length === 1) {
463
+ return Box(!args[0]);
464
+ }
465
+ return Box(true);
466
+ }
467
+ },
468
+
469
+ If: {
470
+ arguments: [
471
+ { name: 'test value', boxed: true },
472
+ { name: 'value if true', boxed: true, allow_error: true },
473
+ { name: 'value if false', boxed: true, allow_error: true },
474
+ ],
475
+
476
+ /**
477
+ * should we really have defaults for the t/f paths? not sure what X does
478
+ * @returns
479
+ */
480
+ fn: (a: UnionValue,
481
+ b: UnionValue = {type: ValueType.boolean, value: true},
482
+ c: UnionValue = {type: ValueType.boolean, value: false}): UnionValue => {
483
+
484
+ const b_array = b.type === ValueType.array;
485
+ const c_array = c.type === ValueType.array;
486
+
487
+ if (a.type === ValueType.array) {
488
+ return {
489
+ type: ValueType.array,
490
+ value: a.value.map((row, x) => row.map((cell, y) => {
491
+ const value = (cell.type === ValueType.string) ?
492
+ (cell.value.toLowerCase() !== 'false' && cell.value.toLowerCase() !== 'f') : !!cell.value;
493
+ return value ? (b_array ? b.value[x][y] : b) : (c_array ? c.value[x][y] : c);
494
+ })) as UnionValue[][],
495
+ };
496
+ }
497
+
498
+ const value = a.type === ValueType.string ? // UnionIs.String(a) ?
499
+ (a.value.toLowerCase() !== 'false' && a.value.toLowerCase() !== 'f') : !!a.value;
500
+
501
+ return value ? b : c;
502
+
503
+ },
504
+ },
505
+
506
+ Fact: {
507
+ description: 'Returns the factorial of a number',
508
+ arguments: [
509
+ { name: 'number' },
510
+ ],
511
+ fn: Utils.ApplyAsArray((number: number): UnionValue => {
512
+ number = Math.floor(number);
513
+ let value = 1;
514
+ while (number > 1) {
515
+ value *= number;
516
+ number--;
517
+ }
518
+ return {
519
+ type: ValueType.number,
520
+ value,
521
+ }
522
+ }),
523
+ },
524
+
525
+ Power: {
526
+ description: 'Returns base raised to the given power',
527
+ arguments: [
528
+ { name: 'base', boxed: true, },
529
+ { name: 'exponent', boxed: true, }
530
+ ],
531
+ fn: Utils.ApplyAsArray2((base: UnionValue, exponent: UnionValue): UnionValue => {
532
+
533
+ /*
534
+ if (base.type === ValueType.number) {
535
+ base = {
536
+ type: ValueType.complex,
537
+ value: { imaginary: 0, real: base.value },
538
+ };
539
+ }
540
+ */
541
+
542
+ if (base.type === ValueType.complex || exponent.type === ValueType.complex) {
543
+
544
+ const a = base.type === ValueType.complex ? base.value :
545
+ { real: base.value || 0, imaginary: 0, };
546
+ const b = exponent.type === ValueType.complex ? exponent.value :
547
+ { real: exponent.value || 0, imaginary: 0, };
548
+
549
+ const value = ComplexPower(a, b);
550
+
551
+ return ComplexOrReal(value);
552
+
553
+ }
554
+ else {
555
+ const value = Math.pow(base.value, exponent.value);
556
+ if (isNaN(value)) {
557
+ return ValueError();
558
+ }
559
+ return { type: ValueType.number, value };
560
+ // return Box(Math.pow(base.value, exponent.value))
561
+ }
562
+
563
+ }),
564
+ },
565
+
566
+ Mod: {
567
+ fn: Utils.ApplyAsArray2((num: number, divisor: number): UnionValue => {
568
+ if (!divisor) {
569
+ return DivideByZeroError();
570
+ }
571
+ return Box(num % divisor);
572
+ })
573
+ },
574
+
575
+ /**
576
+ * sort arguments, but ensure we return empty strings to
577
+ * fill up the result array
578
+ *
579
+ * FIXME: instead of boxing all the values, why not pass them in boxed?
580
+ * was this function just written at the wrong time?
581
+ */
582
+ Sort: {
583
+ arguments: [
584
+ { name: 'values' }
585
+ ],
586
+ fn: (...args: any[]): UnionValue => {
587
+
588
+ args = Utils.FlattenUnboxed(args);
589
+
590
+ if(args.every(test => typeof test === 'number')) {
591
+ args.sort((a, b) => a - b);
592
+ }
593
+ else {
594
+ args.sort(); // lexical
595
+ }
596
+
597
+ return { type: ValueType.array, value: [args.map(value => Box(value))] };
598
+
599
+ },
600
+ },
601
+
602
+ Transpose: {
603
+ description: 'Returns transpose of input matrix',
604
+ arguments: [{name: 'matrix', boxed: true}],
605
+ fn: (mat: UnionValue): UnionValue => {
606
+
607
+ if (mat.type === ValueType.array) {
608
+ return {
609
+ type: ValueType.array,
610
+ value: Utils.Transpose2(mat.value),
611
+ };
612
+ }
613
+
614
+ /*
615
+ if (Array.isArray(mat)) {
616
+ return Utils.Transpose2(mat);
617
+ }
618
+ */
619
+
620
+ return mat;
621
+ }
622
+ },
623
+
624
+ Max: {
625
+ fn: (...args: any[]): UnionValue => {
626
+ return {
627
+ type: ValueType.number,
628
+ value: Math.max.apply(0, Utils.FlattenUnboxed(args).filter(x => typeof x === 'number')),
629
+ };
630
+ },
631
+ },
632
+
633
+ Min: {
634
+ fn: (...args: any[]): UnionValue => {
635
+ return {
636
+ type: ValueType.number,
637
+ value: Math.min.apply(0, Utils.FlattenUnboxed(args).filter(x => typeof x === 'number')),
638
+ };
639
+ },
640
+ },
641
+
642
+
643
+ /*
644
+ MMult: {
645
+ description: 'Multiplies two matrices',
646
+ arguments: [{ name: 'Matrix 1'}, { name: 'Matrix 2'}],
647
+ fn: (a, b) => {
648
+ if (!a || !b) return ArgumentError;
649
+
650
+ const a_cols = a.length || 0;
651
+ const a_rows = a[0]?.length || 0;
652
+
653
+ const b_cols = b.length || 0;
654
+ const b_rows = b[0]?.length || 0;
655
+
656
+ if (!a_rows || !b_rows || !a_cols || !b_cols
657
+ || a_rows !== b_cols || a_cols !== b_rows) return ValueError;
658
+
659
+ const result: number[][] = [];
660
+
661
+ // slightly confusing because we're column-major
662
+
663
+ for (let c = 0; c < b_cols; c++) {
664
+ result[c] = [];
665
+ for (let r = 0; r < a_rows; r++) {
666
+ result[c][r] = 0;
667
+ for (let x = 0; x < a_cols; x++) {
668
+ result[c][r] += a[x][r] * b[c][x];
669
+ }
670
+ }
671
+ }
672
+ return result;
673
+
674
+ }
675
+ },
676
+ */
677
+
678
+ SumProduct: {
679
+ description: 'Returns the sum of pairwise products of two or more ranges',
680
+ fn: (...args: any[]): UnionValue => {
681
+
682
+ const flattened = args.map(arg => Utils.FlattenUnboxed(arg));
683
+ const len = Math.max.apply(0, flattened.map(x => x.length));
684
+
685
+ let sum = 0;
686
+ for (let i = 0; i < len; i++) {
687
+ sum += flattened.reduce((a, arg) => {
688
+ return a * (arg[i] || 0);
689
+ }, 1);
690
+ }
691
+
692
+ return { type: ValueType.number, value: sum };
693
+
694
+ },
695
+ },
696
+
697
+ /**
698
+ *
699
+ * match type:
700
+ *
701
+ * 1: largest value <= target value; assumes table is in ascending order.
702
+ * 0: exact match only.
703
+ * -1: smallest value >= target value; assumes table is in descending order.
704
+ *
705
+ * NOTE that string matches can accept wildcards in Excel, not sure if we
706
+ * necessarily want to support that... how does string matching deal with
707
+ * inequalities?
708
+ * /
709
+ Match: {
710
+ fn: (value: CellValue, table: CellValue[][], match_type: 1|0|-1 = 1) => {
711
+
712
+ const flat = table.reduce((a, row) => ([...a, ...row]), []);
713
+ for (let i = 0; i < flat.length; i++) {
714
+
715
+ const compare = flat[i];
716
+
717
+ console.info("CV", compare, value);
718
+
719
+ // this is true regardless of match type... right?
720
+ if (compare === value) {
721
+ return { type: ValueType.number, value: i + 1 };
722
+ }
723
+
724
+ if ((typeof compare !== 'undefined' && typeof value !== 'undefined') && (
725
+ (match_type === 1 && compare > value) ||
726
+ (match_type === -1 && compare < value))) {
727
+
728
+ if (i === 0 || i === flat.length - 1) {
729
+ return NAError();
730
+ }
731
+
732
+ return { type: ValueType.number, value: i }; // implicit -1
733
+ }
734
+
735
+ }
736
+ return NAError();
737
+ },
738
+ },
739
+ */
740
+
741
+ /**
742
+ * FIXME: does not implement inexact matching (what's the algo for
743
+ * that, anyway? nearest? price is right style? what about ties?)
744
+ */
745
+ VLookup: {
746
+ fn: (value: any, table: any[][], col: number, inexact = true): UnionValue => {
747
+
748
+ col = Math.max(0, col - 1);
749
+
750
+ if (inexact) {
751
+
752
+ let min = Math.abs(value - table[0][0]);
753
+ let result: any = table[col][0];
754
+
755
+ for (let i = 1; i < table[0].length; i++) {
756
+
757
+ const abs = Math.abs(table[0][i] - value);
758
+
759
+ if (abs < min) { // implies first match
760
+ min = abs;
761
+ result = table[col][i];
762
+ }
763
+ }
764
+
765
+ return Box(result);
766
+
767
+ }
768
+ else {
769
+ for (let i = 1; i < table[0].length; i++) {
770
+ if (table[0][i] == value) { // ==
771
+ return table[col][i];
772
+ }
773
+ }
774
+ return NAError();
775
+ }
776
+
777
+ },
778
+ },
779
+
780
+ Product: {
781
+ arguments: [{boxed: true}],
782
+ fn: (...args: any[]): UnionValue => {
783
+
784
+ let product: Complex = { real: 1, imaginary: 0 };
785
+
786
+ args = Utils.FlattenBoxed(args);
787
+
788
+ for (const arg of args as UnionValue[]) {
789
+ if (arg.type === ValueType.complex) {
790
+ product = ComplexMultply(product, arg.value);
791
+ }
792
+ else if (arg.type === ValueType.number) {
793
+ product.real *= arg.value;
794
+ product.imaginary *= arg.value;
795
+ }
796
+ }
797
+
798
+ return ComplexOrReal(product);
799
+
800
+ /*
801
+ return { type: ValueType.number, value: Utils.Flatten(args).reduce((a: number, b: any) => {
802
+ if (typeof b === 'undefined') return a;
803
+ return a * Number(b);
804
+ }, 1) };
805
+ */
806
+
807
+ },
808
+ },
809
+
810
+ Log: {
811
+ /** default is base 10; allow specific base */
812
+ fn: Utils.ApplyAsArray2((a: number, base = 10): UnionValue => {
813
+ return { type: ValueType.number, value: Math.log(a) / Math.log(base) };
814
+ }),
815
+ },
816
+
817
+ Log10: {
818
+ fn: Utils.ApplyAsArray((a: number): UnionValue => {
819
+ return { type: ValueType.number, value: Math.log(a) / Math.log(10) };
820
+ }),
821
+ },
822
+
823
+ Ln: {
824
+ fn: Utils.ApplyAsArray((a: number): UnionValue => {
825
+ return { type: ValueType.number, value: Math.log(a) };
826
+ }),
827
+ },
828
+
829
+ Round: {
830
+ fn: Utils.ApplyAsArray2((a, digits = 0) => {
831
+ const m = Math.pow(10, digits);
832
+ return {
833
+ type: ValueType.number,
834
+ value: Math.round(m * a) / m,
835
+ };
836
+ }),
837
+ },
838
+
839
+ RoundDown: {
840
+ fn: Utils.ApplyAsArray2((a, digits = 0) => {
841
+ const m = Math.pow(10, digits);
842
+ const positive = a >= 0;
843
+ return {
844
+ type: ValueType.number,
845
+ value: positive ? Math.floor(m * a) / m : Math.ceil(m * a) / m,
846
+ };
847
+ }),
848
+ },
849
+
850
+ /*
851
+
852
+ Round: {
853
+ description: 'Round to a specified number of digits',
854
+
855
+ / ** round with variable digits * /
856
+ fn: (value: number, digits = 0) => {
857
+ const m = Math.pow(10, digits);
858
+ return Math.round(m * value) / m;
859
+ },
860
+ },
861
+
862
+ RoundDown: {
863
+ / ** round down with variable digits * /
864
+ fn: (value: number, digits = 0) => {
865
+ digits = Math.max(0, digits);
866
+ const m = Math.pow(10, digits);
867
+ return Math.floor(m * value) / m;
868
+ },
869
+ },
870
+
871
+
872
+ */
873
+
874
+
875
+ Reverse: {
876
+ arguments: [
877
+ { boxed: true },
878
+ ],
879
+ fn: (a: UnionValue): UnionValue => {
880
+
881
+ /*
882
+
883
+ what is this? this would do anything useful
884
+ ...oh I see, it reverses along one axis or the other
885
+
886
+ if ( Array.isArray(a)) {
887
+ if (a.length === 1 ) return [a[0].reverse()];
888
+ return a.reverse();
889
+ }
890
+ */
891
+
892
+ if (a.type === ValueType.array) {
893
+ if (a.value.length === 1) {
894
+ a.value[0].reverse();
895
+ }
896
+ else {
897
+ a.value.reverse();
898
+ }
899
+ return a;
900
+ }
901
+
902
+ return {
903
+ type: ValueType.string,
904
+ value: a.value.toString().split('').reverse().join(''),
905
+ };
906
+ },
907
+ },
908
+
909
+ /**
910
+ * exp was not broken out, but added so we can support complex numbers.
911
+ */
912
+ Exp: {
913
+ arguments: [
914
+ { boxed: true },
915
+ ],
916
+ fn: Utils.ApplyAsArray((x: UnionValue) => {
917
+ if (x.type === ValueType.complex) {
918
+ const value = ComplexExp(x.value);
919
+ return ComplexOrReal(value);
920
+ }
921
+ return { type: ValueType.number, value: Math.exp(x.value || 0) };
922
+ }),
923
+ },
924
+
925
+ /**
926
+ * abs was already broken out so we could support array application,
927
+ * then updated to support complex numbers.
928
+ */
929
+ Abs: {
930
+ arguments: [
931
+ { boxed: true },
932
+ ],
933
+ fn: Utils.ApplyAsArray((a: UnionValue) => {
934
+ if (a.type === ValueType.complex) {
935
+ return {
936
+ type: ValueType.number,
937
+ value: Math.sqrt(a.value.real * a.value.real + a.value.imaginary * a.value.imaginary),
938
+ };
939
+ }
940
+ return { type: ValueType.number, value: Math.abs(a.value || 0) };
941
+ }),
942
+ },
943
+
944
+ Simplify: {
945
+ arguments: [
946
+ { name: 'value' },
947
+ { name: 'significant digits' },
948
+ ],
949
+ fn: Utils.ApplyAsArray2((value: number, significant_digits = 2): UnionValue => {
950
+ significant_digits = significant_digits || 2;
951
+ if (value === 0) {
952
+ return { type: ValueType.number, value };
953
+ }
954
+ const negative = value < 0 ? -1 : 1;
955
+ value *= negative;
956
+ const x = Math.pow(10, Math.floor(Math.log10(value)) + 1 - significant_digits);
957
+ return {
958
+ type: ValueType.number,
959
+ value: Math.round(value / x) * x * negative
960
+ };
961
+ }),
962
+ },
963
+
964
+ Erf: {
965
+ fn: (a: number): UnionValue => {
966
+ return { type: ValueType.number, value: erf(a) };
967
+ },
968
+ },
969
+
970
+ 'NormsInv': {
971
+
972
+ description: 'Inverse of the normal cumulative distribution',
973
+ arguments: [
974
+ {name: 'probability'},
975
+ ],
976
+
977
+ fn: (q: number): UnionValue => {
978
+ return {
979
+ type: ValueType.number,
980
+ value: inverse_normal(q),
981
+ }
982
+ }
983
+ },
984
+
985
+ 'Norm.Inv': {
986
+ description: 'Inverse of the normal cumulative distribution',
987
+ arguments: [
988
+ {name: 'probability'},
989
+ {name: 'mean', default: 0},
990
+ {name: 'standard deviation', default: 1},
991
+ ],
992
+ xlfn: true,
993
+ fn: (q: number, mean = 0, stdev = 1): UnionValue => {
994
+ return {
995
+ type: ValueType.number,
996
+ value: inverse_normal(q) * stdev + mean,
997
+ }
998
+ }
999
+ },
1000
+
1001
+ 'Norm.Dist': {
1002
+
1003
+ description: 'Cumulative normal distribution',
1004
+ arguments: [
1005
+ {name: 'value'},
1006
+ {name: 'mean', default: 0},
1007
+ {name: 'standard deviation', default: 1},
1008
+ {name: 'cumulative', default: true},
1009
+ ],
1010
+
1011
+ // this does need xlfn but it also requires four parameters
1012
+ // (we have three and they are not required).
1013
+
1014
+ xlfn: true,
1015
+
1016
+ fn: (x: number, mean = 0, stdev = 1, cumulative = true): UnionValue => {
1017
+
1018
+ let value = 0;
1019
+
1020
+ if (cumulative) {
1021
+ const sign = (x < mean) ? -1 : 1;
1022
+ value = 0.5 * (1.0 + sign * erf((Math.abs(x - mean)) / (stdev * Math.sqrt(2))));
1023
+ }
1024
+ else {
1025
+ value = Math.exp(-1/2 * Math.pow((x - mean) / stdev, 2)) / (stdev * sqrt2pi);
1026
+ }
1027
+
1028
+ return {
1029
+ type: ValueType.number,
1030
+ value,
1031
+ };
1032
+
1033
+ },
1034
+ },
1035
+
1036
+ Sqrt: {
1037
+ description: 'Returns the square root of the argument',
1038
+ arguments: [
1039
+ {boxed: true},
1040
+ ],
1041
+ fn: Utils.ApplyAsArray((ref: UnionValue): UnionValue => {
1042
+
1043
+ // little bit torn on this. what should sqrt(-1) return? a complex
1044
+ // number, or NaN? or should we control that with a flag?
1045
+
1046
+ // UPDATE: now optional, see AltFunctionLibrary
1047
+
1048
+ if (ref.type === ValueType.complex) {
1049
+ const value = ComplexPower(ref.value, {real: 0.5, imaginary: 0});
1050
+ return ComplexOrReal(value);
1051
+ }
1052
+ else if (ref.type === ValueType.undefined || !ref.value) {
1053
+ return {
1054
+ type: ValueType.number, value: 0,
1055
+ }
1056
+ }
1057
+ /*
1058
+ else if (ref.type === ValueType.number && ref.value < 0) {
1059
+ const value = ComplexPower({real: ref.value, imaginary: 0}, {real: 0.5, imaginary: 0});
1060
+ return {
1061
+ type: ValueType.complex,
1062
+ value,
1063
+ }
1064
+ }
1065
+ */
1066
+ else {
1067
+ const value = Math.sqrt(ref.value);
1068
+ if (isNaN(value)) {
1069
+ return ValueError();
1070
+ }
1071
+ return { type: ValueType.number, value };
1072
+ }
1073
+ }),
1074
+ },
1075
+
1076
+ HexToDec: {
1077
+ arguments: [{ description: 'hexadecimal string' }],
1078
+ fn: (hex: string): UnionValue => {
1079
+ return { type: ValueType.number, value: parseInt(hex, 16) };
1080
+ },
1081
+ },
1082
+
1083
+ DecToHex: {
1084
+ arguments: [{ description: 'number' }],
1085
+ fn: (num: number): UnionValue => {
1086
+ return { type: ValueType.string, value: num.toString(16) };
1087
+ },
1088
+ },
1089
+
1090
+ Checkbox: {
1091
+ arguments: [
1092
+ { name: 'checked' },
1093
+ ],
1094
+ click: ClickCheckbox,
1095
+ render: RenderCheckbox,
1096
+ fn: (checked: boolean): UnionValue => {
1097
+ return { value: !!checked, type: ValueType.boolean, }
1098
+ },
1099
+ },
1100
+
1101
+ 'Sparkline.Column': {
1102
+ arguments: [
1103
+ {name: 'data' },
1104
+ {name: 'color'},
1105
+ {name: 'negative color'}],
1106
+ render: (options: RenderFunctionOptions): RenderFunctionResult => {
1107
+ Sparkline.RenderColumn(options.width, options.height, options.context, options.cell, options.style);
1108
+ return { handled: true }; // painted
1109
+ },
1110
+ fn: (...args: unknown[]): UnionValue => {
1111
+ return { type: ValueType.object, value: args, key: 'sparkline-data' };
1112
+ },
1113
+ },
1114
+
1115
+ 'Sparkline.Line': {
1116
+ arguments: [
1117
+ {name: 'data'},
1118
+ {name: 'color'},
1119
+ {name: 'line width'},
1120
+ ],
1121
+ render: (options: RenderFunctionOptions): RenderFunctionResult => {
1122
+ Sparkline.RenderLine(options.width, options.height, options.context, options.cell, options.style);
1123
+ return { handled: true }; // painted
1124
+ },
1125
+ fn: (...args: unknown[]): UnionValue => {
1126
+ return { type: ValueType.object, value: args, key: 'sparkline-data' };
1127
+ },
1128
+ }
1129
+
1130
+ };
1131
+
1132
+ // alias
1133
+
1134
+ // add functions from Math (intrinsic), unless the name overlaps
1135
+ // with something already in there
1136
+
1137
+ // we need to construct a separate map to match icase (this is now
1138
+ // even more useful since we have a separate section for aliases)
1139
+
1140
+ const name_map: {[index: string]: string} = {};
1141
+
1142
+ for (const key of Object.keys(BaseFunctionLibrary)) {
1143
+ name_map[key.toLowerCase()] = key;
1144
+ }
1145
+
1146
+ // block these names from auto-import from Math
1147
+
1148
+ const block_list = [
1149
+ 'pow',
1150
+ ];
1151
+
1152
+ const block_map: Record<string, string> = {};
1153
+ for (const entry of block_list) {
1154
+ block_map[entry.toLowerCase()] = entry;
1155
+ }
1156
+
1157
+ for (const name of Object.getOwnPropertyNames(Math)) {
1158
+
1159
+ // check if it exists (we have already registered something
1160
+ // with the same name) -- don't override existing
1161
+
1162
+ const lc = name.toLowerCase();
1163
+
1164
+ if (name_map[lc]) { continue; }
1165
+
1166
+ // also explicitly block some names we don't want to include (pow vs. power, etc)
1167
+
1168
+ if (block_map[lc]) { continue; }
1169
+
1170
+ const descriptor = Object.getOwnPropertyDescriptor(Math, name);
1171
+ if (!descriptor) { continue; }
1172
+
1173
+ const value = descriptor.value;
1174
+ const type = typeof (value);
1175
+
1176
+ switch (type) {
1177
+ case 'number':
1178
+ // console.info("MATH CONSTANT", name);
1179
+ BaseFunctionLibrary[name] = {
1180
+ fn: () => {
1181
+ return { type: ValueType.number, value }
1182
+ },
1183
+ category: ['Math Functions'],
1184
+ };
1185
+ break;
1186
+
1187
+ case 'function':
1188
+ // console.info("MATH FUNC", name);
1189
+ BaseFunctionLibrary[name] = {
1190
+ fn: (...args: any) => {
1191
+ return Box(value(...args));
1192
+ },
1193
+ category: ['Math Functions'],
1194
+ };
1195
+ break;
1196
+
1197
+ default:
1198
+ console.info('unexpected type:', type, name);
1199
+ break;
1200
+ }
1201
+
1202
+ }
1203
+
1204
+ // IE11: patch log10 function // FIXME: is this necessary anymore?
1205
+
1206
+ if (!Math.log10) {
1207
+ Math.log10 = (a) => Math.log(a) / Math.log(10);
1208
+ /*
1209
+ BaseFunctionLibrary.log10 = {
1210
+ fn: (x) => Math.log(x) / Math.log(10),
1211
+ category: ['Math Functions'],
1212
+ };
1213
+ */
1214
+ }