<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Luke Hannan</title>
    <subtitle>Biostatistics notes on clinical trials, microbiome analysis, and reproducible research</subtitle>
    <link rel="self" type="application/atom+xml" href="https://luhann.com/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://luhann.com/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-07-01T00:00:00+00:00</updated>
    <id>https://luhann.com/atom.xml</id>
    <entry xml:lang="en">
        <title>On Transparent Metagenomics</title>
        <published>2026-07-01T00:00:00+00:00</published>
        <updated>2026-07-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Luke Hannan
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://luhann.com/posts/transparent-metagenomics/"/>
        <id>https://luhann.com/posts/transparent-metagenomics/</id>
        
        <content type="html" xml:base="https://luhann.com/posts/transparent-metagenomics/">&lt;h2 id=&quot;a-note-on-terminology&quot;&gt;A Note on Terminology&lt;&#x2F;h2&gt;
&lt;p&gt;The terminology used to describe microbial communities is diverse and often subject to debate. Common proposed definitions are:&lt;&#x2F;p&gt;
&lt;dl style=&quot;margin-left: 2cm;&quot;&gt;
  &lt;dt style=&quot;font-weight: bold; font-variant: small-caps;&quot;&gt;Microbiota&lt;&#x2F;dt&gt;
  &lt;dd&gt;The assemblage of living microorganisms (bacteria, archaea, fungi, and viruses) present in a defined environment.&lt;&#x2F;dd&gt;
  &lt;dt style=&quot;font-weight: bold; font-variant: small-caps;&quot;&gt;Metagenome&lt;&#x2F;dt&gt;
  &lt;dd&gt;Strictly, the collection of genes and genomes from the members of a microbiota.&lt;&#x2F;dd&gt;
  &lt;dt style=&quot;font-weight: bold; font-variant: small-caps;&quot;&gt;Microbiome&lt;&#x2F;dt&gt;
  &lt;dd&gt;The entire ecosystem, encompassing the microbiota, their genomes, and the surrounding environmental conditions.&lt;&#x2F;dd&gt;
&lt;&#x2F;dl&gt;
&lt;p&gt;While these biological distinctions are important, in the literature (and within this thesis) the terms &lt;em&gt;microbiome data&lt;&#x2F;em&gt; and
&lt;em&gt;metagenomic data&lt;&#x2F;em&gt; are frequently used interchangeably to refer to the high-throughput sequencing read count tables derived
from these communities, regardless of whether the underlying biological material was the organisms themselves or their
extracted genetic material.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h2&gt;
&lt;p&gt;In an ideal world, every research project would be perfectly reproducible. That is, anyone should
be able to take the detailed description of your methods and reproduce your results, either with your data or with
their own data. Much has been written about reproducible research, and in fact the field of
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Metascience&quot;&gt;metascience&lt;&#x2F;a&gt; itself is primarily concerned with reproducibility.
The concepts that I will describe here are &lt;em&gt;not new&lt;&#x2F;em&gt;. However, while the general ideas behind reproducible research are
broadly the same in every field, transparent and reproducible reporting in metagenomics requires substantially more effort.
&lt;aside class=&quot;aside&quot;&gt;
  &lt;p&gt;Some recommended reading on reproducibility in general:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;book.the-turing-way.org&#x2F;reproducible-research&#x2F;reproducible-research&quot;&gt;The Turing Way: Guide for Reproducible Research&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;statsepi.substack.com&#x2F;p&#x2F;reproducible-data-analysis-workflows&quot;&gt;Reproducible Data Analysis Workflows&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;journals.plos.org&#x2F;ploscompbiol&#x2F;article?id=10.1371&#x2F;journal.pcbi.1003285&quot;&gt;Ten Simple Rules for Reproducible Computational Research&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The first resource is a particularly thorough introduction to reproducible data science.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;In metascience, a distinction between reproducibility and replicability is often made. Reproducibility is whether
other scientists (including future you) are able to reproduce your analysis as-is, with your given data and code.
Reproducibility, therefore, is primarily a matter of good house-keeping. Replicability on the other hand is more of a scientific
concern. Are others able to find results that match yours in study populations other than the original study population?
Both reproducibility and replicability are important, but we’ll start with reproducibility given that without reproducibility
your results cannot be replicable. Reproducibility is necessary but not sufficient for replicability.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;on-sampling-metagenomic-data&quot;&gt;On Sampling Metagenomic Data&lt;&#x2F;h2&gt;
&lt;p&gt;While the majority of this post will deal with &lt;em&gt;analysing and reporting&lt;&#x2F;em&gt; on metagenomic data, it is important to recognise
that metagenomic data is extracted from real biological samples. The physical act of sampling is inherently messy, and
often the stage which can most drastically alter study conclusions based on the decisions made. The journey to reproducible
research begins the moment you collect that sample, long before the data ever gets to a computer.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;aside class=&quot;aside&quot;&gt;
  &lt;p&gt;Documenting this step is critical and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gensc.org&#x2F;&quot;&gt;Genomic Standards Consortium&lt;&#x2F;a&gt; has provided some guidance
on the appropriate information to document, see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gensc.org&#x2F;pages&#x2F;standards-intro.html&quot;&gt;Minimum Information about any (X) Sequence (MIxS) standard.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;

Consider the simple impact of storage, on two samples from the same individual. If sample &lt;code&gt;x&lt;&#x2F;code&gt; remains at room temperature
on a lab bench-top for two hours while sample &lt;code&gt;y&lt;&#x2F;code&gt; is immediately stored in a -80°C freezer, you may generate drastically
different microbial compositions. Differential degradation rates, sample lysis, as well as a host of other factors can
impact extraction and replication efficiency. No downstream bioinformatics pipeline, regardless of how sophisticated, can
magically recover information that is lost prior to sequencing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bit-rot-and-the-end-of-things&quot;&gt;Bit-rot and the End of Things&lt;&#x2F;h2&gt;
&lt;p&gt;Before I get to suggestions for a transparent metagenomic analysis workflow I need to point out that reproducibility is
only as good as your &lt;strong&gt;available documentation.&lt;&#x2F;strong&gt; If you have great documentation on your workflow, but absolutely nobody
can find it then unfortunately your study cannot be reproduced. I have lost track of the number of times I have searched
for detailed methods for a metagenomics manuscript only to be met with this:&lt;&#x2F;p&gt;
&lt;div style=&quot;border: 1px solid var(--site-line); border-radius: 6px; padding: 1.75rem 2rem; margin: 2rem 0; background: var(--site-surface);&quot;&gt;
  &lt;p class=&quot;eyebrow&quot;&gt;Error 404&lt;&#x2F;p&gt;
  &lt;p style=&quot;font-size: 2rem; line-height: 1.05; margin: 0.15rem 0 0.6rem; font-family: &#x27;EB Garamond&#x27;, Georgia, serif; font-weight: 700;&quot;&gt;Page Not Found&lt;&#x2F;p&gt;
  &lt;p style=&quot;margin: 0;&quot;&gt;The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Often the links to the supplementary data maintained by the journals themselves point to moved or unavailable resources.
As an author, while this is not your fault, it is ultimately &lt;strong&gt;your responsibility.&lt;&#x2F;strong&gt; Therefore, rule number one of
transparent metagenomics is:
&lt;aside class=&quot;aside left&quot;&gt;
  &lt;p&gt;&lt;strong&gt;Rule 1&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You must have a permanent link to your detailed methods documentation that is attached directly to the manuscript and
always points to the current documentation for said analysis.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Ideally this would be located in the same place that your final analysis scripts are located, and the link to both of those
things is somewhat permanent. A good option for this is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;zenodo.org&#x2F;&quot;&gt;zenodo&lt;&#x2F;a&gt;. &lt;span class=&quot;margin&quot;&gt;
  See
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;help.zenodo.org&#x2F;docs&#x2F;github&#x2F;&quot;&gt;zenodo’s github documentation&lt;&#x2F;a&gt; for information on how to do this.
&lt;&#x2F;span&gt;
 Zenodo,
will not only provide a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.doi.org&#x2F;&quot;&gt;Digital Object Identifier (DOI)&lt;&#x2F;a&gt; that links directly to your chosen
repository, but will archive the version of your repository at the time you choose to publish on zenodo. The exact data
and code you used to generate your findings, will be permanently linked to a specific identifier that is not tied to
account names (which may change) or proprietary software enterprises. &lt;span class=&quot;margin left&quot;&gt;
  GitHub is usually pretty
good at tracking account name changes, but sometimes it can still lead to dead links.
&lt;&#x2F;span&gt;
 Now that we are in
agreement about having permanent documentation, you may be asking what it is you should document.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pipeline-pathways&quot;&gt;Pipeline Pathways&lt;&#x2F;h2&gt;
&lt;p&gt;At a minimum, the exact code you used for your analysis should be documented. However, metagenomic analyses typically
involve complex data cleaning and preparation before any actual analysis. The decisions made in the data processing
pipeline can &lt;strong&gt;drastically&lt;&#x2F;strong&gt; alter your final results. Every single one of the steps in the pipeline below has the potential
to alter the final results if different choices are made. Much like Gelman’s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sites.stat.columbia.edu&#x2F;gelman&#x2F;research&#x2F;unpublished&#x2F;p_hacking.pdf&quot;&gt;garden of forking paths&lt;&#x2F;a&gt;,
although in this instance it is more like a forest of forking paths.&lt;&#x2F;p&gt;
&lt;figure id=&quot;pipeline-figure&quot;&gt;
&lt;div class=&quot;tufte-plot-container&quot;&gt;
  &lt;div class=&quot;tufte-plot-item&quot;&gt;
    
&lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; viewBox=&quot;0 0 640 670&quot; role=&quot;img&quot; aria-label=&quot;Metagenomic sequencing pipeline&quot; style=&quot;width:100%;max-width:640px;display:block;margin:0 auto;&quot;&gt;
  &lt;defs&gt;
    &lt;marker id=&quot;mg-arrow&quot; markerWidth=&quot;8&quot; markerHeight=&quot;6&quot; refX=&quot;7&quot; refY=&quot;3&quot; orient=&quot;auto&quot;&gt;
      &lt;polygon points=&quot;0 0, 8 3, 0 6&quot; fill=&quot;var(--site-muted)&quot; &#x2F;&gt;
    &lt;&#x2F;marker&gt;
  &lt;&#x2F;defs&gt;
  &lt;style&gt;
    .mg text { font-family: &#x27;IBM Plex Sans&#x27;, Arial, sans-serif; }
    .mg-title { fill: var(--site-headline); font-size: 12.5px; font-weight: 600; }
    .mg-detail { fill: var(--site-muted); font-size: 10.5px; }
    .mg-phase-label { font-size: 9px; font-weight: 700; letter-spacing: 0.13em; }
  &lt;&#x2F;style&gt;
  &lt;g class=&quot;mg&quot;&gt;

    &lt;!-- ── WET LAB ── --&gt;
    &lt;rect x=&quot;100&quot; y=&quot;10&quot; width=&quot;440&quot; height=&quot;20&quot; rx=&quot;3&quot; fill=&quot;#cf7d55&quot; fill-opacity=&quot;0.12&quot; stroke=&quot;#cf7d55&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;text x=&quot;320&quot; y=&quot;24&quot; text-anchor=&quot;middle&quot; class=&quot;mg-phase-label&quot; fill=&quot;#cf7d55&quot;&gt;WET LAB&lt;&#x2F;text&gt;

    &lt;!-- Step 1 --&gt;
    &lt;rect x=&quot;100&quot; y=&quot;38&quot; width=&quot;440&quot; height=&quot;52&quot; rx=&quot;3&quot; fill=&quot;var(--site-surface-soft)&quot; stroke=&quot;var(--site-line)&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;rect x=&quot;100&quot; y=&quot;38&quot; width=&quot;5&quot;   height=&quot;52&quot; rx=&quot;2&quot; fill=&quot;#cf7d55&quot;&#x2F;&gt;
    &lt;text x=&quot;120&quot; y=&quot;61&quot;  class=&quot;mg-title&quot;&gt;Sample Collection&lt;&#x2F;text&gt;
    &lt;text x=&quot;120&quot; y=&quot;79&quot;  class=&quot;mg-detail&quot;&gt;Stool, tissue, environmental, or clinical specimens&lt;&#x2F;text&gt;
    &lt;line x1=&quot;320&quot; y1=&quot;90&quot;  x2=&quot;320&quot; y2=&quot;106&quot; stroke=&quot;var(--site-muted)&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#mg-arrow)&quot;&#x2F;&gt;

    &lt;!-- Step 2 --&gt;
    &lt;rect x=&quot;100&quot; y=&quot;108&quot; width=&quot;440&quot; height=&quot;52&quot; rx=&quot;3&quot; fill=&quot;var(--site-surface-soft)&quot; stroke=&quot;var(--site-line)&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;rect x=&quot;100&quot; y=&quot;108&quot; width=&quot;5&quot;   height=&quot;52&quot; rx=&quot;2&quot; fill=&quot;#cf7d55&quot;&#x2F;&gt;
    &lt;text x=&quot;120&quot; y=&quot;131&quot; class=&quot;mg-title&quot;&gt;DNA &#x2F; RNA Extraction&lt;&#x2F;text&gt;
    &lt;text x=&quot;120&quot; y=&quot;149&quot; class=&quot;mg-detail&quot;&gt;Bead-beating, enzymatic lysis, column purification&lt;&#x2F;text&gt;
    &lt;line x1=&quot;320&quot; y1=&quot;160&quot; x2=&quot;320&quot; y2=&quot;176&quot; stroke=&quot;var(--site-muted)&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#mg-arrow)&quot;&#x2F;&gt;

    &lt;!-- Step 3 --&gt;
    &lt;rect x=&quot;100&quot; y=&quot;178&quot; width=&quot;440&quot; height=&quot;52&quot; rx=&quot;3&quot; fill=&quot;var(--site-surface-soft)&quot; stroke=&quot;var(--site-line)&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;rect x=&quot;100&quot; y=&quot;178&quot; width=&quot;5&quot;   height=&quot;52&quot; rx=&quot;2&quot; fill=&quot;#cf7d55&quot;&#x2F;&gt;
    &lt;text x=&quot;120&quot; y=&quot;201&quot; class=&quot;mg-title&quot;&gt;Library Preparation &amp;amp; Sequencing&lt;&#x2F;text&gt;
    &lt;text x=&quot;120&quot; y=&quot;219&quot; class=&quot;mg-detail&quot;&gt;Illumina short-read · Nanopore &#x2F; PacBio long-read&lt;&#x2F;text&gt;
    &lt;line x1=&quot;320&quot; y1=&quot;230&quot; x2=&quot;320&quot; y2=&quot;258&quot; stroke=&quot;var(--site-muted)&quot; stroke-width=&quot;1.5&quot; stroke-dasharray=&quot;4 3&quot; marker-end=&quot;url(#mg-arrow)&quot;&#x2F;&gt;

    &lt;!-- ── BIOINFORMATICS ── --&gt;
    &lt;rect x=&quot;100&quot; y=&quot;260&quot; width=&quot;440&quot; height=&quot;20&quot; rx=&quot;3&quot; fill=&quot;#4f8dbf&quot; fill-opacity=&quot;0.12&quot; stroke=&quot;#4f8dbf&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;text x=&quot;320&quot; y=&quot;274&quot; text-anchor=&quot;middle&quot; class=&quot;mg-phase-label&quot; fill=&quot;#4f8dbf&quot;&gt;BIOINFORMATICS PROCESSING&lt;&#x2F;text&gt;

    &lt;!-- Step 4 --&gt;
    &lt;rect x=&quot;100&quot; y=&quot;288&quot; width=&quot;440&quot; height=&quot;52&quot; rx=&quot;3&quot; fill=&quot;var(--site-surface-soft)&quot; stroke=&quot;var(--site-line)&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;rect x=&quot;100&quot; y=&quot;288&quot; width=&quot;5&quot;   height=&quot;52&quot; rx=&quot;2&quot; fill=&quot;#4f8dbf&quot;&#x2F;&gt;
    &lt;text x=&quot;120&quot; y=&quot;311&quot; class=&quot;mg-title&quot;&gt;Quality Control&lt;&#x2F;text&gt;
    &lt;text x=&quot;120&quot; y=&quot;329&quot; class=&quot;mg-detail&quot;&gt;FastQC · fastp · Trimmomatic · MultiQC&lt;&#x2F;text&gt;
    &lt;line x1=&quot;320&quot; y1=&quot;340&quot; x2=&quot;320&quot; y2=&quot;356&quot; stroke=&quot;var(--site-muted)&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#mg-arrow)&quot;&#x2F;&gt;

    &lt;!-- Step 5 --&gt;
    &lt;rect x=&quot;100&quot; y=&quot;358&quot; width=&quot;440&quot; height=&quot;52&quot; rx=&quot;3&quot; fill=&quot;var(--site-surface-soft)&quot; stroke=&quot;var(--site-line)&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;rect x=&quot;100&quot; y=&quot;358&quot; width=&quot;5&quot;   height=&quot;52&quot; rx=&quot;2&quot; fill=&quot;#4f8dbf&quot;&#x2F;&gt;
    &lt;text x=&quot;120&quot; y=&quot;381&quot; class=&quot;mg-title&quot;&gt;Host Read Removal&lt;&#x2F;text&gt;
    &lt;text x=&quot;120&quot; y=&quot;399&quot; class=&quot;mg-detail&quot;&gt;Bowtie2 · BWA · KneadData — align reads to host genome&lt;&#x2F;text&gt;
    &lt;line x1=&quot;320&quot; y1=&quot;410&quot; x2=&quot;320&quot; y2=&quot;426&quot; stroke=&quot;var(--site-muted)&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#mg-arrow)&quot;&#x2F;&gt;

    &lt;!-- Step 6 --&gt;
    &lt;rect x=&quot;100&quot; y=&quot;428&quot; width=&quot;440&quot; height=&quot;52&quot; rx=&quot;3&quot; fill=&quot;var(--site-surface-soft)&quot; stroke=&quot;var(--site-line)&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;rect x=&quot;100&quot; y=&quot;428&quot; width=&quot;5&quot;   height=&quot;52&quot; rx=&quot;2&quot; fill=&quot;#4f8dbf&quot;&#x2F;&gt;
    &lt;text x=&quot;120&quot; y=&quot;451&quot; class=&quot;mg-title&quot;&gt;Taxonomic Profiling&lt;&#x2F;text&gt;
    &lt;text x=&quot;120&quot; y=&quot;469&quot; class=&quot;mg-detail&quot;&gt;MetaPhlAn4 · Kraken2 &#x2F; Bracken · DIAMOND&lt;&#x2F;text&gt;
    &lt;line x1=&quot;320&quot; y1=&quot;480&quot; x2=&quot;320&quot; y2=&quot;496&quot; stroke=&quot;var(--site-muted)&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#mg-arrow)&quot;&#x2F;&gt;

    &lt;!-- Step 7 --&gt;
    &lt;rect x=&quot;100&quot; y=&quot;498&quot; width=&quot;440&quot; height=&quot;52&quot; rx=&quot;3&quot; fill=&quot;var(--site-surface-soft)&quot; stroke=&quot;var(--site-line)&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;rect x=&quot;100&quot; y=&quot;498&quot; width=&quot;5&quot;   height=&quot;52&quot; rx=&quot;2&quot; fill=&quot;#4f8dbf&quot;&#x2F;&gt;
    &lt;text x=&quot;120&quot; y=&quot;521&quot; class=&quot;mg-title&quot;&gt;Functional Annotation&lt;&#x2F;text&gt;
    &lt;text x=&quot;120&quot; y=&quot;539&quot; class=&quot;mg-detail&quot;&gt;HUMAnN3 · eggNOG-mapper · PROKKA&lt;&#x2F;text&gt;
    &lt;line x1=&quot;320&quot; y1=&quot;550&quot; x2=&quot;320&quot; y2=&quot;578&quot; stroke=&quot;var(--site-muted)&quot; stroke-width=&quot;1.5&quot; stroke-dasharray=&quot;4 3&quot; marker-end=&quot;url(#mg-arrow)&quot;&#x2F;&gt;

    &lt;!-- ── STATISTICAL ANALYSIS ── --&gt;
    &lt;rect x=&quot;100&quot; y=&quot;580&quot; width=&quot;440&quot; height=&quot;20&quot; rx=&quot;3&quot; fill=&quot;#a51c30&quot; fill-opacity=&quot;0.12&quot; stroke=&quot;#a51c30&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;text x=&quot;320&quot; y=&quot;594&quot; text-anchor=&quot;middle&quot; class=&quot;mg-phase-label&quot; fill=&quot;#a51c30&quot;&gt;STATISTICAL ANALYSIS&lt;&#x2F;text&gt;

    &lt;!-- Step 8 --&gt;
    &lt;rect x=&quot;100&quot; y=&quot;608&quot; width=&quot;440&quot; height=&quot;52&quot; rx=&quot;3&quot; fill=&quot;var(--site-surface-soft)&quot; stroke=&quot;var(--site-line)&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;rect x=&quot;100&quot; y=&quot;608&quot; width=&quot;5&quot;   height=&quot;52&quot; rx=&quot;2&quot; fill=&quot;#a51c30&quot;&#x2F;&gt;
    &lt;text x=&quot;120&quot; y=&quot;631&quot; class=&quot;mg-title&quot;&gt;Diversity &amp;amp; Differential Abundance Analysis&lt;&#x2F;text&gt;
    &lt;text x=&quot;120&quot; y=&quot;649&quot; class=&quot;mg-detail&quot;&gt;vegan · MaAsLin3 · ANCOMBC2 · breakaway · radEmu&lt;&#x2F;text&gt;

  &lt;&#x2F;g&gt;
&lt;&#x2F;svg&gt;

  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;figcaption style=&quot;text-align:center;font-size:0.8rem;color:var(--site-muted);margin-top:0.6rem;&quot;&gt;A simplified shotgun
metagenomics analysis pipeline. Dashed arrows indicate a phase transition.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;To complicate matters further, metagenomic analyses often depend on externally maintained resources for many steps in the
QC pipeline. For example, taxonomic classification often makes use of reference databases like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.arb-silva.de&#x2F;&quot;&gt;silva&lt;&#x2F;a&gt;
or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ngdc.cncb.ac.cn&#x2F;databasecommons&#x2F;database&#x2F;id&#x2F;3120&quot;&gt;greengenes&lt;&#x2F;a&gt; — for 16S rRNA sequencing. Thankfully,
all of these resources are typically versioned, and therefore you merely need to document the specific version of the
resource that you used.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;aside class=&quot;aside&quot;&gt;
  &lt;p&gt;See &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doi.org&#x2F;10.1186&#x2F;s12915-014-0087-z&quot;&gt;Salter, et al., 2014. Reagent and laboratory contamination can critically
impact sequence-based microbiome analyses. BMC Biol 12.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;

For quality control and contaminant checking, it is not enough to just record the names and versions of software that you use.
Ideally you should report both the code that you used to identify contaminants, and the final list of contaminants so
that readers of your work can determine for themselves if your approach and its results are reasonable.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;versions-and-the-beginning-of-things&quot;&gt;Versions and the Beginning of Things&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;aside class=&quot;aside&quot;&gt;
  &lt;p&gt;It goes without saying, but I’ll say it anyway — the
choice of analysis software will also drastically alter your results.  See &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doi.org&#x2F;10.1038&#x2F;s41467-022-28034-z&quot;&gt;Nearing, et al., 2022. Microbiome differential abundance methods produce
different results across 38 datasets. Nat Commun 13, 342.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;

Metagenomic software moves fast. Not only are there new software packages for metagenomic analysis almost weekly, but
existing software updates constantly. The results obtained by a script written today, might not be reproduced if someone —
including yourself — re-runs the script in six months time. In fact, many R and python users can attest to the fact that
code written today might &lt;strong&gt;not even work&lt;&#x2F;strong&gt; six months in the future, and a typical metagenomic analysis script will
depend on a large number of external software packages. An example of a very simple analysis metagenomic analysis script
is shown below.&lt;&#x2F;p&gt;
&lt;aside class=&quot;aside left&quot;&gt;
  &lt;p&gt;I’m omitting the full script for brevity, but assume that data cleaning and contaminant removal has already been done for
the following analysis script.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;r&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;library&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;TreeSummarizedExperiment&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;library&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;mia&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;library&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;breakaway&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;library&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;vegan&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;library&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;maaslin3&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;library&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;ANCOMBC&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;library&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;radEmu&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;# I&amp;#39;ve excluded non-metagenomic software for brevity, but each of those packages have their own defaults.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;root&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; rprojroot&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;is_rstudio_project&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;tse_analysis&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; readRDS&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;root&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;find_file&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;data&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;processed&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;tse_analysis.rds&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;# This is a typical command call&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;output_ancom&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; ancombc2&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  data&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; tse_analysis&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  assay_name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;counts&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  fix_formula&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;hiv_status + bmi + age + months_post_partum&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  p_adj_method&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;holm&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  pseudo_sens&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; TRUE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  group&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;hiv_status&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  struc_zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; TRUE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  neg_lb&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; TRUE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  alpha&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0.05&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All of the external libraries used above, as well as the function calls (like &lt;code&gt;ancombc2()&lt;&#x2F;code&gt;) have two things that make
reproducibility hard, implicit versions and implicit defaults. The options shown in the &lt;code&gt;ancombc2&lt;&#x2F;code&gt; are not all of the
available options in &lt;code&gt;ancombc2&lt;&#x2F;code&gt; and as such, any option that is not explicitly set will be set to the default value
the function expects. &lt;span class=&quot;margin&quot;&gt;
  See &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bioconductor.posit.co&#x2F;packages&#x2F;3.19&#x2F;bioc&#x2F;vignettes&#x2F;ANCOMBC&#x2F;inst&#x2F;doc&#x2F;ANCOMBC2.html&quot;&gt;this vignette&lt;&#x2F;a&gt;
for all the options you can set in &lt;code&gt;ancombc2&lt;&#x2F;code&gt;.
&lt;&#x2F;span&gt;
 If the default values have changed since you last ran your script
then your results will unfortunately also change.&lt;&#x2F;p&gt;
&lt;p&gt;Which leads me to rule number two for transparent metagenomics:
&lt;aside class=&quot;aside left&quot;&gt;
  &lt;p&gt;&lt;strong&gt;Rule 2&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Everything&lt;&#x2F;strong&gt; should have a version number attached.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Ideally one would document all of the default options of all of the software used, but for modern metagenomics analysis
contexts, this is almost always not a reasonable nor productive use of time. I would like to keep this post as
software agnostic as possible, and therefore won’t go into detailed description of all of the available software
for recording software versions there are a few standouts that I can recommended.&lt;&#x2F;p&gt;
&lt;p&gt;If you are working in R only, then the venerable &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rstudio.github.io&#x2F;renv&#x2F;articles&#x2F;renv.html&quot;&gt;renv&lt;&#x2F;a&gt; — via the
&lt;code&gt;renv.lock&lt;&#x2F;code&gt; file — will record the versions of all of the R packages you use and all of their dependencies. In this way,
you can hand off an R project to someone else and they should, in theory, be able to download and install the exact versions
of all of the packages you used &lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.ropensci.org&#x2F;rix&#x2F;&quot;&gt;rix&lt;&#x2F;a&gt; is a modern contender, that aims to not only
snapshot your R packages, but also all of the required system dependencies for your analysis.
&lt;span class=&quot;margin&quot;&gt;
  &lt;code&gt;rix&lt;&#x2F;code&gt; uses the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;&quot;&gt;nix&lt;&#x2F;a&gt; package manager and sources under the hood, but attempts to
simplify interacting with &lt;code&gt;nix&lt;&#x2F;code&gt; through &lt;code&gt;rix&lt;&#x2F;code&gt;.
&lt;&#x2F;span&gt;
 Finally, if you find yourself working in more than one language
often, then you might find &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pixi.prefix.dev&#x2F;latest&#x2F;&quot;&gt;pixi&lt;&#x2F;a&gt; to provide a more frictionless versioning environment
that the previous two packages.&lt;&#x2F;p&gt;
&lt;p&gt;A minimal &lt;code&gt;pixi.toml&lt;&#x2F;code&gt; to specify the required versions of the dependencies for your project. &lt;code&gt;pixi&lt;&#x2F;code&gt; will use this file
to generate a &lt;code&gt;pixi.lock&lt;&#x2F;code&gt; file that has the exact versions and locations of all of your dependencies. With the &lt;code&gt;pixi.toml&lt;&#x2F;code&gt;
and &lt;code&gt;pixi.lock&lt;&#x2F;code&gt; files, any collaborator can just type &lt;code&gt;pixi install&lt;&#x2F;code&gt; to reproduce the entire environment.&lt;&#x2F;p&gt;
&lt;aside class=&quot;aside left&quot;&gt;
  &lt;p&gt;Pixi has some other very helpful features that I recommended investigating, such as task running and shell invocation.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;toml&quot; data-name=&quot;minimal_setup.toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;workspace&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;authors = [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;Luke Hannan &amp;lt;luke@lukehannan.com&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;channels = [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;conda-forge&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;bioconda&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;name =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;microbiome&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;platforms = [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;linux-64&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;version =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;0.1.0&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;dependencies&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;fastexcel =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;&amp;gt;=0.19.0,&amp;lt;0.20&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;polars =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;&amp;gt;=1.40.1,&amp;lt;2&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;r =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;&amp;gt;=4.5,&amp;lt;4.6&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;r-qiime2r =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;&amp;gt;=0.99.20,&amp;lt;0.100&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;bioconductor-treesummarizedexperiment =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;&amp;gt;=2.18.0,&amp;lt;3&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;bioconductor-decontam =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;&amp;gt;=1.30.0,&amp;lt;2&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div class=&quot;tufte-plot-container&quot;&gt;
  &lt;div class=&quot;tufte-plot-item&quot;&gt;
    &lt;img src=&quot;images&#x2F;pixi-workflow.gif&quot; alt=&quot;Setting up an analysis environment with pixi&quot; style=&quot;max-width: 700px; width: 110%; margin-left: -5%; margin-right: -5%; height: auto; border-radius: 7px;&quot;&gt;
  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;The tools described above are both useful and incredibly powerful, however all of the tools mentioned far are mainly
designed to version one part of the full lifecycle of a metagenomic analysis. Often, bioinformatics processing happens
separately to data exploration and analysis and as such are versioned separately. Domain specific languages aim to address
that challenge by allowing you to specify the &lt;strong&gt;entire data preparation and analysis pipeline&lt;&#x2F;strong&gt; in a DSL.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;snakemake.readthedocs.io&#x2F;en&#x2F;stable&#x2F;&quot;&gt;Snakemake&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.nextflow.io&#x2F;&quot;&gt;Nextflow&lt;&#x2F;a&gt; are the two
most popular workflow managers in bioinformatics. The example below shows how &lt;code&gt;snakemake&lt;&#x2F;code&gt; allows you to reproduce the
entire &lt;a href=&quot;https:&#x2F;&#x2F;luhann.com&#x2F;posts&#x2F;transparent-metagenomics&#x2F;#pipeline-figure&quot;&gt;pipeline figure&lt;&#x2F;a&gt; in a single easily versioned text file. Importantly, &lt;code&gt;snakemake&lt;&#x2F;code&gt; describes
every step of your analysis and the causal pathways between inputs and outputs.
&lt;span class=&quot;margin&quot;&gt;
  We’ll come back to causal pathways and Directed Acyclic Graphs in the reproducibility section.
&lt;&#x2F;span&gt;

It is able to figure out that in order to use &lt;code&gt;data&#x2F;dehost&#x2F;{sample}_R1.fastq.gz&lt;&#x2F;code&gt; as an input target for the &lt;code&gt;taxonomic_profiling&lt;&#x2F;code&gt;
rule, the &lt;code&gt;remove_host&lt;&#x2F;code&gt; rule (and all of its upstream rules) must first be executed before &lt;code&gt;taxonomic_profiling&lt;&#x2F;code&gt; can
be executed.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;make&quot; data-name=&quot;snakemake_example&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;SAMPLES = [&amp;quot;sample_A&amp;quot;, &amp;quot;sample_B&amp;quot;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;rule all&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    input&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        expand(&amp;quot;results&#x2F;taxonomy&#x2F;{sample}_report.tsv&amp;quot;, sample=SAMPLES),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        expand(&amp;quot;results&#x2F;functional&#x2F;{sample}_genefamilies.tsv&amp;quot;, sample=SAMPLES)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;rule quality_control&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    input&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r1 = &amp;quot;data&#x2F;raw&#x2F;{sample}_R1.fastq.gz&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r2 = &amp;quot;data&#x2F;raw&#x2F;{sample}_R2.fastq.gz&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    output&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r1 = &amp;quot;data&#x2F;qc&#x2F;{sample}_R1.fastq.gz&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r2 = &amp;quot;data&#x2F;qc&#x2F;{sample}_R2.fastq.gz&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        report = &amp;quot;results&#x2F;qc&#x2F;{sample}.json&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    shell&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;fastp --in1 {input.r1} --in2 {input.r2} &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;--out1 {output.r1} --out2 {output.r2} &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;--json {output.report}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;rule remove_host&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    input&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r1 = &amp;quot;data&#x2F;qc&#x2F;{sample}_R1.fastq.gz&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r2 = &amp;quot;data&#x2F;qc&#x2F;{sample}_R2.fastq.gz&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    output&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r1 = &amp;quot;data&#x2F;dehost&#x2F;{sample}_R1.fastq.gz&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r2 = &amp;quot;data&#x2F;dehost&#x2F;{sample}_R2.fastq.gz&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    params&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        index = &amp;quot;resources&#x2F;GRCh38&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    shell&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;bowtie2 -x {params.index} &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;-1 {input.r1} -2 {input.r2} &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;--un-conc-gz data&#x2F;dehost&#x2F;{wildcards.sample}_R%.fastq.gz &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;&amp;gt; &#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;rule taxonomic_profiling&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    input&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r1 = &amp;quot;data&#x2F;dehost&#x2F;{sample}_R1.fastq.gz&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r2 = &amp;quot;data&#x2F;dehost&#x2F;{sample}_R2.fastq.gz&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    output&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        report = &amp;quot;results&#x2F;taxonomy&#x2F;{sample}_report.tsv&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    params&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        db = &amp;quot;resources&#x2F;kraken2_db&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    shell&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;kraken2 --db {params.db} &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;--paired {input.r1} {input.r2} &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;--report {output.report}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;rule functional_annotation&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    input&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r1 = &amp;quot;data&#x2F;dehost&#x2F;{sample}_R1.fastq.gz&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r2 = &amp;quot;data&#x2F;dehost&#x2F;{sample}_R2.fastq.gz&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    output&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        genefamilies = &amp;quot;results&#x2F;functional&#x2F;{sample}_genefamilies.tsv&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    shell&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;humann --input {input.r1} &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;--output results&#x2F;functional&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once you have reached the end of your analysis, and you are ready for publication, there are two more things that you
need to version control. Your analysis code itself, and your data. Without version controlled analysis code, and
data all of the fancy dependency tracking mentioned above will be useless because your project will not be
reproducible.&lt;&#x2F;p&gt;
&lt;p&gt;Versioning of metagenomic data is as challenging, if not more so, than versioning analysis code given
the common scale of metagenomic data. While it is a &lt;strong&gt;crucial aspect&lt;&#x2F;strong&gt; of reproducibility, it is beyond the scope of
the current article. For a thorough overview of the challenges and best-practices, see
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doi.org&#x2F;10.1093&#x2F;gigascience&#x2F;gix047&quot;&gt;The metagenomic data life-cycle: standards and best practices&lt;&#x2F;a&gt; and the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.ebi.ac.uk&#x2F;training&#x2F;events&#x2F;submitting-metagenomic-data-ena&#x2F;&quot;&gt;EMBL-EBI training on submitting metagenomic data to ENA&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, you also need to upload your analysis code. Git and GitHub are the defacto standards for uploading and storing
your code. &lt;span class=&quot;margin&quot;&gt;
  And as mentioned earlier, integrates well with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;zenodo.org&#x2F;&quot;&gt;zenodo&lt;&#x2F;a&gt;.
&lt;&#x2F;span&gt;

There are &lt;em&gt;many, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.blog&#x2F;developer-skills&#x2F;github&#x2F;beginners-guide-to-github-uploading-files-and-folders-to-github&#x2F;&quot;&gt;many&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;swcarpentry.github.io&#x2F;git-novice&#x2F;&quot;&gt;tutorials&lt;&#x2F;a&gt; on using Git and GitHub, but if the aim is to just archive the final
analysis code, using GitHub is the simplest option. Just click the green upload button in the interface shown below,
and give your file upload a meaningful comment and you are done. Once the analysis has been published, the
GitHub code can be permanently archived &lt;a href=&quot;https:&#x2F;&#x2F;luhann.com&#x2F;posts&#x2F;transparent-metagenomics&#x2F;#bit-rot-and-the-end-of-things&quot;&gt;as described above&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;div class=&quot;tufte-plot-container&quot;&gt;
  &lt;div class=&quot;tufte-plot-item&quot;&gt;
    &lt;img src=&quot;images&#x2F;upload-web-github.webp&quot; alt=&quot;Upload to GitHub&quot; style=&quot;max-width: 800px; width: 110%; margin-left: -5%; margin-right: -5%; height: auto;&quot;&gt;
  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;As a statistician or analyst, I believe advanced knowledge of git to be incredibly useful, both for version control and
understanding the software you depend on, but for purely archival purposes directly interfacing with the GitHub web-ui
will be enough for many researchers. Using the methods described above is sufficient to satisfy rule three of transparent
metagenomics:&lt;&#x2F;p&gt;
&lt;aside class=&quot;aside left&quot;&gt;
  &lt;p&gt;&lt;strong&gt;Rule 3&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;blockquote&gt;
&lt;p&gt;If it is mentioned in your manuscript, it must be online and &lt;strong&gt;accessible&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;merenlab.org&#x2F;2026&#x2F;04&#x2F;15&#x2F;unfalsifiable-by-design&#x2F;&quot;&gt;This excellent post by the Meren lab&lt;&#x2F;a&gt; describes how paying
lip-service to data availability means nothing for reproducibility if interested parties cannot actually access the data.
In particular, it shows that while the sequencing data itself is important, without the associated metadata it is
functionally useless for answering epidemiological questions, and is at best fun training data for bioinformaticians.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;causes-and-effects&quot;&gt;Causes and Effects&lt;&#x2F;h2&gt;
&lt;p&gt;Reproducibility is necessary but not sufficient for replicability. Generating an exact &lt;em&gt;in silico&lt;&#x2F;em&gt; reproduction of an
analysis is a good first step, but it cannot determine if the association you determined both exists &lt;em&gt;in vivo&lt;&#x2F;em&gt; and
is causal. Therefore, in order to make sure your science is &lt;strong&gt;replicable&lt;&#x2F;strong&gt; there are two important considerations.&lt;&#x2F;p&gt;
&lt;p&gt;Firstly, are your results &lt;strong&gt;internally replicable&lt;&#x2F;strong&gt;. If you performed your analysis for a second time — from the beginning —
using different but equivalent tools would you still come to the same conclusions.
&lt;span class=&quot;margin&quot;&gt;
  There are many alternatives for all the common tools used in metagenomic analyses. Often choosing between
alternatives is a matter of preference not performance.
&lt;&#x2F;span&gt;

Does choosing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;frederickhuanglin&#x2F;ANCOMBC&quot;&gt;ANCOM-BC2&lt;&#x2F;a&gt; alter your final conclusions, compared to if you
had used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;statdivlab&#x2F;radEmu&quot;&gt;radEmu&lt;&#x2F;a&gt;. If you re-run your &lt;em&gt;k-means&lt;&#x2F;em&gt; clustering algorithm with a different
random seed do your results change entirely?&lt;&#x2F;p&gt;
&lt;p&gt;In metagenomic studies, the answer to both of the questions above is: likely yes. In fact, the analysis method you
choose &lt;strong&gt;will&lt;&#x2F;strong&gt; drastically alter your final conclusions. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doi.org&#x2F;10.1038&#x2F;s41467-022-28034-z&quot;&gt;Nearing et al.&lt;&#x2F;a&gt;
tested 14 different differential abundance algorithms, across 38 datasets, and found:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The percentage of significant ASVs identified by each DA method varied widely across datasets, with means ranging
from 3.8–32.5% and 0.8–40.5%, respectively. Interestingly, we found that many tools behaved differently between datasets.
Specifically, some tools identified the most features in one dataset while identifying only an intermediate number in other datasets.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Furthermore, both within and across tool differences in performance were altered depending on whether or not the datasets
were filtered prior to analysis or not &lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-2-1&quot;&gt;&lt;a href=&quot;#fn-2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Unfortunately, it is not possible to accurately determine the true associations
from false positive associations when the method you use is so sensitive to many possible differences in study design.&lt;&#x2F;p&gt;
&lt;p&gt;If your aim is differential abundance analyses, current recommended practice is then to run a variety of differential abundance
methods and report concordance and discordance in the results generated by these tools. Reporting your results in this
manner is much more work, however you gain much for your extra work. Reporting in this way allows you to explicitly
report on the confidence of the associations you have found, and importantly when others are trying to reproduce your
results they are able to discern which ASVs should reproduce and which might not due to the strength of the evidence.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;div class=&quot;tufte-plot-container&quot;&gt;
  &lt;div class=&quot;tufte-plot-item&quot;&gt;
    
&lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; viewBox=&quot;0 0 580 440&quot; role=&quot;img&quot; aria-label=&quot;Venn diagram showing confidence levels from running three differential abundance methods&quot; style=&quot;width:100%;max-width:580px;display:block;margin:0 auto;&quot;&gt;
  &lt;style&gt;
    .vd text { font-family: &#x27;IBM Plex Sans&#x27;, Arial, sans-serif; }
    .vd-header { font-size: 9px; font-weight: 700; letter-spacing: 0.13em; }
    .vd-method { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; }
    .vd-conf { font-size: 10.5px; font-weight: 700; }
    .vd-conf-sub { font-size: 9.5px; }
  &lt;&#x2F;style&gt;
  &lt;g class=&quot;vd&quot;&gt;

    &lt;!-- Title --&gt;
    &lt;text x=&quot;290&quot; y=&quot;24&quot; text-anchor=&quot;middle&quot; class=&quot;vd-header&quot; fill=&quot;var(--site-muted)&quot;&gt;&lt;&#x2F;text&gt;

    &lt;!-- Three overlapping circles --&gt;
    &lt;!-- Circle A: ANCOM-BC2 (red &#x2F; statistical analysis colour) --&gt;
    &lt;circle cx=&quot;210&quot; cy=&quot;180&quot; r=&quot;115&quot; fill=&quot;#a51c30&quot; fill-opacity=&quot;0.12&quot; stroke=&quot;#a51c30&quot; stroke-width=&quot;1.5&quot; stroke-opacity=&quot;0.55&quot;&#x2F;&gt;
    &lt;!-- Circle B: radEmu (blue &#x2F; bioinformatics colour) --&gt;
    &lt;circle cx=&quot;360&quot; cy=&quot;180&quot; r=&quot;115&quot; fill=&quot;#4f8dbf&quot; fill-opacity=&quot;0.12&quot; stroke=&quot;#4f8dbf&quot; stroke-width=&quot;1.5&quot; stroke-opacity=&quot;0.55&quot;&#x2F;&gt;
    &lt;!-- Circle C: MaAsLin3 (orange &#x2F; wet-lab colour) --&gt;
    &lt;circle cx=&quot;285&quot; cy=&quot;272&quot; r=&quot;115&quot; fill=&quot;#cf7d55&quot; fill-opacity=&quot;0.12&quot; stroke=&quot;#cf7d55&quot; stroke-width=&quot;1.5&quot; stroke-opacity=&quot;0.55&quot;&#x2F;&gt;

    &lt;!-- Method name labels (outside &#x2F; above each circle) --&gt;
    &lt;text x=&quot;115&quot; y=&quot;55&quot; text-anchor=&quot;middle&quot; class=&quot;vd-method&quot; fill=&quot;#a51c30&quot;&gt;ANCOM-BC2&lt;&#x2F;text&gt;
    &lt;text x=&quot;462&quot; y=&quot;55&quot; text-anchor=&quot;middle&quot; class=&quot;vd-method&quot; fill=&quot;#4f8dbf&quot;&gt;radEmu&lt;&#x2F;text&gt;
    &lt;text x=&quot;285&quot; y=&quot;420&quot; text-anchor=&quot;middle&quot; class=&quot;vd-method&quot; fill=&quot;#cf7d55&quot;&gt;MaAsLin3&lt;&#x2F;text&gt;

    &lt;!-- LOW confidence: unique to a single method --&gt;
    &lt;text x=&quot;143&quot; y=&quot;173&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf&quot; fill=&quot;var(--site-muted)&quot;&gt;Low&lt;&#x2F;text&gt;
    &lt;text x=&quot;143&quot; y=&quot;187&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf-sub&quot; fill=&quot;var(--site-muted)&quot;&gt;Conf.&lt;&#x2F;text&gt;

    &lt;text x=&quot;427&quot; y=&quot;173&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf&quot; fill=&quot;var(--site-muted)&quot;&gt;Low&lt;&#x2F;text&gt;
    &lt;text x=&quot;427&quot; y=&quot;187&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf-sub&quot; fill=&quot;var(--site-muted)&quot;&gt;Conf.&lt;&#x2F;text&gt;

    &lt;text x=&quot;285&quot; y=&quot;367&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf&quot; fill=&quot;var(--site-muted)&quot;&gt;Low&lt;&#x2F;text&gt;
    &lt;text x=&quot;285&quot; y=&quot;381&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf-sub&quot; fill=&quot;var(--site-muted)&quot;&gt;Conf.&lt;&#x2F;text&gt;

    &lt;!-- MEDIUM confidence: found by exactly two methods --&gt;
    &lt;!-- A ∩ B (top, above circle C) --&gt;
    &lt;text x=&quot;285&quot; y=&quot;134&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf&quot; fill=&quot;var(--site-headline)&quot;&gt;Med.&lt;&#x2F;text&gt;
    &lt;text x=&quot;285&quot; y=&quot;148&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf-sub&quot; fill=&quot;var(--site-headline)&quot;&gt;Conf.&lt;&#x2F;text&gt;

    &lt;!-- A ∩ C (left two-way overlap) --&gt;
    &lt;text x=&quot;220&quot; y=&quot;262&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf&quot; fill=&quot;var(--site-headline)&quot;&gt;Med.&lt;&#x2F;text&gt;
    &lt;text x=&quot;220&quot; y=&quot;276&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf-sub&quot; fill=&quot;var(--site-headline)&quot;&gt;Conf.&lt;&#x2F;text&gt;

    &lt;!-- B ∩ C (right two-way overlap) --&gt;
    &lt;text x=&quot;350&quot; y=&quot;262&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf&quot; fill=&quot;var(--site-headline)&quot;&gt;Med.&lt;&#x2F;text&gt;
    &lt;text x=&quot;350&quot; y=&quot;276&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf-sub&quot; fill=&quot;var(--site-headline)&quot;&gt;Conf.&lt;&#x2F;text&gt;

    &lt;!-- HIGH confidence: found by all three methods (centroid of circle centres) --&gt;
    &lt;text x=&quot;285&quot; y=&quot;205&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf&quot; style=&quot;font-size:12px;&quot; fill=&quot;var(--site-headline)&quot;&gt;High&lt;&#x2F;text&gt;
    &lt;text x=&quot;285&quot; y=&quot;221&quot; text-anchor=&quot;middle&quot; class=&quot;vd-conf&quot; style=&quot;font-size:12px;&quot; fill=&quot;var(--site-headline)&quot;&gt;Conf.&lt;&#x2F;text&gt;

  &lt;&#x2F;g&gt;
&lt;&#x2F;svg&gt;


  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;figcaption style=&quot;text-align:center;font-size:0.8rem;color:var(--site-muted);margin-top:0.6rem;&quot;&gt;Significant ASVs from three DA methods. ASVs reproduced across all methods (centre) carry the highest confidence; those unique to a single method (outer arcs) should be interpreted with caution.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;This also allows you, the researcher, to be appropriately cautious in interpreting your findings, significant ASVs that do
not reproduce across methods within your own study should be interpreted with caution. Particularly when working with
sample sizes of typical metagenomic studies. The need for caution in interpretation is much greater when the aim is to
evaluate the relationship between microbial relative abundance and health biomarkers &lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-3-1&quot;&gt;&lt;a href=&quot;#fn-3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. This would lead to rule four
for transparent metagenomics:&lt;&#x2F;p&gt;
&lt;aside class=&quot;aside left&quot;&gt;
  &lt;p&gt;&lt;strong&gt;Rule 4&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;blockquote&gt;
&lt;p&gt;Make sure that your analyses reproduce within your study, across methods.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If you’ll indulge me one concluding footnote, in metagenomics as in all science the most important concerns of reproducibility
are not technical they are scientific. The recent article by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doi.org&#x2F;10.1016&#x2F;j.neuron.2025.10.006&quot;&gt;Mitchell et al.&lt;&#x2F;a&gt;
illustrates many of the aspects discussed in this post. &lt;span class=&quot;margin&quot;&gt;
  The title is: “Conceptual and methodological flaws
undermine claims of a link between the gut microbiome and autism”.
&lt;&#x2F;span&gt;

The fundamental issue it raises is not about software versions or dead hyperlinks, it is about whether the associations
being reported are meaningful in the first place. In metagenomics, as in all observational science, association does
not imply causation. Microbial relative abundances are &lt;strong&gt;compositional&lt;&#x2F;strong&gt;,
&lt;span class=&quot;margin&quot;&gt;
  Meaning every taxon’s apparent abundance is defined in relation to all others, a feature that makes standard
statistical tests misleading without appropriate correction.
&lt;&#x2F;span&gt;
 and the associations we observe between community
composition and health outcomes may reflect confounding, reverse causation, or simply artefacts of the garden of forking paths
described above. Making genuine causal claims from metagenomic data requires either experimental intervention or very careful
application of causal inference methods. Without that, a perfectly reproducible and internally replicable association
study may still leave the most important scientific questions with unsatisfactory answers.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;div class=&quot;tufte-plot-container&quot;&gt;
  &lt;div class=&quot;tufte-plot-item&quot;&gt;
    
&lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; viewBox=&quot;0 0 620 340&quot; role=&quot;img&quot; aria-label=&quot;Directed acyclic graph showing causal ambiguity in microbiome association studies&quot; style=&quot;width:100%;max-width:620px;display:block;margin:0 auto;&quot;&gt;
  &lt;defs&gt;
    &lt;marker id=&quot;dag-arr&quot; markerWidth=&quot;8&quot; markerHeight=&quot;6&quot; refX=&quot;7&quot; refY=&quot;3&quot; orient=&quot;auto&quot;&gt;
      &lt;polygon points=&quot;0 0, 8 3, 0 6&quot; fill=&quot;var(--site-headline)&quot; &#x2F;&gt;
    &lt;&#x2F;marker&gt;
    &lt;marker id=&quot;dag-arr-assoc&quot; markerWidth=&quot;8&quot; markerHeight=&quot;6&quot; refX=&quot;7&quot; refY=&quot;3&quot; orient=&quot;auto&quot;&gt;
      &lt;polygon points=&quot;0 0, 8 3, 0 6&quot; fill=&quot;#cf7d55&quot; &#x2F;&gt;
    &lt;&#x2F;marker&gt;
    &lt;marker id=&quot;dag-arr-rev&quot; markerWidth=&quot;8&quot; markerHeight=&quot;6&quot; refX=&quot;7&quot; refY=&quot;3&quot; orient=&quot;auto&quot;&gt;
      &lt;polygon points=&quot;0 0, 8 3, 0 6&quot; fill=&quot;var(--site-muted)&quot; &#x2F;&gt;
    &lt;&#x2F;marker&gt;
  &lt;&#x2F;defs&gt;
  &lt;style&gt;
    .dag text { font-family: &#x27;IBM Plex Sans&#x27;, Arial, sans-serif; }
    .dag-title  { fill: var(--site-headline); font-size: 12.5px; font-weight: 600; }
    .dag-detail { fill: var(--site-muted);    font-size: 10px; }
    .dag-edge   { fill: var(--site-muted);    font-size: 9px; font-style: italic; }
  &lt;&#x2F;style&gt;

  &lt;g class=&quot;dag&quot;&gt;

    &lt;!-- ── NODE: CONFOUNDERS (top-centre, purple) ── --&gt;
    &lt;rect x=&quot;175&quot; y=&quot;40&quot; width=&quot;270&quot; height=&quot;60&quot; rx=&quot;3&quot; fill=&quot;var(--site-surface-soft)&quot; stroke=&quot;var(--site-line)&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;rect x=&quot;175&quot; y=&quot;40&quot; width=&quot;5&quot;   height=&quot;60&quot; rx=&quot;2&quot; fill=&quot;#7c5cbf&quot;&#x2F;&gt;
    &lt;text x=&quot;195&quot; y=&quot;64&quot;  class=&quot;dag-title&quot;&gt;Unmeasured Confounders&lt;&#x2F;text&gt;
    &lt;text x=&quot;195&quot; y=&quot;82&quot;  class=&quot;dag-detail&quot;&gt;diet · host genetics · antibiotic use · age&lt;&#x2F;text&gt;

    &lt;!-- ── NODE: MICROBIOME (bottom-left, teal) ── --&gt;
    &lt;rect x=&quot;20&quot;  y=&quot;210&quot; width=&quot;200&quot; height=&quot;58&quot; rx=&quot;3&quot; fill=&quot;var(--site-surface-soft)&quot; stroke=&quot;var(--site-line)&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;rect x=&quot;20&quot;  y=&quot;210&quot; width=&quot;5&quot;   height=&quot;58&quot; rx=&quot;2&quot; fill=&quot;#3d9e8c&quot;&#x2F;&gt;
    &lt;text x=&quot;40&quot;  y=&quot;236&quot; class=&quot;dag-title&quot;&gt;Microbiome&lt;&#x2F;text&gt;
    &lt;text x=&quot;40&quot;  y=&quot;254&quot; class=&quot;dag-detail&quot;&gt;community composition&lt;&#x2F;text&gt;

    &lt;!-- ── NODE: HEALTH OUTCOME (bottom-right, red) ── --&gt;
    &lt;rect x=&quot;400&quot; y=&quot;210&quot; width=&quot;200&quot; height=&quot;58&quot; rx=&quot;3&quot; fill=&quot;var(--site-surface-soft)&quot; stroke=&quot;var(--site-line)&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
    &lt;rect x=&quot;400&quot; y=&quot;210&quot; width=&quot;5&quot;   height=&quot;58&quot; rx=&quot;2&quot; fill=&quot;#a51c30&quot;&#x2F;&gt;
    &lt;text x=&quot;420&quot; y=&quot;236&quot; class=&quot;dag-title&quot;&gt;Health Outcome&lt;&#x2F;text&gt;
    &lt;text x=&quot;420&quot; y=&quot;254&quot; class=&quot;dag-detail&quot;&gt;diagnosis · symptom score&lt;&#x2F;text&gt;

    &lt;!-- ── ARROWS: CONFOUNDERS → NODES ── --&gt;
    &lt;line x1=&quot;278&quot; y1=&quot;100&quot; x2=&quot;96&quot;  y2=&quot;212&quot; stroke=&quot;var(--site-headline)&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#dag-arr)&quot;&#x2F;&gt;
    &lt;line x1=&quot;342&quot; y1=&quot;100&quot; x2=&quot;504&quot; y2=&quot;212&quot; stroke=&quot;var(--site-headline)&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#dag-arr)&quot;&#x2F;&gt;

    &lt;!-- ── ARROW: M → Y  (observed association, dashed orange, upper) ── --&gt;
    &lt;line x1=&quot;222&quot; y1=&quot;228&quot; x2=&quot;400&quot; y2=&quot;228&quot; stroke=&quot;#cf7d55&quot; stroke-width=&quot;1.5&quot; stroke-dasharray=&quot;5 3&quot; marker-end=&quot;url(#dag-arr-assoc)&quot;&#x2F;&gt;
    &lt;text x=&quot;311&quot; y=&quot;220&quot; text-anchor=&quot;middle&quot; class=&quot;dag-edge&quot; fill=&quot;#cf7d55&quot;&gt;observed association (p &amp;lt; 0.05)&lt;&#x2F;text&gt;

    &lt;!-- ── ARROW: Y → M  (reverse causation, dashed muted, lower) ── --&gt;
    &lt;line x1=&quot;400&quot; y1=&quot;249&quot; x2=&quot;222&quot; y2=&quot;249&quot; stroke=&quot;var(--site-muted)&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;3 4&quot; marker-end=&quot;url(#dag-arr-rev)&quot;&#x2F;&gt;
    &lt;text x=&quot;311&quot; y=&quot;270&quot; text-anchor=&quot;middle&quot; class=&quot;dag-edge&quot;&gt;reverse causation?&lt;&#x2F;text&gt;

    &lt;!-- ── LEGEND ── --&gt;
    &lt;line x1=&quot;35&quot;  y1=&quot;308&quot; x2=&quot;68&quot;  y2=&quot;308&quot; stroke=&quot;var(--site-headline)&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#dag-arr)&quot;&#x2F;&gt;
    &lt;text x=&quot;78&quot;  y=&quot;312&quot; class=&quot;dag-edge&quot; font-style=&quot;normal&quot; fill=&quot;var(--site-muted)&quot;&gt;established causal path&lt;&#x2F;text&gt;

    &lt;line x1=&quot;220&quot; y1=&quot;308&quot; x2=&quot;253&quot; y2=&quot;308&quot; stroke=&quot;#cf7d55&quot; stroke-width=&quot;1.5&quot; stroke-dasharray=&quot;5 3&quot; marker-end=&quot;url(#dag-arr-assoc)&quot;&#x2F;&gt;
    &lt;text x=&quot;263&quot; y=&quot;312&quot; class=&quot;dag-edge&quot; font-style=&quot;normal&quot; fill=&quot;var(--site-muted)&quot;&gt;observed association&lt;&#x2F;text&gt;

    &lt;line x1=&quot;400&quot; y1=&quot;308&quot; x2=&quot;433&quot; y2=&quot;308&quot; stroke=&quot;var(--site-muted)&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;3 4&quot; marker-end=&quot;url(#dag-arr-rev)&quot;&#x2F;&gt;
    &lt;text x=&quot;443&quot; y=&quot;312&quot; class=&quot;dag-edge&quot; font-style=&quot;normal&quot; fill=&quot;var(--site-muted)&quot;&gt;reverse causation&lt;&#x2F;text&gt;

  &lt;&#x2F;g&gt;
&lt;&#x2F;svg&gt;


  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;figcaption style=&quot;text-align:center;font-size:0.8rem;color:var(--site-muted);margin-top:0.6rem;&quot;&gt;A directed acyclic graph (DAG) for a typical microbiome association study. Unmeasured confounders — diet, host genetics, antibiotic use, age — independently shape both community composition and health outcomes, inducing a spurious association even when no direct causal path exists. The dashed arrows between Microbiome and Health Outcome represent the association we observe; distinguishing genuine causation from confounding or reverse causation requires experimental intervention or formal causal inference.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;That question, the one that motivates almost all human microbiome research, is whether the microbiome can be
modified to improve health. Probiotics, dietary interventions, and faecal microbiota transplantation are all downstream
attempts to answer it in practice. But the intervention literature can only be as trustworthy as the observational
literature that motivates it. If our reported associations are not reproducible, not replicable across methods, and do
not reflect genuine causal relationships, then the clinical promise of the microbiome will remain perpetually just out of
reach. Transparent, reproducible science is a prerequisite for doing research that is actually useful.&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;There are some caveats to this, see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rstudio.github.io&#x2F;renv&#x2F;articles&#x2F;renv.html#caveats&quot;&gt;renv caveats&lt;&#x2F;a&gt;. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;It will not come as a surprise to anyone, that both filtering methods and their general utility are hotly debated
topics in metagenomics. See &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doi.org&#x2F;10.1371&#x2F;journal.pcbi.1003531&quot;&gt;Waste not, want not&lt;&#x2F;a&gt; for a rough idea. &lt;a href=&quot;#fr-2-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doi.org&#x2F;10.1016&#x2F;j.scib.2025.02.022&quot;&gt;Identifying reproducible biomarkers for
microbiome association studies requires thousands of sample sizes&lt;&#x2F;a&gt;. &lt;a href=&quot;#fr-3-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>On Reservoir Sampling</title>
        <published>2026-06-14T00:00:00+00:00</published>
        <updated>2026-06-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Luke Hannan
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://luhann.com/posts/reservoir-sampling/"/>
        <id>https://luhann.com/posts/reservoir-sampling/</id>
        
        <content type="html" xml:base="https://luhann.com/posts/reservoir-sampling/">&lt;p&gt;I personally find that one of the most fun ways to learn a new programming language (particularly a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;System_programming_language&quot;&gt;systems programming
language&lt;&#x2F;a&gt;) is to take a relatively simple program and
see how much you can optimise it. Just for the hell of it. I like to call this:
&lt;span class=&quot;margin&quot;&gt;
  Perhaps in a later blog post I’ll expand on this further.
&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;In pursuit of meaningless minimalism.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;span class=&quot;margin quote-note&quot;&gt;
  We should forget about small efficiencies, say about 97% of the time: &lt;strong&gt;premature optimization is the root of all evil&lt;&#x2F;strong&gt;. Yet we should not pass up our opportunities in that critical 3% - Donald Knuth
&lt;&#x2F;span&gt;

Recently, with the goal of learning &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; I converted all of my old Fish shell scripts to
Rust binaries. This will come as no surprise to anyone but Rust is far more capable than Fish, and so while translating
my shell scripts to Rust I decided to see if I could make them &lt;strong&gt;faster&lt;&#x2F;strong&gt;. One utility that I thought would be ripe for
premature optimisation is my wallpaper picker. The task is simple, select a single random wallpaper from a given directory
containing all of my wallpapers.&lt;&#x2F;p&gt;
&lt;p&gt;My naïve Fish implementation looked something like this (I omit the full script for brevity):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;fish&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Wallpaper directory&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;set&lt;&#x2F;span&gt;&lt;span&gt; -l WALLPAPER_DIR &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$HOME&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;&#x2F;onedrive&#x2F;wallpapers&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Randomly select a wallpaper from the specified directory&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;set&lt;&#x2F;span&gt;&lt;span&gt; -l WALL (&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;find&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;.&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;  $WALLPAPER_DIR \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    --type file -e jpg -e jpeg -e png -e webp \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; sort&lt;&#x2F;span&gt;&lt;span&gt; -R \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; head&lt;&#x2F;span&gt;&lt;span&gt; -n 1)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see, the Fish implementation loads the full list of files into memory, sorts them randomly and picks the first one.
Between shell invocation overhead, firing up &lt;strong&gt;three separate utilities&lt;&#x2F;strong&gt;, and piping text streams between them, we incur
significant overhead. All of that, just to discard the entire list of files bar one.
&lt;span class=&quot;margin&quot;&gt;
  To guard against the hypothetical edge case where I have more images than there are atoms in the universe.
&lt;&#x2F;span&gt;

For my Rust utility, I wanted to make sure that I never have to read the entire list of files into memory.&lt;&#x2F;p&gt;
&lt;p&gt;Enter &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Reservoir_sampling&quot;&gt;Reservoir Sampling&lt;&#x2F;a&gt;. Reservoir sampling is a family of algorithms
for choosing a simple random sample of &lt;em&gt;k&lt;&#x2F;em&gt; elements, without replacement, from a population of size &lt;em&gt;n&lt;&#x2F;em&gt; — without needing
to know the population size (&lt;em&gt;n&lt;&#x2F;em&gt;). &lt;span class=&quot;margin left&quot;&gt;
  I know, I know, em-dashes are signs of AI use. You can take
my em-dashes from my cold, dead hands.
&lt;&#x2F;span&gt;
The naïve implementation is known as Algorithm R, created by Alan Waterman&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;,
and is conceptually relatively straightforward.&lt;&#x2F;p&gt;
&lt;p&gt;First select the first &lt;em&gt;k&lt;&#x2F;em&gt; elements of &lt;em&gt;n&lt;&#x2F;em&gt;, where &lt;em&gt;k&lt;&#x2F;em&gt; is the sample size you would like. Next initialise a new index to
track the remainder of our elements: &lt;em&gt;i&lt;&#x2F;em&gt;. Then for each element in the list of remainders (after the first &lt;em&gt;k&lt;&#x2F;em&gt; elements
have been selected), we generate a random number between 1 and &lt;em&gt;i&lt;&#x2F;em&gt;. If that random number is between 1 and &lt;em&gt;k&lt;&#x2F;em&gt; we replace
the element in &lt;em&gt;k&lt;&#x2F;em&gt; at that position with our new number, otherwise we discard the number. Iterate over every element of
the remainder, and at the end you will have your random sample of &lt;em&gt;k&lt;&#x2F;em&gt; elements. When &lt;em&gt;k&lt;&#x2F;em&gt; = 1, it is even simpler as you
can just check if the random number generated is equal to 1 (or 0 in zero-indexed arrays). In Rust it would look something
like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot; data-name=&quot;algorithm_r_naive.rs&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; algorithm_r&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;I&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;&amp;gt;(inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; I&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;PathBuf&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;where&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    I&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Iterator&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Item&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; PathBuf&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-storage&quot;&gt;    let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; chosen&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; (i, item)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;enumerate&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; fastrand&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;..=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;i)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            chosen&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(item);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    chosen&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Items at the front of the remainder have a higher chance of initially entering the sample than items towards the end,
but they must survive a brutal gauntlet to make it to the end. While conceptually simple, Algorithm R has two major
drawbacks with respect to the pursuit of meaningless minimalism:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You must evaluate every element of the remainder, one at a time.&lt;&#x2F;li&gt;
&lt;li&gt;You must generate a random number for every element of the remainder.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Generating random numbers (particularly true RNG) is computationally expensive, doing so when you are going to discard
99% of the results is even more so. Thankfully, there exists a more efficient algorithm for reservoir sampling.&lt;&#x2F;p&gt;
&lt;p&gt;Enter Algorithm L. First select the first &lt;em&gt;k&lt;&#x2F;em&gt; elements of &lt;em&gt;n&lt;&#x2F;em&gt;, where &lt;em&gt;k&lt;&#x2F;em&gt; is the sample size you would like, and initialize a
floating-point variable &lt;em&gt;W&lt;&#x2F;em&gt; to track the reservoir’s threshold property. Instead of generating a random number for
every single remaining element, we use a geometric distribution formula based on &lt;em&gt;W&lt;&#x2F;em&gt; to calculate a random number of
elements to skip entirely. After we calculate the skip, we generate a random number between 1 and &lt;em&gt;k&lt;&#x2F;em&gt; and replace the
element in &lt;em&gt;k&lt;&#x2F;em&gt; at that position with our new random number. Finally we update our reservoir threshold with another random
number. Algorithm L generates 3 random numbers for each evaluated element, but 0 random numbers for each skipped element.
Given that the majority of elements are skipped, this works out to far fewer random number generations than Algorithm R.&lt;&#x2F;p&gt;
&lt;p&gt;A standard implementation of Algorithm L in Rust looks something like this:
&lt;aside class=&quot;aside left&quot;&gt;
  &lt;p&gt;For &lt;em&gt;k&lt;&#x2F;em&gt; = 1.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;&#x2F;p&gt;
&lt;aside class=&quot;aside&quot;&gt;
  &lt;p&gt;A bit of Rust pedantry here: looking at &lt;code&gt;inputs.nth(skip)&lt;&#x2F;code&gt;, it might seem like we are cleanly skipping over unneeded elements.
However, because our iterator is an unknown-length filesystem stream, &lt;code&gt;nth()&lt;&#x2F;code&gt; is forced to call &lt;code&gt;next()&lt;&#x2F;code&gt; under the hood
until it reaches the target. We are saving ourselves from generating random numbers, but we are still evaluating every
single directory entry in between.&lt;&#x2F;p&gt;
&lt;p&gt;If we were sampling from a data structure that supported &lt;em&gt;O(1)&lt;&#x2F;em&gt; random access, Algorithm L would be significantly faster.
But converting the directory stream into an array would require loading all the file paths into memory first, which obviates
the entire raison d’être of reservoir sampling.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot; data-name=&quot;algorithm_l.rs&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; algorithm_l&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;I&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;&amp;gt;(inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; I&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;PathBuf&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;where&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    I&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Iterator&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Item&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; PathBuf&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-storage&quot;&gt;    let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; inputs;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-storage&quot;&gt;    let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; chosen&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-storage z-type&quot;&gt;    if let&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(item)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        chosen&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(item);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-storage&quot;&gt;    let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; w&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; f64&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; fastrand&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    loop&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; skip&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;fastrand&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ln&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; w)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ln&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-storage z-type&quot;&gt;        if let&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(item)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;nth&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(skip) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            chosen&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(item);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            w&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; fastrand&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            break&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    chosen&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We have achieved peak meaningless minimalism. End of story, or so I thought. However, every good meaningless minimalist knows
that peak minimalism is only achieved once the benchmarks say it has been achieved.
&lt;span class=&quot;margin&quot;&gt;
  Andrew Gallant, aka &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;burntsushi&quot;&gt;BurntSushi&lt;&#x2F;a&gt;, has perhaps the best &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;burntsushi.net&#x2F;ripgrep&#x2F;&quot;&gt;post on benchmarking&lt;&#x2F;a&gt; I have ever
read, and it should be required reading for anyone comparing anything with benchmarks.
&lt;&#x2F;span&gt;

Using the Rust implementations described above, we can benchmark just the random wallpaper selection section of code using
the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;criterion&quot;&gt;criterion crate&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;div class=&quot;tufte-plot-container&quot;&gt;
  &lt;div class=&quot;tufte-plot-item&quot;&gt;
    
&lt;svg viewBox=&quot;0 0 960 186&quot; xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot;&gt;
&lt;text x=&quot;8&quot; y=&quot;82&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot; transform=&quot;rotate(270, 8, 82)&quot;&gt;
Input
&lt;&#x2F;text&gt;
&lt;text x=&quot;528&quot; y=&quot;178&quot; dy=&quot;-0.5ex&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
Average time (ns)
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;103,34 103,129 &quot;&#x2F;&gt;
&lt;text x=&quot;94&quot; y=&quot;106&quot; dy=&quot;0.5ex&quot; text-anchor=&quot;end&quot; font-family=&quot;sans-serif&quot; font-size=&quot;8.064516129032258&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
Algorithm R
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;98,106 103,106 &quot;&#x2F;&gt;
&lt;text x=&quot;94&quot; y=&quot;58&quot; dy=&quot;0.5ex&quot; text-anchor=&quot;end&quot; font-family=&quot;sans-serif&quot; font-size=&quot;8.064516129032258&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
Algorithm L
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;98,58 103,58 &quot;&#x2F;&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;104,130 951,130 &quot;&#x2F;&gt;
&lt;text x=&quot;104&quot; y=&quot;140&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
0.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;104,130 104,135 &quot;&#x2F;&gt;
&lt;text x=&quot;229&quot; y=&quot;140&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
10.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;229,130 229,135 &quot;&#x2F;&gt;
&lt;text x=&quot;355&quot; y=&quot;140&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
20.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;355,130 355,135 &quot;&#x2F;&gt;
&lt;text x=&quot;481&quot; y=&quot;140&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
30.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;481,130 481,135 &quot;&#x2F;&gt;
&lt;text x=&quot;607&quot; y=&quot;140&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
40.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;607,130 607,135 &quot;&#x2F;&gt;
&lt;text x=&quot;733&quot; y=&quot;140&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
50.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;733,130 733,135 &quot;&#x2F;&gt;
&lt;text x=&quot;858&quot; y=&quot;140&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
60.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;858,130 858,135 &quot;&#x2F;&gt;
&lt;polygon opacity=&quot;1&quot; fill=&quot;var(--site-accent-meta)&quot; points=&quot;702,106 702,106 702,106 702,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 704,106 704,106 704,106 704,106 704,106 704,105 704,105 704,105 704,105 704,105 704,105 704,105 704,105 704,105 704,105 705,104 705,104 705,104 705,104 705,104 705,103 705,103 705,103 705,102 705,102 705,101 705,101 705,100 705,100 705,99 705,98 706,98 706,97 706,96 706,96 706,95 706,94 706,93 706,92 706,92 706,91 706,90 706,89 706,88 706,87 706,87 706,86 707,85 707,85 707,84 707,84 707,83 707,83 707,83 707,82 707,82 707,82 707,82 707,82 707,82 707,82 707,83 707,83 708,83 708,84 708,84 708,85 708,85 708,86 708,87 708,87 708,88 708,89 708,89 708,90 708,91 708,92 708,92 709,93 709,94 709,94 709,95 709,95 709,96 709,96 709,97 709,97 709,98 709,98 709,99 709,99 709,99 709,100 709,100 710,100 710,101 710,101 710,101 710,101 710,102 710,102 710,102 710,102 710,102 710,102 710,102 710,102 710,103 710,103 710,103 711,103 711,103 711,103 711,103 711,103 711,103 711,103 711,103 711,103 711,103 711,103 711,103 711,103 711,103 711,103 712,103 712,103 712,103 712,103 712,104 712,104 712,104 712,104 712,104 712,104 712,104 712,104 712,104 712,104 712,104 712,104 713,104 713,104 713,104 713,104 713,104 713,104 713,104 713,104 713,104 713,104 713,104 713,104 713,104 713,104 713,104 713,104 714,104 714,104 714,104 714,104 714,104 714,104 714,104 714,104 714,104 714,104 714,104 714,104 714,104 714,104 714,104 715,104 715,104 715,104 715,104 715,104 715,104 715,104 715,104 715,104 715,104 715,104 715,104 715,104 715,104 715,104 715,104 716,104 716,104 716,104 716,104 716,104 716,104 716,104 716,104 716,105 716,105 716,105 716,105 716,105 716,105 716,105 716,105 717,105 717,105 717,105 717,105 717,105 717,105 717,105 717,105 717,105 717,105 717,105 717,105 717,105 717,105 717,105 717,105 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,105 720,105 720,105 720,105 720,105 720,105 721,105 721,105 721,105 721,105 721,105 721,105 721,105 721,105 721,105 721,105 721,105 721,105 721,105 721,105 721,105 722,105 722,105 722,105 722,105 722,105 722,105 722,105 722,105 722,105 722,105 722,105 722,105 722,105 722,105 722,105 722,105 723,105 723,105 723,105 723,105 723,105 723,105 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 734,106 734,106 734,106 734,106 734,106 734,106 734,106 734,106 734,106 734,106 702,106 &quot;&#x2F;&gt;
&lt;polygon opacity=&quot;1&quot; fill=&quot;var(--site-accent-meta)&quot; points=&quot;702,106 702,106 702,106 702,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 703,106 704,106 704,106 704,106 704,106 704,106 704,106 704,106 704,106 704,106 704,106 704,106 704,106 704,107 704,107 704,107 705,107 705,107 705,107 705,108 705,108 705,108 705,109 705,109 705,109 705,110 705,110 705,111 705,111 705,112 705,112 705,113 706,114 706,114 706,115 706,116 706,117 706,117 706,118 706,119 706,120 706,121 706,122 706,122 706,123 706,124 706,125 706,125 707,126 707,127 707,127 707,128 707,128 707,129 707,129 707,129 707,129 707,129 707,129 707,129 707,129 707,129 707,129 707,129 708,128 708,128 708,127 708,127 708,126 708,125 708,125 708,124 708,123 708,123 708,122 708,121 708,121 708,120 708,119 709,119 709,118 709,117 709,117 709,116 709,116 709,115 709,115 709,114 709,114 709,113 709,113 709,112 709,112 709,112 709,111 710,111 710,111 710,111 710,110 710,110 710,110 710,110 710,110 710,109 710,109 710,109 710,109 710,109 710,109 710,109 710,109 711,109 711,109 711,109 711,109 711,108 711,108 711,108 711,108 711,108 711,108 711,108 711,108 711,108 711,108 711,108 712,108 712,108 712,108 712,108 712,108 712,108 712,108 712,108 712,108 712,108 712,108 712,108 712,108 712,108 712,108 712,108 713,108 713,108 713,108 713,108 713,108 713,108 713,108 713,108 713,108 713,108 713,108 713,108 713,108 713,108 713,108 713,108 714,108 714,108 714,108 714,108 714,108 714,108 714,108 714,108 714,108 714,108 714,108 714,108 714,108 714,108 714,108 715,108 715,108 715,108 715,108 715,108 715,108 715,108 715,108 715,108 715,108 715,108 715,108 715,108 715,108 715,108 715,107 716,107 716,107 716,107 716,107 716,107 716,107 716,107 716,107 716,107 716,107 716,107 716,107 716,107 716,107 716,107 716,107 717,107 717,106 717,106 717,106 717,106 717,106 717,106 717,106 717,106 717,106 717,106 717,106 717,106 717,106 717,106 717,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 718,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 719,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 720,106 721,106 721,106 721,106 721,106 721,106 721,106 721,106 721,106 721,106 721,106 721,106 721,106 721,106 721,106 721,106 722,106 722,106 722,106 722,106 722,106 722,106 722,106 722,106 722,106 722,106 722,106 722,106 722,106 722,106 722,106 722,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 723,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 724,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 725,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 726,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 727,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 728,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 729,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 730,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 731,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 732,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 733,106 734,106 734,106 734,106 734,106 734,106 734,106 734,106 734,106 734,106 734,106 702,106 &quot;&#x2F;&gt;
&lt;polygon opacity=&quot;1&quot; fill=&quot;var(--site-accent-meta)&quot; points=&quot;888,58 888,58 889,58 889,58 889,58 889,58 889,58 889,58 889,58 889,58 890,58 890,58 890,58 890,58 890,58 890,58 890,58 890,58 891,58 891,58 891,58 891,58 891,58 891,58 891,58 891,58 892,58 892,58 892,58 892,58 892,58 892,58 892,58 892,58 893,58 893,58 893,58 893,58 893,58 893,58 893,58 893,58 894,58 894,58 894,58 894,58 894,58 894,58 894,58 894,58 895,58 895,58 895,58 895,58 895,57 895,57 895,57 895,57 896,57 896,57 896,57 896,57 896,56 896,56 896,56 896,56 897,56 897,55 897,55 897,55 897,54 897,54 897,54 897,53 898,53 898,52 898,52 898,51 898,51 898,50 898,50 898,49 899,49 899,48 899,47 899,47 899,46 899,45 899,45 899,44 899,43 900,43 900,42 900,41 900,41 900,40 900,40 900,39 900,38 901,38 901,37 901,37 901,36 901,36 901,36 901,35 901,35 902,35 902,35 902,35 902,35 902,34 902,35 902,35 902,35 903,35 903,35 903,35 903,36 903,36 903,36 903,37 903,37 904,37 904,38 904,38 904,39 904,39 904,40 904,40 904,41 905,42 905,42 905,43 905,43 905,44 905,44 905,45 905,45 906,46 906,46 906,47 906,47 906,48 906,48 906,48 906,49 907,49 907,50 907,50 907,50 907,50 907,51 907,51 907,51 908,51 908,52 908,52 908,52 908,52 908,52 908,53 908,53 909,53 909,53 909,53 909,53 909,53 909,53 909,53 909,54 910,54 910,54 910,54 910,54 910,54 910,54 910,54 910,54 911,54 911,54 911,54 911,54 911,54 911,54 911,54 911,54 912,54 912,54 912,55 912,55 912,55 912,55 912,55 912,55 913,55 913,55 913,55 913,55 913,55 913,55 913,55 913,55 914,55 914,55 914,55 914,55 914,56 914,56 914,56 914,56 915,56 915,56 915,56 915,56 915,56 915,56 915,56 915,56 916,56 916,56 916,56 916,56 916,57 916,57 916,57 916,57 917,57 917,57 917,57 917,57 917,57 917,57 917,57 917,57 918,57 918,57 918,57 918,57 918,57 918,57 918,57 918,57 919,57 919,57 919,57 919,57 919,57 919,57 919,57 919,57 920,57 920,57 920,57 920,57 920,57 920,57 920,57 920,57 921,57 921,57 921,57 921,57 921,57 921,57 921,57 921,57 922,57 922,57 922,58 922,58 922,58 922,58 922,58 922,58 923,58 923,58 923,58 923,58 923,58 923,58 923,58 923,58 924,58 924,58 924,58 924,58 924,58 924,58 924,58 924,58 925,58 925,58 925,58 925,58 925,58 925,58 925,58 925,58 926,58 926,58 926,58 926,58 926,58 926,58 926,58 926,58 927,58 927,58 927,58 927,58 927,58 927,58 927,58 927,58 928,58 928,58 928,58 928,58 928,58 928,58 928,58 928,58 929,58 929,58 929,58 929,58 929,58 929,58 929,58 929,58 930,58 930,58 930,58 930,58 930,58 930,58 930,58 930,58 931,58 931,58 931,58 931,58 931,58 931,58 931,58 931,58 932,58 932,58 932,58 932,58 932,58 932,58 932,58 932,58 933,58 933,58 933,58 933,58 933,58 933,58 933,58 933,58 934,58 934,58 934,58 934,58 934,58 934,58 934,58 934,58 935,58 935,58 935,58 935,58 935,58 935,58 935,58 935,58 936,58 936,58 936,58 936,58 936,58 936,58 936,58 936,58 937,58 937,58 937,58 937,58 937,58 937,58 937,58 937,58 938,58 938,58 938,58 938,58 938,58 938,58 938,58 938,58 939,58 939,58 939,58 939,58 939,58 939,58 939,58 939,58 940,58 940,58 940,58 940,58 940,58 940,58 940,58 940,58 941,58 941,58 941,58 941,58 941,58 941,58 941,58 941,58 942,58 942,58 942,58 942,58 942,58 942,58 942,58 942,58 943,58 943,58 943,58 943,58 943,58 943,58 943,58 943,58 944,58 944,58 944,58 944,58 944,58 944,58 944,58 944,58 945,58 945,58 945,58 945,58 945,58 945,58 945,58 945,58 946,58 946,58 946,58 946,58 946,58 946,58 946,58 946,58 947,58 947,58 947,58 947,58 947,58 947,58 947,58 947,58 948,58 948,58 948,58 948,58 948,58 948,58 948,58 948,58 949,58 949,58 949,58 949,58 949,58 949,58 949,58 949,58 950,58 950,58 950,58 950,58 950,58 950,58 950,58 950,58 951,58 951,58 888,58 &quot;&#x2F;&gt;
&lt;polygon opacity=&quot;1&quot; fill=&quot;var(--site-accent-meta)&quot; points=&quot;888,58 888,58 889,58 889,58 889,58 889,58 889,58 889,58 889,58 889,58 890,58 890,58 890,58 890,58 890,58 890,58 890,58 890,58 891,58 891,58 891,58 891,58 891,58 891,58 891,58 891,58 892,58 892,58 892,58 892,58 892,58 892,58 892,58 892,58 893,58 893,58 893,58 893,58 893,58 893,58 893,58 893,58 894,59 894,59 894,59 894,59 894,59 894,59 894,59 894,59 895,59 895,59 895,59 895,59 895,59 895,59 895,59 895,59 896,59 896,60 896,60 896,60 896,60 896,60 896,60 896,61 897,61 897,61 897,61 897,62 897,62 897,62 897,63 897,63 898,64 898,64 898,65 898,65 898,66 898,66 898,67 898,67 899,68 899,68 899,69 899,70 899,70 899,71 899,72 899,72 899,73 900,74 900,74 900,75 900,76 900,76 900,77 900,78 900,78 901,79 901,79 901,80 901,80 901,80 901,81 901,81 901,81 902,82 902,82 902,82 902,82 902,82 902,82 902,82 902,82 903,82 903,81 903,81 903,81 903,81 903,80 903,80 903,79 904,79 904,79 904,78 904,78 904,77 904,77 904,76 904,75 905,75 905,74 905,74 905,73 905,73 905,72 905,72 905,71 906,71 906,70 906,70 906,69 906,69 906,68 906,68 906,68 907,67 907,67 907,67 907,66 907,66 907,66 907,66 907,65 908,65 908,65 908,65 908,64 908,64 908,64 908,64 908,64 909,64 909,64 909,63 909,63 909,63 909,63 909,63 909,63 910,63 910,63 910,63 910,63 910,63 910,63 910,63 910,62 911,62 911,62 911,62 911,62 911,62 911,62 911,62 911,62 912,62 912,62 912,62 912,62 912,62 912,62 912,62 912,62 913,62 913,62 913,62 913,61 913,61 913,61 913,61 913,61 914,61 914,61 914,61 914,61 914,61 914,61 914,61 914,61 915,61 915,61 915,61 915,60 915,60 915,60 915,60 915,60 916,60 916,60 916,60 916,60 916,60 916,60 916,60 916,60 917,60 917,60 917,60 917,60 917,60 917,60 917,60 917,60 918,59 918,59 918,59 918,59 918,59 918,59 918,59 918,59 919,59 919,59 919,59 919,59 919,59 919,59 919,59 919,59 920,59 920,59 920,59 920,59 920,59 920,59 920,59 920,59 921,59 921,59 921,59 921,59 921,59 921,59 921,59 921,59 922,59 922,59 922,59 922,59 922,59 922,59 922,59 922,59 923,59 923,59 923,59 923,59 923,59 923,59 923,59 923,59 924,59 924,59 924,59 924,59 924,59 924,59 924,59 924,59 925,59 925,59 925,59 925,59 925,59 925,59 925,59 925,59 926,59 926,59 926,59 926,59 926,59 926,59 926,59 926,59 927,59 927,59 927,59 927,59 927,59 927,59 927,59 927,59 928,59 928,59 928,59 928,59 928,59 928,59 928,59 928,59 929,59 929,59 929,59 929,59 929,59 929,59 929,59 929,59 930,59 930,59 930,59 930,59 930,59 930,59 930,59 930,59 931,59 931,59 931,59 931,59 931,59 931,59 931,59 931,59 932,59 932,59 932,59 932,59 932,59 932,59 932,59 932,59 933,59 933,59 933,59 933,59 933,59 933,59 933,59 933,59 934,59 934,59 934,59 934,59 934,59 934,59 934,59 934,59 935,59 935,59 935,59 935,59 935,59 935,59 935,59 935,59 936,59 936,59 936,59 936,59 936,59 936,59 936,59 936,59 937,59 937,59 937,59 937,59 937,59 937,59 937,59 937,59 938,59 938,59 938,59 938,59 938,59 938,59 938,59 938,58 939,58 939,58 939,58 939,58 939,58 939,58 939,58 939,58 940,58 940,58 940,58 940,58 940,58 940,58 940,58 940,58 941,58 941,58 941,58 941,58 941,58 941,58 941,58 941,58 942,58 942,58 942,58 942,58 942,58 942,58 942,58 942,58 943,58 943,58 943,58 943,58 943,58 943,58 943,58 943,58 944,58 944,58 944,58 944,58 944,58 944,58 944,58 944,58 945,58 945,58 945,58 945,58 945,58 945,58 945,58 945,58 946,58 946,58 946,58 946,58 946,58 946,58 946,58 946,58 947,58 947,58 947,58 947,58 947,58 947,58 947,58 947,58 948,58 948,58 948,58 948,58 948,58 948,58 948,58 948,58 949,58 949,58 949,58 949,58 949,58 949,58 949,58 949,58 950,58 950,58 950,58 950,58 950,58 950,58 950,58 950,58 951,58 951,58 888,58 &quot;&#x2F;&gt;
&lt;&#x2F;svg&gt;


  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;aside class=&quot;aside&quot;&gt;
  &lt;p&gt;The benchmarks shown here were calculated on my Ryzen 5950x in a TTY.&lt;&#x2F;p&gt;
&lt;p&gt;Notably, I used the list of my wallpapers stored in RAM as the test case. I currently have 51 wallpapers in my directory.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;

&lt;div style=&quot;display: flex; gap: 20px; overflow-x: auto; font-size: 0.75rem; font-family: sans-serif;&quot;&gt;
&lt;div style=&quot;flex: 1; min-width: 300px;&quot;&gt;
&lt;div style=&quot;text-align: center; font-weight: bold; border-bottom: 2px solid #ddd; padding-bottom: 5px; margin-bottom: 5px; font-size: 0.95rem;&quot;&gt;Algorithm L&lt;&#x2F;div&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Metric&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: center&quot;&gt;Lower Bound&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: center&quot;&gt;Estimate&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: center&quot;&gt;Upper Bound&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Slope&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;63.625 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;63.685 ns&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;63.753 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;R²&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;0.9538650&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;0.9544209&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;0.9537203&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Mean&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;63.666 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;63.713 ns&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;63.762 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Std. Dev.&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;465.83 ps&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;547.00 ps&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;621.83 ps&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Median&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;63.512 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;63.534 ns&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;63.553 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;MAD&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;187.12 ps&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;210.03 ps&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;240.41 ps&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;&#x2F;div&gt;
&lt;div style=&quot;flex: 1; min-width: 300px;&quot;&gt;
&lt;div style=&quot;text-align: center; font-weight: bold; border-bottom: 2px solid #ddd; padding-bottom: 5px; margin-bottom: 5px; font-size: 0.95rem;&quot;&gt;Algorithm R&lt;&#x2F;div&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: center&quot;&gt;Lower Bound&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: center&quot;&gt;Estimate&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: center&quot;&gt;Upper Bound&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;48.057 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;48.085 ns&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;48.116 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;0.9829652&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;0.9831714&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;0.9829132&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;48.072 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;48.094 ns&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;48.117 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;217.27 ps&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;259.18 ps&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;300.39 ps&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;47.980 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;47.985 ns&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;47.992 ns&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;41.994 ps&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;51.568 ps&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;span style=&quot;color: #888;&quot;&gt;64.680 ps&lt;&#x2F;span&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;As we can see, Algorithm R is faster than Algorithm L … Wait, What? The keen-eyed might already suspect why this is the
case. There are two related issues leading to the surprising performance difference, operation cost and mechanical sympathy.
Namely, Big O says that the time complexity for algorithm R is &lt;em&gt;O(n)&lt;&#x2F;em&gt; and for algorithm L it is &lt;em&gt;O(k(1 + log(n&#x2F;k)))&lt;&#x2F;em&gt;.
However, this is only true if all primitive operations take the same length of time or number of CPU cycles — which
does not happen on modern CPUs.&lt;&#x2F;p&gt;
&lt;p&gt;On modern CPUs algorithm R is (roughly) an integer increment, a pseudo-random integer generation, and a simple integer comparison. All
in all on most modern CPUs this will be between 3 to 7 CPU cycles per iteration. Algorithm L is (roughly) a pseudo-random
floating-point generation, two natural logarithms, a floating-point division and then a cast to &lt;code&gt;usize&lt;&#x2F;code&gt;. Logarithms are transcendental functions.
They cannot be executed in a single CPU cycle. Instead, they require complex microcode approximations or iterative hardware loops.
All in all algorithm L will take roughly  60 to 120 CPU cycles per iteration — before we even get to things like
pipelining and branch prediction. Of course there are fewer iterations in algorithm L, but not enough to outweigh the raw
speed of simple instructions &lt;strong&gt;for this use-case.&lt;&#x2F;strong&gt; If &lt;em&gt;k&lt;&#x2F;em&gt; and &lt;em&gt;n&lt;&#x2F;em&gt; were both much, much larger algorithm L would snatch the crown,
as the cost of floating-point operations is dwarfed by the number of elements algorithm L would allow us to skip relative to
algorithm R. We pursued meaningless minimalism, and discovered that the real meaningless minimalism was the friends we made along
the way, and that mechanical sympathy beats theoretical complexity every single time.&lt;&#x2F;p&gt;
&lt;p&gt;To tie a bow in the wallpaper selector saga, there are a couple of things we can do outside of the reservoir sampling
algorithm to make the final utility faster. Use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;fastrand&#x2F;latest&#x2F;fastrand&#x2F;&quot;&gt;fastrand&lt;&#x2F;a&gt;
crate to generate random numbers, they are not true random numbers but are very fast to generate. Avoid allocations
by making use of Rust’s iterators which are lazily evaluated. Filter unsupported image types to reduce the number of
elements evaluated for reservoir sampling. Use only &lt;code&gt;DirEntry&lt;&#x2F;code&gt; not &lt;code&gt;PathBuf&lt;&#x2F;code&gt;, so no allocations are needed. Putting
that all together, the final version of my random wallpaper utility looks something like this:
&lt;aside class=&quot;aside&quot;&gt;
  &lt;p&gt;The core implementation of algorithm R is in the commented reservoir sampling block. A plus side of using Algorithm R
is that it is much easier to understand, even if you know nothing about reservoir sampling.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot; data-name=&quot;algorithm_r.rs&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; pick_random_wallpaper&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(dir&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Path&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;&amp;lt;walkdir&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;DirEntry&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Box&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; candidates&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; WalkDir&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(dir)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;into_iter&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;filter_map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ok)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; Filter using borrowed DirEntry to avoid early allocations&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;entry&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; entry&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;file_type&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;is_file&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; is_supported_image&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(entry&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;()));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-storage&quot;&gt;    let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; chosen&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;&amp;lt;walkdir&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;DirEntry&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-storage&quot;&gt;    let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; rng&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; fastrand&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Rng&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; Reservoir sampling for a single item&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; (i, entry)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; candidates&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;enumerate&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; rng&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;..=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;i)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            chosen&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(entry);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    Ok&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;(chosen)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;appendix&quot;&gt;Appendix&lt;&#x2F;h2&gt;
&lt;p&gt;Now that you know for a wallpaper directory of 51 images, algorithm R is faster. The natural next question is:
at what directory size does algorithm L dethrone algorithm R? It turns out that my current directory size was
right at the inflection point, if I had slightly more images this post might never have happened. The figure
below shows implementations for algorithm R and algorithm L optimised in the same way as the snippet above:&lt;&#x2F;p&gt;
&lt;div class=&quot;tufte-plot-container&quot;&gt;
  &lt;div class=&quot;tufte-plot-item&quot;&gt;
    
&lt;svg viewBox=&quot;0 0 960 540&quot; xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot;&gt;
&lt;text x=&quot;26&quot; y=&quot;263&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot; transform=&quot;rotate(270, 26, 263)&quot;&gt;
Average time (ns)
&lt;&#x2F;text&gt;
&lt;text x=&quot;510&quot; y=&quot;514&quot; dy=&quot;-0.5ex&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
Files
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;85,52 85,473 &quot;&#x2F;&gt;
&lt;text x=&quot;76&quot; y=&quot;472&quot; dy=&quot;0.5ex&quot; text-anchor=&quot;end&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
10.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;80,472 85,472 &quot;&#x2F;&gt;
&lt;text x=&quot;76&quot; y=&quot;424&quot; dy=&quot;0.5ex&quot; text-anchor=&quot;end&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
20.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;80,424 85,424 &quot;&#x2F;&gt;
&lt;text x=&quot;76&quot; y=&quot;375&quot; dy=&quot;0.5ex&quot; text-anchor=&quot;end&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
30.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;80,375 85,375 &quot;&#x2F;&gt;
&lt;text x=&quot;76&quot; y=&quot;327&quot; dy=&quot;0.5ex&quot; text-anchor=&quot;end&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
40.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;80,327 85,327 &quot;&#x2F;&gt;
&lt;text x=&quot;76&quot; y=&quot;279&quot; dy=&quot;0.5ex&quot; text-anchor=&quot;end&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
50.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;80,279 85,279 &quot;&#x2F;&gt;
&lt;text x=&quot;76&quot; y=&quot;231&quot; dy=&quot;0.5ex&quot; text-anchor=&quot;end&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
60.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;80,231 85,231 &quot;&#x2F;&gt;
&lt;text x=&quot;76&quot; y=&quot;183&quot; dy=&quot;0.5ex&quot; text-anchor=&quot;end&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
70.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;80,183 85,183 &quot;&#x2F;&gt;
&lt;text x=&quot;76&quot; y=&quot;135&quot; dy=&quot;0.5ex&quot; text-anchor=&quot;end&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
80.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;80,135 85,135 &quot;&#x2F;&gt;
&lt;text x=&quot;76&quot; y=&quot;87&quot; dy=&quot;0.5ex&quot; text-anchor=&quot;end&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
90.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;80,87 85,87 &quot;&#x2F;&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;86,474 933,474 &quot;&#x2F;&gt;
&lt;text x=&quot;86&quot; y=&quot;484&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
10.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;86,474 86,479 &quot;&#x2F;&gt;
&lt;text x=&quot;180&quot; y=&quot;484&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
20.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;180,474 180,479 &quot;&#x2F;&gt;
&lt;text x=&quot;274&quot; y=&quot;484&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
30.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;274,474 274,479 &quot;&#x2F;&gt;
&lt;text x=&quot;368&quot; y=&quot;484&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
40.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;368,474 368,479 &quot;&#x2F;&gt;
&lt;text x=&quot;462&quot; y=&quot;484&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
50.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;462,474 462,479 &quot;&#x2F;&gt;
&lt;text x=&quot;556&quot; y=&quot;484&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
60.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;556,474 556,479 &quot;&#x2F;&gt;
&lt;text x=&quot;650&quot; y=&quot;484&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
70.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;650,474 650,479 &quot;&#x2F;&gt;
&lt;text x=&quot;744&quot; y=&quot;484&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
80.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;744,474 744,479 &quot;&#x2F;&gt;
&lt;text x=&quot;838&quot; y=&quot;484&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
90.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;838,474 838,479 &quot;&#x2F;&gt;
&lt;text x=&quot;933&quot; y=&quot;484&quot; dy=&quot;0.76em&quot; text-anchor=&quot;middle&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
100.0
&lt;&#x2F;text&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-text)&quot; stroke-width=&quot;1&quot; points=&quot;933,474 933,479 &quot;&#x2F;&gt;
&lt;circle cx=&quot;86&quot; cy=&quot;311&quot; r=&quot;3&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-alizarin)&quot; stroke=&quot;none&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
&lt;circle cx=&quot;462&quot; cy=&quot;210&quot; r=&quot;3&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-alizarin)&quot; stroke=&quot;none&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
&lt;circle cx=&quot;697&quot; cy=&quot;184&quot; r=&quot;3&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-alizarin)&quot; stroke=&quot;none&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
&lt;circle cx=&quot;791&quot; cy=&quot;181&quot; r=&quot;3&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-alizarin)&quot; stroke=&quot;none&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
&lt;circle cx=&quot;933&quot; cy=&quot;170&quot; r=&quot;3&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-alizarin)&quot; stroke=&quot;none&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-accent-alizarin)&quot; stroke-width=&quot;1&quot; points=&quot;86,311 462,210 697,184 791,181 933,170 &quot;&#x2F;&gt;
&lt;circle cx=&quot;86&quot; cy=&quot;473&quot; r=&quot;3&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-cool)&quot; stroke=&quot;none&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
&lt;circle cx=&quot;462&quot; cy=&quot;288&quot; r=&quot;3&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-cool)&quot; stroke=&quot;none&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
&lt;circle cx=&quot;697&quot; cy=&quot;164&quot; r=&quot;3&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-cool)&quot; stroke=&quot;none&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
&lt;circle cx=&quot;791&quot; cy=&quot;127&quot; r=&quot;3&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-cool)&quot; stroke=&quot;none&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
&lt;circle cx=&quot;933&quot; cy=&quot;52&quot; r=&quot;3&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-cool)&quot; stroke=&quot;none&quot; stroke-width=&quot;1&quot;&#x2F;&gt;
&lt;polyline fill=&quot;none&quot; opacity=&quot;1&quot; stroke=&quot;var(--site-accent-cool)&quot; stroke-width=&quot;1&quot; points=&quot;86,473 462,288 697,164 791,127 933,52 &quot;&#x2F;&gt;
&lt;text x=&quot;131&quot; y=&quot;67&quot; dy=&quot;0.76em&quot; text-anchor=&quot;start&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
Algorithm L
&lt;&#x2F;text&gt;
&lt;text x=&quot;131&quot; y=&quot;82&quot; dy=&quot;0.76em&quot; text-anchor=&quot;start&quot; font-family=&quot;sans-serif&quot; font-size=&quot;9.67741935483871&quot; opacity=&quot;1&quot; fill=&quot;var(--site-text)&quot;&gt;
Algorithm R
&lt;&#x2F;text&gt;
&lt;rect x=&quot;101&quot; y=&quot;67&quot; width=&quot;20&quot; height=&quot;10&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-alizarin)&quot; stroke=&quot;none&quot;&#x2F;&gt;
&lt;rect x=&quot;101&quot; y=&quot;82&quot; width=&quot;20&quot; height=&quot;10&quot; opacity=&quot;1&quot; fill=&quot;var(--site-accent-cool)&quot; stroke=&quot;none&quot;&#x2F;&gt;
&lt;&#x2F;svg&gt;


  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;If you have more than approximately 70 wallpapers in your folder, you have my explicit permission to use Algorithm L,
it will be faster.&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;markkm.com&#x2F;blog&#x2F;reservoir-sampling&#x2F;&quot;&gt;Who discovered Algorithm R?&lt;&#x2F;a&gt; &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>On Blogging</title>
        <published>2026-05-29T00:00:00+00:00</published>
        <updated>2026-05-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Luke Hannan
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://luhann.com/posts/blog-setup/"/>
        <id>https://luhann.com/posts/blog-setup/</id>
        
        <content type="html" xml:base="https://luhann.com/posts/blog-setup/">&lt;p&gt;Like many academics before me, I have spent years upholding a storied tradition: attempting to maintain a public-facing
website, failing, and trying again. My initial foray into the world of personal academic websites was
with the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt; static site generator using the Academic theme. &lt;span class=&quot;margin&quot;&gt;
  I went
looking for the original Academic theme I used, and I can’t find it anywhere. There are still some screenshots of the
old theme on Google Images.
&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;My problem with Hugo was two-fold. Firstly, I didn’t understand &lt;strong&gt;how&lt;&#x2F;strong&gt; to really use Hugo, and I didn’t put in any
effort to rectify that shortcoming. Secondly, I did not keep up-to-date with any of the developments of Hugo or the
Academic theme that I was using. This meant that updates to Hugo — or the academic theme — frequently broke
my website. I would apply the updated theme, and then Hugo would fail to build as I hadn’t set one of the new configuration
options or additional settings.&lt;&#x2F;p&gt;
&lt;p&gt;Could I have remedied the situation by spending some time learning Hugo and how the themes worked? Absolutely. However,
at the time I wanted something that just worked ™, without having to spend any time or effort. &lt;span class=&quot;margin left&quot;&gt;
  History
has shown that this is not a winning strategy in any endeavour.
&lt;&#x2F;span&gt;
 I would routinely have to spend hours getting
my site up-to-date with all of the new changes before I could even think of writing a blog post. Given how infrequently
I actually worked on my website, the ratio of fixing to writing was sub-optimal to say the least.&lt;&#x2F;p&gt;
&lt;p&gt;Eventually, I got sick of that vicious cycle and gave up on writing my own blog posts. Instead, I switched to a purely
static personal website that was made up of just a single &lt;code&gt;index.html&lt;&#x2F;code&gt; with a &lt;em&gt;very&lt;&#x2F;em&gt; brief biography and some social
media links.
&lt;aside class=&quot;aside&quot;&gt;
  &lt;p&gt;Without the CSS, my entire website could fit into a single &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Jumbo_frame&quot;&gt;jumbo frame&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Although the above site did not look particularly aesthetically pleasing, it had the distinct advantage of minimalism.
No updates would cause it to not work, and editing or adding content was extremely easy. There was no framework updating
or tinkering with settings. Perhaps a version of myself born in the 80s would be appreciative of such a website. The
downside was that adding additional &lt;strong&gt;non-homepage&lt;&#x2F;strong&gt; content — like say for example a blog — was extremely challenging,
unless your idea of a good-looking blog was a loose collection of roughly related html files in the same directory.
&lt;span class=&quot;margin&quot;&gt;
  Like three &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Honey_badger&quot;&gt;honey badgers&lt;&#x2F;a&gt; in a trench coat.
&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;I have recently been spending most of my free time writing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; and connecting it to R, through
the excellent &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;extendr.rs&#x2F;&quot;&gt;extendr&lt;&#x2F;a&gt; project. Both the extendr website, and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;extendr.rs&#x2F;blog&#x2F;&quot;&gt;blog&lt;&#x2F;a&gt;
are excellent examples of relatively lightweight but good looking websites. Both are built using the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt;
static site generator. Zola promises that it is &lt;q&gt;A fast static site generator in a single binary with everything built-in. Forget dependencies.
Everything you need in one binary.&lt;&#x2F;q&gt; After dealing with Hugo dependency hell, no dependencies sounded like music to my ears.
Particularly, Zola felt like a perfect middle ground between the extreme minimalism of hand-writing your own &lt;code&gt;html&lt;&#x2F;code&gt; and
using a fully defined Byzantine theme.&lt;&#x2F;p&gt;
&lt;p&gt;A Zola site is just a collection of files and folders, with a specific structure.&lt;&#x2F;p&gt;
&lt;div class=&quot;terminal-window&quot;&gt;
  &lt;div class=&quot;terminal-header&quot;&gt;
    &lt;span class=&quot;dot red&quot;&gt;&lt;&#x2F;span&gt;
    &lt;span class=&quot;dot yellow&quot;&gt;&lt;&#x2F;span&gt;
    &lt;span class=&quot;dot green&quot;&gt;&lt;&#x2F;span&gt;
    &lt;span class=&quot;terminal-title&quot;&gt;~&#x2F;posts&#x2F;blog-setup&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;terminal-body&quot;&gt;
&lt;pre&gt;&lt;code&gt;.
├── &lt;span class=&quot;file-config&quot;&gt;config.toml&lt;&#x2F;span&gt;
├── &lt;span class=&quot;folder&quot;&gt;content&lt;&#x2F;span&gt;
│   ├── &lt;span class=&quot;folder&quot;&gt;about&lt;&#x2F;span&gt;
│   │   └── _index.md
│   ├── &lt;span class=&quot;folder&quot;&gt;posts&lt;&#x2F;span&gt;
│   │   ├── &lt;span class=&quot;folder&quot;&gt;hello_world&lt;&#x2F;span&gt;
│   │   │   └── index.md
│   │   └── _index.md
│   └── _index.md
├── &lt;span class=&quot;folder-highlight&quot;&gt;sass&lt;&#x2F;span&gt;
│   └── main.scss
├── &lt;span class=&quot;folder&quot;&gt;static&lt;&#x2F;span&gt;
│   └─ &lt;span class=&quot;folder&quot;&gt;font&lt;&#x2F;span&gt;
│      └── custom_font.woff2
└── &lt;span class=&quot;folder-highlight&quot;&gt;templates&lt;&#x2F;span&gt;
    ├── base.html
    ├── index.html
    └── post.html&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;aside class=&quot;aside&quot;&gt;
  &lt;p&gt;See &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;getting-started&#x2F;overview&#x2F;&quot;&gt;getting started&lt;&#x2F;a&gt;
for a more official introduction to Zola.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;What attracted me to this layout is how fiercely it aligns with my preference for ownership and predictability. Zola doesn’t
abstract the machinery of a website away behind magic black boxes or deeply nested theme configurations. It offers a transparent,
flat structure that says exactly what it does on the tin. If a page renders incorrectly, I don’t have to hunt through
upstream GitHub theme repositories to find a breaking change; the issue can only be directly inside my own &lt;code&gt;templates&#x2F;&lt;&#x2F;code&gt; directory.
It forces a certain level of intentionality that my previous Hugo setup lacked.&lt;&#x2F;p&gt;
&lt;p&gt;When you run &lt;code&gt;zola build&lt;&#x2F;code&gt;, Zola parses your Markdown files and automatically injects the text into the corresponding HTML
templates. What this means is that you only have to write your core template logic once. From then on, creating a new post
is straightforward, just add a new Markdown file into a folder and &lt;code&gt;zola build&lt;&#x2F;code&gt;. &lt;span class=&quot;margin&quot;&gt;
  The source for my website can be found
here &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;luhann&#x2F;luhann.github.io&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;luhann&#x2F;luhann.github.io&lt;&#x2F;a&gt; as a more detailed demonstration.
&lt;&#x2F;span&gt;

What this gives me is a website that requires no external themes or dependencies — so cannot break like my previous Hugo site —
but the flexibility of editing raw &lt;code&gt;html&lt;&#x2F;code&gt; for each page of my site if I so desire.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, I am being entirely hypocritical here. My main problems from Hugo stemmed from using external
themes, yet I tout the advantages of Zola by creating my own theme from scratch. Of course it will never break.
If I had used an external Zola theme I might eventually run into the same problems as using the Hugo Academic theme.
Conversely, if I had created my own Hugo theme from scratch it is unlikely I would have had any of the issues I did.
What then does this mean for blogging and static site generation? Regardless of the tool you pick, spending some
time upfront to learn the ins-and-outs of the tool is likely going to lead to a much less frustrating workflow in the long
run. &lt;span class=&quot;margin left&quot;&gt;
  Or perhaps the true message is that one should do all they can to limit
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;recology.info&#x2F;2018&#x2F;10&#x2F;limiting-dependencies&#x2F;&quot;&gt;external dependencies&lt;&#x2F;a&gt; wherever possible.
&lt;&#x2F;span&gt;
&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hello World</title>
        <published>2026-05-21T00:00:00+00:00</published>
        <updated>2026-05-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Luke Hannan
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://luhann.com/posts/hello-world/"/>
        <id>https://luhann.com/posts/hello-world/</id>
        
        <content type="html" xml:base="https://luhann.com/posts/hello-world/">&lt;aside class=&quot;aside&quot;&gt;
  &lt;p&gt;Some recent examples:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;opensource.posit.co&#x2F;blog&#x2F;2026-05-21_in-defense-of-yaml&#x2F;&quot;&gt;In Defense of YAML&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kobzol.github.io&#x2F;rust&#x2F;python&#x2F;2023&#x2F;05&#x2F;20&#x2F;writing-python-like-its-rust.html&quot;&gt;Writing Python like it’s Rust&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;josiah.rs&#x2F;posts&#x2F;type-safety&#x2F;&quot;&gt;Type safe(r) R code&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;josiah.rs&#x2F;posts&#x2F;cross-language-ds&#x2F;&quot;&gt;Apache Arrow, Rust, and cross-langauge data science&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;and perhaps more controversially:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;josiah.rs&#x2F;posts&#x2F;gpl-cost&#x2F;&quot;&gt;GPL is holding R back&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;These days I spend much of my time reading excellent blog posts about making data science and R faster (typically using Rust).
I have learnt a great deal from those posts, not just about computer science, but also about the value of writing. Both
for your readers and for yourself as well. Writing forces can sometimes be laborious, but it forces you to convert
your understanding of a thing into something that should (hopefully) be intelligible to your target audience.&lt;&#x2F;p&gt;
&lt;p&gt;Given the above, I’ve decided to attempt to get more practice at both understanding and explaining the tools and methods
I use regularly. I’ll mainly write for applied statisticians and data scientists working in R-heavy workflows.
Expect notes on biostatistics, clinical-trial thinking, and computational methods for microbiome and related omics data.&lt;&#x2F;p&gt;
&lt;p&gt;If any of the above sounds interesting to you, you’re welcome to follow along or to reach out to me:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;elsewhere&quot;&gt;Elsewhere&lt;&#x2F;h2&gt;
&lt;div class=&quot;post-social-links&quot; aria-label=&quot;Social links&quot;&gt;
  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;bsky.app&amp;#x2F;profile&amp;#x2F;luhann.com&quot; aria-label=&quot;Bluesky&quot; title=&quot;Bluesky&quot;&gt;
  &lt;svg class=&quot;social-icon&quot; viewBox=&quot;0 0 16 16&quot; aria-hidden=&quot;true&quot;&gt;
    &lt;path d=&quot;M3.468 5.367C5.303 6.796 7.276 9.695 8 11.192c.724-1.497 2.697-4.396 4.532-5.825C13.855 4.337 16 .331 16 2.769c0 .487-.279 4.09-.443 4.675-.57 2.034-2.646 2.554-4.493 2.24 3.228.548 4.05 2.364 2.277 4.18-3.367 3.447-4.838-.865-5.215-1.97-.069-.202-.101-.296-.126-.296-.024 0-.057.094-.125.296-.377 1.105-1.848 5.417-5.215 1.97-1.774-1.816-.951-3.632 2.277-4.18-1.846.314-3.922-.206-4.493-2.24C.279 6.86 0 3.256 0 2.77 0 .33 2.145 4.337 3.468 5.367z&quot; &#x2F;&gt;
  &lt;&#x2F;svg&gt;
&lt;&#x2F;a&gt;

  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;luhann&quot; aria-label=&quot;GitHub&quot; title=&quot;GitHub&quot;&gt;
  &lt;svg class=&quot;social-icon&quot; viewBox=&quot;0 0 24 24&quot; aria-hidden=&quot;true&quot;&gt;
    &lt;path d=&quot;M12 0C5.37 0 0 5.37 0 12c0 5.3 3.44 9.8 8.21 11.39.6.11.79-.26.79-.58v-2.23c-3.34.73-4.03-1.42-4.03-1.42-.55-1.38-1.33-1.75-1.33-1.75-1.09-.75.08-.73.08-.73 1.2.09 1.84 1.24 1.84 1.24 1.07 1.83 2.81 1.3 3.49 1 .11-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.52.12-3.17 0 0 1.01-.33 3.3 1.23a11.46 11.46 0 0 1 6.01 0c2.29-1.56 3.3-1.23 3.3-1.23.66 1.65.25 2.87.12 3.17.77.84 1.23 1.91 1.23 3.22 0 4.61-2.8 5.62-5.48 5.92.43.37.83 1.1.83 2.22v3.29c0 .32.19.69.8.57A12 12 0 0 0 24 12c0-6.63-5.37-12-12-12z&quot; &#x2F;&gt;
  &lt;&#x2F;svg&gt;
&lt;&#x2F;a&gt;

  &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.linkedin.com&amp;#x2F;in&amp;#x2F;luke-hannan-13bb19b5&amp;#x2F;&quot; aria-label=&quot;LinkedIn&quot; title=&quot;LinkedIn&quot;&gt;
  &lt;svg class=&quot;social-icon&quot; viewBox=&quot;0 0 24 24&quot; aria-hidden=&quot;true&quot;&gt;
    &lt;path d=&quot;M20.45 20.45H16.9v-5.57c0-1.33-.03-3.04-1.85-3.04s-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43c-1.14 0-2.06-.93-2.06-2.07 0-1.14.92-2.06 2.06-2.06s2.06.92 2.06 2.06c0 1.14-.92 2.07-2.06 2.07zM7.12 20.45H3.55V9h3.57v11.45z&quot; &#x2F;&gt;
  &lt;&#x2F;svg&gt;
&lt;&#x2F;a&gt;

&lt;&#x2F;div&gt;
</content>
        
    </entry>
</feed>
