<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://davemq.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://davemq.github.io/" rel="alternate" type="text/html" /><updated>2026-05-12T18:29:42+00:00</updated><id>https://davemq.github.io/feed.xml</id><title type="html">Dave’s Blog</title><subtitle>Dave&apos;s blog</subtitle><entry><title type="html">Remote Linux kernel development with Emacs</title><link href="https://davemq.github.io/2026/05/12/remote-linux-dev-with-emacs.html" rel="alternate" type="text/html" title="Remote Linux kernel development with Emacs" /><published>2026-05-12T00:00:00+00:00</published><updated>2026-05-12T00:00:00+00:00</updated><id>https://davemq.github.io/2026/05/12/remote-linux-dev-with-emacs</id><content type="html" xml:base="https://davemq.github.io/2026/05/12/remote-linux-dev-with-emacs.html"><![CDATA[<p>I work on developing features and fixing bugs in the Linux kernel in
areas specific to IBM Power. I use a number of Emacs’ facilities to
get my work done.</p>

<h1 id="magit">Magit</h1>

<p>I use <a href="https://magit.vc/">Magit</a> extensively with clones of various Git repositories from
<a href="https://git.kernel.org/">https://git.kernel.org/</a>. I keep learning new (to me) things about git
and Magit and using them. I’ve found it useful to learn how things work
with the command line before attempting to use them in Magit.</p>

<h1 id="compilation">Compilation</h1>

<p>This work being with C source code, I make heavy use of <code class="language-plaintext highlighter-rouge">M-x
compile</code>. I’ve made my life easier by preloading a couple of items in
<code class="language-plaintext highlighter-rouge">compile-history</code>. I don’t need this everywhere, just in
my Linux trees, so I put all of my Linux repositories under ~/linux,
and there I’ve created a .dir-locals.el for directory local variables:</p>

<figure class="highlight"><pre><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="p">((</span><span class="no">nil</span> <span class="o">.</span> <span class="p">((</span><span class="nv">compile-history</span> 
	  <span class="o">.</span> 
	  <span class="p">(</span><span class="s">"nice make -k ARCH=powerpc CROSS_COMPILE=powerpc64le-linux-gnu- -j `nproc` all compile_commands.json gtags"</span>
	   <span class="s">"nice make -k ARCH=powerpc CROSS_COMPILE=powerpc64le-linux-gnu- -j `nproc` tarzst-pkg"</span>
	   <span class="p">)</span>
	  <span class="p">)</span>
	 <span class="p">)</span>
      <span class="p">)</span>
 <span class="p">)</span></code></pre></figure>

<p>I do my work on my x86_64 laptop, and cross compile for the powerpc
architecture in 64-bit little endian mode. I take advantage of all of
the threads available on my laptop. I use <code class="language-plaintext highlighter-rouge">nice</code> to make it a bit
easier to interactive things while compiling.</p>

<p>The first history item does the big work. I build the <strong>all</strong> target to
build the kernel and modules. I build compile_commands.json to capture
the information needed by <code class="language-plaintext highlighter-rouge">clangd</code> to use with Eglot. I also
keep gtags databases up-to-date, though I should probably drop that
since I’m completely using Eglot with clangd at this point.</p>

<p>The second history item creates a .tar.zst file from the kernel and
modules, which I transfer to the remote Power partition.</p>

<h1 id="c-mode-and-eglot">C mode and Eglot</h1>

<p>I use c-ts-mode with Eglot. Eglot is integrated with xref.</p>

<h1 id="tramp">TRAMP</h1>

<p>My test systems are remote, so I need to transfer the kernel and
modules over to it. I initially use TRAMP with the <strong>scp</strong> method to
transfer the compressed tar file. If I rebuild and create a new
package with the same name, I switch to the  <strong>rsync</strong> method to
transfer. This saves several seconds each time.</p>

<p>In both of these cases, I’ve set up keys so I do passwordless
authentication.</p>

<h1 id="non-emacs-things-on-the-remote-system">Non-Emacs things on the remote system</h1>

<p>Once I have the tar file on the remote system, there are some
non-Emacs things to do. I’m almost certain I can do them in Emacs, as
they’re just command line things, but I’m just not used to that and
haven’t pushed myself to try it.</p>

<ul>
  <li>untar the tarball in / (as root)</li>
  <li>run <code class="language-plaintext highlighter-rouge">dracut</code> to create an init ramdisk</li>
  <li>optionally remove older kernels and modules</li>
  <li>run <code class="language-plaintext highlighter-rouge">grub2-mkconfig</code> to add the new kernel to the GRUB menu</li>
  <li>copy the new grub config file to the right place</li>
  <li>reboot</li>
</ul>]]></content><author><name></name></author><category term="emacs" /><category term="linux" /><category term="kernel" /><category term="eglot" /><category term="magit" /><category term="tramp" /><summary type="html"><![CDATA[I work on developing features and fixing bugs in the Linux kernel in areas specific to IBM Power. I use a number of Emacs’ facilities to get my work done.]]></summary></entry><entry><title type="html">Browsing URLs from Gnus Summary buffer</title><link href="https://davemq.github.io/emacs/gnus/url/2026/04/29/visiting-urls-from-gnus-summary.html" rel="alternate" type="text/html" title="Browsing URLs from Gnus Summary buffer" /><published>2026-04-29T00:00:00+00:00</published><updated>2026-04-29T00:00:00+00:00</updated><id>https://davemq.github.io/emacs/gnus/url/2026/04/29/visiting-urls-from-gnus-summary</id><content type="html" xml:base="https://davemq.github.io/emacs/gnus/url/2026/04/29/visiting-urls-from-gnus-summary.html"><![CDATA[<p>I’ve been reading RSS and Atom feeds in Gnus for a few weeks, having
moved over from elfeed. IIRC in elfeed it was pretty easy to visit the
URL that an entry summarized. I’ve been searching for a while to do
the same in Gnus, without much luck.</p>

<p>But today I finally hit <code class="language-plaintext highlighter-rouge">C-h b</code> while in the Gnus summary buffer, and
finally noticed <code class="language-plaintext highlighter-rouge">w</code> bound to
<code class="language-plaintext highlighter-rouge">gnus-summary-browse-url</code>. Aha! This function will “Scan
the current article body for links, and offer to browse them.” For
many articles, this will get me the one and only link, to the
article. For some of the articles, this finds several URLs, so I just
have to try to pick the right one. For that, I note</p>

<blockquote>
  <p>If only one link is found, browse that directly, otherwise use
completion to select a link.  The first link marked in the
article text with ‘gnus-collect-urls-primary-text’ is the
default.</p>
</blockquote>

<p>And what is <code class="language-plaintext highlighter-rouge">gnus-collect-urls-primary-text</code>? It’s “The
button text for the default link in ‘gnus-summary-browse-url’” and its
value is “Link”. Exactly what I’m usually seeking!</p>

