@trebco/treb 37.0.0 → 37.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (528) hide show
  1. package/build/package.json +119 -0
  2. package/build/treb-base-types/src/api_types.d.ts +11 -0
  3. package/build/treb-base-types/src/api_types.js +22 -0
  4. package/build/treb-base-types/src/api_types.js.map +1 -0
  5. package/build/treb-base-types/src/area-utils.d.ts +9 -0
  6. package/build/treb-base-types/src/area-utils.js +50 -0
  7. package/build/treb-base-types/src/area-utils.js.map +1 -0
  8. package/build/treb-base-types/src/area.d.ts +182 -0
  9. package/build/treb-base-types/src/area.js +715 -0
  10. package/build/treb-base-types/src/area.js.map +1 -0
  11. package/build/treb-base-types/src/basic_types.d.ts +20 -0
  12. package/build/treb-base-types/src/basic_types.js +22 -0
  13. package/build/treb-base-types/src/basic_types.js.map +1 -0
  14. package/build/treb-base-types/src/cell.d.ts +167 -0
  15. package/build/treb-base-types/src/cell.js +432 -0
  16. package/build/treb-base-types/src/cell.js.map +1 -0
  17. package/build/treb-base-types/src/cells.d.ts +251 -0
  18. package/build/treb-base-types/src/cells.js +1136 -0
  19. package/build/treb-base-types/src/cells.js.map +1 -0
  20. package/build/treb-base-types/src/color.d.ts +35 -0
  21. package/build/treb-base-types/src/color.js +162 -0
  22. package/build/treb-base-types/src/color.js.map +1 -0
  23. package/build/treb-base-types/src/dom-utilities.d.ts +70 -0
  24. package/build/treb-base-types/src/dom-utilities.js +144 -0
  25. package/build/treb-base-types/src/dom-utilities.js.map +1 -0
  26. package/build/treb-base-types/src/evaluate-options.d.ts +35 -0
  27. package/build/treb-base-types/src/evaluate-options.js +22 -0
  28. package/build/treb-base-types/src/evaluate-options.js.map +1 -0
  29. package/build/treb-base-types/src/font-stack.d.ts +37 -0
  30. package/build/treb-base-types/src/font-stack.js +93 -0
  31. package/build/treb-base-types/src/font-stack.js.map +1 -0
  32. package/build/treb-base-types/src/gradient.d.ts +18 -0
  33. package/build/treb-base-types/src/gradient.js +86 -0
  34. package/build/treb-base-types/src/gradient.js.map +1 -0
  35. package/build/treb-base-types/src/import.d.ts +48 -0
  36. package/build/treb-base-types/src/import.js +22 -0
  37. package/build/treb-base-types/src/import.js.map +1 -0
  38. package/build/treb-base-types/src/index-standalone.d.ts +6 -0
  39. package/build/treb-base-types/src/index-standalone.js +27 -0
  40. package/build/treb-base-types/src/index-standalone.js.map +1 -0
  41. package/build/treb-base-types/src/index.d.ts +22 -0
  42. package/build/treb-base-types/src/index.js +45 -0
  43. package/build/treb-base-types/src/index.js.map +1 -0
  44. package/build/treb-base-types/src/layout.d.ts +22 -0
  45. package/build/treb-base-types/src/layout.js +22 -0
  46. package/build/treb-base-types/src/layout.js.map +1 -0
  47. package/build/treb-base-types/src/localization.d.ts +37 -0
  48. package/build/treb-base-types/src/localization.js +157 -0
  49. package/build/treb-base-types/src/localization.js.map +1 -0
  50. package/build/treb-base-types/src/rectangle.d.ts +51 -0
  51. package/build/treb-base-types/src/rectangle.js +123 -0
  52. package/build/treb-base-types/src/rectangle.js.map +1 -0
  53. package/build/treb-base-types/src/render_text.d.ts +34 -0
  54. package/build/treb-base-types/src/render_text.js +22 -0
  55. package/build/treb-base-types/src/render_text.js.map +1 -0
  56. package/build/treb-base-types/src/style.d.ts +214 -0
  57. package/build/treb-base-types/src/style.js +373 -0
  58. package/build/treb-base-types/src/style.js.map +1 -0
  59. package/build/treb-base-types/src/table.d.ts +58 -0
  60. package/build/treb-base-types/src/table.js +27 -0
  61. package/build/treb-base-types/src/table.js.map +1 -0
  62. package/build/treb-base-types/src/text_part.d.ts +26 -0
  63. package/build/treb-base-types/src/text_part.js +47 -0
  64. package/build/treb-base-types/src/text_part.js.map +1 -0
  65. package/build/treb-base-types/src/theme.d.ts +120 -0
  66. package/build/treb-base-types/src/theme.js +460 -0
  67. package/build/treb-base-types/src/theme.js.map +1 -0
  68. package/build/treb-base-types/src/union.d.ts +73 -0
  69. package/build/treb-base-types/src/union.js +61 -0
  70. package/build/treb-base-types/src/union.js.map +1 -0
  71. package/build/treb-base-types/src/value-type.d.ts +86 -0
  72. package/build/treb-base-types/src/value-type.js +168 -0
  73. package/build/treb-base-types/src/value-type.js.map +1 -0
  74. package/build/treb-base-types/src/worker-proxy.d.ts +95 -0
  75. package/build/treb-base-types/src/worker-proxy.js +221 -0
  76. package/build/treb-base-types/src/worker-proxy.js.map +1 -0
  77. package/build/treb-calculator/src/calculator.d.ts +249 -0
  78. package/build/treb-calculator/src/calculator.js +2755 -0
  79. package/build/treb-calculator/src/calculator.js.map +1 -0
  80. package/build/treb-calculator/src/complex-math.d.ts +75 -0
  81. package/build/treb-calculator/src/complex-math.js +559 -0
  82. package/build/treb-calculator/src/complex-math.js.map +1 -0
  83. package/build/treb-calculator/src/dag/array-vertex.d.ts +71 -0
  84. package/build/treb-calculator/src/dag/array-vertex.js +156 -0
  85. package/build/treb-calculator/src/dag/array-vertex.js.map +1 -0
  86. package/build/treb-calculator/src/dag/calculation_leaf_vertex.d.ts +48 -0
  87. package/build/treb-calculator/src/dag/calculation_leaf_vertex.js +84 -0
  88. package/build/treb-calculator/src/dag/calculation_leaf_vertex.js.map +1 -0
  89. package/build/treb-calculator/src/dag/graph.d.ts +134 -0
  90. package/build/treb-calculator/src/dag/graph.js +842 -0
  91. package/build/treb-calculator/src/dag/graph.js.map +1 -0
  92. package/build/treb-calculator/src/dag/spreadsheet_vertex.d.ts +58 -0
  93. package/build/treb-calculator/src/dag/spreadsheet_vertex.js +232 -0
  94. package/build/treb-calculator/src/dag/spreadsheet_vertex.js.map +1 -0
  95. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.d.ts +20 -0
  96. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js +25 -0
  97. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js.map +1 -0
  98. package/build/treb-calculator/src/dag/state_leaf_vertex.d.ts +43 -0
  99. package/build/treb-calculator/src/dag/state_leaf_vertex.js +81 -0
  100. package/build/treb-calculator/src/dag/state_leaf_vertex.js.map +1 -0
  101. package/build/treb-calculator/src/dag/vertex.d.ts +71 -0
  102. package/build/treb-calculator/src/dag/vertex.js +274 -0
  103. package/build/treb-calculator/src/dag/vertex.js.map +1 -0
  104. package/build/treb-calculator/src/descriptors.d.ts +189 -0
  105. package/build/treb-calculator/src/descriptors.js +22 -0
  106. package/build/treb-calculator/src/descriptors.js.map +1 -0
  107. package/build/treb-calculator/src/expression-calculator.d.ts +127 -0
  108. package/build/treb-calculator/src/expression-calculator.js +1033 -0
  109. package/build/treb-calculator/src/expression-calculator.js.map +1 -0
  110. package/build/treb-calculator/src/function-error.d.ts +35 -0
  111. package/build/treb-calculator/src/function-error.js +85 -0
  112. package/build/treb-calculator/src/function-error.js.map +1 -0
  113. package/build/treb-calculator/src/function-library.d.ts +22 -0
  114. package/build/treb-calculator/src/function-library.js +96 -0
  115. package/build/treb-calculator/src/function-library.js.map +1 -0
  116. package/build/treb-calculator/src/functions/base-functions.d.ts +7 -0
  117. package/build/treb-calculator/src/functions/base-functions.js +2611 -0
  118. package/build/treb-calculator/src/functions/base-functions.js.map +1 -0
  119. package/build/treb-calculator/src/functions/beta.d.ts +17 -0
  120. package/build/treb-calculator/src/functions/beta.js +201 -0
  121. package/build/treb-calculator/src/functions/beta.js.map +1 -0
  122. package/build/treb-calculator/src/functions/checkbox.d.ts +3 -0
  123. package/build/treb-calculator/src/functions/checkbox.js +128 -0
  124. package/build/treb-calculator/src/functions/checkbox.js.map +1 -0
  125. package/build/treb-calculator/src/functions/complex-functions.d.ts +2 -0
  126. package/build/treb-calculator/src/functions/complex-functions.js +217 -0
  127. package/build/treb-calculator/src/functions/complex-functions.js.map +1 -0
  128. package/build/treb-calculator/src/functions/date-utils.d.ts +3 -0
  129. package/build/treb-calculator/src/functions/date-utils.js +59 -0
  130. package/build/treb-calculator/src/functions/date-utils.js.map +1 -0
  131. package/build/treb-calculator/src/functions/finance-functions.d.ts +2 -0
  132. package/build/treb-calculator/src/functions/finance-functions.js +547 -0
  133. package/build/treb-calculator/src/functions/finance-functions.js.map +1 -0
  134. package/build/treb-calculator/src/functions/fp.d.ts +2 -0
  135. package/build/treb-calculator/src/functions/fp.js +463 -0
  136. package/build/treb-calculator/src/functions/fp.js.map +1 -0
  137. package/build/treb-calculator/src/functions/function-utilities.d.ts +2 -0
  138. package/build/treb-calculator/src/functions/function-utilities.js +36 -0
  139. package/build/treb-calculator/src/functions/function-utilities.js.map +1 -0
  140. package/build/treb-calculator/src/functions/gamma.d.ts +20 -0
  141. package/build/treb-calculator/src/functions/gamma.js +142 -0
  142. package/build/treb-calculator/src/functions/gamma.js.map +1 -0
  143. package/build/treb-calculator/src/functions/information-functions.d.ts +2 -0
  144. package/build/treb-calculator/src/functions/information-functions.js +71 -0
  145. package/build/treb-calculator/src/functions/information-functions.js.map +1 -0
  146. package/build/treb-calculator/src/functions/lambda-functions.d.ts +2 -0
  147. package/build/treb-calculator/src/functions/lambda-functions.js +85 -0
  148. package/build/treb-calculator/src/functions/lambda-functions.js.map +1 -0
  149. package/build/treb-calculator/src/functions/matrix-functions.d.ts +2 -0
  150. package/build/treb-calculator/src/functions/matrix-functions.js +144 -0
  151. package/build/treb-calculator/src/functions/matrix-functions.js.map +1 -0
  152. package/build/treb-calculator/src/functions/normal.d.ts +2 -0
  153. package/build/treb-calculator/src/functions/normal.js +32 -0
  154. package/build/treb-calculator/src/functions/normal.js.map +1 -0
  155. package/build/treb-calculator/src/functions/regex-functions.d.ts +2 -0
  156. package/build/treb-calculator/src/functions/regex-functions.js +188 -0
  157. package/build/treb-calculator/src/functions/regex-functions.js.map +1 -0
  158. package/build/treb-calculator/src/functions/sparkline.d.ts +37 -0
  159. package/build/treb-calculator/src/functions/sparkline.js +264 -0
  160. package/build/treb-calculator/src/functions/sparkline.js.map +1 -0
  161. package/build/treb-calculator/src/functions/statistics-functions.d.ts +6 -0
  162. package/build/treb-calculator/src/functions/statistics-functions.js +989 -0
  163. package/build/treb-calculator/src/functions/statistics-functions.js.map +1 -0
  164. package/build/treb-calculator/src/functions/students-t.d.ts +3 -0
  165. package/build/treb-calculator/src/functions/students-t.js +64 -0
  166. package/build/treb-calculator/src/functions/students-t.js.map +1 -0
  167. package/build/treb-calculator/src/functions/text-functions.d.ts +3 -0
  168. package/build/treb-calculator/src/functions/text-functions.js +320 -0
  169. package/build/treb-calculator/src/functions/text-functions.js.map +1 -0
  170. package/build/treb-calculator/src/index.d.ts +2 -0
  171. package/build/treb-calculator/src/index.js +22 -0
  172. package/build/treb-calculator/src/index.js.map +1 -0
  173. package/build/treb-calculator/src/notifier-types.d.ts +26 -0
  174. package/build/treb-calculator/src/notifier-types.js +22 -0
  175. package/build/treb-calculator/src/notifier-types.js.map +1 -0
  176. package/build/treb-calculator/src/primitives.d.ts +15 -0
  177. package/build/treb-calculator/src/primitives.js +398 -0
  178. package/build/treb-calculator/src/primitives.js.map +1 -0
  179. package/build/treb-calculator/src/utilities.d.ts +68 -0
  180. package/build/treb-calculator/src/utilities.js +324 -0
  181. package/build/treb-calculator/src/utilities.js.map +1 -0
  182. package/build/treb-charts/src/chart-functions.d.ts +8 -0
  183. package/build/treb-charts/src/chart-functions.js +209 -0
  184. package/build/treb-charts/src/chart-functions.js.map +1 -0
  185. package/build/treb-charts/src/chart-types.d.ts +233 -0
  186. package/build/treb-charts/src/chart-types.js +57 -0
  187. package/build/treb-charts/src/chart-types.js.map +1 -0
  188. package/build/treb-charts/src/chart-utils.d.ts +106 -0
  189. package/build/treb-charts/src/chart-utils.js +1060 -0
  190. package/build/treb-charts/src/chart-utils.js.map +1 -0
  191. package/build/treb-charts/src/chart.d.ts +23 -0
  192. package/build/treb-charts/src/chart.js +94 -0
  193. package/build/treb-charts/src/chart.js.map +1 -0
  194. package/build/treb-charts/src/default-chart-renderer.d.ts +16 -0
  195. package/build/treb-charts/src/default-chart-renderer.js +533 -0
  196. package/build/treb-charts/src/default-chart-renderer.js.map +1 -0
  197. package/build/treb-charts/src/index.d.ts +5 -0
  198. package/build/treb-charts/src/index.js +24 -0
  199. package/build/treb-charts/src/index.js.map +1 -0
  200. package/build/treb-charts/src/main.d.ts +1 -0
  201. package/build/treb-charts/src/main.js +34 -0
  202. package/build/treb-charts/src/main.js.map +1 -0
  203. package/build/treb-charts/src/quicksort.d.ts +1 -0
  204. package/build/treb-charts/src/quicksort.js +49 -0
  205. package/build/treb-charts/src/quicksort.js.map +1 -0
  206. package/build/treb-charts/src/rectangle.d.ts +18 -0
  207. package/build/treb-charts/src/rectangle.js +41 -0
  208. package/build/treb-charts/src/rectangle.js.map +1 -0
  209. package/build/treb-charts/src/renderer-type.d.ts +24 -0
  210. package/build/treb-charts/src/renderer-type.js +22 -0
  211. package/build/treb-charts/src/renderer-type.js.map +1 -0
  212. package/build/treb-charts/src/renderer.d.ts +127 -0
  213. package/build/treb-charts/src/renderer.js +1518 -0
  214. package/build/treb-charts/src/renderer.js.map +1 -0
  215. package/build/treb-charts/src/util.d.ts +18 -0
  216. package/build/treb-charts/src/util.js +71 -0
  217. package/build/treb-charts/src/util.js.map +1 -0
  218. package/build/treb-data-model/src/annotation.d.ts +167 -0
  219. package/build/treb-data-model/src/annotation.js +120 -0
  220. package/build/treb-data-model/src/annotation.js.map +1 -0
  221. package/build/treb-data-model/src/conditional_format.d.ts +155 -0
  222. package/build/treb-data-model/src/conditional_format.js +62 -0
  223. package/build/treb-data-model/src/conditional_format.js.map +1 -0
  224. package/build/treb-data-model/src/data-validation.d.ts +28 -0
  225. package/build/treb-data-model/src/data-validation.js +22 -0
  226. package/build/treb-data-model/src/data-validation.js.map +1 -0
  227. package/build/treb-data-model/src/data_model.d.ts +173 -0
  228. package/build/treb-data-model/src/data_model.js +637 -0
  229. package/build/treb-data-model/src/data_model.js.map +1 -0
  230. package/build/treb-data-model/src/index.d.ts +13 -0
  231. package/build/treb-data-model/src/index.js +28 -0
  232. package/build/treb-data-model/src/index.js.map +1 -0
  233. package/build/treb-data-model/src/language-model.d.ts +22 -0
  234. package/build/treb-data-model/src/language-model.js +22 -0
  235. package/build/treb-data-model/src/language-model.js.map +1 -0
  236. package/build/treb-data-model/src/named.d.ts +124 -0
  237. package/build/treb-data-model/src/named.js +372 -0
  238. package/build/treb-data-model/src/named.js.map +1 -0
  239. package/build/treb-data-model/src/serialize_options.d.ts +49 -0
  240. package/build/treb-data-model/src/serialize_options.js +22 -0
  241. package/build/treb-data-model/src/serialize_options.js.map +1 -0
  242. package/build/treb-data-model/src/sheet.d.ts +499 -0
  243. package/build/treb-data-model/src/sheet.js +2904 -0
  244. package/build/treb-data-model/src/sheet.js.map +1 -0
  245. package/build/treb-data-model/src/sheet_collection.d.ts +58 -0
  246. package/build/treb-data-model/src/sheet_collection.js +112 -0
  247. package/build/treb-data-model/src/sheet_collection.js.map +1 -0
  248. package/build/treb-data-model/src/sheet_selection.d.ts +42 -0
  249. package/build/treb-data-model/src/sheet_selection.js +39 -0
  250. package/build/treb-data-model/src/sheet_selection.js.map +1 -0
  251. package/build/treb-data-model/src/sheet_types.d.ts +104 -0
  252. package/build/treb-data-model/src/sheet_types.js +22 -0
  253. package/build/treb-data-model/src/sheet_types.js.map +1 -0
  254. package/build/treb-data-model/src/types.d.ts +59 -0
  255. package/build/treb-data-model/src/types.js +22 -0
  256. package/build/treb-data-model/src/types.js.map +1 -0
  257. package/build/treb-embed/src/custom-element/spreadsheet-constructor.d.ts +75 -0
  258. package/build/treb-embed/src/custom-element/spreadsheet-constructor.js +1144 -0
  259. package/build/treb-embed/src/custom-element/spreadsheet-constructor.js.map +1 -0
  260. package/build/treb-embed/src/custom-element/treb-global.d.ts +36 -0
  261. package/build/treb-embed/src/custom-element/treb-global.js +64 -0
  262. package/build/treb-embed/src/custom-element/treb-global.js.map +1 -0
  263. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.d.ts +1 -0
  264. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js +61 -0
  265. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js.map +1 -0
  266. package/build/treb-embed/src/embedded-spreadsheet.d.ts +1358 -0
  267. package/build/treb-embed/src/embedded-spreadsheet.js +5205 -0
  268. package/build/treb-embed/src/embedded-spreadsheet.js.map +1 -0
  269. package/build/treb-embed/src/index.d.ts +12 -0
  270. package/build/treb-embed/src/index.js +34 -0
  271. package/build/treb-embed/src/index.js.map +1 -0
  272. package/build/treb-embed/src/options.d.ts +266 -0
  273. package/build/treb-embed/src/options.js +56 -0
  274. package/build/treb-embed/src/options.js.map +1 -0
  275. package/build/treb-embed/src/plugin.d.ts +9 -0
  276. package/build/treb-embed/src/plugin.js +22 -0
  277. package/build/treb-embed/src/plugin.js.map +1 -0
  278. package/build/treb-embed/src/progress-dialog.d.ts +49 -0
  279. package/build/treb-embed/src/progress-dialog.js +178 -0
  280. package/build/treb-embed/src/progress-dialog.js.map +1 -0
  281. package/build/treb-embed/src/selection-state.d.ts +15 -0
  282. package/build/treb-embed/src/selection-state.js +22 -0
  283. package/build/treb-embed/src/selection-state.js.map +1 -0
  284. package/build/treb-embed/src/spinner.d.ts +8 -0
  285. package/build/treb-embed/src/spinner.js +40 -0
  286. package/build/treb-embed/src/spinner.js.map +1 -0
  287. package/build/treb-embed/src/toolbar-message.d.ts +72 -0
  288. package/build/treb-embed/src/toolbar-message.js +22 -0
  289. package/build/treb-embed/src/toolbar-message.js.map +1 -0
  290. package/build/treb-embed/src/types.d.ts +185 -0
  291. package/build/treb-embed/src/types.js +45 -0
  292. package/build/treb-embed/src/types.js.map +1 -0
  293. package/build/treb-embed/tsconfig.tsbuildinfo +1 -0
  294. package/build/treb-export/src/address-type.d.ts +34 -0
  295. package/build/treb-export/src/address-type.js +53 -0
  296. package/build/treb-export/src/address-type.js.map +1 -0
  297. package/build/treb-export/src/base-template.d.ts +1 -0
  298. package/build/treb-export/src/base-template.js +22 -0
  299. package/build/treb-export/src/base-template.js.map +1 -0
  300. package/build/treb-export/src/column-width.d.ts +2 -0
  301. package/build/treb-export/src/column-width.js +80 -0
  302. package/build/treb-export/src/column-width.js.map +1 -0
  303. package/build/treb-export/src/drawing/bubble-chart-template.d.ts +514 -0
  304. package/build/treb-export/src/drawing/bubble-chart-template.js +544 -0
  305. package/build/treb-export/src/drawing/bubble-chart-template.js.map +1 -0
  306. package/build/treb-export/src/drawing/chart-template-components2.d.ts +365 -0
  307. package/build/treb-export/src/drawing/chart-template-components2.js +386 -0
  308. package/build/treb-export/src/drawing/chart-template-components2.js.map +1 -0
  309. package/build/treb-export/src/drawing/chart.d.ts +26 -0
  310. package/build/treb-export/src/drawing/chart.js +247 -0
  311. package/build/treb-export/src/drawing/chart.js.map +1 -0
  312. package/build/treb-export/src/drawing/column-chart-template2.d.ts +490 -0
  313. package/build/treb-export/src/drawing/column-chart-template2.js +518 -0
  314. package/build/treb-export/src/drawing/column-chart-template2.js.map +1 -0
  315. package/build/treb-export/src/drawing/donut-chart-template2.d.ts +272 -0
  316. package/build/treb-export/src/drawing/donut-chart-template2.js +293 -0
  317. package/build/treb-export/src/drawing/donut-chart-template2.js.map +1 -0
  318. package/build/treb-export/src/drawing/drawing.d.ts +49 -0
  319. package/build/treb-export/src/drawing/drawing.js +193 -0
  320. package/build/treb-export/src/drawing/drawing.js.map +1 -0
  321. package/build/treb-export/src/drawing/embedded-image.d.ts +12 -0
  322. package/build/treb-export/src/drawing/embedded-image.js +54 -0
  323. package/build/treb-export/src/drawing/embedded-image.js.map +1 -0
  324. package/build/treb-export/src/drawing/scatter-chart-template2.d.ts +520 -0
  325. package/build/treb-export/src/drawing/scatter-chart-template2.js +551 -0
  326. package/build/treb-export/src/drawing/scatter-chart-template2.js.map +1 -0
  327. package/build/treb-export/src/export.d.ts +72 -0
  328. package/build/treb-export/src/export.js +2039 -0
  329. package/build/treb-export/src/export.js.map +1 -0
  330. package/build/treb-export/src/import-export-messages.d.ts +31 -0
  331. package/build/treb-export/src/import-export-messages.js +22 -0
  332. package/build/treb-export/src/import-export-messages.js.map +1 -0
  333. package/build/treb-export/src/import.d.ts +33 -0
  334. package/build/treb-export/src/import.js +1258 -0
  335. package/build/treb-export/src/import.js.map +1 -0
  336. package/build/treb-export/src/index.worker.d.ts +1 -0
  337. package/build/treb-export/src/index.worker.js +93 -0
  338. package/build/treb-export/src/index.worker.js.map +1 -0
  339. package/build/treb-export/src/metadata.d.ts +51 -0
  340. package/build/treb-export/src/metadata.js +153 -0
  341. package/build/treb-export/src/metadata.js.map +1 -0
  342. package/build/treb-export/src/ooxml.d.ts +7 -0
  343. package/build/treb-export/src/ooxml.js +41 -0
  344. package/build/treb-export/src/ooxml.js.map +1 -0
  345. package/build/treb-export/src/relationship.d.ts +8 -0
  346. package/build/treb-export/src/relationship.js +27 -0
  347. package/build/treb-export/src/relationship.js.map +1 -0
  348. package/build/treb-export/src/shared-strings.d.ts +11 -0
  349. package/build/treb-export/src/shared-strings.js +105 -0
  350. package/build/treb-export/src/shared-strings.js.map +1 -0
  351. package/build/treb-export/src/template-2.d.ts +1 -0
  352. package/build/treb-export/src/template-2.js +22 -0
  353. package/build/treb-export/src/template-2.js.map +1 -0
  354. package/build/treb-export/src/unescape_xml.d.ts +1 -0
  355. package/build/treb-export/src/unescape_xml.js +61 -0
  356. package/build/treb-export/src/unescape_xml.js.map +1 -0
  357. package/build/treb-export/src/workbook-sheet.d.ts +75 -0
  358. package/build/treb-export/src/workbook-sheet.js +128 -0
  359. package/build/treb-export/src/workbook-sheet.js.map +1 -0
  360. package/build/treb-export/src/workbook-style.d.ts +110 -0
  361. package/build/treb-export/src/workbook-style.js +1134 -0
  362. package/build/treb-export/src/workbook-style.js.map +1 -0
  363. package/build/treb-export/src/workbook-theme.d.ts +13 -0
  364. package/build/treb-export/src/workbook-theme.js +85 -0
  365. package/build/treb-export/src/workbook-theme.js.map +1 -0
  366. package/build/treb-export/src/workbook.d.ts +123 -0
  367. package/build/treb-export/src/workbook.js +644 -0
  368. package/build/treb-export/src/workbook.js.map +1 -0
  369. package/build/treb-export/src/xml-test.d.ts +9 -0
  370. package/build/treb-export/src/xml-test.js +52 -0
  371. package/build/treb-export/src/xml-test.js.map +1 -0
  372. package/build/treb-export/src/xml-utils.d.ts +76 -0
  373. package/build/treb-export/src/xml-utils.js +223 -0
  374. package/build/treb-export/src/xml-utils.js.map +1 -0
  375. package/build/treb-export/src/zip-wrapper.d.ts +22 -0
  376. package/build/treb-export/src/zip-wrapper.js +93 -0
  377. package/build/treb-export/src/zip-wrapper.js.map +1 -0
  378. package/build/treb-format/src/format.d.ts +130 -0
  379. package/build/treb-format/src/format.js +805 -0
  380. package/build/treb-format/src/format.js.map +1 -0
  381. package/build/treb-format/src/format_cache.d.ts +55 -0
  382. package/build/treb-format/src/format_cache.js +166 -0
  383. package/build/treb-format/src/format_cache.js.map +1 -0
  384. package/build/treb-format/src/format_parser.d.ts +70 -0
  385. package/build/treb-format/src/format_parser.js +618 -0
  386. package/build/treb-format/src/format_parser.js.map +1 -0
  387. package/build/treb-format/src/index.d.ts +4 -0
  388. package/build/treb-format/src/index.js +25 -0
  389. package/build/treb-format/src/index.js.map +1 -0
  390. package/build/treb-format/src/number_format_section.d.ts +58 -0
  391. package/build/treb-format/src/number_format_section.js +78 -0
  392. package/build/treb-format/src/number_format_section.js.map +1 -0
  393. package/build/treb-format/src/value_parser.d.ts +48 -0
  394. package/build/treb-format/src/value_parser.js +244 -0
  395. package/build/treb-format/src/value_parser.js.map +1 -0
  396. package/build/treb-grid/src/editors/autocomplete.d.ts +39 -0
  397. package/build/treb-grid/src/editors/autocomplete.js +316 -0
  398. package/build/treb-grid/src/editors/autocomplete.js.map +1 -0
  399. package/build/treb-grid/src/editors/autocomplete_matcher.d.ts +74 -0
  400. package/build/treb-grid/src/editors/autocomplete_matcher.js +212 -0
  401. package/build/treb-grid/src/editors/autocomplete_matcher.js.map +1 -0
  402. package/build/treb-grid/src/editors/editor.d.ts +214 -0
  403. package/build/treb-grid/src/editors/editor.js +879 -0
  404. package/build/treb-grid/src/editors/editor.js.map +1 -0
  405. package/build/treb-grid/src/editors/external_editor.d.ts +11 -0
  406. package/build/treb-grid/src/editors/external_editor.js +118 -0
  407. package/build/treb-grid/src/editors/external_editor.js.map +1 -0
  408. package/build/treb-grid/src/editors/formula_bar.d.ts +85 -0
  409. package/build/treb-grid/src/editors/formula_bar.js +444 -0
  410. package/build/treb-grid/src/editors/formula_bar.js.map +1 -0
  411. package/build/treb-grid/src/editors/overlay_editor.d.ts +85 -0
  412. package/build/treb-grid/src/editors/overlay_editor.js +353 -0
  413. package/build/treb-grid/src/editors/overlay_editor.js.map +1 -0
  414. package/build/treb-grid/src/index.d.ts +12 -0
  415. package/build/treb-grid/src/index.js +28 -0
  416. package/build/treb-grid/src/index.js.map +1 -0
  417. package/build/treb-grid/src/layout/base_layout.d.ts +346 -0
  418. package/build/treb-grid/src/layout/base_layout.js +2050 -0
  419. package/build/treb-grid/src/layout/base_layout.js.map +1 -0
  420. package/build/treb-grid/src/layout/grid_layout.d.ts +19 -0
  421. package/build/treb-grid/src/layout/grid_layout.js +235 -0
  422. package/build/treb-grid/src/layout/grid_layout.js.map +1 -0
  423. package/build/treb-grid/src/layout/mock-layout.d.ts +10 -0
  424. package/build/treb-grid/src/layout/mock-layout.js +37 -0
  425. package/build/treb-grid/src/layout/mock-layout.js.map +1 -0
  426. package/build/treb-grid/src/render/selection-renderer.d.ts +97 -0
  427. package/build/treb-grid/src/render/selection-renderer.js +315 -0
  428. package/build/treb-grid/src/render/selection-renderer.js.map +1 -0
  429. package/build/treb-grid/src/render/svg_header_overlay.d.ts +20 -0
  430. package/build/treb-grid/src/render/svg_header_overlay.js +76 -0
  431. package/build/treb-grid/src/render/svg_header_overlay.js.map +1 -0
  432. package/build/treb-grid/src/render/svg_selection_block.d.ts +27 -0
  433. package/build/treb-grid/src/render/svg_selection_block.js +106 -0
  434. package/build/treb-grid/src/render/svg_selection_block.js.map +1 -0
  435. package/build/treb-grid/src/render/tile_renderer.d.ts +121 -0
  436. package/build/treb-grid/src/render/tile_renderer.js +1609 -0
  437. package/build/treb-grid/src/render/tile_renderer.js.map +1 -0
  438. package/build/treb-grid/src/types/border_constants.d.ts +9 -0
  439. package/build/treb-grid/src/types/border_constants.js +34 -0
  440. package/build/treb-grid/src/types/border_constants.js.map +1 -0
  441. package/build/treb-grid/src/types/clipboard_data.d.ts +11 -0
  442. package/build/treb-grid/src/types/clipboard_data.js +22 -0
  443. package/build/treb-grid/src/types/clipboard_data.js.map +1 -0
  444. package/build/treb-grid/src/types/clipboard_data2.d.ts +46 -0
  445. package/build/treb-grid/src/types/clipboard_data2.js +22 -0
  446. package/build/treb-grid/src/types/clipboard_data2.js.map +1 -0
  447. package/build/treb-grid/src/types/drag_mask.d.ts +10 -0
  448. package/build/treb-grid/src/types/drag_mask.js +78 -0
  449. package/build/treb-grid/src/types/drag_mask.js.map +1 -0
  450. package/build/treb-grid/src/types/external_editor_config.d.ts +33 -0
  451. package/build/treb-grid/src/types/external_editor_config.js +22 -0
  452. package/build/treb-grid/src/types/external_editor_config.js.map +1 -0
  453. package/build/treb-grid/src/types/grid.d.ts +806 -0
  454. package/build/treb-grid/src/types/grid.js +6410 -0
  455. package/build/treb-grid/src/types/grid.js.map +1 -0
  456. package/build/treb-grid/src/types/grid_base.d.ts +442 -0
  457. package/build/treb-grid/src/types/grid_base.js +3523 -0
  458. package/build/treb-grid/src/types/grid_base.js.map +1 -0
  459. package/build/treb-grid/src/types/grid_command.d.ts +408 -0
  460. package/build/treb-grid/src/types/grid_command.js +75 -0
  461. package/build/treb-grid/src/types/grid_command.js.map +1 -0
  462. package/build/treb-grid/src/types/grid_events.d.ts +93 -0
  463. package/build/treb-grid/src/types/grid_events.js +36 -0
  464. package/build/treb-grid/src/types/grid_events.js.map +1 -0
  465. package/build/treb-grid/src/types/grid_options.d.ts +50 -0
  466. package/build/treb-grid/src/types/grid_options.js +34 -0
  467. package/build/treb-grid/src/types/grid_options.js.map +1 -0
  468. package/build/treb-grid/src/types/scale-control.d.ts +21 -0
  469. package/build/treb-grid/src/types/scale-control.js +148 -0
  470. package/build/treb-grid/src/types/scale-control.js.map +1 -0
  471. package/build/treb-grid/src/types/set_range_options.d.ts +24 -0
  472. package/build/treb-grid/src/types/set_range_options.js +22 -0
  473. package/build/treb-grid/src/types/set_range_options.js.map +1 -0
  474. package/build/treb-grid/src/types/tab_bar.d.ts +84 -0
  475. package/build/treb-grid/src/types/tab_bar.js +426 -0
  476. package/build/treb-grid/src/types/tab_bar.js.map +1 -0
  477. package/build/treb-grid/src/types/tile.d.ts +29 -0
  478. package/build/treb-grid/src/types/tile.js +22 -0
  479. package/build/treb-grid/src/types/tile.js.map +1 -0
  480. package/build/treb-grid/src/types/update_flags.d.ts +48 -0
  481. package/build/treb-grid/src/types/update_flags.js +22 -0
  482. package/build/treb-grid/src/types/update_flags.js.map +1 -0
  483. package/build/treb-grid/src/util/fontmetrics.d.ts +21 -0
  484. package/build/treb-grid/src/util/fontmetrics.js +82 -0
  485. package/build/treb-grid/src/util/fontmetrics.js.map +1 -0
  486. package/build/treb-grid/src/util/ua.d.ts +33 -0
  487. package/build/treb-grid/src/util/ua.js +86 -0
  488. package/build/treb-grid/src/util/ua.js.map +1 -0
  489. package/build/treb-parser/src/csv-parser.d.ts +13 -0
  490. package/build/treb-parser/src/csv-parser.js +107 -0
  491. package/build/treb-parser/src/csv-parser.js.map +1 -0
  492. package/build/treb-parser/src/index.d.ts +4 -0
  493. package/build/treb-parser/src/index.js +25 -0
  494. package/build/treb-parser/src/index.js.map +1 -0
  495. package/build/treb-parser/src/md-parser.d.ts +97 -0
  496. package/build/treb-parser/src/md-parser.js +403 -0
  497. package/build/treb-parser/src/md-parser.js.map +1 -0
  498. package/build/treb-parser/src/parser-types.d.ts +345 -0
  499. package/build/treb-parser/src/parser-types.js +53 -0
  500. package/build/treb-parser/src/parser-types.js.map +1 -0
  501. package/build/treb-parser/src/parser.d.ts +422 -0
  502. package/build/treb-parser/src/parser.js +2418 -0
  503. package/build/treb-parser/src/parser.js.map +1 -0
  504. package/build/treb-utils/src/event_source.d.ts +34 -0
  505. package/build/treb-utils/src/event_source.js +110 -0
  506. package/build/treb-utils/src/event_source.js.map +1 -0
  507. package/build/treb-utils/src/ievent_source.d.ts +9 -0
  508. package/build/treb-utils/src/ievent_source.js +22 -0
  509. package/build/treb-utils/src/ievent_source.js.map +1 -0
  510. package/build/treb-utils/src/index.d.ts +6 -0
  511. package/build/treb-utils/src/index.js +30 -0
  512. package/build/treb-utils/src/index.js.map +1 -0
  513. package/build/treb-utils/src/measurement.d.ts +42 -0
  514. package/build/treb-utils/src/measurement.js +145 -0
  515. package/build/treb-utils/src/measurement.js.map +1 -0
  516. package/build/treb-utils/src/scale.d.ts +16 -0
  517. package/build/treb-utils/src/scale.js +106 -0
  518. package/build/treb-utils/src/scale.js.map +1 -0
  519. package/build/treb-utils/src/serialize_html.d.ts +5 -0
  520. package/build/treb-utils/src/serialize_html.js +128 -0
  521. package/build/treb-utils/src/serialize_html.js.map +1 -0
  522. package/build/treb-utils/src/validate_uri.d.ts +20 -0
  523. package/build/treb-utils/src/validate_uri.js +55 -0
  524. package/build/treb-utils/src/validate_uri.js.map +1 -0
  525. package/dist/{chunk-XD5PEZBZ.mjs → chunk-E35ONJUS.mjs} +1 -1
  526. package/dist/treb-export-worker.mjs +2 -2
  527. package/dist/treb-spreadsheet.mjs +4 -4
  528. package/package.json +1 -1
