@fraczak/k 6.10.3 → 6.10.5

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 (36) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/DOCS/FILE_FORMATS.md +51 -16
  3. package/Examples/arithmetics.k +222 -110
  4. package/Examples/karatsuba-mult.k +215 -0
  5. package/Examples/poly.k +14 -5
  6. package/backends/arm64/scripts/run-tests.mjs +16 -0
  7. package/backends/llvm/bin/k-llvm-build.mjs +40 -14
  8. package/backends/llvm/bin/k-llvm-compile.mjs +29 -12
  9. package/backends/llvm/runtime/krt.c +10 -10
  10. package/backends/llvm/runtime/krt.h +6 -1
  11. package/backends/llvm/scripts/run-tests.mjs +16 -0
  12. package/backends/llvm/src/cli.mjs +1 -1
  13. package/backends/llvm/src/executable.mjs +36 -0
  14. package/backends/llvm/src/kvm2llvm.mjs +10 -4
  15. package/backends/llvm/tests/integration.sh +11 -0
  16. package/backends/llvm/tests/test-llvm.mjs +15 -1
  17. package/backends/wasm/scripts/run-tests.mjs +16 -0
  18. package/browser/shims/fs.mjs +12 -7
  19. package/browser/vfs-data.mjs +7 -10
  20. package/core.k +25 -18
  21. package/harness/suites/poly.mjs +1 -1
  22. package/package.json +2 -1
  23. package/repl-codecs.mjs +6 -3
  24. package/scripts/benchmark-idempotency.mjs +27 -0
  25. package/scripts/benchmark.mjs +51 -0
  26. package/scripts/build-repl-html.mjs +28 -10
  27. package/scripts/generate-dependency-graph.mjs +25 -1
  28. package/scripts/perf-ieee.mjs +75 -0
  29. package/scripts/perf-int.mjs +67 -0
  30. package/scripts/perf-multiplication.mjs +644 -0
  31. package/scripts/perf-poly.mjs +68 -1
  32. package/scripts/render-benchmark.mjs +25 -0
  33. package/scripts/run-tests.mjs +19 -0
  34. package/tests/test-arithmetics.mjs +96 -0
  35. package/tests/test-karatsuba.mjs +133 -0
  36. package/tests/test-repl-html.mjs +23 -6
package/CHANGELOG.md CHANGED
@@ -5,6 +5,84 @@ All notable changes to this project will be documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
6
  This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [6.10.5] — 2026-10-08 — Adaptive Division and VFS Codecs