<p>This is an example of Emacs being self-documenting.</p>]]></content><author><name></name></author><category term="emacs" /><category term="gnus" /><category term="url" /><summary type="html"><![CDATA[I’ve been reading RSS and Atom feeds in Gnus for a few weeks, having moved over from elfeed. IIRC in elfeed it was pretty easy to visit the URL that an entry summarized. I’ve been searching for a while to do the same in Gnus, without much luck.]]></summary></entry><entry><title type="html">Writing an automated test to try to find an Emacs bug</title><link href="https://davemq.github.io/emacs/test/2026/04/27/test.html" rel="alternate" type="text/html" title="Writing an automated test to try to find an Emacs bug" /><published>2026-04-27T00:00:00+00:00</published><updated>2026-04-27T00:00:00+00:00</updated><id>https://davemq.github.io/emacs/test/2026/04/27/test</id><content type="html" xml:base="https://davemq.github.io/emacs/test/2026/04/27/test.html"><![CDATA[<p>BUG: Emacs is losing the default font information from init.el when
adding a68-mode or alda-mode. It’s difficult to figure out what
exactly is happening. It <em>seems</em> related to having a large number of
packages installed, but perhaps its just the right combination of
combination of packages.</p>

<p>DISCLAIMER: I strongly suspect there are better ways to do some of
this, but what I have seems to be working.</p>

<p>I’m trying to write a test that starts with default font information
specifying a font. It probably doesn’t matter <em>what</em> font, as the end
result of the bug is this getting replaced with</p>

