@ix-xs/node-comfort 1.2.0 → 2.0.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +190 -0
  2. package/README.md +136 -549
  3. package/index.js +80 -35
  4. package/package.json +160 -33
  5. package/src/Arr.js +887 -419
  6. package/src/Async.js +585 -0
  7. package/src/Cache.js +329 -0
  8. package/src/Checker.js +971 -539
  9. package/src/Cli.js +1034 -0
  10. package/src/Color.js +415 -0
  11. package/src/Crypto.js +707 -0
  12. package/src/Emitter.js +289 -0
  13. package/src/Env.js +555 -0
  14. package/src/FS.js +1483 -894
  15. package/src/Func.js +670 -353
  16. package/src/Http.js +538 -0
  17. package/src/Id.js +384 -201
  18. package/src/Logger.js +739 -378
  19. package/src/Num.js +844 -361
  20. package/src/Obj.js +855 -372
  21. package/src/SQLite.js +1308 -1005
  22. package/src/Schema.js +1586 -0
  23. package/src/Str.js +983 -436
  24. package/src/Sys.js +511 -0
  25. package/src/Time.js +1260 -422
  26. package/src/Utils.js +321 -325
  27. package/src/errors.js +292 -0
  28. package/types/index.d.ts +693 -526
  29. package/types/src/Arr.d.ts +455 -145
  30. package/types/src/Arr.d.ts.map +1 -1
  31. package/types/src/Async.d.ts +321 -0
  32. package/types/src/Async.d.ts.map +1 -0
  33. package/types/src/Cache.d.ts +195 -0
  34. package/types/src/Cache.d.ts.map +1 -0
  35. package/types/src/Checker.d.ts +501 -235
  36. package/types/src/Checker.d.ts.map +1 -1
  37. package/types/src/Cli.d.ts +582 -0
  38. package/types/src/Cli.d.ts.map +1 -0
  39. package/types/src/Color.d.ts +294 -0
  40. package/types/src/Color.d.ts.map +1 -0
  41. package/types/src/Crypto.d.ts +534 -0
  42. package/types/src/Crypto.d.ts.map +1 -0
  43. package/types/src/Emitter.d.ts +170 -0
  44. package/types/src/Emitter.d.ts.map +1 -0
  45. package/types/src/Env.d.ts +382 -0
  46. package/types/src/Env.d.ts.map +1 -0
  47. package/types/src/FS.d.ts +686 -238
  48. package/types/src/FS.d.ts.map +1 -1
  49. package/types/src/Func.d.ts +380 -135
  50. package/types/src/Func.d.ts.map +1 -1
  51. package/types/src/Http.d.ts +374 -0
  52. package/types/src/Http.d.ts.map +1 -0
  53. package/types/src/Id.d.ts +149 -68
  54. package/types/src/Id.d.ts.map +1 -1
  55. package/types/src/Logger.d.ts +363 -46
  56. package/types/src/Logger.d.ts.map +1 -1
  57. package/types/src/Num.d.ts +473 -137
  58. package/types/src/Num.d.ts.map +1 -1
  59. package/types/src/Obj.d.ts +390 -104
  60. package/types/src/Obj.d.ts.map +1 -1
  61. package/types/src/SQLite.d.ts +646 -309
  62. package/types/src/SQLite.d.ts.map +1 -1
  63. package/types/src/Schema.d.ts +940 -0
  64. package/types/src/Schema.d.ts.map +1 -0
  65. package/types/src/Str.d.ts +569 -166
  66. package/types/src/Str.d.ts.map +1 -1
  67. package/types/src/Sys.d.ts +269 -0
  68. package/types/src/Sys.d.ts.map +1 -0
  69. package/types/src/Time.d.ts +663 -204
  70. package/types/src/Time.d.ts.map +1 -1
  71. package/types/src/Utils.d.ts +154 -102
  72. package/types/src/Utils.d.ts.map +1 -1
  73. package/types/src/errors.d.ts +292 -0
  74. package/types/src/errors.d.ts.map +1 -0
  75. package/types/index.d.ts.map +0 -1
package/src/Num.js CHANGED
@@ -1,366 +1,849 @@
1
+ "use strict";
2
+
1
3
  /**
2
- * Number & math utilities.
4
+ * Numbers: clamping, rounding that gets decimals right, statistics, and
5
+ * formatting for humans (sizes, money, ordinals, compact numbers) in any
6
+ * language. Formatting is English by default: change it with `setLocale()`
7
+ * or per call with `{ locale }`.
3
8
  *
4
- * Clamping, rounding, ranges, human-friendly formatting (bytes, durations,
5
- * ordinals, abbreviations) and safe parsing • the stuff `Math` leaves out.
9
+ * @example
10
+ * num.round(1.005, 2); // 1.01
11
+ * num.formatBytes(1536); // "1.5 KB"
12
+ * num.currency(1234.5, "EUR", { locale: "fr" }); // "1 234,50 €"
13
+ * num.percentile(latencies, 95);
6
14
  */
