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.
Files changed (174) hide show
  1. package/README.md +2 -2
  2. package/dist/array/impl/array-utils-iterators.mjs +6 -0
  3. package/dist/array/impl/array-utils-iterators.mjs.map +1 -1
  4. package/dist/array/impl/array-utils-modification.mjs +6 -0
  5. package/dist/array/impl/array-utils-modification.mjs.map +1 -1
  6. package/dist/array/impl/array-utils-reducing-value.mjs +6 -0
  7. package/dist/array/impl/array-utils-reducing-value.mjs.map +1 -1
  8. package/dist/array/impl/array-utils-search.mjs +6 -0
  9. package/dist/array/impl/array-utils-search.mjs.map +1 -1
  10. package/dist/array/impl/array-utils-slice-clamped.mjs +6 -0
  11. package/dist/array/impl/array-utils-slice-clamped.mjs.map +1 -1
  12. package/dist/array/impl/array-utils-slicing.mjs +6 -0
  13. package/dist/array/impl/array-utils-slicing.mjs.map +1 -1
  14. package/dist/array/impl/array-utils-transformation.mjs +6 -0
  15. package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
  16. package/dist/array/impl/array-utils-validation.mjs +6 -0
  17. package/dist/array/impl/array-utils-validation.mjs.map +1 -1
  18. package/dist/collections/imap-mapped.mjs +6 -0
  19. package/dist/collections/imap-mapped.mjs.map +1 -1
  20. package/dist/collections/imap.mjs +6 -0
  21. package/dist/collections/imap.mjs.map +1 -1
  22. package/dist/collections/iset-mapped.mjs +6 -0
  23. package/dist/collections/iset-mapped.mjs.map +1 -1
  24. package/dist/collections/iset.mjs +6 -0
  25. package/dist/collections/iset.mjs.map +1 -1
  26. package/dist/collections/queue.mjs +6 -0
  27. package/dist/collections/queue.mjs.map +1 -1
  28. package/dist/collections/stack.mjs +6 -0
  29. package/dist/collections/stack.mjs.map +1 -1
  30. package/dist/entry-point.mjs +6 -0
  31. package/dist/entry-point.mjs.map +1 -1
  32. package/dist/index.mjs +6 -0
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/iterator/range.mjs +6 -0
  35. package/dist/iterator/range.mjs.map +1 -1
  36. package/dist/json/json.mjs +6 -0
  37. package/dist/json/json.mjs.map +1 -1
  38. package/dist/number/branded-types/finite-number.d.mts +1 -1
  39. package/dist/number/branded-types/index.d.mts +6 -0
  40. package/dist/number/branded-types/index.d.mts.map +1 -1
  41. package/dist/number/branded-types/index.mjs +6 -0
  42. package/dist/number/branded-types/index.mjs.map +1 -1
  43. package/dist/number/branded-types/int.d.mts +1 -2
  44. package/dist/number/branded-types/int.d.mts.map +1 -1
  45. package/dist/number/branded-types/int16.d.mts +2 -3
  46. package/dist/number/branded-types/int16.d.mts.map +1 -1
  47. package/dist/number/branded-types/int32.d.mts +2 -3
  48. package/dist/number/branded-types/int32.d.mts.map +1 -1
  49. package/dist/number/branded-types/negative-finite-number.d.mts +143 -0
  50. package/dist/number/branded-types/negative-finite-number.d.mts.map +1 -0
  51. package/dist/number/branded-types/negative-finite-number.mjs +156 -0
  52. package/dist/number/branded-types/negative-finite-number.mjs.map +1 -0
  53. package/dist/number/branded-types/negative-int.d.mts +120 -0
  54. package/dist/number/branded-types/negative-int.d.mts.map +1 -0
  55. package/dist/number/branded-types/negative-int.mjs +125 -0
  56. package/dist/number/branded-types/negative-int.mjs.map +1 -0
  57. package/dist/number/branded-types/negative-safe-int.d.mts +132 -0
  58. package/dist/number/branded-types/negative-safe-int.d.mts.map +1 -0
  59. package/dist/number/branded-types/negative-safe-int.mjs +136 -0
  60. package/dist/number/branded-types/negative-safe-int.mjs.map +1 -0
  61. package/dist/number/branded-types/non-negative-finite-number.d.mts +1 -1
  62. package/dist/number/branded-types/non-negative-int16.d.mts +1 -1
  63. package/dist/number/branded-types/non-negative-int32.d.mts +1 -1
  64. package/dist/number/branded-types/non-positive-finite-number.d.mts +130 -0
  65. package/dist/number/branded-types/non-positive-finite-number.d.mts.map +1 -0
  66. package/dist/number/branded-types/non-positive-finite-number.mjs +134 -0
  67. package/dist/number/branded-types/non-positive-finite-number.mjs.map +1 -0
  68. package/dist/number/branded-types/non-positive-int.d.mts +224 -0
  69. package/dist/number/branded-types/non-positive-int.d.mts.map +1 -0
  70. package/dist/number/branded-types/non-positive-int.mjs +222 -0
  71. package/dist/number/branded-types/non-positive-int.mjs.map +1 -0
  72. package/dist/number/branded-types/non-positive-safe-int.d.mts +119 -0
  73. package/dist/number/branded-types/non-positive-safe-int.d.mts.map +1 -0
  74. package/dist/number/branded-types/non-positive-safe-int.mjs +116 -0
  75. package/dist/number/branded-types/non-positive-safe-int.mjs.map +1 -0
  76. package/dist/number/branded-types/non-zero-finite-number.d.mts +5 -17
  77. package/dist/number/branded-types/non-zero-finite-number.d.mts.map +1 -1
  78. package/dist/number/branded-types/non-zero-finite-number.mjs +5 -17
  79. package/dist/number/branded-types/non-zero-finite-number.mjs.map +1 -1
  80. package/dist/number/branded-types/non-zero-int.d.mts +5 -33
  81. package/dist/number/branded-types/non-zero-int.d.mts.map +1 -1
  82. package/dist/number/branded-types/non-zero-int.mjs +5 -25
  83. package/dist/number/branded-types/non-zero-int.mjs.map +1 -1
  84. package/dist/number/branded-types/non-zero-int16.d.mts +6 -43
  85. package/dist/number/branded-types/non-zero-int16.d.mts.map +1 -1
  86. package/dist/number/branded-types/non-zero-int16.mjs +5 -25
  87. package/dist/number/branded-types/non-zero-int16.mjs.map +1 -1
  88. package/dist/number/branded-types/non-zero-int32.d.mts +6 -39
  89. package/dist/number/branded-types/non-zero-int32.d.mts.map +1 -1
  90. package/dist/number/branded-types/non-zero-int32.mjs +5 -25
  91. package/dist/number/branded-types/non-zero-int32.mjs.map +1 -1
  92. package/dist/number/branded-types/non-zero-safe-int.d.mts +5 -37
  93. package/dist/number/branded-types/non-zero-safe-int.d.mts.map +1 -1
  94. package/dist/number/branded-types/non-zero-safe-int.mjs +5 -28
  95. package/dist/number/branded-types/non-zero-safe-int.mjs.map +1 -1
  96. package/dist/number/branded-types/non-zero-uint16.d.mts +6 -33
  97. package/dist/number/branded-types/non-zero-uint16.d.mts.map +1 -1
  98. package/dist/number/branded-types/non-zero-uint16.mjs +5 -17
  99. package/dist/number/branded-types/non-zero-uint16.mjs.map +1 -1
  100. package/dist/number/branded-types/non-zero-uint32.d.mts +6 -31
  101. package/dist/number/branded-types/non-zero-uint32.d.mts.map +1 -1
  102. package/dist/number/branded-types/non-zero-uint32.mjs +5 -17
  103. package/dist/number/branded-types/non-zero-uint32.mjs.map +1 -1
  104. package/dist/number/branded-types/positive-finite-number.d.mts +4 -2
  105. package/dist/number/branded-types/positive-finite-number.d.mts.map +1 -1
  106. package/dist/number/branded-types/positive-int.d.mts +4 -2
  107. package/dist/number/branded-types/positive-int.d.mts.map +1 -1
  108. package/dist/number/branded-types/positive-int16.d.mts +4 -2
  109. package/dist/number/branded-types/positive-int16.d.mts.map +1 -1
  110. package/dist/number/branded-types/positive-int32.d.mts +4 -2
  111. package/dist/number/branded-types/positive-int32.d.mts.map +1 -1
  112. package/dist/number/branded-types/positive-safe-int.d.mts +4 -2
  113. package/dist/number/branded-types/positive-safe-int.d.mts.map +1 -1
  114. package/dist/number/branded-types/positive-uint16.d.mts +4 -2
  115. package/dist/number/branded-types/positive-uint16.d.mts.map +1 -1
  116. package/dist/number/branded-types/positive-uint32.d.mts +4 -2
  117. package/dist/number/branded-types/positive-uint32.d.mts.map +1 -1
  118. package/dist/number/branded-types/safe-int.d.mts +1 -2
  119. package/dist/number/branded-types/safe-int.d.mts.map +1 -1
  120. package/dist/number/branded-types/safe-uint.d.mts +1 -1
  121. package/dist/number/branded-types/uint.d.mts +1 -1
  122. package/dist/number/branded-types/uint16.d.mts +1 -1
  123. package/dist/number/branded-types/uint32.d.mts +1 -1
  124. package/dist/number/index.d.mts +1 -0
  125. package/dist/number/index.d.mts.map +1 -1
  126. package/dist/number/index.mjs +6 -0
  127. package/dist/number/index.mjs.map +1 -1
  128. package/dist/number/num-arithmetic-types.d.mts +74 -0
  129. package/dist/number/num-arithmetic-types.d.mts.map +1 -0
  130. package/dist/number/num-arithmetic-types.mjs +2 -0
  131. package/dist/number/num-arithmetic-types.mjs.map +1 -0
  132. package/dist/number/num.d.mts +53 -9
  133. package/dist/number/num.d.mts.map +1 -1
  134. package/dist/number/num.mjs +64 -12
  135. package/dist/number/num.mjs.map +1 -1
  136. package/dist/number/refined-number-utils.d.mts +12 -9
  137. package/dist/number/refined-number-utils.d.mts.map +1 -1
  138. package/dist/number/refined-number-utils.mjs.map +1 -1
  139. package/dist/object/object.mjs +6 -0
  140. package/dist/object/object.mjs.map +1 -1
  141. package/package.json +2 -2
  142. package/src/number/branded-types/index.mts +6 -0
  143. package/src/number/branded-types/negative-finite-number.mts +210 -0
  144. package/src/number/branded-types/negative-finite-number.test.mts +172 -0
  145. package/src/number/branded-types/negative-int.mts +160 -0
  146. package/src/number/branded-types/negative-int.test.mts +146 -0
  147. package/src/number/branded-types/negative-safe-int.mts +173 -0
  148. package/src/number/branded-types/negative-safe-int.test.mts +120 -0
  149. package/src/number/branded-types/non-positive-finite-number.mts +191 -0
  150. package/src/number/branded-types/non-positive-finite-number.test.mts +286 -0
  151. package/src/number/branded-types/non-positive-int.mts +251 -0
  152. package/src/number/branded-types/non-positive-int.test.mts +197 -0
  153. package/src/number/branded-types/non-positive-safe-int.mts +147 -0
  154. package/src/number/branded-types/non-positive-safe-int.test.mts +223 -0
  155. package/src/number/branded-types/non-zero-finite-number.mts +14 -22
  156. package/src/number/branded-types/non-zero-finite-number.test.mts +0 -12
  157. package/src/number/branded-types/non-zero-int.mts +10 -32
  158. package/src/number/branded-types/non-zero-int.test.mts +0 -20
  159. package/src/number/branded-types/non-zero-int16.mts +8 -32
  160. package/src/number/branded-types/non-zero-int16.test.mts +0 -30
  161. package/src/number/branded-types/non-zero-int32.mts +8 -32
  162. package/src/number/branded-types/non-zero-int32.test.mts +0 -30
  163. package/src/number/branded-types/non-zero-safe-int.mts +8 -35
  164. package/src/number/branded-types/non-zero-safe-int.test.mts +0 -32
  165. package/src/number/branded-types/non-zero-uint16.mts +8 -22
  166. package/src/number/branded-types/non-zero-uint16.test.mts +0 -16
  167. package/src/number/branded-types/non-zero-uint32.mts +8 -22
  168. package/src/number/branded-types/non-zero-uint32.test.mts +0 -16
  169. package/src/number/index.mts +1 -0
  170. package/src/number/num-arithmetic-types.mts +269 -0
  171. package/src/number/num-arithmetic-types.test.mts +130 -0
  172. package/src/number/num-arithmetic.test.mts +102 -0
  173. package/src/number/num.mts +96 -15
  174. package/src/number/refined-number-utils.mts +19 -9