<figure class="highlight"><pre><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="p">(</span><span class="nv">custom-set-faces</span>
 <span class="c1">;; custom-set-faces was added by Custom.</span>
 <span class="c1">;; If you edit it by hand, you could mess it up, so be careful.</span>
 <span class="c1">;; Your init file should contain only one such instance.</span>
 <span class="c1">;; If there is more than one, they won't work right.</span>
 <span class="o">'</span><span class="p">(</span><span class="nv">default</span> <span class="p">((</span><span class="no">t</span> <span class="p">(</span><span class="ss">:background</span> <span class="no">nil</span><span class="p">)))))</span></code></pre></figure>

<p>So to check whether the bug has happened, I can either</p>

<ul>
  <li>search for the pattern above, OR</li>
  <li>search for the specific font I’ve specified</li>
</ul>

<p>I will search for the specific font, as I’d like to detect <em>any</em>
changes, not just the change above.</p>

<p>The bug seems to happen when installing a68-mode or alda-mode just
after starting Emacs. So, this test will be a loop of many Emacs
invocations.</p>

<ul>
  <li>Start Emacs</li>
  <li>Install a68-mode</li>
  <li>Check whether the default font information has been destroyed
    <ul>
      <li>If so, note the last package installed.</li>
      <li>If not
        <ul>
          <li>delete (uninstall) a68-mode</li>
          <li>install a random package</li>
          <li>write the package name just installed to a file</li>
          <li>exit Emacs</li>
          <li>Run the test again</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h1 id="initel">init.el</h1>

<p>init.el contains the font information. I also initially added a
“blank” <code class="language-plaintext highlighter-rouge">custom-set-variables</code> call, but over time I
discovered that sometimes <code class="language-plaintext highlighter-rouge">restart-emacs</code> would see a
process talking to ELPA and ask if I wanted to kill the process. No, I
don’t want to be asked that. I found <code class="language-plaintext highlighter-rouge">confirm-kill-processes</code>
was the variable I could set to <code class="language-plaintext highlighter-rouge">nil</code> to avoid getting
asked. So into the set of of custom variables it went.</p>

<figure class="highlight"><pre><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="c1">;;; -*- lexical-binding: t -*-</span>
<span class="p">(</span><span class="nv">custom-set-variables</span>
 <span class="c1">;; custom-set-variables was added by Custom.</span>
 <span class="c1">;; If you edit it by hand, you could mess it up, so be careful.</span>
 <span class="c1">;; Your init file should contain only one such instance.</span>
 <span class="c1">;; If there is more than one, they won't work right.</span>
 <span class="o">'</span><span class="p">(</span><span class="nv">confirm-kill-processes</span> <span class="no">nil</span><span class="p">)</span>
 <span class="p">)</span>
<span class="p">(</span><span class="nv">custom-set-faces</span>
 <span class="c1">;; custom-set-faces was added by Custom.</span>
 <span class="c1">;; If you edit it by hand, you could mess it up, so be careful.</span>
 <span class="c1">;; Your init file should contain only one such instance.</span>
 <span class="c1">;; If there is more than one, they won't work right.</span>
 <span class="o">'</span><span class="p">(</span><span class="nv">default</span> <span class="p">((</span><span class="no">t</span> <span class="p">(</span><span class="ss">:family</span> <span class="s">"Fira Code"</span> <span class="ss">:foundry</span> <span class="s">"CTDB"</span> <span class="ss">:slant</span> <span class="nv">normal</span> <span class="ss">:weight</span> <span class="nv">medium</span> <span class="ss">:height</span> <span class="mi">120</span> <span class="ss">:width</span> <span class="nv">normal</span><span class="p">)))))</span></code></pre></figure>

<h1 id="start-testsh">start-test.sh</h1>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/sh</span>

<span class="nv">TESTDIR</span><span class="o">=</span><span class="nv">$HOME</span>/bug-emacs.d
<span class="nb">cd</span> <span class="nv">$TESTDIR</span>
emacs <span class="nt">--init-directory</span><span class="o">=</span><span class="nv">$TESTDIR</span> <span class="nt">--script</span> <span class="nv">$TESTDIR</span>/test.el</code></pre></figure>

<h1 id="test">Test</h1>

<figure class="highlight"><pre><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="c1">;;;</span>
<span class="c1">;;; Copy this script into an empty user-emacs-directory along with</span>
<span class="c1">;;; the accompanying init.el.</span>
<span class="c1">;;;</span>
<span class="c1">;;; Run this script with</span>
<span class="c1">;;;</span>
<span class="c1">;;;     emacs --init-directory=&lt;directory-path&gt; --load=&lt;directory-path&gt;/test.el</span>
<span class="c1">;;;</span></code></pre></figure>

<h1 id="load-up-packages">Load up packages</h1>

<p>List packages and set buffer to <strong>Packages</strong></p>

<figure class="highlight"><pre><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="p">(</span><span class="k">progn</span>
  <span class="p">(</span><span class="nv">list-packages</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">set-buffer</span> <span class="s">"*Packages*"</span><span class="p">)</span>
  <span class="p">)</span></code></pre></figure>

<h1 id="install-a68-mode">Install a68-mode</h1>

<figure class="highlight"><pre><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="p">(</span><span class="nv">message</span> <span class="s">"%s"</span> <span class="s">"Installing a68-mode"</span><span class="p">)</span>
<span class="p">(</span><span class="nv">sit-for</span> <span class="mf">0.5</span><span class="p">)</span>
<span class="p">(</span><span class="nv">package-install</span> <span class="ss">'a68-mode</span><span class="p">)</span></code></pre></figure>

<h1 id="check-that-font-information-is-still-in-initel">Check that font information is still in init.el</h1>

<figure class="highlight"><pre><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="p">(</span><span class="nv">find-file</span> <span class="p">(</span><span class="nv">locate-user-emacs-file</span> <span class="s">"init.el"</span><span class="p">))</span>
<span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">not</span> <span class="p">(</span><span class="nv">search-forward</span> <span class="s">"(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(default ((t (:family \"Fira Code\" :foundry \"CTDB\" :slant normal :weight medium :height 120 :width normal)))))"</span>
			 <span class="no">nil</span> <span class="no">t</span><span class="p">))</span>
    <span class="c1">;; String not found. Read last package installed from file and print it out. Then exit Emacs.</span>
    <span class="c1">;;</span>
    <span class="c1">;; Read string from file and print</span>
    <span class="p">(</span><span class="k">progn</span>
      <span class="p">(</span><span class="nv">find-file</span> <span class="s">"last-installed.txt"</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">display-message-or-buffer</span> <span class="s">"Install of %s caused subsequent install of a68-mode to lose font information"</span> <span class="p">(</span><span class="nv">buffer-string</span><span class="p">))</span>
      <span class="p">(</span><span class="nv">sit-for</span> <span class="mf">0.5</span><span class="p">)</span>
      <span class="c1">;; Exit Emacs</span>
      <span class="p">(</span><span class="nv">kill-emacs</span><span class="p">)</span> 
     <span class="p">)</span>
    <span class="p">)</span></code></pre></figure>

<h1 id="uninstalldelete-a68-mode">Uninstall/delete a68-mode</h1>

<figure class="highlight"><pre><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="p">(</span><span class="nv">message</span> <span class="s">"%s"</span> <span class="s">"Removing a68-mode"</span><span class="p">)</span>
<span class="p">(</span><span class="nv">sit-for</span> <span class="mf">0.5</span><span class="p">)</span>
<span class="p">(</span><span class="nv">package-delete</span> <span class="p">(</span><span class="nv">package-get-descriptor</span> <span class="ss">'a68-mode</span><span class="p">))</span></code></pre></figure>

<h1 id="install-random-package">Install random package</h1>

<p>Install random package and write its name to last-installed file</p>

<figure class="highlight"><pre><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">pkg</span> <span class="p">(</span><span class="nb">car</span> <span class="p">(</span><span class="nv">seq-random-elt</span> <span class="nv">package-archive-contents</span><span class="p">))))</span>
  <span class="p">(</span><span class="nv">save-current-buffer</span>
    <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">buffer</span> <span class="p">(</span><span class="nv">find-file</span> <span class="s">"last-installed.txt"</span><span class="p">)))</span>
      <span class="p">(</span><span class="nv">set-buffer</span> <span class="nv">buffer</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">kill-region</span> <span class="p">(</span><span class="nv">point-min</span><span class="p">)</span> <span class="p">(</span><span class="nv">point-max</span><span class="p">))</span>
      <span class="p">(</span><span class="nb">print</span> <span class="nv">pkg</span> <span class="nv">buffer</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">save-buffer</span><span class="p">)</span>
      <span class="p">)</span>
    <span class="p">)</span>
  <span class="c1">;; Now install pkg</span>
  <span class="p">(</span><span class="nv">message</span> <span class="s">"Installing %s"</span> <span class="nv">pkg</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">sit-for</span> <span class="mi">1</span><span class="p">)</span>
  <span class="p">(</span><span class="nb">ignore-errors</span> <span class="p">(</span><span class="nv">package-install</span> <span class="nv">pkg</span><span class="p">))</span>
  <span class="p">)</span></code></pre></figure>

<h1 id="restart-emacs">Restart emacs</h1>

<p>I was wondering if I’d have to have a more complicated shell script to
keep starting Emacs, but Emacs has <code class="language-plaintext highlighter-rouge">restart-emacs</code> that
does the obvious.</p>

<figure class="highlight"><pre><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="p">(</span><span class="nv">message</span> <span class="s">"%s"</span> <span class="s">"Restarting emacs"</span><span class="p">)</span>
<span class="p">(</span><span class="nv">sit-for</span> <span class="mf">0.5</span><span class="p">)</span>
<span class="p">(</span><span class="nv">restart-emacs</span><span class="p">)</span></code></pre></figure>]]></content><author><name></name></author><category term="emacs" /><category term="test" /><summary type="html"><![CDATA[BUG: Emacs is losing the default font information from init.el when adding a68-mode or alda-mode. It’s difficult to figure out what exactly is happening. It seems related to having a large number of packages installed, but perhaps its just the right combination of combination of packages.]]></summary></entry><entry><title type="html">Posframe for everything</title><link href="https://davemq.github.io/2026/04/14/posframe-everything.html" rel="alternate" type="text/html" title="Posframe for everything" /><published>2026-04-14T00:00:00+00:00</published><updated>2026-04-14T00:00:00+00:00</updated><id>https://davemq.github.io/2026/04/14/posframe-everything</id><content type="html" xml:base="https://davemq.github.io/2026/04/14/posframe-everything.html"><![CDATA[<p>An Emacser recently posted about <a href="https://github.com/CsBigDataHub/popterm.el">popterm</a>, which can use posframe to
toggle a terminal visible and invisible in Emacs. I tried it out, and
ran into problems with it, so abandoned it for now.</p>

<p>However, this got me thinking about other things that can use
<a href="https://github.com/tumashu/posframe">posframe</a>, which pops up a frame at point. I’ve seen other Emacsers use
posframe when they show off their configurations in meetups. I thought
about what I use often that might benefit from a posframe.</p>

<ul>
  <li>magit</li>
  <li>vertico</li>
  <li>which-key</li>
  <li>company</li>
  <li>flymake</li>
</ul>

<p>Which of these has something I can use to enable posframes?</p>

<p>Of course, there are plenty of other packages that have add-on
packages to enable posframes.</p>

<h2 id="magit">Magit</h2>

<p>magit doesn’t have anything directly, but it makes heavy use of
<a href="https://github.com/magit/transient">transient</a>. And there’s a package <a href="https://github.com/yanghaoxie/transient-posframe">transient-posframe</a> that can enable
posframes for transients. When I use magit’s transients, the transient
pops up as a frame in the middle of my Emacs frame.</p>

<h2 id="vertico">vertico</h2>

<p>Install <a href="https://github.com/tumashu/vertico-posframe">vertico-posframe</a> to use posframes with vertico.</p>

<h2 id="which-key">which-key</h2>

<p>Yep, there’s <a href="https://github.com/emacsorphanage/which-key-posframe">which-key-posframe</a>.</p>

<h2 id="company">company</h2>

<p>See <a href="https://github.com/tumashu/company-posframe">company-posframe</a>.</p>

<h2 id="flymake">flymake</h2>

<p>I needed a bit of web searching to find this. <a href="https://codeberg.org/akib/emacs-flymake-popon">flymake-popon</a> can use a
posframe in the GUI and popon in a terminal.</p>]]></content><author><name></name></author><category term="emacs" /><category term="posframe" /><summary type="html"><![CDATA[An Emacser recently posted about popterm, which can use posframe to toggle a terminal visible and invisible in Emacs. I tried it out, and ran into problems with it, so abandoned it for now.]]></summary></entry><entry><title type="html">Calculating RAGBRAI training actual vs. planned mileage</title><link href="https://davemq.github.io/2026/02/24/ragbrai-training-actuals-vs-plan.html" rel="alternate" type="text/html" title="Calculating RAGBRAI training actual vs. planned mileage" /><published>2026-02-24T00:00:00+00:00</published><updated>2026-02-24T00:00:00+00:00</updated><id>https://davemq.github.io/2026/02/24/ragbrai-training-actuals-vs-plan</id><content type="html" xml:base="https://davemq.github.io/2026/02/24/ragbrai-training-actuals-vs-plan.html"><![CDATA[<p>I’m training to ride <a href="https://ragbrai.com/">RAGBRAI</a> LIII in Iowa in July 2026. RAGBRAI
provides a <a href="https://ragbrai.com/2026-training-plan/">training plan</a> which, if followed, helps riders get ready
for the ride.</p>

<p>Being an Emacs Org geek, I grabbed the provided <a href="https://ragbrai.com/wp-content/uploads/2026/02/RAGBRAI_Training_Plan_2026.xlsx">Excel spreadsheet,</a>
exported it as a CSV (comma separated values) file, then converted
that to an Org table.</p>

<p>The spreadsheet shows two lines per week, one with the planned
mileages, and another to record the actual miles ridden. There’s a
total mileage in column 6, which is filled in for the planned rides,
and you fill in yourself for the actuals.</p>

<p>The first few lines of the spreadsheet look like</p>

<figure class="highlight"><pre><code class="language-org" data-lang="org">|----------------------+-----------------------------+-------------------+------------------+------------------+------------|
|                      | 2026 RAGBRAI® Training Plan |                   |                  |                  |            |
|----------------------+-----------------------------+-------------------+------------------+------------------+------------|
| Week of:             | Weekday 1                   | Weekday 2         | Saturday         | Sunday           | Week Total |
|----------------------+-----------------------------+-------------------+------------------+------------------+------------|
| February 9           | 5 miles                     | 5 miles           | 10 miles         | -                | 20 miles   |
|----------------------+-----------------------------+-------------------+------------------+------------------+------------|
| Actual Ridden        |                             |                   |                  |                  |            |
|----------------------+-----------------------------+-------------------+------------------+------------------+------------|
| February 16          | 10 miles                    | 10 miles          | 10 miles         | -                | 30 miles   |
|----------------------+-----------------------------+-------------------+------------------+------------------+------------|
| Actual Ridden        |                             |                   |                  |                  |            |
|----------------------+-----------------------------+-------------------+------------------+------------------+------------|</code></pre></figure>

<p>I wanted to do two things:</p>

<ul>
  <li>make column 6 the sum of columns 2 through 5, which include two
weekday and two weekend rides</li>
  <li>create a column 7 that’s a percentage of the actual vs. planned
mileage</li>
</ul>

<p>Org tables let you specify formulas for cells, columns, and rows.
Getting the sum of columns 2 through 5 for column 6 can be done a
couple of ways:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">$6=$2+$3+$4+$5</code></li>
  <li><code class="language-plaintext highlighter-rouge">$6=vsum($2..$5)</code></li>
</ul>

<p>I used the former for quite a while, until I realized I could use the
latter. Either way, it works.</p>

<p>I had difficulty getting the formula for column 7 working how I wanted
it. Org tables can do formulas using Calc syntax, which is what I used
for column 6, or using Emacs Lisp forms as formulas. I used Calc
syntax to get this working. I wanted to calculate $7 as 100 times the
value of @0$6, the current row’s sum of miles ridden, divided by the
value of the previous row’s sum of planned miles. It’s easy enough to
just do something like <code class="language-plaintext highlighter-rouge">$7=100*@0$6/@-1$6</code>, but the values on
the planned rides rows were 100 times the planned miles for that week
divided by the actual miles for the previous week, which is usually
some number over 100. And not that interesting to me.</p>

<p>I also got rid of all instances of “miles” in the table. I ended up
with weird things like 70.3 miles / miles, and couldn’t figure out how
to get Org to simplify this. Getting rid of “miles” in the spreadsheet
made things look much better.</p>

<p>I wanted to key on the “Actual Ridden” string in $1. If $1 is “Actual
Ridden”, then use the calculation we’ve been discussing. Otherwise,
use 100 or show nothing. I had great difficulty making this work with
Calc syntax.</p>

<p>So, I tried Emacs Lisp forms as formulas. I ended up with</p>

<figure class="highlight"><pre><code class="language-org" data-lang="org">$7='(if (string-equal $1 "Actual Ridden") (format "%.2f" (* 100 (/ (string-to-number @0$6) (string-to-number @-1$6)))) "")</code></pre></figure>

<p>This is a pretty straightforward translation of the percentage
formula, along with using <code class="language-plaintext highlighter-rouge">format</code> to only show a couple
of decimal places. In the case of other lines, just return an empty
string. The final formula works great.</p>]]></content><author><name></name></author><category term="ragbrai" /><category term="bicycling" /><category term="orgmode" /><category term="emacs" /><category term="org-mode" /><category term="org" /><summary type="html"><![CDATA[I’m training to ride RAGBRAI LIII in Iowa in July 2026. RAGBRAI provides a training plan which, if followed, helps riders get ready for the ride.]]></summary></entry><entry><title type="html">A Rofi workspace switcher for Sway</title><link href="https://davemq.github.io/2026/02/24/rofi-workspace-switcher-sway.html" rel="alternate" type="text/html" title="A Rofi workspace switcher for Sway" /><published>2026-02-24T00:00:00+00:00</published><updated>2026-02-24T00:00:00+00:00</updated><id>https://davemq.github.io/2026/02/24/rofi-workspace-switcher-sway</id><content type="html" xml:base="https://davemq.github.io/2026/02/24/rofi-workspace-switcher-sway.html"><![CDATA[<p>I started out on a journey to try to get <code class="language-plaintext highlighter-rouge">sway</code> to create a new
workspace for me, with a “probably” unique name. How could I do that?</p>

<p><code class="language-plaintext highlighter-rouge">sway</code>, like it’s ancestor <code class="language-plaintext highlighter-rouge">i3</code>, has an IPC mechanism that
allows you to query and control <code class="language-plaintext highlighter-rouge">sway</code> from other programs. You
can play with this with <code class="language-plaintext highlighter-rouge">swaymsg</code>. <code class="language-plaintext highlighter-rouge">swaymsg "workspace foo"</code> will switch your current display to show workspace “foo”,
creating “foo” if it doesn’t exist. So one interesting way to achieve
my goal is</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">swaymsg <span class="s2">"workspace </span><span class="si">$(</span>xkcdpass <span class="nt">-n</span> 1<span class="si">)</span><span class="s2">"</span></code></pre></figure>

<p><code class="language-plaintext highlighter-rouge">xkcdpass -n 1</code> prints a single random word from
<code class="language-plaintext highlighter-rouge">xkcdpass</code>’ word file.</p>

<p>One problem with random words is that we don’t typically have nice key
bindings in <code class="language-plaintext highlighter-rouge">sway</code> to switch to these. You can use the mouse and
click on the workspace name on your bar, but if you’d prefer to only
use your keyboard, too bad.</p>

<p>So I thought to use <code class="language-plaintext highlighter-rouge">rofi</code> to show a list of workspaces and let
you select one. <code class="language-plaintext highlighter-rouge">rofi</code> has no builtin mode for this, but it does
have a way to add modes using scripts. Such a script has two modes
itself:</p>

<ul>
  <li>with no arguments, list the items in question. In this case, list
the workspaces</li>
  <li>with one argument, do something with that item. In our case, switch
to that workspace</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">rofi</code> allows you to type a new item, so in our case that would
be another way to switch to a new workspace.</p>

<p>So, how do you get the list of workspaces? Again, the <code class="language-plaintext highlighter-rouge">sway</code> IPC
mechanism. You can run <code class="language-plaintext highlighter-rouge">swaymsg -t get_workspaces</code> to get the
list of workspaces. By default this will pretty print the list of
workspaces. But if you specify <code class="language-plaintext highlighter-rouge">-r</code> or <code class="language-plaintext highlighter-rouge">--raw</code> or pipe
<code class="language-plaintext highlighter-rouge">swaymsg</code> to another program, it outputs JSON. Here’s an
example:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">[</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"workspace"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"orientation"</span><span class="p">:</span><span class="w"> </span><span class="s2">"horizontal"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"percent"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"urgent"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
    </span><span class="nl">"marks"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
    </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"splith"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"border"</span><span class="p">:</span><span class="w"> </span><span class="s2">"none"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"current_border_width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
    </span><span class="nl">"rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">30</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">2560</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">1410</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"deco_rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"window_rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"geometry"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"window"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"nodes"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
    </span><span class="nl">"floating_nodes"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
    </span><span class="nl">"focus"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="mi">17</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="nl">"fullscreen_mode"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"sticky"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
    </span><span class="nl">"floating"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"scratchpad_state"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"num"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"output"</span><span class="p">:</span><span class="w"> </span><span class="s2">"DP-9"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"representation"</span><span class="p">:</span><span class="w"> </span><span class="s2">"H[H[emacs]]"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"focused"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"visible"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">18</span><span class="p">,</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"workspace"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"orientation"</span><span class="p">:</span><span class="w"> </span><span class="s2">"horizontal"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"percent"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"urgent"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
    </span><span class="nl">"marks"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
    </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"splith"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"border"</span><span class="p">:</span><span class="w"> </span><span class="s2">"none"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"current_border_width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
    </span><span class="nl">"rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">2560</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">30</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">1920</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">1170</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"deco_rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"window_rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"geometry"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"window"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"nodes"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
    </span><span class="nl">"floating_nodes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9</span><span class="p">,</span><span class="w">
        </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"floating_con"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"orientation"</span><span class="p">:</span><span class="w"> </span><span class="s2">"none"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"percent"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.61330662393162383</span><span class="p">,</span><span class="w">
        </span><span class="nl">"urgent"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"marks"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"focused"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"none"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"border"</span><span class="p">:</span><span class="w"> </span><span class="s2">"normal"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"current_border_width"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
        </span><span class="nl">"rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">2878</span><span class="p">,</span><span class="w">
          </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">97</span><span class="p">,</span><span class="w">
          </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">1284</span><span class="p">,</span><span class="w">
          </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">1046</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"deco_rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">318</span><span class="p">,</span><span class="w">
          </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">40</span><span class="p">,</span><span class="w">
          </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">1284</span><span class="p">,</span><span class="w">
          </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">27</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"window_rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
          </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
          </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">1280</span><span class="p">,</span><span class="w">
          </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">1044</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"geometry"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
          </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
          </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">696</span><span class="p">,</span><span class="w">
          </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">486</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"foot"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"window"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
        </span><span class="nl">"nodes"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"floating_nodes"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"focus"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"fullscreen_mode"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sticky"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"floating"</span><span class="p">:</span><span class="w"> </span><span class="s2">"user_on"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"scratchpad_state"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fresh"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="mi">5998</span><span class="p">,</span><span class="w">
        </span><span class="nl">"app_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"foot"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"foreign_toplevel_identifier"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0483dba85d6ad4c7b88b28653765ab03"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"visible"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
        </span><span class="nl">"max_render_time"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
        </span><span class="nl">"allow_tearing"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"shell"</span><span class="p">:</span><span class="w"> </span><span class="s2">"xdg_shell"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"inhibit_idle"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sandbox_engine"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sandbox_app_id"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sandbox_instance_id"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
        </span><span class="nl">"idle_inhibitors"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"none"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"application"</span><span class="p">:</span><span class="w"> </span><span class="s2">"none"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="nl">"focus"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="mi">12</span><span class="p">,</span><span class="w">
      </span><span class="mi">9</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="nl">"fullscreen_mode"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"sticky"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
    </span><span class="nl">"floating"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"scratchpad_state"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"num"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
    </span><span class="nl">"output"</span><span class="p">:</span><span class="w"> </span><span class="s2">"DP-8"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"representation"</span><span class="p">:</span><span class="w"> </span><span class="s2">"H[google-chrome]"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"focused"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
    </span><span class="nl">"visible"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">21</span><span class="p">,</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"workspace"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"orientation"</span><span class="p">:</span><span class="w"> </span><span class="s2">"horizontal"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"percent"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"urgent"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
    </span><span class="nl">"marks"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
    </span><span class="nl">"layout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"splith"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"border"</span><span class="p">:</span><span class="w"> </span><span class="s2">"none"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"current_border_width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
    </span><span class="nl">"rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">2560</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">30</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">1920</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">1170</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"deco_rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"window_rect"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"geometry"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"width"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
      </span><span class="nl">"height"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"4"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"window"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"nodes"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
    </span><span class="nl">"floating_nodes"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
    </span><span class="nl">"focus"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="mi">24</span><span class="p">,</span><span class="w">
      </span><span class="mi">25</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="nl">"fullscreen_mode"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"sticky"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
    </span><span class="nl">"floating"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"scratchpad_state"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
    </span><span class="nl">"num"</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w">
    </span><span class="nl">"output"</span><span class="p">:</span><span class="w"> </span><span class="s2">"DP-8"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"representation"</span><span class="p">:</span><span class="w"> </span><span class="s2">"H[V[foot] V[foot]]"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"focused"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
    </span><span class="nl">"visible"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">]</span></code></pre></figure>

<p>Phew, that’s a lot for 3 workspaces! The only part I care about in
this case is the “name” for each. We could do some weird stuff with
<code class="language-plaintext highlighter-rouge">grep</code> and <code class="language-plaintext highlighter-rouge">sed</code> to get the names, or use <code class="language-plaintext highlighter-rouge">jq</code> to
extract what we want.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">swaymsg <span class="nt">-t</span> get_workspaces | jq <span class="s1">'.[] | .name'</span></code></pre></figure>

<p>generates</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"1"
"2"
"4"
</code></pre></div></div>

<p>Perfect!</p>

<p>Putting this all together, here’s a shell script
<code class="language-plaintext highlighter-rouge">sway_list_workspaces</code> that we can use with <code class="language-plaintext highlighter-rouge">rofi</code> to
switch workspaces without using the mouse.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/sh</span>

<span class="k">if</span> <span class="o">[</span> x<span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> x <span class="o">]</span>
<span class="k">then
    </span>swaymsg <span class="nt">-t</span> get_workspaces | jq <span class="s1">'.[] | .name'</span>
<span class="k">else
    </span>swaymsg <span class="s2">"workspace </span><span class="nv">$1</span><span class="s2">"</span> <span class="o">&gt;</span>/dev/null
<span class="k">fi</span></code></pre></figure>

<p>Here’s the <code class="language-plaintext highlighter-rouge">rofi</code> command line to use:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">rofi <span class="nt">-modes</span> <span class="s1">'Workspaces:/home/davemarq/bin/sway_list_workspaces'</span> <span class="nt">-show</span> Workspaces</code></pre></figure>

<p>I bind it to “$mod+Shift+w” in my sway config file:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bindsym $mod+Shift+w exec "rofi -modes 'Workspaces:/home/davemarq/bin/sway_list_workspaces' -show Workspaces"
</code></pre></div></div>]]></content><author><name></name></author><category term="sway" /><category term="wayland" /><summary type="html"><![CDATA[I started out on a journey to try to get sway to create a new workspace for me, with a “probably” unique name. How could I do that?]]></summary></entry><entry><title type="html">Tmux and the mouse</title><link href="https://davemq.github.io/2026/01/13/tmux-mouse.html" rel="alternate" type="text/html" title="Tmux and the mouse" /><published>2026-01-13T00:00:00+00:00</published><updated>2026-01-13T00:00:00+00:00</updated><id>https://davemq.github.io/2026/01/13/tmux-mouse</id><content type="html" xml:base="https://davemq.github.io/2026/01/13/tmux-mouse.html"><![CDATA[<p>I use <code class="language-plaintext highlighter-rouge">tmux</code> for just a few things, particular when
connecting to a remote system. But otherwise, I don’t use it for local
terminals, as I can just open up another terminal in Wayland.</p>

<p>I have a Kensington trackball with a big scroll wheel. I’ve gotten
very used to using that scroll wheel for scrolling in terminals,
Emacs, my web browser, etc. But one place it didn’t work was in
<code class="language-plaintext highlighter-rouge">tmux</code>.</p>

<p>But today I finally went searching around for how to turn on mouse
support. Something I read prompted me to think “Oh yeah, how do I do
that?” It turns out <code class="language-plaintext highlighter-rouge">tmux</code> does support the mouse, but not by
default. So you have to enable the support in your tmux configuration
file. I added</p>

<figure class="highlight"><pre><code class="language-ini" data-lang="ini"><span class="err">set</span> <span class="err">-g</span> <span class="err">mouse</span> <span class="err">on</span></code></pre></figure>

<p>to $HOME/.config/tmux/tmux.conf, restarted tmux, and boom, I have
mouse wheel scrolling like most other applications I use.</p>]]></content><author><name></name></author><category term="tmux" /><category term="linux" /><category term="unix" /><summary type="html"><![CDATA[I use tmux for just a few things, particular when connecting to a remote system. But otherwise, I don’t use it for local terminals, as I can just open up another terminal in Wayland.]]></summary></entry><entry><title type="html">Keeping appointments from my Org agenda updated</title><link href="https://davemq.github.io/2026/01/07/org-agenda-to-appt-timer.html" rel="alternate" type="text/html" title="Keeping appointments from my Org agenda updated" /><published>2026-01-07T00:00:00+00:00</published><updated>2026-01-07T00:00:00+00:00</updated><id>https://davemq.github.io/2026/01/07/org-agenda-to-appt-timer</id><content type="html" xml:base="https://davemq.github.io/2026/01/07/org-agenda-to-appt-timer.html"><![CDATA[<p>Among many other things, Emacs’ Org mode manages tasks and allows you
to easily see an agenda of tasks that are scheduled or approaching a
deadline. Emacs also has an appointments system that shows scheduled
appointments at various times ahead of time. Org provides
<code class="language-plaintext highlighter-rouge">org-agenda-to-appt</code> to create appointments from your
agenda items.</p>

