@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.
- package/.eslintignore +8 -0
- package/.eslintrc.js +164 -0
- package/README-shadow-DOM.md +88 -0
- package/README.md +37 -130
- package/api-config.json +29 -0
- package/api-generator/api-generator-types.ts +82 -0
- package/api-generator/api-generator.ts +1172 -0
- package/api-generator/package.json +3 -0
- package/build/treb-spreadsheet.mjs +14 -0
- package/{treb.d.ts → build/treb.d.ts} +323 -271
- package/esbuild-custom-element.mjs +336 -0
- package/esbuild.js +305 -0
- package/package.json +49 -14
- package/treb-base-types/package.json +5 -0
- package/treb-base-types/src/api_types.ts +36 -0
- package/treb-base-types/src/area.ts +583 -0
- package/treb-base-types/src/basic_types.ts +45 -0
- package/treb-base-types/src/cell.ts +612 -0
- package/treb-base-types/src/cells.ts +1066 -0
- package/treb-base-types/src/color.ts +124 -0
- package/treb-base-types/src/import.ts +71 -0
- package/treb-base-types/src/index-standalone.ts +29 -0
- package/treb-base-types/src/index.ts +42 -0
- package/treb-base-types/src/layout.ts +47 -0
- package/treb-base-types/src/localization.ts +187 -0
- package/treb-base-types/src/rectangle.ts +145 -0
- package/treb-base-types/src/render_text.ts +72 -0
- package/treb-base-types/src/style.ts +545 -0
- package/treb-base-types/src/table.ts +109 -0
- package/treb-base-types/src/text_part.ts +54 -0
- package/treb-base-types/src/theme.ts +608 -0
- package/treb-base-types/src/union.ts +152 -0
- package/treb-base-types/src/value-type.ts +164 -0
- package/treb-base-types/style/resizable.css +59 -0
- package/treb-calculator/modern.tsconfig.json +11 -0
- package/treb-calculator/package.json +5 -0
- package/treb-calculator/src/calculator.ts +2546 -0
- package/treb-calculator/src/complex-math.ts +558 -0
- package/treb-calculator/src/dag/array-vertex.ts +198 -0
- package/treb-calculator/src/dag/graph.ts +951 -0
- package/treb-calculator/src/dag/leaf_vertex.ts +118 -0
- package/treb-calculator/src/dag/spreadsheet_vertex.ts +327 -0
- package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +44 -0
- package/treb-calculator/src/dag/vertex.ts +352 -0
- package/treb-calculator/src/descriptors.ts +162 -0
- package/treb-calculator/src/expression-calculator.ts +1069 -0
- package/treb-calculator/src/function-error.ts +103 -0
- package/treb-calculator/src/function-library.ts +103 -0
- package/treb-calculator/src/functions/base-functions.ts +1214 -0
- package/treb-calculator/src/functions/checkbox.ts +164 -0
- package/treb-calculator/src/functions/complex-functions.ts +253 -0
- package/treb-calculator/src/functions/finance-functions.ts +399 -0
- package/treb-calculator/src/functions/information-functions.ts +102 -0
- package/treb-calculator/src/functions/matrix-functions.ts +182 -0
- package/treb-calculator/src/functions/sparkline.ts +335 -0
- package/treb-calculator/src/functions/statistics-functions.ts +350 -0
- package/treb-calculator/src/functions/text-functions.ts +298 -0
- package/treb-calculator/src/index.ts +27 -0
- package/treb-calculator/src/notifier-types.ts +59 -0
- package/treb-calculator/src/primitives.ts +428 -0
- package/treb-calculator/src/utilities.ts +305 -0
- package/treb-charts/package.json +5 -0
- package/treb-charts/src/chart-functions.ts +156 -0
- package/treb-charts/src/chart-types.ts +230 -0
- package/treb-charts/src/chart.ts +1288 -0
- package/treb-charts/src/index.ts +24 -0
- package/treb-charts/src/main.ts +37 -0
- package/treb-charts/src/rectangle.ts +52 -0
- package/treb-charts/src/renderer.ts +1841 -0
- package/treb-charts/src/util.ts +122 -0
- package/treb-charts/style/charts.scss +221 -0
- package/treb-charts/style/old-charts.scss +250 -0
- package/treb-embed/markup/layout.html +137 -0
- package/treb-embed/markup/toolbar.html +175 -0
- package/treb-embed/modern.tsconfig.json +25 -0
- package/treb-embed/src/custom-element/content-types.d.ts +18 -0
- package/treb-embed/src/custom-element/global.d.ts +11 -0
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +1228 -0
- package/treb-embed/src/custom-element/treb-global.ts +44 -0
- package/treb-embed/src/custom-element/treb-spreadsheet-element.ts +52 -0
- package/treb-embed/src/embedded-spreadsheet.ts +5358 -0
- package/treb-embed/src/index.ts +16 -0
- package/treb-embed/src/language-model.ts +41 -0
- package/treb-embed/src/options.ts +298 -0
- package/treb-embed/src/progress-dialog.ts +228 -0
- package/treb-embed/src/selection-state.ts +16 -0
- package/treb-embed/src/spinner.ts +42 -0
- package/treb-embed/src/toolbar-message.ts +96 -0
- package/treb-embed/src/types.ts +167 -0
- package/treb-embed/style/autocomplete.scss +103 -0
- package/treb-embed/style/dark-theme.scss +114 -0
- package/treb-embed/style/defaults.scss +36 -0
- package/treb-embed/style/dialog.scss +181 -0
- package/treb-embed/style/dropdown-select.scss +101 -0
- package/treb-embed/style/formula-bar.scss +193 -0
- package/treb-embed/style/grid.scss +374 -0
- package/treb-embed/style/layout.scss +424 -0
- package/treb-embed/style/mouse-mask.scss +67 -0
- package/treb-embed/style/note.scss +92 -0
- package/treb-embed/style/overlay-editor.scss +102 -0
- package/treb-embed/style/spinner.scss +92 -0
- package/treb-embed/style/tab-bar.scss +228 -0
- package/treb-embed/style/table.scss +80 -0
- package/treb-embed/style/theme-defaults.scss +444 -0
- package/treb-embed/style/toolbar.scss +416 -0
- package/treb-embed/style/tooltip.scss +68 -0
- package/treb-embed/style/treb-icons.scss +130 -0
- package/treb-embed/style/treb-spreadsheet-element.scss +20 -0
- package/treb-embed/style/z-index.scss +43 -0
- package/treb-export/docs/charts.md +68 -0
- package/treb-export/modern.tsconfig.json +19 -0
- package/treb-export/package.json +4 -0
- package/treb-export/src/address-type.ts +77 -0
- package/treb-export/src/base-template.ts +22 -0
- package/treb-export/src/column-width.ts +85 -0
- package/treb-export/src/drawing2/chart-template-components2.ts +389 -0
- package/treb-export/src/drawing2/chart2.ts +282 -0
- package/treb-export/src/drawing2/column-chart-template2.ts +521 -0
- package/treb-export/src/drawing2/donut-chart-template2.ts +296 -0
- package/treb-export/src/drawing2/drawing2.ts +355 -0
- package/treb-export/src/drawing2/embedded-image.ts +71 -0
- package/treb-export/src/drawing2/scatter-chart-template2.ts +555 -0
- package/treb-export/src/export-worker/export-worker.ts +99 -0
- package/treb-export/src/export-worker/index-modern.ts +22 -0
- package/treb-export/src/export2.ts +2204 -0
- package/treb-export/src/import2.ts +882 -0
- package/treb-export/src/relationship.ts +36 -0
- package/treb-export/src/shared-strings2.ts +128 -0
- package/treb-export/src/template-2.ts +22 -0
- package/treb-export/src/unescape_xml.ts +47 -0
- package/treb-export/src/workbook-sheet2.ts +182 -0
- package/treb-export/src/workbook-style2.ts +1285 -0
- package/treb-export/src/workbook-theme2.ts +88 -0
- package/treb-export/src/workbook2.ts +491 -0
- package/treb-export/src/xml-utils.ts +201 -0
- package/treb-export/template/base/[Content_Types].xml +2 -0
- package/treb-export/template/base/_rels/.rels +2 -0
- package/treb-export/template/base/docProps/app.xml +2 -0
- package/treb-export/template/base/docProps/core.xml +12 -0
- package/treb-export/template/base/xl/_rels/workbook.xml.rels +2 -0
- package/treb-export/template/base/xl/sharedStrings.xml +2 -0
- package/treb-export/template/base/xl/styles.xml +2 -0
- package/treb-export/template/base/xl/theme/theme1.xml +2 -0
- package/treb-export/template/base/xl/workbook.xml +2 -0
- package/treb-export/template/base/xl/worksheets/sheet1.xml +2 -0
- package/treb-export/template/base.xlsx +0 -0
- package/treb-format/package.json +8 -0
- package/treb-format/src/format.test.ts +213 -0
- package/treb-format/src/format.ts +942 -0
- package/treb-format/src/format_cache.ts +199 -0
- package/treb-format/src/format_parser.ts +723 -0
- package/treb-format/src/index.ts +25 -0
- package/treb-format/src/number_format_section.ts +100 -0
- package/treb-format/src/value_parser.ts +337 -0
- package/treb-grid/package.json +5 -0
- package/treb-grid/src/editors/autocomplete.ts +394 -0
- package/treb-grid/src/editors/autocomplete_matcher.ts +260 -0
- package/treb-grid/src/editors/formula_bar.ts +473 -0
- package/treb-grid/src/editors/formula_editor_base.ts +910 -0
- package/treb-grid/src/editors/overlay_editor.ts +511 -0
- package/treb-grid/src/index.ts +37 -0
- package/treb-grid/src/layout/base_layout.ts +2618 -0
- package/treb-grid/src/layout/grid_layout.ts +299 -0
- package/treb-grid/src/layout/rectangle_cache.ts +86 -0
- package/treb-grid/src/render/selection-renderer.ts +414 -0
- package/treb-grid/src/render/svg_header_overlay.ts +93 -0
- package/treb-grid/src/render/svg_selection_block.ts +187 -0
- package/treb-grid/src/render/tile_renderer.ts +2122 -0
- package/treb-grid/src/types/annotation.ts +216 -0
- package/treb-grid/src/types/border_constants.ts +34 -0
- package/treb-grid/src/types/clipboard_data.ts +31 -0
- package/treb-grid/src/types/data_model.ts +334 -0
- package/treb-grid/src/types/drag_mask.ts +81 -0
- package/treb-grid/src/types/grid.ts +7743 -0
- package/treb-grid/src/types/grid_base.ts +3644 -0
- package/treb-grid/src/types/grid_command.ts +470 -0
- package/treb-grid/src/types/grid_events.ts +124 -0
- package/treb-grid/src/types/grid_options.ts +97 -0
- package/treb-grid/src/types/grid_selection.ts +60 -0
- package/treb-grid/src/types/named_range.ts +369 -0
- package/treb-grid/src/types/scale-control.ts +202 -0
- package/treb-grid/src/types/serialize_options.ts +72 -0
- package/treb-grid/src/types/set_range_options.ts +52 -0
- package/treb-grid/src/types/sheet.ts +3099 -0
- package/treb-grid/src/types/sheet_types.ts +95 -0
- package/treb-grid/src/types/tab_bar.ts +464 -0
- package/treb-grid/src/types/tile.ts +59 -0
- package/treb-grid/src/types/update_flags.ts +75 -0
- package/treb-grid/src/util/dom_utilities.ts +44 -0
- package/treb-grid/src/util/fontmetrics2.ts +179 -0
- package/treb-grid/src/util/ua.ts +104 -0
- package/treb-logo.svg +18 -0
- package/treb-parser/package.json +5 -0
- package/treb-parser/src/csv-parser.ts +122 -0
- package/treb-parser/src/index.ts +25 -0
- package/treb-parser/src/md-parser.ts +526 -0
- package/treb-parser/src/parser-types.ts +397 -0
- package/treb-parser/src/parser.test.ts +298 -0
- package/treb-parser/src/parser.ts +2673 -0
- package/treb-utils/package.json +5 -0
- package/treb-utils/src/dispatch.ts +57 -0
- package/treb-utils/src/event_source.ts +147 -0
- package/treb-utils/src/ievent_source.ts +33 -0
- package/treb-utils/src/index.ts +31 -0
- package/treb-utils/src/measurement.ts +174 -0
- package/treb-utils/src/resizable.ts +160 -0
- package/treb-utils/src/scale.ts +137 -0
- package/treb-utils/src/serialize_html.ts +124 -0
- package/treb-utils/src/template.ts +70 -0
- package/treb-utils/src/validate_uri.ts +61 -0
- package/tsconfig.json +10 -0
- package/tsproject.json +30 -0
- package/util/license-plugin-esbuild.js +86 -0
- package/util/list-css-vars.sh +46 -0
- package/README-esm.md +0 -37
- package/treb-bundle.css +0 -2
- package/treb-bundle.mjs +0 -15
|
@@ -0,0 +1,399 @@
|
|
|
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 { CellValue, UnionValue, ValueType } from 'treb-base-types';
|
|
24
|
+
import { FlattenUnboxed } from '../utilities';
|
|
25
|
+
|
|
26
|
+
// use a single, static object for base functions
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* some random web resources, more or less helpful:
|
|
30
|
+
*
|
|
31
|
+
* http://www.ultimatecalculators.com/future_value_annuity_calculator.html
|
|
32
|
+
* https://financeformulas.net/Annuity-Due-Payment-from-Present-Value.html
|
|
33
|
+
* http://www.tvmcalcs.com/tvm/formulas/regular_annuity_formulas
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* this function is broken out because we use it in the rate function
|
|
38
|
+
* (to search). we could reasonably use any of them (we probably should
|
|
39
|
+
* use the one most likely to be zero -- FV maybe?)
|
|
40
|
+
*
|
|
41
|
+
* this is now used in a couple of functions, so it makes sense to leave
|
|
42
|
+
* it broken out irrespective of what we use for Rate.
|
|
43
|
+
*
|
|
44
|
+
*/
|
|
45
|
+
const payment_function = (rate: number, periods: number, pv = 0, fv = 0, type = 0): number => {
|
|
46
|
+
if (type) {
|
|
47
|
+
return -(pv * (rate / (1 - Math.pow(1 + rate, -periods)))) / (1 + rate)
|
|
48
|
+
- (fv * (1 / ((1 + rate) * ((Math.pow(1 + rate, periods) -1)/rate))));
|
|
49
|
+
}
|
|
50
|
+
return -(pv * rate * Math.pow(1 + rate, periods) + fv * rate) / (Math.pow(1 + rate, periods) - 1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** broken out for use in ipmt, ppmt functions */
|
|
54
|
+
const fv_function = (rate: number, periods: number, payment: number, pv = 0, type = 0): number => {
|
|
55
|
+
if (type) {
|
|
56
|
+
return (1 + rate) * -payment / rate * (Math.pow(1 + rate, periods) - 1) - pv * Math.pow(1 + rate, periods);
|
|
57
|
+
}
|
|
58
|
+
return -payment / rate * (Math.pow(1 + rate, periods) - 1) - pv * Math.pow(1 + rate, periods);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** ppmt is calculated as payment less interest payment */
|
|
62
|
+
const ipmt_function = (rate: number, period: number, periods: number, pv = 0, fv = 0, type = 0): number => {
|
|
63
|
+
|
|
64
|
+
// invalid
|
|
65
|
+
if (period < 1) { return NaN; }
|
|
66
|
+
|
|
67
|
+
// if payment is at the start of the period, there's no interest in payment 1
|
|
68
|
+
if (period === 1 && type) {
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const total_payment = payment_function(rate, periods, pv, fv, type);
|
|
73
|
+
const interest = fv_function(rate, period - 1, total_payment, pv, type) * rate;
|
|
74
|
+
|
|
75
|
+
// for payments at start of period, after period 1, we need to discount
|
|
76
|
+
return type ? interest / (1 + rate) : interest;
|
|
77
|
+
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const ppmt_function = (rate: number, period: number, periods: number, pv = 0, fv = 0, type = 0): number => {
|
|
81
|
+
return payment_function(rate, periods, pv, fv, type) -
|
|
82
|
+
ipmt_function(rate, period, periods, pv, fv, type);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const FinanceFunctionLibrary: FunctionMap = {
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Excel's NPV function is somewhat broken because it assumes the first
|
|
89
|
+
* (usually negative) cashflow is in year 1, not year 0. so the thing to
|
|
90
|
+
* do is just use it on the future cashflows and add the initial outlay
|
|
91
|
+
* as a scalar value.
|
|
92
|
+
*/
|
|
93
|
+
NPV: {
|
|
94
|
+
description: 'Returns the present value of a series of future cashflows',
|
|
95
|
+
arguments: [
|
|
96
|
+
{ name: 'Rate' },
|
|
97
|
+
{ name: 'Cashflow' },
|
|
98
|
+
],
|
|
99
|
+
fn: (rate = 0, ...args: any[]): UnionValue => {
|
|
100
|
+
|
|
101
|
+
let result = 0;
|
|
102
|
+
|
|
103
|
+
const flat = FlattenUnboxed(args);
|
|
104
|
+
for (let i = 0; i < flat.length; i++) {
|
|
105
|
+
const arg = flat[i];
|
|
106
|
+
if (typeof arg === 'number') {
|
|
107
|
+
result += Math.pow(1 + rate, -(i + 1)) * arg;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
type: ValueType.number, value: result,
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
IRR: {
|
|
118
|
+
description: 'Calculates the internal rate of return of a series of cashflows',
|
|
119
|
+
arguments: [
|
|
120
|
+
{ name: 'Cashflows' },
|
|
121
|
+
{ name: 'Guess', default: .1 },
|
|
122
|
+
],
|
|
123
|
+
fn: (args: CellValue[], guess = .1): UnionValue => {
|
|
124
|
+
|
|
125
|
+
const flat = FlattenUnboxed(args).map(value => typeof value === 'number' ? value : 0);
|
|
126
|
+
|
|
127
|
+
const step = .1; // initial step
|
|
128
|
+
|
|
129
|
+
const bounds = [
|
|
130
|
+
{found: false, value: 0},
|
|
131
|
+
{found: false, value: 0},
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
// FIXME: parameterize max step count, resolution?
|
|
135
|
+
|
|
136
|
+
for (let i = 0; i < 50; i++) {
|
|
137
|
+
|
|
138
|
+
// calculate npv
|
|
139
|
+
let npv = 0;
|
|
140
|
+
for (let j = 0; j < flat.length; j++) { npv += Math.pow(1 + guess, -(j + 1)) * flat[j]; }
|
|
141
|
+
|
|
142
|
+
if (Math.abs(npv) <= 0.00125) { // resolution
|
|
143
|
+
// console.info(`** found in ${i + 1} steps`)
|
|
144
|
+
return {
|
|
145
|
+
type: ValueType.number,
|
|
146
|
+
value: guess,
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// search space is unbounded, unfortunately. we can expand exponentially
|
|
151
|
+
// until we have bounds, at which point it's a standard bounded binary search
|
|
152
|
+
|
|
153
|
+
// ...or we can expand linearly, using a reasonable initial step size?
|
|
154
|
+
|
|
155
|
+
if (npv > 0) {
|
|
156
|
+
bounds[0].value = bounds[0].found ? Math.max(bounds[0].value, guess) : guess;
|
|
157
|
+
bounds[0].found = true;
|
|
158
|
+
if (!bounds[1].found) {
|
|
159
|
+
guess += step;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
bounds[1].value = bounds[1].found ? Math.min(bounds[1].value, guess) : guess;
|
|
165
|
+
bounds[1].found = true;
|
|
166
|
+
if (!bounds[0].found) {
|
|
167
|
+
guess -= step;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
guess = bounds[0].value + (bounds[1].value - bounds[0].value) / 2;
|
|
173
|
+
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
type: ValueType.error,
|
|
178
|
+
value: 'NUM',
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
CUMPRINC: {
|
|
184
|
+
description: 'Returns cumulative principal paid on a loan between two periods',
|
|
185
|
+
arguments: [
|
|
186
|
+
{ name: 'Rate', },
|
|
187
|
+
{ name: 'Periods', },
|
|
188
|
+
{ name: 'Present Value' },
|
|
189
|
+
{ name: 'Start Period' },
|
|
190
|
+
{ name: 'End Period' },
|
|
191
|
+
{ name: 'Type', default: 0 },
|
|
192
|
+
],
|
|
193
|
+
fn: (rate: number, periods: number, pv: number, start: number, end: number, type = 0): UnionValue => {
|
|
194
|
+
let accum = 0;
|
|
195
|
+
for (let i = start; i <= end; i++ ) {
|
|
196
|
+
accum += ppmt_function(rate, i, periods, pv, 0, type);
|
|
197
|
+
}
|
|
198
|
+
return { type: ValueType.number, value: accum };
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
CUMIPMT: {
|
|
204
|
+
description: 'Returns cumulative interest paid on a loan between two periods',
|
|
205
|
+
arguments: [
|
|
206
|
+
{ name: 'Rate', },
|
|
207
|
+
{ name: 'Periods', },
|
|
208
|
+
{ name: 'Present Value' },
|
|
209
|
+
{ name: 'Start Period' },
|
|
210
|
+
{ name: 'End Period' },
|
|
211
|
+
{ name: 'Type', default: 0 },
|
|
212
|
+
],
|
|
213
|
+
fn: (rate: number, periods: number, pv: number, start: number, end: number, type = 0): UnionValue => {
|
|
214
|
+
let accum = 0;
|
|
215
|
+
for (let i = start; i <= end; i++ ) {
|
|
216
|
+
accum += ipmt_function(rate, i, periods, pv, 0, type);
|
|
217
|
+
}
|
|
218
|
+
return { type: ValueType.number, value: accum };
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
IPMT: {
|
|
224
|
+
description: 'Returns the interest portion of a payment',
|
|
225
|
+
arguments: [
|
|
226
|
+
{ name: 'Rate', },
|
|
227
|
+
{ name: 'Period', },
|
|
228
|
+
{ name: 'Periods', },
|
|
229
|
+
{ name: 'Present Value', default: 0 },
|
|
230
|
+
{ name: 'Future Value', default: 0 },
|
|
231
|
+
{ name: 'Type', default: 0 },
|
|
232
|
+
],
|
|
233
|
+
fn: (rate: number, period: number, periods: number, pv = 0, fv = 0, type = 0): UnionValue => {
|
|
234
|
+
return { type: ValueType.number, value: ipmt_function(rate, period, periods, pv, fv, type) };
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
PPMT: {
|
|
239
|
+
description: 'Returns the principal portion of a payment',
|
|
240
|
+
arguments: [
|
|
241
|
+
{ name: 'Rate', },
|
|
242
|
+
{ name: 'Period', },
|
|
243
|
+
{ name: 'Periods', },
|
|
244
|
+
{ name: 'Present Value', default: 0 },
|
|
245
|
+
{ name: 'Future Value', default: 0 },
|
|
246
|
+
{ name: 'Type', default: 0 },
|
|
247
|
+
],
|
|
248
|
+
fn: (rate: number, period: number, periods: number, pv = 0, fv = 0, type = 0): UnionValue => {
|
|
249
|
+
return { type: ValueType.number, value: ppmt_function(rate, period, periods, pv, fv, type) };
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
Rate: {
|
|
254
|
+
description: 'Returns the interest rate of a loan',
|
|
255
|
+
arguments: [
|
|
256
|
+
{ name: 'Periods', },
|
|
257
|
+
{ name: 'Payment', },
|
|
258
|
+
{ name: 'Present Value', default: 0 },
|
|
259
|
+
{ name: 'Future Value', default: 0 },
|
|
260
|
+
{ name: 'Type', default: 0 },
|
|
261
|
+
],
|
|
262
|
+
fn: (periods: number, payment: number, pv = 0, fv = 0, type = 0): UnionValue => {
|
|
263
|
+
|
|
264
|
+
let rate = .25; // guess
|
|
265
|
+
const bounds = [-1, 1];
|
|
266
|
+
|
|
267
|
+
const steps = 32; // max iterations
|
|
268
|
+
const epsilon = 1e-6;
|
|
269
|
+
|
|
270
|
+
for (let i = 0; i < steps; i++) {
|
|
271
|
+
|
|
272
|
+
const a = payment_function(rate, periods, pv, fv, type);
|
|
273
|
+
|
|
274
|
+
if (Math.abs(a - payment) <= epsilon) {
|
|
275
|
+
return { type: ValueType.number, value: rate };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const b = payment_function(bounds[1], periods, pv, fv, type);
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
if ((payment >= a && payment <= b) || (payment >= b && payment <= a)) {
|
|
282
|
+
bounds[0] = rate;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
bounds[1] = rate;
|
|
286
|
+
}
|
|
287
|
+
rate = bounds[0] + (bounds[1] - bounds[0]) / 2;
|
|
288
|
+
|
|
289
|
+
/*
|
|
290
|
+
const test = payment_function(rate, periods, pv, fv, type);
|
|
291
|
+
console.info("R", rate, "TP", test, payment, "d", Math.abs(payment-test), bounds);
|
|
292
|
+
|
|
293
|
+
if (Math.abs(payment - test) < epsilon) {
|
|
294
|
+
return { type: ValueType.number, value: rate };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if ((test < payment && payment > 0) || (test > payment && payment < 0)) { // reduce rate
|
|
298
|
+
console.info("T<P");
|
|
299
|
+
const next_rate = (bounds[0] + rate) / 2;
|
|
300
|
+
bounds = [bounds[0], rate];
|
|
301
|
+
rate = next_rate;
|
|
302
|
+
}
|
|
303
|
+
else { // increase rate
|
|
304
|
+
console.info("T>=P");
|
|
305
|
+
const next_rate = (bounds[1] + rate) / 2;
|
|
306
|
+
bounds = [rate, bounds[1]];
|
|
307
|
+
rate = next_rate;
|
|
308
|
+
}
|
|
309
|
+
*/
|
|
310
|
+
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return { type: ValueType.number, value: rate };
|
|
314
|
+
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
FV: {
|
|
320
|
+
description: 'Returns the future value of an investment',
|
|
321
|
+
arguments: [
|
|
322
|
+
{ name: 'Rate', },
|
|
323
|
+
{ name: 'Periods', },
|
|
324
|
+
{ name: 'Payment', },
|
|
325
|
+
{ name: 'Present Value', default: 0 },
|
|
326
|
+
{ name: 'Type', default: 0 },
|
|
327
|
+
],
|
|
328
|
+
fn: (rate: number, periods: number, payment: number, pv = 0, type = 0): UnionValue => {
|
|
329
|
+
return { type: ValueType.number, value: fv_function(rate, periods, payment, pv, type) };
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
PV: {
|
|
334
|
+
description: 'Returns the present value of an investment',
|
|
335
|
+
arguments: [
|
|
336
|
+
{ name: 'Rate', },
|
|
337
|
+
{ name: 'Periods', },
|
|
338
|
+
{ name: 'Payment', },
|
|
339
|
+
{ name: 'Future Value', default: 0 },
|
|
340
|
+
{ name: 'Type', default: 0 },
|
|
341
|
+
],
|
|
342
|
+
fn: (rate: number, periods: number, payment: number, fv = 0, type = 0): UnionValue => {
|
|
343
|
+
if (type) {
|
|
344
|
+
payment += (fv * (1 / ((1 + rate) * ((Math.pow(1 + rate, periods) -1)/rate))));
|
|
345
|
+
return {
|
|
346
|
+
type: ValueType.number,
|
|
347
|
+
value: -(payment + payment / rate * (1 - Math.pow(1 + rate, -(periods - 1))))
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
type: ValueType.number,
|
|
352
|
+
value: -(fv + (payment / rate * (Math.pow(1 + rate, periods) - 1))) / Math.pow(1 + rate, periods)
|
|
353
|
+
};
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
NPER: {
|
|
358
|
+
description: 'Returns the number of periods of an investment',
|
|
359
|
+
arguments: [
|
|
360
|
+
{ name: 'Rate', },
|
|
361
|
+
{ name: 'Payment', },
|
|
362
|
+
{ name: 'Present Value', },
|
|
363
|
+
{ name: 'Future Value', default: 0 },
|
|
364
|
+
{ name: 'Type', default: 0 },
|
|
365
|
+
],
|
|
366
|
+
fn: (rate :number, payment: number, pv = 0, fv = 0, type = 0): UnionValue => {
|
|
367
|
+
if (type) {
|
|
368
|
+
return {
|
|
369
|
+
type: ValueType.number,
|
|
370
|
+
value: 1 + (-Math.log(1 + rate * (1 - pv / -payment)) + Math.log(1 + fv * rate / (-payment * (1 + rate)))) / Math.log(1 + rate)
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
type: ValueType.number,
|
|
375
|
+
value: (Math.log(Math.pow(1 - pv * rate / -payment, -1)) + Math.log(1 + fv * rate / -payment)) / Math.log(1 + rate)
|
|
376
|
+
};
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
PMT: {
|
|
381
|
+
description: 'Returns the periodic payment of a loan',
|
|
382
|
+
arguments: [
|
|
383
|
+
{ name: 'Rate', },
|
|
384
|
+
{ name: 'Periods', },
|
|
385
|
+
{ name: 'Present Value', },
|
|
386
|
+
{ name: 'Future Value', default: 0 },
|
|
387
|
+
{ name: 'Type', default: 0 },
|
|
388
|
+
],
|
|
389
|
+
fn: (rate: number, periods: number, pv: number, fv = 0, type = 0): UnionValue => {
|
|
390
|
+
return {
|
|
391
|
+
type: ValueType.number,
|
|
392
|
+
value: payment_function(rate, periods, pv, fv, type),
|
|
393
|
+
};
|
|
394
|
+
},
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
};
|
|
399
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
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 { UnionValue, ValueType } from 'treb-base-types';
|
|
24
|
+
import * as Utils from '../utilities';
|
|
25
|
+
|
|
26
|
+
export const InformationFunctionLibrary: FunctionMap = {
|
|
27
|
+
|
|
28
|
+
IsBlank: {
|
|
29
|
+
description: 'Returns true if the reference is blank',
|
|
30
|
+
arguments: [{
|
|
31
|
+
name: 'Reference',
|
|
32
|
+
metadata: true,
|
|
33
|
+
}],
|
|
34
|
+
fn: Utils.ApplyAsArray((ref: UnionValue): UnionValue => {
|
|
35
|
+
return {
|
|
36
|
+
type: ValueType.boolean,
|
|
37
|
+
value: !ref?.value || typeof ref.value.value === 'undefined',
|
|
38
|
+
};
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
IsNumber: {
|
|
43
|
+
description: 'Returns true if the reference is a number',
|
|
44
|
+
arguments: [{
|
|
45
|
+
name: 'Reference',
|
|
46
|
+
metadata: true,
|
|
47
|
+
}],
|
|
48
|
+
fn: Utils.ApplyAsArray((ref: UnionValue): UnionValue => {
|
|
49
|
+
return {
|
|
50
|
+
type: ValueType.boolean,
|
|
51
|
+
value: ref?.value && typeof ref.value.value === 'number',
|
|
52
|
+
};
|
|
53
|
+
}),
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
IsLogical: {
|
|
57
|
+
description: 'Returns true if the reference is a logical TRUE or FALSE',
|
|
58
|
+
arguments: [{
|
|
59
|
+
name: 'Reference',
|
|
60
|
+
metadata: true,
|
|
61
|
+
}],
|
|
62
|
+
fn: Utils.ApplyAsArray((ref: UnionValue): UnionValue => {
|
|
63
|
+
return {
|
|
64
|
+
type: ValueType.boolean,
|
|
65
|
+
value: ref?.value && typeof ref.value.value === 'boolean',
|
|
66
|
+
};
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
IsText: {
|
|
71
|
+
description: 'Returns true if the reference is text',
|
|
72
|
+
arguments: [{
|
|
73
|
+
name: 'Reference',
|
|
74
|
+
metadata: true,
|
|
75
|
+
}],
|
|
76
|
+
fn: Utils.ApplyAsArray((ref: UnionValue): UnionValue => {
|
|
77
|
+
return {
|
|
78
|
+
type: ValueType.boolean,
|
|
79
|
+
value: ref?.value && typeof ref.value.value === 'string'
|
|
80
|
+
};
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
/* needs more data
|
|
85
|
+
ISFORMULA: {
|
|
86
|
+
description: 'Returns true if the reference is a formula',
|
|
87
|
+
arguments: [{
|
|
88
|
+
name: 'Reference',
|
|
89
|
+
metadata: true,
|
|
90
|
+
}],
|
|
91
|
+
fn: (ref: UnionValue): UnionValue => {
|
|
92
|
+
console.info("RR", ref);
|
|
93
|
+
return {
|
|
94
|
+
type: ValueType.boolean,
|
|
95
|
+
value: ref?.value && typeof ref.value.value === 'string' && ref.value.value[0] === '=',
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
};
|
|
102
|
+
|
|
@@ -0,0 +1,182 @@
|
|
|
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 { Complex, UnionValue, ValueType, ComplexOrReal } from 'treb-base-types';
|
|
24
|
+
import { ValueError } from '../function-error';
|
|
25
|
+
import * as ComplexMath from '../complex-math';
|
|
26
|
+
|
|
27
|
+
import type { ComplexMatrixType } from '../complex-math';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* given a range of data, ensure it's an array, check dimensions, and
|
|
31
|
+
* convert all real entries to complex (if possible).
|
|
32
|
+
*/
|
|
33
|
+
const ComplexMatrix = (input: UnionValue): ComplexMatrixType => {
|
|
34
|
+
|
|
35
|
+
let a: UnionValue[][] = [];
|
|
36
|
+
|
|
37
|
+
// ensure array
|
|
38
|
+
|
|
39
|
+
if (input.type === ValueType.array) {
|
|
40
|
+
a = input.value;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
a = [[input.value]];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// if (!Array.isArray(a)) { a = [[a]]; }
|
|
47
|
+
|
|
48
|
+
const m = a.length;
|
|
49
|
+
const n = m ? a[0].length : 0;
|
|
50
|
+
const array: Complex[][] = [];
|
|
51
|
+
|
|
52
|
+
// 0-length
|
|
53
|
+
if (!m || !n) {
|
|
54
|
+
return { m, n, array };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < m; i++) {
|
|
58
|
+
const row: Complex[] = [];
|
|
59
|
+
for (let j = 0; j < n; j++) {
|
|
60
|
+
const ref = a[i][j];
|
|
61
|
+
if (ref.type === ValueType.complex) {
|
|
62
|
+
row.push({...ref.value});
|
|
63
|
+
}
|
|
64
|
+
else if (ref.type === ValueType.number) {
|
|
65
|
+
row.push({real: ref.value, imaginary: 0});
|
|
66
|
+
}
|
|
67
|
+
else if (ref.type === ValueType.undefined || ref.value === '') {
|
|
68
|
+
row.push({real: 0, imaginary: 0});
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
return {m, n, error: true, array: []};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
array.push(row);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { m, n, array };
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const MatrixFunctionLibrary: FunctionMap = {
|
|
82
|
+
|
|
83
|
+
MDeterm: {
|
|
84
|
+
description: 'Returns the determinant of a matrix',
|
|
85
|
+
arguments: [
|
|
86
|
+
{ name: 'matrix', boxed: true },
|
|
87
|
+
],
|
|
88
|
+
fn: (a: UnionValue): UnionValue => {
|
|
89
|
+
|
|
90
|
+
const matrix = ComplexMatrix(a);
|
|
91
|
+
|
|
92
|
+
if (!matrix.array || !matrix.m || !matrix.n || matrix.m !== matrix.n || matrix.error) {
|
|
93
|
+
return ValueError();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const result = ComplexMath.ComplexDeterminant(matrix);
|
|
97
|
+
|
|
98
|
+
if (!result) {
|
|
99
|
+
return ValueError();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return ComplexOrReal(result);
|
|
103
|
+
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
MInverse: {
|
|
108
|
+
description: 'Returns the inverse matrix',
|
|
109
|
+
arguments: [
|
|
110
|
+
{ name: 'matrix', boxed: true },
|
|
111
|
+
],
|
|
112
|
+
fn: (a: UnionValue): UnionValue => {
|
|
113
|
+
const matrix = ComplexMatrix(a);
|
|
114
|
+
|
|
115
|
+
if (!matrix.array || !matrix.m || !matrix.n || matrix.m !== matrix.n || matrix.error) {
|
|
116
|
+
return ValueError();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// check singular
|
|
120
|
+
const det = ComplexMath.ComplexDeterminant(matrix);
|
|
121
|
+
if (det && (det.real === 0 && det?.imaginary === 0)) {
|
|
122
|
+
return ValueError();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const inverse = ComplexMath.MatrixInverse(matrix);
|
|
126
|
+
|
|
127
|
+
if (!inverse) {
|
|
128
|
+
return ValueError();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
type: ValueType.array,
|
|
133
|
+
value: inverse.map(row => row.map(value => ComplexOrReal(value))),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// return inverse.map(row => row.map(value => ComplexOrReal(value)));
|
|
137
|
+
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
MMult: {
|
|
142
|
+
description: 'Returns the dot product of A and B',
|
|
143
|
+
arguments: [
|
|
144
|
+
{ name: 'A', boxed: true },
|
|
145
|
+
{ name: 'B', boxed: true },
|
|
146
|
+
],
|
|
147
|
+
fn: (a: UnionValue, b: UnionValue): UnionValue => {
|
|
148
|
+
|
|
149
|
+
const A = ComplexMatrix(a);
|
|
150
|
+
const B = ComplexMatrix(b);
|
|
151
|
+
|
|
152
|
+
// check data
|
|
153
|
+
if (!A.array || A.error || !B.array || B.error) {
|
|
154
|
+
return ValueError();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// check sizes
|
|
158
|
+
if (A.m !== B.n) {
|
|
159
|
+
return ValueError();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// NOTE the swap here. the input arrays are column-major,
|
|
163
|
+
// so we either need to transpose three times, or we can
|
|
164
|
+
// swap.
|
|
165
|
+
|
|
166
|
+
const product = ComplexMath.ComplexProduct(B, A);
|
|
167
|
+
|
|
168
|
+
// convert to reals where possible
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
type: ValueType.array,
|
|
172
|
+
value: product.map(row => row.map(value => ComplexOrReal(value))),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// return product.map(row => row.map(value => ComplexOrReal(value)));
|
|
176
|
+
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
}
|