aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/module.c16
-rw-r--r--core/module.h1
-rw-r--r--core/test-module.c28
-rw-r--r--docs/design/008-module-format.md3
-rw-r--r--docs/design/027-computation-core.md26
-rw-r--r--research/experiments/040-against-c/2026-10-19-counts-s43.jsonl12
-rw-r--r--research/experiments/041-batches/2026-10-19-counts.jsonl10
7 files changed, 94 insertions, 2 deletions
diff --git a/core/module.c b/core/module.c
index 71a0a25..052528c 100644
--- a/core/module.c
+++ b/core/module.c
@@ -130,7 +130,7 @@ static const struct { unsigned char pops, in[3], out; unsigned short where; unsi
/* LOOP takes its low and high, two numbers or two i64s (the operands are its argument's). */
[PURE_OP_LOOP] = {0, {0}, 0, IN_HANDLER | IN_COMPUTE, A_LOOP, O_ANY},
[PURE_OP_BREAK] = {0, {0}, 0, IN_HANDLER | IN_COMPUTE, A_NONE, O_ANY},
- [PURE_OP_FLOOR] = NUMS(1), [PURE_OP_MOD] = NUMS(2),
+ [PURE_OP_FLOOR] = NUMS(1), [PURE_OP_MOD] = NUMS(2), [PURE_OP_FLOORDIV] = NUMS(2),
[PURE_OP_I64_ADD] = I64S(2), [PURE_OP_I64_SUB] = I64S(2), [PURE_OP_I64_MUL] = I64S(2), [PURE_OP_I64_DIV] = I64S(2),
[PURE_OP_I64_MOD] = I64S(2), [PURE_OP_I64_NEG] = I64S(1), [PURE_OP_I64_MIN] = I64S(2), [PURE_OP_I64_MAX] = I64S(2),
[PURE_OP_I64_COMPARE] = {2, {T_I64, T_I64}, T_BOOL, READ, A_RELATION, O_ANY},
@@ -1526,6 +1526,16 @@ static uint32_t nesting_of(const PureModule *m) {
return most;
}
+/* a // b for numbers as Python's float floor division: the quotient of a less its floored remainder, rounded to the
+ * nearest whole (it is one but for rounding), a zero with the sign of a / b (027's stage 5). b is not 0. */
+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;
+}
+
/* A name and its place among those of one namespace, in order of name and then of place. */
typedef struct { const char *name; size_t index; } Named;
static int by_name(const void *a, const void *b) {
@@ -4510,6 +4520,10 @@ static inline __attribute__((always_inline)) int step(Frame *frame, const PureOp
if (value != 0 && (value < 0) != (b < 0)) value += b;
else if (value == 0) value = copysign(0.0, b);
break;
+ case PURE_OP_FLOORDIV: /* MOD's quotient, exactly (027's stage 5): a less its remainder, divided, as Python's // */
+ if (b == 0) { kind = PURE_FAULT_DIVIDE; FAILED; }
+ value = floor_divide(a, b);
+ break;
default: value = maximum(a, b); break;
}
if (!isfinite(value)) { kind = PURE_FAULT_NON_FINITE; FAILED; }
diff --git a/core/module.h b/core/module.h
index 8a84ddb..ce8cebf 100644
--- a/core/module.h
+++ b/core/module.h
@@ -47,6 +47,7 @@ typedef enum {
PURE_OP_BYTES_SLICE, PURE_OP_BYTE, PURE_OP_BYTES_LOAD, PURE_OP_BYTES_STORE,
PURE_OP_LIST_VALUE, PURE_OP_LIST_BIND, PURE_OP_APPEND_IN, PURE_OP_NUM_PARSE,
PURE_OP_CALL, PURE_OP_RETURN, PURE_OP_CHAR_AT, PURE_OP_CHAR,
+ PURE_OP_FLOORDIV,
PURE_OP_COUNT
} PureOpcode;
/* 027's 4c: an anchor is a place in a text or a list that follows its edits. A text's (a cursor) is a gap between
diff --git a/core/test-module.c b/core/test-module.c
index cb8e228..36722d2 100644
--- a/core/test-module.c
+++ b/core/test-module.c
@@ -6962,6 +6962,33 @@ static void timers(void) {
CHECK(load(bad, &module_b) == PURE_INVALID && strstr(error, "view with an effect"));
}
+/* Floor division of numbers (027's stage 5): as Python's //, the quotient MOD's remainder leaves, exactly (1 // 0.1 is 9:
+ * 0.1 is a little more than a tenth), its zero signed as a / b; by zero it fails as divide. */
+static void floor_division(void) {
+ enum { FDIV = PURE_OP_FLOORDIV };
+ static const S nums[] = {{"a", 0}, {"b", 0}, {"c", 0}, {"d", 0}, {"e", 0}, {"f", 0}};
+ static const I inputs[] = {KEY("go", "KeyG"), KEY("zero", "KeyZ")};
+ static const O code[] = {
+ {C, 0, 7}, {C, 0, 2}, {FDIV, 0, 0}, {ST, 0, 0}, {C, 0, -7}, {C, 0, 2}, {FDIV, 0, 0}, {ST, 1, 0},
+ {C, 0, 7}, {C, 0, -2}, {FDIV, 0, 0}, {ST, 2, 0}, {C, 0, 1}, {C, 0, 0.1}, {FDIV, 0, 0}, {ST, 3, 0},
+ {C, 0, 0}, {C, 0, -3}, {FDIV, 0, 0}, {ST, 4, 0}, {C, 0, 1e300}, {C, 0, 7}, {FDIV, 0, 0}, {ST, 5, 0},
+ {C, 0, 1}, {C, 0, 0}, {FDIV, 0, 0}, {ST, 0, 0},
+ };
+ static const H handlers[] = {{1, 0, 24}, {3, 24, 4}};
+ static const V none[] = {{"v", 0, 1}};
+ Spec spec = SPEC(nums, inputs, none, handlers, code);
+ spec.nv = 0;
+ if (load(spec, &module_a) != PURE_OK) { fprintf(stderr, "floor division: %s\n", error); abort(); }
+ PureApp app;
+ CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK && press(&app, 0) == PURE_OK);
+ double huge = 1e300, quotient = (huge - fmod(huge, 7)) / 7;
+ CHECK(app.values[0].num == 3 && app.values[1].num == -4 && app.values[2].num == -4 && app.values[3].num == 9 &&
+ app.values[4].num == 0 && signbit(app.values[4].num) && app.values[5].num == floor(quotient));
+ CHECK(press(&app, 1) == PURE_INVALID && app.fault == PURE_FAULT_DIVIDE && app.values[0].num == 3);
+ pure_app_stop(&app);
+ printf("{\"floor_division\":6}\n");
+}
+
/* A number is written with the fewest digits that read back as it, the
* digits %g gives (with its exponent's leading zeros dropped): the same
* text as that search over precisions, for values of every kind. */
@@ -7312,6 +7339,7 @@ int main(void) {
bytes_fields();
anchors();
list_views();
+ floor_division();
memory_budget();
work_budget();
fold_mutations();
diff --git a/docs/design/008-module-format.md b/docs/design/008-module-format.md
index dde292e..201baf9 100644
--- a/docs/design/008-module-format.md
+++ b/docs/design/008-module-format.md
@@ -36,7 +36,7 @@ v7은 027의 4단계(패턴 모으기)의 판이다. 4b로 **키별 문서**를
인자 셋이고, 키의 누름과 뗌, text 입력, 타이머, frame, 가리킴은 메시지 입력의 묶음(무엇이 그 메시지를 만드는가)이다. 핸들러의
이벤트는 그 입력의 번호이고, DT와 TEXT_INPUT은 필드 읽기(FIELD)가 대신한다(아래 표의 번호가 당겨졌다). 4e로 **보기가 아무 값**이 되었다: 보기와 열의 타입이
레코드 필드의 것(바이트와 리스트가 더해졌다)이고, 표는 리스트 보기다. 리스트 보기는 리스트 상태 그대로나 그 지도, 또는 블록이
-낸 리스트를 행으로 가지며, 보기 항목은 20바이트다. 4단계가 끝날 때까지 v7은 걸음마다 바뀐다(1.0 전, 생산자는 aup 하나다).
+낸 리스트를 행으로 가지며, 보기 항목은 20바이트다. 5단계(수의 모델)로 내림한 몫(FLOORDIV, 95)이 더해졌다. 4단계가 끝날 때까지 v7은 걸음마다 바뀐다(1.0 전, 생산자는 aup 하나다).
**레코드 안의 리스트**(027의 2b).
- 레코드 필드의 타입 4(LIST)는 항목들의 레코드를 기본값 자리(i64)에 둔다.
@@ -181,6 +181,7 @@ v7은 027의 4단계(패턴 모으기)의 판이다. 4b로 **키별 문서**를
| 92 | RETURN | … r… → | 함수 `인자`(자신)를 끝낸다. 맨 위의 결과가 부른 쪽으로 간다 |
| 93 | CHAR_AT | t q → q | text의 바이트 자리 q(i64)에서 시작하는 글자의 코드 포인트(i64). 글자가 시작하지 않는 자리나 밖이면 `limit` |
| 94 | CHAR | q → t | 코드 포인트 하나의 text. 0–0x10FFFF 밖이면 `limit`, 서로게이트면 `encoding` |
+| 95 | FLOORDIV | n n → n | 내림한 몫(027의 5단계): MOD의 나머지를 뺀 a를 b로 나눈 것을 가까운 정수로(파이썬의 float `//`와 같다. `1 // 0.1`은 9). 0의 부호는 a / b의 것. 0으로 나누면 `divide` |
bool은 실행 중에 수의 칸에 든 0이나 1이고, i64는 같은 8바이트의 정수다. CONST의 인자 2는 i64 상수(값의 8바이트), NUM_TEXT의
인자 2는 i64의 10진 글자다. LOAD·STORE·지역 칸·항목·FIELD·SEND는 i64를 수처럼 옮긴다. FOLD의 종류에 64(PURE_FOLD_INTEGER)를
diff --git a/docs/design/027-computation-core.md b/docs/design/027-computation-core.md
index 28c03b2..4c7fb60 100644
--- a/docs/design/027-computation-core.md
+++ b/docs/design/027-computation-core.md
@@ -513,6 +513,32 @@
- 정수 리터럴의 기본, `/`와 `//`, 돈의 십진수, 묶음 실행기의 2^53 검사를 정한다.
- **막대.** TPC-C가 SQLite와 행마다 같은 상태를 남기는 것이 그대로다.
+- **설계(2026-10-19).**
+ - **두 수.** `number`는 binary64이고 리터럴과 셈(개수, 자리, 길이, u32 필드)의 기본이다. 정수는 정수인 number이고 2^53까지 정확하다.
+ `i64`는 정확한 정수의 타입이다: 2^53을 넘는 정수, 비트, 돈. 둘은 섞이지 않는다. 옮기기는 `number(i)`(정확할 때만, 넘으면
+ `limit`)와 `i64(x)`(정수이고 범위 안일 때만)다. 리터럴은 맥락을 따른다(i64가 오는 자리의 `1`은 i64).
+ - 왜 i64가 기본이 아닌가: 개수·자리·길이·u32 필드가 모두 number라서, 리터럴만 i64로 하면 `n = xs.count` 같은 줄마다 옮기기가
+ 든다. 그것들을 모두 i64로 하면 묶음 실행기와 JIT의 수의 길(binary64 레인)을 i64로 다시 지어야 한다. 앱의 셈은 거의 2^53 안이고,
+ 그 밖은 i64가 맡는다. 한 기본 타입이 압축이다.
+ - **나눗셈.** `/`는 number의 나눗셈이다(i64끼리는 거절: `//`를 쓰거나 옮긴다). `//`는 내림한 몫이다: number는 파이썬의 그것과
+ 같은 정확한 내림(나머지를 빼고 나눈다), i64는 I64_DIV. `%`는 그 짝(내림한 나머지, 나누는 수의 부호)으로 이미 있다.
+ - **접기.** i64의 합은 정확하게 셈하고 결과만 범위를 본다(중간이 넘쳐도 끝이 i64 안이면 답이다): 차례에 매이지 않는다. number의
+ 합은 차례대로의 왼쪽 접기이고, 구현은 답이 같을 때만 차례를 바꾼다. 묶음 실행기의 2^53 검사가 그 조건이다: 모든 항이 정수이고
+ 그 크기의 합이 2^53 아래면 어느 차례든 정확하다. 아니면 차례대로다.
+ - **돈.** 새 타입 없이 i64의 최소 단위(센트)다. 셈은 정확하고, 반올림은 앱이 쓴다(`price * rate // 10000`). 보이는 꼴은
+ `{cents:.2d}`: i64를 소수점 아래 두 자리로 나눠 쓴다(1234 → `12.34`, −5 → `-0.05`). JSON의 경계는 i64를 정수로 쓴다.
+ - **교체.** number, bool, i64는 정확할 때만 서로 옮긴다(`pure_cell_as`: 정수인 number ↔ i64, 0·1 ↔ bool). 아니면 기본값이다.
+ - 걸음: 5a(`//`와 `/`의 규칙), 5b(i64 접기의 정확한 합), 5c(`.Nd`와 TPC-C의 돈을 i64 센트로, SQLite도), 5d(교체의 규칙과 008).
+ - **5a 만듦(2026-10-19).**
+ - Pure: 명령 FLOORDIV(95, 명령 96개). number의 내림한 몫을 파이썬의 float `//`처럼 정확하게 낸다(나머지를 뺀 a를 b로 나누고
+ 가까운 정수로, 0의 부호는 a / b의 것, 0으로 나누면 `divide`). 새 명령이라 JIT, 계층, 묶음 실행기의 빠른 길은 그것을 모르고
+ 실행기로 넘긴다(DIV의 인자로 했다면 그 길마다 고쳐야 했다).
+ - aup: `//`를 `/`와 같은 우선순위로 더했다(number는 FLOORDIV, i64는 I64_DIV). i64끼리의 `/`는 거절한다("an i64 divides by //").
+ i64의 `/`를 쓰던 시험 셋(받아들인 꼴 하나, i64의 시험 앱, 무작위 프로그램의 i64 식)을 `//`로 고쳤다.
+ - 시험: 단위 시험 `floor_division`(7 // 2, −7 // 2, 7 // −2, 1 // 0.1 = 9, 0 // −3 = −0, 1e300 // 7, 0으로 나누기). aup의 시험이 무작위
+ 200쌍의 `a // b`와 `a % b`를 파이썬과 비트까지(−0의 부호까지) 견준다. 진단 하나(305).
+ - 명령 수(b43 대 b42): 실행기만의 BFS −0.09%, 나머지 ±0.001% 안이다. 은행 질의(s42와 s43을 한 번에 번갈아 여섯 번)는 −0.20%에서
+ 0.00% 사이다.
**순서의 까닭.**
- 1과 2가 묶음의 명세 대부분을 연다. 가장 값이 크다.
diff --git a/research/experiments/040-against-c/2026-10-19-counts-s43.jsonl b/research/experiments/040-against-c/2026-10-19-counts-s43.jsonl
new file mode 100644
index 0000000..3c376cb
--- /dev/null
+++ b/research/experiments/040-against-c/2026-10-19-counts-s43.jsonl
@@ -0,0 +1,12 @@
+{"serve": "pure-serve", "per_branch": {"instructions": 27047243, "cycles": 11689798}, "in_credit": {"instructions": 3496991, "cycles": 1607154}, "large": {"instructions": 1412830, "cycles": 600237}, "ranks": {"instructions": 24415275, "cycles": 14986084}, "none": {"instructions": 617346, "cycles": 188795}, "field": {"instructions": 8917677, "cycles": 3131398}, "found": {"instructions": 14070799, "cycles": 10410020}}
+{"serve": "pure-serve", "per_branch": {"instructions": 27096179, "cycles": 11779409}, "in_credit": {"instructions": 3508003, "cycles": 1643044}, "large": {"instructions": 1412862, "cycles": 589402}, "ranks": {"instructions": 24443281, "cycles": 14729351}, "none": {"instructions": 617346, "cycles": 197858}, "field": {"instructions": 8917574, "cycles": 3100270}, "found": {"instructions": 14069967, "cycles": 10336650}}
+{"serve": "pure-serve", "per_branch": {"instructions": 27046648, "cycles": 11407491}, "in_credit": {"instructions": 3496871, "cycles": 1607966}, "large": {"instructions": 1412854, "cycles": 595377}, "ranks": {"instructions": 24415831, "cycles": 15099521}, "none": {"instructions": 617346, "cycles": 195509}, "field": {"instructions": 8917574, "cycles": 3112623}, "found": {"instructions": 14070670, "cycles": 10552319}}
+{"serve": "pure-serve", "per_branch": {"instructions": 27172179, "cycles": 11797248}, "in_credit": {"instructions": 3525116, "cycles": 1604609}, "large": {"instructions": 1412766, "cycles": 596630}, "ranks": {"instructions": 24485315, "cycles": 14888250}, "none": {"instructions": 617346, "cycles": 190584}, "field": {"instructions": 8917574, "cycles": 3103366}, "found": {"instructions": 14067734, "cycles": 10384052}}
+{"serve": "pure-serve", "per_branch": {"instructions": 27243839, "cycles": 11832991}, "in_credit": {"instructions": 3541033, "cycles": 1752046}, "large": {"instructions": 1412902, "cycles": 588650}, "ranks": {"instructions": 24523700, "cycles": 15063501}, "none": {"instructions": 617350, "cycles": 189045}, "field": {"instructions": 8917574, "cycles": 3096652}, "found": {"instructions": 14071735, "cycles": 10551854}}
+{"serve": "pure-serve", "per_branch": {"instructions": 27166328, "cycles": 11864741}, "in_credit": {"instructions": 3523683, "cycles": 1637515}, "large": {"instructions": 1412846, "cycles": 605901}, "ranks": {"instructions": 24481539, "cycles": 14558275}, "none": {"instructions": 617346, "cycles": 188551}, "field": {"instructions": 8917574, "cycles": 3107277}, "found": {"instructions": 14070214, "cycles": 10469226}}
+{"serve": "pure-serve", "per_branch": {"instructions": 27186157, "cycles": 11822639}, "in_credit": {"instructions": 3528111, "cycles": 1641510}, "large": {"instructions": 1412918, "cycles": 591550}, "ranks": {"instructions": 24491803, "cycles": 15034811}, "none": {"instructions": 617346, "cycles": 192547}, "field": {"instructions": 8917574, "cycles": 3098023}, "found": {"instructions": 14069918, "cycles": 10570428}}
+{"serve": "pure-serve", "per_branch": {"instructions": 27075881, "cycles": 12587949}, "in_credit": {"instructions": 3503428, "cycles": 1662798}, "large": {"instructions": 1412878, "cycles": 589913}, "ranks": {"instructions": 24430910, "cycles": 14718538}, "none": {"instructions": 617449, "cycles": 189277}, "field": {"instructions": 8917574, "cycles": 3104978}, "found": {"instructions": 14070911, "cycles": 10608808}}
+{"serve": "pure-serve", "per_branch": {"instructions": 27187691, "cycles": 11787503}, "in_credit": {"instructions": 3528550, "cycles": 1660533}, "large": {"instructions": 1412766, "cycles": 588274}, "ranks": {"instructions": 24493383, "cycles": 14704362}, "none": {"instructions": 617346, "cycles": 189544}, "field": {"instructions": 8917574, "cycles": 3119834}, "found": {"instructions": 14066702, "cycles": 10432332}}
+{"serve": "pure-serve", "per_branch": {"instructions": 27005720, "cycles": 11716175}, "in_credit": {"instructions": 3487749, "cycles": 1557087}, "large": {"instructions": 1412870, "cycles": 592784}, "ranks": {"instructions": 24394115, "cycles": 14783563}, "none": {"instructions": 617346, "cycles": 192543}, "field": {"instructions": 8917574, "cycles": 3114559}, "found": {"instructions": 14071278, "cycles": 10343596}}
+{"serve": "pure-serve", "per_branch": {"instructions": 27128475, "cycles": 11757578}, "in_credit": {"instructions": 3515189, "cycles": 1614634}, "large": {"instructions": 1412846, "cycles": 623225}, "ranks": {"instructions": 24460528, "cycles": 15001740}, "none": {"instructions": 617346, "cycles": 189534}, "field": {"instructions": 8917677, "cycles": 3096738}, "found": {"instructions": 14071143, "cycles": 10528194}}
+{"serve": "pure-serve", "per_branch": {"instructions": 27154153, "cycles": 11748579}, "in_credit": {"instructions": 3520998, "cycles": 1620124}, "large": {"instructions": 1412790, "cycles": 587241}, "ranks": {"instructions": 24474542, "cycles": 14856006}, "none": {"instructions": 617346, "cycles": 189148}, "field": {"instructions": 8917575, "cycles": 3102194}, "found": {"instructions": 14066870, "cycles": 10315697}}
diff --git a/research/experiments/041-batches/2026-10-19-counts.jsonl b/research/experiments/041-batches/2026-10-19-counts.jsonl
index df0c154..3746b9e 100644
--- a/research/experiments/041-batches/2026-10-19-counts.jsonl
+++ b/research/experiments/041-batches/2026-10-19-counts.jsonl
@@ -18,3 +18,13 @@
{"version":"b42","way":"rounds","work":"matmul","instructions":21186409896,"cycles":7038688726}
{"version":"b42","way":"batches","work":"bfs","instructions":3341462103,"cycles":1673340823}
{"version":"b42","way":"rounds","work":"bfs","instructions":4709148541,"cycles":2184940702}
+{"version":"b43","way":"batches","work":"roots","instructions":157246397,"cycles":109505019}
+{"version":"b43","way":"rounds","work":"roots","instructions":2715503987,"cycles":999976397}
+{"version":"b43","way":"batches","work":"knapsack","instructions":348031324,"cycles":105474340}
+{"version":"b43","way":"rounds","work":"knapsack","instructions":22162562687,"cycles":7749754834}
+{"version":"b43","way":"batches","work":"join","instructions":1783720391,"cycles":1012592603}
+{"version":"b43","way":"rounds","work":"join","instructions":7723677646,"cycles":3345712383}
+{"version":"b43","way":"batches","work":"matmul","instructions":702230543,"cycles":227373960}
+{"version":"b43","way":"rounds","work":"matmul","instructions":21186409838,"cycles":6817006402}
+{"version":"b43","way":"batches","work":"bfs","instructions":3341463903,"cycles":1640907916}
+{"version":"b43","way":"rounds","work":"bfs","instructions":4705147005,"cycles":2086468365}