<p>So running <code class="language-plaintext highlighter-rouge">org-agenda-to-appt</code> once during Emacs
startup is great, but it has limitations:</p>

<ul>
  <li>it only creates appointments from today’s agenda items</li>
  <li>it has no clue when you’ve added an appointment to one of your
agenda files</li>
</ul>

<p>Assuming you run your Emacs for longer than a day, or that you add
appointments, this is a problem!</p>

<p>My solution is to run <code class="language-plaintext highlighter-rouge">org-agenda-to-appt</code> with
<code class="language-plaintext highlighter-rouge">run-at-time</code> with a repeater of 3600 seconds, i.e. 1
hour, like this, at Emacs start:</p>

<figure class="highlight"><pre><code class="language-emacs-lisp" data-lang="emacs-lisp"><span class="p">(</span><span class="nv">run-at-time</span> <span class="mi">0</span> <span class="mi">3600</span> <span class="ss">'org-agenda-to-appt</span><span class="p">)</span></code></pre></figure>

<p>Time 0 means “right now”, and the repeater is 3600 seconds. After
adding a new task to an agenda file, it shows up as an appointment
within an hour. This may not work if you add an appointment that’s due
in the next hour, as the timer that adds the apointment may run after
the appointment time.</p>]]></content><author><name></name></author><category term="emacs" /><category term="org" /><summary type="html"><![CDATA[Among many other things, Emacs’ Org mode manages tasks and allows you to easily see an agenda of tasks that are scheduled or approaching a deadline. Emacs also has an appointments system that shows scheduled appointments at various times ahead of time. Org provides org-agenda-to-appt to create appointments from your agenda items.]]></summary></entry><entry><title type="html">Creating weekly diary-style timestamps in Org Mode</title><link href="https://davemq.github.io/2026/01/06/org-diary-every-monday.html" rel="alternate" type="text/html" title="Creating weekly diary-style timestamps in Org Mode" /><published>2026-01-06T00:00:00+00:00</published><updated>2026-01-06T00:00:00+00:00</updated><id>https://davemq.github.io/2026/01/06/org-diary-every-monday</id><content type="html" xml:base="https://davemq.github.io/2026/01/06/org-diary-every-monday.html"><![CDATA[<p>With the beginning of the new year, I received meeting invitations for
a twice weekly meeting, on Mondays and Thursdays. I added this to my
Org agenda file, and used a diary-style expression. I <em>thought</em> I
could use <code class="language-plaintext highlighter-rouge">diary-float</code> for every Monday and another for
every Thursday, like this</p>

