@helpers4/number 2.0.2 → 2.0.4

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/README.md CHANGED
@@ -14,14 +14,17 @@ A set of helpers for working with URLs.
14
14
  <!-- AUTOMATIC-METHODS -->
15
15
  - clamp
16
16
  - correctFloat
17
+ - extractNumber
17
18
  - formatCompact
18
19
  - formatSize
19
20
  - inRange
21
+ - isEven
22
+ - isNegative
23
+ - isOdd
24
+ - isPositive
20
25
  - lerp
21
- - mean
22
26
  - random
23
27
  - roundTo
24
- - sum
25
28
  <!-- /AUTOMATIC-METHODS -->
26
29
 
27
30
  ## See
package/lib/index.d.ts CHANGED
@@ -30,13 +30,85 @@ declare function clamp(value: number, min: number, max: number): number;
30
30
  * digits and will silently truncate them. Increase `precision` if you
31
31
  * need to correct drift in very large numbers.
32
32
  *
33
+ * Note: IEEE-754 doubles carry at most ~17 significant decimal digits.
34
+ * Precision values above 17 pad with digits that reflect the underlying
35
+ * binary representation rather than correcting drift.
36
+ *
33
37
  * @param value - The floating-point value to correct
34
- * @param precision - Integer number of significant digits (default: 14)
38
+ * @param precision - Integer number of significant digits between 1 and 100
39
+ * (default: 14). Values above 17 are valid but expose binary noise beyond
40
+ * IEEE-754's meaningful range.
35
41
  * @returns The corrected value
42
+ * @example
43
+ * correctFloat(0.1 + 0.2) // => 0.3
44
+ * correctFloat(1.1 - 0.3) // => 0.8
45
+ * correctFloat(0.1 * 3) // => 0.3
46
+ *
47
+ * // Custom precision: keep 4 significant digits
48
+ * correctFloat(1.23456789, 4) // => 1.235
49
+ * correctFloat(1.23456789, 6) // => 1.23457
36
50
  * @since 2.0.2
37
51
  */
38
52
  declare function correctFloat(value: number, precision?: number): number;
39
53
 
54
+ /**
55
+ * This file is part of helpers4.
56
+ * Copyright (C) 2025 baxyz
57
+ * SPDX-License-Identifier: LGPL-3.0-or-later
58
+ */
59
+ /**
60
+ * Options for {@link extractNumber}.
61
+ */
62
+ interface ExtractNumberOptions {
63
+ /**
64
+ * How to interpret a `-` immediately before the matched digits.
65
+ * - `'auto'`: treated as a minus sign unless it is glued to a preceding letter/digit
66
+ * (e.g. `'-111'` and `'xxx -111'` → negative, but `'xxx-111'` → separator, positive).
67
+ * - `'strict'`: always treated as a minus sign.
68
+ * - `'ignore'`: never treated as a minus sign (always a separator).
69
+ * @default 'auto'
70
+ */
71
+ sign?: 'auto' | 'strict' | 'ignore';
72
+ /**
73
+ * How to interpret a trailing scientific-notation suffix (`e`/`E` + digits, e.g. `1.5e-10`).
74
+ * - `'auto'`: treated as an exponent unless it is glued to a following letter/digit
75
+ * (e.g. `'1e5'` and `'1e5 mol'` → exponential, but `'1e5kg'` → mantissa only).
76
+ * - `'strict'`: always treated as an exponent.
77
+ * - `'ignore'`: never treated as an exponent (mantissa only).
78
+ * @default 'auto'
79
+ */
80
+ exponent?: 'auto' | 'strict' | 'ignore';
81
+ }
82
+ /**
83
+ * Extracts the first number embedded anywhere in a string, or passes through a `number`.
84
+ *
85
+ * Unlike a plain `parseFloat`/`parseInt`, the number does not need to be at the start of
86
+ * the string: digits are searched for anywhere, so leading/trailing text (units, labels, ...)
87
+ * is ignored. A `-` before the digits and a scientific-notation suffix (`e`/`E`) are
88
+ * disambiguated with {@link ExtractNumberOptions.sign} and {@link ExtractNumberOptions.exponent}.
89
+ *
90
+ * Returns `undefined` if no number can be found.
91
+ *
92
+ * @param value - The value to extract a number from
93
+ * @param options - Options controlling sign and exponent disambiguation
94
+ * @returns The extracted number, or `undefined` if none was found
95
+ * @example
96
+ * extractNumber('16.5px') // => 16.5
97
+ * extractNumber('.5rem') // => 0.5 (leading-dot decimal)
98
+ * extractNumber('-.5') // => -0.5 (leading-dot with sign)
99
+ * extractNumber('Wafer 10') // => 10
100
+ * extractNumber('xxx-111') // => 111 ('-' glued to text → separator)
101
+ * extractNumber('xxx -111') // => -111 ('-' preceded by a space → sign)
102
+ * extractNumber('x-.5') // => 0.5 ('-' glued to 'x' → separator; leading-dot decimal follows)
103
+ * extractNumber('-111') // => -111 ('-' at the start of the string → sign)
104
+ * extractNumber('1e5 mol') // => 100000
105
+ * extractNumber('1e5kg') // => 1 ('e5' glued to text → mantissa only)
106
+ * extractNumber('no number') // => undefined
107
+ * extractNumber(42) // => 42
108
+ * @since 2.0.4
109
+ */
110
+ declare function extractNumber(value: unknown, options?: ExtractNumberOptions): number | undefined;
111
+
40
112
  /**
41
113
  * This file is part of helpers4.
42
114
  * Copyright (C) 2025 baxyz
@@ -119,6 +191,98 @@ interface InRangeOptions {
119
191
  */
120
192
  declare function inRange(value: number, min: number, max: number, options?: InRangeOptions): boolean;
121
193
 
