From 021f7106ccfffa762885c7fb814fb8796be484b9 Mon Sep 17 00:00:00 2001 From: Louis Date: Sat, 17 Oct 2026 15:51:27 +0200 Subject: [PATCH] replica(rechner): architecture and schema Co-Authored-By: Claude Opus 5.5 --- replica/rechner/architecture.md | 43 +++++++++++++++++++++++++++++++++ replica/rechner/schema.sql | 9 +++++++ 2 files changed, 52 insertions(+) create mode 100644 replica/rechner/architecture.md create mode 100644 replica/rechner/schema.sql diff --git a/replica/rechner/architecture.md b/replica/rechner/architecture.md new file mode 100644 index 0000000..7862f7d --- /dev/null +++ b/replica/rechner/architecture.md @@ -0,0 +1,43 @@ +# Architecture: Rechner (Android, ohne Google) + +Same conventions as `wetter/` and `uhr/`: one native app, no server, **no network permission**. + +## Stack + +| Schicht | Wahl | warum | +| --- | --- | --- | +| UI | Kotlin + Compose, Material 3 Expressive, dynamic colour | same look and pipeline as the other apps | +| Rechenkern | own recursive-descent parser → AST → evaluator on `BigDecimal` (MathContext 50 digits), integers stay exact `BigInteger` | no floating-point surprises (0.1+0.2 = 0.3), 100! exact; pure Kotlin, unit-tested on the JVM | +| Funktionen | sin/cos/tan/ln/log/eˣ/√/^ via `Math` on doubles only where unavoidable, results rounded to 15 significant digits, exact values snapped (sin 30° = 0.5, cos π = −1) | readable answers without fake precision | +| Persistenz | Room (history) + DataStore (memory, angle unit, current expression) | – | +| Build/Verteilung | minSdk 29, `scripts/release.sh rechner` (Obtainium) | – | + +## Schema + +`schema.sql`: 1 table `history`. Single user, app-private storage. + +## Interne API + +| Aufruf | macht | Flow | +| --- | --- | --- | +| `Engine.evaluate(expr, angle)` | parse + evaluate, tolerant of unfinished input (auto-closes parentheses, ignores a trailing operator) | F01, F02 | +| `Editor.input(key)` | cursor-aware editing: digits, operators (replace a trailing operator), smart parentheses, %, functions insert `sin(`, backspace removes whole tokens | F01, F02 | +| `Calc.equals()` | evaluate, store history row, result becomes the expression | F01 | +| `History.observe()/clear()` | grouped by local day | F03 | +| `Memory.store(value)/recall()` | DataStore | F05 | +| `Clipboard.copy/paste` | system clipboard; paste is sanitised to calculator tokens | F04 | + +## Was beißt + +- **Precision:** division keeps 50 digits internally; display rounds to fit and strips trailing zeros; repeating decimals shown truncated with an ellipsis rather than rounded up wrongly. +- **Percent:** `50+10%` = 55 (percent of the left operand for + and −), `50×10%` = 5; standalone `10%` = 0.1. +- **Locale:** German uses comma decimal and dot grouping; the parser accepts the locale separator only. +- **Big results:** 100! has 158 digits; display shows them scientifically (1,5E157) and the full value on scroll. +- **Domain errors:** √(−1), ln 0, tan 90°, 0÷0, factorial of non-integers → error text, never a crash. + +## Build-Reihenfolge + +1. Vertical slice (S01): engine + keypad + live result + equals; engine unit tests. +2. Must: scientific row and panel (S02), Deg/Rad, Inv, errors, auto-shrinking display, history (S03), copy/paste (S04), expressive keys, dynamic colour. +3. Should: memory, implicit multiplication, pull-down history, haptics, big numbers, locale separators, clear history. +4. Could: landscape layout with all functions. diff --git a/replica/rechner/schema.sql b/replica/rechner/schema.sql new file mode 100644 index 0000000..2bd3ba2 --- /dev/null +++ b/replica/rechner/schema.sql @@ -0,0 +1,9 @@ +-- Local SQLite (Room). No network, no accounts. +CREATE TABLE history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + expression TEXT NOT NULL CHECK (length(expression) > 0), + result TEXT NOT NULL, + created_at INTEGER NOT NULL -- epoch ms UTC; grouped by local day in the UI +); +CREATE INDEX history_created ON history(created_at); +-- Memory value, angle unit, theme and the current (unfinished) expression live in DataStore.