<figure class="highlight"><pre><code class="language-org" data-lang="org">* Recurring meeting
&lt;%%(diary-float t 1 0) 11:30-12:00&gt;
&lt;%%(diary-float t 4 0) 11:30-12:00&gt;</code></pre></figure>

<p>But when I looked my agenda, I didn’t see anything for Mondays or
Thursdays at 11:30-12:00. Hmm.</p>

<p>The help for <code class="language-plaintext highlighter-rouge">diary-float</code> only specifies how the third
argument, N, applies if it is positive or negative. It would be
nice if 0, or some other value like t, meant every week.</p>

<p>I eventually settled on</p>

<figure class="highlight"><pre><code class="language-org" data-lang="org">* Recurring meeting
&lt;%%(diary-cyclic 7 1 5 2026) 11:30-12:00&gt;
&lt;%%(diary-cyclic 7 1 8 2026) 11:30-12:00&gt;</code></pre></figure>

<p>for the meeting. It works, but it wasn’t obvious to me that this was
the way to go.</p>

<p>I could also have put 5 entries for Mondays and 5 for Thursdays, to
get all five possible occurences of each in a month. Seems kind of
weird, so I didn’t.</p>

<p>And finally, I could just use a regular +1w repeater, but then I need
to have two separate TODOs in my agenda file, one for Mondays and one
for Thursdays.</p>]]></content><author><name></name></author><category term="emacs" /><category term="org" /><summary type="html"><![CDATA[With the beginning of the new year, I received meeting invitations for a twice weekly meeting, on Mondays and Thursdays. I added this to my Org agenda file, and used a diary-style expression. I thought I could use diary-float for every Monday and another for every Thursday, like this]]></summary></entry><entry><title type="html">Possible RAGBRAI routes via Python</title><link href="https://davemq.github.io/2025/12/24/ragbrai-routes.html" rel="alternate" type="text/html" title="Possible RAGBRAI routes via Python" /><published>2025-12-24T00:00:00+00:00</published><updated>2025-12-24T00:00:00+00:00</updated><id>https://davemq.github.io/2025/12/24/ragbrai-routes</id><content type="html" xml:base="https://davemq.github.io/2025/12/24/ragbrai-routes.html"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>I’ve signed up to ride RAGBRAI LIII in July 2026. This is my first
time riding RAGBRAI, and being a geek, I was curious about possible
routes. So, using the first 52 routes, some Python, and the Monte
Carlo method, I wrote a little program to figure out the possible
routes and the likelihood of each route.</p>

