{"id":46627848,"url":"https://github.com/karloti/typeahead-kmp","last_synced_at":"2026-04-09T19:24:43.702Z","repository":{"id":342891891,"uuid":"1174629859","full_name":"karloti/typeahead-kmp","owner":"karloti","description":"A high-performance, lock-free, in-memory fuzzy search engine for Kotlin Multiplatform. Built with sparse vectors for zero-latency, typo-tolerant typeahead. Designed to understand human typing behavior with O(1) lookup times.","archived":false,"fork":false,"pushed_at":"2026-03-15T08:30:32.000Z","size":6525,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-15T21:32:11.151Z","etag":null,"topics":["ios","jvm","kmp","kmp-algorithm","kmp-sample","kotlin","kotlin-android","kotlin-library","kotlin-multiplatform","maxosx","multiplatform","multiplatform-kotlin-library","windows"],"latest_commit_sha":null,"homepage":"https://smartcoding.youtrack.cloud/projects/typeahead_kmp","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karloti.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-06T16:57:59.000Z","updated_at":"2026-03-15T20:49:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/karloti/typeahead-kmp","commit_stats":null,"previous_names":["karloti/typeahead-kmp"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/karloti/typeahead-kmp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karloti%2Ftypeahead-kmp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karloti%2Ftypeahead-kmp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karloti%2Ftypeahead-kmp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karloti%2Ftypeahead-kmp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karloti","download_url":"https://codeload.github.com/karloti/typeahead-kmp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karloti%2Ftypeahead-kmp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30552529,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-15T15:03:43.933Z","status":"ssl_error","status_checked_at":"2026-03-15T15:03:37.630Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ios","jvm","kmp","kmp-algorithm","kmp-sample","kotlin","kotlin-android","kotlin-library","kotlin-multiplatform","maxosx","multiplatform","multiplatform-kotlin-library","windows"],"created_at":"2026-03-07T23:08:28.981Z","updated_at":"2026-03-16T02:02:15.789Z","avatar_url":"https://github.com/karloti.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Typeahead KMP Preview](assets/typeahead.png)\n\n# Typeahead KMP\n\nA high-performance, asynchronous, and lock-free in-memory fuzzy search engine designed specifically for Kotlin\nMultiplatform (KMP).\n\nUnlike standard search algorithms that fail during real-time typing, `typeahead-kmp` is built to understand the **\"Blind\nContinuation\" phenomenon**—where users make an early typo but intuitively continue typing the rest of the word\ncorrectly.\n\nPowered by a custom **L2-Normalized Sparse Vector Space** algorithm and immutable state management, it acts as a highly\noptimized, local vector database. It provides `O(1)` lookup times while gracefully handling skipped characters, swapped\nletters, and phonetic typos, yielding a Cosine Similarity score between `0.0` and `1.0`.\n\n---\n\n## Features\n\n* **Zero Network Latency:** Runs entirely on the Edge (the user's device memory), making it perfect for instant UI\n  feedback.\n* **Typo \u0026 Transposition Tolerant:** N-gram and positional embedding techniques catch mistakes that break standard\n  prefix searches.\n* **100% Thread-Safe \u0026 Lock-Free:** Built on `MutableStateFlow` and `PersistentMap` (HAMT), allowing thousands of\n  concurrent reads and writes without blocking threads.\n* **Cold-Start Elimination:** Blazing-fast export and import of pre-computed vector states via JSON serialization.\n* **Cross-Platform:** Write once, run anywhere across JVM, iOS, Android, JS, and Wasm.\n\n---\n\n## Installation\n\nAdd the dependency to your `build.gradle.kts` in the `commonMain` source set:\n\n```kotlin\nkotlin {\n    sourceSets {\n        commonMain.dependencies {\n            implementation(\"io.github.karloti:typeahead-kmp:1.5.0\")  // Replace it with the latest version\n        }\n    }\n}\n```\n\n## Supported Platforms\n\n`typeahead-kmp` is fully written in pure Kotlin with absolutely no platform-specific dependencies (like Foundation on iOS or java.util on JVM for its core logic). This allows it to be compiled and executed seamlessly across all major Kotlin Multiplatform targets:\n\n| Category | Platform | Supported KMP Targets |\n| :--- | :--- | :--- |\n| **Mobile** | Android | `androidTarget` |\n| **Mobile** | iOS | `iosX64`, `iosArm64`, `iosSimulatorArm64` |\n| **Desktop \u0026 Server** | JVM | `jvm` |\n| **Desktop \u0026 Server** | macOS | `macosX64`, `macosArm64` |\n| **Desktop \u0026 Server** | Linux | `linuxX64`, `linuxArm64` |\n| **Desktop \u0026 Server** | Windows | `mingwX64` |\n| **Web** | JavaScript | `js` (with IR compiler) |\n| **Web** | WebAssembly | `wasmJs` |\n\nBecause the engine manages its own memory efficiently via primitive arrays and lock-free concurrency, you will get the exact same deterministic search results and blazing-fast performance regardless of the platform you compile for.\n\n## Quick Start\n\n### 1. Initialization and Indexing\n\nYou can initialize the engine and populate it with your dataset asynchronously:\n\n```kotlin\ndata class Country(val id: Int, val countryName: String)\n\nval searchEngine = TypeaheadSearchEngine(textSelector = Country::countryName)\n\n// Add a single item dynamically\nsearchEngine.add(Country(id = 999, countryName = \"Bulgaria\"))\n\n// Add multiple items in bulk (highly optimized)\nsearchEngine.addAll(listOfCountries)\n```\n\n### 2. Searching with Highlights\n\nRetrieve matches along with a character-level heatmap for UI highlighting:\n\n```kotlin\nval results = searchEngine.findWithHighlights(\"buglaria\", maxResults = 5)\n\nresults.forEach { (country, score, heatmap) -\u003e\n    val highlightedText = heatmap.renderHighlightedString(country.countryName)\n    val formattedScore = score.toString().take(5)\n    println(\" Score: $formattedScore | Match: $highlightedText\")\n}\n```\n\n![visualizing-heatmap](assets/visualizing-heatmap.png)\n\n**Visualizing the Heatmap**: The heatmap array maps each character to a visual tier (e.g., exact match, skipped, wrong),\nallowing you to build beautiful, intuitive UI highlights like the one below:\n\n### 3. Exporting \u0026 Importing State (Eliminating Cold Starts)\n\nAvoid recalculating vectors on the client device by pre-computing them on your backend or during the build process, then\nshipping a static JSON file.\n\n### Exporting:\n\n```kotlin\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.Json\nimport java.io.File\n\n// 1. Export the engine's internal state to a list\nval exportedRecords = searchEngine.exportAsSequence().toList()\n\n// 2. Serialize the records to a JSON string using kotlinx.serialization\nval jsonString = Json.encodeToString(exportedRecords)\n\n// 3. Write the JSON directly to a file\nFile(\"typeahead_vectors.json\").writeText(jsonString)\n```\n\n### Importing (Instant Restore):\n\n```kotlin\nimport kotlinx.serialization.decodeFromString\nimport kotlinx.serialization.json.Json\nimport io.github.karloti.typeahead.TypeaheadRecord\nimport java.io.File\n\n// 1. Read the JSON string from the file\nval jsonFromFile = File(\"typeahead_vectors.json\").readText()\n\n// 2. Deserialize the JSON back into a List of TypeaheadRecord objects\nval deserializedRecords = Json.decodeFromString\u003cList\u003cTypeaheadRecord\u003cCity\u003e\u003e\u003e(jsonFromFile)\n\n// 3. Restore the engine instantly without recalculating mathematical vectors\nsearchEngine.importFromSequence(deserializedRecords.asSequence())\n```\n\n## Real-World Typing Simulation: The \"Cnada\" Problem\n\nTo truly understand the power of `typeahead-kmp`, let's look at a real-time keystroke simulation.\nImagine a user is trying to type **\"Canada\"**, but they accidentally type **\"Cnada\"** (a classic transposition error).\n\nHere is how the engine's internal mathematical weighting dynamically reacts at each keystroke in `O(1)` time:\n\n### Step 1: Initial Input (L2 Normalization \u0026 Short-Word Bias)\n\nAt this early stage, the user types `C` and then `Cn`. The `P0` (First Letter) anchor heavily restricts the search\nspace. Because the input is extremely short, **L2 Normalization** naturally favors shorter words (Short-Word Bias). This\nbrings 4-letter countries like `Cuba` and `Chad` to the top. By the second keystroke, `Canada` barely enters the top 5.\n\n```lua\n=== Typing: 'C' with typing error of 'Cnada' ===\n1. Cuba - Score: 0.19181583900475285\n2. Chad - Score: 0.19181583900475285\n3. China - Score: 0.14776063566992276\n4. Chile - Score: 0.14776063566992276\n5. Cyprus - Score: 0.11811359847672041\n\n=== Typing: 'Cn' with typing error of 'Cnada' ===\n1. Cuba - Score: 0.10297213760008117\n2. Chad - Score: 0.10297213760008117\n...\n5. Canada - Score: 0.07255630308706752\n```\n\n### Step 2: Transposition Recovery (Fuzzy Prefix)\n\nThe user meant `Can` but typed `Cna`. A strict-prefix algorithm would drop \"Canada\" entirely at this exact moment. Our *\n*Fuzzy Prefix** dynamically anchors the first letter (`C`) and alphabetically sorts the remaining characters (`a`, `n`).\nBoth the input `Cna` and the target `Can` generate the exact same spatial feature (`FPR_c_an`). `Canada` instantly\nrockets to the #1 spot!\n\n```lua\n=== Typing: 'Cna' with typing error of 'Cnada' ===\n1. Canada - Score: 0.14257617990546595 \u003c-- Rockets to #1 via Fuzzy Prefix intersection!\n2. Chad - Score: 0.08281542504942256\n3. Cuba - Score: 0.07409801188632545\n4. China - Score: 0.06757216102651037\n5. Chile - Score: 0.05707958943854292\n```\n\n### Step 3: Spellchecker Takeover (Typoglycemia Gestalt)\n\nThe user types `d`. The engine momentarily switches from \"Typeahead Mode\" to \"Spellchecker Mode\" via the **Typoglycemia\nGestalt Anchor**. It detects a 4-letter word starting with `C` and ending with `d`. The algorithm mathematically assumes\nthe user is actively trying to spell `Chad` and applies a massive 15.0 spatial intersection multiplier to that specific\nvector, temporarily overtaking `Canada`.\n\n```lua\n=== Typing: 'Cnad' with typing error of 'Cnada' ===\n1. Chad - Score: 0.1853988462303561 \u003c-- Massive spike due to Gestalt anchor (C...d)!\n2. Canada - Score: 0.1278792484954006\n3. Cuba - Score: 0.07957032027053908\n4. China - Score: 0.04934251382749997\n5. Chile - Score: 0.04168063279838507\n```\n\n### Step 4: Final Resolution (Skip-Grams \u0026 N-Grams)\n\nThe final `a` is typed (length 5). The Gestalt anchor for `Chad` (length 4) completely breaks. The engine reverts to\ndeep structural analysis. Overlapping Skip-Grams seamlessly bridge the transposed letters (`C-n-a-d-a`). This structural\nskeleton perfectly aligns with the core features of `Canada`, accumulating a massive dot-product score that completely\novercomes the length penalty. `Canada` firmly reclaims the #1 spot!\n\n```lua\n=== Typing: 'Cnada' with typing error of 'Cnada' ===\n1. Canada - Score: 0.2563201621199545 \u003c-- Reclaims the lead via deep structural sequence momentum!\n2. China - Score: 0.10623856459894943\n3. Chad - Score: 0.05424611768613351\n4. Grenada - Score: 0.04955129623022677\n5. Chile - Score: 0.047217139821755294\n```\n\n**This dynamic, keystroke-by-keystroke shifting between prefix-matching, gestalt spellchecking, and sequence\nmomentum—all happening in `O(1)` time without memory allocations—is what makes `typeahead-kmp` uniquely powerful for\nhuman-driven inputs.**\n\n![Real-World Typing Simulation: The \"Cnada\" Problem](assets/cnada-problem.gif)\n\n_This dynamic, keystroke-by-keystroke shifting between prefix-matching, gestalt spellchecking, and sequence momentum—all\nhappening without memory allocations—is what makes this engine uniquely powerful._\n\n## Beyond UI: CLI Fuzzy File Finder\n\nWhile `typeahead-kmp` is heavily optimized for real-time mobile and web UIs, its underlying L2-normalized sparse vector\nengine is highly versatile and can be applied to backend utilities and Command Line Interfaces (CLIs).\n\nIncluded in the repository is a practical example of a **CLI Fuzzy File Search**. When navigating deeply nested\ndirectory structures, it is incredibly common to mistype a filename. Instead of a frustrating \"File not found\" error,\nthis tool leverages the typeahead engine to act as a resilient safety net, instantly suggesting the closest possible\nmatches.\n\n![CLI Search Example](assets/findme.png)\n\n### How It Works\n\n1. **Recursive Indexing**: The script recursively traverses the local file system (skipping hidden directories like\n   `.git`), feeding file names into the `TypeaheadSearchEngine` while storing their absolute paths as the associated\n   payload.\n2. **Intelligent Fallback**: The CLI first checks for an exact match. If the user makes a typo (e.g., typing `Typeahed`\n   instead of `Typeahead`), the exact search fails, and the engine immediately falls back to its vector space search to\n   find the nearest neighbors.\n3. **Visual Heatmap Highlighting**: This example showcases the true power of the engine's `findWithHighlights()` API.\n   Instead of just returning a score, the engine returns an `IntArray` spatial heatmap for each match. The CLI uses this\n   data to render color-coded terminal output:\n\n* 🟩 **Solid Prefix**: Exact starting matches.\n* 🟨 **Floating N-Gram**: Contiguous blocks of characters found in the middle of the word.\n* 🟦 **Skip-Gram**: Scattered, individual character matches (the \"fuzzy bridge\").\n* ⬜ **Unmatched**: Characters that were skipped or mistyped.\n\nThis demonstrates how the internal scoring mechanism can be directly piped into UI rendering logic—whether that's a\nterminal output or styled text in Jetpack Compose / SwiftUI.\n\n## The Evolution: Why standard algorithms fail\n\nBuilding a perfect typeahead engine is notoriously difficult. During the development of this library, we evaluated and\ndiscarded several standard approaches because they fundamentally misalign with human typing behavior.\n\n## The Problem with Server-Side Giants (Algolia, Typesense)\n\nWhile engines like **Algolia** and **Typesense** are industry standards for massive databases, they require network\nrequests. In mobile or web front-ends, **network latency kills the instant \"typeahead\" feel**. `typeahead-kmp` brings\nvector-search intelligence directly to the client.\n\n## The Problem with Traditional Algorithms\n\nBuilding a perfect typeahead engine requires balancing real-time UI performance with human typing psychology. During the\ndevelopment of this library, we evaluated, implemented, and ultimately discarded several standard algorithmic approaches\nbecause they fundamentally misalign with the constraints of mobile environments (60fps UI rendering, memory\nfragmentation, Garbage Collection pauses) or human behavior.\n\n- **Strict Prefix Tries / Radix Trees**: Extremely fast (`O(L)` where L is query length) and highly memory-efficient.\n  However, they offer absolutely zero typo tolerance. A single transposed or missed character instantly shatters the\n  search path, frustrating users who type quickly on glass screens.\n\n- **Levenshtein \u0026 Damerau-Levenshtein Distance**: The industry standard for spelling correction. Unfortunately, these\n  operate with `O(N*M)` time complexity via dynamic programming matrices. Running this math across thousands of records\n  on every single keystroke quickly blocks the Main UI thread on mobile devices, causing frame drops and stuttering.\n  Furthermore, they heavily penalize string length differences and fail completely at the \"Blind Continuation\"\n  phenomenon (where an early typo derails the entire score, even if the rest of the word is typed perfectly).\n\n- **Jaro-Winkler Similarity**: Specifically designed to give heavy weight to matching prefixes, making it seemingly\n  ideal for autocomplete. However, it still suffers from `O(N*M)` CPU bottlenecks. More importantly, its strict prefix\n  anchor means that if the user makes a mistake in the very first or second character, the similarity score degrades\n  drastically, destroying the typeahead experience.\n\n- **Weighted Longest Common Subsequence (LCS)**: While applying index-based positional weights to an LCS algorithm\n  improves accuracy for scattered keystrokes and dropped characters, it still requires complex matrix backtracking. This\n  mathematical overhead is simply too slow for asynchronous, per-keystroke rendering.\n\n- **Standard N-Grams \u0026 Jaccard / Cosine Sets**: Breaking strings into overlapping chunks (N-grams) solves the typo and\n  transposition problems beautifully. However, traditional implementations rely heavily on creating massive amounts of\n  intermediate `String` objects or `Set` collections. On mobile (JVM/ART or Kotlin Native), this triggers aggressive\n  Garbage Collection (GC) pauses, leading to UI jitter. Additionally, standard sets treat words as bags-of-tokens,\n  completely losing the critical \"Prefix Anchor\" (the fact that the beginning of a word matters more than the end).\n\n- **Ratcliff-Obershelp \u0026 SIFT4**: While experimental algorithms like SIFT4 simulate human perception and operate in\n  linear time (`O(max(N,M))`), they lack the structural optimizations required for concurrent environments. They don't\n  natively integrate with lock-free data structures, making them difficult to scale across highly concurrent\n  reader/writer coroutines without blocking.\n\n- **In-Memory Dense Vector Databases**: Using traditional dense embeddings (like standard AI vector DBs) provides\n  excellent semantic and fuzzy matching (`O(log N)` search times). However, the memory footprint is massive. Inserting,\n  updating, and holding full dense vectors in RAM is computationally expensive and battery-draining, making them\n  absolute overkill for purely syntactic typeahead.\n\n### The Solution: Typeahead KMP\n\nTo solve these compounding issues, `typeahead-kmp` abandons traditional string-to-string comparisons during the search\nphase.\n\nInstead, it functions as a highly specialized, local vector database:\n\n1. **Zero-Allocation Math**: We use an **L2-Normalized Sparse Vector Space**. Vectors are represented by parallel,\n   primitive `FloatArray` structures, strictly halving memory footprints and completely eliminating object fragmentation\n   and GC pauses.\n2. **`O(K)` Search Complexity**: Because vectors are pre-computed and alphabetically sorted, the core search reduces to\n   an ultra-fast, two-pointer dot-product intersection.\n3. **Lock-Free Concurrency**: Utilizing immutable state (`PersistentMap`) and atomic Compare-And-Swap (CAS) operations\n   via a custom `BoundedConcurrentPriorityQueue`, the engine handles thousands of parallel reads and writes without ever\n   locking a thread.\n4. **Human-Centric Scoring**: By combining positional weighting (P0 Anchors) with N-gram tokenization, the engine\n   seamlessly handles typos, transpositions, and the \"Blind Continuation\" effect, dynamically yielding a precise Cosine\n   Similarity score (`0.0` to `1.0`) in milliseconds.\n\n## Algorithm Comparison\n\n| Algorithm                            | Typo Tolerance  |  Prefix Anchor  |    Memory Cost     | Blind Continuation |  Search Complexity  |\n|:-------------------------------------|:---------------:|:---------------:|:------------------:|:------------------:|:-------------------:|\n| **Strict Prefix Tries**              |     ❌ None      |    ✅ Perfect    |       ✅ Low        |      ❌ Fails       |       `O(L)`        |\n| **Levenshtein Distance**             |     ✅ Good      |     ❌ Poor      |       ✅ Low        |       ❌ Poor       |      `O(N*M)`       |\n| **Weighted \u0026 Damerau-Levenshtein**   |   ✅ Excellent   |     ❌ Poor      |     ⚠️ Medium      |       ❌ Poor       |      `O(N*M)`       |\n| **Jaro-Winkler**                     |     ✅ Good      |   ✅ Excellent   |       ✅ Low        |       ❌ Poor       |      `O(N*M)`       |\n| **Longest Common Subsequence (LCS)** |     ✅ Good      |   ⚠️ Moderate   |     ⚠️ Medium      |    ⚠️ Moderate     |      `O(N*M)`       |\n| **Standard N-Grams \u0026 Q-Grams**       |     ✅ Good      |     ❌ Poor      |      ⚠️ High       |       ✅ Good       |      `O(N+M)`       |\n| **Cosine / Jaccard / Dice (Sets)**   |     ✅ Good      |     ❌ Poor      |      ⚠️ High       |       ✅ Good       |      `O(N+M)`       |\n| **Ratcliff-Obershelp**               |   ⚠️ Moderate   |     ❌ Poor      |     ⚠️ Medium      |    ⚠️ Moderate     | `O(N³)` ~ `O(N*M)`  |\n| **SIFT4 (Experimental)**             |   ✅ Excellent   |   ⚠️ Moderate   |       ✅ Low        |       ✅ Good       |    `O(max(N,M))`    |\n| **Dense Vector DBs**                 |   ✅ Excellent   |     ❌ Poor      |     ❌ Massive      |    ✅ Excellent     |     `O(log N)`      |\n| **Typeahead KMP (Ours)**             | **✅ Excellent** | **✅ Excellent** | **✅ Low (Sparse)** |  **✅ Excellent**   | **`O(K)` ~ `O(1)`** |\n\n_(Note: K represents the number of non-zero overlapping features, which is strictly bounded by the string length,\nrendering the search time effectively O(1) relative to the total dataset size)._\n\n---\n\n## Performance \u0026 Architecture\n\nThe engine is engineered for environments with strict memory and CPU constraints:\n\n- **Memory Efficiency**: Uses primitive parallel arrays (`FloatArray` and `IntArray`) internally instead of object\n  wrappers, cutting memory footprint by more than 50%.\n\n- **Top-K Retrieval**: Built on top of a custom `BoundedConcurrentPriorityQueue` that discards low-priority matches\n  instantly without allocation.\n\n- **Concurrency**: Uses a Compare-And-Swap (CAS) approach via `MutableStateFlow` and Kotlin's `PersistentMap`. This\n  ensures that even if you hammer the engine with 10,000 concurrent `.add()` operations, no data is lost and the thread\n  is never blocked.\n\n## Performance, Memory \u0026 Cold-Start Elimination\n\nTo demonstrate the engine's efficiency under heavy loads, we run an aggressive benchmark indexing **10,000 complex\nproduct records** (resulting in an 84 MB JSON export).\n\nBecause string tokenization and mathematical L2-normalization are computationally heavy, the initial insertion takes\nsome CPU time and memory. However, **importing pre-computed vectors bypasses all mathematical operations**, resulting in\na near-instant cold-start with virtually zero memory bloat.\n\n## Benchmark Output (JVM):\n\n```lua\n================================================================================\nPERFORMANCE SUMMARY (10,000 Records)\n================================================================================\nInsertion:        419 ms | Memory: 114 MB  \u003c-- Heavy mathematical tokenization\nExport:           7 ms   | Memory: 1 MB    \u003c-- Instant state read via PersistentMap\nSerialization:    432 ms \nFile Write:       117 ms | Disk: 84 MB\nFile Read:        108 ms\nDeserialization:  428 ms | Memory: 192 MB\nImport (Restore): 9 ms   | Memory: 5 MB    \u003c-- Bypasses math; instant state hydration!\n================================================================================\nTotal Time:       1520 ms\n================================================================================\n✅ Import verification completed successfully - all results are identical!\n```\n\n### Key Takeaways:\n\n- **Zero-Cost Reads**: Exporting the current state takes only `7 ms` because the internal `PersistentMap` allows instant\n  iteration without locking the data structure.\n\n- **Lightning-Fast Hydration**: Restoring 10,000 records takes only `9 ms` and `5 MB` of RAM. By generating the JSON\n  index on your backend or during CI/CD, you can ship it to mobile clients for instant search availability on app\n  launch.\n\n---\n\n## Tracking \u0026 Roadmap\n\nWe use YouTrack for task management and issue tracking.\nYou can view the current tasks and progress here:\n[Typeahead KMP Issues \u0026 Roadmap](https://smartcoding.youtrack.cloud/projects/typeahead_kmp)\n\n## License\n\nThis project is licensed under the **Apache License Version 2.0** - see the [LICENSE](LICENSE) file for details.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarloti%2Ftypeahead-kmp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarloti%2Ftypeahead-kmp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarloti%2Ftypeahead-kmp/lists"}