9
+
10
+ ### Arithmetic & Algorithms
11
+
12
+ - **Adaptive Hybrid Division (`Examples/arithmetics.k`)**:
13
+ - Implemented hybrid integer division that dynamically chooses between
14
+ down-shift division ($|x| < 2|y|$) and restoring long division
15
+ ($|x| \ge 2|y|$).
16
+ - Down-shift division scales divisor $y$ up to match length of $x$,
17
+ performing trial subtraction and down-shifting in $O(1)$ per step.
18
+ - Crossover condition is evaluated structurally in $O(\min(|y|, |x|/2))$
19
+ without prior length computations.
20
+ - Added native number-theory operations: `mod`, Euclidean `gcd`, and
21
+ `lcm`.
22
+ - Refactored `arithmetics.k` to focus purely on arithmetic operations.
23
+
24
+ ### Polymorphic Lists
25
+
26
+ - **Polymorphic List Library (`Examples/poly.k`)**:
27
+ - Encapsulated list constructors and destructors (`list?`, `nil`,
28
+ `cons`, `car`, `cdr`, `singleton`, `nil?`) directly in `poly.k`.
29
+ - Updated benchmark suites (`harness/suites/poly.mjs`,
30
+ `scripts/perf-poly.mjs`) to import only arithmetic symbols from
31
+ `arithmetics.k`.
32
+
33
+ ### Web REPL & Codecs
34
+
35
+ - **VFS Codec Subfolder Organization**:
36
+ - Reorganized all codec files in the Virtual File System under the
37
+ `codecs/` subfolder (`codecs/int.mjs`, `codecs/json.mjs`, etc.).
38
+ - Added backward-compatible resolution in `repl-codecs.mjs`.
39
+ - Added `karatsuba-mult.k` to the VFS and example picker in `repl.html`.
40
+
41
+ ---
42
+
43
+ ## [6.10.4] — 2026-10-06 — Karatsuba Multiplication, Multi-Backend Harness, and CLI Help
44
+
45
+ ### Arithmetic & Algorithms
46
+
47
+ - **Karatsuba Multiplication (`Examples/karatsuba-mult.k`)**:
48
+ - Implemented standalone, minimal Karatsuba integer multiplication with
49
+ asymptotic time complexity $O(n^{\log_2 3}) \approx O(n^{1.585})$.
50
+ - Utilizes unary counter (`$unat`) splitting with a 512-bit threshold
51
+ switching to grade-school multiplication (`times_bits`) for small
52
+ inputs.
53
+ - Implements direct bit-level arithmetic (`plus_bits`, `minus_bits`,
54
+ `times_bits`) and direct $2m$ bit shifting (`shift2_bits_u`).
55
+ - Added dedicated test suite in `tests/test-karatsuba.mjs` verifying
56
+ squaring cases and mixed-sign pair multiplications against BigInt
57
+ oracle.
58
+
59
+ ### Performance & Benchmarking
60
+
61
+ - **Multiplication Performance Harness**
62
+ (`scripts/perf-multiplication.mjs`):
63
+ - Added multi-backend performance runner comparing `times` and
64
+ `karatsuba` across LLVM, Wasm, kVM, and JS engines.
65
+ - Supports `--mode squaring` (doubling bit width per step) and
66
+ `--mode sizes` (random operands of fixed bit lengths).
67
+ - Includes per-step timeout protection and process RSS memory guards.
68
+ - Added `npm run perf:mult` script in `package.json`.
69
+
70
+ ### CLI Usability & Backend Enhancements
71
+
72
+ - **Unified `-h` and `--help` CLI Documentation**:
73
+ - Added comprehensive option and environment variable documentation
74
+ across all benchmark, performance, and test scripts in `scripts/`
75
+ and `backends/*/scripts/`.
76
+ - **LLVM Direct `.kvm` Bytecode Compilation**:
77
+ - Added `compileKVMToExecutable` in `backends/llvm/src/executable.mjs`,
78
+ enabling direct binary compilation from lowered `.kvm` IR.
79
+ - **Runtime Mark Compaction Fixes**:
80
+ - Updated LLVM runtime compaction tracking (`%k_rt_tail_mark`) to
81
+ accurately preserve base and last compaction arena marks in
82
+ tail loops.
83
+
84
+ ---
85
+
8
86
  ## [6.10.3] — 2026-10-04 — Unified File Formats Specification and Documentation Modernization
9
87
 
10
88
  ### Documentation & Core Architecture
@@ -14,7 +14,7 @@ type-checked artifacts and JIT/native execution.
14
14
  | **`.k`** | Human-readable source code | UTF-8 plain text | Optional (trailing expression) | Editor, `k-decompile` | `k.mjs`, `k-repl`, `k-compile`, backends |
15
15
  | **`.ko`** | Executable compiled object | Binary (`KOBJ\n` + JSON payload) | **Required** (`"__main__"`) | `k-compile`, REPL (`:ko`) | `k.mjs`, `k-wasm-compile`, `k-llvm-build`, inspect tools |
16
16
  | **`.klib`**| Reusable compiled library | UTF-8 JSON payload | **None** (`null`) | `k-compile --lib`, REPL (`:klib`) | REPL (`:load`), CLI linkers (`--lib`) |
17
- | **`.kvm`** | Polymorphic kVM bytecode template | JSON / CBOR (`format: "k-vm", layer: "KVM-P"`) | Per-relation functions | `k-compile --format kvm`, kvm.mjs | kVM runtime, JIT specializer, `k-wasm-compile` |
17
+ | **`.kvm`** | Polymorphic kVM bytecode template | JSON / CBOR (`format: "k-vm", layer: "KVM-P"`) | Per-relation functions | `k-compile --format kvm`, kvm.mjs | `kvm.mjs`, `k-wasm-compile`, `k-llvm-build` |
18
18
 
19
19
  ### Format Transformation Workflow
20
20
 
