AG
TR

// writing / May 5, 2026

Letter counts and Abjad totals: verifying a Quranic engine letter-by-letter

Mizan is a Quranic text-analysis and hybrid-search engine where every count is verified against the Tanzil corpus at load time and every result reports the counting method and model used.

Mizan is a hybrid search and text-analysis engine for the Quran. I built it in Python 3.11 with FastAPI, SQLAlchemy (async), PostgreSQL + pgvector (HNSW index), and Redis 7 on the backend, and Next.js 16 / React 19 / Tailwind on the front end. It is deployed on a Hetzner server at mizan.rollingcatsoftware.com (front end) and mizan-api.rollingcatsoftware.com (API). The source is at github.com/ahmetabdullahgultekin/Mizan.

The retriever runs four parallel paths fused with Reciprocal Rank Fusion (RRF): three pgvector cosine-similarity paths (Arabic, English, and Turkish verses, all embedded with intfloat/multilingual-e5-base at 768 dimensions) and one PostgreSQL tsvector/GIN keyword path with an ISRI Arabic stemmer. The ISRI stemmer is a pure-Python implementation included directly in the repository, not an external package. It reduces morphological variants so a query for “patience” can match the Arabic root even when it surfaces in inflected forms. An optional cross-encoder reranker (ms-marco-MiniLM) sits behind a feature flag for cases where precision matters more than latency.

The part that required the most care was the letter-counting and Abjad engine. Letter counts circulate widely in Islamic scholarship and are frequently cited without any indication of which script (Uthmani vs Imla’i), which counting convention (whether alif wasla counts as a letter, for instance), or which corpus version was used. Mizan exposes three explicit counting methods and requires the caller to pick one; there is no ambient default that silently makes the choice. The Abjad calculator similarly exposes both Mashriqi (Eastern) and Maghribi (Western) letter-value orderings, since the two systems disagree on several letters. The corpus is SHA-checked at load time: if the file on disk has been modified, the engine refuses to start. Every API response includes the counting method and the embedding model used to produce it, so the numbers are reproducible.

The test suite covers counting correctness against pinned Tanzil corpus values, SHA-based corpus integrity, verse navigation round-trips, and multi-script API responses (Uthmani, Imla’i, Uthmani-min). Pinned invariants include Al-Fatiha letter count (139, TRADITIONAL method), Basmalah letter count (19), and Basmalah Abjad total (786, Mashriqi). If any of these fail, the build fails. That constraint forced every design decision in the counter to be explicit and documented rather than implicit.

  • nlp
  • arabic
  • testing
  • reproducibility