194
+ /**
195
+ * This file is part of helpers4.
196
+ * Copyright (C) 2025 baxyz
197
+ * SPDX-License-Identifier: LGPL-3.0-or-later
198
+ */
199
+ /**
200
+ * Checks if a value is an even integer.
201
+ *
202
+ * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and odd integers.
203
+ *
204
+ * @param value - The value to check
205
+ * @returns `true` if value is an integer divisible by 2
206
+ * @example
207
+ * isEven(2) // => true
208
+ * isEven(0) // => true
209
+ * isEven(-4) // => true
210
+ * isEven(3) // => false
211
+ * isEven(1.5) // => false (not an integer)
212
+ * isEven('2') // => false
213
+ * @since 2.0.3
214
+ */
215
+ declare function isEven(value: unknown): value is number;
216
+
217
+ /**
218
+ * This file is part of helpers4.
219
+ * Copyright (C) 2025 baxyz
220
+ * SPDX-License-Identifier: LGPL-3.0-or-later
221
+ */
222
+ /**
223
+ * Checks if a value is a number less than 0.
224
+ *
225
+ * Returns `false` for `NaN`, `0`, positive numbers, and non-number types.
226
+ *
227
+ * @param value - The value to check
228
+ * @returns True if value is a negative number
229
+ * @example
230
+ * isNegative(-1) // => true
231
+ * isNegative(-0.5) // => true
232
+ * isNegative(-Infinity) // => true
233
+ * isNegative(0) // => false
234
+ * isNegative(1) // => false
235
+ * isNegative(NaN) // => false
236
+ * @since 2.0.0
237
+ */
238
+ declare function isNegative(value: unknown): value is number;
239
+
240
+ /**
241
+ * This file is part of helpers4.
242
+ * Copyright (C) 2025 baxyz
243
+ * SPDX-License-Identifier: LGPL-3.0-or-later
244
+ */
245
+ /**
246
+ * Checks if a value is an odd integer.
247
+ *
248
+ * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and even integers.
249
+ *
250
+ * @param value - The value to check
251
+ * @returns `true` if value is an integer not divisible by 2
252
+ * @example
253
+ * isOdd(3) // => true
254
+ * isOdd(1) // => true
255
+ * isOdd(-7) // => true
256
+ * isOdd(2) // => false
257
+ * isOdd(1.5) // => false (not an integer)
258
+ * isOdd('3') // => false
259
+ * @since 2.0.3
260
+ */
261
+ declare function isOdd(value: unknown): value is number;
262
+
263
+ /**
264
+ * This file is part of helpers4.
265
+ * Copyright (C) 2025 baxyz
266
+ * SPDX-License-Identifier: LGPL-3.0-or-later
267
+ */
268
+ /**
269
+ * Checks if a value is a number greater than 0.
270
+ *
271
+ * Returns `false` for `NaN`, `0`, negative numbers, and non-number types.
272
+ *
273
+ * @param value - The value to check
274
+ * @returns True if value is a positive number
275
+ * @example
276
+ * isPositive(42) // => true
277
+ * isPositive(0.1) // => true
278
+ * isPositive(Infinity) // => true
279
+ * isPositive(0) // => false
280
+ * isPositive(-1) // => false
281
+ * isPositive(NaN) // => false
282
+ * @since 2.0.0
283
+ */
284
+ declare function isPositive(value: unknown): value is number;
285
+
122
286
  /**
123
287
  * This file is part of helpers4.
124
288
  * Copyright (C) 2025 baxyz
@@ -144,27 +308,6 @@ declare function inRange(value: number, min: number, max: number, options?: InRa
144
308
  */
145
309
  declare function lerp(start: number, end: number, t: number): number;
146
310
 
147
- /**
148
- * This file is part of helpers4.
149
- * Copyright (C) 2025 baxyz
150
- * SPDX-License-Identifier: LGPL-3.0-or-later
151
- */
152
- /**
153
- * Calculates the arithmetic mean (average) of an array of numbers.
154
- * Returns `NaN` for an empty array.
155
- *
156
- * Pairs with {@link sum} for aggregate operations.
157
- *
158
- * @param array - The array of numbers to average
159
- * @returns The arithmetic mean, or `NaN` if the array is empty
160
- * @example
161
- * mean([1, 2, 3, 4]) // => 2.5
162
- * mean([10]) // => 10
163
- * mean([]) // => NaN
164
- * @since 2.0.0
165
- */
166
- declare function mean(array: readonly number[]): number;
167
-
168
311
  /**
169
312
  * This file is part of helpers4.
170
313
  * Copyright (C) 2025 baxyz
@@ -201,21 +344,5 @@ declare function randomIntBetween(min: number, max: number): number;
201
344
  */
202
345
  declare function roundTo(value: number, decimals: number): number;
203
346
 
