@trebco/treb 23.6.5 → 25.0.0-rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/.eslintignore +8 -0
  2. package/.eslintrc.js +164 -0
  3. package/README-shadow-DOM.md +88 -0
  4. package/README.md +37 -130
  5. package/api-config.json +29 -0
  6. package/api-generator/api-generator-types.ts +82 -0
  7. package/api-generator/api-generator.ts +1172 -0
  8. package/api-generator/package.json +3 -0
  9. package/build/treb-spreadsheet.mjs +14 -0
  10. package/{treb.d.ts → build/treb.d.ts} +323 -271
  11. package/esbuild-custom-element.mjs +336 -0
  12. package/esbuild.js +305 -0
  13. package/package.json +49 -14
  14. package/treb-base-types/package.json +5 -0
  15. package/treb-base-types/src/api_types.ts +36 -0
  16. package/treb-base-types/src/area.ts +583 -0
  17. package/treb-base-types/src/basic_types.ts +45 -0
  18. package/treb-base-types/src/cell.ts +612 -0
  19. package/treb-base-types/src/cells.ts +1066 -0
  20. package/treb-base-types/src/color.ts +124 -0
  21. package/treb-base-types/src/import.ts +71 -0
  22. package/treb-base-types/src/index-standalone.ts +29 -0
  23. package/treb-base-types/src/index.ts +42 -0
  24. package/treb-base-types/src/layout.ts +47 -0
  25. package/treb-base-types/src/localization.ts +187 -0
  26. package/treb-base-types/src/rectangle.ts +145 -0
  27. package/treb-base-types/src/render_text.ts +72 -0
  28. package/treb-base-types/src/style.ts +545 -0
  29. package/treb-base-types/src/table.ts +109 -0
  30. package/treb-base-types/src/text_part.ts +54 -0
  31. package/treb-base-types/src/theme.ts +608 -0
  32. package/treb-base-types/src/union.ts +152 -0
  33. package/treb-base-types/src/value-type.ts +164 -0
  34. package/treb-base-types/style/resizable.css +59 -0
  35. package/treb-calculator/modern.tsconfig.json +11 -0
  36. package/treb-calculator/package.json +5 -0
  37. package/treb-calculator/src/calculator.ts +2546 -0
  38. package/treb-calculator/src/complex-math.ts +558 -0
  39. package/treb-calculator/src/dag/array-vertex.ts +198 -0
  40. package/treb-calculator/src/dag/graph.ts +951 -0
  41. package/treb-calculator/src/dag/leaf_vertex.ts +118 -0
  42. package/treb-calculator/src/dag/spreadsheet_vertex.ts +327 -0
  43. package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +44 -0
  44. package/treb-calculator/src/dag/vertex.ts +352 -0
  45. package/treb-calculator/src/descriptors.ts +162 -0
  46. package/treb-calculator/src/expression-calculator.ts +1069 -0
  47. package/treb-calculator/src/function-error.ts +103 -0
  48. package/treb-calculator/src/function-library.ts +103 -0
  49. package/treb-calculator/src/functions/base-functions.ts +1214 -0
  50. package/treb-calculator/src/functions/checkbox.ts +164 -0
  51. package/treb-calculator/src/functions/complex-functions.ts +253 -0
  52. package/treb-calculator/src/functions/finance-functions.ts +399 -0
  53. package/treb-calculator/src/functions/information-functions.ts +102 -0
  54. package/treb-calculator/src/functions/matrix-functions.ts +182 -0
  55. package/treb-calculator/src/functions/sparkline.ts +335 -0
  56. package/treb-calculator/src/functions/statistics-functions.ts +350 -0
  57. package/treb-calculator/src/functions/text-functions.ts +298 -0
  58. package/treb-calculator/src/index.ts +27 -0
  59. package/treb-calculator/src/notifier-types.ts +59 -0
  60. package/treb-calculator/src/primitives.ts +428 -0
  61. package/treb-calculator/src/utilities.ts +305 -0
  62. package/treb-charts/package.json +5 -0
  63. package/treb-charts/src/chart-functions.ts +156 -0
  64. package/treb-charts/src/chart-types.ts +230 -0
  65. package/treb-charts/src/chart.ts +1288 -0
  66. package/treb-charts/src/index.ts +24 -0
  67. package/treb-charts/src/main.ts +37 -0
  68. package/treb-charts/src/rectangle.ts +52 -0
  69. package/treb-charts/src/renderer.ts +1841 -0
  70. package/treb-charts/src/util.ts +122 -0
  71. package/treb-charts/style/charts.scss +221 -0
  72. package/treb-charts/style/old-charts.scss +250 -0
  73. package/treb-embed/markup/layout.html +137 -0
  74. package/treb-embed/markup/toolbar.html +175 -0
  75. package/treb-embed/modern.tsconfig.json +25 -0
  76. package/treb-embed/src/custom-element/content-types.d.ts +18 -0
  77. package/treb-embed/src/custom-element/global.d.ts +11 -0
  78. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +1228 -0
  79. package/treb-embed/src/custom-element/treb-global.ts +44 -0
  80. package/treb-embed/src/custom-element/treb-spreadsheet-element.ts +52 -0
  81. package/treb-embed/src/embedded-spreadsheet.ts +5358 -0
  82. package/treb-embed/src/index.ts +16 -0
  83. package/treb-embed/src/language-model.ts +41 -0
  84. package/treb-embed/src/options.ts +298 -0
  85. package/treb-embed/src/progress-dialog.ts +228 -0
  86. package/treb-embed/src/selection-state.ts +16 -0
  87. package/treb-embed/src/spinner.ts +42 -0
  88. package/treb-embed/src/toolbar-message.ts +96 -0
  89. package/treb-embed/src/types.ts +167 -0
  90. package/treb-embed/style/autocomplete.scss +103 -0
  91. package/treb-embed/style/dark-theme.scss +114 -0
  92. package/treb-embed/style/defaults.scss +36 -0
  93. package/treb-embed/style/dialog.scss +181 -0
  94. package/treb-embed/style/dropdown-select.scss +101 -0
  95. package/treb-embed/style/formula-bar.scss +193 -0
  96. package/treb-embed/style/grid.scss +374 -0
  97. package/treb-embed/style/layout.scss +424 -0
  98. package/treb-embed/style/mouse-mask.scss +67 -0
  99. package/treb-embed/style/note.scss +92 -0
  100. package/treb-embed/style/overlay-editor.scss +102 -0
  101. package/treb-embed/style/spinner.scss +92 -0
  102. package/treb-embed/style/tab-bar.scss +228 -0
  103. package/treb-embed/style/table.scss +80 -0
  104. package/treb-embed/style/theme-defaults.scss +444 -0
  105. package/treb-embed/style/toolbar.scss +416 -0
  106. package/treb-embed/style/tooltip.scss +68 -0
  107. package/treb-embed/style/treb-icons.scss +130 -0
  108. package/treb-embed/style/treb-spreadsheet-element.scss +20 -0
  109. package/treb-embed/style/z-index.scss +43 -0
  110. package/treb-export/docs/charts.md +68 -0
  111. package/treb-export/modern.tsconfig.json +19 -0
  112. package/treb-export/package.json +4 -0
  113. package/treb-export/src/address-type.ts +77 -0
  114. package/treb-export/src/base-template.ts +22 -0
  115. package/treb-export/src/column-width.ts +85 -0
  116. package/treb-export/src/drawing2/chart-template-components2.ts +389 -0
  117. package/treb-export/src/drawing2/chart2.ts +282 -0
  118. package/treb-export/src/drawing2/column-chart-template2.ts +521 -0
  119. package/treb-export/src/drawing2/donut-chart-template2.ts +296 -0
  120. package/treb-export/src/drawing2/drawing2.ts +355 -0
  121. package/treb-export/src/drawing2/embedded-image.ts +71 -0
  122. package/treb-export/src/drawing2/scatter-chart-template2.ts +555 -0
  123. package/treb-export/src/export-worker/export-worker.ts +99 -0
  124. package/treb-export/src/export-worker/index-modern.ts +22 -0
  125. package/treb-export/src/export2.ts +2204 -0
  126. package/treb-export/src/import2.ts +882 -0
  127. package/treb-export/src/relationship.ts +36 -0
  128. package/treb-export/src/shared-strings2.ts +128 -0
  129. package/treb-export/src/template-2.ts +22 -0
  130. package/treb-export/src/unescape_xml.ts +47 -0
  131. package/treb-export/src/workbook-sheet2.ts +182 -0
  132. package/treb-export/src/workbook-style2.ts +1285 -0
  133. package/treb-export/src/workbook-theme2.ts +88 -0
  134. package/treb-export/src/workbook2.ts +491 -0
  135. package/treb-export/src/xml-utils.ts +201 -0
  136. package/treb-export/template/base/[Content_Types].xml +2 -0
  137. package/treb-export/template/base/_rels/.rels +2 -0
  138. package/treb-export/template/base/docProps/app.xml +2 -0
  139. package/treb-export/template/base/docProps/core.xml +12 -0
  140. package/treb-export/template/base/xl/_rels/workbook.xml.rels +2 -0
  141. package/treb-export/template/base/xl/sharedStrings.xml +2 -0
  142. package/treb-export/template/base/xl/styles.xml +2 -0
  143. package/treb-export/template/base/xl/theme/theme1.xml +2 -0
  144. package/treb-export/template/base/xl/workbook.xml +2 -0
  145. package/treb-export/template/base/xl/worksheets/sheet1.xml +2 -0
  146. package/treb-export/template/base.xlsx +0 -0
  147. package/treb-format/package.json +8 -0
  148. package/treb-format/src/format.test.ts +213 -0
  149. package/treb-format/src/format.ts +942 -0
  150. package/treb-format/src/format_cache.ts +199 -0
  151. package/treb-format/src/format_parser.ts +723 -0
  152. package/treb-format/src/index.ts +25 -0
  153. package/treb-format/src/number_format_section.ts +100 -0
  154. package/treb-format/src/value_parser.ts +337 -0
  155. package/treb-grid/package.json +5 -0
  156. package/treb-grid/src/editors/autocomplete.ts +394 -0
  157. package/treb-grid/src/editors/autocomplete_matcher.ts +260 -0
  158. package/treb-grid/src/editors/formula_bar.ts +473 -0
  159. package/treb-grid/src/editors/formula_editor_base.ts +910 -0
  160. package/treb-grid/src/editors/overlay_editor.ts +511 -0
  161. package/treb-grid/src/index.ts +37 -0
  162. package/treb-grid/src/layout/base_layout.ts +2618 -0
  163. package/treb-grid/src/layout/grid_layout.ts +299 -0
  164. package/treb-grid/src/layout/rectangle_cache.ts +86 -0
  165. package/treb-grid/src/render/selection-renderer.ts +414 -0
  166. package/treb-grid/src/render/svg_header_overlay.ts +93 -0
  167. package/treb-grid/src/render/svg_selection_block.ts +187 -0
  168. package/treb-grid/src/render/tile_renderer.ts +2122 -0
  169. package/treb-grid/src/types/annotation.ts +216 -0
  170. package/treb-grid/src/types/border_constants.ts +34 -0
  171. package/treb-grid/src/types/clipboard_data.ts +31 -0
  172. package/treb-grid/src/types/data_model.ts +334 -0
  173. package/treb-grid/src/types/drag_mask.ts +81 -0
  174. package/treb-grid/src/types/grid.ts +7743 -0
  175. package/treb-grid/src/types/grid_base.ts +3644 -0
  176. package/treb-grid/src/types/grid_command.ts +470 -0
  177. package/treb-grid/src/types/grid_events.ts +124 -0
  178. package/treb-grid/src/types/grid_options.ts +97 -0
  179. package/treb-grid/src/types/grid_selection.ts +60 -0
  180. package/treb-grid/src/types/named_range.ts +369 -0
  181. package/treb-grid/src/types/scale-control.ts +202 -0
  182. package/treb-grid/src/types/serialize_options.ts +72 -0
  183. package/treb-grid/src/types/set_range_options.ts +52 -0
  184. package/treb-grid/src/types/sheet.ts +3099 -0
  185. package/treb-grid/src/types/sheet_types.ts +95 -0
  186. package/treb-grid/src/types/tab_bar.ts +464 -0
  187. package/treb-grid/src/types/tile.ts +59 -0
  188. package/treb-grid/src/types/update_flags.ts +75 -0
  189. package/treb-grid/src/util/dom_utilities.ts +44 -0
  190. package/treb-grid/src/util/fontmetrics2.ts +179 -0
  191. package/treb-grid/src/util/ua.ts +104 -0
  192. package/treb-logo.svg +18 -0
  193. package/treb-parser/package.json +5 -0
  194. package/treb-parser/src/csv-parser.ts +122 -0
  195. package/treb-parser/src/index.ts +25 -0
  196. package/treb-parser/src/md-parser.ts +526 -0
  197. package/treb-parser/src/parser-types.ts +397 -0
  198. package/treb-parser/src/parser.test.ts +298 -0
  199. package/treb-parser/src/parser.ts +2673 -0
  200. package/treb-utils/package.json +5 -0
  201. package/treb-utils/src/dispatch.ts +57 -0
  202. package/treb-utils/src/event_source.ts +147 -0
  203. package/treb-utils/src/ievent_source.ts +33 -0
  204. package/treb-utils/src/index.ts +31 -0
  205. package/treb-utils/src/measurement.ts +174 -0
  206. package/treb-utils/src/resizable.ts +160 -0
  207. package/treb-utils/src/scale.ts +137 -0
  208. package/treb-utils/src/serialize_html.ts +124 -0
  209. package/treb-utils/src/template.ts +70 -0
  210. package/treb-utils/src/validate_uri.ts +61 -0
  211. package/tsconfig.json +10 -0
  212. package/tsproject.json +30 -0
  213. package/util/license-plugin-esbuild.js +86 -0
  214. package/util/list-css-vars.sh +46 -0
  215. package/README-esm.md +0 -37
  216. package/treb-bundle.css +0 -2
  217. package/treb-bundle.mjs +0 -15