@@ -63,6 +63,7 @@ flowchart TD
63
63
 
64
64
  %% Lowering from .kvm
65
65
  KVM -->|"k-wasm-compile"| WASM
66
+ KVM -->|"k-llvm-build"| NATIVE
66
67
  KVM -->|"kvm.mjs / specializeKVM"| INTERP
67
68
  ```
68
69
 
@@ -276,21 +277,38 @@ Export all currently loaded relations and types into a reusable library:
276
277
  `.kvm` is a polymorphic register-IR template artifact
277
278
  (`format: "k-vm"`, `layer: "KVM-P"`, `isPolymorphic: true`).
278
279
 
279
- #### How `.kvm` Differs from `.ko` and `.klib`:
280
- - **Abstraction Level:** `.ko` and `.klib` store high-level relational
281
- expressions (KIR) and structural type graphs. In contrast, `.kvm` is
282
- lowered into explicit register-based virtual machine instructions
283
- (`alloc`, `make_variant`, `project_variant`, `union`, `call`, and
284
- tail loops).
285
- - **Polymorphic Template vs. Fixed Object:**
286
- - `.ko` is a concrete executable bound to an entrypoint (`main`).
287
- - `.klib` is a library of high-level relation definitions for linking.
288
- - `.kvm` stores compiled bytecode functions paired with principal
289
- pattern graphs ($P_{in}, P_{out}$).
290
- - **Instant Specialization:** Instead of running the compiler's full
291
- constraint-derivation engine, the runtime specializer (`specializeKVM`)
292
- prunes dead union branches and folds redundant guards on-the-fly
293
- against concrete input envelopes $(v, P_v)$.
280
+ #### Why `.kvm` Exists (What You Gain):
281
+
282
+ 1. **Relational AST vs. Linear Register Machine:**
283
+ `.ko` and `.klib` store high-level relational combinators (compositions
284
+ `(f g)`, products `{a x, b y}`, unions `<f, g>`) and structural type
285
+ graphs. They contain no virtual registers, instruction sequences, or
286
+ control-flow branches. `.kvm` transforms this functional tree into a
287
+ linear 3-address virtual register machine IR (`alloc`, `project_field`,
288
+ `make_variant`, `project_variant`, `jump_if_fail`, `call`, and loops).
289
+
290
+ 2. **$1 \times N$ Backend Decoupling:**
291
+ Rather than having every code generator (LLVM, WebAssembly, ARM64,
292
+ and the JIT interpreter) independently lower the relational tree and
293
+ re-implement type-guided control flow, relational-to-imperative
294
+ lowering occurs once in `kvm.mjs`. All backends consume the exact same
295
+ ~10 linear kVM instructions (`kvm2llvm.mjs`, `kvm2wasm.mjs`,
296
+ `kvm2arm64.mjs`, `kvm.mjs`).
297
+
298
+ 3. **Microsecond Polymorphic Specialization (`layer: "KVM-P"`):**
299
+ In polymorphic programs, specializing `.ko` requires re-running the
300
+ compiler's full structural type derivation and constraint convergence
301
+ engine. A `.kvm` template stores pre-compiled register bytecode with
302
+ principal pattern graphs. The runtime specializer (`specializeKVM`)
303
+ prunes dead branches and folds redundant guards against concrete
304
+ runtime input envelopes $(v, P_v)$ in microseconds with zero AST
305
+ recompilation overhead.
306
+
307
+ 4. **Inspectable & Cacheable Register IR:**
308
+ `.kvm` produces a human- and machine-readable JSON or CBOR format.
309
+ You can inspect virtual register assignments, verify dead-branch
310
+ elimination, diff compiler output across runs, or cache the lowered IR
311
+ to bypass frontend compilation when building native or WASM targets.
294
312
 
295
313
  ### How to Generate
296
314
 
@@ -309,7 +327,24 @@ k-compile add5.ko add5.kvm
309
327
 
310
328
  ### How to Consume
311
329
 
330
+ - **Direct lowering from `.kvm` to Native Binaries (`k-llvm-build`):**
331
+ Compile a `.kvm` template directly into a native executable:
332
+ ```bash
333
+ k-llvm-build add5.kvm add5-bin
334
+ echo "10" | ./codecs/int.mjs --parse | \
335
+ ./add5-bin | \
336
+ ./codecs/int.mjs --print
337
+ # Output: 15
338
+ ```
339
+
340
+ - **Direct lowering from `.kvm` to LLVM IR (`k-llvm-compile`):**
341
+ Emit LLVM assembly directly from the register IR:
342
+ ```bash
343
+ k-llvm-compile add5.kvm add5.ll
344
+ ```
345
+
312
346
  - **Direct lowering from `.kvm` to WebAssembly (`k-wasm-compile`):**
347
+ Compile a `.kvm` template directly into a standalone `.wasm` module:
313
348
  ```bash