<p>There’s a lot to unpack in that first paragraph. So, here goes:</p>

<ul>
  <li>RAGBRAI: This originally meant Register’s Annual Great Bike Ride
Across Iowa. I don’t think this acronym is used these days. But the
name stuck.</li>
  <li>Monte Carlo method: a numerical method that relies on repeated
random sampling to obtain results. So this does simulations with
random number generation to figure out routes.</li>
</ul>

<h1 id="what-do-we-know">What Do We Know?</h1>

<p>The first 52 RAGBRAI routes are published in various places. I grabbed
the routes from this <a href="https://en.wikipedia.org/wiki/List_of_RAGBRAI_overnight_stops#By_year">Wikipedia</a> page, and converted them to a CSV file
using Emacs Org mode and a little hand editing.</p>

<h1 id="now-what">Now what?</h1>

<p>From the CSV file, I can create adjacency lists, which I represent in
Python as a dictionary of unordered lists that describe the neighbors
for each node. In this case, this is representing the overnight towns
and the next or previous town on the route.</p>

<p>I also save a list of starting and ending towns, so I know where
routes have started or ended in the past.</p>

<p>51 of the 52 routes have 8 nodes, as RAGBRAI runs from Sunday to
Saturday. But the first RAGBRAI route only has 7 nodes, so I needed to
take that into account. This was just a little extra code.</p>