204
- /**
205
- * This file is part of helpers4.
206
- * Copyright (C) 2025 baxyz
207
- * SPDX-License-Identifier: LGPL-3.0-or-later
208
- */
209
- /**
210
- * Calculates the sum of an array of numbers.
211
- * @param array - The array of numbers to sum
212
- * @returns The sum of all values
213
- * @example
214
- * sum([1, 2, 3, 4])
215
- * // => 10
216
- * @since 2.0.0
217
- */
218
- declare function sum(array: readonly number[]): number;
219
-
220
- export { clamp, correctFloat, formatCompact, formatSize, inRange, lerp, mean, randomBetween, randomIntBetween, roundTo, sum };
221
- export type { InRangeOptions };
347
+ export { clamp, correctFloat, extractNumber, formatCompact, formatSize, inRange, isEven, isNegative, isOdd, isPositive, lerp, randomBetween, randomIntBetween, roundTo };
348
+ export type { ExtractNumberOptions, InRangeOptions };
package/lib/index.js CHANGED
@@ -34,16 +34,86 @@ function clamp(value, min, max) {
34
34
  * digits and will silently truncate them. Increase `precision` if you
35
35
  * need to correct drift in very large numbers.
36
36
  *
37
+ * Note: IEEE-754 doubles carry at most ~17 significant decimal digits.
38
+ * Precision values above 17 pad with digits that reflect the underlying
39
+ * binary representation rather than correcting drift.
40
+ *
37
41
  * @param value - The floating-point value to correct
38
- * @param precision - Integer number of significant digits (default: 14)
42
+ * @param precision - Integer number of significant digits between 1 and 100
43
+ * (default: 14). Values above 17 are valid but expose binary noise beyond
44
+ * IEEE-754's meaningful range.
39
45
  * @returns The corrected value
46
+ * @example
47
+ * correctFloat(0.1 + 0.2) // => 0.3
48
+ * correctFloat(1.1 - 0.3) // => 0.8
49
+ * correctFloat(0.1 * 3) // => 0.3
50
+ *
51
+ * // Custom precision: keep 4 significant digits
52
+ * correctFloat(1.23456789, 4) // => 1.235
53
+ * correctFloat(1.23456789, 6) // => 1.23457
40
54
  * @since 2.0.2
41
55
  */
42
56
  function correctFloat(value, precision = 14) {
43
57
  if (!Number.isFinite(value)) throw new RangeError(`correctFloat: value must be a finite number, got ${value}`);
44
58
  if (!Number.isInteger(precision) || precision < 1 || precision > 100) throw new RangeError(`correctFloat: precision must be an integer between 1 and 100, got ${precision}`);
45
59
  const result = parseFloat(value.toPrecision(precision));
46
- return Object.is(value, -0) && result === 0 ? -0 : result;
60
+ return Object.is(value, -0) ? -0 : result;
61
+ }
62
+ //#endregion
63
+ //#region helpers/number/extractNumber.ts
64
+ var NUMBER_TOKEN = /(-)?(\d+(?:\.\d+)?|(?<![\p{L}\p{N}_])\.\d+)([eE][+-]?\d+)?/u;
65
+ var WORD_CHAR = /[\p{L}\p{N}_]/u;
66
+ function isWordChar(char) {
67
+ return char !== void 0 && WORD_CHAR.test(char);
68
+ }
69
+ /**
70
+ * Extracts the first number embedded anywhere in a string, or passes through a `number`.
71
+ *
72
+ * Unlike a plain `parseFloat`/`parseInt`, the number does not need to be at the start of
73
+ * the string: digits are searched for anywhere, so leading/trailing text (units, labels, ...)
74
+ * is ignored. A `-` before the digits and a scientific-notation suffix (`e`/`E`) are
75
+ * disambiguated with {@link ExtractNumberOptions.sign} and {@link ExtractNumberOptions.exponent}.
76
+ *
77
+ * Returns `undefined` if no number can be found.
78
+ *
79
+ * @param value - The value to extract a number from
80
+ * @param options - Options controlling sign and exponent disambiguation
81
+ * @returns The extracted number, or `undefined` if none was found
82
+ * @example
83
+ * extractNumber('16.5px') // => 16.5
84
+ * extractNumber('.5rem') // => 0.5 (leading-dot decimal)
85
+ * extractNumber('-.5') // => -0.5 (leading-dot with sign)
86
+ * extractNumber('Wafer 10') // => 10
87
+ * extractNumber('xxx-111') // => 111 ('-' glued to text → separator)
88
+ * extractNumber('xxx -111') // => -111 ('-' preceded by a space → sign)
89
+ * extractNumber('x-.5') // => 0.5 ('-' glued to 'x' → separator; leading-dot decimal follows)
90
+ * extractNumber('-111') // => -111 ('-' at the start of the string → sign)
91
+ * extractNumber('1e5 mol') // => 100000
92
+ * extractNumber('1e5kg') // => 1 ('e5' glued to text → mantissa only)
93
+ * extractNumber('no number') // => undefined
94
+ * extractNumber(42) // => 42
95
+ * @since 2.0.4
96
+ */
97
+ function extractNumber(value, options = {}) {
98
+ const { sign = "auto", exponent = "auto" } = options;
99
+ if (typeof value === "number") return Number.isNaN(value) ? void 0 : value;
100
+ if (typeof value !== "string") return;
101
+ const re = new RegExp(NUMBER_TOKEN.source, "gu");
102
+ let match;
103
+ while ((match = re.exec(value)) !== null) {
104
+ const [full, signChar, mantissa, exponentPart] = match;
105
+ const start = match.index;
106
+ if (signChar) {
107
+ const before = value[start - 1];
108
+ if (!(sign === "strict" ? true : sign === "ignore" ? false : !isWordChar(before))) {
109
+ re.lastIndex = start + 1;
110
+ continue;
111
+ }
112
+ }
113
+ const keepExponent = exponentPart ? exponent === "strict" ? true : exponent === "ignore" ? false : !isWordChar(value[start + full.length]) : false;
114
+ const numericText = (signChar ?? "") + mantissa + (keepExponent ? exponentPart : "");
115
+ return Number(numericText);
116
+ }
47
117
  }
48
118
  //#endregion
49
119
  //#region helpers/number/formatCompact.ts
@@ -134,6 +204,110 @@ function inRange(value, min, max, options = {}) {
134
204
  return (inclusive === "both" || inclusive === "min" ? value >= min : value > min) && (inclusive === "both" || inclusive === "max" ? value <= max : value < max);
135
205
  }
136
206
  //#endregion
207
+ //#region helpers/number/isEven.ts
208
+ /**
209
+ * This file is part of helpers4.
210
+ * Copyright (C) 2025 baxyz
211
+ * SPDX-License-Identifier: LGPL-3.0-or-later
212
+ */
213
+ /**
214
+ * Checks if a value is an even integer.
215
+ *
216
+ * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and odd integers.
217
+ *
218
+ * @param value - The value to check
219
+ * @returns `true` if value is an integer divisible by 2
220
+ * @example
221
+ * isEven(2) // => true
222
+ * isEven(0) // => true
223
+ * isEven(-4) // => true
224
+ * isEven(3) // => false
225
+ * isEven(1.5) // => false (not an integer)
226
+ * isEven('2') // => false
227
+ * @since 2.0.3
228
+ */
229
+ function isEven(value) {
230
+ return typeof value === "number" && Number.isInteger(value) && value % 2 === 0;
231
+ }
232
+ //#endregion
233
+ //#region helpers/number/isNegative.ts
234
+ /**
235
+ * This file is part of helpers4.
236
+ * Copyright (C) 2025 baxyz
237
+ * SPDX-License-Identifier: LGPL-3.0-or-later
238
+ */
239
+ /**
240
+ * Checks if a value is a number less than 0.
241
+ *
242
+ * Returns `false` for `NaN`, `0`, positive numbers, and non-number types.
243
+ *
244
+ * @param value - The value to check
245
+ * @returns True if value is a negative number
246
+ * @example
247
+ * isNegative(-1) // => true
248
+ * isNegative(-0.5) // => true
249
+ * isNegative(-Infinity) // => true
250
+ * isNegative(0) // => false
251
+ * isNegative(1) // => false
252
+ * isNegative(NaN) // => false
253
+ * @since 2.0.0
254
+ */
255
+ function isNegative(value) {
256
+ return typeof value === "number" && value < 0;
257
+ }
258
+ //#endregion
259
+ //#region helpers/number/isOdd.ts
260
+ /**
261
+ * This file is part of helpers4.
262
+ * Copyright (C) 2025 baxyz
263
+ * SPDX-License-Identifier: LGPL-3.0-or-later
264
+ */
265
+ /**
266
+ * Checks if a value is an odd integer.
267
+ *
268
+ * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and even integers.
269
+ *
270
+ * @param value - The value to check
271
+ * @returns `true` if value is an integer not divisible by 2
272
+ * @example
273
+ * isOdd(3) // => true
274
+ * isOdd(1) // => true
275
+ * isOdd(-7) // => true
276
+ * isOdd(2) // => false
277
+ * isOdd(1.5) // => false (not an integer)
278
+ * isOdd('3') // => false
279
+ * @since 2.0.3
280
+ */
281
+ function isOdd(value) {
282
+ return typeof value === "number" && Number.isInteger(value) && value % 2 !== 0;
283
+ }
284
+ //#endregion
285
+ //#region helpers/number/isPositive.ts
286
+ /**
287
+ * This file is part of helpers4.
288
+ * Copyright (C) 2025 baxyz
289
+ * SPDX-License-Identifier: LGPL-3.0-or-later
290
+ */
291
+ /**
292
+ * Checks if a value is a number greater than 0.
293
+ *
294
+ * Returns `false` for `NaN`, `0`, negative numbers, and non-number types.
295
+ *
296
+ * @param value - The value to check
297
+ * @returns True if value is a positive number
298
+ * @example
299
+ * isPositive(42) // => true
300
+ * isPositive(0.1) // => true
301
+ * isPositive(Infinity) // => true
302
+ * isPositive(0) // => false
303
+ * isPositive(-1) // => false
304
+ * isPositive(NaN) // => false
305
+ * @since 2.0.0
306
+ */
307
+ function isPositive(value) {
308
+ return typeof value === "number" && value > 0;
309
+ }
310
+ //#endregion
137
311
  //#region helpers/number/lerp.ts
138
312
  /**
139
313
  * This file is part of helpers4.
@@ -165,31 +339,6 @@ function lerp(start, end, t) {
165
339
  return start + (end - start) * t;
166
340
  }
167
341
  //#endregion
168
- //#region helpers/number/mean.ts
169
- /**
170
- * This file is part of helpers4.
171
- * Copyright (C) 2025 baxyz
172
- * SPDX-License-Identifier: LGPL-3.0-or-later
173
- */
174
- /**
175
- * Calculates the arithmetic mean (average) of an array of numbers.
176
- * Returns `NaN` for an empty array.
177
- *
178
- * Pairs with {@link sum} for aggregate operations.
179
- *
180
- * @param array - The array of numbers to average
181
- * @returns The arithmetic mean, or `NaN` if the array is empty
182
- * @example
183
- * mean([1, 2, 3, 4]) // => 2.5
184
- * mean([10]) // => 10
185
- * mean([]) // => NaN
186
- * @since 2.0.0
187
- */
188
- function mean(array) {
189
- if (array.length === 0) return NaN;
190
- return array.reduce((acc, n) => acc + n, 0) / array.length;
191
- }
192
- //#endregion
193
342
  //#region helpers/number/random.ts
194
343
  /**
195
344
  * This file is part of helpers4.
@@ -235,25 +384,6 @@ function roundTo(value, decimals) {
235
384
  return Math.round(value * multiplier) / multiplier;
236
385
  }
237
386
  //#endregion
238
- //#region helpers/number/sum.ts
239
- /**
240
- * This file is part of helpers4.
241
- * Copyright (C) 2025 baxyz
242
- * SPDX-License-Identifier: LGPL-3.0-or-later
243
- */
244
- /**
245
- * Calculates the sum of an array of numbers.
246
- * @param array - The array of numbers to sum
247
- * @returns The sum of all values
248
- * @example
249
- * sum([1, 2, 3, 4])
250
- * // => 10
251
- * @since 2.0.0
252
- */
253
- function sum(array) {
254
- return array.reduce((acc, n) => acc + n, 0);
255
- }
256
- //#endregion
257
- export { clamp, correctFloat, formatCompact, formatSize, inRange, lerp, mean, randomBetween, randomIntBetween, roundTo, sum };
387
+ export { clamp, correctFloat, extractNumber, formatCompact, formatSize, inRange, isEven, isNegative, isOdd, isPositive, lerp, randomBetween, randomIntBetween, roundTo };
258
388
 
259
389
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../helpers/number/clamp.ts","../../../helpers/number/correctFloat.ts","../../../helpers/number/formatCompact.ts","../../../helpers/number/formatSize.ts","../../../helpers/number/inRange.ts","../../../helpers/number/lerp.ts","../../../helpers/number/mean.ts","../../../helpers/number/random.ts","../../../helpers/number/roundTo.ts","../../../helpers/number/sum.ts"],"sourcesContent":["/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Clamps a number between min and max values\n * @param value - The value to clamp\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Clamped value\n * @since 1.9.0\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Corrects floating-point arithmetic errors by rounding to a given number\n * of significant digits. Useful after calculations that accumulate binary\n * floating-point drift (e.g. `0.1 + 0.2 === 0.30000000000000004`).\n *\n * The default precision of 14 significant digits eliminates typical\n * rounding noise for values in the range used by most applications.\n * Note: for values whose integer part already consumes 14 or more digits\n * (i.e. |value| ≥ 1e13), toPrecision(14) has no room left for decimal\n * digits and will silently truncate them. Increase `precision` if you\n * need to correct drift in very large numbers.\n *\n * @param value - The floating-point value to correct\n * @param precision - Integer number of significant digits (default: 14)\n * @returns The corrected value\n * @since 2.0.2\n */\nexport function correctFloat(value: number, precision: number = 14): number {\n if (!Number.isFinite(value)) {\n throw new RangeError(`correctFloat: value must be a finite number, got ${value}`);\n }\n if (!Number.isInteger(precision) || precision < 1 || precision > 100) {\n throw new RangeError(`correctFloat: precision must be an integer between 1 and 100, got ${precision}`);\n }\n const result = parseFloat(value.toPrecision(precision));\n // toPrecision(-0) produces \"0.000…\" (no minus sign per spec) — restore the sign bit.\n return Object.is(value, -0) && result === 0 ? -0 : result;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Formats a number using compact notation (e.g. `1_500_000 → \"1.5M\"`).\n *\n * Thin wrapper over `Intl.NumberFormat` with `notation: 'compact'`. Companion\n * of `formatSize` in the same `format*` family.\n *\n * @param value - The number to format.\n * @param locale - BCP 47 locale tag. Defaults to the runtime locale.\n * @returns A compact string representation of the number.\n * @example\n * formatCompact(1_500_000); // '1.5M' (en-US locale)\n * formatCompact(1_000); // '1K'\n * formatCompact(999); // '999'\n * formatCompact(1_500_000, 'fr'); // '1,5 M'\n * @since 2.0.0\n */\nexport function formatCompact(value: number, locale?: string): string {\n return new Intl.NumberFormat(locale, { notation: 'compact' }).format(value);\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/** Size units in ascending order. */\nconst UNITS = ['B', 'KB', 'MB', 'GB', 'TB'] as const;\n\n/**\n * Format a byte count into a human-readable string with the appropriate unit.\n *\n * Each unit is 1024 of the previous (binary prefix). The result is formatted\n * with one decimal place.\n *\n * @param bytes - A non-negative integer representing a byte count.\n * @returns A human-readable string such as `'0.0B'`, `'1.5KB'`, `'3.2MB'`.\n * @example\n * formatSize(0) // '0.0B'\n * formatSize(512) // '512.0B'\n * formatSize(1024) // '1.0KB'\n * formatSize(1536) // '1.5KB'\n * formatSize(1_048_576) // '1.0MB'\n * formatSize(1_073_741_824) // '1.0GB'\n * @since 2.0.0\n */\nexport function formatSize(bytes: number): string {\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < UNITS.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n return `${size.toFixed(1)}${UNITS[unitIndex]}`;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Options for {@link inRange}.\n */\nexport interface InRangeOptions {\n /**\n * Which boundary (or boundaries) are included in the range.\n * - `'both'` (default): `min <= value <= max`\n * - `'min'`: `min <= value < max`\n * - `'max'`: `min < value <= max`\n * - `'none'`: `min < value < max`\n */\n inclusive?: 'both' | 'min' | 'max' | 'none';\n}\n\n/**\n * Checks whether a number falls within `[min, max]` (both inclusive by default).\n *\n * @param value - The number to test\n * @param min - Lower bound\n * @param max - Upper bound\n * @param options - Boundary inclusion mode (default: `'both'`)\n * @returns `true` if `value` is within the specified range\n * @example\n * inRange(5, 1, 10) // => true\n * inRange(1, 1, 10) // => true (min inclusive)\n * inRange(1, 1, 10, { inclusive: 'none' }) // => false\n * inRange(10, 1, 10, { inclusive: 'max' }) // => true\n * @since 2.0.0\n */\nexport function inRange(value: number, min: number, max: number, options: InRangeOptions = {}): boolean {\n const { inclusive = 'both' } = options;\n const minOk = inclusive === 'both' || inclusive === 'min' ? value >= min : value > min;\n const maxOk = inclusive === 'both' || inclusive === 'max' ? value <= max : value < max;\n return minOk && maxOk;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Linearly interpolates between `start` and `end` by the factor `t`.\n *\n * - `t = 0` returns `start`.\n * - `t = 1` returns `end`.\n * - Values of `t` outside `[0, 1]` extrapolate beyond the range.\n *\n * @param start - The start value.\n * @param end - The end value.\n * @param t - The interpolation factor.\n * @returns The interpolated value.\n * @example\n * lerp(0, 100, 0); // 0\n * lerp(0, 100, 1); // 100\n * lerp(0, 100, 0.5); // 50\n * lerp(0, 100, 0.25); // 25\n * @since 2.0.0\n */\nexport function lerp(start: number, end: number, t: number): number {\n if (t === 0) return start;\n if (t === 1) return end;\n if (Object.is(start, end)) return start;\n return start + (end - start) * t;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Calculates the arithmetic mean (average) of an array of numbers.\n * Returns `NaN` for an empty array.\n *\n * Pairs with {@link sum} for aggregate operations.\n *\n * @param array - The array of numbers to average\n * @returns The arithmetic mean, or `NaN` if the array is empty\n * @example\n * mean([1, 2, 3, 4]) // => 2.5\n * mean([10]) // => 10\n * mean([]) // => NaN\n * @since 2.0.0\n */\nexport function mean(array: readonly number[]): number {\n if (array.length === 0) return NaN;\n return array.reduce((acc, n) => acc + n, 0) / array.length;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Generates a random number between min and max (inclusive)\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random number between min and max\n * @since 1.9.0\n */\nexport function randomBetween(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n}\n\n/**\n * Generates a random integer between min and max (inclusive)\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random integer between min and max\n * @since 1.9.0\n */\nexport function randomIntBetween(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Rounds a number to specified decimal places\n * @param value - The number to round\n * @param decimals - Number of decimal places\n * @returns Rounded number\n * @since 1.9.0\n */\nexport function roundTo(value: number, decimals: number): number {\n const multiplier = Math.pow(10, decimals);\n return Math.round(value * multiplier) / multiplier;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Calculates the sum of an array of numbers.\n * @param array - The array of numbers to sum\n * @returns The sum of all values\n * @example\n * sum([1, 2, 3, 4])\n * // => 10\n * @since 2.0.0\n */\nexport function sum(array: readonly number[]): number {\n return array.reduce((acc, n) => acc + n, 0);\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,MAAM,OAAe,KAAa,KAAqB;CACrE,OAAO,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG;AAC3C;;;;;;;;;;;;;;;;;;;;;;;;;ACOA,SAAgB,aAAa,OAAe,YAAoB,IAAY;CAC1E,IAAI,CAAC,OAAO,SAAS,KAAK,GACxB,MAAM,IAAI,WAAW,oDAAoD,OAAO;CAElF,IAAI,CAAC,OAAO,UAAU,SAAS,KAAK,YAAY,KAAK,YAAY,KAC/D,MAAM,IAAI,WAAW,qEAAqE,WAAW;CAEvG,MAAM,SAAS,WAAW,MAAM,YAAY,SAAS,CAAC;CAEtD,OAAO,OAAO,GAAG,OAAO,EAAE,KAAK,WAAW,IAAI,KAAK;AACrD;;;;;;;;;;;;;;;;;;;;;;;;ACXA,SAAgB,cAAc,OAAe,QAAyB;CACpE,OAAO,IAAI,KAAK,aAAa,QAAQ,EAAE,UAAU,UAAU,CAAC,EAAE,OAAO,KAAK;AAC5E;;;;;;;;;ACjBA,IAAM,QAAQ;CAAC;CAAK;CAAM;CAAM;CAAM;AAAI;;;;;;;;;;;;;;;;;;AAmB1C,SAAgB,WAAW,OAAuB;CAChD,IAAI,OAAO;CACX,IAAI,YAAY;CAEhB,OAAO,QAAQ,QAAQ,YAAY,MAAM,SAAS,GAAG;EACnD,QAAQ;EACR;CACF;CAEA,OAAO,GAAG,KAAK,QAAQ,CAAC,IAAI,MAAM;AACpC;;;;;;;;;;;;;;;;;;ACDA,SAAgB,QAAQ,OAAe,KAAa,KAAa,UAA0B,CAAC,GAAY;CACtG,MAAM,EAAE,YAAY,WAAW;CAG/B,QAFc,cAAc,UAAU,cAAc,QAAQ,SAAS,MAAM,QAAQ,SACrE,cAAc,UAAU,cAAc,QAAQ,SAAS,MAAM,QAAQ;AAErF;;;;;;;;;;;;;;;;;;;;;;;;;;AChBA,SAAgB,KAAK,OAAe,KAAa,GAAmB;CAClE,IAAI,MAAM,GAAG,OAAO;CACpB,IAAI,MAAM,GAAG,OAAO;CACpB,IAAI,OAAO,GAAG,OAAO,GAAG,GAAG,OAAO;CAClC,OAAO,SAAS,MAAM,SAAS;AACjC;;;;;;;;;;;;;;;;;;;;;;ACTA,SAAgB,KAAK,OAAkC;CACrD,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,OAAO,MAAM,QAAQ,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI,MAAM;AACtD;;;;;;;;;;;;;;;ACVA,SAAgB,cAAc,KAAa,KAAqB;CAC9D,OAAO,KAAK,OAAO,KAAK,MAAM,OAAO;AACvC;;;;;;;;AASA,SAAgB,iBAAiB,KAAa,KAAqB;CACjE,OAAO,KAAK,MAAM,KAAK,OAAO,KAAK,MAAM,MAAM,EAAE,IAAI;AACvD;;;;;;;;;;;;;;;ACbA,SAAgB,QAAQ,OAAe,UAA0B;CAC/D,MAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;CACxC,OAAO,KAAK,MAAM,QAAQ,UAAU,IAAI;AAC1C;;;;;;;;;;;;;;;;;ACDA,SAAgB,IAAI,OAAkC;CACpD,OAAO,MAAM,QAAQ,KAAK,MAAM,MAAM,GAAG,CAAC;AAC5C"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../helpers/number/clamp.ts","../../../helpers/number/correctFloat.ts","../../../helpers/number/extractNumber.ts","../../../helpers/number/formatCompact.ts","../../../helpers/number/formatSize.ts","../../../helpers/number/inRange.ts","../../../helpers/number/isEven.ts","../../../helpers/number/isNegative.ts","../../../helpers/number/isOdd.ts","../../../helpers/number/isPositive.ts","../../../helpers/number/lerp.ts","../../../helpers/number/random.ts","../../../helpers/number/roundTo.ts"],"sourcesContent":["/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Clamps a number between min and max values\n * @param value - The value to clamp\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Clamped value\n * @since 1.9.0\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Corrects floating-point arithmetic errors by rounding to a given number\n * of significant digits. Useful after calculations that accumulate binary\n * floating-point drift (e.g. `0.1 + 0.2 === 0.30000000000000004`).\n *\n * The default precision of 14 significant digits eliminates typical\n * rounding noise for values in the range used by most applications.\n * Note: for values whose integer part already consumes 14 or more digits\n * (i.e. |value| ≥ 1e13), toPrecision(14) has no room left for decimal\n * digits and will silently truncate them. Increase `precision` if you\n * need to correct drift in very large numbers.\n *\n * Note: IEEE-754 doubles carry at most ~17 significant decimal digits.\n * Precision values above 17 pad with digits that reflect the underlying\n * binary representation rather than correcting drift.\n *\n * @param value - The floating-point value to correct\n * @param precision - Integer number of significant digits between 1 and 100\n * (default: 14). Values above 17 are valid but expose binary noise beyond\n * IEEE-754's meaningful range.\n * @returns The corrected value\n * @example\n * correctFloat(0.1 + 0.2) // => 0.3\n * correctFloat(1.1 - 0.3) // => 0.8\n * correctFloat(0.1 * 3) // => 0.3\n *\n * // Custom precision: keep 4 significant digits\n * correctFloat(1.23456789, 4) // => 1.235\n * correctFloat(1.23456789, 6) // => 1.23457\n * @since 2.0.2\n */\nexport function correctFloat(value: number, precision: number = 14): number {\n if (!Number.isFinite(value)) {\n throw new RangeError(`correctFloat: value must be a finite number, got ${value}`);\n }\n if (!Number.isInteger(precision) || precision < 1 || precision > 100) {\n throw new RangeError(`correctFloat: precision must be an integer between 1 and 100, got ${precision}`);\n }\n const result = parseFloat(value.toPrecision(precision));\n // toPrecision(-0) produces \"0.000…\" (no minus sign per spec) — restore the sign bit.\n return Object.is(value, -0) ? -0 : result;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Options for {@link extractNumber}.\n */\nexport interface ExtractNumberOptions {\n /**\n * How to interpret a `-` immediately before the matched digits.\n * - `'auto'`: treated as a minus sign unless it is glued to a preceding letter/digit\n * (e.g. `'-111'` and `'xxx -111'` → negative, but `'xxx-111'` → separator, positive).\n * - `'strict'`: always treated as a minus sign.\n * - `'ignore'`: never treated as a minus sign (always a separator).\n * @default 'auto'\n */\n sign?: 'auto' | 'strict' | 'ignore';\n\n /**\n * How to interpret a trailing scientific-notation suffix (`e`/`E` + digits, e.g. `1.5e-10`).\n * - `'auto'`: treated as an exponent unless it is glued to a following letter/digit\n * (e.g. `'1e5'` and `'1e5 mol'` → exponential, but `'1e5kg'` → mantissa only).\n * - `'strict'`: always treated as an exponent.\n * - `'ignore'`: never treated as an exponent (mantissa only).\n * @default 'auto'\n */\n exponent?: 'auto' | 'strict' | 'ignore';\n}\n\n// (?<![\\p{L}\\p{N}_]) prevents matching a leading-dot decimal immediately after a word\n// character (e.g. the '.5' in 'text.5' is a property separator, not a decimal point).\n// No `g` flag — cloned per call via `new RegExp(…, 'gu')` so each invocation has its own lastIndex.\nconst NUMBER_TOKEN = /(-)?(\\d+(?:\\.\\d+)?|(?<![\\p{L}\\p{N}_])\\.\\d+)([eE][+-]?\\d+)?/u;\nconst WORD_CHAR = /[\\p{L}\\p{N}_]/u;\n\nfunction isWordChar(char: string | undefined): boolean {\n return char !== undefined && WORD_CHAR.test(char);\n}\n\n/**\n * Extracts the first number embedded anywhere in a string, or passes through a `number`.\n *\n * Unlike a plain `parseFloat`/`parseInt`, the number does not need to be at the start of\n * the string: digits are searched for anywhere, so leading/trailing text (units, labels, ...)\n * is ignored. A `-` before the digits and a scientific-notation suffix (`e`/`E`) are\n * disambiguated with {@link ExtractNumberOptions.sign} and {@link ExtractNumberOptions.exponent}.\n *\n * Returns `undefined` if no number can be found.\n *\n * @param value - The value to extract a number from\n * @param options - Options controlling sign and exponent disambiguation\n * @returns The extracted number, or `undefined` if none was found\n * @example\n * extractNumber('16.5px') // => 16.5\n * extractNumber('.5rem') // => 0.5 (leading-dot decimal)\n * extractNumber('-.5') // => -0.5 (leading-dot with sign)\n * extractNumber('Wafer 10') // => 10\n * extractNumber('xxx-111') // => 111 ('-' glued to text → separator)\n * extractNumber('xxx -111') // => -111 ('-' preceded by a space → sign)\n * extractNumber('x-.5') // => 0.5 ('-' glued to 'x' → separator; leading-dot decimal follows)\n * extractNumber('-111') // => -111 ('-' at the start of the string → sign)\n * extractNumber('1e5 mol') // => 100000\n * extractNumber('1e5kg') // => 1 ('e5' glued to text → mantissa only)\n * extractNumber('no number') // => undefined\n * extractNumber(42) // => 42\n * @since 2.0.4\n */\nexport function extractNumber(value: unknown, options: ExtractNumberOptions = {}): number | undefined {\n const { sign = 'auto', exponent = 'auto' } = options;\n\n if (typeof value === 'number') {\n return Number.isNaN(value) ? undefined : value;\n }\n\n if (typeof value !== 'string') {\n return undefined;\n }\n\n const re = new RegExp(NUMBER_TOKEN.source, 'gu');\n let match: RegExpExecArray | null;\n\n while ((match = re.exec(value)) !== null) {\n const [full, signChar, mantissa, exponentPart] = match;\n const start = match.index;\n\n if (signChar) {\n const before = value[start - 1];\n const keepSign = sign === 'strict' ? true : sign === 'ignore' ? false : !isWordChar(before);\n\n if (!keepSign) {\n // Re-scan from right after the '-' so the digits are matched as a fresh, unsigned number.\n re.lastIndex = start + 1;\n continue;\n }\n }\n\n const keepExponent = exponentPart\n ? exponent === 'strict'\n ? true\n : exponent === 'ignore'\n ? false\n : !isWordChar(value[start + full.length])\n : false;\n\n const numericText = (signChar ?? '') + mantissa + (keepExponent ? exponentPart : '');\n return Number(numericText);\n }\n\n return undefined;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Formats a number using compact notation (e.g. `1_500_000 → \"1.5M\"`).\n *\n * Thin wrapper over `Intl.NumberFormat` with `notation: 'compact'`. Companion\n * of `formatSize` in the same `format*` family.\n *\n * @param value - The number to format.\n * @param locale - BCP 47 locale tag. Defaults to the runtime locale.\n * @returns A compact string representation of the number.\n * @example\n * formatCompact(1_500_000); // '1.5M' (en-US locale)\n * formatCompact(1_000); // '1K'\n * formatCompact(999); // '999'\n * formatCompact(1_500_000, 'fr'); // '1,5 M'\n * @since 2.0.0\n */\nexport function formatCompact(value: number, locale?: string): string {\n return new Intl.NumberFormat(locale, { notation: 'compact' }).format(value);\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/** Size units in ascending order. */\nconst UNITS = ['B', 'KB', 'MB', 'GB', 'TB'] as const;\n\n/**\n * Format a byte count into a human-readable string with the appropriate unit.\n *\n * Each unit is 1024 of the previous (binary prefix). The result is formatted\n * with one decimal place.\n *\n * @param bytes - A non-negative integer representing a byte count.\n * @returns A human-readable string such as `'0.0B'`, `'1.5KB'`, `'3.2MB'`.\n * @example\n * formatSize(0) // '0.0B'\n * formatSize(512) // '512.0B'\n * formatSize(1024) // '1.0KB'\n * formatSize(1536) // '1.5KB'\n * formatSize(1_048_576) // '1.0MB'\n * formatSize(1_073_741_824) // '1.0GB'\n * @since 2.0.0\n */\nexport function formatSize(bytes: number): string {\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < UNITS.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n return `${size.toFixed(1)}${UNITS[unitIndex]}`;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Options for {@link inRange}.\n */\nexport interface InRangeOptions {\n /**\n * Which boundary (or boundaries) are included in the range.\n * - `'both'` (default): `min <= value <= max`\n * - `'min'`: `min <= value < max`\n * - `'max'`: `min < value <= max`\n * - `'none'`: `min < value < max`\n */\n inclusive?: 'both' | 'min' | 'max' | 'none';\n}\n\n/**\n * Checks whether a number falls within `[min, max]` (both inclusive by default).\n *\n * @param value - The number to test\n * @param min - Lower bound\n * @param max - Upper bound\n * @param options - Boundary inclusion mode (default: `'both'`)\n * @returns `true` if `value` is within the specified range\n * @example\n * inRange(5, 1, 10) // => true\n * inRange(1, 1, 10) // => true (min inclusive)\n * inRange(1, 1, 10, { inclusive: 'none' }) // => false\n * inRange(10, 1, 10, { inclusive: 'max' }) // => true\n * @since 2.0.0\n */\nexport function inRange(value: number, min: number, max: number, options: InRangeOptions = {}): boolean {\n const { inclusive = 'both' } = options;\n const minOk = inclusive === 'both' || inclusive === 'min' ? value >= min : value > min;\n const maxOk = inclusive === 'both' || inclusive === 'max' ? value <= max : value < max;\n return minOk && maxOk;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is an even integer.\n *\n * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and odd integers.\n *\n * @param value - The value to check\n * @returns `true` if value is an integer divisible by 2\n * @example\n * isEven(2) // => true\n * isEven(0) // => true\n * isEven(-4) // => true\n * isEven(3) // => false\n * isEven(1.5) // => false (not an integer)\n * isEven('2') // => false\n * @since 2.0.3\n */\nexport function isEven(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value % 2 === 0;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is a number less than 0.\n *\n * Returns `false` for `NaN`, `0`, positive numbers, and non-number types.\n *\n * @param value - The value to check\n * @returns True if value is a negative number\n * @example\n * isNegative(-1) // => true\n * isNegative(-0.5) // => true\n * isNegative(-Infinity) // => true\n * isNegative(0) // => false\n * isNegative(1) // => false\n * isNegative(NaN) // => false\n * @since 2.0.0\n */\nexport function isNegative(value: unknown): value is number {\n return typeof value === 'number' && value < 0;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is an odd integer.\n *\n * Returns `false` for non-numbers, non-integers, `NaN`, `Infinity`, and even integers.\n *\n * @param value - The value to check\n * @returns `true` if value is an integer not divisible by 2\n * @example\n * isOdd(3) // => true\n * isOdd(1) // => true\n * isOdd(-7) // => true\n * isOdd(2) // => false\n * isOdd(1.5) // => false (not an integer)\n * isOdd('3') // => false\n * @since 2.0.3\n */\nexport function isOdd(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value % 2 !== 0;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Checks if a value is a number greater than 0.\n *\n * Returns `false` for `NaN`, `0`, negative numbers, and non-number types.\n *\n * @param value - The value to check\n * @returns True if value is a positive number\n * @example\n * isPositive(42) // => true\n * isPositive(0.1) // => true\n * isPositive(Infinity) // => true\n * isPositive(0) // => false\n * isPositive(-1) // => false\n * isPositive(NaN) // => false\n * @since 2.0.0\n */\nexport function isPositive(value: unknown): value is number {\n return typeof value === 'number' && value > 0;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Linearly interpolates between `start` and `end` by the factor `t`.\n *\n * - `t = 0` returns `start`.\n * - `t = 1` returns `end`.\n * - Values of `t` outside `[0, 1]` extrapolate beyond the range.\n *\n * @param start - The start value.\n * @param end - The end value.\n * @param t - The interpolation factor.\n * @returns The interpolated value.\n * @example\n * lerp(0, 100, 0); // 0\n * lerp(0, 100, 1); // 100\n * lerp(0, 100, 0.5); // 50\n * lerp(0, 100, 0.25); // 25\n * @since 2.0.0\n */\nexport function lerp(start: number, end: number, t: number): number {\n if (t === 0) return start;\n if (t === 1) return end;\n if (Object.is(start, end)) return start;\n return start + (end - start) * t;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Generates a random number between min and max (inclusive)\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random number between min and max\n * @since 1.9.0\n */\nexport function randomBetween(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n}\n\n/**\n * Generates a random integer between min and max (inclusive)\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random integer between min and max\n * @since 1.9.0\n */\nexport function randomIntBetween(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Rounds a number to specified decimal places\n * @param value - The number to round\n * @param decimals - Number of decimal places\n * @returns Rounded number\n * @since 1.9.0\n */\nexport function roundTo(value: number, decimals: number): number {\n const multiplier = Math.pow(10, decimals);\n return Math.round(value * multiplier) / multiplier;\n}\n"],"mappings":"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"}