314
349
  k-wasm-compile add5.kvm add5.wasm
315
350
  echo "10" | ./codecs/int.mjs --parse | \
@@ -4,14 +4,6 @@ swap_yes_no = < /yes |no, /no |yes > $yes_no;
4
4
  yes = {}|yes $yes_no;
5
5
  no = yes swap_yes_no;
6
6
 
7
- list? = ?< {} nil, {X car, Y cdr} cons > = Y;
8
- nil = {}|nil list?;
9
- singleton = {() car, nil cdr} |cons list?;
10
- cons = {.car car, .cdr cdr} |cons list?;
11
- nil? = list? / nil nil;
12
- car = list? /cons .car;
13
- cdr = list? /cons .cdr;
14
-
15
7
  $ bits = < {} _, bits 0, bits 1 >;
16
8
 
17
9
  0b = {} | _;
@@ -70,29 +62,10 @@ inOrder_ = ${bits x, bits y, yes_no inOrder}
70
62
 
71
63
  GT = {inOrder /no if, () then} . then;
72
64
 
73
- EQ = GT {.x y, .y x} GT;
74
-
75
- toBitList = {inv x, nil r} _toBitList;
76
- _toBitList = <
77
- {.x empty? if, .r then}.then,
78
- <
79
- {.x /0 x, {0 car, .r cdr} cons r},
80
- {.x /1 x, {1 car, .r cdr} cons r}
81
- > _toBitList
82
- >;
83
- fromBitList = {() l, 0b r} _fromBitList inv $bits;
84
- _fromBitList =
85
- <
86
- {.l nil? if, .r then} .then,
87
- <
88
- {.l car /0 if, {.l cdr l, .r |0 r} then} .then,
89
- {.l car /1 if, {.l cdr l, .r |1 r} then} .then
90
- > _fromBitList
91
- >;
65
+ EQ = {inOrder /yes if, {.x y, .y x} inOrder /yes and, () then} .then;
92
66
 
93
67
  --------------------------------------
94
68
  $ int = < bits '+', bits '-' >;
95
- $ ints = <{int car, ints cdr} cons, {} nil>;
96
69
 
97
70
  remove_leading_zeros = <
98
71
  /0 remove_leading_zeros,
@@ -102,7 +75,7 @@ nat = remove_leading_zeros <empty? 0, ()>;
102
75
  int = nat | '+' $int;
103
76
  abs = $int </'+',/'-'>;
104
77
 
105
- pos? = $int {dec /'+' if, () then} .then;
78
+ pos? = $int {/'+' remove_leading_zeros /1 if, () then} .then;
106
79
  neg? = $int {/'-' if, () then} .then;
107
80
  zero_int? = $int {/'+' /0 empty? if, () then} .then;
108
81
  minus_one? = $int {/'-' /1 empty? if, () then} .then;
@@ -125,12 +98,12 @@ dec = $int <
125
98
  /'-' {inv x, 0b y} _inc nat |'-'
126
99
  > $int;
127
100
 
128
- _dec = ?{X x, X y, ...} <
101
+ _dec = ${bits x, bits y} <
129
102
  {.x /1 x, .y |0 y} concat_,
130
103
  {.x /0 x, .y |1 y} _dec
131
- > ?X;
104
+ >;
132
105
 
133
- $carry = <{} 0, {} 1>;
106
+ $ carry = <{} 0, {} 1>;
134
107
  c0 = {}|0 $carry;
135
108
  c1 = {}|1 $carry;
136
109
 
@@ -218,7 +191,7 @@ times = ${int x, int y}
218
191
  sign_minus