<p>Once I’ve got the adjacency lists, I can run the Monte Carlo
simulations. I initially implemented this beginning with a random
starting town. For each town, I picked a random link to the next town.
If there’s a town that next visits the same town multiple times, then
that next visited town will be in the adjacency list multiple times.</p>

<p>But I noticed there were many routes created that didn’t end in one of
the ending towns. Eliminating those, the method was heavily biased
towards only starting a couple of towns. Hmmm.</p>

<p>So I tried started with the end towns and moving backwards. This gave
a much better spread of starting and ending towns in the simulation.
So I stuck with building routes from the end town backwards to the
start.</p>

<h1 id="code">Code</h1>

<p>First there are some <code class="language-plaintext highlighter-rouge">imports</code>:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
<span class="kn">import</span> <span class="nn">csv</span>
<span class="kn">import</span> <span class="nn">graphviz</span>
<span class="kn">from</span> <span class="nn">operator</span> <span class="kn">import</span> <span class="n">itemgetter</span>
<span class="kn">import</span> <span class="nn">random</span></code></pre></figure>

<p>Next I set up a tuple of days I can use with the CSV reader to grab
fields from each line of the CSV file.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">days</span> <span class="o">=</span> <span class="p">(</span>
    <span class="s">"Starting City"</span><span class="p">,</span>
    <span class="s">"Sunday"</span><span class="p">,</span>
    <span class="s">"Monday"</span><span class="p">,</span>
    <span class="s">"Tuesday"</span><span class="p">,</span>
    <span class="s">"Wednesday"</span><span class="p">,</span>
    <span class="s">"Thursday"</span><span class="p">,</span>
    <span class="s">"Friday"</span><span class="p">,</span>
    <span class="s">"Saturday"</span><span class="p">,</span>
<span class="p">)</span></code></pre></figure>

<p>I also create empty lists for the starting towns and ending towns, and
create the adjacencies dictionary using <code class="language-plaintext highlighter-rouge">defaultdict</code> so
each dictionary value will be set up as a list when it is first
accessed.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">starts</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">ends</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">adjacencies</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span></code></pre></figure>

<h2 id="parsing-the-csv-file-into-adjacency-lists">Parsing the CSV file into adjacency lists</h2>

<p>This function parses the CSV file a couple of times using the Python
“csv” package to read the CSV file as a dictionary. The first time
through is to find the starting and ending towns.</p>