@@ -16,17 +16,14 @@ const {
16
16
  MAX_VALUE,
17
17
  MIN_VALUE,
18
18
  abs,
19
- add,
20
19
  castType,
21
20
  clamp,
22
- div,
23
21
  is,
24
22
  max: max_,
25
23
  min: min_,
26
24
  mul,
27
25
  pow,
28
26
  randomNonZero: random,
29
- sub,
30
27
  } = TsDataForgeInternals.RefinedNumberUtils.operatorsForInteger<
31
28
  ElementType,
32
29
  SafeInt,
@@ -144,52 +141,27 @@ export const NonZeroSafeInt = {
144
141
  */
145
142
  pow,
146
143
 
147
- /**
148
- * Adds two NonZeroSafeInt values.
149
- *
150
- * @param a The first NonZeroSafeInt.
151
- * @param b The second NonZeroSafeInt.
152
- * @returns `a + b` clamped to non-zero safe integer range as a
153
- * NonZeroSafeInt.
154
- */
155
- add,
156
-
157
- /**
158
- * Subtracts one NonZeroSafeInt from another.
159
- *
160
- * @param a The minuend NonZeroSafeInt.
161
- * @param b The subtrahend NonZeroSafeInt.
162
- * @returns `a - b` clamped to non-zero safe integer range as a
163
- * NonZeroSafeInt.
164
- */
165
- sub,
166
-
167
144
  /**
168
145
  * Multiplies two NonZeroSafeInt values.
169
146
  *
147
+ * The product of two non-zero safe integers is always non-zero, so this stays
148
+ * closed; for the non-closed operations (`add`/`sub`/`div`, whose result may
149
+ * be `0`) use {@link Num.add}/{@link Num.sub}/{@link Num.divInt}.
150
+ *
170
151
  * @param a The first NonZeroSafeInt.
171
152
  * @param b The second NonZeroSafeInt.
172
153
  * @returns `a * b` clamped to non-zero safe integer range as a
173
154
  * NonZeroSafeInt.
174
155
  */
175
156
  mul,
176
-
177
- /**
178
- * Divides one NonZeroSafeInt by another using floor division.
179
- *
180
- * @param a The dividend NonZeroSafeInt.
181
- * @param b The divisor NonZeroSafeInt.
182
- * @returns `⌊a / b⌋` clamped to non-zero safe integer range as a
183
- * NonZeroSafeInt.
184
- */
185
- div,
186
157
  } as const;