219
192
  > sign,
220
193
  {
221
- 0 result,
194
+ 0b result,
222
195
  .x abs inv _x,
223
196
  .y abs y
224
197
  } _times result
@@ -249,105 +222,244 @@ _times =
249
222
  ;
250
223
 
251
224
 
252
- mod =
253
- ${bits x, bits y}
254
- -- _log!
255
- <
256
- -- if y > x then (x mod y) = x
257
- {.y x, .x y} GT .y,
258
- -- else, i.e.
259
- -- if x >= y then (x mod y) = ((x-y mod 2y) mod y)
260
- {
261
- {
262
- {.x int x, .y int y} minus /"+" x,
263
- .y inv |0 inv y
264
- } mod x,
265
- .y y
266
- } mod
267
- >
268
- ;
269
225
 
270
226
 
271
- // gratest common divisor
272
- gcd = ${bits x, bits y}
273
- <
274
- {.y x, .x y} GT,
275
- ()
276
- > _gcd
277
- ;
278
227
 
279
- _gcd = <
280
- { .y /0 if, .x then } .then,
281
- { .y x, mod y } _gcd
282
- >;
228
+ // ----------------------------------------------------------------------
229
+ // Integer Division: Adaptive Hybrid Algorithm
230
+ //
231
+ // Combines two complementary strategies based on the bit-length
232
+ // comparison between dividend x and divisor y (|x| vs |y|):
233
+ //
234
+ // 1. Down-Shift Division (|x| < 2|y|, y is large relative to x):
235
+ // - Scales y up to y_max = y * 2^k to match length(x) via 'y inv'
236
+ // and prepending k zeros (|0).
237
+ // - In each step: trial subtraction x = x - y_scaled, then down-shifts
238
+ // y_scaled by 1 bit in O(1) by popping the outer zero (/_0).
239
+ // - Runs in exactly (|x| - |y| + 1) iterations. When y ~= x, this
240
+ // terminates in 1-2 steps rather than looping through all bits of x.
241
+ //
242
+ // 2. Restoring Division (|x| >= 2|y|, y is small relative to x):
243
+ // - Consumes bits of x from MSB to LSB into remainder accumulator r.
244
+ // - Remainder r stays small (r <= 2y), so trial subtractions operate
245
+ // on tiny numbers of length |y| rather than full-length |x|.
246
+ //
247
+ // The crossover condition |x| < 2|y| is evaluated structurally in
248
+ // O(min(|y|, |x|/2)) by consuming 2 bits of x for every 1 bit of y.
249
+ // ----------------------------------------------------------------------
250
+
251
+ // Helpers for Restoring Division (shift-and-subtract)
252
+ shift_in_0 = inv |0 inv nat;
253
+ shift_in_1 = inv |1 inv nat;
254
+ sub_bits = ${bits x, bits y} {.x inv x, .y inv y, c0 c} _minus inv nat;
255
+
256
+ _div_restoring =
257
+ ${bits x, bits y, bits r, bits q}
258
+ <
259
+ {.x empty? if, {.q inv int div, .r rem} then} .then,
260
+ <
261
+ {.x /0 x, .r shift_in_0 r, .y y, .q q},
262
+ {.x /1 x, .r shift_in_1 r, .y y, .q q}
263
+ >
264
+ {
265
+ .x x,
266
+ .y y,
267
+ .q q,
268
+ .r r,
269
+ {.y x, .r y} inOrder test
270
+ }
271
+ <
272
+ {.test /yes if, {.x x, .y y, .q |1 q, {.r x, .y y} sub_bits r} then} .then,
273
+ {.test /no if, {.x x, .y y, .q |0 q, .r r} then} .then
274
+ >
275
+ _div_restoring
276
+ >;
283
277
 
278
+ // Helpers for Down-Shift Division (align and shift divisor down)
279
+ // Phase 1: pad _y with |0 to match length of x, counting shift steps
280
+ _pad_down = ${bits x, bits _y, bits steps}
281
+ <
282
+ {.x empty? if, {._y _y, .steps steps} then} .then,
283
+ {
284
+ .x </0, /1> x,
285
+ ._y |0 _y,
286
+ .steps |0 steps
287
+ } _pad_down
288
+ >;
284
289
 
