make-mp-data 1.3.3 → 1.4.0
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/.vscode/launch.json +11 -3
- package/.vscode/settings.json +12 -2
- package/README.md +2 -2
- package/chart.js +180 -0
- package/cli.js +30 -17
- package/index.js +459 -287
- package/package.json +59 -49
- package/{models → schemas}/complex.js +39 -19
- package/schemas/foobar.js +110 -0
- package/schemas/funnels.js +222 -0
- package/{models → schemas}/simple.js +10 -10
- package/scratch.mjs +20 -0
- package/testCases.mjs +229 -0
- package/testSoup.mjs +27 -0
- package/tests/e2e.test.js +27 -20
- package/tests/jest.config.js +30 -0
- package/tests/unit.test.js +360 -19
- package/tmp/.gitkeep +0 -0
- package/tsconfig.json +18 -0
- package/types.d.ts +186 -113
- package/utils.js +634 -124
- package/timesoup.js +0 -92
- /package/{models → schemas}/deepNest.js +0 -0
package/tests/unit.test.js
CHANGED
|
@@ -1,19 +1,54 @@
|
|
|
1
1
|
const generate = require('../index.js');
|
|
2
2
|
const dayjs = require("dayjs");
|
|
3
3
|
const utc = require("dayjs/plugin/utc");
|
|
4
|
+
const fs = require('fs');
|
|
4
5
|
const u = require('ak-tools');
|
|
5
6
|
dayjs.extend(utc);
|
|
6
|
-
const { timeSoup } = generate;
|
|
7
7
|
require('dotenv').config();
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const { applySkew,
|
|
10
|
+
boxMullerRandom,
|
|
11
|
+
choose,
|
|
12
|
+
date,
|
|
13
|
+
dates,
|
|
14
|
+
day,
|
|
15
|
+
exhaust,
|
|
16
|
+
generateEmoji,
|
|
17
|
+
getUniqueKeys,
|
|
18
|
+
integer,
|
|
19
|
+
mapToRange,
|
|
20
|
+
person,
|
|
21
|
+
pick,
|
|
22
|
+
range,
|
|
23
|
+
pickAWinner,
|
|
24
|
+
weightedRange,
|
|
25
|
+
enrichArray,
|
|
26
|
+
fixFirstAndLast,
|
|
27
|
+
generateUser,
|
|
28
|
+
openFinder,
|
|
29
|
+
progress,
|
|
30
|
+
shuffleArray,
|
|
31
|
+
shuffleExceptFirst,
|
|
32
|
+
shuffleExceptLast,
|
|
33
|
+
shuffleMiddle,
|
|
34
|
+
shuffleOutside,
|
|
35
|
+
streamCSV,
|
|
36
|
+
streamJSON,
|
|
37
|
+
weighArray,
|
|
38
|
+
weighFunnels,
|
|
39
|
+
buildFileNames,
|
|
40
|
+
TimeSoup,
|
|
41
|
+
getChance,
|
|
42
|
+
initChance
|
|
43
|
+
} = require('../utils');
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
describe('timesoup', () => {
|
|
47
|
+
test('always valid times', () => {
|
|
13
48
|
const dates = [];
|
|
14
|
-
for (let i = 0; i <
|
|
15
|
-
const earliest = dayjs().subtract(u.rand(
|
|
16
|
-
dates.push(
|
|
49
|
+
for (let i = 0; i < 10000; i++) {
|
|
50
|
+
const earliest = dayjs().subtract(u.rand(5, 50), 'D');
|
|
51
|
+
dates.push(TimeSoup());
|
|
17
52
|
}
|
|
18
53
|
const tooOld = dates.filter(d => dayjs(d).isBefore(dayjs.unix(0)));
|
|
19
54
|
const badYear = dates.filter(d => !d.startsWith('202'));
|
|
@@ -24,8 +59,207 @@ describe('timeSoup', () => {
|
|
|
24
59
|
});
|
|
25
60
|
|
|
26
61
|
|
|
62
|
+
describe('naming things', () => {
|
|
63
|
+
|
|
64
|
+
test('default config', () => {
|
|
65
|
+
const config = { simulationName: 'testSim' };
|
|
66
|
+
const result = buildFileNames(config);
|
|
67
|
+
expect(result.eventFiles).toEqual(['testSim-EVENTS.csv']);
|
|
68
|
+
expect(result.userFiles).toEqual(['testSim-USERS.csv']);
|
|
69
|
+
expect(result.scdFiles).toEqual([]);
|
|
70
|
+
expect(result.groupFiles).toEqual([]);
|
|
71
|
+
expect(result.lookupFiles).toEqual([]);
|
|
72
|
+
expect(result.mirrorFiles).toEqual([]);
|
|
73
|
+
expect(result.folder).toEqual('./');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('json format', () => {
|
|
77
|
+
const config = { simulationName: 'testSim', format: 'json' };
|
|
78
|
+
const result = buildFileNames(config);
|
|
79
|
+
expect(result.eventFiles).toEqual(['testSim-EVENTS.json']);
|
|
80
|
+
expect(result.userFiles).toEqual(['testSim-USERS.json']);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('with scdProps', () => {
|
|
84
|
+
const config = {
|
|
85
|
+
simulationName: 'testSim',
|
|
86
|
+
scdProps: { prop1: {}, prop2: {} }
|
|
87
|
+
};
|
|
88
|
+
const result = buildFileNames(config);
|
|
89
|
+
expect(result.scdFiles).toEqual([
|
|
90
|
+
'testSim-prop1-SCD.csv',
|
|
91
|
+
'testSim-prop2-SCD.csv'
|
|
92
|
+
]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('with groupKeys', () => {
|
|
96
|
+
const config = {
|
|
97
|
+
simulationName: 'testSim',
|
|
98
|
+
groupKeys: [['group1'], ['group2']]
|
|
99
|
+
};
|
|
100
|
+
const result = buildFileNames(config);
|
|
101
|
+
expect(result.groupFiles).toEqual([
|
|
102
|
+
'testSim-group1-GROUP.csv',
|
|
103
|
+
'testSim-group2-GROUP.csv'
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('with lookupTables', () => {
|
|
108
|
+
const config = {
|
|
109
|
+
simulationName: 'testSim',
|
|
110
|
+
lookupTables: [{ key: 'lookup1' }, { key: 'lookup2' }]
|
|
111
|
+
};
|
|
112
|
+
const result = buildFileNames(config);
|
|
113
|
+
expect(result.lookupFiles).toEqual([
|
|
114
|
+
'testSim-lookup1-LOOKUP.csv',
|
|
115
|
+
'testSim-lookup2-LOOKUP.csv'
|
|
116
|
+
]);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('with mirrorProps', () => {
|
|
120
|
+
const config = {
|
|
121
|
+
simulationName: 'testSim',
|
|
122
|
+
mirrorProps: { prop1: {} }
|
|
123
|
+
};
|
|
124
|
+
const result = buildFileNames(config);
|
|
125
|
+
expect(result.mirrorFiles).toEqual(['testSim-MIRROR.csv']);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('writeToDisk', async () => {
|
|
129
|
+
const config = { simulationName: 'testSim', writeToDisk: true };
|
|
130
|
+
const result = await buildFileNames(config);
|
|
131
|
+
expect(result.folder).toBeDefined();
|
|
132
|
+
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
test('invalid simName', () => {
|
|
137
|
+
const config = { simulationName: 123 };
|
|
138
|
+
expect(() => buildFileNames(config)).toThrow('simName must be a string');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
test('streamJSON: writes to file', async () => {
|
|
143
|
+
const path = 'test.json';
|
|
144
|
+
const data = [{ a: 1, b: 2 }, { a: 3, b: 4 }];
|
|
145
|
+
await streamJSON(path, data);
|
|
146
|
+
const content = fs.readFileSync(path, 'utf8');
|
|
147
|
+
const lines = content.trim().split('\n').map(line => JSON.parse(line));
|
|
148
|
+
expect(lines).toEqual(data);
|
|
149
|
+
fs.unlinkSync(path);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('streamCSV: writes to file', async () => {
|
|
153
|
+
const path = 'test.csv';
|
|
154
|
+
const data = [{ a: 1, b: 2 }, { a: 3, b: 4 }];
|
|
155
|
+
await streamCSV(path, data);
|
|
156
|
+
const content = fs.readFileSync(path, 'utf8');
|
|
157
|
+
const lines = content.trim().split('\n');
|
|
158
|
+
expect(lines.length).toBe(3); // Including header
|
|
159
|
+
fs.unlinkSync(path);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
test('generateUser: works', () => {
|
|
164
|
+
const uuid = { guid: jest.fn().mockReturnValue('uuid-123') };
|
|
165
|
+
const numDays = 30;
|
|
166
|
+
const user = generateUser(numDays);
|
|
167
|
+
expect(user).toHaveProperty('distinct_id');
|
|
168
|
+
expect(user).toHaveProperty('name');
|
|
169
|
+
expect(user).toHaveProperty('email');
|
|
170
|
+
expect(user).toHaveProperty('avatar');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('enrichArray: works', () => {
|
|
174
|
+
const arr = [];
|
|
175
|
+
const enrichedArray = enrichArray(arr);
|
|
176
|
+
enrichedArray.hookPush(1);
|
|
177
|
+
enrichedArray.hookPush(2);
|
|
178
|
+
const match = JSON.stringify(enrichedArray) === JSON.stringify([1, 2]);
|
|
179
|
+
expect(match).toEqual(true);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
describe('determined random', () => {
|
|
186
|
+
test('initializes RNG with seed from environment variable', () => {
|
|
187
|
+
process.env.SEED = 'test-seed';
|
|
188
|
+
// @ts-ignore
|
|
189
|
+
initChance();
|
|
190
|
+
const chance = getChance();
|
|
191
|
+
expect(chance).toBeDefined();
|
|
192
|
+
expect(chance.random()).toBeGreaterThanOrEqual(0);
|
|
193
|
+
expect(chance.random()).toBeLessThanOrEqual(1);
|
|
194
|
+
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('initializes RNG only once', () => {
|
|
198
|
+
const seed = 'initial-seed';
|
|
199
|
+
initChance(seed);
|
|
200
|
+
const chance1 = getChance();
|
|
201
|
+
initChance('new-seed');
|
|
202
|
+
const chance2 = getChance();
|
|
203
|
+
expect(chance1).toBe(chance2);
|
|
204
|
+
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
describe('generateUser', () => {
|
|
210
|
+
test('creates a user with valid fields', () => {
|
|
211
|
+
const numDays = 30;
|
|
212
|
+
const user = generateUser('uuid-123', numDays);
|
|
213
|
+
expect(user).toHaveProperty('distinct_id');
|
|
214
|
+
expect(user).toHaveProperty('name');
|
|
215
|
+
expect(user).toHaveProperty('email');
|
|
216
|
+
expect(user).toHaveProperty('avatar');
|
|
217
|
+
expect(user).toHaveProperty('created');
|
|
218
|
+
expect(user).toHaveProperty('anonymousIds');
|
|
219
|
+
expect(user).toHaveProperty('sessionIds');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('creates a user with a created date within the specified range', () => {
|
|
223
|
+
const numDays = 30;
|
|
224
|
+
const user = generateUser('uuid-123', numDays);
|
|
225
|
+
const createdDate = dayjs(user.created, 'YYYY-MM-DD');
|
|
226
|
+
expect(createdDate.isValid()).toBeTruthy();
|
|
227
|
+
expect(createdDate.isBefore(dayjs.unix(global.NOW))).toBeTruthy();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
describe('enrich array', () => {
|
|
234
|
+
test('hook works', () => {
|
|
235
|
+
const arr = [];
|
|
236
|
+
const hook = (item) => item * 2;
|
|
237
|
+
const enrichedArray = enrichArray(arr, { hook });
|
|
238
|
+
enrichedArray.hookPush(1);
|
|
239
|
+
enrichedArray.hookPush(2);
|
|
240
|
+
expect(enrichedArray.includes(2)).toBeTruthy();
|
|
241
|
+
expect(enrichedArray.includes(4)).toBeTruthy();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('filter empties', () => {
|
|
245
|
+
const arr = [];
|
|
246
|
+
const hook = (item) => item ? item.toString() : item;
|
|
247
|
+
const enrichedArray = enrichArray(arr, { hook });
|
|
248
|
+
enrichedArray.hookPush(null);
|
|
249
|
+
enrichedArray.hookPush(undefined);
|
|
250
|
+
enrichedArray.hookPush({});
|
|
251
|
+
enrichedArray.hookPush({ a: 1 });
|
|
252
|
+
enrichedArray.hookPush([1, 2]);
|
|
253
|
+
expect(enrichedArray).toHaveLength(3);
|
|
254
|
+
expect(enrichedArray.includes('null')).toBeFalsy();
|
|
255
|
+
expect(enrichedArray.includes('undefined')).toBeFalsy();
|
|
256
|
+
expect(enrichedArray.includes('[object Object]')).toBeTruthy();
|
|
257
|
+
expect(enrichedArray.includes('1')).toBeTruthy();
|
|
258
|
+
expect(enrichedArray.includes('2')).toBeTruthy();
|
|
259
|
+
|
|
260
|
+
});
|
|
261
|
+
});
|
|
27
262
|
|
|
28
|
-
const { applySkew, boxMullerRandom, choose, date, dates, day, exhaust, generateEmoji, getUniqueKeys, integer, makeHashTags, makeProducts, mapToRange, person, pick, range, weighList, weightedRange } = require('../utils');
|
|
29
263
|
|
|
30
264
|
describe('utils', () => {
|
|
31
265
|
|
|
@@ -58,9 +292,9 @@ describe('utils', () => {
|
|
|
58
292
|
|
|
59
293
|
test('person: fields', () => {
|
|
60
294
|
const generatedPerson = person();
|
|
61
|
-
expect(generatedPerson).toHaveProperty('
|
|
62
|
-
expect(generatedPerson).toHaveProperty('
|
|
63
|
-
expect(generatedPerson).toHaveProperty('
|
|
295
|
+
expect(generatedPerson).toHaveProperty('name');
|
|
296
|
+
expect(generatedPerson).toHaveProperty('email');
|
|
297
|
+
expect(generatedPerson).toHaveProperty('avatar');
|
|
64
298
|
});
|
|
65
299
|
|
|
66
300
|
|
|
@@ -73,7 +307,7 @@ describe('utils', () => {
|
|
|
73
307
|
test('date: future', () => {
|
|
74
308
|
const futureDate = date(10, false, 'YYYY-MM-DD')();
|
|
75
309
|
expect(dayjs(futureDate, 'YYYY-MM-DD').isValid()).toBeTruthy();
|
|
76
|
-
expect(dayjs(futureDate).isAfter(dayjs())).toBeTruthy();
|
|
310
|
+
expect(dayjs(futureDate).isAfter(dayjs.unix(global.NOW))).toBeTruthy();
|
|
77
311
|
});
|
|
78
312
|
|
|
79
313
|
test('dates: pairs', () => {
|
|
@@ -107,7 +341,7 @@ describe('utils', () => {
|
|
|
107
341
|
});
|
|
108
342
|
|
|
109
343
|
test('weightedRange: within range', () => {
|
|
110
|
-
const values = weightedRange(5, 15
|
|
344
|
+
const values = weightedRange(5, 15);
|
|
111
345
|
expect(values.every(v => v >= 5 && v <= 15)).toBe(true);
|
|
112
346
|
expect(values.length).toBe(100);
|
|
113
347
|
});
|
|
@@ -124,8 +358,8 @@ describe('utils', () => {
|
|
|
124
358
|
const sd = 5;
|
|
125
359
|
const mappedValue = mapToRange(value, mean, sd);
|
|
126
360
|
expect(mappedValue).toBe(10);
|
|
127
|
-
|
|
128
|
-
|
|
361
|
+
});
|
|
362
|
+
|
|
129
363
|
|
|
130
364
|
|
|
131
365
|
test('exhaust: elements', () => {
|
|
@@ -146,6 +380,19 @@ describe('utils', () => {
|
|
|
146
380
|
});
|
|
147
381
|
|
|
148
382
|
|
|
383
|
+
test('times', () => {
|
|
384
|
+
const dates = [];
|
|
385
|
+
for (let i = 0; i < 10000; i++) {
|
|
386
|
+
const earliest = dayjs().subtract(u.rand(5, 50), 'D');
|
|
387
|
+
dates.push(TimeSoup());
|
|
388
|
+
}
|
|
389
|
+
const tooOld = dates.filter(d => dayjs(d).isBefore(dayjs.unix(0)));
|
|
390
|
+
const badYear = dates.filter(d => !d.startsWith('202'));
|
|
391
|
+
expect(dates.every(d => dayjs(d).isAfter(dayjs.unix(0)))).toBe(true);
|
|
392
|
+
expect(dates.every(d => d.startsWith('202'))).toBe(true);
|
|
393
|
+
|
|
394
|
+
});
|
|
395
|
+
|
|
149
396
|
test('date', () => {
|
|
150
397
|
const result = date();
|
|
151
398
|
expect(dayjs(result()).isValid()).toBe(true);
|
|
@@ -184,16 +431,110 @@ describe('utils', () => {
|
|
|
184
431
|
test('emoji: works', () => {
|
|
185
432
|
const emojis = generateEmoji(5)();
|
|
186
433
|
expect(typeof emojis).toBe('string');
|
|
187
|
-
|
|
434
|
+
if (!Array.isArray(emojis)) {
|
|
435
|
+
expect(emojis.split(', ').length).toBeLessThanOrEqual(5);
|
|
436
|
+
}
|
|
437
|
+
if (Array.isArray(emojis)) {
|
|
438
|
+
expect(emojis.length).toBeLessThanOrEqual(5);
|
|
439
|
+
}
|
|
188
440
|
});
|
|
189
441
|
|
|
190
442
|
test('emoji: length', () => {
|
|
191
443
|
const result = generateEmoji();
|
|
192
444
|
const emojis = result();
|
|
193
445
|
expect(typeof emojis).toBe('string');
|
|
194
|
-
|
|
195
|
-
|
|
446
|
+
if (!Array.isArray(emojis)) {
|
|
447
|
+
expect(emojis.split(', ').length).toBeLessThanOrEqual(10);
|
|
448
|
+
}
|
|
449
|
+
if (Array.isArray(emojis)) {
|
|
450
|
+
expect(emojis.length).toBeLessThanOrEqual(10);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
});
|
|
454
|
+
|
|
196
455
|
|
|
456
|
+
test('weighArray: works', () => {
|
|
457
|
+
const arr = ['a', 'b', 'c'];
|
|
458
|
+
const weightedArr = weighArray(arr);
|
|
459
|
+
expect(weightedArr.length).toBeGreaterThanOrEqual(arr.length);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test('weighFunnels: works', () => {
|
|
463
|
+
const acc = [];
|
|
464
|
+
const funnel = { weight: 3 };
|
|
465
|
+
const result = weighFunnels(acc, funnel);
|
|
466
|
+
expect(result.length).toBe(3);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
test('progress: outputs correctly', () => {
|
|
470
|
+
// @ts-ignore
|
|
471
|
+
const mockStdoutWrite = jest.spyOn(process.stdout, 'write').mockImplementation(() => { });
|
|
472
|
+
progress('test', 50);
|
|
473
|
+
expect(mockStdoutWrite).toHaveBeenCalled();
|
|
474
|
+
mockStdoutWrite.mockRestore();
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
test('range: works', () => {
|
|
478
|
+
const result = [];
|
|
479
|
+
range.call(result, 1, 5);
|
|
480
|
+
expect(result).toEqual([1, 2, 3, 4, 5]);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
test('shuffleArray: works', () => {
|
|
486
|
+
const arr = [1, 2, 3, 4, 5];
|
|
487
|
+
const shuffled = shuffleArray([...arr]);
|
|
488
|
+
expect(shuffled).not.toEqual(arr);
|
|
489
|
+
expect(shuffled.sort()).toEqual(arr.sort());
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
test('shuffleExceptFirst: works', () => {
|
|
493
|
+
const arr = [1, 2, 3, 4, 5];
|
|
494
|
+
const shuffled = shuffleExceptFirst([...arr]);
|
|
495
|
+
expect(shuffled[0]).toBe(arr[0]);
|
|
496
|
+
expect(shuffled.slice(1).sort()).toEqual(arr.slice(1).sort());
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
test('shuffleExceptLast: works', () => {
|
|
500
|
+
const arr = [1, 2, 3, 4, 5];
|
|
501
|
+
const shuffled = shuffleExceptLast([...arr]);
|
|
502
|
+
expect(shuffled[shuffled.length - 1]).toBe(arr[arr.length - 1]);
|
|
503
|
+
expect(shuffled.slice(0, -1).sort()).toEqual(arr.slice(0, -1).sort());
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
test('fixFirstAndLast: works', () => {
|
|
507
|
+
const arr = [1, 2, 3, 4, 5];
|
|
508
|
+
const shuffled = fixFirstAndLast([...arr]);
|
|
509
|
+
expect(shuffled[0]).toBe(arr[0]);
|
|
510
|
+
expect(shuffled[shuffled.length - 1]).toBe(arr[arr.length - 1]);
|
|
511
|
+
expect(shuffled.slice(1, -1).sort()).toEqual(arr.slice(1, -1).sort());
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test('shuffleMiddle: works', () => {
|
|
515
|
+
const arr = [1, 2, 3, 4, 5];
|
|
516
|
+
const shuffled = shuffleMiddle([...arr]);
|
|
517
|
+
expect(shuffled[0]).toBe(arr[0]);
|
|
518
|
+
expect(shuffled[shuffled.length - 1]).toBe(arr[arr.length - 1]);
|
|
519
|
+
expect(shuffled.slice(1, -1).sort()).toEqual(arr.slice(1, -1).sort());
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
test('shuffleOutside: works', () => {
|
|
523
|
+
const arr = [1, 2, 3, 4, 5];
|
|
524
|
+
const shuffled = shuffleOutside([...arr]);
|
|
525
|
+
expect(shuffled.slice(1, -1)).toEqual(arr.slice(1, -1));
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
test('box normal distribution', () => {
|
|
529
|
+
const values = [];
|
|
530
|
+
for (let i = 0; i < 10000; i++) {
|
|
531
|
+
values.push(boxMullerRandom());
|
|
532
|
+
}
|
|
533
|
+
const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
|
|
534
|
+
const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length;
|
|
535
|
+
const stdDev = Math.sqrt(variance);
|
|
536
|
+
expect(mean).toBeCloseTo(0, 1);
|
|
537
|
+
expect(stdDev).toBeCloseTo(1, 1);
|
|
197
538
|
});
|
|
198
539
|
|
|
199
540
|
|
package/tmp/.gitkeep
ADDED
|
File without changes
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"checkJs": true,
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"strict": false,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"resolveJsonModule": true
|
|
11
|
+
},
|
|
12
|
+
"include": [
|
|
13
|
+
"**/*.js"
|
|
14
|
+
, "soupTemplates.mjs", "testSoup.mjs", "testCases.mjs" ],
|
|
15
|
+
"exclude": [
|
|
16
|
+
"node_modules"
|
|
17
|
+
]
|
|
18
|
+
}
|