@trebco/treb 32.7.1 → 32.9.3
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/dist/treb-export-worker.mjs +2 -2
- package/dist/treb-spreadsheet.mjs +12 -12
- package/dist/treb.d.ts +1 -1
- package/package.json +1 -1
- package/treb-calculator/src/calculator.ts +133 -38
- package/treb-calculator/src/functions/base-functions.ts +20 -0
- package/treb-calculator/src/functions/beta.ts +15 -1
- package/treb-calculator/src/functions/normal.ts +37 -0
- package/treb-calculator/src/functions/statistics-functions.ts +69 -16
- package/treb-calculator/src/functions/students-t.ts +85 -0
- package/treb-embed/src/embedded-spreadsheet.ts +5 -0
- package/treb-embed/style/grid.scss +5 -0
- package/treb-grid/src/types/grid.ts +4 -0
- package/treb-grid/src/util/ua.ts +7 -3
- package/treb-parser/src/parser.ts +17 -7
package/dist/treb.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -273,13 +273,32 @@ export class Calculator extends Graph {
|
|
|
273
273
|
* this is a function that does sumif/averageif/countif.
|
|
274
274
|
* args is one or more sets of [criteria_range, criteria]
|
|
275
275
|
*/
|
|
276
|
-
const XIf = (type: 'sum'|'count'|'average', value_range: CellValue[][], ...args: unknown[]): UnionValue => {
|
|
276
|
+
const XIf = (type: 'sum'|'count'|'average', value_range: CellValue[][]|CellValue, ...args: unknown[]): UnionValue => {
|
|
277
|
+
|
|
278
|
+
// there's a bug here if the value range is a single value?
|
|
279
|
+
// that happens if countif passes in a one-cell range... we should
|
|
280
|
+
// handle this in the caller, or here?
|
|
281
|
+
|
|
282
|
+
// NOTE we also have to address this in the set of
|
|
283
|
+
// arguments, in which each pair could have a single
|
|
284
|
+
// value as the criterion
|
|
285
|
+
|
|
286
|
+
if (!Array.isArray(value_range)) {
|
|
287
|
+
value_range = [[value_range]];
|
|
288
|
+
}
|
|
277
289
|
|
|
278
290
|
const filter: boolean[] = [];
|
|
279
291
|
|
|
280
292
|
for (let i = 0; i < args.length; i += 2) {
|
|
281
|
-
|
|
282
|
-
|
|
293
|
+
|
|
294
|
+
let criteria_range = args[i] as (CellValue|CellValue[][]);
|
|
295
|
+
if (!Array.isArray(criteria_range)) {
|
|
296
|
+
criteria_range = [[criteria_range]];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
{ // if (Array.isArray(args[i])) {
|
|
300
|
+
|
|
301
|
+
const step = CountIfInternal(criteria_range, args[i+1] as CellValue);
|
|
283
302
|
if (step.type !== ValueType.array) {
|
|
284
303
|
return step;
|
|
285
304
|
}
|
|
@@ -995,65 +1014,141 @@ export class Calculator extends Graph {
|
|
|
995
1014
|
|
|
996
1015
|
/**
|
|
997
1016
|
* FIXME: there are cases we are not handling
|
|
1017
|
+
*
|
|
1018
|
+
* update to return a reference so you can use it as part of a
|
|
1019
|
+
* range. we're not handling literal arrays atm.
|
|
998
1020
|
*/
|
|
999
1021
|
Index: {
|
|
1022
|
+
return_type: 'reference',
|
|
1000
1023
|
arguments: [
|
|
1001
|
-
{ name: 'range',
|
|
1024
|
+
{ name: 'range', metadata: true, },
|
|
1002
1025
|
{ name: 'row', },
|
|
1003
1026
|
{ name: 'column', }
|
|
1004
1027
|
],
|
|
1028
|
+
|
|
1029
|
+
// volatile: true, // not sure this is necessary bc input is the range
|
|
1005
1030
|
volatile: false,
|
|
1006
1031
|
|
|
1007
1032
|
// FIXME: handle full row, full column calls
|
|
1008
|
-
fn: (
|
|
1033
|
+
fn: (range: UnionValue, row: number|undefined, column: number|undefined) => {
|
|
1009
1034
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
data = {
|
|
1013
|
-
type: ValueType.array,
|
|
1014
|
-
value: [[data]],
|
|
1015
|
-
};
|
|
1035
|
+
if (!range) {
|
|
1036
|
+
return ArgumentError();
|
|
1016
1037
|
}
|
|
1017
1038
|
|
|
1018
|
-
|
|
1039
|
+
// this is illegal, although we could just default to zeros
|
|
1019
1040
|
|
|
1020
|
-
|
|
1041
|
+
if (row === undefined && column === undefined) {
|
|
1042
|
+
return ArgumentError();
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
row = row || 0;
|
|
1046
|
+
column = column || 0;
|
|
1047
|
+
|
|
1048
|
+
let arr: { value: {address: UnitAddress }}[][] = [];
|
|
1049
|
+
|
|
1050
|
+
// NOTE: could be a single cell. in that case it won't be passed
|
|
1051
|
+
// as an array so we'll need a second branch (or convert it)
|
|
1052
|
+
|
|
1053
|
+
if (range.type === ValueType.array) {
|
|
1054
|
+
|
|
1055
|
+
// FIXME: validate these are addresses (shouldn't be necessary
|
|
1056
|
+
// if we're marking the argument as metadata? what about literals?)
|
|
1057
|
+
|
|
1058
|
+
// check rows and columns. we might need to return an array.
|
|
1059
|
+
|
|
1060
|
+
arr = range.value as unknown as { value: {address: UnitAddress }}[][];
|
|
1021
1061
|
|
|
1022
|
-
const c = data.value[column - 1];
|
|
1023
|
-
if (c) {
|
|
1024
|
-
const cell = c[row - 1];
|
|
1025
|
-
if (cell) {
|
|
1026
|
-
return cell;
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
1062
|
}
|
|
1030
|
-
else if (
|
|
1063
|
+
else if (range.type === ValueType.object) {
|
|
1031
1064
|
|
|
1032
|
-
|
|
1065
|
+
const metadata = range.value as { type: string, address?: UnitAddress };
|
|
1033
1066
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
if (!c[row - 1]) {
|
|
1037
|
-
return ArgumentError();
|
|
1038
|
-
}
|
|
1039
|
-
value.push([c[row-1]]);
|
|
1067
|
+
if (metadata.type === 'metadata' && metadata.address) {
|
|
1068
|
+
arr.push([range as { value: { address: UnitAddress }}]);
|
|
1040
1069
|
}
|
|
1070
|
+
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
const columns = arr.length;
|
|
1074
|
+
const rows = arr[0]?.length || 0;
|
|
1075
|
+
|
|
1076
|
+
if (rows <= 0 || columns <= 0 || row > rows || column > columns || row < 0 || column < 0) {
|
|
1077
|
+
return ArgumentError();
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
// return everything if arguments are (0, 0)
|
|
1082
|
+
|
|
1083
|
+
if (column === 0 && row === 0) {
|
|
1084
|
+
|
|
1085
|
+
// because of the way we're structured this we might be
|
|
1086
|
+
// returning a range of length 1; in that case we want
|
|
1087
|
+
// to return it as an address
|
|
1088
|
+
|
|
1089
|
+
const expression: ExpressionUnit = (columns === 1 && rows === 1) ? {
|
|
1090
|
+
...arr[0][0].value.address,
|
|
1091
|
+
} : {
|
|
1092
|
+
type: 'range',
|
|
1093
|
+
start: arr[0][0].value.address,
|
|
1094
|
+
end: arr[columns-1][rows-1].value.address,
|
|
1095
|
+
label: '', position: 0,
|
|
1096
|
+
id: 0,
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1041
1099
|
return {
|
|
1042
|
-
type: ValueType.
|
|
1043
|
-
value,
|
|
1100
|
+
type: ValueType.object,
|
|
1101
|
+
value: expression,
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// single cell
|
|
1107
|
+
|
|
1108
|
+
if ((row || rows === 1) && (column || columns === 1)) {
|
|
1109
|
+
return {
|
|
1110
|
+
type: ValueType.object,
|
|
1111
|
+
value: arr[column ? column - 1 : 0][row ? row - 1 : 0].value.address,
|
|
1044
1112
|
};
|
|
1045
1113
|
}
|
|
1114
|
+
|
|
1115
|
+
// sub array
|
|
1116
|
+
|
|
1117
|
+
if (row) {
|
|
1118
|
+
|
|
1119
|
+
// return column
|
|
1120
|
+
|
|
1121
|
+
const expression: ExpressionUnit = {
|
|
1122
|
+
type: 'range',
|
|
1123
|
+
start: arr[0][row - 1].value.address,
|
|
1124
|
+
end: arr[columns-1][row-1].value.address,
|
|
1125
|
+
label: '', position: 0,
|
|
1126
|
+
id: 0,
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1129
|
+
return {
|
|
1130
|
+
type: ValueType.object,
|
|
1131
|
+
value: expression,
|
|
1132
|
+
};
|
|
1133
|
+
|
|
1134
|
+
}
|
|
1046
1135
|
else if (column) {
|
|
1047
1136
|
|
|
1048
|
-
// return
|
|
1137
|
+
// return row
|
|
1138
|
+
|
|
1139
|
+
const expression: ExpressionUnit = {
|
|
1140
|
+
type: 'range',
|
|
1141
|
+
start: arr[column - 1][0].value.address,
|
|
1142
|
+
end: arr[column-1][rows-1].value.address,
|
|
1143
|
+
label: '', position: 0,
|
|
1144
|
+
id: 0,
|
|
1145
|
+
};
|
|
1146
|
+
|
|
1147
|
+
return {
|
|
1148
|
+
type: ValueType.object,
|
|
1149
|
+
value: expression,
|
|
1150
|
+
};
|
|
1049
1151
|
|
|
1050
|
-
const c = data.value[column - 1];
|
|
1051
|
-
if (c) {
|
|
1052
|
-
return {
|
|
1053
|
-
type: ValueType.array,
|
|
1054
|
-
value: [c],
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
1152
|
}
|
|
1058
1153
|
|
|
1059
1154
|
return ArgumentError();
|
|
@@ -959,6 +959,26 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
959
959
|
},
|
|
960
960
|
},
|
|
961
961
|
|
|
962
|
+
Factdouble: {
|
|
963
|
+
description: 'Returns the double factorial of a number',
|
|
964
|
+
arguments: [
|
|
965
|
+
{ name: 'number', unroll: true },
|
|
966
|
+
],
|
|
967
|
+
fn: (number: number): UnionValue => {
|
|
968
|
+
number = Math.round(number);
|
|
969
|
+
|
|
970
|
+
let value = 1;
|
|
971
|
+
while (number > 1) {
|
|
972
|
+
value *= number;
|
|
973
|
+
number -= 2;
|
|
974
|
+
}
|
|
975
|
+
return {
|
|
976
|
+
type: ValueType.number,
|
|
977
|
+
value,
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
},
|
|
981
|
+
|
|
962
982
|
Power: {
|
|
963
983
|
description: 'Returns base raised to the given power',
|
|
964
984
|
arguments: [
|
|
@@ -32,7 +32,7 @@ const Log1p = (x: number) => {
|
|
|
32
32
|
/**
|
|
33
33
|
* continued fraction expansion
|
|
34
34
|
*/
|
|
35
|
-
const BetaContFrac = (a: number, b: number, x: number, epsabs: number) => {
|
|
35
|
+
export const BetaContFrac = (a: number, b: number, x: number, epsabs: number) => {
|
|
36
36
|
|
|
37
37
|
const cutoff = 2.0 * Number.MIN_VALUE;
|
|
38
38
|
|
|
@@ -242,3 +242,17 @@ export const BetaCDF = (x: number, a: number, b: number) => {
|
|
|
242
242
|
|
|
243
243
|
};
|
|
244
244
|
|
|
245
|
+
export const BetaInc = (x: number, a: number, b: number): number => {
|
|
246
|
+
if (x < 0 || x > 1) return 0; // throw new Error("Invalid x in betainc");
|
|
247
|
+
const bt =
|
|
248
|
+
x === 0 || x === 1
|
|
249
|
+
? 0
|
|
250
|
+
: Math.exp(LnGamma(a + b) - LnGamma(a) - LnGamma(b) + a * Math.log(x) + b * Math.log(1 - x));
|
|
251
|
+
if (x < (a + 1) / (a + b + 2)) {
|
|
252
|
+
return bt * BetaContFrac(a, b, x, 0) / a;
|
|
253
|
+
} else {
|
|
254
|
+
return 1 - bt * BetaContFrac(b, a, 1 - x, 0) / b;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
/*
|
|
3
|
+
* This file is part of TREB.
|
|
4
|
+
*
|
|
5
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
6
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
7
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
8
|
+
* later version.
|
|
9
|
+
*
|
|
10
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
11
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
12
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
13
|
+
* details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU General Public License along
|
|
16
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*
|
|
18
|
+
* Copyright 2022-2025 trebco, llc.
|
|
19
|
+
* info@treb.app
|
|
20
|
+
*
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** imprecise but reasonably fast normsinv function */
|
|
24
|
+
export const InverseNormal = (q: number): number => {
|
|
25
|
+
|
|
26
|
+
if (q === 0.50) {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const p = (q < 1.0 && q > 0.5) ? (1 - q) : q;
|
|
31
|
+
const t = Math.sqrt(Math.log(1.0 / Math.pow(p, 2.0)));
|
|
32
|
+
const x = t - (2.515517 + 0.802853 * t + 0.010328 * Math.pow(t, 2.0)) /
|
|
33
|
+
(1.0 + 1.432788 * t + 0.189269 * Math.pow(t, 2.0) + 0.001308 * Math.pow(t, 3.0));
|
|
34
|
+
|
|
35
|
+
return (q > 0.5 ? x : -x);
|
|
36
|
+
|
|
37
|
+
};
|
|
@@ -27,6 +27,8 @@ import * as ComplexMath from '../complex-math';
|
|
|
27
27
|
|
|
28
28
|
import { BetaCDF, BetaPDF, InverseBeta, LnGamma } from './beta';
|
|
29
29
|
import { gamma_p } from './gamma';
|
|
30
|
+
import { InverseNormal } from './normal';
|
|
31
|
+
import { tCDF, tInverse, tPDF } from './students-t';
|
|
30
32
|
|
|
31
33
|
/** error function (for gaussian distribution) */
|
|
32
34
|
const erf = (x: number): number => {
|
|
@@ -62,21 +64,7 @@ const norm_dist = (x: number, mean: number, stdev: number, cumulative: boolean)
|
|
|
62
64
|
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
/** imprecise but reasonably fast normsinv function */
|
|
66
|
-
const inverse_normal = (q: number): number => {
|
|
67
67
|
|
|
68
|
-
if (q === 0.50) {
|
|
69
|
-
return 0;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const p = (q < 1.0 && q > 0.5) ? (1 - q) : q;
|
|
73
|
-
const t = Math.sqrt(Math.log(1.0 / Math.pow(p, 2.0)));
|
|
74
|
-
const x = t - (2.515517 + 0.802853 * t + 0.010328 * Math.pow(t, 2.0)) /
|
|
75
|
-
(1.0 + 1.432788 * t + 0.189269 * Math.pow(t, 2.0) + 0.001308 * Math.pow(t, 3.0));
|
|
76
|
-
|
|
77
|
-
return (q > 0.5 ? x : -x);
|
|
78
|
-
|
|
79
|
-
};
|
|
80
68
|
|
|
81
69
|
const Median = (data: number[]) => {
|
|
82
70
|
const n = data.length;
|
|
@@ -478,7 +466,7 @@ export const StatisticsFunctionLibrary: FunctionMap = {
|
|
|
478
466
|
fn: (q: number, mean = 0, stdev = 1): UnionValue => {
|
|
479
467
|
return {
|
|
480
468
|
type: ValueType.number,
|
|
481
|
-
value:
|
|
469
|
+
value: InverseNormal(q) * stdev + mean,
|
|
482
470
|
}
|
|
483
471
|
}
|
|
484
472
|
},
|
|
@@ -492,7 +480,7 @@ export const StatisticsFunctionLibrary: FunctionMap = {
|
|
|
492
480
|
fn: (q: number): UnionValue => {
|
|
493
481
|
return {
|
|
494
482
|
type: ValueType.number,
|
|
495
|
-
value:
|
|
483
|
+
value: InverseNormal(q),
|
|
496
484
|
}
|
|
497
485
|
}
|
|
498
486
|
},
|
|
@@ -831,6 +819,71 @@ export const StatisticsFunctionLibrary: FunctionMap = {
|
|
|
831
819
|
}
|
|
832
820
|
},
|
|
833
821
|
|
|
822
|
+
'T.DIST': {
|
|
823
|
+
description: `Returns the left-tailed Student's t-distribution`,
|
|
824
|
+
arguments: [
|
|
825
|
+
{ name: 'X', unroll: true, boxed: true, },
|
|
826
|
+
{ name: 'degrees of freedom', unroll: true, boxed: true, },
|
|
827
|
+
{ name: 'cumulative', unroll: true, boxed: true, },
|
|
828
|
+
],
|
|
829
|
+
fn: (x: UnionValue, df: UnionValue, cumulative?: UnionValue) => {
|
|
830
|
+
|
|
831
|
+
const cum = cumulative ? !!cumulative.value : false;
|
|
832
|
+
|
|
833
|
+
if (df.type !== ValueType.number || x.type !== ValueType.number || df.value < 1) {
|
|
834
|
+
return ArgumentError();
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
return {
|
|
838
|
+
type: ValueType.number,
|
|
839
|
+
value: cum ? tCDF(x.value, df.value) : tPDF(x.value, df.value),
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
},
|
|
843
|
+
},
|
|
844
|
+
|
|
845
|
+
'T.Inv': {
|
|
846
|
+
description: `Returns the left-tailed inverse of the Student's t-distribution`,
|
|
847
|
+
arguments: [
|
|
848
|
+
{
|
|
849
|
+
name: 'Probability', boxed: true, unroll: true,
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
name: 'Degrees of freedom', boxed: true, unroll: true,
|
|
853
|
+
},
|
|
854
|
+
],
|
|
855
|
+
fn: (p: UnionValue, df: UnionValue) => {
|
|
856
|
+
if (df.type !== ValueType.number || df.value < 1 || p.type !== ValueType.number || p.value <= 0 || p.value >= 1) {
|
|
857
|
+
return ArgumentError();
|
|
858
|
+
}
|
|
859
|
+
return {
|
|
860
|
+
type: ValueType.number,
|
|
861
|
+
value: tInverse(p.value, df.value),
|
|
862
|
+
};
|
|
863
|
+
},
|
|
864
|
+
},
|
|
865
|
+
|
|
866
|
+
'T.Inv.2T': {
|
|
867
|
+
description: `Returns the two-tailed inverse of the Student's t-distribution`,
|
|
868
|
+
arguments: [
|
|
869
|
+
{
|
|
870
|
+
name: 'Probability', boxed: true, unroll: true,
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
name: 'Degrees of freedom', boxed: true, unroll: true,
|
|
874
|
+
},
|
|
875
|
+
],
|
|
876
|
+
fn: (p: UnionValue, df: UnionValue) => {
|
|
877
|
+
if (df.type !== ValueType.number || df.value < 1 ||p.type !== ValueType.number || p.value <= 0 || p.value >= 1) {
|
|
878
|
+
return ArgumentError();
|
|
879
|
+
}
|
|
880
|
+
return {
|
|
881
|
+
type: ValueType.number,
|
|
882
|
+
value: Math.abs(tInverse(1 - p.value/2, df.value)),
|
|
883
|
+
};
|
|
884
|
+
},
|
|
885
|
+
},
|
|
886
|
+
|
|
834
887
|
GammaLn: {
|
|
835
888
|
description: 'Returns the natural log of the gamma function',
|
|
836
889
|
arguments: [{ name: 'value', boxed: true, unroll: true }],
|
|
@@ -0,0 +1,85 @@
|
|
|
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-2025 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { BetaInc, LnGamma } from './beta';
|
|
23
|
+
import { InverseNormal } from './normal';
|
|
24
|
+
|
|
25
|
+
export const tCDF = (t: number, df: number) => {
|
|
26
|
+
const x = df / (df + t * t);
|
|
27
|
+
const a = df / 2;
|
|
28
|
+
const b = 0.5;
|
|
29
|
+
const ib = BetaInc(x, a, b);
|
|
30
|
+
return t >= 0 ? 1 - 0.5 * ib : 0.5 * ib;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const tPDF = (t: number, nu: number): number => {
|
|
34
|
+
|
|
35
|
+
const logNumerator = LnGamma((nu + 1) / 2);
|
|
36
|
+
const logDenominator = LnGamma(nu / 2) + 0.5 * Math.log(nu * Math.PI);
|
|
37
|
+
const logFactor = -((nu + 1) / 2) * Math.log(1 + (t * t) / nu);
|
|
38
|
+
|
|
39
|
+
const logPDF = logNumerator - logDenominator + logFactor;
|
|
40
|
+
|
|
41
|
+
return Math.exp(logPDF);
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const tInverse = (p: number, df: number) => {
|
|
46
|
+
|
|
47
|
+
const EPS = 1e-12;
|
|
48
|
+
|
|
49
|
+
let x = 1;
|
|
50
|
+
|
|
51
|
+
// Hill's approximation for initial guess
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
const t = InverseNormal(p);
|
|
55
|
+
const g1 = (Math.pow(t, 3) + t) / 4;
|
|
56
|
+
const g2 = ((5 * Math.pow(t, 5)) + (16 * Math.pow(t, 3)) + (3 * t)) / 96;
|
|
57
|
+
const g3 = ((3 * Math.pow(t, 7)) + (19 * Math.pow(t, 5)) + (17 * Math.pow(t, 3)) - 15 * t) / 384;
|
|
58
|
+
const g4 = ((79 * Math.pow(t, 9)) + (776 * Math.pow(t, 7)) + (1482 * Math.pow(t, 5)) - (1920 * Math.pow(t, 3)) - (945 * t)) / 92160;
|
|
59
|
+
|
|
60
|
+
x = t + g1 / df + g2 / Math.pow(df, 2) + g3 / Math.pow(df, 3) + g4 / Math.pow(df, 4);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (let i = 0; i < 16; i++) {
|
|
64
|
+
|
|
65
|
+
const error = tCDF(x, df) - p;
|
|
66
|
+
if (Math.abs(error) < EPS) {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const derivative = tPDF(x, df);
|
|
71
|
+
if (derivative === 0) {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const delta = error / derivative;
|
|
76
|
+
const step = Math.max(-1, Math.min(1, delta));
|
|
77
|
+
|
|
78
|
+
x -= step;
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return x;
|
|
83
|
+
|
|
84
|
+
};
|
|
85
|
+
|
|
@@ -894,6 +894,11 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
894
894
|
else if (UA.is_mac) {
|
|
895
895
|
container.parentElement?.classList.add('treb-ua-osx');
|
|
896
896
|
}
|
|
897
|
+
|
|
898
|
+
if (UA.is_iphone) {
|
|
899
|
+
container.parentElement?.classList.add('treb-ua-iphone');
|
|
900
|
+
}
|
|
901
|
+
|
|
897
902
|
}
|
|
898
903
|
|
|
899
904
|
// container is "treb-views", which contains individual "treb-view"
|
|
@@ -43,6 +43,9 @@
|
|
|
43
43
|
position: absolute;
|
|
44
44
|
pointer-events: none;
|
|
45
45
|
|
|
46
|
+
// patch for ios safari bug
|
|
47
|
+
transform: translateZ(0);
|
|
48
|
+
|
|
46
49
|
rect {
|
|
47
50
|
stroke: var(--treb-spill-border-color, rgb(92, 92, 224));
|
|
48
51
|
stroke-dasharray: var(--treb-spill-border-dasharray, 0);
|
|
@@ -311,6 +314,7 @@
|
|
|
311
314
|
background: transparent;
|
|
312
315
|
position: absolute;
|
|
313
316
|
z-index: $z-index-grid-selection;
|
|
317
|
+
transform: translateZ(0); // patch for ios safari bug
|
|
314
318
|
-moz-transform: scale(1); // firefox anti-blur
|
|
315
319
|
}
|
|
316
320
|
|
|
@@ -323,6 +327,7 @@
|
|
|
323
327
|
position: absolute;
|
|
324
328
|
z-index: $z-index-frozen-selection;
|
|
325
329
|
overflow: hidden; // needed for IE11 (put in legacy?)
|
|
330
|
+
transform: translateZ(0); // patch for ios safari bug
|
|
326
331
|
-moz-transform: scale(1); // firefox anti-blur
|
|
327
332
|
pointer-events: none;
|
|
328
333
|
|
|
@@ -7889,6 +7889,10 @@ export class Grid extends GridBase {
|
|
|
7889
7889
|
*/
|
|
7890
7890
|
protected ResizeColumnsInternal(command: ResizeColumnsCommand) {
|
|
7891
7891
|
|
|
7892
|
+
if (this.headless) {
|
|
7893
|
+
return super.ResizeColumnsInternal(command);
|
|
7894
|
+
}
|
|
7895
|
+
|
|
7892
7896
|
const sheet = command.sheet_id ? this.FindSheet(command.sheet_id) : this.active_sheet;
|
|
7893
7897
|
|
|
7894
7898
|
// normalize
|
package/treb-grid/src/util/ua.ts
CHANGED
|
@@ -39,6 +39,9 @@ class UAType {
|
|
|
39
39
|
/** more testing. ios safari doesn't support grid+sticky (apparently) */
|
|
40
40
|
public readonly is_ipad = /iPad|iPhone/.test(user_agent);
|
|
41
41
|
|
|
42
|
+
/** for iphone so we can change font size to prevent auto-zoom */
|
|
43
|
+
public readonly is_iphone = /iPhone/.test(user_agent);
|
|
44
|
+
|
|
42
45
|
/** more testing. firefox android doesn't support grid+sticky (apparently) */
|
|
43
46
|
public readonly is_android = /android|samsung/i.test(user_agent);
|
|
44
47
|
|
|
@@ -84,19 +87,20 @@ class UAType {
|
|
|
84
87
|
/webkit|firefox/i.test(user_agent);
|
|
85
88
|
}
|
|
86
89
|
|
|
87
|
-
const null_ua = {
|
|
90
|
+
const null_ua: UAType = {
|
|
88
91
|
|
|
89
92
|
is_edge: false,
|
|
90
93
|
is_ipad: false,
|
|
94
|
+
is_iphone: false,
|
|
91
95
|
is_android: false,
|
|
92
96
|
is_firefox: false,
|
|
93
97
|
is_safari: false,
|
|
94
98
|
is_mac: false,
|
|
95
99
|
is_chrome: false,
|
|
96
|
-
trident: false,
|
|
100
|
+
// trident: false,
|
|
97
101
|
is_windows: false,
|
|
98
102
|
is_modern: true,
|
|
99
|
-
is_node: true,
|
|
103
|
+
// is_node: true,
|
|
100
104
|
is_mobile: false,
|
|
101
105
|
|
|
102
106
|
};
|
|
@@ -630,13 +630,23 @@ export class Parser {
|
|
|
630
630
|
}).join(', ')).join('; ') + '}';
|
|
631
631
|
|
|
632
632
|
case 'binary':
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
633
|
+
|
|
634
|
+
// in some cases we might see range constructs as binary units
|
|
635
|
+
// because one side (or maybe both sides) of the range is a
|
|
636
|
+
// function. in that case we don't want a space in front of the
|
|
637
|
+
// operator.
|
|
638
|
+
|
|
639
|
+
{
|
|
640
|
+
const separator = (unit.operator === ':' ? '': ' ');
|
|
641
|
+
|
|
642
|
+
return (
|
|
643
|
+
this.Render(unit.left, options) +
|
|
644
|
+
separator +
|
|
645
|
+
unit.operator +
|
|
646
|
+
separator +
|
|
647
|
+
this.Render(unit.right, options)
|
|
648
|
+
);
|
|
649
|
+
}
|
|
640
650
|
|
|
641
651
|
case 'unary':
|
|
642
652
|
return (
|