290
+ _align_down = ${bits x, bits y, bits _y}
291
+ <
292
+ // Both x and y have bits remaining: advance both
293
+ {
294
+ .x </0, /1> x,
295
+ .y </0, /1> y,
296
+ ._y _y
297
+ } _align_down,
285
298
 
286
- // div
299
+ // y is empty while x remains: pad _y with shift zeros
300
+ {
301
+ .y empty? if,
302
+ {.x x, ._y _y, 0b steps} _pad_down then
303
+ } .then,
287
304
 
288
- log2_floor =
289
- ${bits x, bits step}
290
- -- finds the largest k, such that 'step * 2^k <= x',
291
- -- returns k1_, i.e., 0 if x < step, and k1_ otherwise.
292
- {.x x, .step step, 0 log} log2_floor_;
305
+ // Both x and y are empty: lengths equal (0 shift steps)
306
+ {
307
+ .x empty? if,
308
+ .y empty? and,
309
+ {._y _y, 0b steps} then
310
+ } .then
311
+ >;
293
312
 
294
- log2_floor_ = <
295
- {{.step x,.x y} GT if, .log then} .then,
296
- {.x x, {.step x,0 y} GT {.x x,2 y} {.x int x, .y int y} times abs step, .log int inc abs log} log2_floor_
297
- >;
313
+ // Phase 2: down-shift trial subtraction
314
+ _div_down = ${bits x, bits _y, bits steps, bits q}
315
+ {
316
+ .x x,
317
+ ._y _y,
318
+ .steps steps,
319
+ .q q,
320
+ ._y inv nat y_scaled
321
+ }
322
+ {
323
+ .x x,
324
+ ._y _y,
325
+ .steps steps,
326
+ .q q,
327
+ .y_scaled y_scaled,
328
+ {.y_scaled x, .x y} inOrder test
329
+ }
330
+ <
331
+ // If x >= y_scaled, subtract and emit quotient bit 1
332
+ {
333
+ .test /yes if,
334
+ {
335
+ {.x int x, .y_scaled int y} minus /+ x,
336
+ .q |1 q,
337
+ ._y _y,
338
+ .steps steps
339
+ } then
340
+ } .then,
341
+ // If x < y_scaled, keep x and emit quotient bit 0
342
+ {
343
+ .test /no if,
344
+ {.x x, .q |0 q, ._y _y, .steps steps} then
345
+ } .then
346
+ >
347
+ <
348
+ // If no more shift steps remaining, division is complete
349
+ {
350
+ .steps empty? if,
351
+ {.q inv int div, .x rem} then
352
+ } .then,
353
+ // Otherwise down-shift: remove the last 0 from _y and steps
354
+ {
355
+ .x x,
356
+ ._y /0 _y,
357
+ .steps /0 steps,
358
+ .q q
359
+ } _div_down
360
+ >;
298
361
 
362
+ $ choose_div = < {} down_shift, {} restore >;
299
363
 
300
- x_times_2_to_k =
301
- ${bits x, bits k}
302
- {.x inv _x, .k int k} _x_times_2_to_the_power_of_k inv
303
- ;
364
+ // Evaluates whether |x| < 2|y| by advancing 2 bits of x per 1 bit of y
365
+ _check_2y = ${bits x, bits y}
366
+ <
367
+ // x is empty -> |x| < 2|y| (choose down_shift)
368
+ {.x empty? if, {} | down_shift $choose_div then} .then,
369
+ // y is empty while x remains -> |x| >= 2|y| (choose restore)
370
+ {.y empty? if, {} | restore $choose_div then} .then,
371
+ // x has only 1 bit left while y remains -> |x| < 2|y|
372
+ {.x </0, /1> empty? if, {} | down_shift $choose_div then} .then,
373
+ // Advance 2 bits from x and 1 bit from y
374
+ {
375
+ .x </0, /1> </0, /1> x,
376
+ .y </0, /1> y
377
+ } _check_2y
378
+ >;
304
379
 