@@ -0,0 +1,335 @@
1
+ /*
2
+ * This file is part of TREB.
3
+ *
4
+ * TREB is free software: you can redistribute it and/or modify it under the
5
+ * terms of the GNU General Public License as published by the Free Software
6
+ * Foundation, either version 3 of the License, or (at your option) any
7
+ * later version.
8
+ *
9
+ * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ * details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License along
15
+ * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
+ *
17
+ * Copyright 2022-2023 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+
22
+ /**
23
+ * moved from sparkline module -- not sure why this needed a separate
24
+ * module. although on reflection it might be better placed in the charts
25
+ * module? (...)
26
+ *
27
+ * perhaps not because we use that module elsewhere and this one is only
28
+ * ever used in spreadsheets.
29
+ *
30
+ */
31
+
32
+
33
+ import type { Cell, Style } from 'treb-base-types';
34
+
35
+ export interface SparklineRenderOptions {
36
+ context: CanvasRenderingContext2D;
37
+ width: number;
38
+ height: number;
39
+ cell: Cell;
40
+ }
41
+
42
+ enum LineOperation {
43
+ move,
44
+ line,
45
+ }
46
+
47
+ export class Sparkline {
48
+
49
+ // public static SingleColor = ['#333'];
50
+ // public static TwoColors = ['green', 'red'];
51
+
52
+ public static readonly default_color = '#888'; // should never be used, but jic
53
+
54
+ /**
55
+ * three possible cases:
56
+ *
57
+ * (1) array of numbers, possibly including missing values
58
+ * (2) typed array
59
+ * (3) nested array, so we need to recurse and concatenate
60
+ *
61
+ * there's actually one more case, where data comes in from serialized
62
+ * representation (loading file); in that case it's an object with numeric
63
+ * indexes (sadly)
64
+ */
65
+ protected static UnpackValues(underlying: any): number[] {
66
+
67
+ if (Array.isArray(underlying)) {
68
+ const test = underlying[0];
69
+ if (Array.isArray(test) || test instanceof Float64Array || test instanceof Float32Array) {
70
+ return underlying.reduce((a, subset) => a.concat(this.UnpackValues(subset)), []);
71
+ }
72
+ else {
73
+ return underlying.map(test => isNaN(test) ? undefined : test);
74
+ }
75
+ }
76
+ else if (underlying instanceof Float32Array || underlying instanceof Float64Array) {
77
+ return Array.prototype.slice.call(underlying);
78
+ }
79
+ else if (underlying && typeof underlying === 'object') {
80
+
81
+ const keys = Object.keys(underlying);
82
+ const len = keys.length;
83
+
84
+ // this is maybe overdoing it? (...) there's probably a smarter test
85
+ // if (keys.every(key => !isNaN(Number(key)))) {
86
+
87
+ // check first, last
88
+ if (typeof underlying['0'] !== 'undefined' && typeof underlying[(len - 1).toString()] !== 'undefined') {
89
+ const data: number[] = [];
90
+
91
+ // we probably don't have to explicitly use strings -- although it's not
92
+ // clear that it would be any faster because someone still has to do the
93
+ // conversion
94
+
95
+ for (let i = 0; i < len; i++) {
96
+ data[i] = underlying[i.toString()];
97
+ }
98
+ return data;
99
+ }
100
+
101
+ }
102
+
103
+ return [];
104
+
105
+ }
106
+
107
+ protected static SparklineCommon(cell: Cell, style: Style.Properties): {
108
+ values: Array<number|undefined>,
109
+ colors: string[],
110
+ } {
111
+
112
+ // the cell function echoes back arguments. the first argument
113
+ // should be an array, but it will be 2D...
114
+
115
+ let values: Array<number|undefined> = [];
116
+
117
+ // use text color, or default. because this is called from renderer,
118
+ // theme default _should_ always be passed in, so we should (theoretically)
119
+ // never need our default.
120
+
121
+ const text_color = style.text?.text || this.default_color;
122
+ const colors: string[] = [ text_color, text_color ];
123
+
124
+ if (Array.isArray(cell.calculated)) {
125
+ values = this.UnpackValues(cell.calculated[0]);
126
+ if (typeof cell.calculated[1] === 'string') {
127
+ colors[0] = cell.calculated[1];
128
+ }
129
+ if (typeof cell.calculated[2] === 'string') {
130
+ colors[1] = cell.calculated[2];
131
+ }
132
+ }
133
+
134
+ return { values, colors };
135
+
136
+ }
137
+
138
+ public static RenderLine(
139
+ width: number,
140
+ height: number,
141
+ context: CanvasRenderingContext2D,
142
+ cell: Cell,
143
+ style: Style.Properties,
144
+ ): void {
145
+
146
+ const {values, colors} = this.SparklineCommon(cell, style);
147
+
148
+ const x_margin = 0.05; // FIXME: parameterize? (...)
149
+ const y_margin = 0.10;
150
+
151
+ let line_width = 1;
152
+ if (Array.isArray(cell.calculated) && typeof cell.calculated[2] === 'number') {
153
+ line_width = cell.calculated[2];
154
+ }
155
+
156
+ let min = 0;
157
+ let max = 0;
158
+ let first_index = -1;
159
+
160
+ for (let i = 0; i < values.length; i++) {
161
+ const value = values[i];
162
+ if (typeof value === 'number') {
163
+ if (first_index >= 0) {
164
+ min = Math.min(min, value);
165
+ max = Math.max(max, value);
166
+ }
167
+ else {
168
+ first_index = i;
169
+ min = max = value;
170
+ }
171
+ }
172
+ }
173
+
174
+ if (min !== max) {
175
+
176
+ const step = (width * (1 - 2 * x_margin)) / (values.length - 1);
177
+ const range = max - min;
178
+ const pixel_range = height * (1 - 2 * y_margin); // ?
179
+ const base = height * y_margin;
180
+
181
+ context.strokeStyle = colors[0];
182
+ context.lineWidth = line_width;
183
+ context.lineCap = 'round';
184
+ context.lineJoin = 'round';
185
+
186
+ // let x = width * x_margin + step * first_index;
187
+ // let y = height - ((values[first_index] as number) - min) * pixel_range / range - base;
188
+
189
+ context.beginPath();
190
+
191
+ let op = LineOperation.move;
192
+
193
+ for (let i = first_index; i < values.length; i++) {
194
+ const value = values[i];
195
+ if (typeof value === 'number') {
196
+ const x = width * x_margin + step * i;
197
+ const y = height - (value - min) * pixel_range / range - base;
198
+ if (op === LineOperation.move) {
199
+ context.moveTo(x, y);
200
+ op = LineOperation.line;
201
+ }
202
+ else {
203
+ context.lineTo(x, y);
204
+ }
205
+ }
206
+ else {
207
+ op = LineOperation.move;
208
+ }
209
+ }
210
+
211
+ context.stroke();
212
+
213
+ }
214
+
215
+ }
216
+
217
+
218
+ public static RenderColumn(
219
+ width: number,
220
+ height: number,
221
+ context: CanvasRenderingContext2D,
222
+ cell: Cell,
223
+ style: Style.Properties,
224
+ ): void {
225
+
226
+ const {values, colors} = this.SparklineCommon(cell, style);
227
+
228
+ // const x_margin = 0.05; // FIXME: parameterize? (...)
229
+ // const y_margin = 0.10;
230
+ // const y_margin = Math.max(1, Math.min(0.10 * height, 2));
231
+
232
+ const x_margin = 3;
233
+ const y_margin = 2.5;
234
+
235
+ let min = 0;
236
+ let max = 0;
237
+ let first_value = false;
238
+
239
+ for (const value of values) {
240
+ if (typeof value === 'number') {
241
+ if (first_value) {
242
+ min = Math.min(min, value);
243
+ max = Math.max(max, value);
244
+ }
245
+ else {
246
+ first_value = true;
247
+ min = max = value;
248
+ }
249
+ }
250
+ }
251
+
252
+ if (values.length) {
253
+
254
+ // const step = (width - 2 * x_margin - 2) / (values.length-1);
255
+ const step = (width - 2 * x_margin - 2) / (values.length-0);
256
+ const pixel_range = (height - 2 * y_margin); // ?
257
+ const base = y_margin;
258
+
259
+ // let x = Math.round(width * x_margin);
260
+
261
+ if (min !== max) {
262
+
263
+ if (min < 0 && max > 0) {
264
+
265
+ const range = max - min;
266
+ const zero = base + max / range * pixel_range;
267
+
268
+ // use an indexed loop so we can multiply to get x instead of adding
269
+ for (let i = 0; i < values.length; i++) {
270
+ const value = values[i];
271
+ if (typeof value === 'number') {
272
+ const x = (x_margin + i * step);
273
+ const bar_height = (Math.abs(value) / range) * pixel_range;
274
+
275
+ if (value >= 0) {
276
+ context.fillStyle = colors[0];
277
+ const top = zero - bar_height;
278
+ context.fillRect(x + 2, top, step - 2, bar_height);
279
+ }
280
+ else {
281
+ context.fillStyle = colors[1];
282
+ const top = zero;
283
+ context.fillRect(x + 2, top, step - 2, bar_height);
284
+ }
285
+
286
+ }
287
+ }
288
+
289
+ }
290
+ else if (max > 0) {
291
+
292
+ // all positive
293
+
294
+ context.fillStyle = colors[0];
295
+ const range = max - min;
296
+
297
+ // use an indexed loop so we can multiply to get x instead of adding
298
+ for (let i = 0; i < values.length; i++) {
299
+ const value = values[i];
300
+ if (typeof value === 'number') {
301
+ const x = (x_margin + i * step);
302
+ const bar_height = Math.max(1, ((value - min) / range) * pixel_range);
303
+ const top = height - base - bar_height;
304
+ context.fillRect(x + 2, top, step - 2, bar_height);
305
+ }
306
+ }
307
+
308
+ }
309
+ else {
310
+
311
+ // all negative
312
+
313
+ context.fillStyle = colors[1];
314
+ const range = max - min;
315
+
316
+ // use an indexed loop so we can multiply to get x instead of adding
317
+ for (let i = 0; i < values.length; i++) {
318
+ const value = values[i];
319
+ if (typeof value === 'number') {
320
+ const x = (x_margin + i * step);
321
+ const bar_height = Math.max( 1, (Math.abs(max - value) / range) * pixel_range);
322
+ const top = base;
323
+ context.fillRect(x + 2, top, step - 2, bar_height);
324
+ }
325
+ }
326
+
327
+ }
328
+
329
+ }
330
+
331
+ }
332
+
333
+ }
334
+
335
+ }
@@ -0,0 +1,350 @@
1
+ /*
2
+ * This file is part of TREB.
3
+ *
4
+ * TREB is free software: you can redistribute it and/or modify it under the
5
+ * terms of the GNU General Public License as published by the Free Software
6
+ * Foundation, either version 3 of the License, or (at your option) any
7
+ * later version.
8
+ *
9
+ * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ * details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License along
15
+ * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
+ *
17
+ * Copyright 2022-2023 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+
22
+ import type { FunctionMap } from '../descriptors';
23
+ import * as Utils from '../utilities';
24
+ import { ValueError, ArgumentError, NAError } from '../function-error';
25
+ import { Complex, UnionValue, ValueType } from 'treb-base-types';
26
+ import * as ComplexMath from '../complex-math';
27
+
28
+ export const Variance = (data: number[], sample = false) => {
29
+
30
+ const len = data.length;
31
+
32
+ let m = 0;
33
+ let v = 0;
34
+ // let k = 0;
35
+ // let s = 0;
36
+
37
+ for (let i = 0; i < len; i++) m += data[i];
38
+
39
+ m /= len;
40
+ // const mean = m;
41
+
42
+ for (let i = 0; i < len; i++) {
43
+ const d = data[i] - m;
44
+ v += (d * d);
45
+ // s += (d * d * d);
46
+ // k += (d * d * d * d);
47
+ }
48
+
49
+ // const N = len;
50
+ // const variance = v / len;
51
+ // const stdev = Math.sqrt(v / len);
52
+ return sample ? (v / (len-1)) : (v/len);
53
+
54
+ };
55
+
56
+ export const StatisticsFunctionLibrary: FunctionMap = {
57
+
58
+ 'StDev.P': {
59
+ description: 'Returns the standard deviation of a set of values, corresponding to a population',
60
+ arguments: [{ name: 'data', }],
61
+ fn: (...args: any[]): UnionValue => {
62
+ return { type: ValueType.number, value: Math.sqrt(Variance(Utils.FlattenUnboxed(args), false)) };
63
+ },
64
+ },
65
+
66
+ 'StDev.S': {
67
+ description: 'Returns the standard deviation of a set of values, corresponding to a sample of a population',
68
+ arguments: [{ name: 'data', }],
69
+ fn: (...args: any[]): UnionValue => {
70
+ return { type: ValueType.number, value: Math.sqrt(Variance(Utils.FlattenUnboxed(args), true)) };
71
+ },
72
+ },
73
+
74
+ 'Var.P': {
75
+ description: 'Returns the variance of a set of values, corresponding to a population',
76
+ arguments: [{ name: 'data', }],
77
+ fn: (...args: any[]): UnionValue => {
78
+ return { type: ValueType.number, value: Variance(Utils.FlattenUnboxed(args), false) };
79
+ },
80
+ },
81
+
82
+ 'Var.S': {
83
+ description: 'Returns the variance of a set of values, corresponding to a sample of a population',
84
+ arguments: [{ name: 'data', }],
85
+ fn: (...args: any[]): UnionValue => {
86
+ return { type: ValueType.number, value: Variance(Utils.FlattenUnboxed(args), true) };
87
+ },
88
+ },
89
+
90
+ Covar: {
91
+ description: 'Returns the covariance between two ranges of values',
92
+ arguments: [{
93
+ name: 'A',
94
+ }, {
95
+ name: 'B',
96
+ }],
97
+ fn: (x: any[], y: any[]): UnionValue => {
98
+
99
+ // both must be 2d arrays, we're assuming the same or mostly similar shape
100
+ if (!Array.isArray(x) || !Array.isArray(y)) { return ValueError(); }
101
+ if (!Array.isArray(x[0]) || !Array.isArray(y[0])) { return ValueError(); }
102
+
103
+ if (x.length !== y.length) {
104
+ return ArgumentError();
105
+ }
106
+
107
+ let sum = 0;
108
+ let length = 0;
109
+
110
+ const mean = { x: 0, y: 0 };
111
+ const data: { x: number[], y: number[] } = { x: [], y: [] };
112
+
113
+ for (let j = 0; j < x.length; j++) {
114
+
115
+ if (!x[j] || !y[j]) { continue; }
116
+ if (x[j].length !== y[j].length) {
117
+ return ArgumentError();
118
+ }
119
+
120
+ const len = x[j].length;
121
+ length += len;
122
+
123
+ for (let i = 0; i < len; i++) {
124
+ mean.x += (x[j][i] || 0);
125
+ mean.y += (y[j][i] || 0);
126
+
127
+ data.x.push(x[j][i] || 0);
128
+ data.y.push(y[j][i] || 0);
129
+ }
130
+
131
+ }
132
+
133
+ if (length === 0) {
134
+ return NAError();
135
+ }
136
+
137
+ mean.x /= length;
138
+ mean.y /= length;
139
+
140
+ for (let i = 0; i < length; i++) {
141
+ sum += (data.x[i] - mean.x) * (data.y[i] - mean.y);
142
+ }
143
+
144
+ return { type: ValueType.number, value: sum / length, };
145
+
146
+ },
147
+ },
148
+
149
+ Correl: {
150
+ description: 'Returns the correlation between two ranges of values',
151
+ arguments: [{
152
+ name: 'A',
153
+ }, {
154
+ name: 'B',
155
+ }],
156
+ fn: (x: any[], y: any[]): UnionValue => {
157
+
158
+ // both must be 2d arrays, we're assuming the same or mostly similar shape
159
+ if (!Array.isArray(x) || !Array.isArray(y)) { return ValueError(); }
160
+ if (!Array.isArray(x[0]) || !Array.isArray(y[0])) { return ValueError(); }
161
+
162
+ let rslt = 0;
163
+ let sumProduct = 0;
164
+ let sumX = 0;
165
+ let sumY = 0;
166
+ let sumSquaredX = 0;
167
+ let sumSquaredY = 0;
168
+ let count = 0;
169
+
170
+ for (let j = 0; j < x.length; j++) {
171
+ if (!x[j] || !y[j]) { continue; }
172
+
173
+ const len = x[j].length;
174
+ for (let i = 0; i < len; i++) {
175
+ const a = Number(x[j][i]);
176
+ const b = Number(y[j][i]);
177
+
178
+ if (isNaN(a) || isNaN(b)) { continue; }
179
+
180
+ sumProduct += (a * b);
181
+ sumX += a;
182
+ sumY += b;
183
+ sumSquaredX += (a * a);
184
+ sumSquaredY += (b * b);
185
+ count++;
186
+ }
187
+ }
188
+
189
+ rslt = ((count * sumProduct) - (sumX * sumY));
190
+ if (rslt) {
191
+ rslt /= Math.sqrt(((count * sumSquaredX) - (sumX * sumX)) * ((count * sumSquaredY) - (sumY * sumY)));
192
+ }
193
+
194
+ return { type: ValueType.number, value: rslt };
195
+
196
+ },
197
+ },
198
+
199
+ GeoMean: {
200
+
201
+ description: 'Returns the geometric mean of all numeric arguments',
202
+ arguments: [{ boxed: true }],
203
+
204
+ fn: (...args: any[]): UnionValue => {
205
+
206
+ args = Utils.FlattenBoxed(args);
207
+
208
+ let count = 0;
209
+ let product: Complex = {real: 1, imaginary: 0};
210
+ let complex = false;
211
+ let negative = false;
212
+
213
+ for (const arg of args as UnionValue[]) {
214
+
215
+ if (arg.type === ValueType.complex) {
216
+ complex = true;
217
+ product = ComplexMath.Multiply(product, arg.value);
218
+ count++;
219
+ }
220
+ else if (arg.type === ValueType.number) {
221
+ if (arg.value < 0) {
222
+ negative = true;
223
+ }
224
+ count++;
225
+ // product = ComplexMath.Multiply(product, {real: arg.value, imaginary: 0});
226
+ product.real *= arg.value;
227
+ product.imaginary *= arg.value;
228
+
229
+ }
230
+
231
+ }
232
+
233
+ if (complex) {
234
+ const value = ComplexMath.Power(product, {real: 1/count, imaginary: 0});
235
+ if (value.imaginary) {
236
+ return { type: ValueType.complex, value };
237
+ }
238
+ return { type: ValueType.number, value: value.real };
239
+ }
240
+ else {
241
+ if (negative) {
242
+ return ValueError();
243
+ }
244
+ return { type: ValueType.number, value: Math.pow(product.real, 1 / count) };
245
+ }
246
+
247
+ /*
248
+ for (const arg of args) {
249
+ if (typeof arg === 'undefined') { continue; }
250
+ const value = Number(arg);
251
+ if (value < 0) { return ValueError(); }
252
+ count++;
253
+ product *= value;
254
+ }
255
+ return { type: ValueType.number, value: Math.pow(product, 1 / count) };
256
+ */
257
+
258
+
259
+
260
+ },
261
+ },
262
+
263
+ Average: {
264
+
265
+ description: 'Returns the arithmetic mean of all numeric arguments',
266
+ arguments: [{ boxed: true }],
267
+
268
+ fn: (...args: any[]): UnionValue => {
269
+ args = Utils.FlattenBoxed(args);
270
+
271
+ const result = { real: 0, imaginary: 0 };
272
+ let count = 0;
273
+
274
+ for (const ref of args as UnionValue[]) {
275
+ if (ref.type === ValueType.error) {
276
+ return ref;
277
+ }
278
+ if (ref.type === ValueType.number) {
279
+ result.real += ref.value;
280
+ count++;
281
+ }
282
+ if (ref.type === ValueType.complex) {
283
+ result.real += ref.value.real;
284
+ result.imaginary += ref.value.imaginary;
285
+ count++;
286
+ }
287
+ }
288
+
289
+ result.real /= count;
290
+ result.imaginary /= count;
291
+
292
+ if (result.imaginary) {
293
+ return { type: ValueType.complex, value: result, };
294
+ }
295
+ return { type: ValueType.number, value: result.real };
296
+
297
+ },
298
+ },
299
+
300
+ Percentile: {
301
+ description: 'Returns the kth percentile value from the range of data',
302
+ arguments: [
303
+ { name: 'range' },
304
+ { name: 'percentile' },
305
+ ],
306
+ fn: (range: number[][], percentile: number): UnionValue => {
307
+
308
+ const flat = Utils.FlattenUnboxed(range).filter((test) => typeof test === 'number');
309
+ flat.sort((a, b) => a - b);
310
+ const n = flat.length;
311
+
312
+ // try to stabilize this number
313
+ const factor = Math.pow(10,8);
314
+ const x = Math.round((1 + (n-1) * percentile) * factor)/factor;
315
+
316
+ const lo = Math.floor(x);
317
+ const hi = Math.ceil(x);
318
+
319
+ return { type: ValueType.number, value: (flat[lo-1] + flat[hi-1]) / 2 };
320
+
321
+ },
322
+ },
323
+
324
+ Median: {
325
+ description: 'Returns the median value of the range of data',
326
+ arguments: [
327
+ { name: 'range' },
328
+ ],
329
+ fn: (...args: any[]): UnionValue => {
330
+
331
+ const flat = Utils.FlattenUnboxed(args).filter((test) => typeof test === 'number');
332
+ flat.sort((a, b) => a - b);
333
+ const n = flat.length;
334
+
335
+ const x = 1 + (n-1) * .5;
336
+ const lo = Math.floor(x);
337
+ const hi = Math.ceil(x);
338
+
339
+ return { type: ValueType.number, value: (flat[lo-1] + flat[hi-1]) / 2 };
340
+
341
+ },
342
+ }
343
+
344
+ };
345
+
346
+ export const StatisticsFunctionAliases: {[index: string]: string} = {
347
+ Mean: 'Average',
348
+ 'StDev': 'StDev.S',
349
+ 'Var': 'Var.S',
350
+ };