<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Data-Structure on CuriousCoding</title><link>https://curiouscoding.nl/tags/data-structure/</link><description>Recent content in Data-Structure on CuriousCoding</description><generator>Hugo</generator><language>en</language><lastBuildDate>Mon, 02 Mar 2026 00:00:00 +0100</lastBuildDate><atom:link href="https://curiouscoding.nl/tags/data-structure/index.xml" rel="self" type="application/rss+xml"/><item><title>Route Planning using Customizable Contraction Hierarchies</title><link>https://curiouscoding.nl/posts/cch/</link><pubDate>Mon, 02 Mar 2026 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/cch/</guid><description>&lt;div class="ox-hugo-toc toc has-section-numbers"&gt;
&lt;div class="heading"&gt;Table of Contents&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1&lt;/span&gt; &lt;a href="#problem-statement-customizable-route-planning--crp" &gt;Problem Statement: Customizable Route Planning (CRP)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2&lt;/span&gt; &lt;a href="#contraction-hierarchies--ch" &gt;Contraction Hierarchies (CH)&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.1&lt;/span&gt; &lt;a href="#classic-contraction-hierarchies" &gt;&lt;em&gt;Classic&lt;/em&gt; Contraction Hierarchies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.2&lt;/span&gt; &lt;a href="#customizable-contraction-hierarchies--cch" &gt;&lt;em&gt;Customizable&lt;/em&gt; Contraction Hierarchies (CCH)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3&lt;/span&gt; &lt;a href="#analogy-with-trees" &gt;Analogy with trees&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4&lt;/span&gt; &lt;a href="#shortest-paths-in-chs" &gt;Shortest Paths in CHs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5&lt;/span&gt; &lt;a href="#parents-faster-shortest-paths-in-cchs" &gt;Parents: Faster Shortest Paths in CCHs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;6&lt;/span&gt; &lt;a href="#input-graph" &gt;Input Graph&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7&lt;/span&gt; &lt;a href="#initial-algorithm" &gt;Initial Algorithm&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.1&lt;/span&gt; &lt;a href="#permute-input" &gt;Permute input&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.2&lt;/span&gt; &lt;a href="#chordal-completion-and-parents" &gt;Chordal Completion and Parents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.3&lt;/span&gt; &lt;a href="#customize" &gt;Customize&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.4&lt;/span&gt; &lt;a href="#query" &gt;Query&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8&lt;/span&gt; &lt;a href="#optimizing-things" &gt;Optimizing things&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.1&lt;/span&gt; &lt;a href="#binary-searching-in-find-edge" &gt;Binary searching in &lt;code&gt;find_edge&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.2&lt;/span&gt; &lt;a href="#hashmap-of-edges" &gt;&lt;code&gt;HashMap&lt;/code&gt; of edges&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.3&lt;/span&gt; &lt;a href="#ranges-of-neighbours" &gt;Ranges of neighbours&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.4&lt;/span&gt; &lt;a href="#linear-scan" &gt;Linear scan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.5&lt;/span&gt; &lt;a href="#proper-query-algorithm" &gt;Proper query algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.6&lt;/span&gt; &lt;a href="#pruning-edges" &gt;Pruning edges&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.7&lt;/span&gt; &lt;a href="#pruning" &gt;Pruning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.8&lt;/span&gt; &lt;a href="#unconditional-edge-relaxing" &gt;Unconditional edge relaxing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.9&lt;/span&gt; &lt;a href="#early-edge-break" &gt;Early edge break&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.10&lt;/span&gt; &lt;a href="#dfs-ordering-the-nodes" &gt;DFS-ordering the nodes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.11&lt;/span&gt; &lt;a href="#not-inclining-queries" &gt;Not inclining queries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;9&lt;/span&gt; &lt;a href="#some-stats" &gt;Some stats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;10&lt;/span&gt; &lt;a href="#serializing-the-final-structure" &gt;Serializing the final structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;11&lt;/span&gt; &lt;a href="#merging-adjacent-edges" &gt;Merging adjacent edges&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;11.1&lt;/span&gt; &lt;a href="#perf-stat" &gt;Perf stat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;11.2&lt;/span&gt; &lt;a href="#all-ranges-are-multiples-of-8" &gt;All ranges are multiples of 8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;11.3&lt;/span&gt; &lt;a href="#all-ranges-have-size-8" &gt;All ranges have size 8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;11.4&lt;/span&gt; &lt;a href="#finding-the-bottleneck" &gt;Finding the bottleneck&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;12&lt;/span&gt; &lt;a href="#bugfixing" &gt;Bugfixing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;13&lt;/span&gt; &lt;a href="#further-ideas" &gt;Further ideas&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;13.1&lt;/span&gt; &lt;a href="#failed-doubling-the-graph" &gt;Failed: Doubling the graph&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;13.2&lt;/span&gt; &lt;a href="#edge-pruning" &gt;Edge pruning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;13.3&lt;/span&gt; &lt;a href="#failed-expanding-a-node-and-its-parent-in-parallel" &gt;Failed: Expanding a node and its parent in parallel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;14&lt;/span&gt; &lt;a href="#current-best-results" &gt;Current best results&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;14.1&lt;/span&gt; &lt;a href="#bottleneck" &gt;Bottleneck&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;15&lt;/span&gt; &lt;a href="#d41d8c" &gt;&lt;span class="org-todo todo TODO"&gt;TODO&lt;/span&gt; &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;p&gt;These are some notes on &lt;em&gt;customizable contraction hierarchies&lt;/em&gt;, based on talks
with Michael Zündorf and the survey paper by Bläsius, Buchhold, Wagner, Zeitz, and Zündorf (&lt;a href="#citeproc_bib_item_1"&gt;2025&lt;/a&gt;).&lt;/p&gt;</description></item><item><title>Wheeler graphs</title><link>https://curiouscoding.nl/posts/wheeler-graphs/</link><pubDate>Thu, 26 Feb 2026 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/wheeler-graphs/</guid><description>&lt;div class="ox-hugo-toc toc has-section-numbers"&gt;
&lt;div class="heading"&gt;Table of Contents&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1&lt;/span&gt; &lt;a href="#deterministic-finite-automaton--dfa" &gt;Deterministic Finite Automaton (DFA)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2&lt;/span&gt; &lt;a href="#wheeler-dfa" &gt;Wheeler-DFA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3&lt;/span&gt; &lt;a href="#linear-graphs-prefix-array" &gt;Linear graphs: Prefix array&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4&lt;/span&gt; &lt;a href="#de-bruijn-graphs-are-wheeler" &gt;De Bruijn graphs are Wheeler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5&lt;/span&gt; &lt;a href="#not-all-dfas-are-wheeler" &gt;Not all DFAs are Wheeler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;6&lt;/span&gt; &lt;a href="#locating-patterns-via-binary-search" &gt;Locating patterns via binary search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7&lt;/span&gt; &lt;a href="#locating-patterns-via-the-boss-table" &gt;Locating patterns via the BOSS table&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;p&gt;These are some notes on Wheeler DFAs after chatting with Nicola
Prezza&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; and others
from the RAVEN lab at DSB 2026 in Venice.&lt;/p&gt;
&lt;h3 id="deterministic-finite-automaton--dfa"&gt;
 &lt;span class="section-num"&gt;1&lt;/span&gt; Deterministic Finite Automaton (DFA)
 &lt;a class="heading-link" href="#deterministic-finite-automaton--dfa"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;A DFA is a graph where edges are labelled by characters.