305
- _x_times_2_to_the_power_of_k = <
306
- {.k zero_int? if, ._x then} .then,
307
- {._x |0 _x, .k dec k} _x_times_2_to_the_power_of_k
308
- >
309
- ;
310
-
311
- div =
312
- ${int x, int y}
313
- // remember sign
380
+ // Public division interface
381
+ div = ${int x, int y}
314
382
  {
315
383
  <
316
384
  { .x/'+' if, .y get_sign then } .then,
317
- { .x/'-' if, .y swap_sign get_sign then } .then
385
+ { .x/'-' if, .y swap_sign get_sign then } .then
318
386
  > sign,
319
- {.x abs x, .y abs step, 0 int res} _div res_wo_sign
387
+ .x abs remove_leading_zeros x_norm,
388
+ .y abs remove_leading_zeros y_norm
320
389
  }
321
- <
322
- {.sign/'+' if, {.res_wo_sign.div div, .res_wo_sign.rem rem} then} .then,
323
- { .res_wo_sign.div swap_sign div, .res_wo_sign.rem rem }
324
- >
325
- ;
326
-
327
- _div =
328
- {
329
- .x x,
330
- .step step,
331
- .res res,
332
- {.x x, .step step} log2_floor log
333
- }
334
- <
335
- { .log int zero_int? if, {.res div, .x rem} then } .then,
336
- {
337
- {1 x, .log int dec abs k} x_times_2_to_k factor,
338
- () i
339
- }
390
+ <
391
+ // Case 1: x == 0 -> quotient 0, remainder 0
392
+ {
393
+ .x_norm empty? if,
394
+ { 0 int div, 0 rem } then
395
+ } .then,
396
+ // Case 2: |x| < |y| -> x < y, quotient 0, remainder x
397
+ {
398
+ {.y_norm x, .x_norm y} GT if,
399
+ { 0 int div, .x_norm rem } then
400
+ } .then,
401
+ // Case 3: general division via adaptive hybrid choice
340
402
  {
341
- {.i.x int x, {.i.step int x, .factor int y} times y} minus abs x,
342
- .i.step step,
343
- {.i.res x, .factor int y} plus res
344
- } _div
403
+ .sign sign,
404
+ {
405
+ .x_norm x,
406
+ .y_norm y,
407
+ {.x_norm x, .y_norm y} _check_2y choice
408
+ }
409
+ <
410
+ // If |x| < 2|y|, use down-shift division
411
+ {
412
+ .choice /down_shift if,
413
+ {
414
+ .x x,
415
+ {.x x, .y y, .y inv _y} _align_down aligned
416
+ }
417
+ {
418
+ .x x,
419
+ .aligned._y _y,
420
+ .aligned.steps steps,
421
+ 0b q
422
+ } _div_down then
423
+ } .then,
424
+ // If |x| >= 2|y|, use restoring long division
425
+ {
426
+ .choice /restore if,
427
+ {.x x, .y y, 0 r, 0b q} _div_restoring then
428
+ } .then
429
+ > res
430
+ }
431
+ <
432
+ {.sign/'+' if, {.res.div div, .res.rem rem} then} .then,
433
+ { .res.div swap_sign div, .res.rem rem }
434
+ >
345
435
  >;
346
436
 
347
437
 
438
+ // ----------------------------------------------------------------------
439
+ // Number Theory: Modulo, Greatest Common Divisor, Least Common Multiple
440
+ // ----------------------------------------------------------------------
441
+
442
+ // Modulo: remainder of x divided by y in $bits, delegated to div
443
+ mod = ${bits x, bits y}
444
+ {{.x int x, .y int y} div .rem rem} .rem;
445
+
446
+ // Greatest common divisor (Euclidean algorithm)
447
+ gcd = ${bits x, bits y}
448
+ <
449
+ {.y x, .x y} GT,
450
+ ()
451
+ > _gcd
452
+ ;
453
+
454
+ _gcd = <
455
+ { .y remove_leading_zeros empty? if, .x nat then } .then,
456
+ { .y x, mod y } _gcd
457
+ >;
458
+
459
+ // Least common multiple: (x / gcd(x, y)) * y
348
460
  lcm =
349
461
  ${bits x, bits y}
350
- {{.x int x, .y int y} times x, gcd int y} div .div abs
462
+ {{.x int x, gcd int y} div .div x, .y int y} times abs
351
463
  ;
352
464
 
353
465
  factorial = {()n, 1 int acc} factorial_;