@dreamforgestudiollc/emet 0.2.4 → 0.2.6
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/.env.example +53 -0
- package/CHANGELOG.md +67 -3
- package/CHARTER.md +19 -7
- package/README.md +106 -21
- package/docs/AUTHORSHIP.md +3 -3
- package/docs/BOTS.md +4 -0
- package/docs/COVERAGE.md +2 -2
- package/docs/DEPLOY.md +9 -4
- package/docs/GUARDRAILS-AND-MONORAILS.md +2 -1
- package/docs/GUIDED-SETUP.md +8 -3
- package/docs/PERMISSIONS.md +6 -3
- package/docs/ROADMAP.md +25 -22
- package/docs/SETUP.md +8 -2
- package/docs/SUBKEYS.md +59 -0
- package/gems/artifact-index.json +12 -0
- package/gems/bot-team-backup/floor.json +15 -0
- package/gems/bot-team-backup/gem.json +43 -0
- package/gems/bot-team-backup/guides/BOT_TEAM_BACKUP.md +27 -0
- package/gems/bot-team-backup/guides/BOT_TEAM_RECOVERY.md +23 -0
- package/gems/fieldnote/floor.json +14 -0
- package/gems/fieldnote/gem.json +70 -0
- package/gems/fieldnote/runes/fieldnote.js +4 -0
- package/gems/lean-six-sigma/CHANGELOG.md +10 -0
- package/gems/lean-six-sigma/README.md +92 -0
- package/gems/lean-six-sigma/floor.json +31 -0
- package/gems/lean-six-sigma/gem.json +1296 -0
- package/gems/lean-six-sigma/guides/ASSUMPTIONS.md +45 -0
- package/gems/lean-six-sigma/guides/BELTS.md +52 -0
- package/gems/lean-six-sigma/guides/DMADV.md +56 -0
- package/gems/lean-six-sigma/guides/DMAIC.md +127 -0
- package/gems/lean-six-sigma/guides/FORMULAS.md +330 -0
- package/gems/lean-six-sigma/guides/LEAN.md +97 -0
- package/gems/lean-six-sigma/guides/TEMPLATES.md +279 -0
- package/gems/lean-six-sigma/runes/lss_capability.js +132 -0
- package/gems/lean-six-sigma/runes/lss_control_limits.js +240 -0
- package/gems/lean-six-sigma/runes/lss_dpmo_sigma.js +81 -0
- package/gems/lean-six-sigma/runes/lss_hypothesis.js +298 -0
- package/gems/lean-six-sigma/runes/lss_sample_size.js +81 -0
- package/gems/neurendipity/floor.json +65 -0
- package/gems/neurendipity/gem.json +42 -0
- package/gems/neurendipity/guides/AUTISTIC_BURNOUT.md +81 -0
- package/{guides → gems/neurendipity/guides}/NEURODIVERGENT_SUPPORT.md +17 -8
- package/guides/README.md +3 -0
- package/package.json +11 -5
- package/server.json +44 -4
- package/src/aleph.js +4 -4
- package/src/annotations.js +38 -3
- package/src/atlas-role.js +1 -1
- package/src/attribution.js +555 -0
- package/src/conformance.js +2 -2
- package/src/database.js +6 -5
- package/src/dispatch.js +116 -13
- package/src/env.js +54 -9
- package/src/floor.js +82 -41
- package/src/gaps.js +39 -6
- package/src/gem-check.js +1359 -0
- package/src/guides.js +31 -4
- package/src/host-reach.js +23 -0
- package/src/http-remote.js +46 -2
- package/src/index.js +9 -2
- package/src/layers.js +1 -1
- package/src/lessons-to-checks.js +3 -3
- package/src/logger.js +2 -1
- package/src/mongo-connect.js +45 -2
- package/src/oauth-provider.js +2 -2
- package/src/owner-approvals.js +281 -0
- package/src/persons.js +20 -0
- package/src/redact.js +10 -4
- package/src/request-log.js +1 -1
- package/src/rune-dispatch.js +360 -0
- package/src/rune-registry.js +71 -0
- package/src/rune-scope.js +42 -0
- package/src/scopes.js +3 -0
- package/src/session-graph.js +25 -23
- package/src/session.js +9 -9
- package/src/setup.js +297 -30
- package/src/socket.js +545 -0
- package/src/startup-pages.js +23 -6
- package/src/startup-payload.js +9 -6
- package/src/subkey-store.js +357 -0
- package/src/subkeys.js +947 -0
- package/src/tools.js +299 -62
- package/src/transcript-session.js +1 -1
- package/src/validation.js +26 -2
- package/src/version.js +1 -1
- package/templates/BOARD.md +2 -2
- package/templates/BOTHANDOFF.md +2 -2
- package/templates/CHARACTER.md +1 -1
- package/templates/HANDOFF.md +1 -1
- package/templates/IDENTITY.md +1 -1
- package/templates/INDEX.md +19 -19
- package/templates/INSTALL.md +1 -1
- package/templates/PROCEDURE.md +1 -1
- package/templates/ROSTER.md +2 -2
- package/templates/SEED.md +2 -2
- package/templates/STAGING.md +2 -2
- package/templates/STANDARD.md +5 -5
- package/templates/THREAD.md +1 -1
- package/templates/USER.md +4 -1
- package/verification/load-gate/corpus.json +1765 -0
- package/verification/load-gate/load-gate.mjs +1246 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// Pure function of the caller's inputs. Returns numbers. Writes nothing.
|
|
2
|
+
// Factor table: NIST/SEMATECH e-Handbook 6.3.2.1, D4 for n = 5 is 2.115.
|
|
3
|
+
// Floor only R, moving range, p, np, c, and u. X-bar and individuals stay negative.
|
|
4
|
+
|
|
5
|
+
const MISSING = "missing-input";
|
|
6
|
+
const ASSUMPTION = "assumption-not-checked";
|
|
7
|
+
const FLOOR = "lcl-floored-at-zero";
|
|
8
|
+
const FACTOR = "factor-out-of-range";
|
|
9
|
+
|
|
10
|
+
const FACTORS = Object.freeze({
|
|
11
|
+
__proto__: null,
|
|
12
|
+
2: Object.freeze({ __proto__: null, a2: 1.88, d3: 0, d4: 3.267 }),
|
|
13
|
+
3: Object.freeze({ __proto__: null, a2: 1.023, d3: 0, d4: 2.575 }),
|
|
14
|
+
4: Object.freeze({ __proto__: null, a2: 0.729, d3: 0, d4: 2.282 }),
|
|
15
|
+
5: Object.freeze({ __proto__: null, a2: 0.577, d3: 0, d4: 2.115 }),
|
|
16
|
+
6: Object.freeze({ __proto__: null, a2: 0.483, d3: 0, d4: 2.004 }),
|
|
17
|
+
7: Object.freeze({ __proto__: null, a2: 0.419, d3: 0.076, d4: 1.924 }),
|
|
18
|
+
8: Object.freeze({ __proto__: null, a2: 0.373, d3: 0.136, d4: 1.864 }),
|
|
19
|
+
9: Object.freeze({ __proto__: null, a2: 0.337, d3: 0.184, d4: 1.816 }),
|
|
20
|
+
10: Object.freeze({ __proto__: null, a2: 0.308, d3: 0.223, d4: 1.777 }),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const CHARTS = Object.freeze({
|
|
24
|
+
__proto__: null,
|
|
25
|
+
"xbar-r": true,
|
|
26
|
+
"i-mr": true,
|
|
27
|
+
p: true,
|
|
28
|
+
np: true,
|
|
29
|
+
c: true,
|
|
30
|
+
u: true,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function isNum(value) {
|
|
34
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function push(flags, flag) {
|
|
38
|
+
if (flag && flags.indexOf(flag) < 0) flags.push(flag);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function empty(flags, missing, error) {
|
|
42
|
+
return {
|
|
43
|
+
ok: false,
|
|
44
|
+
flags,
|
|
45
|
+
missing,
|
|
46
|
+
wrote: false,
|
|
47
|
+
error,
|
|
48
|
+
center: null,
|
|
49
|
+
ucl: null,
|
|
50
|
+
lcl: null,
|
|
51
|
+
centerR: null,
|
|
52
|
+
uclR: null,
|
|
53
|
+
lclR: null,
|
|
54
|
+
constantsUsed: null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function fail(flags, missing, error) {
|
|
59
|
+
if (missing.length) push(flags, MISSING);
|
|
60
|
+
return empty(flags, missing, error);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function floorAtZero(value, flags) {
|
|
64
|
+
if (value < 0) {
|
|
65
|
+
push(flags, FLOOR);
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function constantsFrom(input, missing) {
|
|
72
|
+
const hasA2 = isNum(input.a2);
|
|
73
|
+
const hasD3 = isNum(input.d3);
|
|
74
|
+
const hasD4 = isNum(input.d4);
|
|
75
|
+
const any = hasA2 || hasD3 || hasD4;
|
|
76
|
+
if (any) {
|
|
77
|
+
if (!hasA2) missing.push("a2");
|
|
78
|
+
if (!hasD3) missing.push("d3");
|
|
79
|
+
if (!hasD4) missing.push("d4");
|
|
80
|
+
if (missing.length) return null;
|
|
81
|
+
return { a2: input.a2, d3: input.d3, d4: input.d4, fromTable: false };
|
|
82
|
+
}
|
|
83
|
+
if (!isNum(input.n) || !Number.isInteger(input.n)) return false;
|
|
84
|
+
const key = String(input.n);
|
|
85
|
+
if (!Object.hasOwn(FACTORS, key)) return false;
|
|
86
|
+
return { a2: FACTORS[key].a2, d3: FACTORS[key].d3, d4: FACTORS[key].d4, fromTable: true };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function run(input) {
|
|
90
|
+
const flags = [];
|
|
91
|
+
const missing = [];
|
|
92
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
93
|
+
missing.push("chart");
|
|
94
|
+
return fail(flags, missing, "Missing chart.");
|
|
95
|
+
}
|
|
96
|
+
const chart = String(input.chart);
|
|
97
|
+
if (!Object.hasOwn(CHARTS, chart)) {
|
|
98
|
+
missing.push("chart");
|
|
99
|
+
return fail(flags, missing, "Missing chart.");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (chart === "xbar-r") {
|
|
103
|
+
if (!isNum(input.xDoubleBar)) missing.push("xDoubleBar");
|
|
104
|
+
if (!isNum(input.rBar)) missing.push("rBar");
|
|
105
|
+
const constants = constantsFrom(input, missing);
|
|
106
|
+
if (constants === false) {
|
|
107
|
+
if (isNum(input.n)) push(flags, FACTOR);
|
|
108
|
+
else missing.push("n");
|
|
109
|
+
const error = isNum(input.n)
|
|
110
|
+
? "Subgroup size is outside the factor table."
|
|
111
|
+
: "Missing " + missing.join(", ") + ".";
|
|
112
|
+
return fail(flags, missing, error);
|
|
113
|
+
}
|
|
114
|
+
if (missing.length || constants === null) {
|
|
115
|
+
return fail(flags, missing, "Missing " + missing.join(", ") + ".");
|
|
116
|
+
}
|
|
117
|
+
if (!(input.rBar >= 0)) {
|
|
118
|
+
missing.push("rBar");
|
|
119
|
+
return fail(flags, missing, "rBar must be greater than or equal to 0.");
|
|
120
|
+
}
|
|
121
|
+
const span = constants.a2 * input.rBar;
|
|
122
|
+
push(flags, ASSUMPTION);
|
|
123
|
+
return {
|
|
124
|
+
ok: true,
|
|
125
|
+
flags,
|
|
126
|
+
missing,
|
|
127
|
+
wrote: false,
|
|
128
|
+
error: null,
|
|
129
|
+
center: input.xDoubleBar,
|
|
130
|
+
ucl: input.xDoubleBar + span,
|
|
131
|
+
lcl: input.xDoubleBar - span,
|
|
132
|
+
centerR: input.rBar,
|
|
133
|
+
uclR: constants.d4 * input.rBar,
|
|
134
|
+
lclR: floorAtZero(constants.d3 * input.rBar, flags),
|
|
135
|
+
constantsUsed: constants,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (chart === "i-mr") {
|
|
140
|
+
if (!isNum(input.xBar)) missing.push("xBar");
|
|
141
|
+
if (!isNum(input.mrBar)) missing.push("mrBar");
|
|
142
|
+
if (missing.length) {
|
|
143
|
+
return fail(flags, missing, "Missing " + missing.join(", ") + ".");
|
|
144
|
+
}
|
|
145
|
+
if (!(input.mrBar >= 0)) {
|
|
146
|
+
missing.push("mrBar");
|
|
147
|
+
return fail(flags, missing, "mrBar must be greater than or equal to 0.");
|
|
148
|
+
}
|
|
149
|
+
const half = (3 * input.mrBar) / 1.128;
|
|
150
|
+
const constants = { d2: 1.128, d4: 3.267, fromTable: true };
|
|
151
|
+
push(flags, ASSUMPTION);
|
|
152
|
+
return {
|
|
153
|
+
ok: true,
|
|
154
|
+
flags,
|
|
155
|
+
missing,
|
|
156
|
+
wrote: false,
|
|
157
|
+
error: null,
|
|
158
|
+
center: input.xBar,
|
|
159
|
+
ucl: input.xBar + half,
|
|
160
|
+
lcl: input.xBar - half,
|
|
161
|
+
centerR: input.mrBar,
|
|
162
|
+
uclR: 3.267 * input.mrBar,
|
|
163
|
+
lclR: 0,
|
|
164
|
+
constantsUsed: constants,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (chart === "c") {
|
|
169
|
+
if (!isNum(input.cBar)) {
|
|
170
|
+
missing.push("cBar");
|
|
171
|
+
return fail(flags, missing, "Missing cBar.");
|
|
172
|
+
}
|
|
173
|
+
if (!(input.cBar >= 0)) {
|
|
174
|
+
missing.push("cBar");
|
|
175
|
+
return fail(flags, missing, "cBar must be greater than or equal to 0.");
|
|
176
|
+
}
|
|
177
|
+
const band = 3 * Math.sqrt(input.cBar);
|
|
178
|
+
push(flags, ASSUMPTION);
|
|
179
|
+
return {
|
|
180
|
+
ok: true,
|
|
181
|
+
flags,
|
|
182
|
+
missing,
|
|
183
|
+
wrote: false,
|
|
184
|
+
error: null,
|
|
185
|
+
center: input.cBar,
|
|
186
|
+
ucl: input.cBar + band,
|
|
187
|
+
lcl: floorAtZero(input.cBar - band, flags),
|
|
188
|
+
centerR: null,
|
|
189
|
+
uclR: null,
|
|
190
|
+
lclR: null,
|
|
191
|
+
constantsUsed: null,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!isNum(input.n) || !(input.n > 0)) missing.push("n");
|
|
196
|
+
if (chart === "u") {
|
|
197
|
+
if (!isNum(input.uBar)) missing.push("uBar");
|
|
198
|
+
} else if (!isNum(input.pBar)) {
|
|
199
|
+
missing.push("pBar");
|
|
200
|
+
}
|
|
201
|
+
if (missing.length) {
|
|
202
|
+
return fail(flags, missing, "Missing " + missing.join(", ") + ".");
|
|
203
|
+
}
|
|
204
|
+
if (chart !== "u" && (input.pBar < 0 || input.pBar > 1)) {
|
|
205
|
+
missing.push("pBar");
|
|
206
|
+
return fail(flags, missing, "pBar must be between 0 and 1.");
|
|
207
|
+
}
|
|
208
|
+
if (chart === "u" && !(input.uBar >= 0)) {
|
|
209
|
+
missing.push("uBar");
|
|
210
|
+
return fail(flags, missing, "uBar must be greater than or equal to 0.");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let center = null;
|
|
214
|
+
let band = null;
|
|
215
|
+
if (chart === "p") {
|
|
216
|
+
center = input.pBar;
|
|
217
|
+
band = 3 * Math.sqrt((input.pBar * (1 - input.pBar)) / input.n);
|
|
218
|
+
} else if (chart === "np") {
|
|
219
|
+
center = input.n * input.pBar;
|
|
220
|
+
band = 3 * Math.sqrt(input.n * input.pBar * (1 - input.pBar));
|
|
221
|
+
} else {
|
|
222
|
+
center = input.uBar;
|
|
223
|
+
band = 3 * Math.sqrt(input.uBar / input.n);
|
|
224
|
+
}
|
|
225
|
+
push(flags, ASSUMPTION);
|
|
226
|
+
return {
|
|
227
|
+
ok: true,
|
|
228
|
+
flags,
|
|
229
|
+
missing,
|
|
230
|
+
wrote: false,
|
|
231
|
+
error: null,
|
|
232
|
+
center,
|
|
233
|
+
ucl: center + band,
|
|
234
|
+
lcl: floorAtZero(center - band, flags),
|
|
235
|
+
centerR: null,
|
|
236
|
+
uclR: null,
|
|
237
|
+
lclR: null,
|
|
238
|
+
constantsUsed: null,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Pure function of the caller's inputs. Returns numbers. Writes nothing.
|
|
2
|
+
|
|
3
|
+
const MISSING = "missing-input";
|
|
4
|
+
const SIGMA = "sigma-not-computed";
|
|
5
|
+
|
|
6
|
+
function isNum(value) {
|
|
7
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function push(flags, flag) {
|
|
11
|
+
if (flag && flags.indexOf(flag) < 0) flags.push(flag);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function blank(flags, missing, error) {
|
|
15
|
+
return {
|
|
16
|
+
ok: false,
|
|
17
|
+
flags,
|
|
18
|
+
missing,
|
|
19
|
+
wrote: false,
|
|
20
|
+
error,
|
|
21
|
+
dpo: null,
|
|
22
|
+
dpmo: null,
|
|
23
|
+
opportunityYield: null,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function fail(flags, missing, error) {
|
|
28
|
+
if (missing.length) push(flags, MISSING);
|
|
29
|
+
return blank(flags, missing, error);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function run(input) {
|
|
33
|
+
const flags = [];
|
|
34
|
+
const missing = [];
|
|
35
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
36
|
+
missing.push("defects", "units", "opportunitiesPerUnit");
|
|
37
|
+
return fail(flags, missing, "Missing defects, units, opportunitiesPerUnit.");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!isNum(input.defects)) missing.push("defects");
|
|
41
|
+
if (!isNum(input.units)) missing.push("units");
|
|
42
|
+
if (!isNum(input.opportunitiesPerUnit)) missing.push("opportunitiesPerUnit");
|
|
43
|
+
if (missing.length) {
|
|
44
|
+
return fail(flags, missing, "Missing " + missing.join(", ") + ".");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const problems = [];
|
|
48
|
+
if (!(input.defects >= 0)) {
|
|
49
|
+
missing.push("defects");
|
|
50
|
+
problems.push("defects must be greater than or equal to 0");
|
|
51
|
+
}
|
|
52
|
+
if (!(input.units > 0)) {
|
|
53
|
+
missing.push("units");
|
|
54
|
+
problems.push("units must be greater than 0");
|
|
55
|
+
}
|
|
56
|
+
if (!(input.opportunitiesPerUnit > 0)) {
|
|
57
|
+
missing.push("opportunitiesPerUnit");
|
|
58
|
+
problems.push("opportunities per unit must be greater than 0");
|
|
59
|
+
}
|
|
60
|
+
if (problems.length) {
|
|
61
|
+
return fail(flags, missing, problems.join(". ") + ".");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const dpo = input.defects / (input.units * input.opportunitiesPerUnit);
|
|
65
|
+
const opportunityYield = 1 - dpo;
|
|
66
|
+
if (opportunityYield < 0 || opportunityYield > 1) {
|
|
67
|
+
return fail(flags, [], "Opportunity yield is outside 0 to 1.");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
push(flags, SIGMA);
|
|
71
|
+
return {
|
|
72
|
+
ok: true,
|
|
73
|
+
flags,
|
|
74
|
+
missing,
|
|
75
|
+
wrote: false,
|
|
76
|
+
error: null,
|
|
77
|
+
dpo,
|
|
78
|
+
dpmo: dpo * 1000000,
|
|
79
|
+
opportunityYield,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// Pure function of the caller's inputs. Returns numbers. Writes nothing.
|
|
2
|
+
// This version does not return a p-value.
|
|
3
|
+
|
|
4
|
+
const MISSING = "missing-input";
|
|
5
|
+
const ASSUMPTION = "assumption-not-checked";
|
|
6
|
+
const SMALL = "small-n";
|
|
7
|
+
const ZERO = "zero-variance";
|
|
8
|
+
|
|
9
|
+
const TESTS = Object.freeze({
|
|
10
|
+
__proto__: null,
|
|
11
|
+
"one-sample-t": true,
|
|
12
|
+
"chi-square-gof": true,
|
|
13
|
+
"anova-oneway": true,
|
|
14
|
+
"simple-regression": true,
|
|
15
|
+
"z-interval": true,
|
|
16
|
+
"t-interval": true,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
function isNum(value) {
|
|
20
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function push(flags, flag) {
|
|
24
|
+
if (flag && flags.indexOf(flag) < 0) flags.push(flag);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function shell(flags, missing, ok, error) {
|
|
28
|
+
return {
|
|
29
|
+
ok,
|
|
30
|
+
flags,
|
|
31
|
+
missing,
|
|
32
|
+
wrote: false,
|
|
33
|
+
error,
|
|
34
|
+
t: null,
|
|
35
|
+
df: null,
|
|
36
|
+
exceedsCritical: null,
|
|
37
|
+
chiSquare: null,
|
|
38
|
+
grandMean: null,
|
|
39
|
+
groupMeans: null,
|
|
40
|
+
ssb: null,
|
|
41
|
+
ssw: null,
|
|
42
|
+
msb: null,
|
|
43
|
+
msw: null,
|
|
44
|
+
f: null,
|
|
45
|
+
dfBetween: null,
|
|
46
|
+
dfWithin: null,
|
|
47
|
+
slope: null,
|
|
48
|
+
intercept: null,
|
|
49
|
+
r2: null,
|
|
50
|
+
low: null,
|
|
51
|
+
high: null,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function refuse(flags, missing, error) {
|
|
56
|
+
if (missing.length) push(flags, MISSING);
|
|
57
|
+
return shell(flags, missing, false, error);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function nums(list) {
|
|
61
|
+
return Array.isArray(list) && list.length > 0 && list.every(isNum);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function sampleN(input, missing) {
|
|
65
|
+
if (!Number.isInteger(input.n) || !(input.n >= 2)) {
|
|
66
|
+
missing.push("n");
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function run(input) {
|
|
73
|
+
const flags = [];
|
|
74
|
+
const missing = [];
|
|
75
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
76
|
+
missing.push("test");
|
|
77
|
+
return refuse(flags, missing, "Missing test.");
|
|
78
|
+
}
|
|
79
|
+
const test = String(input.test);
|
|
80
|
+
if (!Object.hasOwn(TESTS, test)) {
|
|
81
|
+
missing.push("test");
|
|
82
|
+
return refuse(flags, missing, "Missing test.");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (test === "one-sample-t") {
|
|
86
|
+
if (!isNum(input.xBar)) missing.push("xBar");
|
|
87
|
+
if (!isNum(input.mu0)) missing.push("mu0");
|
|
88
|
+
if (!isNum(input.s)) missing.push("s");
|
|
89
|
+
if (!isNum(input.n)) missing.push("n");
|
|
90
|
+
if (missing.length) {
|
|
91
|
+
return refuse(flags, missing, "Missing " + missing.join(", ") + ".");
|
|
92
|
+
}
|
|
93
|
+
if (!(input.s > 0)) {
|
|
94
|
+
missing.push("s");
|
|
95
|
+
return refuse(flags, missing, "s must be greater than 0.");
|
|
96
|
+
}
|
|
97
|
+
if (!sampleN(input, missing)) {
|
|
98
|
+
return refuse(flags, missing, "n must be an integer of 2 or more.");
|
|
99
|
+
}
|
|
100
|
+
const t = (input.xBar - input.mu0) / (input.s / Math.sqrt(input.n));
|
|
101
|
+
const df = input.n - 1;
|
|
102
|
+
let exceedsCritical = null;
|
|
103
|
+
if (Object.hasOwn(input, "tCrit")) {
|
|
104
|
+
if (!isNum(input.tCrit)) {
|
|
105
|
+
missing.push("tCrit");
|
|
106
|
+
return refuse(flags, missing, "Missing tCrit.");
|
|
107
|
+
}
|
|
108
|
+
exceedsCritical = Math.abs(t) > input.tCrit;
|
|
109
|
+
}
|
|
110
|
+
push(flags, ASSUMPTION);
|
|
111
|
+
return {
|
|
112
|
+
...shell(flags, missing, true, null),
|
|
113
|
+
t,
|
|
114
|
+
df,
|
|
115
|
+
exceedsCritical,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (test === "chi-square-gof") {
|
|
120
|
+
if (!nums(input.observed)) missing.push("observed");
|
|
121
|
+
if (!nums(input.expected)) missing.push("expected");
|
|
122
|
+
if (!isNum(input.parametersEstimated)) missing.push("parametersEstimated");
|
|
123
|
+
if (missing.length) {
|
|
124
|
+
return refuse(flags, missing, "Missing " + missing.join(", ") + ".");
|
|
125
|
+
}
|
|
126
|
+
if (input.observed.length !== input.expected.length) {
|
|
127
|
+
missing.push("expected");
|
|
128
|
+
return refuse(flags, missing, "observed and expected must be the same length.");
|
|
129
|
+
}
|
|
130
|
+
if (input.expected.some((value) => !(value > 0))) {
|
|
131
|
+
missing.push("expected");
|
|
132
|
+
return refuse(flags, missing, "Each expected count must be greater than 0.");
|
|
133
|
+
}
|
|
134
|
+
const df = input.observed.length - 1 - input.parametersEstimated;
|
|
135
|
+
if (!(df > 0)) {
|
|
136
|
+
return refuse(flags, missing, "Degrees of freedom must be greater than 0.");
|
|
137
|
+
}
|
|
138
|
+
let chi = 0;
|
|
139
|
+
for (let i = 0; i < input.observed.length; i += 1) {
|
|
140
|
+
const observed = input.observed.at(i);
|
|
141
|
+
const expected = input.expected.at(i);
|
|
142
|
+
const diff = observed - expected;
|
|
143
|
+
chi += (diff * diff) / expected;
|
|
144
|
+
}
|
|
145
|
+
push(flags, ASSUMPTION);
|
|
146
|
+
return {
|
|
147
|
+
...shell(flags, missing, true, null),
|
|
148
|
+
df,
|
|
149
|
+
chiSquare: chi,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (test === "anova-oneway") {
|
|
154
|
+
const groups = input.groups;
|
|
155
|
+
if (!Array.isArray(groups) || groups.length < 2 || !groups.every(nums)) {
|
|
156
|
+
missing.push("groups");
|
|
157
|
+
return refuse(flags, missing, "Missing groups.");
|
|
158
|
+
}
|
|
159
|
+
const flat = groups.flat();
|
|
160
|
+
const grand = flat.reduce((sum, value) => sum + value, 0) / flat.length;
|
|
161
|
+
const means = groups.map((group) => group.reduce((sum, value) => sum + value, 0) / group.length);
|
|
162
|
+
let ssb = 0;
|
|
163
|
+
let ssw = 0;
|
|
164
|
+
groups.forEach((group, index) => {
|
|
165
|
+
const mean = means.at(index);
|
|
166
|
+
ssb += group.length * (mean - grand) * (mean - grand);
|
|
167
|
+
group.forEach((value) => {
|
|
168
|
+
ssw += (value - mean) * (value - mean);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
const dfBetween = groups.length - 1;
|
|
172
|
+
const dfWithin = flat.length - groups.length;
|
|
173
|
+
let ok = dfWithin > 0;
|
|
174
|
+
let error = null;
|
|
175
|
+
let msb = null;
|
|
176
|
+
let msw = null;
|
|
177
|
+
let f = null;
|
|
178
|
+
if (!(dfWithin > 0)) {
|
|
179
|
+
push(flags, ZERO);
|
|
180
|
+
ok = false;
|
|
181
|
+
error = "Degrees of freedom must be greater than 0.";
|
|
182
|
+
} else {
|
|
183
|
+
msb = ssb / dfBetween;
|
|
184
|
+
msw = ssw / dfWithin;
|
|
185
|
+
f = msw === 0 ? null : msb / msw;
|
|
186
|
+
if (msw === 0) {
|
|
187
|
+
push(flags, ZERO);
|
|
188
|
+
ok = false;
|
|
189
|
+
error = "Mean square within is 0.";
|
|
190
|
+
}
|
|
191
|
+
if (ok) push(flags, ASSUMPTION);
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
...shell(flags, missing, ok, error),
|
|
195
|
+
grandMean: grand,
|
|
196
|
+
groupMeans: means,
|
|
197
|
+
ssb,
|
|
198
|
+
ssw,
|
|
199
|
+
msb,
|
|
200
|
+
msw,
|
|
201
|
+
f,
|
|
202
|
+
dfBetween,
|
|
203
|
+
dfWithin,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (test === "simple-regression") {
|
|
208
|
+
if (!nums(input.x)) missing.push("x");
|
|
209
|
+
if (!nums(input.y)) missing.push("y");
|
|
210
|
+
if (missing.length) {
|
|
211
|
+
return refuse(flags, missing, "Missing " + missing.join(", ") + ".");
|
|
212
|
+
}
|
|
213
|
+
if (input.x.length !== input.y.length) {
|
|
214
|
+
missing.push("y");
|
|
215
|
+
return refuse(flags, missing, "x and y must be the same length.");
|
|
216
|
+
}
|
|
217
|
+
const n = input.x.length;
|
|
218
|
+
const xBar = input.x.reduce((sum, value) => sum + value, 0) / n;
|
|
219
|
+
const yBar = input.y.reduce((sum, value) => sum + value, 0) / n;
|
|
220
|
+
let num = 0;
|
|
221
|
+
let den = 0;
|
|
222
|
+
let tss = 0;
|
|
223
|
+
for (let i = 0; i < n; i += 1) {
|
|
224
|
+
const dx = input.x.at(i) - xBar;
|
|
225
|
+
const y = input.y.at(i);
|
|
226
|
+
num += dx * (y - yBar);
|
|
227
|
+
den += dx * dx;
|
|
228
|
+
const dy = y - yBar;
|
|
229
|
+
tss += dy * dy;
|
|
230
|
+
}
|
|
231
|
+
if (den === 0) {
|
|
232
|
+
push(flags, ZERO);
|
|
233
|
+
return shell(flags, missing, false, "The x values have no spread.");
|
|
234
|
+
}
|
|
235
|
+
const slope = num / den;
|
|
236
|
+
const intercept = yBar - slope * xBar;
|
|
237
|
+
let rss = 0;
|
|
238
|
+
for (let i = 0; i < n; i += 1) {
|
|
239
|
+
const residual = input.y.at(i) - (intercept + slope * input.x.at(i));
|
|
240
|
+
rss += residual * residual;
|
|
241
|
+
}
|
|
242
|
+
const r2 = tss === 0 ? 1 : 1 - rss / tss;
|
|
243
|
+
if (n < 3) push(flags, SMALL);
|
|
244
|
+
push(flags, ASSUMPTION);
|
|
245
|
+
return {
|
|
246
|
+
...shell(flags, missing, true, null),
|
|
247
|
+
slope,
|
|
248
|
+
intercept,
|
|
249
|
+
r2,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (test === "z-interval") {
|
|
254
|
+
if (!isNum(input.xBar)) missing.push("xBar");
|
|
255
|
+
if (!isNum(input.sigma)) missing.push("sigma");
|
|
256
|
+
if (!isNum(input.n)) missing.push("n");
|
|
257
|
+
if (!isNum(input.z)) missing.push("z");
|
|
258
|
+
if (missing.length) {
|
|
259
|
+
return refuse(flags, missing, "Missing " + missing.join(", ") + ".");
|
|
260
|
+
}
|
|
261
|
+
if (!(input.sigma > 0)) {
|
|
262
|
+
missing.push("sigma");
|
|
263
|
+
return refuse(flags, missing, "sigma must be greater than 0.");
|
|
264
|
+
}
|
|
265
|
+
if (!sampleN(input, missing)) {
|
|
266
|
+
return refuse(flags, missing, "n must be an integer of 2 or more.");
|
|
267
|
+
}
|
|
268
|
+
const margin = input.z * (input.sigma / Math.sqrt(input.n));
|
|
269
|
+
push(flags, ASSUMPTION);
|
|
270
|
+
return {
|
|
271
|
+
...shell(flags, missing, true, null),
|
|
272
|
+
low: input.xBar - margin,
|
|
273
|
+
high: input.xBar + margin,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!isNum(input.xBar)) missing.push("xBar");
|
|
278
|
+
if (!isNum(input.s)) missing.push("s");
|
|
279
|
+
if (!isNum(input.n)) missing.push("n");
|
|
280
|
+
if (!isNum(input.tCrit)) missing.push("tCrit");
|
|
281
|
+
if (missing.length) {
|
|
282
|
+
return refuse(flags, missing, "Missing " + missing.join(", ") + ".");
|
|
283
|
+
}
|
|
284
|
+
if (!(input.s > 0)) {
|
|
285
|
+
missing.push("s");
|
|
286
|
+
return refuse(flags, missing, "s must be greater than 0.");
|
|
287
|
+
}
|
|
288
|
+
if (!sampleN(input, missing)) {
|
|
289
|
+
return refuse(flags, missing, "n must be an integer of 2 or more.");
|
|
290
|
+
}
|
|
291
|
+
const margin = input.tCrit * (input.s / Math.sqrt(input.n));
|
|
292
|
+
push(flags, ASSUMPTION);
|
|
293
|
+
return {
|
|
294
|
+
...shell(flags, missing, true, null),
|
|
295
|
+
low: input.xBar - margin,
|
|
296
|
+
high: input.xBar + margin,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Pure function of the caller's inputs. Returns numbers. Writes nothing.
|
|
2
|
+
|
|
3
|
+
const MISSING = "missing-input";
|
|
4
|
+
const ASSUMPTION = "assumption-not-checked";
|
|
5
|
+
|
|
6
|
+
function isNum(value) {
|
|
7
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function push(flags, flag) {
|
|
11
|
+
if (flag && flags.indexOf(flag) < 0) flags.push(flag);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function ceilToInt(value) {
|
|
15
|
+
const nearest = Math.round(value);
|
|
16
|
+
if (Math.abs(value - nearest) < 1e-9) return nearest;
|
|
17
|
+
return Math.ceil(value);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function blank(flags, missing, error) {
|
|
21
|
+
return {
|
|
22
|
+
ok: false,
|
|
23
|
+
flags,
|
|
24
|
+
missing,
|
|
25
|
+
wrote: false,
|
|
26
|
+
error,
|
|
27
|
+
n: null,
|
|
28
|
+
marginOfError: false,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function fail(flags, missing, error) {
|
|
33
|
+
if (missing.length) push(flags, MISSING);
|
|
34
|
+
return blank(flags, missing, error);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function run(input) {
|
|
38
|
+
const flags = [];
|
|
39
|
+
const missing = [];
|
|
40
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
41
|
+
missing.push("kind", "z", "e");
|
|
42
|
+
return fail(flags, missing, "Missing kind, z, e.");
|
|
43
|
+
}
|
|
44
|
+
if (input.kind !== "proportion" && input.kind !== "mean") missing.push("kind");
|
|
45
|
+
if (!isNum(input.z)) missing.push("z");
|
|
46
|
+
if (!isNum(input.e)) missing.push("e");
|
|
47
|
+
if (missing.length) {
|
|
48
|
+
return fail(flags, missing, "Missing " + missing.join(", ") + ".");
|
|
49
|
+
}
|
|
50
|
+
if (!(input.e > 0)) {
|
|
51
|
+
missing.push("e");
|
|
52
|
+
return fail(flags, missing, "e must be greater than 0.");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let raw = null;
|
|
56
|
+
if (input.kind === "proportion") {
|
|
57
|
+
if (!isNum(input.p) || input.p < 0 || input.p > 1) {
|
|
58
|
+
missing.push("p");
|
|
59
|
+
return fail(flags, missing, "p must be between 0 and 1.");
|
|
60
|
+
}
|
|
61
|
+
raw = (input.z * input.z * input.p * (1 - input.p)) / (input.e * input.e);
|
|
62
|
+
} else {
|
|
63
|
+
if (!isNum(input.sigma) || !(input.sigma > 0)) {
|
|
64
|
+
missing.push("sigma");
|
|
65
|
+
return fail(flags, missing, "sigma must be greater than 0.");
|
|
66
|
+
}
|
|
67
|
+
const ratio = (input.z * input.sigma) / input.e;
|
|
68
|
+
raw = ratio * ratio;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
push(flags, ASSUMPTION);
|
|
72
|
+
return {
|
|
73
|
+
ok: true,
|
|
74
|
+
flags,
|
|
75
|
+
missing,
|
|
76
|
+
wrote: false,
|
|
77
|
+
error: null,
|
|
78
|
+
n: ceilToInt(raw),
|
|
79
|
+
marginOfError: true,
|
|
80
|
+
};
|
|
81
|
+
}
|