Each node can have at most one outgoing edge with each label. (Otherwise it
would be &lt;em&gt;non-deterministic&lt;/em&gt;.)&lt;/p&gt;</description></item><item><title>QuadRank: Engineering a High-Throughput Rank</title><link>https://curiouscoding.nl/posts/quadrank-slides/</link><pubDate>Wed, 18 Feb 2026 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/quadrank-slides/</guid><description>&lt;script src="https://curiouscoding.nl/livereload.js?mindelay=10&amp;amp;v=2&amp;amp;port=1313&amp;amp;path=livereload" data-no-instant defer&gt;&lt;/script&gt;
&lt;h2 id="problem-statement"&gt;
 Binary Rank: Problem statement
 &lt;a class="heading-link" href="#problem-statement"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;div display="none"&gt;
&lt;ul&gt;
&lt;li&gt;Input: a many-GB text \(T = t_0\dots t_{n-1}\) of \(n\) bits.&lt;/li&gt;
&lt;li&gt;Queries: given \(q\), find&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;\begin{equation*}
\newcommand{\rank}{\mathsf{rank}}
\rank(q) := \sum_{0\leq i&amp;lt; q} t_i.
\end{equation*}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;\(T = \underline{\texttt{1001001}}\texttt{110010100}\)
&lt;ul&gt;
&lt;li&gt;\(\rank(0) = 0\)&lt;/li&gt;
&lt;li&gt;\(\rank(7) = 3\)&lt;/li&gt;
&lt;li&gt;\(\rank(16) = 7\)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Why? Occurrences table in FM-index.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="history"&gt;
 History
 &lt;a class="heading-link" href="#history"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;figure class="full post-large"&gt;&lt;img src="https://curiouscoding.nl/ox-hugo/rank-overview.svg"&gt;
&lt;/figure&gt;

&lt;h2 id="naive"&gt;
 Naive solutions
 &lt;a class="heading-link" href="#naive"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Linear scan:
&lt;ul&gt;
&lt;li&gt;\(O(n/w)\) time using \(w\) bit popcount, no space overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="post-large"&gt;&lt;img src="https://curiouscoding.nl/ox-hugo/1-linear.svg"&gt;
&lt;/figure&gt;

&lt;ul&gt;
&lt;li&gt;Precompute all 64-bit answers \(r_i := \rank(i)\):
&lt;ul&gt;
&lt;li&gt;\(O(1)\) time, \(64\times\) overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="post-large"&gt;&lt;img src="https://curiouscoding.nl/ox-hugo/2-precompute.svg"&gt;
&lt;/figure&gt;

&lt;h2 id="blocks"&gt;
 Middle-ground: block-based offsets
 &lt;a class="heading-link" href="#blocks"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Use \(B=512\) bit blocks, and store all \(b_j := \rank(j\cdot B)\).&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="post-large"&gt;&lt;img src="https://curiouscoding.nl/ox-hugo/3-blocks.svg"&gt;
&lt;/figure&gt;

&lt;ul&gt;
&lt;li&gt;Query \(q = j\cdot B + q&amp;rsquo;\):
\(\rank(q) = b_j + \sum_{jB\leq i &amp;lt; jB+q&amp;rsquo;} t_i\).&lt;/li&gt;
&lt;li&gt;\(O(B/w) = O(512/64) = O(1)\) time.&lt;/li&gt;
&lt;li&gt;\(64/512 = 12.5\%\) space overhead.&lt;/li&gt;
&lt;li&gt;2 cache misses:
&lt;ul&gt;
&lt;li&gt;in \(n/8\) bit array: offset \(b_j\)&lt;/li&gt;
&lt;li&gt;in \(n\) bit array: block \(j\) bits&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="levels"&gt;
 Reducing overhead: PastaWide [and others]
 &lt;a class="heading-link" href="#levels"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;2 levels:
&lt;ul&gt;
&lt;li&gt;L2 with 16-bit &lt;em&gt;delta&lt;/em&gt; every block: 3.125% overhead&lt;/li&gt;
&lt;li&gt;L1 with 64-bit &lt;em&gt;offset&lt;/em&gt; every 128 blocks: 0.1% overhead&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="post-large"&gt;&lt;img src="https://curiouscoding.nl/ox-hugo/4-pasta.svg"&gt;
&lt;/figure&gt;

