<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Posts on CS Theorems</title><link>https://cs.lozic.me/posts/</link><description>Recent content in Posts on CS Theorems</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 24 Dec 2027 12:00:00 +0100</lastBuildDate><atom:link href="https://cs.lozic.me/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Mutual Exclusion Without Hardware Support</title><link>https://cs.lozic.me/posts/t052-mutual-exclusion-without-hardware-support/</link><pubDate>Fri, 24 Dec 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t052-mutual-exclusion-without-hardware-support/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Two threads increment a shared counter a million times each. The final value is
1,374,522.&lt;/p&gt;
&lt;p&gt;You know why: the increment is a read, an add, and a write, and the interleavings
lose updates. The fix is a lock. But a lock is provided by your runtime, which
gets it from the OS, which gets it from a hardware instruction — &lt;code&gt;lock xchg&lt;/code&gt;,
&lt;code&gt;cmpxchg&lt;/code&gt;, &lt;code&gt;ldrex&lt;/code&gt;/&lt;code&gt;strex&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Work Stealing and Fair Scheduling</title><link>https://cs.lozic.me/posts/t109-work-stealing-and-fair-scheduling/</link><pubDate>Fri, 17 Dec 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t109-work-stealing-and-fair-scheduling/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You parallelize a recursive algorithm. Quicksort, or a tree traversal, or a
divide-and-conquer numeric kernel. You have 16 cores and a central task queue,
and you measure a speedup of 3.&lt;/p&gt;</description></item><item><title>Resolution and CDCL</title><link>https://cs.lozic.me/posts/t050-resolution-and-cdcl/</link><pubDate>Fri, 10 Dec 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t050-resolution-and-cdcl/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;SAT is NP-complete (T027). It is &lt;em&gt;the&lt;/em&gt; NP-complete problem, the one everything
else reduces to. The textbook conclusion is that you should avoid it.&lt;/p&gt;
&lt;p&gt;Meanwhile: a hardware verification team routinely discharges instances with two
million variables and ten million clauses in under a minute. Package managers
solve dependency resolution with SAT. Every bounded model checker (T049) is a SAT
solver with a front end. The planning community abandoned custom search for
SAT encodings and got faster.&lt;/p&gt;</description></item><item><title>Register Allocation Is Graph Coloring</title><link>https://cs.lozic.me/posts/t107-register-allocation-is-graph-coloring/</link><pubDate>Fri, 03 Dec 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t107-register-allocation-is-graph-coloring/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You write a function with forty local variables. The x86-64 machine it runs on
has sixteen general-purpose registers, two of which are effectively spoken for.&lt;/p&gt;
&lt;p&gt;Something must decide which values live in registers and which live in memory.
Get it right and the function runs at register speed. Get it wrong and every
operation costs a load and a store, and on a modern machine an L1 hit is around
4 cycles against roughly 1 for a register, with an L2 miss costing 12 to 20 and
a trip to DRAM 200 or more.&lt;/p&gt;</description></item><item><title>Model Checking, LTL, and Buchi Automata</title><link>https://cs.lozic.me/posts/t049-model-checking-ltl-and-buchi-automata/</link><pubDate>Fri, 26 Nov 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t049-model-checking-ltl-and-buchi-automata/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your distributed lock service deadlocks once a month. You cannot reproduce it.&lt;/p&gt;
&lt;p&gt;The logs show three nodes in states that, as far as you can reconstruct, should
not co-occur. You add tracing. It does not happen again for six weeks, then
happens twice in a day during a deploy. You write a stress test that runs a
million random interleavings overnight and finds nothing.&lt;/p&gt;</description></item><item><title>Hoare Logic and Weakest Preconditions</title><link>https://cs.lozic.me/posts/t048-hoare-logic-and-weakest-preconditions/</link><pubDate>Fri, 19 Nov 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t048-hoare-logic-and-weakest-preconditions/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You changed a loop. It looks right. The tests pass. You are not confident.&lt;/p&gt;
&lt;p&gt;The reason you are not confident is that the tests check a finite set of inputs
and the loop runs on all of them. You know this. So you stare at the loop and
try to convince yourself by simulating it in your head, which works for three
iterations and then stops working.&lt;/p&gt;</description></item><item><title>Bloom Filters</title><link>https://cs.lozic.me/posts/t073-bloom-filters/</link><pubDate>Fri, 12 Nov 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t073-bloom-filters/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Every request to your service checks whether a user ID exists before doing
anything else. The check hits the database. Ninety-eight percent of the time the
answer is no, because most of the traffic is bots probing for accounts that were
never created.&lt;/p&gt;</description></item><item><title>Codd's Theorem</title><link>https://cs.lozic.me/posts/t069-codds-theorem/</link><pubDate>Fri, 05 Nov 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t069-codds-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You write:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Berlin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You did not say whether to scan the table or use an index. You did not say which
of the two predicates to apply first. You did not say anything about join order,
because there is no join, but if there were you would not have said that either.&lt;/p&gt;</description></item><item><title>Consistent Hashing</title><link>https://cs.lozic.me/posts/t072-consistent-hashing/</link><pubDate>Fri, 29 Oct 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t072-consistent-hashing/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You shard a cache across ten servers with the obvious rule:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It works. Then you add an eleventh server, and &lt;code&gt;% 10&lt;/code&gt; becomes &lt;code&gt;% 11&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Every key now maps somewhere else. Not one tenth of them — essentially all of
them. A key landing on the same server after the change is a coincidence with
probability about $1/11$, so roughly 91% of your cache is instantly invalid.
Every request misses, every miss hits the database, and the database, sized for
a 95% cache hit rate, now receives twenty times its provisioned load and falls
over.&lt;/p&gt;</description></item><item><title>Abstract Interpretation and Galois Connections</title><link>https://cs.lozic.me/posts/t047-abstract-interpretation-and-galois-connections/</link><pubDate>Fri, 22 Oct 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t047-abstract-interpretation-and-galois-connections/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Rice&amp;rsquo;s theorem (T011) told you every non-trivial semantic property is
undecidable. Your static analyzer must therefore be unsound, incomplete, or
non-terminating. In practice it is incomplete: it reports things that cannot
happen.&lt;/p&gt;</description></item><item><title>Parametricity: Theorems for Free</title><link>https://cs.lozic.me/posts/t045-parametricity-theorems-for-free/</link><pubDate>Fri, 15 Oct 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t045-parametricity-theorems-for-free/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Someone hands you a function and no source:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-haskell" data-lang="haskell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;::&lt;/span&gt; &lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;How much can you say about it? The instinct is nothing: any implementation is
possible, that is what polymorphism means.&lt;/p&gt;</description></item><item><title>IP = PSPACE</title><link>https://cs.lozic.me/posts/t036-ip-equals-pspace/</link><pubDate>Fri, 08 Oct 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t036-ip-equals-pspace/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have a claim you cannot check. A cloud provider ran a computation on a
petabyte you do not have and reports an answer. A model was trained on data you
cannot access. A database returned an aggregate over rows you are not permitted
to read.&lt;/p&gt;</description></item><item><title>Progress and Preservation</title><link>https://cs.lozic.me/posts/t044-progress-and-preservation/</link><pubDate>Fri, 01 Oct 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t044-progress-and-preservation/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;Well-typed programs don&amp;rsquo;t go wrong.&amp;rdquo; You have heard it. You have probably said
it. And then:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// NullPointerException&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That program is well typed and it went wrong. So either the slogan is false or
it means something narrower than it sounds. It means something narrower, and the
narrowness is precisely specifiable.&lt;/p&gt;</description></item><item><title>Hindley–Milner and Principal Types</title><link>https://cs.lozic.me/posts/t043-hindley-milner-and-principal-types/</link><pubDate>Fri, 24 Sep 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t043-hindley-milner-and-principal-types/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You write this in OCaml or Haskell and annotate nothing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ocaml" data-lang="ocaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;rec&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The compiler reports &lt;code&gt;('a -&amp;gt; 'b) -&amp;gt; 'a list -&amp;gt; 'b list&lt;/code&gt;. It found the most
general possible type, on its own, with no hints, and it will reject any call
that does not fit.&lt;/p&gt;</description></item><item><title>The Curry–Howard Correspondence</title><link>https://cs.lozic.me/posts/t042-the-curry-howard-correspondence/</link><pubDate>Fri, 17 Sep 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t042-the-curry-howard-correspondence/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You write a function signature and it looks like a sentence:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-haskell" data-lang="haskell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;::&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Read it aloud: if $a$ implies $b$, and $b$ implies $c$, then $a$ implies $c$.
That is transitivity of implication, a rule of logic you have known since school.
And the implementation, &lt;code&gt;f g h = h . g&lt;/code&gt;, is the &lt;em&gt;proof&lt;/em&gt; of it: given evidence for
$a$, apply the first, then apply the second.&lt;/p&gt;</description></item><item><title>The Lambda Calculus and the Y Combinator</title><link>https://cs.lozic.me/posts/t041-the-lambda-calculus-and-the-y-combinator/</link><pubDate>Fri, 10 Sep 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t041-the-lambda-calculus-and-the-y-combinator/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You are writing a language, or a configuration evaluator, or a template engine,
and you need recursion. So you add a &lt;code&gt;letrec&lt;/code&gt; form, and now the evaluator needs
to bind a name before the value it names exists. You reach for a mutable cell, a
placeholder that gets patched after construction, and something about it feels
like cheating.&lt;/p&gt;</description></item><item><title>The CALM Theorem and CRDTs</title><link>https://cs.lozic.me/posts/t067-the-calm-theorem-and-crdts/</link><pubDate>Fri, 03 Sep 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t067-the-calm-theorem-and-crdts/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Every distributed result so far has been a prohibition. Two Generals: no
agreement over a lossy channel. FLP: no deterministic asynchronous consensus.
Byzantine: not below $3f+1$. CAP: not all three.&lt;/p&gt;</description></item><item><title>The CAP Theorem and PACELC</title><link>https://cs.lozic.me/posts/t066-the-cap-theorem-and-pacelc/</link><pubDate>Fri, 27 Aug 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t066-the-cap-theorem-and-pacelc/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Someone in a design review says &amp;ldquo;we&amp;rsquo;re AP, not CP,&amp;rdquo; and the room nods.&lt;/p&gt;
&lt;p&gt;Nobody asks which consistency, which availability, or what happens when there is
no partition, which is 99.9% of the time. The phrase has done the work of a
decision without containing one.&lt;/p&gt;</description></item><item><title>Paxos, Raft, and the Shape of Consensus</title><link>https://cs.lozic.me/posts/t064-paxos-raft-and-the-shape-of-consensus/</link><pubDate>Fri, 20 Aug 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t064-paxos-raft-and-the-shape-of-consensus/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your database has two nodes that both believe they are the primary.&lt;/p&gt;
&lt;p&gt;Each has been accepting writes. Each has a coherent log. Neither has crashed.
The old primary was declared dead by a health check that timed out during a GC
pause, a new one was elected, and then the old one woke up still holding what it
thinks is a valid lease. You now have two divergent histories of the same data
and no principled way to merge them, because both contain acknowledged writes
that customers were told had succeeded.&lt;/p&gt;</description></item><item><title>Quorum Intersection (R + W &gt; N)</title><link>https://cs.lozic.me/posts/t065-quorum-intersection/</link><pubDate>Fri, 13 Aug 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t065-quorum-intersection/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have three replicas of a value. A client writes &lt;code&gt;v2&lt;/code&gt; and gets an
acknowledgement. Another client reads and gets &lt;code&gt;v1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Nothing crashed. No network partition healed badly. Both operations completed
successfully and reported success. The write went to one replica, the read came
from another, and the two never spoke.&lt;/p&gt;</description></item><item><title>The Myhill–Nerode Theorem</title><link>https://cs.lozic.me/posts/t040-the-myhill-nerode-theorem/</link><pubDate>Fri, 06 Aug 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t040-the-myhill-nerode-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You need to prove a language is not regular. Everyone points you at the pumping
lemma, and you spend an afternoon losing to it.&lt;/p&gt;
&lt;p&gt;The statement is a nest of quantifiers: for every regular language there exists
a pumping length $p$ such that for every string $w$ with $|w| \ge p$ there exists
a decomposition $w = xyz$ with $|xy| \le p$ and $|y| &amp;gt; 0$ such that for all $i
\ge 0$, $xy^i z$ is in the language. To use it you negate all of that and play a
game against an adversary who picks $p$ and the decomposition while you pick $w$
and $i$.&lt;/p&gt;</description></item><item><title>Kleene's Theorem: Regexes Are Finite Automata</title><link>https://cs.lozic.me/posts/t039-kleenes-theorem-regexes-are-finite-automata/</link><pubDate>Fri, 30 Jul 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t039-kleenes-theorem-regexes-are-finite-automata/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your service goes down. CPU pinned at 100% on one core, no memory growth, no
error logs, requests timing out.&lt;/p&gt;
&lt;p&gt;The cause is a validation regex — something reasonable-looking like
&lt;code&gt;^(a+)+$&lt;/code&gt;, or in real life a URL or email validator with nested quantifiers —
matched against a 30-character string that happens not to match. The engine is
exploring an exponential number of ways to split the input among the nested
groups, and it will get there eventually, some time after the heat death of
everything.&lt;/p&gt;</description></item><item><title>BPP, Pseudorandomness, and Derandomization</title><link>https://cs.lozic.me/posts/t035-bpp-pseudorandomness-and-derandomization/</link><pubDate>Fri, 23 Jul 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t035-bpp-pseudorandomness-and-derandomization/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You need to test whether two arithmetic circuits compute the same polynomial.
The deterministic approach is to expand both into normal form, and the expansion
is exponentially large.&lt;/p&gt;
&lt;p&gt;The randomized approach takes four lines. Pick a random point, evaluate both
circuits there, compare. If they differ as polynomials, the
Schwartz-Zippel lemma says a random point catches it with probability at least
$1 - d/|S|$. Repeat a few times and the error is negligible.&lt;/p&gt;</description></item><item><title>The Exponential Time Hypothesis and Fine-Grained Complexity</title><link>https://cs.lozic.me/posts/t037-the-exponential-time-hypothesis-and-fine-grained-complexity/</link><pubDate>Fri, 16 Jul 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t037-the-exponential-time-hypothesis-and-fine-grained-complexity/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have a string algorithm. Edit distance between two sequences, the classic
dynamic program, $O(n^2)$ time. It has been in production for years.&lt;/p&gt;
&lt;p&gt;Now the inputs are genome-scale. At $n = 10^5$ characters, $n^2 = 10^{10}$
operations, about &lt;strong&gt;10 seconds&lt;/strong&gt; at a billion ops per second. At $n = 10^6$ it is
$10^{12}$ operations, about &lt;strong&gt;1000 seconds&lt;/strong&gt; — seventeen minutes for one pair of
strings. You need to do a million pairs.&lt;/p&gt;</description></item><item><title>Parity Is Not in AC^0</title><link>https://cs.lozic.me/posts/t034-parity-is-not-in-ac0/</link><pubDate>Fri, 09 Jul 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t034-parity-is-not-in-ac0/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have just read T030 and you are demoralized. Relativization kills the
techniques that treat machines as black boxes. Natural proofs kill the
combinatorial ones, assuming pseudorandom functions exist. Between them they
appear to rule out everything anyone knows how to do, and the honest summary of
fifty years is that nobody can prove any interesting problem is hard.&lt;/p&gt;</description></item><item><title>The No-Cloning Theorem</title><link>https://cs.lozic.me/posts/t110-the-no-cloning-theorem/</link><pubDate>Fri, 02 Jul 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t110-the-no-cloning-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have spent this whole part learning that information wants to be copied.
Entropy bounds compression (T018), Huffman hits the bound (T020), redundancy
buys error correction (T021). Every result so far treats &amp;ldquo;make another copy&amp;rdquo; as
the free operation — the thing you do without thinking, the reason &lt;code&gt;cp&lt;/code&gt; has no
interesting failure modes.&lt;/p&gt;</description></item><item><title>The Nyquist–Shannon Sampling Theorem</title><link>https://cs.lozic.me/posts/t106-the-nyquist-shannon-sampling-theorem/</link><pubDate>Fri, 25 Jun 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t106-the-nyquist-shannon-sampling-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your dashboard shows CPU utilization averaging 40%, comfortably under budget.
Users are reporting timeouts. You add more logging and the average stays at 40%.&lt;/p&gt;
&lt;p&gt;Eventually someone captures a one-second trace and finds the truth: the service
spikes to 100% for eight seconds out of every ninety, and your monitoring
samples every sixty seconds. The spikes are real, periodic, and invisible.&lt;/p&gt;</description></item><item><title>The Byzantine Generals Problem</title><link>https://cs.lozic.me/posts/t063-the-byzantine-generals-problem/</link><pubDate>Fri, 18 Jun 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t063-the-byzantine-generals-problem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;A node in your cluster is not down. It is worse than down.&lt;/p&gt;
&lt;p&gt;Its disk is returning corrupted blocks that pass the checksum because the
checksum is corrupted too. Or a NIC with a firmware bug is duplicating and
reordering frames. Or a bad memory module flipped a bit in a config value and
the node now believes it owns a shard it does not. Or someone has root on one
machine.&lt;/p&gt;</description></item><item><title>FLP Impossibility</title><link>https://cs.lozic.me/posts/t062-flp-impossibility/</link><pubDate>Fri, 11 Jun 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t062-flp-impossibility/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your cluster of five nodes elects a leader. It works. It has worked for a year.&lt;/p&gt;
&lt;p&gt;Then one afternoon a garbage collection pause on the leader runs for twelve
seconds. The followers time out, start an election, and elect a new leader. The
old leader wakes up, has no idea it was deposed, and keeps serving writes. For a
few hundred milliseconds you have two leaders, and if your fencing is not
airtight, two writes that should have been ordered land in an order nobody
chose.&lt;/p&gt;</description></item><item><title>Vector Clocks and Causal Consistency</title><link>https://cs.lozic.me/posts/t059-vector-clocks-and-causal-consistency/</link><pubDate>Fri, 04 Jun 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t059-vector-clocks-and-causal-consistency/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You took the lesson from T058 and stopped trusting wall clocks. Every event now
carries a Lamport timestamp, and your writes are ordered by it. The vanishing
updates stopped.&lt;/p&gt;</description></item><item><title>Happens-Before and Lamport Clocks</title><link>https://cs.lozic.me/posts/t058-happens-before-and-lamport-clocks/</link><pubDate>Fri, 28 May 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t058-happens-before-and-lamport-clocks/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Two writes to the same key. Which one wins?&lt;/p&gt;
&lt;p&gt;Your first instinct is timestamps: whichever has the later wall-clock time is
newer. This works in testing. In production you get a bug report saying an
update vanished, and when you dig in, node B&amp;rsquo;s clock was 40 ms behind node A&amp;rsquo;s,
so B&amp;rsquo;s &lt;em&gt;later&lt;/em&gt; write carried an &lt;em&gt;earlier&lt;/em&gt; timestamp and lost. NTP is running.
NTP does not make clocks identical, it makes them close, and &amp;ldquo;close&amp;rdquo; is not
&amp;ldquo;ordered.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Parameterized Complexity (FPT and W[1])</title><link>https://cs.lozic.me/posts/t033-parameterized-complexity-fpt-and-w1/</link><pubDate>Fri, 21 May 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t033-parameterized-complexity-fpt-and-w1/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your problem is NP-hard, and yet it keeps being easy.&lt;/p&gt;
&lt;p&gt;You are computing a minimum vertex cover to select monitoring points in a
network. Twelve thousand nodes. NP-hard, per T028, so you brace for the worst —
and the answer comes back in under a second, every time. The cover is small,
around thirty nodes, because the network is sparse and the interesting nodes are
few.&lt;/p&gt;</description></item><item><title>Approximation Algorithms and Ratios</title><link>https://cs.lozic.me/posts/t032-approximation-algorithms-and-ratios/</link><pubDate>Fri, 14 May 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t032-approximation-algorithms-and-ratios/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have accepted that the problem is NP-hard. Now what?&lt;/p&gt;
&lt;p&gt;The literature offers a wall of results with numbers attached: 2-approximation,
$\ln n$-approximation, PTAS, FPTAS, 0.878. Nobody explains what these numbers
buy you, how they are proved, or how they connect to the heuristic you already
wrote. Meanwhile your greedy heuristic is running in production and you have no
idea whether it is within 5% or a factor of 50 of optimal, because &lt;strong&gt;you cannot
compute the optimum to compare against&lt;/strong&gt; — that was the whole problem.&lt;/p&gt;</description></item><item><title>The PCP Theorem and Inapproximability</title><link>https://cs.lozic.me/posts/t031-the-pcp-theorem-and-inapproximability/</link><pubDate>Fri, 07 May 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t031-the-pcp-theorem-and-inapproximability/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You gave up on exact (T028) and went looking for an approximation.&lt;/p&gt;
&lt;p&gt;Vertex cover: you find a 2-approximation in four lines. Knapsack: an FPTAS, any
accuracy you want. Encouraged, you go looking for the same for max-clique, and
you find nothing. Not &amp;ldquo;nothing good&amp;rdquo; — the best known ratio is around
$n/(\log n)^2$, which on a 10,000-vertex graph means the algorithm might return
a clique nearly 60 times smaller than the true maximum. That is not an
approximation, it is a rumour.&lt;/p&gt;</description></item><item><title>Streaming Lower Bounds and Sketching</title><link>https://cs.lozic.me/posts/t105-streaming-lower-bounds-and-sketching/</link><pubDate>Fri, 30 Apr 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t105-streaming-lower-bounds-and-sketching/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Product wants the daily unique-visitor count. You have a firehose of events.&lt;/p&gt;
&lt;p&gt;The obvious implementation is a set. Add each visitor ID, report the size. At a
billion distinct IDs, eight bytes each, that is 8 GB before any hash table
overhead, and in practice a &lt;code&gt;HashSet&lt;/code&gt; will cost you two to three times that.
Per day. Per dimension you want to slice by. Multiply by country, by platform,
by campaign, and the memory bill is absurd for a number nobody looks at past two
significant figures.&lt;/p&gt;</description></item><item><title>Universal Hashing</title><link>https://cs.lozic.me/posts/t104-universal-hashing/</link><pubDate>Fri, 23 Apr 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t104-universal-hashing/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your service went down under a hash collision attack.&lt;/p&gt;
&lt;p&gt;Somebody noticed your web framework put POST parameters into a hash table, found
thousands of distinct keys colliding under its hash function, and posted a form
with 20,000 of them. Every insert walked a chain. Quadratic behaviour, one CPU
pinned per request, service dead. This actually happened, across PHP, Python,
Ruby, Java and .NET in 2011, and again against Rust&amp;rsquo;s default &lt;code&gt;HashMap&lt;/code&gt; before
it switched to SipHash.&lt;/p&gt;</description></item><item><title>Minimum Description Length and Occam's Razor</title><link>https://cs.lozic.me/posts/t025-minimum-description-length-and-occams-razor/</link><pubDate>Fri, 16 Apr 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t025-minimum-description-length-and-occams-razor/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You fit a model. It scores 94% on training data and 71% on held-out data.&lt;/p&gt;
&lt;p&gt;So you simplify: fewer parameters, more regularization. Training drops to 88%,
held-out rises to 84%. You simplify further and both drop. Somewhere in there
was an optimum, and you found it by trial and error, with a validation set and
patience.&lt;/p&gt;</description></item><item><title>Kolmogorov Complexity</title><link>https://cs.lozic.me/posts/t023-kolmogorov-complexity/</link><pubDate>Fri, 09 Apr 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t023-kolmogorov-complexity/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have two files, each exactly one megabyte.&lt;/p&gt;
&lt;p&gt;The first is a megabyte of the byte &lt;code&gt;0x00&lt;/code&gt;. The second is a megabyte from
&lt;code&gt;/dev/urandom&lt;/code&gt;. Gzip the first and you get a few hundred bytes. Gzip the second
and you get slightly more than a megabyte, because the header costs something and
there is nothing to exploit.&lt;/p&gt;</description></item><item><title>The Barriers: Relativization and Natural Proofs</title><link>https://cs.lozic.me/posts/t030-the-barriers-relativization-and-natural-proofs/</link><pubDate>Fri, 02 Apr 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t030-the-barriers-relativization-and-natural-proofs/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have read T026 and T027 and you are wondering the obvious thing.&lt;/p&gt;
&lt;p&gt;Fifty years. Thousands of researchers. A million-dollar prize. And P vs NP has
not moved. That is strange — most famous problems yield partial results, near
misses, special cases that suggest the shape of the answer. Here there is
almost nothing. We cannot even prove that NP requires more than linear-size
circuits, which is a laughably weak statement compared to what everyone
believes.&lt;/p&gt;</description></item><item><title>Karp's 21 Problems</title><link>https://cs.lozic.me/posts/t028-karps-21-problems/</link><pubDate>Fri, 26 Mar 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t028-karps-21-problems/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Three weeks into a project, you are still trying to write an exact algorithm.&lt;/p&gt;
&lt;p&gt;The problem is yours and it looks specific: assign delivery vans to routes such
that every stop is covered, no van exceeds its capacity, and the total distance
is minimized. Nothing in the literature matches it exactly. So you keep going —
better data structures, smarter pruning, a cleverer greedy pass with a repair
step — and each version works on your test set and falls over on production
data.&lt;/p&gt;</description></item><item><title>The Cook–Levin Theorem</title><link>https://cs.lozic.me/posts/t027-the-cook-levin-theorem/</link><pubDate>Fri, 19 Mar 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t027-the-cook-levin-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You know reductions (T017): to prove your problem hard, reduce a known-hard
problem to it. Fine. But that begs the obvious question, and it is the question
a sharp colleague asks the first time you use the technique.&lt;/p&gt;</description></item><item><title>P vs NP</title><link>https://cs.lozic.me/posts/t026-p-vs-np/</link><pubDate>Fri, 12 Mar 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t026-p-vs-np/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You are handed a scheduling problem. Two hundred tasks, precedence constraints,
shared resources, and a deadline. Somebody wants an optimal schedule.&lt;/p&gt;
&lt;p&gt;Checking a proposed schedule takes seconds: walk the list, verify each
constraint, add up the makespan. Finding one is different. Your search runs for
a day and finds nothing. You try simulated annealing, then a genetic algorithm,
then branch and bound. Each gives good schedules and none gives the best one, and
you cannot tell whether the problem is hard or you are not clever enough.&lt;/p&gt;</description></item><item><title>The Hamming Bound and Error-Correcting Codes</title><link>https://cs.lozic.me/posts/t022-the-hamming-bound/</link><pubDate>Fri, 05 Mar 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t022-the-hamming-bound/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You are specifying ECC memory for a fleet. The vendor quotes SECDED: single
error correct, double error detect, at 8 check bits per 64 data bits. That is
12.5% overhead, and someone in the room asks the obvious question — why not
correct two errors? Or three? What does it cost?&lt;/p&gt;</description></item><item><title>The Noisy-Channel Coding Theorem</title><link>https://cs.lozic.me/posts/t021-the-noisy-channel-coding-theorem/</link><pubDate>Fri, 26 Feb 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t021-the-noisy-channel-coding-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your link drops 1% of bits. You need reliable delivery, so you triple every bit
and take a majority vote. Now an error needs two of three bits to flip, which
happens with probability about $3 \times 10^{-4}$ — better, but you paid 3x in
bandwidth and the error rate is still not zero.&lt;/p&gt;</description></item><item><title>Huffman Coding Is Optimal</title><link>https://cs.lozic.me/posts/t020-huffman-coding-is-optimal/</link><pubDate>Fri, 19 Feb 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t020-huffman-coding-is-optimal/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You are compressing a log file. The symbols are wildly skewed: &lt;code&gt;INFO&lt;/code&gt; is 90% of
the lines, &lt;code&gt;WARN&lt;/code&gt; is 9%, &lt;code&gt;ERROR&lt;/code&gt; and &lt;code&gt;FATAL&lt;/code&gt; split the rest. Fixed-width
two-bit codes give you exactly 2 bits per symbol, and entropy (T018) says the
floor is about 0.53 bits. There is a factor of four sitting there.&lt;/p&gt;</description></item><item><title>Balls Into Bins and the Power of Two Choices</title><link>https://cs.lozic.me/posts/t009-balls-into-bins/</link><pubDate>Fri, 12 Feb 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t009-balls-into-bins/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have 100 backends and a load balancer hashing request IDs to pick one.
Expected load per backend is exactly 1%, and you have checked the hash is good.&lt;/p&gt;</description></item><item><title>Reductions</title><link>https://cs.lozic.me/posts/t017-reductions/</link><pubDate>Fri, 05 Feb 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t017-reductions/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have a new problem. Your build system needs to decide whether two
configuration files can ever produce conflicting outputs. Or your linter needs to
decide whether a regex with backreferences can match a string of a given length.
Or your scheduler needs to decide whether a set of periodic tasks is feasible.&lt;/p&gt;</description></item><item><title>The Coffman Conditions</title><link>https://cs.lozic.me/posts/t051-the-coffman-conditions/</link><pubDate>Fri, 29 Jan 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t051-the-coffman-conditions/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Production is wedged. Not slow — wedged. CPU is at zero, no errors are being
logged, and two threads are sitting in a lock wait with no timeout. The stack
traces show thread A holding the account lock and waiting for the ledger lock,
thread B holding the ledger lock and waiting for the account lock. Somebody
restarts the service and it goes away.&lt;/p&gt;</description></item><item><title>Godel's Incompleteness Theorems</title><link>https://cs.lozic.me/posts/t015-godels-incompleteness-theorems/</link><pubDate>Fri, 22 Jan 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t015-godels-incompleteness-theorems/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;The proof assistant is failing you. You are trying to verify a piece of
concurrent code in Coq or Lean, and the tool will not accept your termination
argument. A colleague says, without much conviction, &amp;ldquo;well, Gödel — you can&amp;rsquo;t
prove everything anyway.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Kleene's Recursion Theorem</title><link>https://cs.lozic.me/posts/t014-kleenes-recursion-theorem/</link><pubDate>Fri, 15 Jan 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t014-kleenes-recursion-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Write a program that prints its own source code. No file I/O, no reading
&lt;code&gt;__file__&lt;/code&gt;, no cheating.&lt;/p&gt;
&lt;p&gt;The first attempt fails instantly. To print the source you must contain the
source, and then the containing text is also part of the source, so you must
contain that too. The regress is obviously infinite, and most people conclude
after ten minutes that the task is impossible.&lt;/p&gt;</description></item><item><title>The Chomsky Hierarchy</title><link>https://cs.lozic.me/posts/t038-the-chomsky-hierarchy/</link><pubDate>Fri, 08 Jan 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t038-the-chomsky-hierarchy/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;The most upvoted answer in Stack Overflow history is a refusal to answer. Someone
asked how to match nested HTML tags with a regular expression, and the reply is
a page of escalating horror about the centre not holding. It is funny, and it is
also a theorem, which is not obvious from reading it.&lt;/p&gt;</description></item><item><title>The Universal Machine</title><link>https://cs.lozic.me/posts/t013-the-universal-machine/</link><pubDate>Fri, 01 Jan 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t013-the-universal-machine/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You write a Python program. It runs. What ran it?&lt;/p&gt;
&lt;p&gt;CPython — itself a program, written in C, compiled to x86 instructions, executed
by a CPU whose control unit is arguably interpreting microcode, possibly inside
a virtual machine, on a kernel that scheduled it, all perhaps within a container
image. At no point in that stack does anyone find it strange that a program&amp;rsquo;s
job is running other programs. We build emulators, JITs, WebAssembly runtimes,
Docker, QEMU, and browsers that download and execute arbitrary code from
strangers, and treat every layer as ordinary engineering.&lt;/p&gt;</description></item><item><title>The Church–Turing Thesis</title><link>https://cs.lozic.me/posts/t012-the-church-turing-thesis/</link><pubDate>Fri, 25 Dec 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t012-the-church-turing-thesis/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Somebody says &amp;ldquo;CSS is Turing-complete&amp;rdquo; and the room splits. Half the people
treat it as a joke about a styling language, the other half as a serious claim
with consequences. Both reactions are common and only one is right.&lt;/p&gt;</description></item><item><title>Concentration Bounds (Chernoff/Hoeffding)</title><link>https://cs.lozic.me/posts/t008-concentration-bounds/</link><pubDate>Fri, 18 Dec 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t008-concentration-bounds/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your service handles four billion requests a month and you want the error rate.
Computing it exactly means a job over four billion log lines. Someone suggests
sampling ten thousand of them.&lt;/p&gt;</description></item><item><title>Amortized Analysis and the Potential Method</title><link>https://cs.lozic.me/posts/t006-amortized-analysis-and-the-potential-method/</link><pubDate>Fri, 11 Dec 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t006-amortized-analysis-and-the-potential-method/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have a dynamic array. &lt;code&gt;push&lt;/code&gt; writes one element and bumps a counter, which
is clearly $O(1)$ — except when the array is full, in which case it allocates a
new buffer of twice the size, copies every element across, and frees the old
one. That is $O(n)$.&lt;/p&gt;</description></item><item><title>Adversary Arguments</title><link>https://cs.lozic.me/posts/t005-adversary-arguments/</link><pubDate>Fri, 04 Dec 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t005-adversary-arguments/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You need the largest and second-largest element of an array. The obvious way is
two passes: $n-1$ comparisons for the max, then $n-2$ for the max of the rest.
That is $2n - 3$.&lt;/p&gt;</description></item><item><title>Linearity of Expectation</title><link>https://cs.lozic.me/posts/t007-linearity-of-expectation/</link><pubDate>Fri, 27 Nov 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t007-linearity-of-expectation/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You are analyzing a hash table. You want the expected number of buckets that end
up empty. The bucket occupancies are all tangled together — one key landing in
bucket 3 makes every other bucket slightly less likely to be chosen — and the
dependencies look like they will make the sum intractable.&lt;/p&gt;</description></item><item><title>The Omega(n log n) Comparison-Sort Bound</title><link>https://cs.lozic.me/posts/t003-comparison-sort-lower-bound/</link><pubDate>Fri, 20 Nov 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t003-comparison-sort-lower-bound/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Every general-purpose sort in every standard library is $O(n \log n)$.
Timsort, introsort, pdqsort, merge sort, heapsort. Decades of work by extremely
motivated people, an enormous amount of money riding on it, and they all land on
the same exponent.&lt;/p&gt;</description></item><item><title>Shannon Entropy and the Source Coding Theorem</title><link>https://cs.lozic.me/posts/t018-shannon-entropy-and-source-coding/</link><pubDate>Fri, 13 Nov 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t018-shannon-entropy-and-source-coding/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You gzip a log file and it drops to 8% of its original size. You gzip the
already-gzipped file and it gets slightly &lt;em&gt;bigger&lt;/em&gt;. Somebody asks why, and the
honest answer you have is &amp;ldquo;because it&amp;rsquo;s already compressed,&amp;rdquo; which is a
restatement, not a reason.&lt;/p&gt;</description></item><item><title>The Birthday Bound</title><link>https://cs.lozic.me/posts/t074-the-birthday-bound/</link><pubDate>Fri, 06 Nov 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t074-the-birthday-bound/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You need a short ID for uploads. Eight hex characters feels generous, so you
take the first 32 bits of a hash and move on.&lt;/p&gt;
&lt;p&gt;At about 80,000 uploads, two files collide, and one of them silently overwrites
the other, and the bug report says the customer&amp;rsquo;s invoice contains someone
else&amp;rsquo;s line items.&lt;/p&gt;</description></item><item><title>The Two Generals Problem</title><link>https://cs.lozic.me/posts/t061-the-two-generals-problem/</link><pubDate>Fri, 30 Oct 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t061-the-two-generals-problem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You call the payment API. The request goes out. Nothing comes back.&lt;/p&gt;
&lt;p&gt;Did the charge happen? You genuinely do not know. Retrying might double-charge
the customer. Not retrying might drop the payment. There is no third option
available, and no amount of care in your client library creates one.&lt;/p&gt;</description></item><item><title>Conway's Law</title><link>https://cs.lozic.me/posts/t089-conways-law/</link><pubDate>Fri, 23 Oct 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t089-conways-law/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;The service has four layers. Nobody designed four layers. There were four teams.&lt;/p&gt;
&lt;p&gt;Or the version that arrives in a postmortem: the outage happened at the boundary
between two services, in the retry logic, because each side assumed the other
owned the deduplication. Both teams were right about their own component and
neither had ever been in a room with the other.&lt;/p&gt;</description></item><item><title>Rice's Theorem</title><link>https://cs.lozic.me/posts/t011-rices-theorem/</link><pubDate>Fri, 16 Oct 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t011-rices-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;The security team asks for a scanner with no false positives and no false
negatives. Every piece of malware caught, nothing legitimate quarantined.&lt;/p&gt;
&lt;p&gt;The platform team asks whether the analyzer can flag every function that
performs I/O, so the pure ones can be cached automatically.&lt;/p&gt;</description></item><item><title>The Halting Problem</title><link>https://cs.lozic.me/posts/t010-the-halting-problem/</link><pubDate>Fri, 09 Oct 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t010-the-halting-problem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your CI has a test that hangs. Not fails — hangs. Somebody suggests the obvious
fix: before running a test, check whether it terminates, and skip it if not.&lt;/p&gt;</description></item><item><title>Cantor's Diagonal Argument</title><link>https://cs.lozic.me/posts/t002-cantors-diagonal-argument/</link><pubDate>Fri, 02 Oct 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t002-cantors-diagonal-argument/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Somebody proposes a tool that will find all the bugs. Not most bugs. All of
them. The pitch has the shape: we enumerate the failure modes, we write a
checker for each, we keep adding checkers, and the set of undetected bugs
shrinks toward zero.&lt;/p&gt;</description></item><item><title>Gustafson's Law and the Universal Scalability Law</title><link>https://cs.lozic.me/posts/t082-gustafsons-law-and-the-usl/</link><pubDate>Fri, 25 Sep 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t082-gustafsons-law-and-the-usl/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Two measurements that both look like they break the previous post.&lt;/p&gt;
&lt;p&gt;The first: your Spark job takes four hours on ten nodes. You move it to a
hundred nodes and it still takes four hours — but it is now processing ten times
the data. &lt;a href="https://cs.lozic.me/posts/t081-amdahls-law/"&gt;T081&lt;/a&gt; said the ceiling was
20×, and nobody hit a ceiling.&lt;/p&gt;</description></item><item><title>Amdahl's Law</title><link>https://cs.lozic.me/posts/t081-amdahls-law/</link><pubDate>Fri, 18 Sep 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t081-amdahls-law/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;The profile was unambiguous: 94% of wall-clock time in one loop, and the loop&amp;rsquo;s
iterations are independent. You parallelise it across sixteen cores, the flame
graph flattens exactly as predicted, and the end-to-end time goes from 40
seconds to 17.&lt;/p&gt;</description></item><item><title>Hyrum's Law and Postel's Principle</title><link>https://cs.lozic.me/posts/t091-hyrums-law-and-postels-principle/</link><pubDate>Fri, 11 Sep 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t091-hyrums-law-and-postels-principle/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You fix a typo in an error message. Four days later a customer escalates:
their alerting pipeline greps for the old string and has gone quiet.&lt;/p&gt;
&lt;p&gt;Or you make a function faster, and a test suite that had been green for two years
starts failing intermittently — not because the function is wrong, but because
it used to be slow enough to hide a race.&lt;/p&gt;</description></item><item><title>No Universal Lossless Compressor</title><link>https://cs.lozic.me/posts/t004-no-universal-lossless-compressor/</link><pubDate>Fri, 04 Sep 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t004-no-universal-lossless-compressor/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Someone sends you a pitch deck. The claim is a compression algorithm that
reduces &lt;em&gt;any&lt;/em&gt; file by a guaranteed ratio, and — this is always the tell — that
it can be applied repeatedly. Four gigabytes down to one, then one down to two
fifty, and so on until the whole film is a few hundred bytes and a clever
decoder.&lt;/p&gt;</description></item><item><title>The Pigeonhole Principle</title><link>https://cs.lozic.me/posts/t001-pigeonhole-principle/</link><pubDate>Fri, 28 Aug 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t001-pigeonhole-principle/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You are asked to build a cache key. You have a 64-bit hash, and someone on the
team says: &amp;ldquo;collisions are basically impossible, there are eighteen quintillion
values.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Or: your service assigns short IDs to uploads, six characters of base-36, and
you are wondering when you need to worry.&lt;/p&gt;</description></item></channel></rss>