aboutsummaryrefslogtreecommitdiffstats
path: root/research/experiments/045-proof-compiler
diff options
context:
space:
mode:
Diffstat (limited to 'research/experiments/045-proof-compiler')
-rw-r--r--research/experiments/045-proof-compiler/2026-10-19-app.jsonl18
-rw-r--r--research/experiments/045-proof-compiler/2026-10-19-where-v0.txt75
-rw-r--r--research/experiments/045-proof-compiler/app-way.h193
-rw-r--r--research/experiments/045-proof-compiler/app.c417
4 files changed, 703 insertions, 0 deletions
diff --git a/research/experiments/045-proof-compiler/2026-10-19-app.jsonl b/research/experiments/045-proof-compiler/2026-10-19-app.jsonl
new file mode 100644
index 0000000..c8886cc
--- /dev/null
+++ b/research/experiments/045-proof-compiler/2026-10-19-app.jsonl
@@ -0,0 +1,18 @@
+{"what": "the executor and the stitched build on the app (cost.c, 2026-10-19-break.jsonl), and the empty handler (v0: the event itself, the same in both)", "executor": {"instructions_per_point": 40189.2, "cycles_per_point": 15416}, "stitched": {"instructions_per_point": 28088.7, "cycles_per_point": 11704}, "event_v0": {"instructions_per_point": 1565.6, "cycles_per_point": 625}, "event_v0_by_file": {"module-list.c": 674, "libc (malloc, free)": 389, "module.c": 386, "store-tree.c": 113}}
+{"rung": "exact", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 8784.6, "cycles_per_point": 3996.8, "answers": "the test's, and exact's", "run": 1}
+{"rung": "integer", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 7979.0, "cycles_per_point": 3539.1, "answers": "the test's, and exact's", "run": 1}
+{"rung": "stored_flat", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 6560.8, "cycles_per_point": 2800.3, "answers": "the test's, and exact's", "run": 1}
+{"rung": "local_flat", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 696.4, "cycles_per_point": 364.4, "answers": "the test's, and exact's", "run": 1}
+{"rung": "shared", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 564.3, "cycles_per_point": 319.4, "answers": "the test's, and exact's", "run": 1}
+{"rung": "unbounded", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 501.9, "cycles_per_point": 300.3, "answers": "the test's, and exact's", "run": 1}
+{"rung": "plain", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 361.7, "cycles_per_point": 260.1, "answers": "the test's, and exact's", "run": 1}
+{"rung": "vega", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 224.7, "cycles_per_point": 87.7, "answers": "the test's, and exact's", "run": 1}
+{"rung": "exact", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 8784.6, "cycles_per_point": 4007.0, "answers": "the test's, and exact's", "run": 2}
+{"rung": "integer", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 7979.0, "cycles_per_point": 3554.2, "answers": "the test's, and exact's", "run": 2}
+{"rung": "stored_flat", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 6560.8, "cycles_per_point": 2799.8, "answers": "the test's, and exact's", "run": 2}
+{"rung": "local_flat", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 696.4, "cycles_per_point": 362.9, "answers": "the test's, and exact's", "run": 2}
+{"rung": "shared", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 564.3, "cycles_per_point": 314.7, "answers": "the test's, and exact's", "run": 2}
+{"rung": "unbounded", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 501.9, "cycles_per_point": 295.5, "answers": "the test's, and exact's", "run": 2}
+{"rung": "plain", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 361.7, "cycles_per_point": 255.1, "answers": "the test's, and exact's", "run": 2}
+{"rung": "vega", "lines": 18672, "code_points": 61285, "rounds": 5, "instructions_per_point": 224.7, "cycles_per_point": 87.2, "answers": "the test's, and exact's", "run": 2}
+{"what": "built with -DBREAK_ONE_RULE (LB18 wrong): every hand rung refused, 6,233 of 18,672 lines wrong each"}
diff --git a/research/experiments/045-proof-compiler/2026-10-19-where-v0.txt b/research/experiments/045-proof-compiler/2026-10-19-where-v0.txt
new file mode 100644
index 0000000..47850cb
--- /dev/null
+++ b/research/experiments/045-proof-compiler/2026-10-19-where-v0.txt
@@ -0,0 +1,75 @@
+1,577 machine instructions a code point, 48,284 samples
+
+by mechanism:
+ 660 41.9% unknown
+ 375 23.8% list build
+ 249 15.8% memory
+ 178 11.3% event
+ 45 2.9% dispatch
+ 44 2.8% compute
+ 18 1.2% list read
+ 4 0.2% moves
+ 2 0.1% loop
+
+in step(), by the case its line is in (code the compiler shares between cases goes to one of them):
+ 47 LIST_APPEND
+ 45 (fetch, decode)
+ 39 TEXT_INSERT
+ 13 BYTES_OF
+ 5 TEXT_REMOVE
+ 4 CONST
+ 1 FLOORDIV
+
+outside step(), by function as linked:
+ 127 ??
+ 121 arrays_free
+ 96 free
+ 88 place
+ 79 decode_cell
+ 79 pure_list_insert
+ 77 message
+ 57 malloc
+ 52 __default_morecore
+ 49 pure_list_read
+ 47 prepare_extras
+ 37 __xpg_strerror_r
+ 35 clear_rows
+ 34 open_burst
+ 30 pure_list_clear
+ 25 pure_list_settle
+ 23 chunk_bytes
+ 23 own
+ 22 sum
+ 20 mark_event
+ 20 run_batches
+ 19 collect
+ 19 pure_list_changes
+ 19 sum_path
+ 18 close_burst
+ 14 put_cells
+ 14 descend
+ 14 add_node
+ 13 let_go_tree
+ 12 timer_settime
+
+no mechanism:
+ 67 ?? (where:0) < ??
+ 61 ?? (??:0) < ??
+ 50 decode_cell (module-list.c:2132) < decode_cell
+ 45 prepare_extras (module-list.c:625) < prepare_extras
+ 26 decode_item (module-list.c:2344) < pure_list_read
+ 13 move_items (module-list.c:337) < place
+ 13 decode_cell (module-list.c:2130) < decode_cell
+ 12 place (module-list.c:729) < place
+ 12 sum_path (module-list.c:591) < sum_path
+ 11 place (module-list.c:689) < place
+ 9 own (module-list.c:563) < own
+ 9 own (module-list.c:552) < own
+ 8 pure_list_read (module-list.c:2358) < pure_list_read
+ 8 run_batches (module.c:4968) < run_batches
+ 8 release_extras (module-list.c:673) < place
+ 8 pure_draft_size (store-tree.c:1530) < pure_draft_size
+ 8 pure_draft_version (store-tree.c:1584) < pure_draft_version
+ 8 place (module-list.c:682) < place
+ 8 chunk_bytes (store-tree.c:615) < chunk_bytes
+ 7 sum (module-list.c:511) < sum
diff --git a/research/experiments/045-proof-compiler/app-way.h b/research/experiments/045-proof-compiler/app-way.h
new file mode 100644
index 0000000..44e3d98
--- /dev/null
+++ b/research/experiments/045-proof-compiler/app-way.h
@@ -0,0 +1,193 @@
+/* Research 045: UAX #14's app (aup's examples/linebreak.aup: on line, decide and the fns it calls), written once by hand
+ * as an emitter would write it, against the macros a way (app.c) defines first: how numbers that are whole are held
+ * (IDX) and computed, how an index is checked, how the event's lists, the message's points and the stored ranges are
+ * held and read (LIST_N numbers, LIST_I whole numbers, LIST_B bools), what a record of lists takes (CAPTURE), and what
+ * the budget charges. Included once a way, with WAY
+ * the suffix of its names. Locals are C's (registers) and each aup fn a C function in every way.
+ *
+ * C's && and || go no further than they must; where the executor evaluates both sides, this does less (so the first
+ * way is cheaper than the module's code would be, never dearer). */
+#define JOIN2(a, b) a##_##b
+#define JOIN(a, b) JOIN2(a, b)
+#define FN(name) JOIN(name, WAY)
+
+/* -1 aside, every value the app keeps of a code point's position: the result of a call that faulted is 0 and x->fault set. */
+#define FAIL do { x->fault = true; x->fault_line = __LINE__; return 0; } while (0)
+#define CALLED(e) ({ __typeof__(e) called_ = (e); if (x->fault) return 0; called_; })
+
+typedef struct { LIST_N codes, cls, eff; LIST_I start, nonspace, ri, at; LIST_B joined, numeric; } FN(Text);
+
+static double FN(range)(CONTEXT *x, double c) {
+ CHARGE(CALL_COST);
+ IDX low = 0, high = R_COUNT;
+ while (GT(SUB(high, low), 1)) {
+ CHARGE(ROUND_COST);
+ IDX middle = DIV2(ADD(low, high));
+ if (R_START(middle) <= c) low = middle;
+ else high = middle;
+ }
+ return (double)low;
+}
+
+static IDX FN(next_unit)(CONTEXT *x, const FN(Text) *t, IDX i) {
+ CHARGE(CALL_COST);
+ IDX j = ADD(i, 1);
+ while (j < NCOUNT(t->codes) && GETB(t->joined, j)) {
+ CHARGE(ROUND_COST);
+ j = ADD(j, 1);
+ }
+ return j;
+}
+
+static bool FN(aksara)(CONTEXT *x, const FN(Text) *t, IDX u) {
+ CHARGE(CALL_COST);
+ return GETN(t->eff, u) == AK || GETN(t->eff, u) == AS || GETN(t->codes, u) == 0x25CC;
+}
+static bool FN(pi)(CONTEXT *x, const FN(Text) *t, IDX k) { CHARGE(CALL_COST); return R_PI(GETI(t->at, k)); }
+static bool FN(pf)(CONTEXT *x, const FN(Text) *t, IDX k) { CHARGE(CALL_COST); return R_PF(GETI(t->at, k)); }
+static bool FN(east)(CONTEXT *x, const FN(Text) *t, IDX k) { CHARGE(CALL_COST); return R_EAST(GETI(t->at, k)); }
+
+#define END(c) ((c) == BK || (c) == CR || (c) == LF || (c) == NL)
+#define NEXT_UNIT(i) CALLED(FN(next_unit)(x, t, (i)))
+#define AKSARA(u) CALLED(FN(aksara)(x, t, (u)))
+#define PI(k) CALLED(FN(pi)(x, t, (k)))
+#define PF(k) CALLED(FN(pf)(x, t, (k)))
+#define EAST(k) CALLED(FN(east)(x, t, (k)))
+
+static int FN(decide)(CONTEXT *x, const FN(Text) *t, IDX i) {
+ CHARGE(CALL_COST);
+ IDX n = NCOUNT(t->codes);
+ double a = GETN(t->cls, SUB(i, 1)), b = GETN(t->cls, i), A = GETN(t->eff, SUB(i, 1)), B = GETN(t->eff, i);
+ IDX unit = GETI(t->start, SUB(i, 1)), before = GETI(t->nonspace, SUB(i, 1));
+ double base = GETN(t->codes, unit);
+ if (a == BK) return MANDATORY; /* LB4 */
+ if (a == CR && b == LF) return NONE; /* LB5 */
+ if (a == CR || a == LF || a == NL) return MANDATORY;
+ if (END(b)) return NONE; /* LB6 */
+ if (b == SP || b == ZW) return NONE; /* LB7 */
+ if (before >= 0 && GETN(t->eff, before) == ZW) return ALLOWED; /* LB8 */
+ if (a == ZWJ) return NONE; /* LB8a */
+ if (GETB(t->joined, i)) return NONE; /* LB9 */
+ if (A == WJ || B == WJ) return NONE; /* LB11 */
+ if (A == GL) return NONE; /* LB12 */
+ if (B == GL && A != SP && A != BA && A != HY) return NONE; /* LB12a */
+ if (B == CL || B == CP || B == EX || B == SY) return NONE; /* LB13 */
+ if (before >= 0 && GETN(t->eff, before) == OP) return NONE; /* LB14 */
+ if (before >= 0 && GETN(t->eff, before) == QU && PI(before)) { /* LB15a */
+ if (before == 0) return NONE;
+ double q = GETN(t->eff, SUB(before, 1));
+ if (END(q) || q == OP || q == QU || q == GL || q == SP || q == ZW) return NONE;
+ }
+ if (B == QU && PF(i)) { /* LB15b */
+ IDX j = NEXT_UNIT(i);
+ if (j == n) return NONE;
+ double e = GETN(t->eff, j);
+ if (e == SP || e == GL || e == WJ || e == CL || e == QU || e == CP || e == EX || e == IS || e == SY || END(e) || e == ZW)
+ return NONE;
+ }
+ if (A == SP && B == IS) { /* LB15c */
+ IDX j = NEXT_UNIT(i);
+ if (j < n && GETN(t->eff, j) == NU) return ALLOWED;
+ }
+ if (B == IS) return NONE; /* LB15d */
+ if (B == NS && before >= 0 && (GETN(t->eff, before) == CL || GETN(t->eff, before) == CP)) return NONE; /* LB16 */
+ if (B == B2 && before >= 0 && GETN(t->eff, before) == B2) return NONE; /* LB17 */
+#ifdef BREAK_ONE_RULE
+ if (A == SP) return NONE; /* a rule made wrong: the check must refuse the way */
+#else
+ if (A == SP) return ALLOWED; /* LB18 */
+#endif
+ if (B == QU && !PI(i)) return NONE; /* LB19 */
+ if (A == QU && !PF(unit)) return NONE;
+ if (B == QU) { /* LB19a */
+ IDX j = NEXT_UNIT(i);
+ if (!EAST(unit) || j == n || !EAST(j)) return NONE;
+ }
+ if (A == QU && (!EAST(i) || unit == 0 || !EAST(GETI(t->start, SUB(unit, 1))))) return NONE;
+ if (B == CB || A == CB) return ALLOWED; /* LB20 */
+ if ((A == HY || base == 0x2010) && B == AL) { /* LB20a */
+ double w = unit > 0 ? GETN(t->eff, SUB(unit, 1)) : SP;
+ if (END(w) || w == SP || w == ZW || w == CB || w == GL) return NONE;
+ }
+ if (B == BA || B == HY || B == NS || A == BB) return NONE; /* LB21 */
+ if ((A == HY || (A == BA && !EAST(unit))) && unit > 0 && GETN(t->eff, SUB(unit, 1)) == HL && B != HL) return NONE; /* LB21a */
+ if (A == SY && B == HL) return NONE; /* LB21b */
+ if (B == IN) return NONE; /* LB22 */
+ if (((A == AL || A == HL) && B == NU) || (A == NU && (B == AL || B == HL))) return NONE; /* LB23 */
+ if ((A == PR && (B == ID || B == EB || B == EM)) || ((A == ID || A == EB || A == EM) && B == PO)) return NONE; /* LB23a */
+ if (((A == PR || A == PO) && (B == AL || B == HL)) || ((A == AL || A == HL) && (B == PR || B == PO))) return NONE; /* LB24 */
+ if ((B == PO || B == PR) &&
+ (GETB(t->numeric, SUB(i, 1)) || ((A == CL || A == CP) && unit > 0 && GETB(t->numeric, SUB(unit, 1)))))
+ return NONE; /* LB25 */
+ if (A == PO || A == PR) {
+ if (B == NU) return NONE;
+ if (B == OP) {
+ IDX j = NEXT_UNIT(i);
+ if (j < n && GETN(t->eff, j) == IS) j = NEXT_UNIT(j);
+ if (j < n && GETN(t->eff, j) == NU) return NONE;
+ }
+ }
+ if ((A == HY || A == IS || GETB(t->numeric, SUB(i, 1))) && B == NU) return NONE;
+ if (A == JL && (B == JL || B == JV || B == H2 || B == H3)) return NONE; /* LB26 */
+ if (((A == JV || A == H2) && (B == JV || B == JT)) || ((A == JT || A == H3) && B == JT)) return NONE;
+ bool jamo = A == JL || A == JV || A == JT || A == H2 || A == H3; /* LB27 */
+ if ((jamo && B == PO) || (A == PR && (B == JL || B == JV || B == JT || B == H2 || B == H3))) return NONE;
+ if ((A == AL || A == HL) && (B == AL || B == HL)) return NONE; /* LB28 */
+ if (A == AP && AKSARA(i)) return NONE; /* LB28a */
+ if (AKSARA(unit) && (B == VF || B == VI)) return NONE;
+ if (A == VI && unit > 0 && AKSARA(GETI(t->start, SUB(unit, 1))) && (B == AK || GETN(t->codes, i) == 0x25CC)) return NONE;
+ if (AKSARA(unit) && AKSARA(i)) {
+ IDX j = NEXT_UNIT(i);
+ if (j < n && GETN(t->eff, j) == VF) return NONE;
+ }
+ if (A == IS && (B == AL || B == HL)) return NONE; /* LB29 */
+ if ((A == AL || A == HL || A == NU) && B == OP && !EAST(i)) return NONE; /* LB30 */
+ if (A == CP && !EAST(unit) && (B == AL || B == HL || B == NU)) return NONE;
+ if (A == RI && B == RI && MOD2(GETI(t->ri, SUB(i, 1))) == 1) return NONE; /* LB30a */
+ if (B == EM && (A == EB || R_PICT(GETI(t->at, unit)))) return NONE; /* LB30b */
+ return ALLOWED; /* LB31 */
+}
+
+/* on line: the boundaries of the line's points into got (0 none, 1 allowed, 2 mandatory), count + 1 of them; false on a fault. */
+static bool FN(on_line)(CONTEXT *x, const POINTS *points, uint8_t *got) {
+ LINE_BEGIN;
+ for (IDX k = 0; k < P_COUNT(points); k = k + 1) {
+ CHARGE(ROUND_COST);
+ double code = P_CODE(points, k);
+ IDX i = NCOUNT(L(codes));
+ IDX r = (IDX)CALLED(FN(range)(x, code));
+ double c = R_CLASS(r);
+ bool mark = c == CM || c == ZWJ;
+ double previous = i > 0 ? GETN(L(eff), SUB(i, 1)) : SP;
+ bool joins = mark && i > 0 && !END(previous) && previous != SP && previous != ZW; /* LB9, LB10 */
+ double e = joins ? previous : mark ? AL : c;
+ APPN(L(codes), code);
+ APPN(L(cls), c);
+ APPI(L(at), r);
+ APPB(L(joined), joins);
+ APPN(L(eff), e);
+ APPI(L(start), joins ? GETI(L(start), SUB(i, 1)) : i);
+ APPI(L(nonspace), e != SP ? GETI(L(start), i) : i > 0 ? GETI(L(nonspace), SUB(i, 1)) : (IDX)-1);
+ APPB(L(numeric), joins ? GETB(L(numeric), SUB(i, 1)) : e == NU || ((e == SY || e == IS) && i > 0 && GETB(L(numeric), SUB(i, 1))));
+ APPI(L(ri), joins ? GETI(L(ri), SUB(i, 1)) : e == RI ? ADD(i > 0 ? GETI(L(ri), SUB(i, 1)) : (IDX)0, 1) : (IDX)0);
+ }
+ FN(Text) t;
+ CAPTURE(t.codes, codes); CAPTURE(t.cls, cls); CAPTURE(t.eff, eff); CAPTURE(t.joined, joined); CAPTURE(t.start, start);
+ CAPTURE(t.nonspace, nonspace); CAPTURE(t.numeric, numeric); CAPTURE(t.ri, ri); CAPTURE(t.at, at);
+ OUT_APPEND(0); /* LB2 */
+ for (IDX i = 1; i < NCOUNT(L(codes)); i = i + 1) {
+ CHARGE(ROUND_COST);
+ OUT_APPEND(CALLED(FN(decide)(x, &t, i)));
+ }
+ if (NCOUNT(L(codes)) > 0) OUT_APPEND(2); /* LB3 */
+ OUT_EMIT(got);
+ LINE_END;
+ return true;
+}
+
+#undef END
+#undef NEXT_UNIT
+#undef AKSARA
+#undef PI
+#undef PF
+#undef EAST
diff --git a/research/experiments/045-proof-compiler/app.c b/research/experiments/045-proof-compiler/app.c
new file mode 100644
index 0000000..2c54a8e
--- /dev/null
+++ b/research/experiments/045-proof-compiler/app.c
@@ -0,0 +1,417 @@
+/* Research 045: UAX #14's whole app emitted by hand (app-way.h), a rung at a time, each dropping one more thing the
+ * runtime holds that a fact of the proof could let go. Cumulative:
+ *
+ * exact Pure's meaning and the runtime's forms: numbers doubles, each sum and difference checked finite, //
+ * floor_divide and % Pure's (fmod); every index checked whole and in bounds (INDEX_AT) and again
+ * (ITEM_GET); the event's lists PureLists in FLAT arrays as the runtime keeps them (021: columns of
+ * the narrowest width that holds what they hold; cleared each line, appended with pure_list_insert and
+ * the memory a change holds counted as list_held and grows count it), the points the message's list,
+ * ranges the stored tree (pure_list_get); a Text's lists copied (pure_list_copied, LIST_VALUE) and let
+ * go at the line's end; the work budget charged each round and call. Locals in registers, plain calls.
+ * integer + whole numbers as integers (positions, counts, ranges, start, nonspace, ri, at): one bounds check.
+ * stored_flat + ranges' columns flat (unchanged in the event).
+ * local_flat + the event's lists, the points and out as flat buffers (a memory charge an append kept).
+ * shared + a Text's lists the buffers themselves (no copy).
+ * unbounded - the bounds checks (the budget and memory still charged).
+ * plain - every charge too: the same algorithm in C.
+ * vega Vega's C (text/linebreak.c; its classes from a two-stage table, no halving).
+ *
+ * Every rung's boundaries are compared first with the test's for all lines (and exactly with exact's); built with
+ * -DBREAK_ONE_RULE (LB18 made wrong) every rung must be refused. Then machine instructions and cycles a code point
+ * (perf_event_open, user mode), ROUNDS passes a rung, the rungs interleaved.
+ *
+ * cc -O2 -std=gnu17 -I../../../core -I../../../../vega/text app.c ../../../../vega/text/linebreak.c \
+ * ../../../../vega/text/unicode-data.c ../../../build/libpure.a -lm -pthread -o app
+ * ./app linebreak.pmod RANGES LINES ROUNDS one JSON line a rung */
+#define _GNU_SOURCE
+#include "module.h"
+#include "module-list.h"
+#include "text.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/perf_event.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+enum {
+ BK = 0, CR = 1, LF = 2, NL = 3, SP = 4, ZW = 5, ZWJ = 6, CM = 7, WJ = 8, GL = 9, BA = 10, BB = 11, B2 = 12, HY = 13, CB = 14,
+ CL = 15, CP = 16, EX = 17, IN = 18, NS = 19, OP = 20, QU = 21, IS = 22, NU = 23, PO = 24, PR = 25, SY = 26, AK = 27, AL = 28,
+ AP = 29, AS = 30, EB = 31, EM = 32, H2 = 33, H3 = 34, HL = 35, ID = 36, JL = 37, JV = 38, JT = 39, RI = 40, VF = 41, VI = 42
+};
+enum { NONE = 0, ALLOWED = 1, MANDATORY = 2 };
+enum { ROUND_COST = 24, CALL_COST = 8 }; /* what a round and a call charge (constants: what is charged, not how much) */
+enum { START = 0, CLASS = 1, PI_FIELD = 2, PF_FIELD = 3, EAST_FIELD = 4, PICT_FIELD = 5 }; /* Range's fields */
+
+typedef struct { double *v; size_t n, cap; } BufN;
+typedef struct { int64_t *v; size_t n, cap; } BufI;
+typedef struct { unsigned char *v; size_t n, cap; } BufB;
+typedef struct { size_t stored, kept; } Held;
+typedef PureList *ListP; /* a list's handle as one name, so a declaration of several declares each a handle */
+typedef BufN *BufNP;
+typedef BufI *BufIP;
+typedef BufB *BufBP;
+
+typedef struct {
+ bool fault;
+ int fault_line; /* where the last fault was (a macro's use) */
+ uint64_t work, budget;
+ int64_t grown, spent, room; /* the memory the event's changes hold (module.c's grows) */
+ PureUndo undo; /* empty: the event's lists keep none (undo_for), list_held still asks its size */
+ const PureList *ranges;
+ size_t ranges_count;
+ const double *r_start, *r_class;
+ const unsigned char *r_pi, *r_pf, *r_east, *r_pict;
+ PureList *codes, *cls, *eff, *start, *nonspace, *ri, *at, *joined, *numeric, *out; /* the event's lists */
+ PureList *copies[9];
+ unsigned copied;
+ BufN b_codes, b_cls, b_eff, k_codes, k_cls, k_eff; /* the event's lists as buffers, and a Text's copies of them */
+ BufI b_start, b_nonspace, b_ri, b_at, b_out, k_start, k_nonspace, k_ri, k_at;
+ BufB b_joined, b_numeric, k_joined, k_numeric;
+} Context;
+typedef Context CONTEXT;
+
+typedef struct { const PureList *list; const double *codes; size_t count; } Points;
+typedef Points POINTS;
+
+/* core/module.c's, as they are. */
+static double floor_divide(double a, double b) {
+ double mod = fmod(a, b), quotient = (a - mod) / b;
+ if (mod != 0 && (b < 0) != (mod < 0)) quotient -= 1.0;
+ if (quotient == 0) return copysign(0.0, a / b);
+ double whole = floor(quotient);
+ return quotient - whole > 0.5 ? whole + 1.0 : whole;
+}
+static double pure_mod(double a, double b) {
+ double value = fmod(a, b);
+ if (value != 0 && (value < 0) != (b < 0)) value += b;
+ else if (value == 0) value = copysign(0.0, b);
+ return value;
+}
+static inline Held held(const Context *x, const PureList *l) {
+ return (Held){pure_list_size(l), pure_list_changes(l).size + pure_undo_size(&x->undo) + pure_list_extra(l)};
+}
+static inline bool grows(Context *x, Held before, Held after) {
+ x->grown += (int64_t)after.stored - (int64_t)before.stored;
+ x->spent += (int64_t)(after.stored + after.kept) - (int64_t)(before.stored + before.kept);
+ return x->spent <= x->room;
+}
+static __attribute__((noinline)) bool grow(void **v, size_t *cap, size_t size) {
+ size_t more = *cap ? *cap * 2 : 64;
+ void *p = realloc(*v, more * size);
+ if (!p) return false;
+ *v = p, *cap = more;
+ return true;
+}
+
+#define CHARGES_ON(n) do { if ((uint64_t)(n) > x->budget - x->work) FAIL; x->work += (uint64_t)(n); } while (0)
+
+/* ======== exact ======== */
+#define WAY exact
+#define IDX double
+#define ADD(a, b) ({ double v_ = (double)(a) + (double)(b); if (!isfinite(v_)) FAIL; v_; })
+#define SUB(a, b) ({ double v_ = (double)(a) - (double)(b); if (!isfinite(v_)) FAIL; v_; })
+#define GT(a, b) ((a) > (b))
+#define DIV2(a) ({ double q_ = floor_divide((a), 2); if (!isfinite(q_)) FAIL; q_; })
+#define MOD2(a) ({ double m_ = pure_mod((a), 2); if (!isfinite(m_)) FAIL; m_; })
+#define CHECK_AT(count, i) do { size_t c_ = (count); double i_ = (i); /* INDEX_AT, then ITEM_GET */ \
+ if (i_ != floor(i_) || i_ < 0 || i_ >= (double)c_) FAIL; if ((size_t)i_ >= c_) FAIL; } while (0)
+#define CHARGE(n) CHARGES_ON(n)
+#define LIST_N ListP
+#define LIST_I ListP
+#define LIST_B ListP
+#define L(name) (x->name)
+#define NCOUNT(l) ((IDX)pure_list_count(l))
+#define GET_CELL(l, i) ({ const PureList *l_ = (l); IDX at_ = (i); CHECK_AT(pure_list_count(l_), at_); pure_list_get(l_, (size_t)at_, 0); })
+#define GETN(l, i) (GET_CELL(l, i).num)
+#define GETI(l, i) ((IDX)GET_CELL(l, i).num)
+#define GETB(l, i) (GET_CELL(l, i).num != 0)
+#define APP_LIST(l, cell) do { PureList *l_ = (l); Held b_ = held(x, l_); PureCell c_ = cell; \
+ if (pure_list_insert(l_, pure_list_count(l_), &c_, NULL) != PURE_OK) FAIL; if (!grows(x, b_, held(x, l_))) FAIL; } while (0)
+#define APPN(l, v) APP_LIST(l, ((PureCell){.num = (v)}))
+#define APPI(l, v) APP_LIST(l, ((PureCell){.num = (double)(v)}))
+#define APPB(l, v) APP_LIST(l, ((PureCell){.num = (v) ? 1.0 : 0.0}))
+#define R_COUNT ((IDX)pure_list_count(x->ranges))
+#define R_CELL(f, k) ({ IDX k_ = (k); CHECK_AT(pure_list_count(x->ranges), k_); pure_list_get(x->ranges, (size_t)k_, f); })
+#define R_START(k) (R_CELL(START, k).num)
+#define R_CLASS(k) (R_CELL(CLASS, k).num)
+#define R_PI(k) (R_CELL(PI_FIELD, k).num != 0)
+#define R_PF(k) (R_CELL(PF_FIELD, k).num != 0)
+#define R_EAST(k) (R_CELL(EAST_FIELD, k).num != 0)
+#define R_PICT(k) (R_CELL(PICT_FIELD, k).num != 0)
+#define P_COUNT(p) ((IDX)pure_list_count((p)->list))
+#define P_CODE(p, k) ({ IDX k_ = (k); CHECK_AT(pure_list_count((p)->list), k_); pure_list_get((p)->list, (size_t)k_, 0).num; })
+#define CAPTURE(dst, name) do { PureList *s_ = L(name); /* LIST_VALUE: nest's copy, an empty list none */ \
+ if (pure_list_count(s_)) { PureList *c_ = pure_list_copied(s_); if (!c_) FAIL; \
+ if (!grows(x, (Held){0, 0}, (Held){0, pure_list_size(c_)})) FAIL; x->copies[x->copied++] = c_; s_ = c_; } \
+ dst = s_; } while (0)
+#define OUT_APPEND(v) APP_LIST(x->out, ((PureCell){.i64 = (int64_t)(v)}))
+#define OUT_EMIT(got) do { size_t n_ = pure_list_count(x->out); for (size_t k_ = 0; k_ < n_; ++k_) { \
+ int64_t v_ = pure_list_get(x->out, k_, 0).i64; if ((uint64_t)v_ > 255) FAIL; (got)[k_] = (uint8_t)v_; } } while (0)
+#define LINE_BEGIN do { PureList *all_[] = {x->codes, x->cls, x->eff, x->start, x->nonspace, x->ri, x->at, x->joined, \
+ x->numeric, x->out}; for (unsigned l_ = 0; l_ < 10; ++l_) pure_list_clear(all_[l_]); x->copied = 0; } while (0)
+#define LINE_END do { for (unsigned c_ = 0; c_ < x->copied; ++c_) pure_list_free(x->copies[c_]); x->copied = 0; } while (0)
+#include "app-way.h"
+#undef WAY
+#undef IDX
+#undef ADD
+#undef SUB
+#undef DIV2
+#undef MOD2
+#undef CHECK_AT
+#undef GETI
+
+/* ======== integer: whole numbers as integers ======== */
+#define WAY integer
+#define IDX int64_t
+#define ADD(a, b) ((a) + (b))
+#define SUB(a, b) ((a) - (b))
+#define DIV2(a) ((a) / 2) /* whole and at or above 0 */
+#define MOD2(a) ((a) % 2) /* ri: whole and at or above 0 */
+#define CHECK_AT(count, i) do { if ((uint64_t)(i) >= (uint64_t)(count)) FAIL; } while (0)
+#define GETI(l, i) ((IDX)GET_CELL(l, i).num)
+#include "app-way.h"
+#undef WAY
+
+/* ======== stored_flat: ranges' columns flat ======== */
+#define WAY stored_flat
+#undef R_COUNT
+#undef R_START
+#undef R_CLASS
+#undef R_PI
+#undef R_PF
+#undef R_EAST
+#undef R_PICT
+#define R_COUNT ((IDX)x->ranges_count)
+#define R_AT(column, k) ({ IDX k_ = (k); CHECK_AT(x->ranges_count, k_); (column)[k_]; })
+#define R_START(k) R_AT(x->r_start, k)
+#define R_CLASS(k) R_AT(x->r_class, k)
+#define R_PI(k) (R_AT(x->r_pi, k) != 0)
+#define R_PF(k) (R_AT(x->r_pf, k) != 0)
+#define R_EAST(k) (R_AT(x->r_east, k) != 0)
+#define R_PICT(k) (R_AT(x->r_pict, k) != 0)
+#include "app-way.h"
+#undef WAY
+
+/* ======== local_flat: the event's lists, the points and out as buffers ======== */
+#define WAY local_flat
+#undef LIST_N
+#undef LIST_I
+#undef LIST_B
+#undef L
+#undef NCOUNT
+#undef GETN
+#undef GETI
+#undef GETB
+#undef APPN
+#undef APPI
+#undef APPB
+#undef P_COUNT
+#undef P_CODE
+#undef CAPTURE
+#undef OUT_APPEND
+#undef OUT_EMIT
+#undef LINE_BEGIN
+#undef LINE_END
+#define LIST_N BufNP
+#define LIST_I BufIP
+#define LIST_B BufBP
+#define L(name) (&x->b_##name)
+#define NCOUNT(l) ((IDX)(l)->n)
+#define GET_BUF(l, i) ({ __typeof__(l) l_ = (l); IDX at_ = (i); CHECK_AT(l_->n, at_); l_->v[at_]; })
+#define GETN(l, i) GET_BUF(l, i)
+#define GETI(l, i) GET_BUF(l, i)
+#define GETB(l, i) (GET_BUF(l, i) != 0)
+#define MEMORY(bytes) do { if ((x->spent += (bytes)) > x->room) FAIL; } while (0)
+#define APP_BUF(l, value) do { __typeof__(l) l_ = (l); \
+ if (l_->n == l_->cap && !grow((void **)&l_->v, &l_->cap, sizeof(*l_->v))) FAIL; \
+ MEMORY(sizeof(*l_->v)); l_->v[l_->n++] = (value); } while (0)
+#define APPN(l, v) APP_BUF(l, (v))
+#define APPI(l, v) APP_BUF(l, (v))
+#define APPB(l, v) APP_BUF(l, (unsigned char)((v) ? 1 : 0))
+#define P_COUNT(p) ((IDX)(p)->count)
+#define P_CODE(p, k) ({ IDX k_ = (k); CHECK_AT((p)->count, k_); (p)->codes[k_]; })
+#define CAPTURE(dst, name) do { __typeof__(L(name)) s_ = L(name), c_ = &x->k_##name; /* LIST_VALUE: the items copied */ \
+ while (c_->cap < s_->n) if (!grow((void **)&c_->v, &c_->cap, sizeof(*c_->v))) FAIL; \
+ MEMORY(s_->n * sizeof(*s_->v)); memcpy(c_->v, s_->v, s_->n * sizeof(*s_->v)); c_->n = s_->n; dst = c_; } while (0)
+#define OUT_APPEND(v) APP_BUF(&x->b_out, (int64_t)(v))
+#define OUT_EMIT(got) do { for (size_t k_ = 0; k_ < x->b_out.n; ++k_) { int64_t v_ = x->b_out.v[k_]; \
+ if ((uint64_t)v_ > 255) FAIL; (got)[k_] = (uint8_t)v_; } } while (0)
+#define LINE_BEGIN do { x->b_codes.n = x->b_cls.n = x->b_eff.n = x->b_start.n = x->b_nonspace.n = x->b_ri.n = x->b_at.n = \
+ x->b_joined.n = x->b_numeric.n = x->b_out.n = 0; } while (0)
+#define LINE_END do { } while (0)
+#include "app-way.h"
+#undef WAY
+
+/* ======== shared: a Text's lists the buffers ======== */
+#define WAY shared
+#undef CAPTURE
+#define CAPTURE(dst, name) ((dst) = L(name))
+#include "app-way.h"
+#undef WAY
+
+/* ======== unbounded: shared, no bounds checks (the budget and memory charged) ======== */
+#define WAY unbounded
+#undef CHECK_AT
+#define CHECK_AT(count, i) do { (void)(count); } while (0)
+#include "app-way.h"
+#undef WAY
+
+/* ======== plain: no check, no charge ======== */
+#define WAY plain
+#undef CHARGE
+#undef MEMORY
+#define CHARGE(n) do { (void)x; } while (0)
+#define MEMORY(bytes) do { } while (0)
+#undef OUT_EMIT
+#define OUT_EMIT(got) do { for (size_t k_ = 0; k_ < x->b_out.n; ++k_) (got)[k_] = (uint8_t)x->b_out.v[k_]; } while (0)
+#include "app-way.h"
+#undef WAY
+
+static bool on_line_vega(Context *x, const Points *p, const uint32_t *codes, uint8_t *got) {
+ (void)x, (void)p;
+ return vega_line_breaks(codes, p->count, got);
+}
+
+/* ---- the harness ---- */
+static unsigned char *whole(const char *path, size_t *size) {
+ FILE *f = fopen(path, "rb");
+ if (!f) return NULL;
+ fseek(f, 0, SEEK_END);
+ *size = (size_t)ftell(f);
+ rewind(f);
+ unsigned char *bytes = malloc(*size ? *size : 1);
+ if (fread(bytes, 1, *size, f) != *size) { free(bytes); bytes = NULL; }
+ fclose(f);
+ return bytes;
+}
+
+static int counter(uint64_t config) {
+ struct perf_event_attr a;
+ memset(&a, 0, sizeof(a));
+ a.size = sizeof(a), a.type = PERF_TYPE_HARDWARE, a.config = config, a.disabled = 1, a.exclude_kernel = 1, a.exclude_hv = 1;
+ return (int)syscall(SYS_perf_event_open, &a, 0, -1, -1, 0);
+}
+
+static uint64_t read_counter(int fd) {
+ uint64_t v = 0;
+ if (read(fd, &v, sizeof(v)) != sizeof(v)) return 0;
+ return v;
+}
+
+typedef struct { Points points; const uint32_t *codes; const unsigned char *want; } Line;
+enum { RUNGS = 8 };
+static const char *const names[RUNGS] = {"exact", "integer", "stored_flat", "local_flat", "shared", "unbounded", "plain", "vega"};
+
+static bool run(int rung, Context *x, const Line *l, uint8_t *got) {
+ switch (rung) {
+ case 0: return on_line_exact(x, &l->points, got);
+ case 1: return on_line_integer(x, &l->points, got);
+ case 2: return on_line_stored_flat(x, &l->points, got);
+ case 3: return on_line_local_flat(x, &l->points, got);
+ case 4: return on_line_shared(x, &l->points, got);
+ case 5: return on_line_unbounded(x, &l->points, got);
+ case 6: return on_line_plain(x, &l->points, got);
+ default: return on_line_vega(x, &l->points, l->codes, got);
+ }
+}
+
+int main(int argc, char **argv) {
+ if (argc != 5) { fprintf(stderr, "usage: %s linebreak.pmod RANGES LINES ROUNDS\n", argv[0]); return 2; }
+ size_t ranges_size, lines_size;
+ unsigned char *ranges = whole(argv[2], &ranges_size), *bytes = whole(argv[3], &lines_size);
+ int rounds = atoi(argv[4]);
+ if (!ranges || !bytes) return 1;
+ PureModule module = {0};
+ char error[160];
+ if (pure_module_read(argv[1], &module, error, sizeof(error)) != PURE_OK) { fprintf(stderr, "%s\n", error); return 1; }
+ PureApp app;
+ if (pure_app_start(&app, &module, (PureData){0}) != PURE_OK) return 1;
+ int load = pure_module_input(&module, "load"), state = pure_module_state(&module, "ranges");
+ if (load < 0 || state < 0 || pure_app_message(&app, (uint32_t)load, (PureSpan){ranges, ranges_size}) != PURE_OK) return 1;
+ static Context x;
+ x.budget = UINT64_MAX, x.room = INT64_MAX;
+ x.ranges = app.lists[state], x.ranges_count = pure_list_count(x.ranges);
+ double *r_start = malloc(x.ranges_count * sizeof(double)), *r_class = malloc(x.ranges_count * sizeof(double));
+ unsigned char *flags = malloc(4 * x.ranges_count);
+ for (size_t i = 0; i < x.ranges_count; ++i) {
+ r_start[i] = pure_list_get(x.ranges, i, START).num, r_class[i] = pure_list_get(x.ranges, i, CLASS).num;
+ for (unsigned f = 0; f < 4; ++f) flags[f * x.ranges_count + i] = pure_list_get(x.ranges, i, PI_FIELD + f).num != 0;
+ }
+ x.r_start = r_start, x.r_class = r_class;
+ x.r_pi = flags, x.r_pf = flags + x.ranges_count, x.r_east = flags + 2 * x.ranges_count, x.r_pict = flags + 3 * x.ranges_count;
+ const uint8_t number[] = {PURE_CELL_NUMBER}, boolean[] = {PURE_CELL_BOOL}, integer[] = {PURE_CELL_INTEGER};
+ PureList **numbers[] = {&x.codes, &x.cls, &x.eff, &x.start, &x.nonspace, &x.ri, &x.at};
+ for (unsigned k = 0; k < 7; ++k) *numbers[k] = pure_list_new(1, number);
+ x.joined = pure_list_new(1, boolean), x.numeric = pure_list_new(1, boolean), x.out = pure_list_new(1, integer);
+ PureList *events[] = {x.codes, x.cls, x.eff, x.start, x.nonspace, x.ri, x.at, x.joined, x.numeric, x.out};
+ for (unsigned k = 0; k < 10; ++k) /* an event's list in FLAT arrays, as the runtime starts one (021) */
+ if (!pure_list_arrays(events[k])) return 1;
+ /* The lines: each its points as the message's list (the event's form) and as the message's numbers. */
+ static Line lines[1 << 16];
+ size_t count = 0, points = 0;
+ for (size_t at = 0; at + 4 <= lines_size && count < sizeof(lines) / sizeof(*lines); ++count) {
+ Line *l = &lines[count];
+ uint32_t n;
+ memcpy(&n, bytes + at, 4);
+ l->codes = (const uint32_t *)(bytes + at + 4);
+ l->want = bytes + at + 4 + 4 * (size_t)n;
+ at += 4 + 4 * (size_t)n + n + 1;
+ double *codes = malloc(((size_t)n + 1) * sizeof(double));
+ PureList *list = pure_list_new(1, number);
+ if (!pure_list_arrays(list)) return 1;
+ for (uint32_t i = 0; i < n; ++i) {
+ codes[i] = l->codes[i];
+ PureCell cell = {.num = codes[i]};
+ if (pure_list_insert(list, i, &cell, NULL) != PURE_OK) return 1;
+ }
+ l->points = (Points){list, codes, n};
+ points += n;
+ }
+ /* Checked first: each rung's boundaries are the test's, and exactly exact's. */
+ static uint8_t got[4096], first[4096];
+ size_t wrong[RUNGS] = {0};
+ for (size_t k = 0; k < count; ++k) {
+ const Line *l = &lines[k];
+ if (l->points.count + 1 > sizeof(got)) return 1;
+ for (int r = 0; r < RUNGS; ++r) {
+ x.fault = false;
+ bool same = run(r, &x, l, got) && !x.fault;
+ for (size_t i = 0; same && i <= l->points.count; ++i) same = (got[i] != NONE) == l->want[i];
+ if (r == 0) memcpy(first, got, l->points.count + 1);
+ else if (r < RUNGS - 1) same = same && !memcmp(first, got, l->points.count + 1);
+ if (!same && getenv("APP_DEBUG") && !wrong[r]) {
+ fprintf(stderr, "%s line %zu fault %d at %d:", names[r], k, x.fault, x.fault_line);
+ for (size_t i = 0; i <= l->points.count; ++i) fprintf(stderr, " %u/%u", got[i], l->want[i]);
+ fprintf(stderr, "\n");
+ }
+ wrong[r] += !same;
+ }
+ }
+ bool refused = false;
+ for (int r = 0; r < RUNGS; ++r)
+ if (wrong[r]) fprintf(stderr, "%s: %zu of %zu lines wrong\n", names[r], wrong[r], count), refused = true;
+ if (refused) return 1;
+ int ins = counter(PERF_COUNT_HW_INSTRUCTIONS), cyc = counter(PERF_COUNT_HW_CPU_CYCLES);
+ if (ins < 0 || cyc < 0) { perror("perf_event_open"); return 1; }
+ uint64_t total_ins[RUNGS] = {0}, total_cyc[RUNGS] = {0};
+ for (int round = 0; round < rounds; ++round)
+ for (int r = 0; r < RUNGS; ++r) {
+ x.work = 0, x.spent = 0, x.grown = 0;
+ ioctl(ins, PERF_EVENT_IOC_RESET, 0), ioctl(cyc, PERF_EVENT_IOC_RESET, 0);
+ ioctl(ins, PERF_EVENT_IOC_ENABLE, 0), ioctl(cyc, PERF_EVENT_IOC_ENABLE, 0);
+ for (size_t k = 0; k < count; ++k) run(r, &x, &lines[k], got);
+ ioctl(ins, PERF_EVENT_IOC_DISABLE, 0), ioctl(cyc, PERF_EVENT_IOC_DISABLE, 0);
+ total_ins[r] += read_counter(ins), total_cyc[r] += read_counter(cyc);
+ x.spent = 0;
+ }
+ double n = (double)points * rounds;
+ for (int r = 0; r < RUNGS; ++r)
+ printf("{\"rung\":\"%s\",\"lines\":%zu,\"code_points\":%zu,\"rounds\":%d,\"instructions_per_point\":%.1f,"
+ "\"cycles_per_point\":%.1f,\"answers\":\"the test's, and exact's\"}\n",
+ names[r], count, points, rounds, total_ins[r] / n, total_cyc[r] / n);
+ pure_app_stop(&app);
+ return 0;
+}