&lt;h2 id="spider"&gt;
 Reducing cache misses: SPIDER
 &lt;a class="heading-link" href="#spider"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Inline the 16-bit L2 deltas bits into each cache line
&lt;ul&gt;
&lt;li&gt;Remaining 0.1% overhead L1 array fits in cache.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="post-large"&gt;&lt;img src="https://curiouscoding.nl/ox-hugo/5-spider.svg"&gt;
&lt;/figure&gt;

&lt;h2 id="pairing"&gt;
 Reducing the popcount: Pairing
 &lt;a class="heading-link" href="#pairing"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Delta is to the &lt;em&gt;middle&lt;/em&gt; instead of &lt;em&gt;start&lt;/em&gt; of a block.
&lt;ul&gt;
&lt;li&gt;Count only 256 bits in first or second half of block.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="post-large"&gt;&lt;img src="https://curiouscoding.nl/ox-hugo/6-pairing.svg"&gt;
&lt;/figure&gt;

&lt;h2 id="birank"&gt;
 All together now: BiRank
 &lt;a class="heading-link" href="#birank"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Inline 16-bit deltas&lt;/li&gt;
&lt;li&gt;Pairing&lt;/li&gt;
&lt;li&gt;32-bit &lt;em&gt;reduced&lt;/em&gt; L1 offsets: 0.05% overhead
&lt;ul&gt;
&lt;li&gt;Low 11 bits are stored in deltas&lt;/li&gt;
&lt;li&gt;Input up to \(2^{43}\) bits&lt;/li&gt;
&lt;li&gt;16 MiB cache supports 32 GiB input&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="post-large"&gt;&lt;img src="https://curiouscoding.nl/ox-hugo/7-birank.svg"&gt;
&lt;/figure&gt;

&lt;h2 id="metric"&gt;
 What is &lt;em&gt;fast&lt;/em&gt;?
 &lt;a class="heading-link" href="#metric"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Latency: 80 ns/q&lt;/p&gt;</description></item><item><title>QuadRank: Engineering a High Throughput Rank</title><link>https://curiouscoding.nl/posts/quadrank/</link><pubDate>Thu, 29 Jan 2026 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/quadrank/</guid><description>&lt;div class="ox-hugo-toc toc has-section-numbers"&gt;
&lt;div class="heading"&gt;Table of Contents&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#abstract" &gt;Abstract&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1&lt;/span&gt; &lt;a href="#introduction" &gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2&lt;/span&gt; &lt;a href="#background" &gt;Background&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.1&lt;/span&gt; &lt;a href="#implementations" &gt;Further implementations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3&lt;/span&gt; &lt;a href="#birank" &gt;BiRank&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.1&lt;/span&gt; &lt;a href="#variants" &gt;Variants&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4&lt;/span&gt; &lt;a href="#quadrank" &gt;QuadRank&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4.1&lt;/span&gt; &lt;a href="#variants" &gt;Variants&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5&lt;/span&gt; &lt;a href="#results" &gt;Results&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5.1&lt;/span&gt; &lt;a href="#evals-birank" &gt;BiRank&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5.2&lt;/span&gt; &lt;a href="#evals-quadrank" &gt;QuadRank&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;6&lt;/span&gt; &lt;a href="#conclusion" &gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7&lt;/span&gt; &lt;a href="#acknowledgements" &gt;Acknowledgements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8&lt;/span&gt; &lt;a href="#snippets" &gt;Code snippets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;9&lt;/span&gt; &lt;a href="#pairing-math" &gt;Pairing superblocks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;10&lt;/span&gt; &lt;a href="#additional-results" &gt;Additional results&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;10.1&lt;/span&gt; &lt;a href="#small-n" &gt;Throughput for small inputs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;10.2&lt;/span&gt; &lt;a href="#epyc" &gt;AMD EPYC evals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;11&lt;/span&gt; &lt;a href="#fm-index" &gt;QuadFm: A Batching FM-index&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;11.1&lt;/span&gt; &lt;a href="#results" &gt;Results&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;p&gt;\[\newcommand{\rank}{\mathsf{rank}}
\newcommand{\rankone}{\mathsf{rank}}
\newcommand{\rankall}{\mathsf{rank_4}}\]&lt;/p&gt;
&lt;p&gt;This is the text for the QuadRank paper (&lt;a href="#citeproc_bib_item_19"&gt;Groot Koerkamp 2026&lt;/a&gt;) (&lt;a href="https://curiouscoding.nl/papers/quadrank-preprint.pdf" &gt;PDF&lt;/a&gt;, &lt;a href="https://doi.org/10.48550/ARXIV.2602.04103" class="external-link" target="_blank" rel="noopener"&gt;arxiv&lt;/a&gt;).
See also the
&lt;a href="https://curiouscoding.nl/slides/quadrank" &gt;reveal-js slides&lt;/a&gt;, &lt;a href="../quadrank-slides/" &gt;plain slide content&lt;/a&gt;, and the &lt;a href="https://github.com/RagnarGrootKoerkamp/quadrank" class="external-link" target="_blank" rel="noopener"&gt;github code&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Recent results on hash tables</title><link>https://curiouscoding.nl/posts/hash-table-bounds/</link><pubDate>Wed, 28 Jan 2026 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/hash-table-bounds/</guid><description>&lt;div class="ox-hugo-toc toc has-section-numbers"&gt;
&lt;div class="heading"&gt;Table of Contents&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1&lt;/span&gt; &lt;a href="#types-of-hash-tables" &gt;Types of hash tables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2&lt;/span&gt; &lt;a href="#metrics" &gt;Metrics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3&lt;/span&gt; &lt;a href="#lower-bounds" &gt;Lower bounds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4&lt;/span&gt; &lt;a href="#iceberg-hashing--2023" &gt;Iceberg hashing (2023)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5&lt;/span&gt; &lt;a href="#iceberg-ht" &gt;IcebergHT (2023)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;6&lt;/span&gt; &lt;a href="#tight-bounds-for-classical-open-addressing--2024" &gt;Tight Bounds for Classical Open Addressing (2024)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7&lt;/span&gt; &lt;a href="#optimal-non-oblivious-open-addressing--2025" &gt;Optimal Non-oblivious Open Addressing (2025)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8&lt;/span&gt; &lt;a href="#optimal-bounds-for-open-addressing-without-reordering" &gt;Optimal Bounds for Open Addressing Without Reordering&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.1&lt;/span&gt; &lt;a href="#funnel-hashing" &gt;Funnel hashing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;8.2&lt;/span&gt; &lt;a href="#elastic-hashing" &gt;Elastic hashing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;p&gt;\[
\newcommand{\inv}{^{-1}}
\newcommand{\di}{\delta\inv}
\newcommand{\poly}{\mathrm{poly}}
\]&lt;/p&gt;
&lt;p&gt;This post summarizes some recent results and idea on various types hash tables.
Collected together with Stefan Walzer.&lt;/p&gt;</description></item><item><title>Elias-Fano</title><link>https://curiouscoding.nl/teaching/elias-fano/</link><pubDate>Sun, 25 Jan 2026 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/teaching/elias-fano/</guid><description>&lt;h2 id="references"&gt;
 References
 &lt;a class="heading-link" href="#references"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;style&gt;.csl-entry{text-indent: -1.5em; margin-left: 1.5em;}&lt;/style&gt;&lt;div class="csl-bib-body"&gt;
