# 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.