187
158
 
188
159
  expectType<
189
160
  keyof typeof NonZeroSafeInt,
190
161
  keyof TsDataForgeInternals.RefinedNumberUtils.NumberClass<
191
162
  ElementType,
192
- 'int' | 'range'
163
+ 'int' | 'range',
164
+ 'mul'
193
165
  >
194
166
  >('=');
195
167
 
@@ -197,6 +169,7 @@ expectType<
197
169
  typeof NonZeroSafeInt,
198
170
  TsDataForgeInternals.RefinedNumberUtils.NumberClass<
199
171
  ElementType,
200
- 'int' | 'range'
172
+ 'int' | 'range',
173
+ 'mul'
201
174
  >
202
175
  >('<=');
@@ -170,26 +170,6 @@ describe('NonZeroSafeInt test', () => {
170
170
  expect(NonZeroSafeInt.max(a, c)).toBe(5);
171
171
  });
172
172
 
173
- test('add (with clamping to safe integer range)', () => {
174
- const largeValue = asNonZeroSafeInt(Number.MAX_SAFE_INTEGER - 1);
175
-
176
- const result = NonZeroSafeInt.add(largeValue, asNonZeroSafeInt(10));
177
-
178
- expect(result).toBe(Number.MAX_SAFE_INTEGER); // clamped to max
179
-
180
- expect(NonZeroSafeInt.add(a, b)).toBe(7);
181
- });
182
-
183
- test('sub (with clamping to safe integer range)', () => {
184
- const smallValue = asNonZeroSafeInt(Number.MIN_SAFE_INTEGER + 1);
185
-
186
- const result = NonZeroSafeInt.sub(smallValue, asNonZeroSafeInt(10));
187
-
188
- expect(result).toBe(Number.MIN_SAFE_INTEGER); // clamped to min
189
-
190
- expect(NonZeroSafeInt.sub(a, b)).toBe(3);
191
- });
192
-
193
173
  test('mul (with clamping to safe integer range)', () => {
194
174
  const largeValue = asNonZeroSafeInt(
195
175
  Math.floor(Math.sqrt(Number.MAX_SAFE_INTEGER)),
@@ -204,18 +184,6 @@ describe('NonZeroSafeInt test', () => {
204
184
  ).toBe(50);
205
185
  });
206
186
 
207
- test('div (floor division with clamping)', () => {
208
- expect(NonZeroSafeInt.div(a, b)).toBe(2);
209
-
210
- expect(NonZeroSafeInt.div(asNonZeroSafeInt(7), asNonZeroSafeInt(3))).toBe(
211
- 2,
212
- );
213
-
214
- expect(
215
- NonZeroSafeInt.div(asNonZeroSafeInt(-7), asNonZeroSafeInt(3)),
216
- ).toBe(-3);
217
- });
218
-
219
187
  test('pow (with clamping to safe integer range)', () => {
220
188
  const result = NonZeroSafeInt.pow(
221
189
  asNonZeroSafeInt(1000),
@@ -14,14 +14,12 @@ const {
14
14
  add,
15
15
  castType,
16
16
  clamp,
17
- div,
18
17
  is,
19
18
  max: max_,
20
19
  min: min_,
21
20
  mul,
22
21
  pow,
23
22
  randomNonZero: random,
24
- sub,
25
23
  } = TsDataForgeInternals.RefinedNumberUtils.operatorsForInteger<
26
24
  ElementType,
27
25
  1,
@@ -136,39 +134,26 @@ export const NonZeroUint16 = {
136
134
  */
137
135
  add,
138
136
 
139
- /**
140
- * Subtracts one NonZeroUint16 from another.
141
- *
142
- * @param a The minuend NonZeroUint16.
143
- * @param b The subtrahend NonZeroUint16.
144
- * @returns `a - b` clamped to [1, 65535] as a NonZeroUint16 (minimum 1).
145
- */
146
- sub,
147
-
148
137
  /**
149
138
  * Multiplies two NonZeroUint16 values.
150
139
  *
140
+ * Both `add` and `mul` stay within `[1, 65535]`. For the non-closed
141
+ * operations (`sub`/`div`, whose result may be `0`) use {@link Num.sub} /
142
+ * {@link Num.divInt}.
143
+ *
151
144
  * @param a The first NonZeroUint16.
152
145
  * @param b The second NonZeroUint16.
153
146
  * @returns `a * b` clamped to [1, 65535] as a NonZeroUint16.
154
147
  */
155
148
  mul,
156
-
157
- /**
158
- * Divides one NonZeroUint16 by another using floor division.
159
- *
160
- * @param a The dividend NonZeroUint16.
161
- * @param b The divisor NonZeroUint16.
162
- * @returns `⌊a / b⌋` clamped to [1, 65535] as a NonZeroUint16.
163
- */
164
- div,
165
149
  } as const;
166
150
 
167
151
  expectType<
168
152
  keyof typeof NonZeroUint16,
169
153
  keyof TsDataForgeInternals.RefinedNumberUtils.NumberClass<
170
154
  ElementType,
171
- 'int' | 'positive' | 'range'
155
+ 'int' | 'positive' | 'range',
156
+ 'add' | 'mul'
172
157
  >
173
158
  >('=');
174
159
 
@@ -176,6 +161,7 @@ expectType<
176
161
  typeof NonZeroUint16,
177
162
  TsDataForgeInternals.RefinedNumberUtils.NumberClass<
178
163
  ElementType,
179
- 'int' | 'positive' | 'range'
164
+ 'int' | 'positive' | 'range',
165
+ 'add' | 'mul'
180
166
  >
181
167
  >('<=');
@@ -159,12 +159,6 @@ describe('NonZeroUint16 test', () => {
159
159
  expect(NonZeroUint16.add(a, b)).toBe(150);
160
160
  });
161
161
 
162
- test('sub (never goes below 1)', () => {
163
- expect(NonZeroUint16.sub(a, b)).toBe(50);
164
-
165
- expect(NonZeroUint16.sub(b, a)).toBe(1); // clamped to 1
166
- });
167
-
168
162
  test('mul (with clamping to non-zero uint16 range)', () => {
169
163
  const result = NonZeroUint16.mul(
170
164
  asNonZeroUint16(1000),
@@ -178,16 +172,6 @@ describe('NonZeroUint16 test', () => {
178
172
  );
179
173
  });
180
174
 
181
- test('div (floor division, never goes below 1)', () => {
182
- expect(NonZeroUint16.div(a, asNonZeroUint16(50))).toBe(2);
183
-
184
- expect(NonZeroUint16.div(asNonZeroUint16(7), asNonZeroUint16(3))).toBe(2);
185
-
186
- expect(NonZeroUint16.div(asNonZeroUint16(50), asNonZeroUint16(100))).toBe(
187
- 1,
188
- ); // floor(50/100) = 0, clamped to 1
189
- });
190
-
191
175
  test('pow (with clamping to non-zero uint16 range)', () => {
192
176
  const result = NonZeroUint16.pow(
193
177
  asNonZeroUint16(256),
@@ -14,14 +14,12 @@ const {
14
14
  add,
15
15
  castType,
16
16
  clamp,
17
- div,
18
17
  is,
19
18
  max: max_,
20
19
  min: min_,
21
20
  mul,
22
21
  pow,
23
22
  randomNonZero: random,
24
- sub,
25
23
  } = TsDataForgeInternals.RefinedNumberUtils.operatorsForInteger<
26
24
  ElementType,
27
25
  1,
@@ -136,39 +134,26 @@ export const NonZeroUint32 = {
136
134
  */
137
135
  add,
138
136
 
139
- /**
140
- * Subtracts one NonZeroUint32 from another.
141
- *
142
- * @param a The minuend NonZeroUint32.
143
- * @param b The subtrahend NonZeroUint32.
144
- * @returns `a - b` clamped to [1, 4294967295] as a NonZeroUint32 (minimum 1).
145
- */
146
- sub,
147
-
148
137
  /**
149
138
  * Multiplies two NonZeroUint32 values.
150
139
  *
140
+ * Both `add` and `mul` stay within `[1, 4294967295]`. For the non-closed
141
+ * operations (`sub`/`div`, whose result may be `0`) use {@link Num.sub} /
142
+ * {@link Num.divInt}.
143
+ *
151
144
  * @param a The first NonZeroUint32.
152
145
  * @param b The second NonZeroUint32.
153
146
  * @returns `a * b` clamped to [1, 4294967295] as a NonZeroUint32.
154
147
  */
155
148
  mul,
156
-
157
- /**
158
- * Divides one NonZeroUint32 by another using floor division.
159
- *
160
- * @param a The dividend NonZeroUint32.
161
- * @param b The divisor NonZeroUint32.
162
- * @returns `⌊a / b⌋` clamped to [1, 4294967295] as a NonZeroUint32.
163
- */
164
- div,
165
149
  } as const;
166
150
 
167
151
  expectType<
168
152
  keyof typeof NonZeroUint32,
169
153
  keyof TsDataForgeInternals.RefinedNumberUtils.NumberClass<
170
154
  ElementType,
171
- 'int' | 'positive' | 'range'
155
+ 'int' | 'positive' | 'range',
156
+ 'add' | 'mul'
172
157
  >
173
158
  >('=');
174
159
 
@@ -176,6 +161,7 @@ expectType<
176
161
  typeof NonZeroUint32,
177
162
  TsDataForgeInternals.RefinedNumberUtils.NumberClass<
178
163
  ElementType,
179
- 'int' | 'positive' | 'range'
164
+ 'int' | 'positive' | 'range',
165
+ 'add' | 'mul'
180
166
  >
181
167
  >('<=');
@@ -159,12 +159,6 @@ describe('NonZeroUint32 test', () => {
159
159
  expect(NonZeroUint32.add(a, b)).toBe(1_500_000);
160
160
  });
161
161
 
162
- test('sub (never goes below 1)', () => {
163
- expect(NonZeroUint32.sub(a, b)).toBe(500_000);
164
-
165
- expect(NonZeroUint32.sub(b, a)).toBe(1); // clamped to 1
166
- });
167
-
168
162
  test('mul (with clamping to non-zero uint32 range)', () => {
169
163
  const result = NonZeroUint32.mul(
170
164
  asNonZeroUint32(100_000),
@@ -178,16 +172,6 @@ describe('NonZeroUint32 test', () => {
178
172
  );
179
173
  });
180
174
 
181
- test('div (floor division, never goes below 1)', () => {
182
- expect(NonZeroUint32.div(a, asNonZeroUint32(500_000))).toBe(2);
183
-
184
- expect(NonZeroUint32.div(asNonZeroUint32(7), asNonZeroUint32(3))).toBe(2);
185
-
186
- expect(
187
- NonZeroUint32.div(asNonZeroUint32(500_000), asNonZeroUint32(1_000_000)),
188
- ).toBe(1); // floor(500000/1000000) = 0, clamped to 1
189
- });
190
-
191
175
  test('pow (with clamping to non-zero uint32 range)', () => {
192
176
  const result = NonZeroUint32.pow(
193
177
  asNonZeroUint32(10_000),
@@ -1,4 +1,5 @@
1
1
  export * from './branded-types/index.mjs';
2
2
  export * from './enum/index.mjs';
3
+ export * from './num-arithmetic-types.mjs';
3
4
  export * from './num.mjs';
4
5
  export * from './refined-number-utils.mjs';
@@ -0,0 +1,269 @@
1
+ import {
2
+ type FiniteNumber,
3
+ type Int,
4
+ type NegativeFiniteNumber,
5
+ type NegativeInt,
6
+ type NegativeNumber,
7
+ type NegativeSafeInt,
8
+ type NonNegativeFiniteNumber,
9
+ type NonNegativeNumber,
10
+ type NonPositiveFiniteNumber,
11
+ type NonPositiveInt,
12
+ type NonPositiveNumber,
13
+ type NonPositiveSafeInt,
14
+ type NonZeroFiniteNumber,
15
+ type NonZeroInt,
16
+ type NonZeroNumber,
17
+ type NonZeroSafeInt,
18
+ type PositiveFiniteNumber,
19
+ type PositiveInt,
20
+ type PositiveNumber,
21
+ type PositiveSafeInt,
22
+ type SafeInt,
23
+ type SafeUint,
24
+ type Uint,
25
+ } from 'ts-type-forge';
26
+
27
+ /**
28
+ * Internal type-level arithmetic for {@link Num}'s `add`/`sub`/`mul`/`div`.
29
+ *
30
+ * The result type of a binary operation is computed from two independent axes of
31
+ * each operand:
32
+ *
33
+ * - **Sign** — `pos | neg | nonneg | nonpos | nonzero | any`, derived from the
34
+ * brand's sign keys and combined with a small boolean algebra over the facts
35
+ * `>= 0`, `<= 0`, `!= 0`.
36
+ * - **Level** — `safeint | int | finite | number`, derived from the brand's
37
+ * level keys.
38
+ *
39
+ * The sign is always computed exactly (so e.g. `neg * neg` becomes `pos`). The
40
+ * level widens on possible overflow (`safeint` results of `+`/`-`/`*` become
41
+ * `int`, since the magnitude may exceed the safe-integer range). No runtime
42
+ * clamping is performed — saturating "stay in the type" behavior is provided by
43
+ * the branded namespaces instead.
44
+ *
45
+ * @internal
46
+ */
47
+
48
+ /** Sign classification of a numeric brand. */
49
+ type Sign = 'any' | 'neg' | 'nonneg' | 'nonpos' | 'nonzero' | 'pos';
50
+
51
+ /** Level (integer/finite) classification of a numeric brand. */
52
+ type Level = 'finite' | 'int' | 'number' | 'safeint';
53
+
54
+ /** Classifies a numeric type's sign. */
55
+ // transformer-ignore-next-line convert-to-readonly
56
+ export type SignOf<N> = [N] extends [PositiveNumber]
57
+ ? 'pos'
58
+ : [N] extends [NegativeNumber]
59
+ ? 'neg'
60
+ : [N] extends [NonNegativeNumber]
61
+ ? 'nonneg'
62
+ : [N] extends [NonPositiveNumber]
63
+ ? 'nonpos'
64
+ : [N] extends [NonZeroNumber]
65
+ ? 'nonzero'
66
+ : 'any';
67
+
68
+ /** Classifies a numeric type's level. */
69
+ // transformer-ignore-next-line convert-to-readonly
70
+ export type LevelOf<N> = [N] extends [SafeInt]
71
+ ? 'safeint'
72
+ : [N] extends [Int]
73
+ ? 'int'
74
+ : [N] extends [FiniteNumber]
75
+ ? 'finite'
76
+ : 'number';
77
+
78
+ // --- boolean fact algebra over a sign ----------------------------------------
79
+
80
+ // transformer-ignore-next-line convert-to-readonly
81
+ type And<A extends boolean, B extends boolean> = [A, B] extends [true, true]
82
+ ? true
83
+ : false;
84
+
85
+ type Or<A extends boolean, B extends boolean> = true extends A | B
86
+ ? true
87
+ : false;
88
+
89
+ /** Whether the value is guaranteed `>= 0`. */
90
+ type Ge0<S extends Sign> = S extends 'pos' | 'nonneg' ? true : false;
91
+
92
+ /** Whether the value is guaranteed `<= 0`. */
93
+ type Le0<S extends Sign> = S extends 'neg' | 'nonpos' ? true : false;
94
+
95
+ /** Whether the value is guaranteed `!= 0`. */
96
+ type Ne0<S extends Sign> = S extends 'pos' | 'neg' | 'nonzero' ? true : false;
97
+
98
+ /** Rebuilds a {@link Sign} from the three facts. */
99
+ type FromFacts<
100
+ ge0 extends boolean,
101
+ le0 extends boolean,
102
+ ne0 extends boolean,
103
+ > = ge0 extends true
104
+ ? ne0 extends true
105
+ ? 'pos'
106
+ : 'nonneg'
107
+ : le0 extends true
108
+ ? ne0 extends true
109
+ ? 'neg'
110
+ : 'nonpos'
111
+ : ne0 extends true
112
+ ? 'nonzero'
113
+ : 'any';
114
+
115
+ type NegateSign<S extends Sign> = FromFacts<Le0<S>, Ge0<S>, Ne0<S>>;
116
+
117
+ // --- sign combination per operation ------------------------------------------
118
+
119
+ type AddSign<A extends Sign, B extends Sign> = FromFacts<
120
+ And<Ge0<A>, Ge0<B>>,
121
+ And<Le0<A>, Le0<B>>,
122
+ Or<
123
+ And<And<Ge0<A>, Ge0<B>>, Or<Ne0<A>, Ne0<B>>>,
124
+ And<And<Le0<A>, Le0<B>>, Or<Ne0<A>, Ne0<B>>>
125
+ >
126
+ >;
127
+
128
+ type SubSign<A extends Sign, B extends Sign> = AddSign<A, NegateSign<B>>;
129
+
130
+ /** Sign of a product/quotient, parameterized by the `!= 0` fact of the result. */
131
+ type MulSignWithNe0<
132
+ A extends Sign,
133
+ B extends Sign,
134
+ ne0 extends boolean,
135
+ > = FromFacts<
136
+ Or<And<Ge0<A>, Ge0<B>>, And<Le0<A>, Le0<B>>>,
137
+ Or<And<Ge0<A>, Le0<B>>, And<Le0<A>, Ge0<B>>>,
138
+ ne0
139
+ >;
140
+
141
+ type MulSign<A extends Sign, B extends Sign> = MulSignWithNe0<
142
+ A,
143
+ B,
144
+ And<Ne0<A>, Ne0<B>>
145
+ >;
146
+
147
+ /** Exact division: nonzero dividend over nonzero divisor stays nonzero. */
148
+ type DivSign<A extends Sign, B extends Sign> = MulSignWithNe0<A, B, Ne0<A>>;
149
+
150
+ /** Floor division: flooring can land on `0`, so the result is never `!= 0`. */
151
+ type DivIntSign<A extends Sign, B extends Sign> = MulSignWithNe0<A, B, false>;
152
+
153
+ // --- level combination per operation -----------------------------------------
154
+
155
+ /** Looser of two levels (number > finite > int > safeint). */
156
+ type LooserLevel<LA extends Level, LB extends Level> = 'number' extends LA | LB
157
+ ? 'number'
158
+ : 'finite' extends LA | LB
159
+ ? 'finite'
160
+ : 'int' extends LA | LB
161
+ ? 'int'
162
+ : 'safeint';
163
+
164
+ /** `+`/`-`/`*`: looser level, widening `safeint -> int` (overflow may occur). */
165
+ type AddMulLevel<LA extends Level, LB extends Level> =
166
+ LooserLevel<LA, LB> extends 'safeint' ? 'int' : LooserLevel<LA, LB>;
167
+
168
+ /** Exact division yields a (possibly fractional) finite number. */
169
+ type DivLevel<LA extends Level, LB extends Level> = 'number' extends LA | LB
170
+ ? 'number'
171
+ : 'finite';
172
+
173
+ /** Floor division yields an integer; the safe range is preserved. */
174
+ // transformer-ignore-next-line convert-to-readonly
175
+ type DivIntLevel<LA extends Level, LB extends Level> = 'number' extends LA | LB
176
+ ? 'number'
177
+ : [LA, LB] extends ['safeint', 'safeint']
178
+ ? 'safeint'
179
+ : 'int';
180
+
181
+ // --- reconstruction (sign, level) -> canonical brand -------------------------
182
+
183
+ type ReconstructNumber<S extends Sign> = S extends 'pos'
184
+ ? PositiveNumber
185
+ : S extends 'neg'
186
+ ? NegativeNumber
187
+ : S extends 'nonneg'
188
+ ? NonNegativeNumber
189
+ : S extends 'nonpos'
190
+ ? NonPositiveNumber
191
+ : S extends 'nonzero'
192
+ ? NonZeroNumber
193
+ : number;
194
+
195
+ type ReconstructFinite<S extends Sign> = S extends 'pos'
196
+ ? PositiveFiniteNumber
197
+ : S extends 'neg'
198
+ ? NegativeFiniteNumber
199
+ : S extends 'nonneg'
200
+ ? NonNegativeFiniteNumber
201
+ : S extends 'nonpos'
202
+ ? NonPositiveFiniteNumber
203
+ : S extends 'nonzero'
204
+ ? NonZeroFiniteNumber
205
+ : FiniteNumber;
206
+
207
+ type ReconstructInt<S extends Sign> = S extends 'pos'
208
+ ? PositiveInt
209
+ : S extends 'neg'
210
+ ? NegativeInt
211
+ : S extends 'nonneg'
212
+ ? Uint
213
+ : S extends 'nonpos'
214
+ ? NonPositiveInt
215
+ : S extends 'nonzero'
216
+ ? NonZeroInt
217
+ : Int;
218
+
219
+ type ReconstructSafeInt<S extends Sign> = S extends 'pos'
220
+ ? PositiveSafeInt
221
+ : S extends 'neg'
222
+ ? NegativeSafeInt
223
+ : S extends 'nonneg'
224
+ ? SafeUint
225
+ : S extends 'nonpos'
226
+ ? NonPositiveSafeInt
227
+ : S extends 'nonzero'
228
+ ? NonZeroSafeInt
229
+ : SafeInt;
230
+
231
+ type Reconstruct<S extends Sign, L extends Level> = L extends 'safeint'
232
+ ? ReconstructSafeInt<S>
233
+ : L extends 'int'
234
+ ? ReconstructInt<S>
235
+ : L extends 'finite'
236
+ ? ReconstructFinite<S>
237
+ : ReconstructNumber<S>;
238
+
239
+ // --- public result types -----------------------------------------------------
240
+
241
+ /** Result type of {@link Num.add} applied to operands of type `A` and `B`. */
242
+ export type AddResult<A, B> = Reconstruct<
243
+ AddSign<SignOf<A>, SignOf<B>>,
244
+ AddMulLevel<LevelOf<A>, LevelOf<B>>
245
+ >;
246
+
247
+ /** Result type of {@link Num.sub} applied to operands of type `A` and `B`. */
248
+ export type SubResult<A, B> = Reconstruct<
249
+ SubSign<SignOf<A>, SignOf<B>>,
250
+ AddMulLevel<LevelOf<A>, LevelOf<B>>
251
+ >;
252
+
253
+ /** Result type of {@link Num.mul} applied to operands of type `A` and `B`. */
254
+ export type MulResult<A, B> = Reconstruct<
255
+ MulSign<SignOf<A>, SignOf<B>>,
256
+ AddMulLevel<LevelOf<A>, LevelOf<B>>
257
+ >;
258
+
259
+ /** Result type of {@link Num.div} (exact) applied to operands of type `A` and `B`. */
260
+ export type DivResult<A, B> = Reconstruct<
261
+ DivSign<SignOf<A>, SignOf<B>>,
262
+ DivLevel<LevelOf<A>, LevelOf<B>>
263
+ >;
264
+
265
+ /** Result type of {@link Num.divInt} (floor) applied to operands of type `A` and `B`. */
266
+ export type DivIntResult<A, B> = Reconstruct<
267
+ DivIntSign<SignOf<A>, SignOf<B>>,
268
+ DivIntLevel<LevelOf<A>, LevelOf<B>>
269
+ >;