&lt;/div&gt;</description></item><item><title>Range-minimum queries</title><link>https://curiouscoding.nl/teaching/rmq/</link><pubDate>Sun, 25 Jan 2026 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/teaching/rmq/</guid><description>&lt;h2 id="references"&gt;
 References
 &lt;a class="heading-link" href="#references"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;style&gt;.csl-entry{text-indent: -1.5em; margin-left: 1.5em;}&lt;/style&gt;&lt;div class="csl-bib-body"&gt;
&lt;/div&gt;</description></item><item><title>Rank</title><link>https://curiouscoding.nl/teaching/rank/</link><pubDate>Sun, 25 Jan 2026 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/teaching/rank/</guid><description>&lt;h2 id="references"&gt;
 References
 &lt;a class="heading-link" href="#references"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;style&gt;.csl-entry{text-indent: -1.5em; margin-left: 1.5em;}&lt;/style&gt;&lt;div class="csl-bib-body"&gt;
&lt;/div&gt;</description></item><item><title>Select</title><link>https://curiouscoding.nl/teaching/select/</link><pubDate>Sun, 25 Jan 2026 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/teaching/select/</guid><description>&lt;h2 id="references"&gt;
 References
 &lt;a class="heading-link" href="#references"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;style&gt;.csl-entry{text-indent: -1.5em; margin-left: 1.5em;}&lt;/style&gt;&lt;div class="csl-bib-body"&gt;