@@ -0,0 +1,2611 @@
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-2026 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+ import * as Utils from '../utilities';
22
+ // import { StringUnion, NumberUnion } from '../utilities';
23
+ import { ReferenceError, NAError, ArgumentError, DivideByZeroError, ValueError } from '../function-error';
24
+ import { Box, ValueType, GetValueType, ComplexOrReal, IsComplex, Area } from 'treb-base-types';
25
+ import { Sparkline } from './sparkline';
26
+ import { LotusDate, UnlotusDate } from 'treb-format';
27
+ import { ClickCheckbox, RenderCheckbox } from './checkbox';
28
+ import { UnionIsMetadata } from '../expression-calculator';
29
+ import { Exp as ComplexExp, Power as ComplexPower, Multiply as ComplexMultply } from '../complex-math';
30
+ import * as ComplexMath from '../complex-math';
31
+ import { CoerceComplex } from './function-utilities';
32
+ import { ConstructDate } from './date-utils';
33
+ // import type { CalculationContext } from '../descriptors';
34
+ /**
35
+ * BaseFunctionLibrary is a static object that has basic spreadsheet
36
+ * functions and associated metadata (there's also a list of aliases).
37
+ *
38
+ * Calculator should register this one first, followed by any other
39
+ * application-specific libraries.
40
+ *
41
+ * FIXME: there's no reason this has to be a single, monolithic library.
42
+ * we could split up by category or something.
43
+ *
44
+ * ALSO: add category to descriptor.
45
+ */
46
+ /** milliseconds in one day, used in time functions */
47
+ // const DAY_MS = 1000 * 60 * 60 * 24;
48
+ // some functions have semantics that can't be represented inline,
49
+ // or we may want to refer to them from other functions.
50
+ // OK, just one.
51
+ const edate_calc = (start, months) => {
52
+ let date = new Date(LotusDate(start));
53
+ let month = date.getUTCMonth() + months;
54
+ let year = date.getUTCFullYear();
55
+ // if we don't ensure the time we'll wind up hitting boundary cases
56
+ date.setUTCHours(12);
57
+ date.setUTCMinutes(0);
58
+ date.setUTCSeconds(0);
59
+ date.setUTCMilliseconds(0);
60
+ while (month < 0) {
61
+ month += 12;
62
+ year--;
63
+ }
64
+ while (month > 11) {
65
+ month -= 12;
66
+ year++;
67
+ }
68
+ date.setUTCMonth(month);
69
+ date.setUTCFullYear(year);
70
+ // if this rolls over the month, then we need to go back to the
71
+ // last valid day of the month. so jan 31 + 1 month needs to equal
72
+ // feb 28 (feb 29 in leap year).
73
+ const check_month = date.getUTCMonth();
74
+ if (check_month !== month) {
75
+ const days = date.getUTCDate();
76
+ date = new Date(date.getTime() - (days * 86400 * 1000));
77
+ }
78
+ return date;
79
+ };
80
+ const zlookup_arguments = [
81
+ {
82
+ name: "Lookup value",
83
+ },
84
+ {
85
+ name: "Table",
86
+ },
87
+ {
88
+ name: "Result index",
89
+ },
90
+ {
91
+ name: "Inexact",
92
+ default: true,
93
+ },
94
+ ];
95
+ /**
96
+ * unified VLOOKUP/HLOOKUP. ordinarily we'd call it XLOOKUP but that's taken.
97
+ * FIXME: can't use use that function for this?
98
+ */
99
+ const ZLookup = (value, table, col, inexact = true, transpose = false) => {
100
+ if (transpose) {
101
+ table = Utils.TransposeArray(table);
102
+ }
103
+ col = Math.max(0, col - 1);
104
+ // inexact is the default. this assumes that the data is sorted,
105
+ // either numerically or alphabetically. it returns the closest
106
+ // value without going over -- meaning walk the list, and when
107
+ // you're over return the _previous_ item. except if there's an
108
+ // exact match, I guess, in that case return the exact match.
109
+ // FIXME: there's a hint in the docs for XLOOKUP that this might
110
+ // be using a binary search. not sure why, but that might be
111
+ // correct.
112
+ if (inexact) {
113
+ let result = table[col][0];
114
+ if (typeof value === 'number') {
115
+ let compare = Number(table[0][0]);
116
+ if (isNaN(compare) || compare > value) {
117
+ return NAError();
118
+ }
119
+ for (let i = 1; i < table[0].length; i++) {
120
+ compare = Number(table[0][i]);
121
+ if (isNaN(compare) || compare > value) {
122
+ break;
123
+ }
124
+ result = table[col][i];
125
+ }
126
+ }
127
+ else {
128
+ value = (value || '').toString().toLowerCase(); // ?
129
+ let compare = (table[0][0] || '').toString().toLowerCase();
130
+ if (compare.localeCompare(value) > 0) {
131
+ return NAError();
132
+ }
133
+ for (let i = 1; i < table[0].length; i++) {
134
+ compare = (table[0][i] || '').toString().toLowerCase();
135
+ if (compare.localeCompare(value) > 0) {
136
+ break;
137
+ }
138
+ result = table[col][i];
139
+ }
140
+ }
141
+ return Box(result);
142
+ }
143
+ else {
144
+ for (let i = 0; i < table[0].length; i++) {
145
+ if (table[0][i] == value) { // ==
146
+ return Box(table[col][i]);
147
+ }
148
+ }
149
+ return NAError();
150
+ }
151
+ };
152
+ const NumberArgument = (argument, default_value = false) => {
153
+ if (!argument) {
154
+ return default_value;
155
+ }
156
+ switch (argument.type) {
157
+ case ValueType.number:
158
+ return argument.value;
159
+ case ValueType.undefined:
160
+ return default_value;
161
+ }
162
+ return false;
163
+ };
164
+ /**
165
+ * helper for trig functions, curious what this does to size
166
+ */
167
+ const TrigFunction = (real, complex, description) => {
168
+ return {
169
+ description,
170
+ arguments: [
171
+ { name: 'number', boxed: true, unroll: true },
172
+ ],
173
+ fn: (a) => {
174
+ if (a.type === ValueType.number) {
175
+ return { type: ValueType.number, value: real(a.value) };
176
+ }
177
+ if (a.type === ValueType.complex) {
178
+ return { type: ValueType.complex, value: complex(a.value) };
179
+ }
180
+ return ArgumentError();
181
+ },
182
+ };
183
+ };
184
+ /**
185
+ * helper for sorting. kind of weird rules
186
+ */
187
+ const SortHelper = (order, av, bv) => {
188
+ // OK from what I can tell the rules are
189
+ //
190
+ // (1) no type coercion (but see below for blanks)
191
+ // (2) strings are sorted case-insensitive
192
+ // (3) blank values are coerced -> 0 but these are not sorted as numbers
193
+ // (4) order is numbers, strings, booleans, then blanks
194
+ //
195
+ // some of which makes sense, I guess...
196
+ //
197
+ // blanks are always sorted last, irrespective of sort order. which
198
+ // means we can't sort and then reverse, because reverse sort is not
199
+ // the inverse of forward sort
200
+ // special case
201
+ if (av === bv) {
202
+ return 0;
203
+ }
204
+ // special case
205
+ if (av === undefined) {
206
+ return 1;
207
+ }
208
+ if (bv === undefined) {
209
+ return -1;
210
+ }
211
+ // actual comparisons
212
+ if (typeof av === 'number' && typeof bv === 'number') {
213
+ return (av - bv) * order;
214
+ }
215
+ if (typeof av === 'string' && typeof bv === 'string') {
216
+ return av.toLocaleLowerCase().localeCompare(bv.toLowerCase()) * order;
217
+ }
218
+ if (typeof av === 'boolean' && typeof bv === 'boolean') {
219
+ return av ? order : -order;
220
+ }
221
+ if (IsComplex(av) && IsComplex(bv)) {
222
+ return 0; // no sort order
223
+ }
224
+ // type rules
225
+ const types = [av, bv].map(x => {
226
+ switch (typeof x) {
227
+ case 'number':
228
+ return 0;
229
+ case 'string':
230
+ return 2;
231
+ case 'boolean':
232
+ return 3;
233
+ default:
234
+ if (IsComplex(x)) {
235
+ return 1;
236
+ }
237
+ return 4;
238
+ }
239
+ });
240
+ return (types[0] - types[1]) * order;
241
+ };
242
+ /**
243
+ * alternate functions. these are used (atm) only for changing complex
244
+ * behavior.
245
+ */
246
+ export const AltFunctionLibrary = {
247
+ Sqrt: {
248
+ description: 'Returns the square root of the argument',
249
+ arguments: [
250
+ { boxed: true, unroll: true },
251
+ ],
252
+ fn: (ref) => {
253
+ if (ref.type === ValueType.complex) {
254
+ const value = ComplexPower(ref.value, { real: 0.5, imaginary: 0 });
255
+ return ComplexOrReal(value);
256
+ }
257
+ else if (ref.type === ValueType.undefined || !ref.value) {
258
+ return {
259
+ type: ValueType.number, value: 0,
260
+ };
261
+ }
262
+ else if (ref.type === ValueType.number && ref.value < 0) {
263
+ const value = ComplexPower({ real: ref.value, imaginary: 0 }, { real: 0.5, imaginary: 0 });
264
+ return {
265
+ type: ValueType.complex,
266
+ value,
267
+ };
268
+ }
269
+ else if (ref.type === ValueType.number) {
270
+ const value = Math.sqrt(ref.value);
271
+ if (isNaN(value)) {
272
+ return ValueError();
273
+ }
274
+ return { type: ValueType.number, value };
275
+ }
276
+ else {
277
+ /*
278
+ const value = Math.sqrt(ref.value);
279
+ if (isNaN(value)) {
280
+ return ValueError();
281
+ }
282
+ return { type: ValueType.number, value };
283
+ */
284
+ return ValueError();
285
+ }
286
+ },
287
+ },
288
+ Power: {
289
+ description: 'Returns base raised to the given power',
290
+ arguments: [
291
+ { name: 'base', boxed: true, unroll: true, },
292
+ { name: 'exponent', boxed: true, unroll: true, }
293
+ ],
294
+ fn: (base, exponent) => {
295
+ // we're leaking complex numbers here because our functions are
296
+ // very slightly imprecise. I would like to stop doing that. try to
297
+ // use real math unless absolutely necessary.
298
+ // in the alternative we could update the epsilon on our ComplexOrReal
299
+ // function, but I would prefer not to do that if we don't have to.
300
+ // so: if both arguments are real, and base is >= 0 we can use real math.
301
+ // also if exponent is either 0 or >= 1 we can use real math.
302
+ if (base.type === ValueType.number && exponent.type === ValueType.number) {
303
+ if (base.value >= 0 || exponent.value === 0 || Math.abs(exponent.value) >= 1) {
304
+ const value = Math.pow(base.value, exponent.value);
305
+ if (isNaN(value)) {
306
+ return ValueError();
307
+ }
308
+ return { type: ValueType.number, value };
309
+ }
310
+ }
311
+ const a = CoerceComplex(base);
312
+ const b = CoerceComplex(exponent);
313
+ if (a && b) {
314
+ const value = ComplexPower(a, b);
315
+ return ComplexOrReal(value);
316
+ }
317
+ return ValueError();
318
+ },
319
+ },
320
+ };
321
+ // use a single, static object for base functions
322
+ export const BaseFunctionLibrary = {
323
+ // not sure why there are functions for booleans, but there are
324
+ True: {
325
+ fn: () => ({ type: ValueType.boolean, value: true }),
326
+ },
327
+ False: {
328
+ fn: () => ({ type: ValueType.boolean, value: false }),
329
+ },
330
+ Int: {
331
+ fn: (value) => {
332
+ return { type: ValueType.number, value: Math.floor(value) };
333
+ },
334
+ },
335
+ Rand: {
336
+ volatile: true,
337
+ fn: () => { return { type: ValueType.number, value: Math.random() }; },
338
+ },
339
+ RandArray: {
340
+ volatile: true,
341
+ arguments: [
342
+ { name: 'rows' },
343
+ { name: 'columns' },
344
+ { name: 'min' },
345
+ { name: 'max' },
346
+ { name: 'integer' },
347
+ ],
348
+ description: 'Returns an array of uniformly-distributed random numbers',
349
+ fn: (rows = 1, columns = 1, min = 0, max = 1, integer = false) => {
350
+ const value = [];
351
+ if (integer) {
352
+ const range = max - min + 1;
353
+ for (let i = 0; i < columns; i++) {
354
+ const row = [];
355
+ for (let j = 0; j < rows; j++) {
356
+ row.push({
357
+ type: ValueType.number,
358
+ value: Math.floor(Math.random() * range + min),
359
+ });
360
+ }
361
+ value.push(row);
362
+ }
363
+ }
364
+ else {
365
+ const range = max - min;
366
+ for (let i = 0; i < columns; i++) {
367
+ const row = [];
368
+ for (let j = 0; j < rows; j++) {
369
+ row.push({
370
+ type: ValueType.number,
371
+ value: Math.random() * range + min,
372
+ });
373
+ }
374
+ value.push(row);
375
+ }
376
+ }
377
+ return {
378
+ type: ValueType.array,
379
+ value,
380
+ };
381
+ },
382
+ },
383
+ RandBetween: {
384
+ arguments: [{ name: 'min' }, { name: 'max' }],
385
+ volatile: true,
386
+ fn: (min = 0, max = 1) => {
387
+ if (min > max) {
388
+ const tmp = min;
389
+ min = max;
390
+ max = tmp;
391
+ }
392
+ return { type: ValueType.number, value: Math.floor(Math.random() * (max + 1 - min) + min) };
393
+ },
394
+ },
395
+ Sum: {
396
+ description: 'Adds arguments and ranges',
397
+ arguments: [{
398
+ boxed: true,
399
+ name: 'values or ranges',
400
+ repeat: true,
401
+ }],
402
+ fn: (...args) => {
403
+ const sum = { real: 0, imaginary: 0 };
404
+ const values = Utils.FlattenBoxed(args); // as UnionValue[];
405
+ for (const value of values) {
406
+ switch (value.type) {
407
+ case ValueType.number:
408
+ sum.real += value.value;
409
+ break;
410
+ case ValueType.boolean:
411
+ // sum.real += (value.value ? 1 : 0); // ??
412
+ break;
413
+ case ValueType.complex:
414
+ sum.real += value.value.real;
415
+ sum.imaginary += value.value.imaginary;
416
+ break;
417
+ case ValueType.error: return value;
418
+ }
419
+ }
420
+ return ComplexOrReal(sum);
421
+ },
422
+ },
423
+ SumSQ: {
424
+ description: 'Returns the sum of the squares of all arguments',
425
+ arguments: [{ boxed: true, name: 'values or ranges', repeat: true, }],
426
+ fn: (...args) => {
427
+ const sum = { real: 0, imaginary: 0 };
428
+ const values = Utils.FlattenBoxed(args); // as UnionValue[];
429
+ for (const value of values) {
430
+ switch (value.type) {
431
+ case ValueType.number:
432
+ sum.real += value.value * value.value;
433
+ break;
434
+ case ValueType.boolean:
435
+ // sum.real += (value.value ? 1 : 0); // ??
436
+ break;
437
+ case ValueType.complex:
438
+ {
439
+ const squared = ComplexMath.Multiply(value.value, value.value);
440
+ // sum.real += value.value.real;
441
+ // sum.imaginary += value.value.imaginary;
442
+ sum.real += squared.real;
443
+ sum.imaginary += squared.imaginary;
444
+ }
445
+ break;
446
+ case ValueType.error: return value;
447
+ }
448
+ }
449
+ return ComplexOrReal(sum);
450
+ },
451
+ },
452
+ // --- FIXME: break out date functions? --------------------------------------
453
+ EDate: {
454
+ arguments: [
455
+ {
456
+ name: 'Start date',
457
+ unroll: true,
458
+ },
459
+ {
460
+ name: 'Months',
461
+ unroll: true,
462
+ },
463
+ ],
464
+ fn: (start, months = 0) => {
465
+ if (typeof start !== 'number' || typeof months !== 'number') {
466
+ return ArgumentError();
467
+ }
468
+ const date = edate_calc(start, months);
469
+ return { type: ValueType.number, value: UnlotusDate(date.getTime(), false) };
470
+ }
471
+ },
472
+ EOMonth: {
473
+ arguments: [
474
+ {
475
+ name: 'Start date',
476
+ unroll: true,
477
+ },
478
+ {
479
+ name: 'Months',
480
+ unroll: true,
481
+ },
482
+ ],
483
+ fn: (start, months = 0) => {
484
+ // this is the same as edate, except it advances to the end of the
485
+ // month. so jan 15, 2023 plus one month -> feb 28, 2023 (last day).
486
+ if (typeof start !== 'number' || typeof months !== 'number') {
487
+ return ArgumentError();
488
+ }
489
+ const date = edate_calc(start, months);
490
+ const month = date.getUTCMonth();
491
+ switch (month) {
492
+ case 1: // feb, special
493
+ {
494
+ const year = date.getUTCFullYear();
495
+ // it's a leap year if it is divisible by 4, unless it's also
496
+ // divisible by 100 AND NOT divisible by 400. 1900 is grand-
497
+ // fathered in via an error in Lotus.
498
+ if (year % 4 === 0 && (year === 1900 || (year % 400 === 0) || (year % 100 !== 0))) {
499
+ date.setUTCDate(29);
500
+ }
501
+ else {
502
+ date.setUTCDate(28);
503
+ }
504
+ }
505
+ break;
506
+ case 0: // jan
507
+ case 2:
508
+ case 4:
509
+ case 6: // july
510
+ case 7: // august
511
+ case 9:
512
+ case 11: // dec
513
+ date.setUTCDate(31);
514
+ break;
515
+ default:
516
+ date.setUTCDate(30);
517
+ break;
518
+ }
519
+ return { type: ValueType.number, value: UnlotusDate(date.getTime(), false) };
520
+ }
521
+ },
522
+ Now: {
523
+ description: 'Returns current time',
524
+ volatile: true,
525
+ fn: () => {
526
+ return { type: ValueType.number, value: UnlotusDate(new Date().getTime()) };
527
+ },
528
+ },
529
+ YearFrac: {
530
+ description: 'Returns the fraction of a year between two dates',
531
+ arguments: [
532
+ { name: 'Start', },
533
+ { name: 'End', },
534
+ { name: 'Basis', default: 0 },
535
+ ],
536
+ fn: (start, end, basis) => {
537
+ // is this in the spec? should it not be negative here? (...)
538
+ if (end < start) {
539
+ const temp = start;
540
+ start = end;
541
+ end = temp;
542
+ }
543
+ const delta = Math.max(0, end - start);
544
+ let divisor = 360;
545
+ if (basis && basis < 0 || basis > 4) {
546
+ return ArgumentError();
547
+ }
548
+ // console.info({start, end, basis, delta});
549
+ switch (basis) {
550
+ case 1:
551
+ break;
552
+ case 2:
553
+ break;
554
+ case 3:
555
+ divisor = 365;
556
+ break;
557
+ }
558
+ return {
559
+ type: ValueType.number,
560
+ value: delta / divisor,
561
+ };
562
+ },
563
+ },
564
+ Date: {
565
+ description: 'Constructs a date from year/month/day',
566
+ arguments: [
567
+ { name: 'year', unroll: true },
568
+ { name: 'month', unroll: true },
569
+ { name: 'day', unroll: true },
570
+ ],
571
+ fn: (year, month, day) => {
572
+ const date = ConstructDate(year, month, day);
573
+ if (date === false) {
574
+ return ArgumentError();
575
+ }
576
+ return { type: ValueType.number, value: date };
577
+ },
578
+ },
579
+ Today: {
580
+ description: 'Returns current day',
581
+ volatile: true,
582
+ fn: () => {
583
+ const date = new Date();
584
+ date.setMilliseconds(0);
585
+ date.setSeconds(0);
586
+ date.setMinutes(0);
587
+ date.setHours(12);
588
+ return { type: ValueType.number, value: UnlotusDate(date.getTime()) };
589
+ },
590
+ },
591
+ // ---------------------------------------------------------------------------
592
+ IfError: {
593
+ description: 'Returns the original value, or the alternate value if the original value contains an error',
594
+ arguments: [{ name: 'original value', allow_error: true, boxed: true }, { name: 'alternate value' }],
595
+ fn: (ref, value_if_error = 0) => {
596
+ if (ref && ref.type === ValueType.error) {
597
+ return { value: value_if_error, type: GetValueType(value_if_error) };
598
+ }
599
+ return ref;
600
+ },
601
+ },
602
+ IsNA: {
603
+ description: 'Checks if another cell contains a #NA error',
604
+ arguments: [{ name: 'reference', allow_error: true, boxed: true }],
605
+ fn: (...args) => {
606
+ const values = Utils.FlattenBoxed(args);
607
+ for (const value of values) {
608
+ if (value.type === ValueType.error) {
609
+ if (value.value === 'N/A') {
610
+ return { type: ValueType.boolean, value: true };
611
+ }
612
+ }
613
+ }
614
+ return { type: ValueType.boolean, value: false };
615
+ },
616
+ },
617
+ IsErr: {
618
+ description: 'Checks if another cell contains an error',
619
+ arguments: [{ name: 'reference', allow_error: true, boxed: true }],
620
+ fn: (...args) => {
621
+ const values = Utils.FlattenBoxed(args);
622
+ for (const value of values) {
623
+ if (value.type === ValueType.error && value.value !== 'N/A') {
624
+ return { type: ValueType.boolean, value: true };
625
+ }
626
+ }
627
+ /*
628
+ if (Array.isArray(ref)) {
629
+ const values = Utils.Flatten(ref) as UnionValue[];
630
+ for (const value of values) {
631
+ if (value.type === ValueType.error) {
632
+ return { type: ValueType.boolean, value: true };
633
+ }
634
+ }
635
+ }
636
+ else if (ref) {
637
+ return { type: ValueType.boolean, value: ref.type === ValueType.error };
638
+ }
639
+ */
640
+ return { type: ValueType.boolean, value: false };
641
+ },
642
+ },
643
+ IsError: {
644
+ description: 'Checks if another cell contains an error',
645
+ arguments: [{ name: 'reference', allow_error: true, boxed: true }],
646
+ fn: (...args) => {
647
+ const values = Utils.FlattenBoxed(args);
648
+ for (const value of values) {
649
+ if (value.type === ValueType.error) {
650
+ return { type: ValueType.boolean, value: true };
651
+ }
652
+ }
653
+ /*
654
+ if (Array.isArray(ref)) {
655
+ const values = Utils.Flatten(ref) as UnionValue[];
656
+ for (const value of values) {
657
+ if (value.type === ValueType.error) {
658
+ return { type: ValueType.boolean, value: true };
659
+ }
660
+ }
661
+ }
662
+ else if (ref) {
663
+ return { type: ValueType.boolean, value: ref.type === ValueType.error };
664
+ }
665
+ */
666
+ return { type: ValueType.boolean, value: false };
667
+ },
668
+ },
669
+ /*
670
+ Cell: {
671
+ description: 'Returns data about a cell',
672
+ arguments: [
673
+ { name: 'type', description: 'Type of data to return', unroll: true, },
674
+ { name: 'reference', description: 'Cell reference', metadata: true, unroll: true, },
675
+ ],
676
+
677
+ // there's no concept of "structure volatile", and structure events
678
+ // don't trigger recalc, so this is not helpful -- we may need to
679
+ // think about both of those things
680
+
681
+ // volatile: true,
682
+
683
+ fn: (type: string, reference: UnionValue): UnionValue => {
684
+
685
+ if (!UnionIsMetadata(reference)) {
686
+ return ReferenceError();
687
+ }
688
+
689
+ if (type) {
690
+ switch (type.toString().toLowerCase()) {
691
+ case 'format':
692
+ return reference.value.format ? // || ReferenceError;
693
+ { type: ValueType.string, value: reference.value.format } : ReferenceError();
694
+ case 'address':
695
+
696
+ // FIXME: this needs to return a fully-qualified address, we'll need access to the model
697
+
698
+ return { type: ValueType.string, value: '[]' + reference.value.address.label.replace(/\$/g, '') };
699
+ }
700
+ }
701
+
702
+ return { type: ValueType.error, value: NotImplError.error };
703
+
704
+ },
705
+
706
+ },
707
+ */
708
+ Year: {
709
+ description: 'Returns year from date',
710
+ arguments: [{
711
+ name: 'date', unroll: true,
712
+ }],
713
+ fn: (source) => {
714
+ return Box(new Date(LotusDate(source)).getUTCFullYear());
715
+ },
716
+ },
717
+ Month: {
718
+ description: 'Returns month from date',
719
+ arguments: [{
720
+ name: 'date', unroll: true,
721
+ }],
722
+ fn: (source) => {
723
+ return Box(new Date(LotusDate(source)).getUTCMonth() + 1); // 0-based
724
+ },
725
+ },
726
+ Day: {
727
+ description: 'Returns day of month from date',
728
+ arguments: [{
729
+ name: 'date', unroll: true,
730
+ }],
731
+ fn: (source) => {
732
+ return Box(new Date(LotusDate(source)).getUTCDate());
733
+ },
734
+ },
735
+ Radians: {
736
+ description: 'Converts degrees to radians',
737
+ arguments: [{ name: 'Degrees', description: 'Angle in degrees', unroll: true }],
738
+ fn: (degrees) => {
739
+ return Box(degrees * Math.PI / 180);
740
+ },
741
+ },
742
+ Degrees: {
743
+ description: 'Converts radians to degrees',
744
+ arguments: [{ name: 'Radians', description: 'Angle in radians', unroll: true }],
745
+ fn: (radians) => {
746
+ return Box(radians / Math.PI * 180);
747
+ },
748
+ },
749
+ CountA: {
750
+ description: 'Counts cells that are not empty',
751
+ fn: (...args) => {
752
+ return Box(Utils.FlattenCellValues(args).reduce((a, b) => {
753
+ if (typeof b === 'undefined')
754
+ return a;
755
+ return a + 1;
756
+ }, 0));
757
+ },
758
+ },
759
+ Count: {
760
+ description: 'Counts cells that contain numbers',
761
+ fn: (...args) => {
762
+ return Box(Utils.FlattenCellValues(args).reduce((a, b) => {
763
+ if (typeof b === 'number' || IsComplex(b))
764
+ return a + 1;
765
+ return a;
766
+ }, 0));
767
+ },
768
+ },
769
+ Or: {
770
+ fn: (...args) => {
771
+ let result = false;
772
+ args = Utils.FlattenCellValues(args);
773
+ for (const arg of args) {
774
+ result = result || !!arg;
775
+ }
776
+ return Box(result);
777
+ },
778
+ },
779
+ And: {
780
+ fn: (...args) => {
781
+ let result = true;
782
+ args = Utils.FlattenCellValues(args);
783
+ for (const arg of args) {
784
+ result = result && !!arg;
785
+ }
786
+ return Box(result);
787
+ },
788
+ },
789
+ Not: {
790
+ arguments: [{ unroll: true, boxed: true }],
791
+ fn: (arg) => {
792
+ let value = false;
793
+ if (arg) {
794
+ switch (arg.type) {
795
+ case ValueType.undefined:
796
+ value = false;
797
+ break;
798
+ case ValueType.string:
799
+ case ValueType.boolean:
800
+ case ValueType.number:
801
+ value = !!arg.value;
802
+ break;
803
+ case ValueType.error:
804
+ return arg;
805
+ }
806
+ }
807
+ return Box(!value);
808
+ },
809
+ },
810
+ If: {
811
+ arguments: [
812
+ { name: 'test value', boxed: true },
813
+ { name: 'value if true', boxed: true, allow_error: true },
814
+ { name: 'value if false', boxed: true, allow_error: true },
815
+ ],
816
+ /**
817
+ * should we really have defaults for the t/f paths? not sure what X does
818
+ * @returns
819
+ */
820
+ fn: (a, b = { type: ValueType.boolean, value: true }, c = { type: ValueType.boolean, value: false }) => {
821
+ const b_array = b.type === ValueType.array;
822
+ const c_array = c.type === ValueType.array;
823
+ if (a.type === ValueType.array) {
824
+ return {
825
+ type: ValueType.array,
826
+ value: a.value.map((row, x) => row.map((cell, y) => {
827
+ const value = (cell.type === ValueType.string) ?
828
+ (cell.value.toLowerCase() !== 'false' && cell.value.toLowerCase() !== 'f') : !!cell.value;
829
+ return value ? (b_array ? b.value[x][y] : b) : (c_array ? c.value[x][y] : c);
830
+ })),
831
+ };
832
+ }
833
+ const value = a.type === ValueType.string ? // UnionIs.String(a) ?
834
+ (a.value.toLowerCase() !== 'false' && a.value.toLowerCase() !== 'f') : !!a.value;
835
+ return value ? b : c;
836
+ },
837
+ },
838
+ Fact: {
839
+ description: 'Returns the factorial of a number',
840
+ arguments: [
841
+ { name: 'number', unroll: true },
842
+ ],
843
+ fn: (number) => {
844
+ number = Math.round(number);
845
+ let value = 1;
846
+ while (number > 1) {
847
+ value *= number;
848
+ number--;
849
+ }
850
+ return {
851
+ type: ValueType.number,
852
+ value,
853
+ };
854
+ },
855
+ },
856
+ Factdouble: {
857
+ description: 'Returns the double factorial of a number',
858
+ arguments: [
859
+ { name: 'number', unroll: true },
860
+ ],
861
+ fn: (number) => {
862
+ number = Math.round(number);
863
+ let value = 1;
864
+ while (number > 1) {
865
+ value *= number;
866
+ number -= 2;
867
+ }
868
+ return {
869
+ type: ValueType.number,
870
+ value,
871
+ };
872
+ },
873
+ },
874
+ Power: {
875
+ description: 'Returns base raised to the given power',
876
+ arguments: [
877
+ { name: 'base', boxed: true, unroll: true, },
878
+ { name: 'exponent', boxed: true, unroll: true, }
879
+ ],
880
+ fn: (base, exponent) => {
881
+ const a = CoerceComplex(base);
882
+ const b = CoerceComplex(exponent);
883
+ if (!a || !b) {
884
+ return ValueError();
885
+ }
886
+ if (base.type === ValueType.complex || exponent.type === ValueType.complex) {
887
+ return ComplexOrReal(ComplexPower(a, b));
888
+ }
889
+ else {
890
+ const value = Math.pow(a.real, b.real);
891
+ if (isNaN(value)) {
892
+ return ValueError();
893
+ }
894
+ return { type: ValueType.number, value };
895
+ // return Box(Math.pow(base.value, exponent.value))
896
+ }
897
+ },
898
+ },
899
+ Mod: {
900
+ arguments: [
901
+ { unroll: true },
902
+ { unroll: true },
903
+ ],
904
+ fn: (num, divisor) => {
905
+ if (!divisor) {
906
+ return DivideByZeroError();
907
+ }
908
+ return Box(num % divisor);
909
+ },
910
+ },
911
+ Large: {
912
+ description: 'Returns the nth numeric value from the data, in descending order',
913
+ arguments: [
914
+ {
915
+ name: 'values',
916
+ },
917
+ {
918
+ name: 'index', unroll: true,
919
+ }
920
+ ],
921
+ fn: (data, index) => {
922
+ if (index <= 0) {
923
+ return ArgumentError();
924
+ }
925
+ // const flat = Utils.FlattenCellValues(data);
926
+ // const numeric: number[] = flat.filter((test): test is number => typeof test === 'number');
927
+ const numeric = Utils.FlattenNumbers(data);
928
+ numeric.sort((a, b) => b - a);
929
+ if (index <= numeric.length) {
930
+ return {
931
+ type: ValueType.number,
932
+ value: numeric[index - 1],
933
+ };
934
+ }
935
+ return ArgumentError();
936
+ },
937
+ },
938
+ Small: {
939
+ description: 'Returns the nth numeric value from the data, in ascending order',
940
+ arguments: [
941
+ {
942
+ name: 'values',
943
+ },
944
+ {
945
+ name: 'index', unroll: true,
946
+ }
947
+ ],
948
+ fn: (data, index) => {
949
+ if (index <= 0) {
950
+ return ArgumentError();
951
+ }
952
+ // const flat = Utils.FlattenCellValues(data);
953
+ // const numeric: number[] = flat.filter((test): test is number => typeof test === 'number');
954
+ const numeric = Utils.FlattenNumbers(data);
955
+ numeric.sort((a, b) => a - b);
956
+ if (index <= numeric.length) {
957
+ return {
958
+ type: ValueType.number,
959
+ value: numeric[index - 1],
960
+ };
961
+ }
962
+ return ArgumentError();
963
+ },
964
+ },
965
+ /**
966
+ *
967
+ */
968
+ Filter: {
969
+ description: "Filter an array using a second array.",
970
+ arguments: [
971
+ { name: 'source', description: 'Source array' },
972
+ { name: 'filter', description: 'Filter array' },
973
+ // if_empty
974
+ ],
975
+ fn: (source, filter) => {
976
+ if (typeof source === 'undefined' || typeof filter === 'undefined') {
977
+ return ArgumentError();
978
+ }
979
+ if (!Array.isArray(source)) {
980
+ source = [[source]];
981
+ }
982
+ if (!Array.isArray(filter)) {
983
+ filter = [[filter]];
984
+ }
985
+ const source_cols = source.length;
986
+ const source_rows = source[0].length;
987
+ const filter_cols = filter.length;
988
+ const filter_rows = filter[0].length;
989
+ // prefer rows
990
+ if (source_rows === filter_rows) {
991
+ const result = [];
992
+ for (let i = 0; i < source_cols; i++) {
993
+ result.push([]);
994
+ }
995
+ for (const [index, entry] of filter[0].entries()) {
996
+ // FIXME: don't allow strings? errors? (...)
997
+ if (entry) {
998
+ for (let i = 0; i < source_cols; i++) {
999
+ result[i].push(Box(source[i][index]));
1000
+ }
1001
+ }
1002
+ }
1003
+ return {
1004
+ type: ValueType.array,
1005
+ value: result,
1006
+ };
1007
+ }
1008
+ else if (source_cols === filter_cols) {
1009
+ const result = [];
1010
+ for (const [index, [entry]] of filter.entries()) {
1011
+ // FIXME: don't allow strings? errors? (...)
1012
+ if (entry) {
1013
+ result.push(source[index].map(value => Box(value)));
1014
+ }
1015
+ }
1016
+ return {
1017
+ type: ValueType.array,
1018
+ value: result,
1019
+ };
1020
+ }
1021
+ return ArgumentError();
1022
+ },
1023
+ },
1024
+ /**
1025
+ * sortby allows multiple sort indexes, but no column sorting
1026
+ */
1027
+ SortBy: {
1028
+ arguments: [
1029
+ { name: 'array', },
1030
+ { name: 'index', },
1031
+ { name: 'order', description: 'Set to -1 to sort in descending order', default: 1 }
1032
+ ],
1033
+ fn: (ref, ...args) => {
1034
+ if (!Array.isArray(ref)) {
1035
+ ref = [[ref]];
1036
+ }
1037
+ // must have at least one sort order?
1038
+ if (args.length < 1) {
1039
+ return ArgumentError();
1040
+ }
1041
+ // ensure any sort argument pairs are valid... I guess they
1042
+ // need to be the same length? what happens if not? [A: error]
1043
+ const rows = ref[0]?.length || 0;
1044
+ const orders = [];
1045
+ const values = [];
1046
+ for (let i = 0; i < args.length; i += 2) {
1047
+ const target = i / 2;
1048
+ let sort_range = args[i];
1049
+ if (!Array.isArray(sort_range)) {
1050
+ sort_range = [[sort_range]];
1051
+ }
1052
+ const check = sort_range[0]?.length || 0;
1053
+ if (check !== rows) {
1054
+ return ArgumentError();
1055
+ }
1056
+ let order = 1;
1057
+ const arg = args[i + 1];
1058
+ if (typeof arg === 'number' && arg < 0) {
1059
+ order = -1;
1060
+ }
1061
+ orders[target] = order;
1062
+ values[target] = sort_range[0]; // (sort_range[0]).slice(0);
1063
+ }
1064
+ const mapped = ref[0]?.map((value, index) => (index));
1065
+ mapped.sort((a, b) => {
1066
+ for (let i = 0; i < orders.length; i++) {
1067
+ const order = orders[i];
1068
+ const value_set = values[i];
1069
+ const result = SortHelper(order, value_set[a], value_set[b]);
1070
+ if (result) {
1071
+ return result;
1072
+ }
1073
+ }
1074
+ return 0;
1075
+ });
1076
+ // output is same shape
1077
+ const columns = ref.length;
1078
+ const result = [];
1079
+ for (let c = 0; c < columns; c++) {
1080
+ const column = [];
1081
+ for (const index of mapped) {
1082
+ column.push(Box(ref[c][index]));
1083
+ }
1084
+ result.push(column);
1085
+ }
1086
+ return { type: ValueType.array, value: result };
1087
+ },
1088
+ },
1089
+ /**
1090
+ * sort arguments, but ensure we return empty strings to
1091
+ * fill up the result array
1092
+ *
1093
+ * FIXME: instead of boxing all the values, why not pass them in boxed?
1094
+ * was this function just written at the wrong time?
1095
+ *
1096
+ * UPDATE: rewriting to match Excel args
1097
+ *
1098
+ */
1099
+ Sort: {
1100
+ arguments: [
1101
+ { name: 'array', },
1102
+ { name: 'index', },
1103
+ { name: 'order', description: 'Set to -1 to sort in descending order', default: 1 }
1104
+ ],
1105
+ fn: (ref, index = 1, order = 1) => {
1106
+ if (!Array.isArray(ref)) {
1107
+ ref = [[ref]];
1108
+ }
1109
+ // FIXME: transpose for column sort
1110
+ const sort_column = ref[index - 1];
1111
+ if (!sort_column) {
1112
+ return ArgumentError();
1113
+ }
1114
+ // clean (and be lenient)
1115
+ if (order < 0) {
1116
+ order = -1;
1117
+ }
1118
+ else {
1119
+ order = 1;
1120
+ }
1121
+ const mapped = sort_column.map((value, index) => ({ value, index }));
1122
+ mapped.sort((a, b) => SortHelper(order, a.value, b.value));
1123
+ // output is same shape
1124
+ const columns = ref.length;
1125
+ const result = [];
1126
+ for (let c = 0; c < columns; c++) {
1127
+ const column = [];
1128
+ for (const { index } of mapped) {
1129
+ column.push(Box(ref[c][index]));
1130
+ }
1131
+ result.push(column);
1132
+ }
1133
+ return { type: ValueType.array, value: result };
1134
+ },
1135
+ },
1136
+ Transpose: {
1137
+ description: 'Returns transpose of input matrix',
1138
+ arguments: [{ name: 'matrix', boxed: true }],
1139
+ fn: (mat) => {
1140
+ if (mat.type === ValueType.array) {
1141
+ return {
1142
+ type: ValueType.array,
1143
+ value: Utils.Transpose2(mat.value),
1144
+ };
1145
+ }
1146
+ /*
1147
+ if (Array.isArray(mat)) {
1148
+ return Utils.Transpose2(mat);
1149
+ }
1150
+ */
1151
+ return mat;
1152
+ }
1153
+ },
1154
+ Max: {
1155
+ fn: (...args) => {
1156
+ return {
1157
+ type: ValueType.number,
1158
+ // value: Math.max.apply(0, Utils.FlattenCellValues(args).filter((x): x is number => typeof x === 'number')),
1159
+ value: Math.max.apply(0, Utils.FlattenNumbers(args)),
1160
+ };
1161
+ },
1162
+ },
1163
+ Min: {
1164
+ fn: (...args) => {
1165
+ return {
1166
+ type: ValueType.number,
1167
+ // value: Math.min.apply(0, Utils.FlattenCellValues(args).filter((x): x is number => typeof x === 'number')),
1168
+ value: Math.min.apply(0, Utils.FlattenNumbers(args)),
1169
+ };
1170
+ },
1171
+ },
1172
+ /*
1173
+ MMult: {
1174
+ description: 'Multiplies two matrices',
1175
+ arguments: [{ name: 'Matrix 1'}, { name: 'Matrix 2'}],
1176
+ fn: (a, b) => {
1177
+ if (!a || !b) return ArgumentError;
1178
+
1179
+ const a_cols = a.length || 0;
1180
+ const a_rows = a[0]?.length || 0;
1181
+
1182
+ const b_cols = b.length || 0;
1183
+ const b_rows = b[0]?.length || 0;
1184
+
1185
+ if (!a_rows || !b_rows || !a_cols || !b_cols
1186
+ || a_rows !== b_cols || a_cols !== b_rows) return ValueError;
1187
+
1188
+ const result: number[][] = [];
1189
+
1190
+ // slightly confusing because we're column-major
1191
+
1192
+ for (let c = 0; c < b_cols; c++) {
1193
+ result[c] = [];
1194
+ for (let r = 0; r < a_rows; r++) {
1195
+ result[c][r] = 0;
1196
+ for (let x = 0; x < a_cols; x++) {
1197
+ result[c][r] += a[x][r] * b[c][x];
1198
+ }
1199
+ }
1200
+ }
1201
+ return result;
1202
+
1203
+ }
1204
+ },
1205
+ */
1206
+ SumProduct: {
1207
+ description: 'Returns the sum of pairwise products of two or more ranges',
1208
+ fn: (...args) => {
1209
+ const flattened = args.map(arg => Utils.FlattenCellValues(arg));
1210
+ const len = Math.max.apply(0, flattened.map(x => x.length));
1211
+ let sum = 0;
1212
+ for (let i = 0; i < len; i++) {
1213
+ sum += flattened.reduce((a, arg) => {
1214
+ let ai = arg[i];
1215
+ if (ai === true) {
1216
+ ai = 1;
1217
+ }
1218
+ return (typeof ai === 'number') ? a * ai : 0;
1219
+ }, 1);
1220
+ }
1221
+ return { type: ValueType.number, value: sum };
1222
+ },
1223
+ },
1224
+ /**
1225
+ *
1226
+ * match type:
1227
+ *
1228
+ * 1: largest value <= target value; assumes table is in ascending order.
1229
+ * 0: exact match only.
1230
+ * -1: smallest value >= target value; assumes table is in descending order.
1231
+ *
1232
+ * NOTE that string matches can accept wildcards in Excel, not sure if we
1233
+ * necessarily want to support that... how does string matching deal with
1234
+ * inequalities?
1235
+ * /
1236
+ Match: {
1237
+ fn: (value: CellValue, table: CellValue[][], match_type: 1|0|-1 = 1) => {
1238
+
1239
+ const flat = table.reduce((a, row) => ([...a, ...row]), []);
1240
+ for (let i = 0; i < flat.length; i++) {
1241
+
1242
+ const compare = flat[i];
1243
+
1244
+ console.info("CV", compare, value);
1245
+
1246
+ // this is true regardless of match type... right?
1247
+ if (compare === value) {
1248
+ return { type: ValueType.number, value: i + 1 };
1249
+ }
1250
+
1251
+ if ((typeof compare !== 'undefined' && typeof value !== 'undefined') && (
1252
+ (match_type === 1 && compare > value) ||
1253
+ (match_type === -1 && compare < value))) {
1254
+
1255
+ if (i === 0 || i === flat.length - 1) {
1256
+ return NAError();
1257
+ }
1258
+
1259
+ return { type: ValueType.number, value: i }; // implicit -1
1260
+ }
1261
+
1262
+ }
1263
+ return NAError();
1264
+ },
1265
+ },
1266
+ */
1267
+ Row: {
1268
+ arguments: [{ name: 'reference', metadata: true }],
1269
+ fn: function (ref) {
1270
+ if (!ref) {
1271
+ if (this?.area) {
1272
+ const value = [];
1273
+ for (let c = this.area.start.column; c <= this.area.end.column; c++) {
1274
+ const col = [];
1275
+ for (let r = this.area.start.row; r <= this.area.end.row; r++) {
1276
+ col.push({
1277
+ type: ValueType.number,
1278
+ value: r + 1,
1279
+ });
1280
+ }
1281
+ value.push(col);
1282
+ }
1283
+ return {
1284
+ type: ValueType.array, value,
1285
+ };
1286
+ }
1287
+ else {
1288
+ return {
1289
+ type: ValueType.number,
1290
+ value: this ? this.address.row + 1 : -1,
1291
+ };
1292
+ }
1293
+ }
1294
+ if (ref.type === ValueType.array) {
1295
+ const arr = ref.value;
1296
+ const first = arr[0][0];
1297
+ if (UnionIsMetadata(first)) {
1298
+ return {
1299
+ type: ValueType.array,
1300
+ value: [arr[0].map((row, index) => ({
1301
+ type: ValueType.number,
1302
+ value: index + first.value.address.row + 1
1303
+ }))],
1304
+ };
1305
+ }
1306
+ }
1307
+ else if (UnionIsMetadata(ref)) {
1308
+ return {
1309
+ type: ValueType.number, value: ref.value.address.row + 1,
1310
+ };
1311
+ }
1312
+ return ArgumentError();
1313
+ },
1314
+ },
1315
+ Column: {
1316
+ arguments: [{ name: 'reference', metadata: true }],
1317
+ fn: function (ref) {
1318
+ if (!ref) {
1319
+ if (this?.area) {
1320
+ const value = [];
1321
+ for (let c = this.area.start.column; c <= this.area.end.column; c++) {
1322
+ const col = [];
1323
+ for (let r = this.area.start.row; r <= this.area.end.row; r++) {
1324
+ col.push({
1325
+ type: ValueType.number,
1326
+ value: c + 1,
1327
+ });
1328
+ }
1329
+ value.push(col);
1330
+ }
1331
+ return {
1332
+ type: ValueType.array, value,
1333
+ };
1334
+ }
1335
+ else {
1336
+ return {
1337
+ type: ValueType.number,
1338
+ value: this ? this.address.column + 1 : -1,
1339
+ };
1340
+ }
1341
+ }
1342
+ if (ref.type === ValueType.array) {
1343
+ const arr = ref.value;
1344
+ const first = arr[0][0];
1345
+ if (UnionIsMetadata(first)) {
1346
+ return {
1347
+ type: ValueType.array,
1348
+ value: arr.map((row, index) => [{
1349
+ type: ValueType.number,
1350
+ value: index + first.value.address.column + 1
1351
+ }]),
1352
+ };
1353
+ }
1354
+ }
1355
+ else if (UnionIsMetadata(ref)) {
1356
+ return {
1357
+ type: ValueType.number, value: ref.value.address.row + 1,
1358
+ };
1359
+ }
1360
+ return ArgumentError();
1361
+ },
1362
+ },
1363
+ Choose: {
1364
+ arguments: [
1365
+ { name: 'Selected index', },
1366
+ { name: 'Choice 1...', metadata: true },
1367
+ ],
1368
+ return_type: 'reference',
1369
+ description: 'Returns one of a list of choices',
1370
+ fn: (selected, ...choices) => {
1371
+ if (selected < 1 || selected > choices.length) {
1372
+ return ValueError();
1373
+ }
1374
+ const value = choices[selected - 1];
1375
+ // this should be metadata. is there a different object we
1376
+ // might run into? maybe we should refactor how metadata works
1377
+ if (UnionIsMetadata(value)) {
1378
+ return {
1379
+ type: ValueType.object,
1380
+ value: value.value.address,
1381
+ };
1382
+ }
1383
+ // check if array is metadata. if it's a literal array
1384
+ // we just want to return it.
1385
+ if (value.type === ValueType.array) {
1386
+ const arr = value.value;
1387
+ const rows = arr.length;
1388
+ const cols = arr[0].length;
1389
+ const first = arr[0][0];
1390
+ const last = arr[rows - 1][cols - 1];
1391
+ if (rows === 1 && cols === 1) {
1392
+ if (UnionIsMetadata(first)) {
1393
+ return {
1394
+ type: ValueType.object,
1395
+ value: first.value.address,
1396
+ };
1397
+ }
1398
+ }
1399
+ else {
1400
+ if (UnionIsMetadata(first) && UnionIsMetadata(last)) {
1401
+ return {
1402
+ type: ValueType.object,
1403
+ value: {
1404
+ type: 'range',
1405
+ position: 0, id: 0, label: '',
1406
+ start: first.value.address,
1407
+ end: last.value.address,
1408
+ }
1409
+ };
1410
+ }
1411
+ }
1412
+ }
1413
+ return {
1414
+ ...value, // should we deep-copy in case of an array?
1415
+ };
1416
+ },
1417
+ },
1418
+ /*
1419
+ * rewrite of xlookup to return a reference. better compatibility.
1420
+ * ---
1421
+ *
1422
+ * unsaid anywhere (that I can locate) aboud XLOOKUP is that lookup
1423
+ * array must be one-dimensional. it can be either a row or a column,
1424
+ * but one dimension must be one. that simplifies things quite a bit.
1425
+ *
1426
+ * there's a note in the docs about binary search over the data --
1427
+ * that might explain how inexact VLOOKUP works as well. seems an odd
1428
+ * choice but maybe back in the day it made sense
1429
+ */
1430
+ XLOOKUP: {
1431
+ arguments: [
1432
+ { name: 'Lookup value', },
1433
+ { name: 'Lookup array', },
1434
+ { name: 'Return array', metadata: true, },
1435
+ { name: 'Not found', boxed: true },
1436
+ { name: 'Match mode', default: 0, },
1437
+ { name: 'Search mode', default: 1, },
1438
+ ],
1439
+ return_type: 'reference',
1440
+ xlfn: true,
1441
+ fn: (lookup_value, lookup_array, return_array, not_found, match_mode = 0, search_mode = 1) => {
1442
+ ////////
1443
+ if (!return_array) {
1444
+ return ArgumentError();
1445
+ }
1446
+ // const parse_result = this.parser.Parse(reference);
1447
+ // if (parse_result.error || !parse_result.expression) {
1448
+ // return ReferenceError;
1449
+ //}
1450
+ let rng;
1451
+ let return_from_array = false;
1452
+ if (return_array.type === ValueType.array) {
1453
+ // console.info({return_array});
1454
+ const arr = return_array.value;
1455
+ const r = arr.length;
1456
+ const c = arr[0].length;
1457
+ const start = arr[0][0];
1458
+ const end = arr[r - 1][c - 1];
1459
+ if (UnionIsMetadata(start) && UnionIsMetadata(end)) {
1460
+ rng = new Area(start.value.address, end.value.address);
1461
+ }
1462
+ // we can allow a regular array here... perhaps we should
1463
+ // check the dimensions to ensure they match? TODO/FIXME
1464
+ else {
1465
+ return_from_array = true;
1466
+ }
1467
+ }
1468
+ if (!rng && !return_from_array) {
1469
+ console.info('invalid range');
1470
+ return ReferenceError();
1471
+ }
1472
+ // console.info({rng});
1473
+ /*
1474
+ if (return_array.type === ValueType.array) {
1475
+
1476
+ // subset array. this is constructed, so we can take ownership
1477
+ // and modify it, although it would be safer to copy. also, what's
1478
+ // the cost of functional vs imperative loops these days?
1479
+
1480
+ const end_row = typeof height === 'number' ? (rows + height) : undefined;
1481
+ const end_column = typeof width === 'number' ? (columns + width) : undefined;
1482
+
1483
+ const result: UnionValue = {
1484
+ type: ValueType.array,
1485
+ value: reference.value.slice(rows, end_row).map(row => row.slice(columns, end_column)),
1486
+ };
1487
+
1488
+ return result;
1489
+
1490
+ }
1491
+ */
1492
+ // FIXME: we could I suppose be more graceful about single values
1493
+ // if passed instead of arrays
1494
+ if (!Array.isArray(lookup_array)) {
1495
+ console.info("lookup is not an array");
1496
+ return ValueError();
1497
+ }
1498
+ const first = lookup_array[0];
1499
+ if (!Array.isArray(first)) {
1500
+ console.info("lookup is not a 2d array");
1501
+ return ValueError();
1502
+ }
1503
+ if (lookup_array.length !== 1 && first.length !== 1) {
1504
+ console.info("lookup array has invalid dimensions");
1505
+ return ValueError();
1506
+ }
1507
+ // FIXME: is it required that the return array be (at least) the
1508
+ // same size? we can return undefineds, but maybe we should error
1509
+ /*
1510
+ if (!Array.isArray(return_array)) {
1511
+ console.info("return array is not an array");
1512
+ return ValueError();
1513
+ }
1514
+ */
1515
+ const transpose = (lookup_array.length === 1);
1516
+ if (transpose) {
1517
+ lookup_array = Utils.TransposeArray(lookup_array);
1518
+ // return_array = Utils.TransposeArray(return_array);
1519
+ }
1520
+ // maybe reverse...
1521
+ if (search_mode < 0) {
1522
+ lookup_array.reverse();
1523
+ // return_array.reverse();
1524
+ }
1525
+ //
1526
+ // return value at index, transpose if necessary, and return
1527
+ // an array. we might prefer to return a scalar if there's only
1528
+ // one value, not sure what's the intended behavior
1529
+ //
1530
+ const ReturnIndex = (index) => {
1531
+ // FIXME: we could almost certainly merge these two paths
1532
+ if (return_from_array && return_array.type === ValueType.array) {
1533
+ // instead of a range, we're returning values from a static array
1534
+ const src_columns = return_array.value.length;
1535
+ const src_rows = return_array.value[0]?.length || 0;
1536
+ const result = [];
1537
+ let start_row = 0;
1538
+ let end_row = src_rows - 1;
1539
+ let start_column = 0;
1540
+ let end_column = src_columns - 1;
1541
+ if (transpose) {
1542
+ if (search_mode < 0) {
1543
+ index = src_rows - 1 - index; // invert FIXME: test
1544
+ }
1545
+ start_row = end_row = index;
1546
+ }
1547
+ else {
1548
+ if (search_mode < 0) {
1549
+ index = src_columns - 1 - index; // invert FIXME: test
1550
+ }
1551
+ start_column = end_column = index;
1552
+ }
1553
+ for (let c = start_column; c <= end_column; c++) {
1554
+ const column = [];
1555
+ for (let r = start_row; r <= end_row; r++) {
1556
+ column.push(return_array.value[c][r]);
1557
+ }
1558
+ result.push(column);
1559
+ }
1560
+ return {
1561
+ type: ValueType.array,
1562
+ value: result,
1563
+ };
1564
+ }
1565
+ if (!rng) {
1566
+ throw new Error('invalid range');
1567
+ }
1568
+ // console.info("transpose?", transpose, {rng}, 'shape', rng.rows, rng.columns);
1569
+ let start;
1570
+ let end;
1571
+ // I guess "transpose" in this context means "return a row from column(s)"? rename
1572
+ if (transpose) {
1573
+ if (search_mode < 0) {
1574
+ index = rng.rows - 1 - index; // invert FIXME: test
1575
+ }
1576
+ if (index >= 0 && index < rng.rows) {
1577
+ start = {
1578
+ type: 'address',
1579
+ position: 0, id: 1, label: '',
1580
+ row: rng.start.row + index,
1581
+ column: rng.start.column,
1582
+ sheet_id: rng.start.sheet_id,
1583
+ };
1584
+ end = {
1585
+ type: 'address',
1586
+ position: 0, id: 2, label: '',
1587
+ row: rng.start.row + index,
1588
+ column: rng.end.column,
1589
+ sheet_id: rng.start.sheet_id,
1590
+ };
1591
+ }
1592
+ }
1593
+ else {
1594
+ if (search_mode < 0) {
1595
+ index = rng.columns - 1 - index; // invert FIXME: test
1596
+ }
1597
+ if (index >= 0 && index < rng.columns) {
1598
+ start = {
1599
+ type: 'address',
1600
+ position: 0, id: 1, label: '',
1601
+ row: rng.start.row,
1602
+ column: rng.start.column + index,
1603
+ sheet_id: rng.start.sheet_id,
1604
+ };
1605
+ end = {
1606
+ type: 'address',
1607
+ position: 0, id: 2, label: '',
1608
+ row: rng.end.row,
1609
+ column: rng.start.column + index,
1610
+ sheet_id: rng.start.sheet_id,
1611
+ };
1612
+ }
1613
+ }
1614
+ if (start && end) {
1615
+ const expr = {
1616
+ type: 'range',
1617
+ position: 0,
1618
+ id: 0,
1619
+ label: '',
1620
+ start, end,
1621
+ };
1622
+ // console.info({expr});
1623
+ return {
1624
+ type: ValueType.object,
1625
+ value: expr,
1626
+ };
1627
+ }
1628
+ return { type: ValueType.undefined };
1629
+ };
1630
+ // if value is not a string, then we can ignore wildcards.
1631
+ // in that case convert to exact match.
1632
+ if (match_mode === 2 && typeof lookup_value !== 'string') {
1633
+ match_mode = 0;
1634
+ }
1635
+ // what does inexact matching mean in this case if the lookup
1636
+ // value is a string or boolean? (...)
1637
+ if ((match_mode === 1 || match_mode === -1) && typeof lookup_value === 'number') {
1638
+ let min_delta = 0;
1639
+ let index = -1;
1640
+ for (let i = 0; i < lookup_array.length; i++) {
1641
+ const value = lookup_array[i][0];
1642
+ if (typeof value === 'number') {
1643
+ // check for exact match first, just in case
1644
+ if (value === lookup_value) {
1645
+ return ReturnIndex(i);
1646
+ }
1647
+ const delta = Math.abs(value - lookup_value);
1648
+ if ((match_mode === 1 && value > lookup_value) || (match_mode === -1 && value < lookup_value)) {
1649
+ if (index < 0 || delta < min_delta) {
1650
+ min_delta = delta;
1651
+ index = i;
1652
+ }
1653
+ }
1654
+ }
1655
+ }
1656
+ if (index >= 0) {
1657
+ return ReturnIndex(index);
1658
+ }
1659
+ }
1660
+ switch (match_mode) {
1661
+ case 2:
1662
+ {
1663
+ // wildcard string match. we only handle strings for
1664
+ // this case (see above).
1665
+ const pattern = Utils.ParseWildcards(lookup_value?.toString() || '');
1666
+ const regex = new RegExp('^' + pattern + '$', 'i'); //.exec(lookup_value);
1667
+ for (let i = 0; i < lookup_array.length; i++) {
1668
+ const value = lookup_array[i][0];
1669
+ if (typeof value === 'string' && regex.exec(value)) {
1670
+ return ReturnIndex(i);
1671
+ }
1672
+ }
1673
+ }
1674
+ break;
1675
+ case 0:
1676
+ if (typeof lookup_value === 'string') {
1677
+ lookup_value = lookup_value.toLowerCase();
1678
+ }
1679
+ for (let i = 0; i < lookup_array.length; i++) {
1680
+ let value = lookup_array[i][0];
1681
+ if (typeof value === 'string') {
1682
+ value = value.toLowerCase();
1683
+ }
1684
+ if (value === lookup_value) {
1685
+ return ReturnIndex(i);
1686
+ }
1687
+ }
1688
+ break;
1689
+ }
1690
+ // FIXME: if we're expecting to return an array maybe we should
1691
+ // pack it up as an array? if it's not already an array? (...)
1692
+ return (not_found && not_found.type !== ValueType.undefined) ? not_found : NAError();
1693
+ },
1694
+ },
1695
+ /*
1696
+ * unsaid anywhere (that I can locate) aboud XLOOKUP is that lookup
1697
+ * array must be one-dimensional. it can be either a row or a column,
1698
+ * but one dimension must be one. that simplifies things quite a bit.
1699
+ *
1700
+ * there's a note in the docs about binary search over the data --
1701
+ * that might explain how inexact VLOOKUP works as well. seems an odd
1702
+ * choice but maybe back in the day it made sense
1703
+ * /
1704
+ XLOOKUP: {
1705
+ arguments: [
1706
+ { name: 'Lookup value', },
1707
+ { name: 'Lookup array', },
1708
+ { name: 'Return array', address: true, },
1709
+ { name: 'Not found', boxed: true },
1710
+ { name: 'Match mode', default: 0, },
1711
+ { name: 'Search mode', default: 1, },
1712
+ ],
1713
+ return_type: 'reference',
1714
+ xlfn: true,
1715
+ fn: (
1716
+ lookup_value: IntrinsicValue,
1717
+ lookup_array: IntrinsicValue[][],
1718
+ return_array: string,
1719
+ not_found?: UnionValue,
1720
+ match_mode = 0,
1721
+ search_mode = 1,
1722
+ ): UnionValue => {
1723
+
1724
+ console.info({return_array});
1725
+
1726
+
1727
+ // FIXME: we could I suppose be more graceful about single values
1728
+ // if passed instead of arrays
1729
+
1730
+ if (!Array.isArray(lookup_array)) {
1731
+ console.info("lookup is not an array");
1732
+ return ValueError();
1733
+ }
1734
+
1735
+ const first = lookup_array[0];
1736
+ if (!Array.isArray(first)) {
1737
+ console.info("lookip is not a 2d array");
1738
+ return ValueError();
1739
+ }
1740
+
1741
+ if (lookup_array.length !== 1 && first.length !== 1) {
1742
+ console.info("lookup array has invalid dimensions");
1743
+ return ValueError();
1744
+ }
1745
+
1746
+ // FIXME: is it required that the return array be (at least) the
1747
+ // same size? we can return undefineds, but maybe we should error
1748
+
1749
+ if (!Array.isArray(return_array)) {
1750
+ console.info("return array is not an array");
1751
+ return ValueError();
1752
+ }
1753
+
1754
+ const transpose = (lookup_array.length === 1);
1755
+ if (transpose) {
1756
+ lookup_array = Utils.TransposeArray(lookup_array);
1757
+ return_array = Utils.TransposeArray(return_array);
1758
+ }
1759
+
1760
+ // maybe reverse...
1761
+
1762
+ if (search_mode < 0) {
1763
+ lookup_array.reverse();
1764
+ return_array.reverse();
1765
+ }
1766
+
1767
+ //
1768
+ // return value at index, transpose if necessary, and return
1769
+ // an array. we might prefer to return a scalar if there's only
1770
+ // one value, not sure what's the intended behavior
1771
+ //
1772
+ const ReturnIndex = (index: number): UnionValue => {
1773
+
1774
+ const values = return_array[index];
1775
+
1776
+ if (!values) {
1777
+ return { type: ValueType.undefined };
1778
+ }
1779
+
1780
+ if (!Array.isArray(values)) {
1781
+ return Box(values);
1782
+ }
1783
+
1784
+ let boxes = [values.map(value => Box(value))];
1785
+
1786
+ if (transpose) {
1787
+ boxes = Utils.TransposeArray(boxes);
1788
+ }
1789
+
1790
+ return {
1791
+ type: ValueType.array,
1792
+ value: boxes,
1793
+ }
1794
+
1795
+ };
1796
+
1797
+ // if value is not a string, then we can ignore wildcards.
1798
+ // in that case convert to exact match.
1799
+
1800
+ if (match_mode === 2 && typeof lookup_value !== 'string') {
1801
+ match_mode = 0;
1802
+ }
1803
+
1804
+ // what does inexact matching mean in this case if the lookup
1805
+ // value is a string or boolean? (...)
1806
+
1807
+ if ((match_mode === 1 || match_mode === -1) && typeof lookup_value === 'number') {
1808
+
1809
+ let min_delta = 0;
1810
+ let index = -1;
1811
+
1812
+ for (let i = 0; i < lookup_array.length; i++) {
1813
+ const value = lookup_array[i][0];
1814
+
1815
+
1816
+ if (typeof value === 'number') {
1817
+
1818
+ // check for exact match first, just in case
1819
+ if (value === lookup_value) {
1820
+ return ReturnIndex(i);
1821
+ }
1822
+
1823
+ const delta = Math.abs(value - lookup_value);
1824
+
1825
+ if ((match_mode === 1 && value > lookup_value) || (match_mode === -1 && value < lookup_value)){
1826
+ if (index < 0 || delta < min_delta) {
1827
+ min_delta = delta;
1828
+ index = i;
1829
+ }
1830
+ }
1831
+
1832
+ }
1833
+ }
1834
+
1835
+ if (index >= 0) {
1836
+ return ReturnIndex(index);
1837
+ }
1838
+
1839
+ }
1840
+
1841
+ switch (match_mode) {
1842
+
1843
+ case 2:
1844
+ {
1845
+ // wildcard string match. we only handle strings for
1846
+ // this case (see above).
1847
+
1848
+ const pattern = Utils.ParseWildcards(lookup_value?.toString() || '');
1849
+ const regex = new RegExp('^' + pattern + '$', 'i'); //.exec(lookup_value);
1850
+
1851
+ for (let i = 0; i < lookup_array.length; i++) {
1852
+ const value = lookup_array[i][0];
1853
+ if (typeof value === 'string' && regex.exec(value)) {
1854
+ return ReturnIndex(i);
1855
+ }
1856
+ }
1857
+
1858
+ }
1859
+ break;
1860
+
1861
+ case 0:
1862
+ if (typeof lookup_value === 'string') {
1863
+ lookup_value = lookup_value.toLowerCase();
1864
+ }
1865
+ for (let i = 0; i < lookup_array.length; i++) {
1866
+ let value = lookup_array[i][0];
1867
+
1868
+ if (typeof value === 'string') {
1869
+ value = value.toLowerCase();
1870
+ }
1871
+ if (value === lookup_value) {
1872
+ return ReturnIndex(i);
1873
+ }
1874
+ }
1875
+
1876
+ break;
1877
+ }
1878
+
1879
+
1880
+ // FIXME: if we're expecting to return an array maybe we should
1881
+ // pack it up as an array? if it's not already an array? (...)
1882
+
1883
+ return (not_found && not_found.type !== ValueType.undefined) ? not_found : NAError();
1884
+
1885
+ },
1886
+ },
1887
+ */
1888
+ /**
1889
+ * copied from HLOOKUP, fix that one first
1890
+ */
1891
+ HLookup: {
1892
+ arguments: [...zlookup_arguments],
1893
+ fn: (value, table, col, inexact = true) => {
1894
+ return ZLookup(value, table, col, inexact, true);
1895
+ },
1896
+ },
1897
+ /**
1898
+ * FIXME: does not implement inexact matching (what's the algo for
1899
+ * that, anyway? nearest? price is right style? what about ties?)
1900
+ */
1901
+ VLookup: {
1902
+ arguments: [...zlookup_arguments],
1903
+ fn: (value, table, col, inexact = true) => {
1904
+ return ZLookup(value, table, col, inexact, false);
1905
+ },
1906
+ },
1907
+ Product: {
1908
+ arguments: [{
1909
+ boxed: true,
1910
+ name: 'values or ranges',
1911
+ repeat: true,
1912
+ }],
1913
+ fn: (...args) => {
1914
+ let product = { real: 1, imaginary: 0 };
1915
+ args = Utils.FlattenBoxed(args);
1916
+ for (const arg of args) {
1917
+ if (arg.type === ValueType.complex) {
1918
+ product = ComplexMultply(product, arg.value);
1919
+ }
1920
+ else if (arg.type === ValueType.number) {
1921
+ product.real *= arg.value;
1922
+ product.imaginary *= arg.value;
1923
+ }
1924
+ }
1925
+ return ComplexOrReal(product);
1926
+ /*
1927
+ return { type: ValueType.number, value: Utils.Flatten(args).reduce((a: number, b: any) => {
1928
+ if (typeof b === 'undefined') return a;
1929
+ return a * Number(b);
1930
+ }, 1) };
1931
+ */
1932
+ },
1933
+ },
1934
+ Log: {
1935
+ arguments: [{
1936
+ name: 'number',
1937
+ unroll: true
1938
+ }, {
1939
+ name: 'base',
1940
+ unroll: true
1941
+ }],
1942
+ /** default is base 10; allow specific base */
1943
+ fn: (a, base = 10) => {
1944
+ return { type: ValueType.number, value: Math.log(a) / Math.log(base) };
1945
+ },
1946
+ },
1947
+ Log10: {
1948
+ arguments: [{
1949
+ name: 'number',
1950
+ unroll: true,
1951
+ }],
1952
+ fn: (a) => {
1953
+ return { type: ValueType.number, value: Math.log(a) / Math.log(10) };
1954
+ },
1955
+ },
1956
+ Ln: {
1957
+ arguments: [{
1958
+ name: 'number',
1959
+ unroll: true,
1960
+ }],
1961
+ fn: (a) => {
1962
+ return { type: ValueType.number, value: Math.log(a) };
1963
+ },
1964
+ },
1965
+ 'Ceiling.Math': {
1966
+ arguments: [{
1967
+ name: 'number',
1968
+ unroll: true
1969
+ }, {
1970
+ name: 'significance',
1971
+ unroll: true
1972
+ }, {
1973
+ name: 'away from zero',
1974
+ unroll: true,
1975
+ }],
1976
+ xlfn: true,
1977
+ fn: (a, significance = 1, mode) => {
1978
+ let value = 0;
1979
+ if (mode && a < 0) {
1980
+ value = -Math.ceil(-a / significance) * significance;
1981
+ }
1982
+ else {
1983
+ value = Math.ceil(a / significance) * significance;
1984
+ }
1985
+ return {
1986
+ type: ValueType.number,
1987
+ value,
1988
+ };
1989
+ },
1990
+ },
1991
+ 'Floor.Math': {
1992
+ arguments: [{
1993
+ name: 'number',
1994
+ unroll: true
1995
+ }, {
1996
+ name: 'significance',
1997
+ unroll: true
1998
+ }, {
1999
+ name: 'away from zero',
2000
+ unroll: true,
2001
+ }],
2002
+ fn: (a, significance = 1, mode) => {
2003
+ let value = 0;
2004
+ if (mode && a < 0) {
2005
+ value = -Math.floor(-a / significance) * significance;
2006
+ }
2007
+ else {
2008
+ value = Math.floor(a / significance) * significance;
2009
+ }
2010
+ return {
2011
+ type: ValueType.number,
2012
+ value,
2013
+ };
2014
+ },
2015
+ },
2016
+ Floor: {
2017
+ arguments: [{
2018
+ name: 'number',
2019
+ unroll: true
2020
+ }, {
2021
+ name: 'significance',
2022
+ unroll: true
2023
+ }],
2024
+ fn: (a, significance = 1) => {
2025
+ return {
2026
+ type: ValueType.number,
2027
+ value: Math.floor(a / significance) * significance,
2028
+ };
2029
+ },
2030
+ },
2031
+ Ceiling: {
2032
+ arguments: [{
2033
+ name: 'number',
2034
+ unroll: true
2035
+ }, {
2036
+ name: 'significance',
2037
+ unroll: true
2038
+ }],
2039
+ fn: (a, significance = 1) => {
2040
+ return {
2041
+ type: ValueType.number,
2042
+ value: Math.ceil(a / significance) * significance,
2043
+ };
2044
+ },
2045
+ },
2046
+ Round: {
2047
+ arguments: [{ unroll: true }, { unroll: true }], // FIXME: lazy
2048
+ fn: (a, digits = 0) => {
2049
+ const m = Math.pow(10, digits);
2050
+ return {
2051
+ type: ValueType.number,
2052
+ value: Math.round(m * a) / m,
2053
+ };
2054
+ },
2055
+ },
2056
+ RoundDown: {
2057
+ arguments: [{ unroll: true }, { unroll: true }], // FIXME: lazy
2058
+ fn: (a, digits = 0) => {
2059
+ const m = Math.pow(10, digits);
2060
+ const positive = a >= 0;
2061
+ return {
2062
+ type: ValueType.number,
2063
+ value: positive ? Math.floor(m * a) / m : Math.ceil(m * a) / m,
2064
+ };
2065
+ },
2066
+ },
2067
+ RoundUp: {
2068
+ arguments: [{ unroll: true }, { unroll: true }], // FIXME: lazy
2069
+ fn: (a, digits = 0) => {
2070
+ const m = Math.pow(10, digits);
2071
+ const positive = a >= 0;
2072
+ return {
2073
+ type: ValueType.number,
2074
+ value: positive ? Math.ceil(m * a) / m : Math.floor(m * a) / m,
2075
+ };
2076
+ },
2077
+ },
2078
+ /*
2079
+
2080
+ Round: {
2081
+ description: 'Round to a specified number of digits',
2082
+
2083
+ / ** round with variable digits * /
2084
+ fn: (value: number, digits = 0) => {
2085
+ const m = Math.pow(10, digits);
2086
+ return Math.round(m * value) / m;
2087
+ },
2088
+ },
2089
+
2090
+ RoundDown: {
2091
+ / ** round down with variable digits * /
2092
+ fn: (value: number, digits = 0) => {
2093
+ digits = Math.max(0, digits);
2094
+ const m = Math.pow(10, digits);
2095
+ return Math.floor(m * value) / m;
2096
+ },
2097
+ },
2098
+
2099
+
2100
+ */
2101
+ Reverse: {
2102
+ arguments: [
2103
+ { boxed: true },
2104
+ ],
2105
+ fn: (a) => {
2106
+ /*
2107
+
2108
+ what is this? this would do anything useful
2109
+ ...oh I see, it reverses along one axis or the other
2110
+
2111
+ if ( Array.isArray(a)) {
2112
+ if (a.length === 1 ) return [a[0].reverse()];
2113
+ return a.reverse();
2114
+ }
2115
+ */
2116
+ if (a.type === ValueType.array) {
2117
+ if (a.value.length === 1) {
2118
+ a.value[0].reverse();
2119
+ }
2120
+ else {
2121
+ a.value.reverse();
2122
+ }
2123
+ return a;
2124
+ }
2125
+ return {
2126
+ type: ValueType.string,
2127
+ value: (a.value ?? '').toString().split('').reverse().join(''),
2128
+ };
2129
+ },
2130
+ },
2131
+ /**
2132
+ * exp was not broken out, but added so we can support complex numbers.
2133
+ */
2134
+ Exp: {
2135
+ arguments: [
2136
+ { boxed: true, unroll: true },
2137
+ ],
2138
+ fn: (x) => {
2139
+ if (x.type === ValueType.complex) {
2140
+ const value = ComplexExp(x.value);
2141
+ return ComplexOrReal(value);
2142
+ }
2143
+ if (x.type !== ValueType.number) {
2144
+ return ValueError();
2145
+ }
2146
+ return { type: ValueType.number, value: Math.exp(x.value) };
2147
+ },
2148
+ },
2149
+ /**
2150
+ * abs was already broken out so we could support array application,
2151
+ * then updated to support complex numbers.
2152
+ */
2153
+ Abs: {
2154
+ arguments: [
2155
+ { boxed: true, unroll: true },
2156
+ ],
2157
+ fn: (a) => {
2158
+ if (a.type === ValueType.complex) {
2159
+ return {
2160
+ type: ValueType.number,
2161
+ value: Math.sqrt(a.value.real * a.value.real + a.value.imaginary * a.value.imaginary),
2162
+ };
2163
+ }
2164
+ if (a.type !== ValueType.number) {
2165
+ return ValueError();
2166
+ }
2167
+ return { type: ValueType.number, value: Math.abs(a.value || 0) };
2168
+ },
2169
+ },
2170
+ Simplify: {
2171
+ arguments: [
2172
+ { name: 'value', unroll: true, },
2173
+ { name: 'significant digits', unroll: true, },
2174
+ ],
2175
+ fn: (value, significant_digits = 2) => {
2176
+ significant_digits = significant_digits || 2;
2177
+ if (value === 0) {
2178
+ return { type: ValueType.number, value };
2179
+ }
2180
+ const negative = value < 0 ? -1 : 1;
2181
+ value *= negative;
2182
+ const x = Math.pow(10, Math.floor(Math.log10(value)) + 1 - significant_digits);
2183
+ return {
2184
+ type: ValueType.number,
2185
+ value: Math.round(value / x) * x * negative
2186
+ };
2187
+ },
2188
+ },
2189
+ Sqrt: {
2190
+ description: 'Returns the square root of the argument',
2191
+ arguments: [
2192
+ { boxed: true, unroll: true },
2193
+ ],
2194
+ fn: (ref) => {
2195
+ // little bit torn on this. what should sqrt(-1) return? a complex
2196
+ // number, or NaN? or should we control that with a flag?
2197
+ // UPDATE: now optional, see AltFunctionLibrary
2198
+ if (ref.type === ValueType.complex) {
2199
+ const value = ComplexPower(ref.value, { real: 0.5, imaginary: 0 });
2200
+ return ComplexOrReal(value);
2201
+ }
2202
+ else if (ref.type === ValueType.undefined || !ref.value) {
2203
+ return {
2204
+ type: ValueType.number, value: 0,
2205
+ };
2206
+ }
2207
+ /*
2208
+ else if (ref.type === ValueType.number && ref.value < 0) {
2209
+ const value = ComplexPower({real: ref.value, imaginary: 0}, {real: 0.5, imaginary: 0});
2210
+ return {
2211
+ type: ValueType.complex,
2212
+ value,
2213
+ }
2214
+ }
2215
+ */
2216
+ else if (ref.type === ValueType.number) {
2217
+ return {
2218
+ type: ValueType.number, value: Math.sqrt(ref.value),
2219
+ };
2220
+ }
2221
+ return ValueError();
2222
+ /*
2223
+ else {
2224
+ const value = Math.sqrt(ref.value);
2225
+ if (isNaN(value)) {
2226
+ return ValueError();
2227
+ }
2228
+ return { type: ValueType.number, value };
2229
+ }
2230
+ */
2231
+ },
2232
+ },
2233
+ HexToDec: {
2234
+ arguments: [{ description: 'hexadecimal string', unroll: true }],
2235
+ fn: (hex) => {
2236
+ return { type: ValueType.number, value: parseInt(hex, 16) };
2237
+ },
2238
+ },
2239
+ DecToHex: {
2240
+ arguments: [{ description: 'number', unroll: true }],
2241
+ fn: (num) => {
2242
+ return { type: ValueType.string, value: num.toString(16) };
2243
+ },
2244
+ },
2245
+ Checkbox: {
2246
+ arguments: [
2247
+ { name: 'checked' },
2248
+ ],
2249
+ click: ClickCheckbox,
2250
+ render: RenderCheckbox,
2251
+ fn: (checked) => {
2252
+ return { value: !!checked, type: ValueType.boolean, };
2253
+ },
2254
+ },
2255
+ 'Sparkline.Column': {
2256
+ arguments: [
2257
+ { name: 'data' },
2258
+ { name: 'color' },
2259
+ { name: 'negative color' }
2260
+ ],
2261
+ render: (options) => {
2262
+ Sparkline.RenderColumn(options.width, options.height, options.context, options.cell, options.style);
2263
+ return { handled: true }; // painted
2264
+ },
2265
+ fn: (...args) => {
2266
+ return { type: ValueType.object, value: args, key: 'sparkline-data', source: 'sparkline.column' };
2267
+ },
2268
+ },
2269
+ 'Sparkline.Line': {
2270
+ arguments: [
2271
+ { name: 'data' },
2272
+ { name: 'color' },
2273
+ { name: 'line width' },
2274
+ ],
2275
+ render: (options) => {
2276
+ Sparkline.RenderLine(options.width, options.height, options.context, options.cell, options.style);
2277
+ return { handled: true }; // painted
2278
+ },
2279
+ fn: (...args) => {
2280
+ return { type: ValueType.object, value: args, key: 'sparkline-data', source: 'sparkline.line' };
2281
+ },
2282
+ },
2283
+ UniqueValues: {
2284
+ arguments: [
2285
+ { name: 'range', boxed: true },
2286
+ ],
2287
+ visibility: 'internal',
2288
+ fn: (area) => {
2289
+ if (area.type === ValueType.array) {
2290
+ // const cols = area.value.length;
2291
+ // const rows = area.value[0]?.length;
2292
+ // how is uniqueness defined in this context? (...)
2293
+ const Normalize = (cell) => {
2294
+ if (!cell) {
2295
+ return '';
2296
+ }
2297
+ else
2298
+ switch (cell.type) {
2299
+ case ValueType.string:
2300
+ case ValueType.number:
2301
+ case ValueType.boolean:
2302
+ return cell.value;
2303
+ case ValueType.undefined:
2304
+ return '';
2305
+ default:
2306
+ console.info("check", cell, cell.value);
2307
+ return cell.value?.toString() || '';
2308
+ }
2309
+ };
2310
+ const set = new Set();
2311
+ const duplicates = new Set();
2312
+ for (const column of area.value) {
2313
+ for (const cell of column) {
2314
+ const normalized = Normalize(cell);
2315
+ if (set.has(normalized)) {
2316
+ duplicates.add(normalized);
2317
+ }
2318
+ else {
2319
+ set.add(normalized);
2320
+ }
2321
+ }
2322
+ }
2323
+ const result = [];
2324
+ for (const column of area.value) {
2325
+ const column_result = [];
2326
+ for (const cell of column) {
2327
+ const value = Normalize(cell);
2328
+ column_result.push({
2329
+ type: ValueType.boolean,
2330
+ value: !duplicates.has(value),
2331
+ });
2332
+ }
2333
+ result.push(column_result);
2334
+ }
2335
+ return {
2336
+ type: ValueType.array,
2337
+ value: result,
2338
+ };
2339
+ }
2340
+ // if it's not an array, by definition it's unique
2341
+ return {
2342
+ type: ValueType.boolean,
2343
+ value: true,
2344
+ };
2345
+ },
2346
+ },
2347
+ Between: {
2348
+ arguments: [
2349
+ { name: 'target', boxed: true, unroll: true },
2350
+ { name: 'min' },
2351
+ { name: 'max' },
2352
+ ],
2353
+ visibility: 'internal',
2354
+ fn: (target, min = 0, max = 1) => {
2355
+ return {
2356
+ type: ValueType.boolean,
2357
+ value: (target.type === ValueType.number) && (target.value >= min && target.value <= max),
2358
+ };
2359
+ }
2360
+ },
2361
+ Gradient: {
2362
+ arguments: [
2363
+ { name: 'range', boxed: true },
2364
+ { name: 'min', },
2365
+ { name: 'max', },
2366
+ /*
2367
+ * data bars use gradients but have to report zero. also we explicitly
2368
+ * set 0 as min/max for data bars, although you can override
2369
+ */
2370
+ { name: 'parameters' },
2371
+ ],
2372
+ visibility: 'internal',
2373
+ fn: (area, static_min, static_max, parameters) => {
2374
+ const tmp = Utils.FlattenBoxed([area]);
2375
+ // let sum = 0;
2376
+ let count = 0;
2377
+ let min = 0;
2378
+ let max = 0;
2379
+ for (const ref of tmp) {
2380
+ if (ref.type === ValueType.error) {
2381
+ return ref;
2382
+ }
2383
+ if (ref.type === ValueType.number) {
2384
+ if (count === 0 && !parameters) { // leave 0 min, max for data bars
2385
+ min = ref.value;
2386
+ max = ref.value;
2387
+ }
2388
+ else {
2389
+ min = Math.min(min, ref.value);
2390
+ max = Math.max(max, ref.value);
2391
+ }
2392
+ count++;
2393
+ }
2394
+ }
2395
+ /*
2396
+ if (parameters) {
2397
+ if (min > 0) { min = 0; }
2398
+ if (max < 0) { max = 0; }
2399
+ }
2400
+ */
2401
+ if (typeof static_max === 'number') {
2402
+ max = static_max;
2403
+ }
2404
+ if (typeof static_min === 'number') {
2405
+ min = static_min;
2406
+ }
2407
+ const range = max - min;
2408
+ let rows = 1;
2409
+ let columns = 1;
2410
+ if (area.type === ValueType.array) {
2411
+ rows = area.value.length;
2412
+ columns = area.value[0]?.length || 0;
2413
+ const result = [];
2414
+ for (let r = 0; r < rows; r++) {
2415
+ const row = [];
2416
+ for (let c = 0; c < columns; c++) {
2417
+ const src = area.value[r][c];
2418
+ if (src.type === ValueType.number) {
2419
+ let calc = 0;
2420
+ // special case: max === min. this can be used to do binary
2421
+ // coloring over a set of data (ignoring the pivot).
2422
+ // FIXME: use a separate loop?
2423
+ if (max === min) {
2424
+ if (src.value > max) {
2425
+ calc = 1;
2426
+ }
2427
+ else if (src.value < max) {
2428
+ calc = 0;
2429
+ }
2430
+ else {
2431
+ calc = 0.5;
2432
+ }
2433
+ }
2434
+ else if (range > 0) {
2435
+ calc = (src.value - min) / range;
2436
+ }
2437
+ if (parameters) {
2438
+ row.push({ type: ValueType.array, value: [[
2439
+ { type: ValueType.number, value: calc },
2440
+ { type: ValueType.number, value: (0 - min) / range }, // zero
2441
+ ]] });
2442
+ }
2443
+ else {
2444
+ row.push({ type: ValueType.number, value: calc });
2445
+ }
2446
+ }
2447
+ else {
2448
+ row.push({ type: ValueType.undefined });
2449
+ }
2450
+ }
2451
+ result.push(row);
2452
+ }
2453
+ return { type: ValueType.array, value: result };
2454
+ }
2455
+ else {
2456
+ return ArgumentError();
2457
+ }
2458
+ },
2459
+ },
2460
+ ATan2: {
2461
+ arguments: [
2462
+ { name: 'y', boxed: true, unroll: true },
2463
+ { name: 'x', boxed: true, unroll: true },
2464
+ ],
2465
+ fn: (y, x) => {
2466
+ if (y.type === ValueType.number && x.type === ValueType.number) {
2467
+ return { type: ValueType.number, value: Math.atan2(y.value, x.value) };
2468
+ }
2469
+ if (y.type === ValueType.number) {
2470
+ y = { type: ValueType.complex, value: { real: y.value, imaginary: 0 } };
2471
+ }
2472
+ if (x.type === ValueType.number) {
2473
+ x = { type: ValueType.complex, value: { real: x.value, imaginary: 0 } };
2474
+ }
2475
+ if (y.type === ValueType.complex && x.type === ValueType.complex) {
2476
+ const value = ComplexMath.ATan2(y.value, x.value);
2477
+ // should we have an error for infinities? not sure. not sure
2478
+ // why this hasn't come up before...
2479
+ if (value === false) {
2480
+ return ArgumentError();
2481
+ }
2482
+ return {
2483
+ type: ValueType.complex, value,
2484
+ };
2485
+ }
2486
+ return ArgumentError();
2487
+ },
2488
+ },
2489
+ Sin: TrigFunction(Math.sin, ComplexMath.Sin),
2490
+ SinH: TrigFunction(Math.sinh, ComplexMath.SinH),
2491
+ ASin: TrigFunction(Math.asin, ComplexMath.ASin),
2492
+ Cos: TrigFunction(Math.cos, ComplexMath.Cos),
2493
+ CosH: TrigFunction(Math.cosh, ComplexMath.CosH),
2494
+ ACos: TrigFunction(Math.acos, ComplexMath.ACos),
2495
+ Tan: TrigFunction(Math.tan, ComplexMath.Tan),
2496
+ TanH: TrigFunction(Math.tanh, ComplexMath.TanH),
2497
+ ATan: TrigFunction(Math.atan, ComplexMath.ATan),
2498
+ E: { fn: () => { return { type: ValueType.number, value: Math.E }; } },
2499
+ PI: { fn: () => { return { type: ValueType.number, value: Math.PI }; } },
2500
+ SQRT2: { fn: () => { return { type: ValueType.number, value: Math.SQRT2 }; } },
2501
+ SQRT1_2: { fn: () => { return { type: ValueType.number, value: Math.SQRT1_2 }; } },
2502
+ Sequence: {
2503
+ arguments: [
2504
+ { name: 'rows', boxed: true },
2505
+ { name: 'columns', default: 1, boxed: true },
2506
+ { name: 'start', default: 1, boxed: true },
2507
+ { name: 'step', default: 1, boxed: true }
2508
+ ],
2509
+ fn: (rows, columns, start, step) => {
2510
+ const rx = NumberArgument(rows, 1);
2511
+ const cx = NumberArgument(columns, 1);
2512
+ const step_ = NumberArgument(step, 1);
2513
+ const start_ = NumberArgument(start, 1);
2514
+ if (rx === false || cx === false || step_ === false || start_ === false) {
2515
+ return ArgumentError();
2516
+ }
2517
+ const value = [];
2518
+ for (let c = 0; c < cx; c++) {
2519
+ const col = [];
2520
+ for (let r = 0; r < rx; r++) {
2521
+ col.push({ type: ValueType.number, value: start_ + r * step_ * cx + c * step_ });
2522
+ }
2523
+ value.push(col);
2524
+ }
2525
+ return { type: ValueType.array, value };
2526
+ },
2527
+ },
2528
+ };
2529
+ // alias
2530
+ // add functions from Math (intrinsic), unless the name overlaps
2531
+ // with something already in there
2532
+ // we need to construct a separate map to match icase (this is now
2533
+ // even more useful since we have a separate section for aliases)
2534
+ const name_map = {};
2535
+ for (const key of Object.keys(BaseFunctionLibrary)) {
2536
+ name_map[key.toLowerCase()] = key;
2537
+ }
2538
+ // block these names from auto-import from Math
2539
+ const block_list = [
2540
+ 'ceil',
2541
+ 'pow',
2542
+ 'ln10',
2543
+ 'ln2',
2544
+ 'log10',
2545
+ 'log10e',
2546
+ 'log1p',
2547
+ 'log2',
2548
+ 'log2e',
2549
+ 'random',
2550
+ 'imul',
2551
+ 'clz32',
2552
+ 'fround',
2553
+ 'f16round',
2554
+ ];
2555
+ const block_map = {};
2556
+ for (const entry of block_list) {
2557
+ block_map[entry.toLowerCase()] = entry;
2558
+ }
2559
+ for (const name of Object.getOwnPropertyNames(Math)) {
2560
+ // check if it exists (we have already registered something
2561
+ // with the same name) -- don't override existing
2562
+ const lc = name.toLowerCase();
2563
+ if (name_map[lc]) {
2564
+ continue;
2565
+ }
2566
+ // also explicitly block some names we don't want to include (pow vs. power, etc)
2567
+ if (block_map[lc]) {
2568
+ continue;
2569
+ }
2570
+ const descriptor = Object.getOwnPropertyDescriptor(Math, name);
2571
+ if (!descriptor) {
2572
+ continue;
2573
+ }
2574
+ const value = descriptor.value;
2575
+ const type = typeof (value);
2576
+ switch (type) {
2577
+ case 'number':
2578
+ // console.info("MATH CONSTANT", name);
2579
+ BaseFunctionLibrary[name] = {
2580
+ fn: () => {
2581
+ return { type: ValueType.number, value };
2582
+ },
2583
+ category: ['Math Functions'],
2584
+ };
2585
+ break;
2586
+ case 'function':
2587
+ // console.info("MATH FUNC", name);
2588
+ BaseFunctionLibrary[name] = {
2589
+ // description: 'Math function',
2590
+ fn: (...args) => {
2591
+ return Box(value(...args));
2592
+ },
2593
+ category: ['Math Functions'],
2594
+ };
2595
+ break;
2596
+ default:
2597
+ console.info('unexpected type:', type, name);
2598
+ break;
2599
+ }
2600
+ }
2601
+ // IE11: patch log10 function // FIXME: is this necessary anymore?
2602
+ if (!Math.log10) {
2603
+ Math.log10 = (a) => Math.log(a) / Math.log(10);
2604
+ /*
2605
+ BaseFunctionLibrary.log10 = {
2606
+ fn: (x) => Math.log(x) / Math.log(10),
2607
+ category: ['Math Functions'],
2608
+ };
2609
+ */
2610
+ }
2611
+ //# sourceMappingURL=base-functions.js.map