ts-data-forge 6.13.1 → 7.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.
- package/README.md +2 -2
- package/dist/array/impl/array-utils-iterators.mjs +6 -0
- package/dist/array/impl/array-utils-iterators.mjs.map +1 -1
- package/dist/array/impl/array-utils-modification.mjs +6 -0
- package/dist/array/impl/array-utils-modification.mjs.map +1 -1
- package/dist/array/impl/array-utils-reducing-value.mjs +6 -0
- package/dist/array/impl/array-utils-reducing-value.mjs.map +1 -1
- package/dist/array/impl/array-utils-search.mjs +6 -0
- package/dist/array/impl/array-utils-search.mjs.map +1 -1
- package/dist/array/impl/array-utils-slice-clamped.mjs +6 -0
- package/dist/array/impl/array-utils-slice-clamped.mjs.map +1 -1
- package/dist/array/impl/array-utils-slicing.mjs +6 -0
- package/dist/array/impl/array-utils-slicing.mjs.map +1 -1
- package/dist/array/impl/array-utils-transformation.mjs +6 -0
- package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
- package/dist/array/impl/array-utils-validation.mjs +6 -0
- package/dist/array/impl/array-utils-validation.mjs.map +1 -1
- package/dist/collections/imap-mapped.mjs +6 -0
- package/dist/collections/imap-mapped.mjs.map +1 -1
- package/dist/collections/imap.mjs +6 -0
- package/dist/collections/imap.mjs.map +1 -1
- package/dist/collections/iset-mapped.mjs +6 -0
- package/dist/collections/iset-mapped.mjs.map +1 -1
- package/dist/collections/iset.mjs +6 -0
- package/dist/collections/iset.mjs.map +1 -1
- package/dist/collections/queue.mjs +6 -0
- package/dist/collections/queue.mjs.map +1 -1
- package/dist/collections/stack.mjs +6 -0
- package/dist/collections/stack.mjs.map +1 -1
- package/dist/entry-point.mjs +6 -0
- package/dist/entry-point.mjs.map +1 -1
- package/dist/index.mjs +6 -0
- package/dist/index.mjs.map +1 -1
- package/dist/iterator/range.mjs +6 -0
- package/dist/iterator/range.mjs.map +1 -1
- package/dist/json/json.mjs +6 -0
- package/dist/json/json.mjs.map +1 -1
- package/dist/number/branded-types/finite-number.d.mts +1 -1
- package/dist/number/branded-types/index.d.mts +6 -0
- package/dist/number/branded-types/index.d.mts.map +1 -1
- package/dist/number/branded-types/index.mjs +6 -0
- package/dist/number/branded-types/index.mjs.map +1 -1
- package/dist/number/branded-types/int.d.mts +1 -2
- package/dist/number/branded-types/int.d.mts.map +1 -1
- package/dist/number/branded-types/int16.d.mts +2 -3
- package/dist/number/branded-types/int16.d.mts.map +1 -1
- package/dist/number/branded-types/int32.d.mts +2 -3
- package/dist/number/branded-types/int32.d.mts.map +1 -1
- package/dist/number/branded-types/negative-finite-number.d.mts +143 -0
- package/dist/number/branded-types/negative-finite-number.d.mts.map +1 -0
- package/dist/number/branded-types/negative-finite-number.mjs +156 -0
- package/dist/number/branded-types/negative-finite-number.mjs.map +1 -0
- package/dist/number/branded-types/negative-int.d.mts +120 -0
- package/dist/number/branded-types/negative-int.d.mts.map +1 -0
- package/dist/number/branded-types/negative-int.mjs +125 -0
- package/dist/number/branded-types/negative-int.mjs.map +1 -0
- package/dist/number/branded-types/negative-safe-int.d.mts +132 -0
- package/dist/number/branded-types/negative-safe-int.d.mts.map +1 -0
- package/dist/number/branded-types/negative-safe-int.mjs +136 -0
- package/dist/number/branded-types/negative-safe-int.mjs.map +1 -0
- package/dist/number/branded-types/non-negative-finite-number.d.mts +1 -1
- package/dist/number/branded-types/non-negative-int16.d.mts +1 -1
- package/dist/number/branded-types/non-negative-int32.d.mts +1 -1
- package/dist/number/branded-types/non-positive-finite-number.d.mts +130 -0
- package/dist/number/branded-types/non-positive-finite-number.d.mts.map +1 -0
- package/dist/number/branded-types/non-positive-finite-number.mjs +134 -0
- package/dist/number/branded-types/non-positive-finite-number.mjs.map +1 -0
- package/dist/number/branded-types/non-positive-int.d.mts +224 -0
- package/dist/number/branded-types/non-positive-int.d.mts.map +1 -0
- package/dist/number/branded-types/non-positive-int.mjs +222 -0
- package/dist/number/branded-types/non-positive-int.mjs.map +1 -0
- package/dist/number/branded-types/non-positive-safe-int.d.mts +119 -0
- package/dist/number/branded-types/non-positive-safe-int.d.mts.map +1 -0
- package/dist/number/branded-types/non-positive-safe-int.mjs +116 -0
- package/dist/number/branded-types/non-positive-safe-int.mjs.map +1 -0
- package/dist/number/branded-types/non-zero-finite-number.d.mts +5 -17
- package/dist/number/branded-types/non-zero-finite-number.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-finite-number.mjs +5 -17
- package/dist/number/branded-types/non-zero-finite-number.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-int.d.mts +5 -33
- package/dist/number/branded-types/non-zero-int.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-int.mjs +5 -25
- package/dist/number/branded-types/non-zero-int.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-int16.d.mts +6 -43
- package/dist/number/branded-types/non-zero-int16.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-int16.mjs +5 -25
- package/dist/number/branded-types/non-zero-int16.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-int32.d.mts +6 -39
- package/dist/number/branded-types/non-zero-int32.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-int32.mjs +5 -25
- package/dist/number/branded-types/non-zero-int32.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-safe-int.d.mts +5 -37
- package/dist/number/branded-types/non-zero-safe-int.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-safe-int.mjs +5 -28
- package/dist/number/branded-types/non-zero-safe-int.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-uint16.d.mts +6 -33
- package/dist/number/branded-types/non-zero-uint16.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-uint16.mjs +5 -17
- package/dist/number/branded-types/non-zero-uint16.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-uint32.d.mts +6 -31
- package/dist/number/branded-types/non-zero-uint32.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-uint32.mjs +5 -17
- package/dist/number/branded-types/non-zero-uint32.mjs.map +1 -1
- package/dist/number/branded-types/positive-finite-number.d.mts +4 -2
- package/dist/number/branded-types/positive-finite-number.d.mts.map +1 -1
- package/dist/number/branded-types/positive-int.d.mts +4 -2
- package/dist/number/branded-types/positive-int.d.mts.map +1 -1
- package/dist/number/branded-types/positive-int16.d.mts +4 -2
- package/dist/number/branded-types/positive-int16.d.mts.map +1 -1
- package/dist/number/branded-types/positive-int32.d.mts +4 -2
- package/dist/number/branded-types/positive-int32.d.mts.map +1 -1
- package/dist/number/branded-types/positive-safe-int.d.mts +4 -2
- package/dist/number/branded-types/positive-safe-int.d.mts.map +1 -1
- package/dist/number/branded-types/positive-uint16.d.mts +4 -2
- package/dist/number/branded-types/positive-uint16.d.mts.map +1 -1
- package/dist/number/branded-types/positive-uint32.d.mts +4 -2
- package/dist/number/branded-types/positive-uint32.d.mts.map +1 -1
- package/dist/number/branded-types/safe-int.d.mts +1 -2
- package/dist/number/branded-types/safe-int.d.mts.map +1 -1
- package/dist/number/branded-types/safe-uint.d.mts +1 -1
- package/dist/number/branded-types/uint.d.mts +1 -1
- package/dist/number/branded-types/uint16.d.mts +1 -1
- package/dist/number/branded-types/uint32.d.mts +1 -1
- package/dist/number/index.d.mts +1 -0
- package/dist/number/index.d.mts.map +1 -1
- package/dist/number/index.mjs +6 -0
- package/dist/number/index.mjs.map +1 -1
- package/dist/number/num-arithmetic-types.d.mts +74 -0
- package/dist/number/num-arithmetic-types.d.mts.map +1 -0
- package/dist/number/num-arithmetic-types.mjs +2 -0
- package/dist/number/num-arithmetic-types.mjs.map +1 -0
- package/dist/number/num.d.mts +53 -9
- package/dist/number/num.d.mts.map +1 -1
- package/dist/number/num.mjs +64 -12
- package/dist/number/num.mjs.map +1 -1
- package/dist/number/refined-number-utils.d.mts +12 -9
- package/dist/number/refined-number-utils.d.mts.map +1 -1
- package/dist/number/refined-number-utils.mjs.map +1 -1
- package/dist/object/object.mjs +6 -0
- package/dist/object/object.mjs.map +1 -1
- package/package.json +2 -2
- package/src/number/branded-types/index.mts +6 -0
- package/src/number/branded-types/negative-finite-number.mts +210 -0
- package/src/number/branded-types/negative-finite-number.test.mts +172 -0
- package/src/number/branded-types/negative-int.mts +160 -0
- package/src/number/branded-types/negative-int.test.mts +146 -0
- package/src/number/branded-types/negative-safe-int.mts +173 -0
- package/src/number/branded-types/negative-safe-int.test.mts +120 -0
- package/src/number/branded-types/non-positive-finite-number.mts +191 -0
- package/src/number/branded-types/non-positive-finite-number.test.mts +286 -0
- package/src/number/branded-types/non-positive-int.mts +251 -0
- package/src/number/branded-types/non-positive-int.test.mts +197 -0
- package/src/number/branded-types/non-positive-safe-int.mts +147 -0
- package/src/number/branded-types/non-positive-safe-int.test.mts +223 -0
- package/src/number/branded-types/non-zero-finite-number.mts +14 -22
- package/src/number/branded-types/non-zero-finite-number.test.mts +0 -12
- package/src/number/branded-types/non-zero-int.mts +10 -32
- package/src/number/branded-types/non-zero-int.test.mts +0 -20
- package/src/number/branded-types/non-zero-int16.mts +8 -32
- package/src/number/branded-types/non-zero-int16.test.mts +0 -30
- package/src/number/branded-types/non-zero-int32.mts +8 -32
- package/src/number/branded-types/non-zero-int32.test.mts +0 -30
- package/src/number/branded-types/non-zero-safe-int.mts +8 -35
- package/src/number/branded-types/non-zero-safe-int.test.mts +0 -32
- package/src/number/branded-types/non-zero-uint16.mts +8 -22
- package/src/number/branded-types/non-zero-uint16.test.mts +0 -16
- package/src/number/branded-types/non-zero-uint32.mts +8 -22
- package/src/number/branded-types/non-zero-uint32.test.mts +0 -16
- package/src/number/index.mts +1 -0
- package/src/number/num-arithmetic-types.mts +269 -0
- package/src/number/num-arithmetic-types.test.mts +130 -0
- package/src/number/num-arithmetic.test.mts +102 -0
- package/src/number/num.mts +96 -15
- package/src/number/refined-number-utils.mts +19 -9
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type FiniteNumber,
|
|
3
|
+
type Int,
|
|
4
|
+
type NegativeFiniteNumber,
|
|
5
|
+
type NegativeInt,
|
|
6
|
+
type NonNegativeFiniteNumber,
|
|
7
|
+
type NonPositiveInt,
|
|
8
|
+
type NonZeroInt,
|
|
9
|
+
type PositiveFiniteNumber,
|
|
10
|
+
type PositiveInt,
|
|
11
|
+
type PositiveSafeInt,
|
|
12
|
+
type SafeUint,
|
|
13
|
+
type Uint,
|
|
14
|
+
} from 'ts-type-forge';
|
|
15
|
+
import { expectType } from '../expect-type.mjs';
|
|
16
|
+
import {
|
|
17
|
+
type AddResult,
|
|
18
|
+
type DivIntResult,
|
|
19
|
+
type DivResult,
|
|
20
|
+
type LevelOf,
|
|
21
|
+
type MulResult,
|
|
22
|
+
type SignOf,
|
|
23
|
+
type SubResult,
|
|
24
|
+
} from './num-arithmetic-types.mjs';
|
|
25
|
+
|
|
26
|
+
describe('NumericArithmetic type-level', () => {
|
|
27
|
+
test('SignOf', () => {
|
|
28
|
+
expectType<SignOf<PositiveInt>, 'pos'>('=');
|
|
29
|
+
|
|
30
|
+
expectType<SignOf<NegativeInt>, 'neg'>('=');
|
|
31
|
+
|
|
32
|
+
expectType<SignOf<Uint>, 'nonneg'>('=');
|
|
33
|
+
|
|
34
|
+
expectType<SignOf<NonPositiveInt>, 'nonpos'>('=');
|
|
35
|
+
|
|
36
|
+
expectType<SignOf<NonZeroInt>, 'nonzero'>('=');
|
|
37
|
+
|
|
38
|
+
expectType<SignOf<Int>, 'any'>('=');
|
|
39
|
+
|
|
40
|
+
expectType<SignOf<number>, 'any'>('=');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('LevelOf', () => {
|
|
44
|
+
expectType<LevelOf<PositiveSafeInt>, 'safeint'>('=');
|
|
45
|
+
|
|
46
|
+
expectType<LevelOf<PositiveInt>, 'int'>('=');
|
|
47
|
+
|
|
48
|
+
expectType<LevelOf<PositiveFiniteNumber>, 'finite'>('=');
|
|
49
|
+
|
|
50
|
+
expectType<LevelOf<number>, 'number'>('=');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('mul: sign flips', () => {
|
|
54
|
+
// <=0 * <=0 = >=0
|
|
55
|
+
expectType<MulResult<NonPositiveInt, NonPositiveInt>, Uint>('=');
|
|
56
|
+
|
|
57
|
+
// <0 * <0 = >0
|
|
58
|
+
expectType<MulResult<NegativeInt, NegativeInt>, PositiveInt>('=');
|
|
59
|
+
|
|
60
|
+
// >0 * <0 = <0
|
|
61
|
+
expectType<MulResult<PositiveInt, NegativeInt>, NegativeInt>('=');
|
|
62
|
+
|
|
63
|
+
// finite negatives -> positive finite
|
|
64
|
+
expectType<
|
|
65
|
+
MulResult<NegativeFiniteNumber, NegativeFiniteNumber>,
|
|
66
|
+
PositiveFiniteNumber
|
|
67
|
+
>('=');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('mul: safeint widens to int', () => {
|
|
71
|
+
expectType<MulResult<PositiveSafeInt, PositiveSafeInt>, PositiveInt>('=');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('add', () => {
|
|
75
|
+
expectType<AddResult<PositiveInt, PositiveInt>, PositiveInt>('=');
|
|
76
|
+
|
|
77
|
+
expectType<AddResult<Uint, Uint>, Uint>('=');
|
|
78
|
+
|
|
79
|
+
// positive + negative -> unknown sign
|
|
80
|
+
expectType<AddResult<PositiveInt, NegativeInt>, Int>('=');
|
|
81
|
+
|
|
82
|
+
// nonzero + nonzero -> can be 0
|
|
83
|
+
expectType<AddResult<NonZeroInt, NonZeroInt>, Int>('=');
|
|
84
|
+
|
|
85
|
+
// safeint widens
|
|
86
|
+
expectType<AddResult<SafeUint, SafeUint>, Uint>('=');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('sub', () => {
|
|
90
|
+
// same-sign sub -> unknown sign (the saturating case lives in branded)
|
|
91
|
+
expectType<SubResult<Uint, Uint>, Int>('=');
|
|
92
|
+
|
|
93
|
+
expectType<SubResult<NonPositiveInt, NonPositiveInt>, Int>('=');
|
|
94
|
+
|
|
95
|
+
// >=0 - <=0 = >=0
|
|
96
|
+
expectType<SubResult<Uint, NonPositiveInt>, Uint>('=');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('div (exact) -> finite level', () => {
|
|
100
|
+
expectType<DivResult<PositiveInt, PositiveInt>, PositiveFiniteNumber>('=');
|
|
101
|
+
|
|
102
|
+
expectType<
|
|
103
|
+
DivResult<NegativeFiniteNumber, NegativeFiniteNumber>,
|
|
104
|
+
PositiveFiniteNumber
|
|
105
|
+
>('=');
|
|
106
|
+
|
|
107
|
+
expectType<DivResult<NonPositiveInt, NegativeInt>, NonNegativeFiniteNumber>(
|
|
108
|
+
'=',
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('divInt (floor)', () => {
|
|
113
|
+
// <0 / <0 = >0 but floor can be 0 -> Uint
|
|
114
|
+
expectType<DivIntResult<NegativeInt, NegativeInt>, Uint>('=');
|
|
115
|
+
|
|
116
|
+
// >0 / >0 floor can be 0 -> Uint
|
|
117
|
+
expectType<DivIntResult<PositiveInt, PositiveInt>, Uint>('=');
|
|
118
|
+
|
|
119
|
+
// safeint preserved under floor division
|
|
120
|
+
expectType<DivIntResult<PositiveSafeInt, PositiveSafeInt>, SafeUint>('=');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('plain number stays number', () => {
|
|
124
|
+
expectType<AddResult<number, number>, number>('=');
|
|
125
|
+
|
|
126
|
+
expectType<MulResult<number, PositiveInt>, number>('=');
|
|
127
|
+
|
|
128
|
+
expectType<AddResult<FiniteNumber, FiniteNumber>, FiniteNumber>('=');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Int,
|
|
3
|
+
type NegativeInt,
|
|
4
|
+
type PositiveFiniteNumber,
|
|
5
|
+
type PositiveInt,
|
|
6
|
+
type Uint,
|
|
7
|
+
} from 'ts-type-forge';
|
|
8
|
+
import { expectType } from '../expect-type.mjs';
|
|
9
|
+
import {
|
|
10
|
+
asNegativeInt,
|
|
11
|
+
asNonPositiveInt,
|
|
12
|
+
asPositiveInt,
|
|
13
|
+
asUint,
|
|
14
|
+
} from './branded-types/index.mjs';
|
|
15
|
+
import { Num } from './num.mjs';
|
|
16
|
+
|
|
17
|
+
describe('Num arithmetic (general / sign-aware)', () => {
|
|
18
|
+
describe('Num.add', () => {
|
|
19
|
+
test('adds and keeps the value (no saturation)', () => {
|
|
20
|
+
expect(Num.add(asPositiveInt(4), asPositiveInt(5))).toBe(9);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('positive + negative is typed Int', () => {
|
|
24
|
+
const r = Num.add(asPositiveInt(4), asNegativeInt(-9));
|
|
25
|
+
|
|
26
|
+
expect(r).toBe(-5);
|
|
27
|
+
|
|
28
|
+
expectType<typeof r, Int>('=');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('positive + positive stays PositiveInt', () => {
|
|
32
|
+
expectType<
|
|
33
|
+
ReturnType<typeof Num.add<PositiveInt, PositiveInt>>,
|
|
34
|
+
PositiveInt
|
|
35
|
+
>('=');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('Num.sub', () => {
|
|
40
|
+
test('does not saturate (unlike branded Uint.sub)', () => {
|
|
41
|
+
const r = Num.sub(asUint(3), asUint(5));
|
|
42
|
+
|
|
43
|
+
expect(r).toBe(-2);
|
|
44
|
+
|
|
45
|
+
expectType<typeof r, Int>('=');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('Num.mul', () => {
|
|
50
|
+
test('negative * negative is positive (PositiveInt)', () => {
|
|
51
|
+
const r = Num.mul(asNegativeInt(-5), asNegativeInt(-3));
|
|
52
|
+
|
|
53
|
+
expect(r).toBe(15);
|
|
54
|
+
|
|
55
|
+
expectType<typeof r, PositiveInt>('=');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('positive * negative is negative (NegativeInt)', () => {
|
|
59
|
+
const r = Num.mul(asPositiveInt(5), asNegativeInt(-3));
|
|
60
|
+
|
|
61
|
+
expect(r).toBe(-15);
|
|
62
|
+
|
|
63
|
+
expectType<typeof r, NegativeInt>('=');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('non-positive * non-positive is non-negative (Uint)', () => {
|
|
67
|
+
const r = Num.mul(asNonPositiveInt(-4), asNonPositiveInt(-2));
|
|
68
|
+
|
|
69
|
+
expect(r).toBe(8);
|
|
70
|
+
|
|
71
|
+
expectType<typeof r, Uint>('=');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('Num.div (exact)', () => {
|
|
76
|
+
test('exact division keeps the fractional value', () => {
|
|
77
|
+
const r = Num.div(asPositiveInt(9), asPositiveInt(2));
|
|
78
|
+
|
|
79
|
+
expect(r).toBe(4.5);
|
|
80
|
+
|
|
81
|
+
expectType<typeof r, PositiveFiniteNumber>('=');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('negative / negative is positive', () => {
|
|
85
|
+
expect(Num.div(asNegativeInt(-10), asNegativeInt(-2))).toBe(5);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('Num.divInt (floor)', () => {
|
|
90
|
+
test('floor division of negatives is non-negative (Uint)', () => {
|
|
91
|
+
const r = Num.divInt(asNegativeInt(-7), asNegativeInt(-2));
|
|
92
|
+
|
|
93
|
+
expect(r).toBe(3);
|
|
94
|
+
|
|
95
|
+
expectType<typeof r, Uint>('=');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('floor can reach 0', () => {
|
|
99
|
+
expect(Num.divInt(asNegativeInt(-2), asNegativeInt(-3))).toBe(0);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
package/src/number/num.mts
CHANGED
|
@@ -19,6 +19,13 @@ import {
|
|
|
19
19
|
import { expectType } from '../expect-type.mjs';
|
|
20
20
|
import { Result } from '../functional/index.mjs';
|
|
21
21
|
import { type SmallPositiveInt } from '../types.mjs';
|
|
22
|
+
import {
|
|
23
|
+
type AddResult,
|
|
24
|
+
type DivIntResult,
|
|
25
|
+
type DivResult,
|
|
26
|
+
type MulResult,
|
|
27
|
+
type SubResult,
|
|
28
|
+
} from './num-arithmetic-types.mjs';
|
|
22
29
|
|
|
23
30
|
/**
|
|
24
31
|
* Namespace providing utility functions for number manipulation and validation.
|
|
@@ -504,19 +511,86 @@ export namespace Num {
|
|
|
504
511
|
}
|
|
505
512
|
|
|
506
513
|
/**
|
|
507
|
-
*
|
|
514
|
+
* Adds two numbers, returning the tightest sound branded result type.
|
|
515
|
+
*
|
|
516
|
+
* The sign of the result is computed exactly (e.g. a non-negative plus a
|
|
517
|
+
* non-negative is non-negative); the integer/finite level is preserved, with
|
|
518
|
+
* `SafeInt` operands widening to `Int` because the sum may exceed the safe
|
|
519
|
+
* range. No clamping is performed — for a result that stays inside a bounded
|
|
520
|
+
* type, use that type's branded `add` instead.
|
|
521
|
+
*
|
|
522
|
+
* @param a - The first addend.
|
|
523
|
+
* @param b - The second addend.
|
|
524
|
+
* @returns `a + b`, typed as the tightest branded type guaranteed to contain
|
|
525
|
+
* it.
|
|
526
|
+
*/
|
|
527
|
+
export const add = <A extends number, B extends number>(
|
|
528
|
+
a: A,
|
|
529
|
+
b: B,
|
|
530
|
+
): AddResult<A, B> =>
|
|
531
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
532
|
+
(a + b) as AddResult<A, B>;
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Subtracts `b` from `a`, returning the tightest sound branded result type.
|
|
508
536
|
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
*
|
|
537
|
+
* The sign is computed exactly; the level is preserved (with `SafeInt`
|
|
538
|
+
* widening to `Int`). No clamping — for saturating subtraction that stays in a
|
|
539
|
+
* bounded type (e.g. `Uint.sub` clamping at `0`), use that type's branded
|
|
540
|
+
* `sub`.
|
|
512
541
|
*
|
|
513
|
-
* @param a - The
|
|
514
|
-
* @param b - The
|
|
515
|
-
* @returns
|
|
542
|
+
* @param a - The minuend.
|
|
543
|
+
* @param b - The subtrahend.
|
|
544
|
+
* @returns `a - b`, typed as the tightest branded type guaranteed to contain
|
|
545
|
+
* it.
|
|
516
546
|
*/
|
|
517
|
-
export const
|
|
518
|
-
|
|
519
|
-
|
|
547
|
+
export const sub = <A extends number, B extends number>(
|
|
548
|
+
a: A,
|
|
549
|
+
b: B,
|
|
550
|
+
): SubResult<A, B> =>
|
|
551
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
552
|
+
(a - b) as SubResult<A, B>;
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Multiplies two numbers, returning the tightest sound branded result type.
|
|
556
|
+
*
|
|
557
|
+
* The sign follows the usual product rule (e.g. negative times negative is
|
|
558
|
+
* positive); the level is preserved (with `SafeInt` widening to `Int`). No
|
|
559
|
+
* clamping.
|
|
560
|
+
*
|
|
561
|
+
* @param a - The first factor.
|
|
562
|
+
* @param b - The second factor.
|
|
563
|
+
* @returns `a * b`, typed as the tightest branded type guaranteed to contain
|
|
564
|
+
* it.
|
|
565
|
+
*/
|
|
566
|
+
export const mul = <A extends number, B extends number>(
|
|
567
|
+
a: A,
|
|
568
|
+
b: B,
|
|
569
|
+
): MulResult<A, B> =>
|
|
570
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
571
|
+
(a * b) as MulResult<A, B>;
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Performs type-safe exact division with a compile-time zero-check.
|
|
575
|
+
*
|
|
576
|
+
* The divisor must be typed as `NonZeroNumber` or a non-zero numeric literal.
|
|
577
|
+
* The result sign follows the quotient rule; because exact division can be
|
|
578
|
+
* fractional, the level is `finite` (or `number`).
|
|
579
|
+
*
|
|
580
|
+
* @param a - The dividend.
|
|
581
|
+
* @param b - The divisor (must be non-zero, enforced by types).
|
|
582
|
+
* @returns `a / b`, typed as the tightest branded type guaranteed to contain
|
|
583
|
+
* it.
|
|
584
|
+
*/
|
|
585
|
+
export const div = <
|
|
586
|
+
A extends number,
|
|
587
|
+
B extends NonZeroNumber | SmallInt<'!=0'>,
|
|
588
|
+
>(
|
|
589
|
+
a: A,
|
|
590
|
+
b: B,
|
|
591
|
+
): DivResult<A, B> =>
|
|
592
|
+
// eslint-disable-next-line total-functions/no-partial-division, total-functions/no-unsafe-type-assertion
|
|
593
|
+
(a / b) as DivResult<A, B>;
|
|
520
594
|
|
|
521
595
|
/**
|
|
522
596
|
* Performs integer division using floor division.
|
|
@@ -531,12 +605,19 @@ export namespace Num {
|
|
|
531
605
|
* @param b - The divisor
|
|
532
606
|
* @returns The integer quotient, or `NaN` if b is zero
|
|
533
607
|
*/
|
|
534
|
-
export const divInt =
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
608
|
+
export const divInt = <
|
|
609
|
+
A extends number,
|
|
610
|
+
B extends NonZeroNumber | SmallInt<'!=0'>,
|
|
611
|
+
>(
|
|
612
|
+
a: A,
|
|
613
|
+
b: B,
|
|
614
|
+
): DivIntResult<A, B> => {
|
|
538
615
|
// eslint-disable-next-line total-functions/no-partial-division
|
|
539
|
-
Math.floor(Math.floor(a) / Math.floor(b));
|
|
616
|
+
const quotient = Math.floor(Math.floor(a) / Math.floor(b));
|
|
617
|
+
|
|
618
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
619
|
+
return quotient as DivIntResult<A, B>;
|
|
620
|
+
};
|
|
540
621
|
|
|
541
622
|
/**
|
|
542
623
|
* Rounds a number to a specified number of decimal places.
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type IntersectBrand,
|
|
7
7
|
type NonNegativeNumber,
|
|
8
8
|
type NonZeroNumber,
|
|
9
|
+
type PositiveNumber,
|
|
9
10
|
type RelaxedExclude,
|
|
10
11
|
type TypeEq,
|
|
11
12
|
type UnknownBrand,
|
|
@@ -84,10 +85,10 @@ export namespace TsDataForgeInternals {
|
|
|
84
85
|
* @template N - A branded number type
|
|
85
86
|
* @internal
|
|
86
87
|
*/
|
|
87
|
-
export type ToNonNegative<N extends UnknownNumberBrand> =
|
|
88
|
-
N
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
export type ToNonNegative<N extends UnknownNumberBrand> =
|
|
89
|
+
'!=0' extends UnwrapBrandTrueKeys<N>
|
|
90
|
+
? IntersectBrand<N, PositiveNumber>
|
|
91
|
+
: IntersectBrand<N, NonNegativeNumber>;
|
|
91
92
|
|
|
92
93
|
/**
|
|
93
94
|
* Removes the non-zero brand constraint from a branded number type. Used
|
|
@@ -99,7 +100,7 @@ export namespace TsDataForgeInternals {
|
|
|
99
100
|
export type RemoveNonZeroBrandKey<N extends UnknownNumberBrand> = Brand<
|
|
100
101
|
GetBrandValuePart<N>,
|
|
101
102
|
RelaxedExclude<UnwrapBrandTrueKeys<N>, '!=0'> & string,
|
|
102
|
-
UnwrapBrandFalseKeys<N> & string
|
|
103
|
+
RelaxedExclude<UnwrapBrandFalseKeys<N>, '<=0' | '>=0'> & string
|
|
103
104
|
>;
|
|
104
105
|
|
|
105
106
|
type CastToInt<N> = N extends Int ? N : never;
|
|
@@ -120,9 +121,13 @@ export namespace TsDataForgeInternals {
|
|
|
120
121
|
* 'positive' | 'range'
|
|
121
122
|
* @internal
|
|
122
123
|
*/
|
|
124
|
+
/** The closed binary arithmetic operations a branded namespace may expose. */
|
|
125
|
+
export type NumberClassBinOp = 'add' | 'div' | 'mul' | 'sub';
|
|
126
|
+
|
|
123
127
|
export type NumberClass<
|
|
124
128
|
N extends UnknownNumberBrand,
|
|
125
129
|
classes extends 'int' | 'non-negative' | 'positive' | 'range',
|
|
130
|
+
binOps extends NumberClassBinOp = NumberClassBinOp,
|
|
126
131
|
> = ('int' extends classes
|
|
127
132
|
? unknown
|
|
128
133
|
: 'positive' extends classes
|
|
@@ -162,16 +167,21 @@ export namespace TsDataForgeInternals {
|
|
|
162
167
|
clamp: (a: number) => N;
|
|
163
168
|
}>
|
|
164
169
|
: unknown) &
|
|
170
|
+
Pick<
|
|
171
|
+
Readonly<{
|
|
172
|
+
add: (x: N, y: N) => N;
|
|
173
|
+
sub: (x: N, y: N) => N;
|
|
174
|
+
mul: (x: N, y: N) => N;
|
|
175
|
+
div: (x: N, y: ToNonZero<N>) => N;
|
|
176
|
+
}>,
|
|
177
|
+
binOps
|
|
178
|
+
> &
|
|
165
179
|
Readonly<{
|
|
166
180
|
is: (a: number) => a is N;
|
|
167
181
|
min: (...values: readonly N[]) => N;
|
|
168
182
|
max: (...values: readonly N[]) => N;
|
|
169
183
|
random: (min: N, max: N) => N;
|
|
170
184
|
pow: (x: N, y: N) => N;
|
|
171
|
-
add: (x: N, y: N) => N;
|
|
172
|
-
sub: (x: N, y: N) => N;
|
|
173
|
-
mul: (x: N, y: N) => N;
|
|
174
|
-
div: (x: N, y: ToNonZero<N>) => N;
|
|
175
185
|
}>;
|
|
176
186
|
|
|
177
187
|
type BaseKeys =
|