&lt;/div&gt;</description></item><item><title>Overview of static data structures</title><link>https://curiouscoding.nl/posts/static-data-structures/</link><pubDate>Wed, 17 Dec 2025 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/static-data-structures/</guid><description>&lt;div class="ox-hugo-toc toc has-section-numbers"&gt;
&lt;div class="heading"&gt;Table of Contents&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1&lt;/span&gt; &lt;a href="#classification-of-static-data-structures" &gt;Classification of static data structures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2&lt;/span&gt; &lt;a href="#space-lower-bounds-and-practical-approaches" &gt;Space lower bounds and practical approaches&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.1&lt;/span&gt; &lt;a href="#rank" &gt;Rank&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.2&lt;/span&gt; &lt;a href="#rank-plus-select" &gt;Rank + Select&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.3&lt;/span&gt; &lt;a href="#minimal-perfect-hash-function--mphf" &gt;Minimal perfect hash function (MPHF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.4&lt;/span&gt; &lt;a href="#monotone-mphf" &gt;&lt;span class="org-todo todo TODO"&gt;TODO&lt;/span&gt; Monotone MPHF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.5&lt;/span&gt; &lt;a href="#order-preserving-mphf" &gt;&lt;span class="org-todo todo TODO"&gt;TODO&lt;/span&gt; Order-preserving MPHF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.6&lt;/span&gt; &lt;a href="#static-retrieval-static-function-with-static-values" &gt;Static retrieval: Static function with static values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.7&lt;/span&gt; &lt;a href="#updatable-retrieval-static-function-with-mutable-values" &gt;Updatable retrieval: Static function with mutable values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.8&lt;/span&gt; &lt;a href="#static-set--membership" &gt;Static set (membership)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.9&lt;/span&gt; &lt;a href="#static-ordered-set" &gt;Static ordered set&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.10&lt;/span&gt; &lt;a href="#static-dictionary-static-keys-and-values" &gt;Static dictionary: static keys and values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.11&lt;/span&gt; &lt;a href="#updatable-dictionary-with-mutable-values" &gt;Updatable dictionary with mutable values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.12&lt;/span&gt; &lt;a href="#dynamic-dictionary-with-mutable-keys-and-values" &gt;&lt;span class="org-todo todo TODO"&gt;TODO&lt;/span&gt; Dynamic dictionary with mutable keys and values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.13&lt;/span&gt; &lt;a href="#static-filter" &gt;&lt;span class="org-todo todo TODO"&gt;TODO&lt;/span&gt; Static filter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.14&lt;/span&gt; &lt;a href="#ordered-static-updatable-dynamic-dictionary" &gt;&lt;span class="org-todo todo TODO"&gt;TODO&lt;/span&gt; Ordered static/updatable/dynamic dictionary?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3&lt;/span&gt; &lt;a href="#summary" &gt;Summary table&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;p&gt;\[
\newcommand{\K}{\mathbb K}
\newcommand{\V}{\mathbb V}
\newcommand{\c}[1]{\mathbf{\mathsf{#1}}}
\]&lt;/p&gt;</description></item><item><title> Thoughts on "Static Retrieval Revisited"</title><link>https://curiouscoding.nl/posts/static-retrieval-revisited/</link><pubDate>Tue, 04 Nov 2025 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/static-retrieval-revisited/</guid><description>&lt;div class="ox-hugo-toc toc has-section-numbers"&gt;
&lt;div class="heading"&gt;Table of Contents&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1&lt;/span&gt; &lt;a href="#problem-definitions" &gt;Problem definitions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2&lt;/span&gt; &lt;a href="#optimal-solutions" &gt;Optimal solutions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3&lt;/span&gt; &lt;a href="#why-an-overhead-is-necessary" &gt;Why an overhead is necessary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4&lt;/span&gt; &lt;a href="#augmented-retrieval" &gt;Augmented Retrieval&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;p&gt;These are some summarizing notes and thoughts on
&lt;a href="#citeproc_bib_item_2"&gt;“Static Retrieval Revisited: To Optimality and beyond”&lt;/a&gt; by &lt;a href="#citeproc_bib_item_2"&gt;Hu, Kuszmaul, Liang, Yu, Zhang, and Zhou&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="problem-definitions"&gt;
 &lt;span class="section-num"&gt;1&lt;/span&gt; Problem definitions
 &lt;a class="heading-link" href="#problem-definitions"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Static Retrieval.&lt;/strong&gt;&lt;/strong&gt; Given \(n\) keys \(X\subseteq U\) and \(n\) $v$-bit values
\(f(X) \in [2^v]\), encode \(f: X\to [2^v]\).
The goal is use a minimal number of bits on top of the trivial
\(nv\) lower bound, while allowing efficient queries.&lt;/p&gt;</description></item><item><title>Binary search variants and the effects of batching</title><link>https://curiouscoding.nl/posts/binsearch/</link><pubDate>Wed, 12 Feb 2025 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/binsearch/</guid><description>&lt;div class="ox-hugo-toc toc has-section-numbers"&gt;
&lt;div class="heading"&gt;Table of Contents&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1&lt;/span&gt; &lt;a href="#optimizing-binary-search-and-interpolation-search" &gt;Optimizing Binary Search And Interpolation Search&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.1&lt;/span&gt; &lt;a href="#problem-statement" &gt;Problem statement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.2&lt;/span&gt; &lt;a href="#inspiration-and-background" &gt;Inspiration and background&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.3&lt;/span&gt; &lt;a href="#benchmarking-setup" &gt;Benchmarking setup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2&lt;/span&gt; &lt;a href="#binary-search" &gt;Binary search&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.1&lt;/span&gt; &lt;a href="#branchless-search" &gt;Branchless search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.2&lt;/span&gt; &lt;a href="#explicit-prefetching" &gt;Explicit prefetching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.3&lt;/span&gt; &lt;a href="#batching" &gt;Batching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.4&lt;/span&gt; &lt;a href="#a-note-on-power-of-two-array-sizes" &gt;A note on power-of-two array sizes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3&lt;/span&gt; &lt;a href="#alternative-memory-layout" &gt;Eytzinger&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.1&lt;/span&gt; &lt;a href="#naive-implementation" &gt;Naive implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.2&lt;/span&gt; &lt;a href="#prefetching" &gt;Prefetching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.3&lt;/span&gt; &lt;a href="#branchless-eytzinger" &gt;Branchless Eytzinger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.4&lt;/span&gt; &lt;a href="#batched-eytzinger" &gt;Batched Eytzinger&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.4.1&lt;/span&gt; &lt;a href="#non-prefetched" &gt;Non-prefetched&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.4.2&lt;/span&gt; &lt;a href="#prefetched" &gt;Prefetched&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4&lt;/span&gt; &lt;a href="#eytzinger-or-binsearch" &gt;Eytzinger or BinSearch?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5&lt;/span&gt; &lt;a href="#memory-efficiency-parallel-search-and-comparison-to-s-trees" &gt;Memory efficiency &amp;ndash; parallel search and comparison to S-trees&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;6&lt;/span&gt; &lt;a href="#interpolation-search" &gt;Interpolation search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7&lt;/span&gt; &lt;a href="#conclusion-and-takeaways" &gt;Conclusion and takeaways&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;h2 id="optimizing-binary-search-and-interpolation-search"&gt;
 &lt;span class="section-num"&gt;1&lt;/span&gt; Optimizing Binary Search And Interpolation Search
 &lt;a class="heading-link" href="#optimizing-binary-search-and-interpolation-search"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This blogpost is a preliminary of the
&lt;a href="https://curiouscoding.nl/posts/static-search-tree/" class="external-link" target="_blank" rel="noopener"&gt;post on static
search trees&lt;/a&gt;. We will be looking into binary search and how it can be
optimized using different memory layouts (Eytzinger), branchless
techniques and careful use of prefetching. In addition, we will explore
batching. Our language of choice will be Rust.&lt;/p&gt;</description></item><item><title>Thoughts on Consensus MPHF and tiny pointers</title><link>https://curiouscoding.nl/posts/consensus/</link><pubDate>Wed, 12 Feb 2025 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/consensus/</guid><description>&lt;div class="ox-hugo-toc toc has-section-numbers"&gt;
&lt;div class="heading"&gt;Table of Contents&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1&lt;/span&gt; &lt;a href="#consensus" &gt;Consensus&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.1&lt;/span&gt; &lt;a href="#consensus-recsplit" &gt;Consensus-RecSplit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2&lt;/span&gt; &lt;a href="#idea-consensus-ptrhash" &gt;IDEA: Consensus-PtrHash&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3&lt;/span&gt; &lt;a href="#hashing" &gt;Tiny pointers and optimal open addressing hash tables&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;p&gt;These are some thoughts on the Consensus-based MPHF presented in
Lehmann et al. (&lt;a href="#citeproc_bib_item_4"&gt;2025&lt;/a&gt;), and how this could be applied to PtrHash:&lt;/p&gt;
&lt;p&gt;Lehmann, Hans-Peter, Peter Sanders, Stefan Walzer, and Jonatan Ziegler. 2025. “Combined Search and Encoding for Seeds, with an Application to Minimal Perfect Hashing.” arXiv; arXiv. &lt;a href="https://doi.org/10.48550/ARXIV.2502.05613"&gt;&lt;a href="https://doi.org/10.48550/ARXIV.2502.05613" class="external-link" target="_blank" rel="noopener"&gt;https://doi.org/10.48550/ARXIV.2502.05613&lt;/a&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Below are also some thoughts on the papers on tiny pointers, used to achieve
hash tables with load factors very close to 1: Bender et al. (&lt;a href="#citeproc_bib_item_1"&gt;2021&lt;/a&gt;), Farach-Colton, Krapivin, and Kuszmaul (&lt;a href="#citeproc_bib_item_2"&gt;2024&lt;/a&gt;).&lt;/p&gt;</description></item><item><title>PtrHash: Minimal Perfect Hashing at RAM Throughput</title><link>https://curiouscoding.nl/posts/ptrhash/</link><pubDate>Mon, 03 Feb 2025 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/ptrhash/</guid><description>&lt;div class="ox-hugo-toc toc has-section-numbers"&gt;
&lt;div class="heading"&gt;Table of Contents&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#abstract" &gt;Abstract&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1&lt;/span&gt; &lt;a href="#sec:orgebb9721" &gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2&lt;/span&gt; &lt;a href="#sec:orgfe4e2e9" &gt;Related work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3&lt;/span&gt; &lt;a href="#sec:orgce4a522" &gt;PtrHash&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.1&lt;/span&gt; &lt;a href="#sec:org06ce748" &gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.2&lt;/span&gt; &lt;a href="#sec:construction" &gt;Construction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.3&lt;/span&gt; &lt;a href="#sec:bucket-fn" &gt;Bucket Assignment Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.4&lt;/span&gt; &lt;a href="#remapping" &gt;Remapping using CacheLineEF&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4&lt;/span&gt; &lt;a href="#sec:orgbf28892" &gt;Results&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4.1&lt;/span&gt; &lt;a href="#construction-eval" &gt;Construction&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4.1.1&lt;/span&gt; &lt;a href="#sec:orge11d60c" &gt;Bucket Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4.1.2&lt;/span&gt; &lt;a href="#sec:org9f908d8" &gt;Tuning Parameters for Construction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4.1.3&lt;/span&gt; &lt;a href="#sec:orgece074a" &gt;Remap&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4.2&lt;/span&gt; &lt;a href="#sec:comparison" &gt;Comparison to Other Methods&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5&lt;/span&gt; &lt;a href="#sec:org9f032dd" &gt;Conclusions and Future Work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#sec:throughput" &gt;Appendix A: Query Throughput&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#sec:orgabb5dd4" &gt;Batching and Streaming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#throughput-evaluation" &gt;Evaluation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#multi-threaded-throughput." &gt;Multi-threaded Throughput.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#sec:sharding" &gt;Appendix B: Sharding&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#sec:sharding-eval" &gt;Evaluation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;p&gt;This is the HTML version of my SEA 2025 paper on PtrHash (&lt;a href="https://doi.org/10.48550/arXiv.2502.15539" class="external-link" target="_blank" rel="noopener"&gt;DOI&lt;/a&gt;, &lt;a href="https://curiouscoding.nl/papers/ptrhash.pdf" &gt;PDF&lt;/a&gt;).
The original development-log can be found &lt;a href="../ptrhash-log" &gt;here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Static search trees: 40x faster than binary search</title><link>https://curiouscoding.nl/posts/static-search-tree/</link><pubDate>Wed, 18 Dec 2024 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/static-search-tree/</guid><description>&lt;div class="ox-hugo-toc toc has-section-numbers"&gt;
&lt;div class="heading"&gt;Table of Contents&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1&lt;/span&gt; &lt;a href="#introduction" &gt;Introduction&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.1&lt;/span&gt; &lt;a href="#problem-statement" &gt;Problem statement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.2&lt;/span&gt; &lt;a href="#motivation" &gt;Motivation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.3&lt;/span&gt; &lt;a href="#recommended-reading" &gt;Recommended reading&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.4&lt;/span&gt; &lt;a href="#binary-search-and-eytzinger-layout" &gt;Binary search and Eytzinger layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.5&lt;/span&gt; &lt;a href="#hugepages" &gt;Hugepages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.6&lt;/span&gt; &lt;a href="#a-note-on-benchmarking" &gt;A note on benchmarking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.7&lt;/span&gt; &lt;a href="#cache-lines" &gt;Cache lines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;1.8&lt;/span&gt; &lt;a href="#s-trees-and-b-trees" &gt;S-trees and B-trees&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2&lt;/span&gt; &lt;a href="#optimizing-find" &gt;Optimizing &lt;code&gt;find&lt;/code&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.1&lt;/span&gt; &lt;a href="#linear" &gt;Linear&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.2&lt;/span&gt; &lt;a href="#auto-vectorization" &gt;Auto-vectorization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.3&lt;/span&gt; &lt;a href="#trailing-zeros" &gt;Trailing zeros&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.4&lt;/span&gt; &lt;a href="#popcount" &gt;Popcount&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;2.5&lt;/span&gt; &lt;a href="#manual-simd" &gt;Manual SIMD&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3&lt;/span&gt; &lt;a href="#optimizing-the-search" &gt;Optimizing the search&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.1&lt;/span&gt; &lt;a href="#batching" &gt;Batching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.2&lt;/span&gt; &lt;a href="#prefetching" &gt;Prefetching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.3&lt;/span&gt; &lt;a href="#pointer-arithmetic" &gt;Pointer arithmetic&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.3.1&lt;/span&gt; &lt;a href="#up-front-splat" &gt;Up-front splat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.3.2&lt;/span&gt; &lt;a href="#byte-based-pointers" &gt;Byte-based pointers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.3.3&lt;/span&gt; &lt;a href="#the-final-version" &gt;The final version&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.4&lt;/span&gt; &lt;a href="#skip-prefetch" &gt;Skip prefetch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;3.5&lt;/span&gt; &lt;a href="#interleave" &gt;Interleave&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4&lt;/span&gt; &lt;a href="#optimizing-the-tree-layout" &gt;Optimizing the tree layout&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4.1&lt;/span&gt; &lt;a href="#left-tree" &gt;Left-tree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4.2&lt;/span&gt; &lt;a href="#memory-layouts" &gt;Memory layouts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4.3&lt;/span&gt; &lt;a href="#node-size-b-15" &gt;Node size \(B=15\)&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4.3.1&lt;/span&gt; &lt;a href="#data-structure-size" &gt;Data structure size&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;4.4&lt;/span&gt; &lt;a href="#summary" &gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5&lt;/span&gt; &lt;a href="#prefix-partitioning" &gt;Prefix partitioning&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5.1&lt;/span&gt; &lt;a href="#full-layout" &gt;Full layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5.2&lt;/span&gt; &lt;a href="#compact-subtrees" &gt;Compact subtrees&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5.3&lt;/span&gt; &lt;a href="#the-best-of-both-compact-first-level" &gt;The best of both: compact first level&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5.4&lt;/span&gt; &lt;a href="#overlapping-trees" &gt;Overlapping trees&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5.5&lt;/span&gt; &lt;a href="#human-data" &gt;Human data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5.6&lt;/span&gt; &lt;a href="#prefix-map" &gt;Prefix map&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;5.7&lt;/span&gt; &lt;a href="#prefix-summary" &gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;6&lt;/span&gt; &lt;a href="#multi-threaded-comparison" &gt;Multi-threaded comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7&lt;/span&gt; &lt;a href="#conclusion" &gt;Conclusion&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.1&lt;/span&gt; &lt;a href="#future-work" &gt;Future work&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.1.1&lt;/span&gt; &lt;a href="#branchy-search" &gt;Branchy search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.1.2&lt;/span&gt; &lt;a href="#interpolation-search" &gt;Interpolation search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.1.3&lt;/span&gt; &lt;a href="#packing-data-smaller" &gt;Packing data smaller&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.1.4&lt;/span&gt; &lt;a href="#returning-indices-in-original-data" &gt;Returning indices in original data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.1.5&lt;/span&gt; &lt;a href="#range-queries" &gt;Range queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.1.6&lt;/span&gt; &lt;a href="#sorting-queries" &gt;Sorting queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="section-num"&gt;7.1.7&lt;/span&gt; &lt;a href="#suffix-array-searching" &gt;Suffix array searching&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;p&gt;In this post, we will implement a static search tree (S+ tree) for
high-throughput searching of sorted data, as &lt;a href="https://en.algorithmica.org/hpc/data-structures/s-tree/" class="external-link" target="_blank" rel="noopener"&gt;introduced&lt;/a&gt; on Algorithmica.
We&amp;rsquo;ll mostly take the code presented there as a starting point, and optimize it
to its limits. For a large part, I&amp;rsquo;m simply taking the &amp;lsquo;future work&amp;rsquo; ideas of that post
and implementing them. And then there will be a bunch of looking at assembly
code to shave off all the instructions we can.
Lastly, there will be one big addition to optimize throughput: &lt;em&gt;batching&lt;/em&gt;.&lt;/p&gt;</description></item><item><title>FM-index implementations</title><link>https://curiouscoding.nl/posts/fm-index-implementations/</link><pubDate>Wed, 02 Oct 2024 00:00:00 +0200</pubDate><guid>https://curiouscoding.nl/posts/fm-index-implementations/</guid><description>&lt;div class="ox-hugo-toc toc"&gt;
&lt;div class="heading"&gt;Table of Contents&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#a-note-on-sdsl-versions" &gt;A note on SDSL versions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;p&gt;Here I&amp;rsquo;ll briefly list some FM-index and related implementations around the web.
Implementations seem relatively inconsistent, mostly because the FM-index is
more of a &amp;lsquo;wrapper&amp;rsquo; type around a given Burrows-Wheeler-transform and an
&lt;em&gt;occurrences&lt;/em&gt; list implementation. Both can be implemented in various ways. In particular
occurrences should be stored using a wavelet tree for optimal compression.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://github.com/wafflespeanut/nucleic-acid/blob/2adbf5181081245423f974a88b5ccf53d7bf26ac/src/bwt.rs#L96" class="external-link" target="_blank" rel="noopener"&gt;nucleic-acid repo&lt;/a&gt; contains a completely unoptimised version.&lt;/li&gt;
&lt;li&gt;The Rust-bio crate contains a &lt;a href="https://github.com/rust-bio/rust-bio/blob/master/src/data_structures/fmindex.rs#L209" class="external-link" target="_blank" rel="noopener"&gt;generic FM-index&lt;/a&gt;. It stores a &lt;a href="https://github.com/rust-bio/rust-bio/blob/master/src/data_structures/bwt.rs#L75-L94" class="external-link" target="_blank" rel="noopener"&gt;sampled
occurrences array&lt;/a&gt;, so that space is relatively small but lookups take \(O(k)\)
time for sampling factor \(k\).&lt;/li&gt;
&lt;li&gt;SDSL-lite contains a &lt;a href="https://github.com/simongog/sdsl-lite/blob/c32874cb2d8524119f25f3b501526fe692df29f4/include/sdsl/wavelet_" class="external-link" target="_blank" rel="noopener"&gt;wavelet tree&lt;/a&gt; and &lt;a href="https://github.com/simongog/sdsl-lite/blob/master/include/sdsl/csa_wt.hpp#L48" class="external-link" target="_blank" rel="noopener"&gt;compressed suffix array&lt;/a&gt; implementation based
on it, that provides the same functionality as an FM-index.&lt;/li&gt;
&lt;li&gt;There is the &lt;a href="https://github.com/rossanoventurini/qwt" class="external-link" target="_blank" rel="noopener"&gt;Quad Wavelet Tree&lt;/a&gt; (QWT) Rust crate (Ceregini, Kurpicz, and Venturini 2024). This uses a 4-ary
tree instead of the usual binary wavelet tree, and improves latency by around
a factor 2 over SDSL wavelet trees.&lt;/li&gt;
&lt;li&gt;Dominik Kempa has the &lt;a href="https://github.com/dominikkempa/faster-minuter?tab=readme-ov-file" class="external-link" target="_blank" rel="noopener"&gt;Faster-Minuter index&lt;/a&gt; (Gog et al. 2019) that contains
an improved wavelet tree as well.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/achacond/gem-cutter" class="external-link" target="_blank" rel="noopener"&gt;GEM-Cutter&lt;/a&gt; contain a GPU implementation of the FM-index (Chacon et al. 2015).&lt;/li&gt;
&lt;li&gt;There is also &lt;a href="https://github.com/lh3/ropebwt3" class="external-link" target="_blank" rel="noopener"&gt;RopeBWT3&lt;/a&gt; (Li 2024), which is basically a run-length
compressed BWT with a B+ tree on top for fast queries.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/UM-Applied-Algorithms-Lab/AWRY" class="external-link" target="_blank" rel="noopener"&gt;AWRY&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="a-note-on-sdsl-versions"&gt;
 A note on SDSL versions
 &lt;a class="heading-link" href="#a-note-on-sdsl-versions"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/simongog/sdsl" class="external-link" target="_blank" rel="noopener"&gt;github:simongog/sdsl&lt;/a&gt; is the original, with last commit in 2013.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/simongog/sdsl-lite" class="external-link" target="_blank" rel="noopener"&gt;github:simongog/sdsl-lite&lt;/a&gt; is v2, with last commit in 2019, and seems the most
used currently.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/xxsds/sdsl-lite" class="external-link" target="_blank" rel="noopener"&gt;github:xxsds/sdsl-lite&lt;/a&gt; is v3 and seems to be actively maintained at the time
of writing (Jan 2025), and is &lt;a href="https://www.reddit.com/r/rust/comments/nlxhym/comment/gzpqejn/?utm_source=share&amp;amp;utm_medium=web3x&amp;amp;utm_name=web3xcss&amp;amp;utm_term=1&amp;amp;utm_content=share_button" class="external-link" target="_blank" rel="noopener"&gt;recommended&lt;/a&gt; by the original developers. From a
quick glance, I think it&amp;rsquo;s somewhat restructured and truly a v3, not just a v2.1.
However, it seems to be much less popular.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vgteam/sdsl-lite" class="external-link" target="_blank" rel="noopener"&gt;github:vgteam/sdsl-lite&lt;/a&gt; is a fork of the original &lt;code&gt;sdsl-lite&lt;/code&gt;, with, I think,
a number of small bug fixes and some updates for recent compiler versions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then there are also some rust versions:&lt;/p&gt;</description></item><item><title>String algorithm visualizations</title><link>https://curiouscoding.nl/posts/alg-viz/</link><pubDate>Tue, 08 Nov 2022 00:00:00 +0100</pubDate><guid>https://curiouscoding.nl/posts/alg-viz/</guid><description>&lt;ol&gt;
&lt;li&gt;Select the algorithm to visualize&lt;/li&gt;
&lt;li&gt;Click the buttons, or click the canvas and use the indicated keys&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Suffix-array construction is explained &lt;a href="https://curiouscoding.nl/posts/suffix-array-construction/" &gt;here&lt;/a&gt; and BWT is explained &lt;a href="https://curiouscoding.nl/posts/bwt/" &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Source code is &lt;a href="https://github.com/RagnarGrootKoerkamp/alg-viz" class="external-link" target="_blank" rel="noopener"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;script defer src="https://curiouscoding.nl/js/alg-viz.js" type="module"&gt;&lt;/script&gt;&lt;/head&gt;
&lt;div class="controls"&gt;
&lt;label for="algorithm"&gt;Algorithm&lt;/label&gt;
&lt;select name="algorithm" id="algorithm"&gt;
 &lt;option value="suffix-array"&gt;Suffix Array Construction&lt;/option&gt;
 &lt;option value="bwt"&gt;Burrows-Wheeler Transform&lt;/option&gt;
 &lt;option value="bibwt"&gt;Bidirectional BWT&lt;/option&gt;
&lt;/select&gt;
&lt;br/&gt;
&lt;label for="string"&gt;String&lt;/label&gt; &lt;input type="string" name="string" id="string"/&gt;&lt;br/&gt;
&lt;label for="query"&gt;Query&lt;/label&gt; &lt;input type="string" name="query" id="query"/&gt;&lt;br/&gt;
&lt;button class="button-primary" id="prev"&gt;prev (←/backspace)&lt;/button&gt;
&lt;button class="button-primary" id="next"&gt;next (→/space)&lt;/button&gt;
&lt;br/&gt;
&lt;label for="delay"&gt;Delay (s)&lt;/label&gt; &lt;input type="number" name="delay" id="delay" value="0.8"/&gt;&lt;br/&gt;
&lt;button class="button-primary" id="faster"&gt;faster (↑/+/f)&lt;/button&gt;
&lt;button class="button-primary" id="slower"&gt;slower (↓/-/s)&lt;/button&gt;
&lt;button class="button-primary" id="pauseplay"&gt;pause/play (p/return)&lt;/button&gt;
&lt;/div&gt;
&lt;div class="canvas"&gt;
&lt;canvas id="canvas" tabindex='1' width="1600" height="1200"&gt;&lt;/canvas&gt;
&lt;/div&gt;</description></item></channel></rss>