7
- module.exports = {
8
- /**
9
- * Clamps a number between a minimum and maximum.
10
- *
11
- * @example
12
- * nodeComfort.num.clamp(15, 0, 10); // 10
13
- * nodeComfort.num.clamp(-3, 0, 10); // 0
14
- *
15
- * @param {number} value - Value to clamp.
16
- * @param {number} min - Lower bound.
17
- * @param {number} max - Upper bound.
18
- * @returns {number} The clamped value.
19
- */
20
- clamp(value, min, max) {
21
- return Math.min(Math.max(value, min), max);
22
- },
23
-
24
- /**
25
- * Checks whether a number is within an inclusive `[min, max]` range.
26
- *
27
- * @param {number} value - Value to test.
28
- * @param {number} min - Lower bound.
29
- * @param {number} max - Upper bound.
30
- * @returns {boolean} True if within range.
31
- */
32
- inRange(value, min, max) {
33
- return value >= min && value <= max;
34
- },
35
-
36
- /**
37
- * Rounds a number to a given number of decimal places (avoids float artifacts).
38
- *
39
- * @example
40
- * nodeComfort.num.round(1.005, 2); // 1.01
41
- *
42
- * @param {number} value - Value to round.
43
- * @param {number} [decimals=0] - Number of decimal places.
44
- * @returns {number} The rounded number.
45
- */
46
- round(value, decimals = 0) {
47
- const factor = 10 ** decimals;
48
- return Math.round((value + Number.EPSILON) * factor) / factor;
49
- },
50
-
51
- /**
52
- * Rounds a number down to a given number of decimal places.
53
- *
54
- * @param {number} value - Value to round.
55
- * @param {number} [decimals=0] - Number of decimal places.
56
- * @returns {number} The floored number.
57
- */
58
- floor(value, decimals = 0) {
59
- const factor = 10 ** decimals;
60
- return Math.floor(value * factor) / factor;
61
- },
62
-
63
- /**
64
- * Rounds a number up to a given number of decimal places.
65
- *
66
- * @param {number} value - Value to round.
67
- * @param {number} [decimals=0] - Number of decimal places.
68
- * @returns {number} The ceiled number.
69
- */
70
- ceil(value, decimals = 0) {
71
- const factor = 10 ** decimals;
72
- return Math.ceil(value * factor) / factor;
73
- },
74
-
75
- /**
76
- * Linearly interpolates between `start` and `end` by ratio `t` (0..1).
77
- *
78
- * @example
79
- * nodeComfort.num.lerp(0, 100, 0.5); // 50
80
- *
81
- * @param {number} start - Start value.
82
- * @param {number} end - End value.
83
- * @param {number} t - Interpolation factor (usually 0..1).
84
- * @returns {number} The interpolated value.
85
- */
86
- lerp(start, end, t) {
87
- return start + (end - start) * t;
88
- },
89
-
90
- /**
91
- * Re-maps a number from one range to another.
92
- *
93
- * @example
94
- * nodeComfort.num.mapRange(5, 0, 10, 0, 100); // 50
95
- *
96
- * @param {number} value - Value to map.
97
- * @param {number} inMin - Input range minimum.
98
- * @param {number} inMax - Input range maximum.
99
- * @param {number} outMin - Output range minimum.
100
- * @param {number} outMax - Output range maximum.
101
- * @returns {number} The mapped value.
102
- */
103
- mapRange(value, inMin, inMax, outMin, outMax) {
104
- if (inMax === inMin) return outMin;
105
- return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
106
- },
107
-
108
- /**
109
- * Returns a random number (float) in `[min, max)`.
110
- *
111
- * Not cryptographically secure. For secure randomness use `nodeComfort.id`.
112
- *
113
- * @param {number} [min=0] - Lower bound (inclusive).
114
- * @param {number} [max=1] - Upper bound (exclusive).
115
- * @returns {number} A random float.
116
- */
117
- random(min = 0, max = 1) {
118
- return Math.random() * (max - min) + min;
119
- },
120
-
121
- /**
122
- * Returns a random integer in `[min, max]` (both inclusive).
123
- *
124
- * @example
125
- * nodeComfort.num.randomInt(1, 6); // dice roll
126
- *
127
- * @param {number} min - Lower bound (inclusive).
128
- * @param {number} max - Upper bound (inclusive).
129
- * @returns {number} A random integer.
130
- */
131
- randomInt(min, max) {
132
- min = Math.ceil(min);
133
- max = Math.floor(max);
134
- return Math.floor(Math.random() * (max - min + 1)) + min;
135
- },
136
-
137
- /**
138
- * Sums all numbers in an array.
139
- *
140
- * @param {number[]} values - Array of numbers.
141
- * @returns {number} The sum (0 for an empty array).
142
- */
143
- sum(values) {
144
- return values.reduce((acc, n) => acc + n, 0);
145
- },
146
-
147
- /**
148
- * Returns the arithmetic mean (average) of an array of numbers.
149
- *
150
- * @param {number[]} values - Array of numbers.
151
- * @returns {number} The average (0 for an empty array).
152
- */
153
- average(values) {
154
- return values.length ? this.sum(values) / values.length : 0;
155
- },
156
-
157
- /**
158
- * Returns the median value of an array of numbers.
159
- *
160
- * @param {number[]} values - Array of numbers.
161
- * @returns {number} The median (0 for an empty array).
162
- */
163
- median(values) {
164
- if (!values.length) return 0;
165
- const sorted = [...values].sort((a, b) => a - b);
166
- const mid = Math.floor(sorted.length / 2);
167
- return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
168
- },
169
-
170
- /**
171
- * Returns the smallest number in an array.
172
- *
173
- * @param {number[]} values - Array of numbers.
174
- * @returns {number|undefined} The minimum, or undefined if empty.
175
- */
176
- min(values) {
177
- return values.length ? Math.min(...values) : undefined;
178
- },
179
-
180
- /**
181
- * Returns the largest number in an array.
182
- *
183
- * @param {number[]} values - Array of numbers.
184
- * @returns {number|undefined} The maximum, or undefined if empty.
185
- */
186
- max(values) {
187
- return values.length ? Math.max(...values) : undefined;
188
- },
189
-
190
- /**
191
- * Computes what percentage `value` is of `total`.
192
- *
193
- * @example
194
- * nodeComfort.num.percent(25, 200); // 12.5
195
- *
196
- * @param {number} value - Partial value.
197
- * @param {number} total - Total value.
198
- * @param {number} [decimals=2] - Decimal places to keep.
199
- * @returns {number} The percentage.
200
- */
201
- percent(value, total, decimals = 2) {
202
- if (!total) return 0;
203
- return this.round((value / total) * 100, decimals);
204
- },
205
-
206
- /**
207
- * Formats a byte count into a human-readable string (KB, MB, GB, ...).
208
- *
209
- * By default uses binary sizing (base 1024) with familiar `KB`/`MB` labels.
210
- * Set `binary: false` for SI sizing (base 1000), or `iec: true` for
211
- * strict `KiB`/`MiB` labels.
212
- *
213
- * @example
214
- * nodeComfort.num.formatBytes(1536); // "1.5 KB"
215
- * nodeComfort.num.formatBytes(1073741824); // "1 GB"
216
- * nodeComfort.num.formatBytes(1000, { binary: false }); // "1 KB"
217
- * nodeComfort.num.formatBytes(1024, { iec: true }); // "1 KiB"
218
- *
219
- * @param {number} bytes - Number of bytes.
220
- * @param {{ decimals?: number, binary?: boolean, iec?: boolean }} [options] - Formatting options.
221
- * @returns {string} The human-readable size.
222
- */
223
- formatBytes(bytes, options = {}) {
224
- if (!Number.isFinite(bytes) || bytes === 0) return "0 B";
225
- const decimals = options.decimals ?? 1;
226
- const base = options.binary === false ? 1000 : 1024;
227
- const units = options.iec
228
- ? ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]
229
- : ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
230
- const sign = bytes < 0 ? "-" : "";
231
- bytes = Math.abs(bytes);
232
- const i = Math.min(Math.floor(Math.log(bytes) / Math.log(base)), units.length - 1);
233
- return `${sign}${this.round(bytes / base ** i, decimals)} ${units[i]}`;
234
- },
235
-
236
- /**
237
- * Formats a large number with a compact suffix (K, M, B, T).
238
- *
239
- * @example
240
- * nodeComfort.num.abbreviate(1500); // "1.5K"
241
- * nodeComfort.num.abbreviate(2_400_000); // "2.4M"
242
- *
243
- * @param {number} value - Number to abbreviate.
244
- * @param {number} [decimals=1] - Decimal places.
245
- * @returns {string} The abbreviated number.
246
- */
247
- abbreviate(value, decimals = 1) {
248
- const sign = value < 0 ? "-" : "";
249
- value = Math.abs(value);
250
- const units = [
251
- { v: 1e12, s: "T" },
252
- { v: 1e9, s: "B" },
253
- { v: 1e6, s: "M" },
254
- { v: 1e3, s: "K" },
255
- ];
256
- for (const { v, s } of units) {
257
- if (value >= v) return `${sign}${this.round(value / v, decimals)}${s}`;
258
- }
259
- return `${sign}${value}`;
260
- },
261
-
262
- /**
263
- * Inserts thousands separators into a number.
264
- *
265
- * @example
266
- * nodeComfort.num.thousands(1234567); // "1,234,567"
267
- * nodeComfort.num.thousands(1234.5, " "); // "1 234.5"
268
- *
269
- * @param {number} value - Number to format.
270
- * @param {string} [separator=","] - Thousands separator.
271
- * @returns {string} The formatted number.
272
- */
273
- thousands(value, separator = ",") {
274
- const [int, dec] = String(value).split(".");
275
- const withSep = int.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
276
- return dec ? `${withSep}.${dec}` : withSep;
277
- },
278
-
279
- /**
280
- * Returns the English ordinal suffix for a number ("st", "nd", "rd", "th").
281
- *
282
- * @example
283
- * nodeComfort.num.ordinal(1); // "1st"
284
- * nodeComfort.num.ordinal(22); // "22nd"
285
- *
286
- * @param {number} value - The number.
287
- * @returns {string} The number with its ordinal suffix.
288
- */
289
- ordinal(value) {
290
- const abs = Math.abs(value) % 100;
291
- const suffix =
292
- abs >= 11 && abs <= 13
293
- ? "th"
294
- : ["th", "st", "nd", "rd"][Math.abs(value) % 10] || "th";
295
- return `${value}${suffix}`;
296
- },
297
-
298
- /**
299
- * Parses a value into a number, returning a fallback when it isn't numeric.
300
- *
301
- * @example
302
- * nodeComfort.num.parse("42px"); // 42
303
- * nodeComfort.num.parse("nope", 0); // 0
304
- *
305
- * @param {*} value - Value to parse.
306
- * @param {number} [fallback=NaN] - Value returned when parsing fails.
307
- * @returns {number} The parsed number or the fallback.
308
- */
309
- parse(value, fallback = NaN) {
310
- if (typeof value === "number") return Number.isNaN(value) ? fallback : value;
311
- const parsed = parseFloat(String(value).replace(/[^0-9.eE+-]/g, ""));
312
- return Number.isNaN(parsed) ? fallback : parsed;
313
- },
314
-
315
- /**
316
- * Checks whether a number is even.
317
- * @param {number} value - Number to check.
318
- * @returns {boolean} True if even.
319
- */
320
- isEven(value) {
321
- return value % 2 === 0;
322
- },
323
-
324
- /**
325
- * Checks whether a number is odd.
326
- * @param {number} value - Number to check.
327
- * @returns {boolean} True if odd.
328
- */
329
- isOdd(value) {
330
- return Math.abs(value % 2) === 1;
331
- },
332
-
333
- /**
334
- * Returns an array of numbers from `start` to `end`.
335
- *
336
- * `end` is exclusive. Supports a custom step and descending ranges.
337
- *
338
- * @example
339
- * nodeComfort.num.range(4); // [0, 1, 2, 3]
340
- * nodeComfort.num.range(1, 5); // [1, 2, 3, 4]
341
- * nodeComfort.num.range(0, 10, 2); // [0, 2, 4, 6, 8]
342
- * nodeComfort.num.range(5, 0); // [5, 4, 3, 2, 1]
343
- *
344
- * @param {number} start - Start (or end if it's the only argument).
345
- * @param {number} [end] - End (exclusive).
346
- * @param {number} [step] - Step size (defaults to 1 or -1 based on direction).
347
- * @returns {number[]} The generated range.
348
- */
349
- range(start, end, step) {
350
- if (end === undefined) {
351
- end = start;
352
- start = 0;
353
- }
354
- const descending = end < start;
355
- step = step ?? (descending ? -1 : 1);
356
- if (step === 0) return [];
357
-
358
- const result = [];
359
- if (step > 0) {
360
- for (let i = start; i < end; i += step) result.push(i);
361
- } else {
362
- for (let i = start; i > end; i += step) result.push(i);
363
- }
15
+
16
+ /** The default language, changed with `setLocale()`. */
17
+ const _config = { locale: "en" };
18
+
19
+ /**
20
+ * Options for `formatBytes()`.
21
+ * @typedef {object} FormatBytesOptions
22
+ * @property {number} [decimals] Maximum decimals. Defaults to `1`.
23
+ * @property {boolean} [binary] 1 KB is 1024 bytes when `true`, 1000 when `false`. Defaults to `true`.
24
+ * @property {boolean} [iec] Use `KiB`, `MiB`, `GiB` labels.
25
+ * @property {string} [locale] Formats the number for a language, like `"fr"` for `"1,5 KB"`.
26
+ * @property {string[]} [units] Your own labels from bytes up, like `["o", "Ko", "Mo", "Go", "To"]`.
27
+ */
28
+
29
+ /**
30
+ * Options for `parseBytes()`.
31
+ * @typedef {object} ParseBytesOptions
32
+ * @property {boolean} [binary] 1 KB is 1024 bytes when `true`, 1000 when `false`. `KiB` units are always 1024. Defaults to `true`.
33
+ */
34
+
35
+ /**
36
+ * Options for `format()`.
37
+ * @typedef {object} FormatNumberOptions
38
+ * @property {string} [locale] Language, like `"fr"` or `"en-US"`. Defaults to the one set with `setLocale()`, or `"en"`.
39
+ * @property {number} [decimals] Exact number of decimals.
40
+ * @property {number} [minDecimals] Minimum decimals.
41
+ * @property {number} [maxDecimals] Maximum decimals. Defaults to `3`.
42
+ * @property {boolean} [compact] Short notation like `1.2K` or `3.4M`.
43
+ * @property {"percent"|"decimal"} [style] `"percent"` multiplies by 100 and adds `%`.
44
+ * @property {string} [unit] Any `Intl` unit: `"kilometer"`, `"celsius"`, `"megabyte"`...
45
+ * @property {"short"|"long"|"narrow"} [unitDisplay] How the unit is written. Defaults to `"short"`.
46
+ * @property {"auto"|"always"|"never"|"exceptZero"} [signDisplay] When to show the sign. `"exceptZero"` gives `+5`.
47
+ * @property {boolean} [grouping] Thousands separators. Defaults to `true`.
48
+ */
49
+
50
+ /**
51
+ * Options for `currency()`.
52
+ * @typedef {object} CurrencyOptions
53
+ * @property {string} [locale] Language. Defaults to the one set with `setLocale()`, or `"en"`.
54
+ * @property {number} [decimals] Defaults to the currency's usual decimals (2 for EUR, 0 for JPY).
55
+ * @property {"symbol"|"narrowSymbol"|"code"|"name"} [display] `€`, `€`, `EUR` or `euros`. Defaults to `"symbol"`.
56
+ * @property {boolean} [accounting] Show negative amounts in parentheses, like `($5.00)`.
57
+ */
58
+
59
+ /**
60
+ * Options for `ordinal()`.
61
+ * @typedef {object} OrdinalOptions
62
+ * @property {string} [locale] Built in: `en`, `fr`, `es`, `it`, `pt`, `de`, `nl`. Defaults to the one set with `setLocale()`, or `"en"`.
63
+ * @property {Partial<Record<Intl.LDMLPluralRule, string>>} [suffixes] Your own suffixes, for other languages.
64
+ */
65
+
66
+ /**
67
+ * Options for `variance()` and `stdDev()`.
68
+ * @typedef {object} VarianceOptions
69
+ * @property {boolean} [sample] Sample statistic (divides by n - 1) instead of population.
70
+ */
71
+
72
+ /**
73
+ * Multiplies by 10^exp without floating-point error.
74
+ * @param {number} value
75
+ * @param {number} exp
76
+ * @returns {number}
77
+ */
78
+ const _shift = (value, exp) => {
79
+ const [mantissa, e = "0"] = String(value).split("e");
80
+ return Number(`${mantissa}e${Number(e) + exp}`);
81
+ };
82
+
83
+ /**
84
+ * @param {number} value
85
+ * @param {number} decimals
86
+ * @param {(n: number) => number} fn
87
+ * @returns {number}
88
+ */
89
+ const _roundWith = (value, decimals, fn) => {
90
+ if (!Number.isFinite(value)) return value;
91
+ return _shift(fn(_shift(value, decimals)), -decimals);
92
+ };
93
+
94
+ const _SIZE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
95
+ const _IEC_UNITS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
96
+
97
+ /** @type {Record<string, Partial<Record<Intl.LDMLPluralRule, string>>>} */
98
+ const _ORDINAL_SUFFIXES = {
99
+ en: { one: "st", two: "nd", few: "rd", other: "th" },
100
+ fr: { one: "er", other: "e" },
101
+ es: { other: "º" },
102
+ it: { other: "º" },
103
+ pt: { other: "º" },
104
+ de: { other: "." },
105
+ nl: { other: "e" },
106
+ };
107
+
108
+ /**
109
+ * Keeps a number between `min` and `max`.
110
+ *
111
+ * @example
112
+ * num.clamp(150, 0, 100); // 100
113
+ * num.clamp(-5, 0, 100); // 0
114
+ *
115
+ * @param {number} value
116
+ * @param {number} min
117
+ * @param {number} max
118
+ * @returns {number}
119
+ */
120
+ function clamp(value, min, max) {
121
+ return Math.min(Math.max(value, min), max);
122
+ }
123
+
124
+ /**
125
+ * Is the number between `min` and `max`, both included?
126
+ *
127
+ * @example
128
+ * num.inRange(10, 0, 10); // true
129
+ *
130
+ * @param {number} value
131
+ * @param {number} min
132
+ * @param {number} max
133
+ * @returns {boolean}
134
+ */
135
+ function inRange(value, min, max) {
136
+ return value >= min && value <= max;
137
+ }
138
+
139
+ /**
140
+ * Wraps a number around a range, like an angle or a carousel index.
141
+ * `max` is excluded.
142
+ *
143
+ * @example
144
+ * num.wrap(370, 0, 360); // 10
145
+ * num.wrap(-1, 0, 5); // 4
146
+ *
147
+ * @param {number} value
148
+ * @param {number} min
149
+ * @param {number} max
150
+ * @returns {number}
151
+ */
152
+ function wrap(value, min, max) {
153
+ const span = max - min;
154
+ if (span <= 0) return min;
155
+ return ((((value - min) % span) + span) % span) + min;
156
+ }
157
+
158
+ /**
159
+ * Rounds to `decimals` places without floating-point surprises:
160
+ * `Math.round(1.005 * 100) / 100` gives `1`, this gives `1.01`. Halves round
161
+ * away from zero, like in a spreadsheet.
162
+ *
163
+ * @example
164
+ * num.round(1.005, 2); // 1.01
165
+ * num.round(1234, -2); // 1200
166
+ * num.round(-2.5); // -3
167
+ *
168
+ * @param {number} value
169
+ * @param {number} [decimals=0] Negative values round to tens, hundreds...
170
+ * @returns {number}
171
+ */
172
+ function round(value, decimals = 0) {
173
+ return _roundWith(value, decimals, (n) => Math.sign(n) * Math.round(Math.abs(n)) || 0);
174
+ }
175
+
176
+ /**
177
+ * Rounds down to `decimals` places.
178
+ *
179
+ * @example
180
+ * num.floor(1.789, 2); // 1.78
181
+ *
182
+ * @param {number} value
183
+ * @param {number} [decimals=0]
184
+ * @returns {number}
185
+ */
186
+ function floor(value, decimals = 0) {
187
+ return _roundWith(value, decimals, Math.floor);
188
+ }
189
+
190
+ /**
191
+ * Rounds up to `decimals` places.
192
+ *
193
+ * @example
194
+ * num.ceil(1.231, 2); // 1.24
195
+ *
196
+ * @param {number} value
197
+ * @param {number} [decimals=0]
198
+ * @returns {number}
199
+ */
200
+ function ceil(value, decimals = 0) {
201
+ return _roundWith(value, decimals, Math.ceil);
202
+ }
203
+
204
+ /**
205
+ * Rounds to the nearest multiple of `step`.
206
+ *
207
+ * @example
208
+ * num.snap(17, 5); // 15
209
+ * num.snap(0.37, 0.25); // 0.25
210
+ *
211
+ * @param {number} value
212
+ * @param {number} step
213
+ * @returns {number}
214
+ */
215
+ function snap(value, step) {
216
+ if (!step) return value;
217
+ const decimals = (String(step).split(".")[1] ?? "").length;
218
+ return round(Math.round(value / step) * step, decimals);
219
+ }
220
+
221
+ /**
222
+ * Are two numbers equal within a small tolerance? The right way to compare
223
+ * float results, since `0.1 + 0.2 === 0.3` is `false`.
224
+ *
225
+ * @example
226
+ * num.approxEqual(0.1 + 0.2, 0.3); // true
227
+ *
228
+ * @param {number} a
229
+ * @param {number} b
230
+ * @param {number} [epsilon=Number.EPSILON * 16] Allowed difference, relative to the size of the numbers.
231
+ * @returns {boolean}
232
+ */
233
+ function approxEqual(a, b, epsilon = Number.EPSILON * 16) {
234
+ if (a === b) return true;
235
+ const diff = Math.abs(a - b);
236
+ return diff <= epsilon * Math.max(1, Math.abs(a), Math.abs(b));
237
+ }
238
+
239
+ /**
240
+ * The value at ratio `t` between `start` and `end`.
241
+ *
242
+ * @example
243
+ * num.lerp(0, 100, 0.25); // 25
244
+ *
245
+ * @param {number} start
246
+ * @param {number} end
247
+ * @param {number} t Usually between 0 and 1.
248
+ * @returns {number}
249
+ */
250
+ function lerp(start, end, t) {
251
+ return start + (end - start) * t;
252
+ }
253
+
254
+ /**
255
+ * Moves a number from one range to another, keeping its relative position.
256
+ *
257
+ * @example
258
+ * num.mapRange(5, 0, 10, 0, 100); // 50
259
+ * num.mapRange(75, 0, 100, 1, 0); // 0.25
260
+ *
261
+ * @param {number} value
262
+ * @param {number} inMin
263
+ * @param {number} inMax
264
+ * @param {number} outMin
265
+ * @param {number} outMax
266
+ * @returns {number}
267
+ */
268
+ function mapRange(value, inMin, inMax, outMin, outMax) {
269
+ if (inMax === inMin) return outMin;
270
+ return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
271
+ }
272
+
273
+ /**
274
+ * A random float from `min` (included) to `max` (excluded). Not for
275
+ * security: it uses `Math.random`.
276
+ *
277
+ * @example
278
+ * num.random(10, 20); // 13.84...
279
+ *
280
+ * @param {number} [min=0]
281
+ * @param {number} [max=1]
282
+ * @returns {number}
283
+ */
284
+ function random(min = 0, max = 1) {
285
+ return Math.random() * (max - min) + min;
286
+ }
287
+
288
+ /**
289
+ * A random integer from `min` to `max`, both included. Not for security.
290
+ *
291
+ * @example
292
+ * num.randomInt(1, 6); // a dice roll
293
+ *
294
+ * @param {number} min
295
+ * @param {number} max
296
+ * @returns {number}
297
+ */
298
+ function randomInt(min, max) {
299
+ const lo = Math.ceil(Math.min(min, max));
300
+ const hi = Math.floor(Math.max(min, max));
301
+ return Math.floor(Math.random() * (hi - lo + 1)) + lo;
302
+ }
303
+
304
+ /**
305
+ * Adds up numbers.
306
+ *
307
+ * @example
308
+ * num.sum([1, 2, 3]); // 6
309
+ *
310
+ * @param {Iterable<number>} values
311
+ * @returns {number}
312
+ */
313
+ function sum(values) {
314
+ let total = 0;
315
+ for (const n of values) total += n;
316
+ return total;
317
+ }
318
+
319
+ /**
320
+ * The mean, or `0` for an empty list.
321
+ *
322
+ * @example
323
+ * num.average([2, 4, 9]); // 5
324
+ *
325
+ * @param {Iterable<number>} values
326
+ * @returns {number}
327
+ */
328
+ function average(values) {
329
+ const list = [...values];
330
+ return list.length ? sum(list) / list.length : 0;
331
+ }
332
+
333
+ /**
334
+ * The middle value once sorted, or `0` for an empty list.
335
+ *
336
+ * @example
337
+ * num.median([3, 1, 2]); // 2
338
+ * num.median([1, 2, 3, 4]); // 2.5
339
+ *
340
+ * @param {Iterable<number>} values
341
+ * @returns {number}
342
+ */
343
+ function median(values) {
344
+ const sorted = [...values].sort((a, b) => a - b);
345
+ if (!sorted.length) return 0;
346
+ const mid = Math.floor(sorted.length / 2);
347
+ return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
348
+ }
349
+
350
+ /**
351
+ * The most frequent values.
352
+ *
353
+ * @example
354
+ * num.mode([1, 1, 2, 2, 3]); // [1, 2]
355
+ *
356
+ * @param {Iterable<number>} values
357
+ * @returns {number[]}
358
+ */
359
+ function mode(values) {
360
+ /** @type {Map<number, number>} */
361
+ const counts = new Map();
362
+ for (const n of values) counts.set(n, (counts.get(n) ?? 0) + 1);
363
+ const best = Math.max(0, ...counts.values());
364
+ return [...counts].filter(([, c]) => c === best && best > 0).map(([n]) => n);
365
+ }
366
+
367
+ /**
368
+ * The variance: the average squared distance from the mean.
369
+ *
370
+ * @example
371
+ * num.variance([2, 4, 4, 4, 5, 5, 7, 9]); // 4
372
+ *
373
+ * @param {Iterable<number>} values
374
+ * @param {VarianceOptions} [options]
375
+ * @returns {number}
376
+ */
377
+ function variance(values, options = {}) {
378
+ const list = [...values];
379
+ const divisor = options.sample ? list.length - 1 : list.length;
380
+ if (divisor <= 0) return 0;
381
+ const mean = sum(list) / list.length;
382
+ return list.reduce((acc, n) => acc + (n - mean) ** 2, 0) / divisor;
383
+ }
384
+
385
+ /**
386
+ * The standard deviation.
387
+ *
388
+ * @example
389
+ * num.stdDev([2, 4, 4, 4, 5, 5, 7, 9]); // 2
390
+ *
391
+ * @param {Iterable<number>} values
392
+ * @param {VarianceOptions} [options]
393
+ * @returns {number}
394
+ */
395
+ function stdDev(values, options = {}) {
396
+ return Math.sqrt(variance(values, options));
397
+ }
398
+
399
+ /**
400
+ * The value under which `p` percent of the values fall, interpolated like
401
+ * Excel's `PERCENTILE.INC`.
402
+ *
403
+ * @example
404
+ * num.percentile([1, 2, 3, 4, 5], 90); // 4.6
405
+ * num.percentile(responseTimes, 95); // p95 latency
406
+ *
407
+ * @param {Iterable<number>} values
408
+ * @param {number} p From 0 to 100.
409
+ * @returns {number} `NaN` for an empty list.
410
+ */
411
+ function percentile(values, p) {
412
+ const sorted = [...values].sort((a, b) => a - b);
413
+ if (!sorted.length) return NaN;
414
+ const rank = (clamp(p, 0, 100) / 100) * (sorted.length - 1);
415
+ const lower = Math.floor(rank);
416
+ const upper = Math.ceil(rank);
417
+ return round(sorted[lower] + (sorted[upper] - sorted[lower]) * (rank - lower), 12);
418
+ }
419
+
420
+ /**
421
+ * The smallest number. Unlike `Math.min(...array)`, huge arrays are fine.
422
+ *
423
+ * @param {Iterable<number>} values
424
+ * @returns {number | undefined} `undefined` for an empty list.
425
+ */
426
+ function min(values) {
427
+ let best;
428
+ for (const n of values) if (best === undefined || n < best) best = n;
429
+ return best;
430
+ }
431
+
432
+ /**
433
+ * The largest number. Unlike `Math.max(...array)`, huge arrays are fine.
434
+ *
435
+ * @param {Iterable<number>} values
436
+ * @returns {number | undefined} `undefined` for an empty list.
437
+ */
438
+ function max(values) {
439
+ let best;
440
+ for (const n of values) if (best === undefined || n > best) best = n;
441
+ return best;
442
+ }
443
+
444
+ /**
445
+ * What percentage `value` is of `total`. Returns `0` when `total` is `0`.
446
+ *
447
+ * @example
448
+ * num.percent(25, 200); // 12.5
449
+ * num.percent(1, 3, 1); // 33.3
450
+ *
451
+ * @param {number} value
452
+ * @param {number} total
453
+ * @param {number} [decimals=2]
454
+ * @returns {number}
455
+ */
456
+ function percent(value, total, decimals = 2) {
457
+ if (!total) return 0;
458
+ return round((value / total) * 100, decimals);
459
+ }
460
+
461
+ /**
462
+ * Greatest common divisor.
463
+ *
464
+ * @example
465
+ * num.gcd(12, 18, 27); // 3
466
+ *
467
+ * @param {...number} values
468
+ * @returns {number}
469
+ */
470
+ function gcd(...values) {
471
+ return values.reduce((a, b) => {
472
+ let x = Math.abs(a);
473
+ let y = Math.abs(b);
474
+ while (y) [x, y] = [y, x % y];
475
+ return x;
476
+ }, 0);
477
+ }
478
+
479
+ /**
480
+ * Least common multiple.
481
+ *
482
+ * @example
483
+ * num.lcm(2, 3, 4); // 12
484
+ *
485
+ * @param {...number} values
486
+ * @returns {number}
487
+ */
488
+ function lcm(...values) {
489
+ if (!values.length) return 0;
490
+ return values.reduce((a, b) => (a === 0 || b === 0 ? 0 : Math.abs(a * b) / gcd(a, b)));
491
+ }
492
+
493
+ /**
494
+ * Is it a prime number?
495
+ *
496
+ * @example
497
+ * num.isPrime(7); // true
498
+ * num.isPrime(1); // false
499
+ *
500
+ * @param {number} value
501
+ * @returns {boolean}
502
+ */
503
+ function isPrime(value) {
504
+ if (!Number.isInteger(value) || value < 2) return false;
505
+ if (value < 4) return true;
506
+ if (value % 2 === 0 || value % 3 === 0) return false;
507
+ for (let i = 5; i * i <= value; i += 6) {
508
+ if (value % i === 0 || value % (i + 2) === 0) return false;
509
+ }
510
+ return true;
511
+ }
512
+
513
+ /**
514
+ * `n!`. Pass `true` to get an exact bigint, since numbers lose precision
515
+ * past 18!.
516
+ *
517
+ * @example
518
+ * num.factorial(5); // 120
519
+ * num.factorial(25, true); // 15511210043330985984000000n
520
+ *
521
+ * @param {number} n
522
+ * @param {boolean} [asBigInt=false]
523
+ * @returns {number | bigint}
524
+ * @throws {RangeError} If `n` is negative or not an integer.
525
+ */
526
+ function factorial(n, asBigInt = false) {
527
+ if (!Number.isInteger(n) || n < 0) throw new RangeError("factorial() expects a non-negative integer");
528
+ if (asBigInt) {
529
+ let result = 1n;
530
+ for (let i = 2n; i <= BigInt(n); i++) result *= i;
364
531
  return result;
365
- },
532
+ }
533
+ let result = 1;
534
+ for (let i = 2; i <= n; i++) result *= i;
535
+ return result;
536
+ }
537
+
538
+ /**
539
+ * Is it even?
540
+ *
541
+ * @param {number} value
542
+ * @returns {boolean}
543
+ */
544
+ function isEven(value) {
545
+ return value % 2 === 0;
546
+ }
547
+
548
+ /**
549
+ * Is it odd? Negative numbers work too.
550
+ *
551
+ * @param {number} value
552
+ * @returns {boolean}
553
+ */
554
+ function isOdd(value) {
555
+ return Math.abs(value % 2) === 1;
556
+ }
557
+
558
+ /**
559
+ * Numbers from `start` up to `end` (excluded). With one argument, counts
560
+ * from 0. Goes down when `end` is smaller.
561
+ *
562
+ * @example
563
+ * num.range(4); // [0, 1, 2, 3]
564
+ * num.range(0, 10, 2); // [0, 2, 4, 6, 8]
565
+ * num.range(5, 0); // [5, 4, 3, 2, 1]
566
+ *
567
+ * @param {number} start
568
+ * @param {number} [end]
569
+ * @param {number} [step] Defaults to `1`, or `-1` when counting down.
570
+ * @returns {number[]}
571
+ */
572
+ function range(start, end, step) {
573
+ let from = start;
574
+ let to = end;
575
+ if (to === undefined) {
576
+ to = from;
577
+ from = 0;
578
+ }
579
+ const inc = step ?? (to < from ? -1 : 1);
580
+ if (inc === 0 || (inc > 0 && from > to) || (inc < 0 && from < to)) return [];
581
+ const decimals = Math.max((String(inc).split(".")[1] ?? "").length, (String(from).split(".")[1] ?? "").length);
582
+ const out = [];
583
+ const count = Math.ceil((to - from) / inc);
584
+ for (let i = 0; i < count; i++) out.push(decimals ? round(from + i * inc, decimals) : from + i * inc);
585
+ return out;
586
+ }
587
+
588
+ /**
589
+ * Reads a number from anything, ignoring trailing text like units.
590
+ *
591
+ * @example
592
+ * num.parse("42px"); // 42
593
+ * num.parse(" -3.5 kg"); // -3.5
594
+ * num.parse("nope", 0); // 0
595
+ *
596
+ * @param {unknown} value
597
+ * @param {number} [fallback=NaN] Returned when there's no number to read.
598
+ * @returns {number}
599
+ */
600
+ function parse(value, fallback = NaN) {
601
+ if (typeof value === "number") return Number.isNaN(value) ? fallback : value;
602
+ if (typeof value === "bigint") return Number(value);
603
+ const match = /[-+]?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/i.exec(String(value));
604
+ const parsed = match ? Number(match[0]) : NaN;
605
+ return Number.isNaN(parsed) ? fallback : parsed;
606
+ }
607
+
608
+ /**
609
+ * Turns a size like `"10 MB"` into bytes. The opposite of `formatBytes()`.
610
+ *
611
+ * @example
612
+ * num.parseBytes("1.5 KB"); // 1536
613
+ * num.parseBytes("2 GiB"); // 2147483648
614
+ * num.parseBytes("1 KB", { binary: false }); // 1000
615
+ *
616
+ * @param {string | number} value A size like `"512kb"` or `"1.5G"`, or a number of bytes.
617
+ * @param {ParseBytesOptions} [options]
618
+ * @returns {number} Bytes, or `NaN` if it can't be read.
619
+ */
620
+ function parseBytes(value, options = {}) {
621
+ if (typeof value === "number") return value;
622
+ const match = /^\s*([-+]?(?:\d+\.?\d*|\.\d+))\s*([kmgtpezy]?)(i?)(b?)(?:ytes?)?\s*$/i.exec(String(value));
623
+ if (!match) return NaN;
624
+ const [, amount, prefix, iec] = match;
625
+ const base = iec || options.binary !== false ? 1024 : 1000;
626
+ const power = prefix ? "kmgtpezy".indexOf(prefix.toLowerCase()) + 1 : 0;
627
+ return Math.floor(Number(amount) * base ** power);
628
+ }
629
+
630
+ /**
631
+ * Formats a byte count as a readable size.
632
+ *
633
+ * @example
634
+ * num.formatBytes(1536); // "1.5 KB"
635
+ * num.formatBytes(1000, { binary: false }); // "1 KB"
636
+ * num.formatBytes(1536, { locale: "fr", units: ["o", "Ko", "Mo", "Go"] }); // "1,5 Ko"
637
+ *
638
+ * @param {number} bytes
639
+ * @param {FormatBytesOptions} [options]
640
+ * @returns {string}
641
+ */
642
+ function formatBytes(bytes, options = {}) {
643
+ const units = options.units ?? (options.iec ? _IEC_UNITS : _SIZE_UNITS);
644
+ if (!Number.isFinite(bytes) || bytes === 0) return `0 ${units[0]}`;
645
+ const decimals = options.decimals ?? 1;
646
+ const base = options.binary === false ? 1000 : 1024;
647
+ const sign = bytes < 0 ? "-" : "";
648
+ const abs = Math.abs(bytes);
649
+ const i = Math.min(Math.floor(Math.log(abs) / Math.log(base)), units.length - 1);
650
+ const amount = round(abs / base ** i, decimals);
651
+ const locale = options.locale ?? (_config.locale === "en" ? undefined : _config.locale);
652
+ const text = locale ? new Intl.NumberFormat(locale, { maximumFractionDigits: decimals }).format(amount) : String(amount);
653
+ return `${sign}${text} ${units[i]}`;
654
+ }
655
+
656
+ /**
657
+ * Formats a number for display, with `Intl.NumberFormat` doing the work:
658
+ * separators, decimals, compact notation, percentages, units.
659
+ *
660
+ * @example
661
+ * num.format(1234567.891); // "1,234,567.891"
662
+ * num.format(1234567.891, { locale: "fr" }); // "1 234 567,891"
663
+ * num.format(0.256, { style: "percent" }); // "26%"
664
+ * num.format(1500, { compact: true }); // "1.5K"
665
+ * num.format(3.5, { unit: "kilometer" }); // "3.5 km"
666
+ *
667
+ * @param {number | bigint} value
668
+ * @param {FormatNumberOptions} [options]
669
+ * @returns {string}
670
+ */
671
+ function format(value, options = {}) {
672
+ /** @type {Intl.NumberFormatOptions} */
673
+ const intl = {};
674
+ if (options.decimals !== undefined) {
675
+ intl.minimumFractionDigits = options.decimals;
676
+ intl.maximumFractionDigits = options.decimals;
677
+ }
678
+ if (options.minDecimals !== undefined) intl.minimumFractionDigits = options.minDecimals;
679
+ if (options.maxDecimals !== undefined) intl.maximumFractionDigits = options.maxDecimals;
680
+ if (options.compact) {
681
+ intl.notation = "compact";
682
+ intl.compactDisplay = "short";
683
+ }
684
+ if (options.style === "percent") intl.style = "percent";
685
+ if (options.unit) {
686
+ intl.style = "unit";
687
+ intl.unit = options.unit;
688
+ intl.unitDisplay = options.unitDisplay ?? "short";
689
+ }
690
+ if (options.signDisplay) intl.signDisplay = options.signDisplay;
691
+ if (options.grouping === false) intl.useGrouping = false;
692
+ return new Intl.NumberFormat(options.locale ?? _config.locale, intl).format(value);
693
+ }
694
+
695
+ /**
696
+ * Formats an amount of money.
697
+ *
698
+ * @example
699
+ * num.currency(1234.5, "USD"); // "$1,234.50"
700
+ * num.currency(1234.5, "EUR", { locale: "fr" }); // "1 234,50 €"
701
+ * num.currency(-5, "USD", { accounting: true }); // "($5.00)"
702
+ *
703
+ * @param {number | bigint} value
704
+ * @param {string} code A currency code like `"EUR"`, `"USD"` or `"JPY"`.
705
+ * @param {CurrencyOptions} [options]
706
+ * @returns {string}
707
+ */
708
+ function currency(value, code, options = {}) {
709
+ /** @type {Intl.NumberFormatOptions} */
710
+ const intl = { style: "currency", currency: code, currencyDisplay: options.display ?? "symbol" };
711
+ if (options.decimals !== undefined) {
712
+ intl.minimumFractionDigits = options.decimals;
713
+ intl.maximumFractionDigits = options.decimals;
714
+ }
715
+ if (options.accounting) intl.currencySign = "accounting";
716
+ return new Intl.NumberFormat(options.locale ?? _config.locale, intl).format(value);
717
+ }
718
+
719
+ /**
720
+ * Shortens big numbers with `K`, `M`, `B` or `T`. For other languages, use
721
+ * `format(value, { compact: true, locale })`.
722
+ *
723
+ * @example
724
+ * num.abbreviate(1500); // "1.5K"
725
+ * num.abbreviate(2_400_000); // "2.4M"
726
+ *
727
+ * @param {number} value
728
+ * @param {number} [decimals=1]
729
+ * @returns {string}
730
+ */
731
+ function abbreviate(value, decimals = 1) {
732
+ const sign = value < 0 ? "-" : "";
733
+ const abs = Math.abs(value);
734
+ for (const [limit, suffix] of /** @type {Array<[number, string]>} */ ([[1e12, "T"], [1e9, "B"], [1e6, "M"], [1e3, "K"]])) {
735
+ if (abs >= limit) return `${sign}${round(abs / limit, decimals)}${suffix}`;
736
+ }
737
+ return `${sign}${round(abs, decimals)}`;
738
+ }
739
+
740
+ /**
741
+ * Adds thousands separators, leaving decimals alone.
742
+ *
743
+ * @example
744
+ * num.thousands(1234567.89, " "); // "1 234 567.89"
745
+ *
746
+ * @param {number | string} value
747
+ * @param {string} [separator=","]
748
+ * @returns {string}
749
+ */
750
+ function thousands(value, separator = ",") {
751
+ const [int, dec] = String(value).split(".");
752
+ const grouped = int.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
753
+ return dec === undefined ? grouped : `${grouped}.${dec}`;
754
+ }
755
+
756
+ /**
757
+ * Adds the ordinal suffix: 1st, 2nd, 3rd... in several languages.
758
+ *
759
+ * @example
760
+ * num.ordinal(22); // "22nd"
761
+ * num.ordinal(1, { locale: "fr" }); // "1er"
762
+ * num.ordinal(3, { locale: "de" }); // "3."
763
+ *
764
+ * @param {number} value
765
+ * @param {OrdinalOptions} [options]
766
+ * @returns {string}
767
+ */
768
+ function ordinal(value, options = {}) {
769
+ const locale = options.locale ?? _config.locale;
770
+ const language = locale.split(/[-_]/)[0].toLowerCase();
771
+ const suffixes = options.suffixes ?? _ORDINAL_SUFFIXES[language] ?? _ORDINAL_SUFFIXES.en;
772
+ let category;
773
+ try {
774
+ category = new Intl.PluralRules(locale, { type: "ordinal" }).select(value);
775
+ } catch {
776
+ category = new Intl.PluralRules("en", { type: "ordinal" }).select(value);
777
+ }
778
+ return `${value}${suffixes[category] ?? suffixes.other ?? ""}`;
779
+ }
780
+
781
+ /**
782
+ * Sets the default language of `format`, `currency`, `percent`,
783
+ * `abbreviate`, `ordinal` and `formatBytes`.
784
+ *
785
+ * @example
786
+ * num.setLocale("fr");
787
+ * num.currency(1234.5, "EUR"); // "1 234,50 €"
788
+ *
789
+ * @param {string} locale Like `"fr"`, `"en-GB"` or `"pt-BR"`.
790
+ * @returns {typeof import("./Num")} The module, so calls chain.
791
+ */
792
+ function setLocale(locale) {
793
+ _config.locale = locale;
794
+ return module.exports;
795
+ }
796
+
797
+ /**
798
+ * The current default language.
799
+ *
800
+ * @example
801
+ * num.getLocale(); // "en"
802
+ *
803
+ * @returns {string}
804
+ */
805
+ function getLocale() {
806
+ return _config.locale;
807
+ }
808
+
809
+ module.exports = {
810
+ setLocale,
811
+ getLocale,
812
+ clamp,
813
+ inRange,
814
+ wrap,
815
+ round,
816
+ floor,
817
+ ceil,
818
+ snap,
819
+ approxEqual,
820
+ lerp,
821
+ mapRange,
822
+ random,
823
+ randomInt,
824
+ sum,
825
+ average,
826
+ median,
827
+ mode,
828
+ variance,
829
+ stdDev,
830
+ percentile,
831
+ min,
832
+ max,
833
+ percent,
834
+ gcd,
835
+ lcm,
836
+ isPrime,
837
+ factorial,
838
+ isEven,
839
+ isOdd,
840
+ range,
841
+ parse,
842
+ parseBytes,
843
+ formatBytes,
844
+ format,
845
+ currency,
846
+ abbreviate,
847
+ thousands,
848
+ ordinal,
366
849
  };