@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.
- package/CHANGELOG.md +78 -0
- package/DOCS/FILE_FORMATS.md +51 -16
- package/Examples/arithmetics.k +222 -110
- package/Examples/karatsuba-mult.k +215 -0
- package/Examples/poly.k +14 -5
- package/backends/arm64/scripts/run-tests.mjs +16 -0
- package/backends/llvm/bin/k-llvm-build.mjs +40 -14
- package/backends/llvm/bin/k-llvm-compile.mjs +29 -12
- package/backends/llvm/runtime/krt.c +10 -10
- package/backends/llvm/runtime/krt.h +6 -1
- package/backends/llvm/scripts/run-tests.mjs +16 -0
- package/backends/llvm/src/cli.mjs +1 -1
- package/backends/llvm/src/executable.mjs +36 -0
- package/backends/llvm/src/kvm2llvm.mjs +10 -4
- package/backends/llvm/tests/integration.sh +11 -0
- package/backends/llvm/tests/test-llvm.mjs +15 -1
- package/backends/wasm/scripts/run-tests.mjs +16 -0
- package/browser/shims/fs.mjs +12 -7
- package/browser/vfs-data.mjs +7 -10
- package/core.k +25 -18
- package/harness/suites/poly.mjs +1 -1
- package/package.json +2 -1
- package/repl-codecs.mjs +6 -3
- package/scripts/benchmark-idempotency.mjs +27 -0
- package/scripts/benchmark.mjs +51 -0
- package/scripts/build-repl-html.mjs +28 -10
- package/scripts/generate-dependency-graph.mjs +25 -1
- package/scripts/perf-ieee.mjs +75 -0
- package/scripts/perf-int.mjs +67 -0
- package/scripts/perf-multiplication.mjs +644 -0
- package/scripts/perf-poly.mjs +68 -1
- package/scripts/render-benchmark.mjs +25 -0
- package/scripts/run-tests.mjs +19 -0
- package/tests/test-arithmetics.mjs +96 -0
- package/tests/test-karatsuba.mjs +133 -0
- 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
|
package/DOCS/FILE_FORMATS.md
CHANGED
|
@@ -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 |
|
|
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
|
-
####
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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 | \
|
package/Examples/arithmetics.k
CHANGED
|
@@ -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 =
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
{
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
306
|
-
|
|
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
|
|
385
|
+
{ .x/'-' if, .y swap_sign get_sign then } .then
|
|
318
386
|
> sign,
|
|
319
|
-
|
|
387
|
+
.x abs remove_leading_zeros x_norm,
|
|
388
|
+
.y abs remove_leading_zeros y_norm
|
|
320
389
|
}
|
|
321
|
-
<
|
|
322
|
-
|
|
323
|
-
{
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
.
|
|
332
|
-
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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,
|
|
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_;
|