Architecture & System Design
Mathua is a single Go binary with a web delivery mode. Click nodes in the diagrams below for details.
I. Five-Layer Architecture
The system is organized into five layers. The UI has a single implementation -- the web frontend (React/Next.js) -- calling into the Go engine through the REST API. The API exposes 25+ REST endpoints through net/http. The Core Engine handles DAG loading, SM-2 scheduling, problem generation, mastery tracking, scoring, and the CAT diagnostic. The Grading layer dispatches to 6+ grader strategies with a SymPy subprocess for symbolic math. Storage is abstracted behind a Repository interface.
II. Data Model
Five core tables store all application state. The `concept_progress` table embeds SM-2 spaced repetition fields (repetitions, interval, efactor) alongside mastery state and weakness scores. All DDL uses CREATE TABLE IF NOT EXISTS for idempotent bootstrapping. Incremental migrations add columns with ALTER TABLE guarded by error-checking.
The SM-2 upsert uses SQLite's ON CONFLICT ... DO UPDATE to atomically save all progress fields in one statement. XP tracking is date-aware: xp_today resets when xp_date differs from the current date, preserving xp_total as a lifetime accumulator.
III. End-to-End Request Flow
When a student submits an answer, the request passes through 12 distinct stages before the next question is served. The entire pipeline runs synchronously in a single Go goroutine, completing in under 100ms for numeric grading and under 500ms for SymPy-based grading (including subprocess round-trip).
IV. Grading System
The grading system uses a strategy pattern dispatched by grading_type. Numeric grading (int, float, fraction, mixed, scientific notation) is pure Go with big.Rat for exact rational arithmetic and 1e-9 float tolerance. Multiple choice is case-insensitive with single-letter matching. Comparison handles operators: > < = >= <= !=. Ordering and tuple graders do positional exact matching.
For symbolic math -- polynomial and expression grading -- Mathua spawns a long-lived Python subprocess running SymPy. The Go client sends a JSON pair over stdin; SymPy parses both into expression trees and checks equivalence via simplify(expected - answer) == 0. If Python/SymPy are not installed, grading falls back gracefully to a pure-Go symbolic string normalizer. The subprocess has a 10-second timeout and 500-character input limit.
The complex grader preprocesses polar form, handles plus-minus notation, and delegates to SymPy. In total there are 8 grading strategies: numeric, multiple choice, comparison, ordering, tuple, complex, symbolic (fallback), and SymPy (polynomial/expression).
V. Computerised Adaptive Testing
The diagnostic engine locates a student's knowledge frontier using binary search on the topologically sorted concept graph. This reduces the assessment from 284 questions (one per concept) to approximately 20-35.
VI. Key Design Decisions
Single ~15MB binary that is both API server and static file server. No runtime dependencies. ~5ms startup vs ~500ms for Python. Goroutines for concurrency. SymPy bridge via os/exec.
Every problem is procedurally generated by a parameterized Go function. Infinite variety, no memorization, adaptive difficulty. Trade-off: generation latency (ns for numeric, ~200ms for SymPy). Worth it.
SQLite for zero-config local development, PostgreSQL for production web. Same schema, same Repository interface. Transparent via DATABASE_URL. The PostgresStore is currently a stub awaiting implementation.
Five tables, straightforward relationships. Raw SQL gives full control over the SM-2 upsert query and leaderboard computation. Transparent debugging. Easy to port between SQLite and PostgreSQL.