<p>The second time reading the CSV file is to create the adjacency lists.
I start with the days, reversed, to go from the end of a route to the
beginning.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">parse</span><span class="p">():</span>
    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span>
        <span class="s">"/home/davemarq/shell-scripts/ragbrai-by-year.csv"</span><span class="p">,</span> <span class="n">newline</span><span class="o">=</span><span class="s">""</span>
    <span class="p">)</span> <span class="k">as</span> <span class="n">csvfile</span><span class="p">:</span>
        <span class="n">reader</span> <span class="o">=</span> <span class="n">csv</span><span class="p">.</span><span class="n">DictReader</span><span class="p">(</span><span class="n">csvfile</span><span class="p">,</span> <span class="n">delimiter</span><span class="o">=</span><span class="s">","</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">reader</span><span class="p">:</span>
            <span class="n">starts</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="s">"Starting City"</span><span class="p">])</span>
            <span class="k">if</span> <span class="n">row</span><span class="p">[</span><span class="s">"Saturday"</span><span class="p">]</span> <span class="o">!=</span> <span class="s">"N/A"</span><span class="p">:</span>
                <span class="n">ends</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="s">"Saturday"</span><span class="p">])</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">ends</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="s">"Friday"</span><span class="p">])</span>

    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span>
        <span class="s">"/home/davemarq/shell-scripts/ragbrai-by-year.csv"</span><span class="p">,</span> <span class="n">newline</span><span class="o">=</span><span class="s">""</span>
    <span class="p">)</span> <span class="k">as</span> <span class="n">csvfile</span><span class="p">:</span>
        <span class="n">reader</span> <span class="o">=</span> <span class="n">csv</span><span class="p">.</span><span class="n">DictReader</span><span class="p">(</span><span class="n">csvfile</span><span class="p">,</span> <span class="n">delimiter</span><span class="o">=</span><span class="s">","</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">reader</span><span class="p">:</span>
            <span class="n">prev</span> <span class="o">=</span> <span class="bp">None</span>
            <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="n">days</span><span class="p">):</span>
                <span class="k">if</span> <span class="n">prev</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
                    <span class="n">prev</span> <span class="o">=</span> <span class="n">d</span>
                <span class="k">elif</span> <span class="n">row</span><span class="p">[</span><span class="n">prev</span><span class="p">]</span> <span class="o">!=</span> <span class="s">"N/A"</span><span class="p">:</span>
                    <span class="n">adjacencies</span><span class="p">[</span><span class="n">row</span><span class="p">[</span><span class="n">prev</span><span class="p">]].</span><span class="n">append</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="n">d</span><span class="p">])</span>
                <span class="n">prev</span> <span class="o">=</span> <span class="n">d</span></code></pre></figure>

<h2 id="making-routes">Making routes</h2>

<p>Once I have the adjacency lists, I want to create random routes. A
route starts with a random end town. For each town, I pick one of its
neighbors based on its adjacency list, using a random number
generator. I append the random choice to the route list. Once I have a
full route, that goes from end to start, I return its reverse.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">makeroute</span><span class="p">():</span>
    <span class="n">route</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">cur</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">choice</span><span class="p">(</span><span class="n">ends</span><span class="p">)</span>
    <span class="n">route</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">cur</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">7</span><span class="p">):</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="nb">next</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">choice</span><span class="p">(</span><span class="n">adjacencies</span><span class="p">[</span><span class="n">cur</span><span class="p">])</span>
        <span class="k">except</span> <span class="nb">IndexError</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">None</span>
        <span class="n">route</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">next</span><span class="p">)</span>
        <span class="n">cur</span> <span class="o">=</span> <span class="nb">next</span>
    <span class="k">if</span> <span class="n">cur</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">starts</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">None</span>

    <span class="k">return</span> <span class="nb">reversed</span><span class="p">(</span><span class="n">route</span><span class="p">)</span></code></pre></figure>

<h2 id="making-a-graph">Making a graph</h2>

<p>I also thought it would be nice to get a graph of all the towns linked
to each other from existing routes. This gives a nice visual
representation showing how often the routes go from town to town. For
example, routes from Sioux City visited Storm Lake next 6 times, so
there will be 6 arrows from Sioux City to Storm Lake.</p>

<p>I inially created this code manually to create a Graphviz directed
graph, or digraph. But I thought, there’s gotta be a Python package
for this. Sure enough, there is.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">make_graph</span><span class="p">():</span>
    <span class="n">dot</span> <span class="o">=</span> <span class="n">graphviz</span><span class="p">.</span><span class="n">Digraph</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">'ragbrai'</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="s">'png'</span><span class="p">,</span> <span class="n">engine</span><span class="o">=</span><span class="s">'dot'</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">a</span> <span class="ow">in</span> <span class="n">adjacencies</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">adjacencies</span><span class="p">[</span><span class="n">a</span><span class="p">]:</span>
            <span class="n">dot</span><span class="p">.</span><span class="n">edge</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">a</span><span class="p">)</span>
    <span class="n">dot</span> <span class="o">=</span> <span class="n">dot</span><span class="p">.</span><span class="n">unflatten</span><span class="p">()</span>
    <span class="n">dot</span><span class="p">.</span><span class="n">render</span><span class="p">(</span><span class="n">view</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span></code></pre></figure>

<p>Here’s the graph based on the 52 existing routes.</p>

<p><img src="/images/ragbrai.gv.png" alt="img" /></p>

<h2 id="do-the-work">Do the work!</h2>

<p>Okay, now the main code starts. It parses, then creates the graph.
Finally, it does the Monte Carlo method.</p>

<p>The Monte Carlo method here creates routes 1,000,000 times. It tracks
how many times each route is created through a dictionary. Once
created, We get told how many unique routes were created and then the
routes are sorted by count and printed, along with the count for each.
Typically the method creates a little over 7000 unique routes. The
results are pretty consistent, with a route from Sioux City to Clinton
getting a bit over 10000 hits, i.e. about 1% of the possibilities.
It’s also fun to look at the bottom of the list for those “1 in a
million” routes.</p>

<p>Here’s the first fiew lines of typical output:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Opening in existing browser session.
Created 7230 unique routes
('Sioux City', 'Storm Lake', 'Fort Dodge', 'Iowa Falls', 'Vinton', 'Mount Vernon', 'Maquoketa', 'Clinton') 10433
('Glenwood', 'Shenandoah', 'Creston', 'Adel', 'Pella', 'Ottumwa', 'Mount Pleasant', 'Burlington') 6969
('Rock Rapids', 'Spencer', 'Algona', 'Clear Lake', 'New Hampton', 'Decorah', 'Manchester', 'Dubuque') 6153
('Glenwood', 'Shenandoah', 'Creston', 'Adel', 'Pella', 'Ottumwa', 'Washington', 'Muscatine') 5483
('Glenwood', 'Shenandoah', 'Creston', 'Adel', 'Pella', 'Ottumwa', 'Mount Pleasant', 'Fort Madison') 5261
</code></pre></div></div>

<p>And here’s the final bits of code.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">parse</span><span class="p">()</span>
<span class="n">make_graph</span><span class="p">()</span>

<span class="n">routes</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">int</span><span class="p">)</span>

<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1000000</span><span class="p">):</span>
    <span class="n">r</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">while</span> <span class="n">r</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">r</span> <span class="o">=</span> <span class="n">makeroute</span><span class="p">()</span>
    <span class="n">routes</span><span class="p">[</span><span class="nb">tuple</span><span class="p">(</span><span class="n">r</span><span class="p">)]</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Created </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">routes</span><span class="p">)</span><span class="si">}</span><span class="s"> unique routes"</span><span class="p">)</span>

<span class="n">sortedroutes</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">sorted</span><span class="p">(</span><span class="n">routes</span><span class="p">.</span><span class="n">items</span><span class="p">(),</span> <span class="n">key</span><span class="o">=</span><span class="n">itemgetter</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">reverse</span><span class="o">=</span><span class="bp">True</span><span class="p">))</span>
<span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">sortedroutes</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">sortedroutes</span><span class="p">[</span><span class="n">r</span><span class="p">])</span></code></pre></figure>]]></content><author><name></name></author><category term="python" /><category term="bicycling" /><summary type="html"><![CDATA[Introduction]]></summary></entry></feed>