
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
 <channel>
   <title>Kenneth Jenkins</title>
   <link>https://kennethjenkins.net/</link>
   <description>Recent content on Kenneth Jenkins</description>
   <generator>Hugo -- gohugo.io</generator>
   <language>en-us</language>
   <copyright>©2020–2023 Kenneth Jenkins</copyright>
   <lastBuildDate>Tue, 18 Apr 2023 10:30:00 -0700</lastBuildDate>
   
       <atom:link href="https://kennethjenkins.net/index.xml" rel="self" type="application/rss+xml" />
   
   
     <item>
       <title>Android APK patching</title>
       <link>https://kennethjenkins.net/posts/apk-patching/</link>
       <pubDate>Tue, 18 Apr 2023 10:30:00 -0700</pubDate>
       
       <guid>https://kennethjenkins.net/posts/apk-patching/</guid>
       <description>&lt;p&gt;&lt;img src=&#34;https://kennethjenkins.net/img/GMM-7.0-icon.png&#34; alt=&#34;Android Google Maps 7.0 launcher icon&#34;&gt;&lt;/p&gt;
&lt;p&gt;Recently I&amp;rsquo;ve been job hunting, and so I&amp;rsquo;ve been reflecting on my past work
experience. One position I applied to had a required &amp;ldquo;portfolio&amp;rdquo; link, and at
first I thought that must have been a mistake in the application form. But upon
further reflection, I thought, for a front-end engineer, why not? This got me
wondering if I could dig up screenshots/recordings of any of the features I&amp;rsquo;ve
worked on in previous roles.&lt;/p&gt;
&lt;p&gt;Specifically, I was thinking about my time working on the Android Google Maps
app. During my time on the team, we went through not one but two major app
redesigns, so a lot of the UI I had worked on back then has since been
completely replaced.&lt;/p&gt;
&lt;h2 id=&#34;archived-builds&#34;&gt;Archived builds&lt;/h2&gt;
&lt;p&gt;Fortunately, &lt;a href=&#34;https://www.apkmirror.com/&#34;&gt;APKMirror&lt;/a&gt; has tons of archived
Google Maps APKs. Their coverage gets pretty spotty before 2014, but still
includes a decent selection. Most relevant for my nostalgia, they have a build
of Google Maps 7.0. This was the major redesign that was underway when I first
joined the team.&lt;/p&gt;
&lt;p&gt;I wasn&amp;rsquo;t sure if these old builds would work with modern Android device images,
but I was glad to see that Android Studio still provides emulator system images
for devices from around the same era.&lt;/p&gt;
&lt;p&gt;After I had downloaded Android Studio, fired up Device Manager, selected what
I thought to be an appropriately-ancient Pixel 2 device image with Android 5.0
(Lollipop), installed &lt;a href=&#34;https://developer.android.com/tools/adb&#34;&gt;&lt;code&gt;adb&lt;/code&gt;&lt;/a&gt;, and
then finally installed the old Google Maps APK, I was greeted with this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://kennethjenkins.net/img/GMM-update.png#center&#34; alt=&#34;&amp;ldquo;Update Google Maps to continue: This app is out of date.&amp;rdquo;&#34;&gt;&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;oh!&lt;/p&gt;
&lt;p&gt;I had completely forgotten about the server-controlled kill switch built in to
the app. The Google Maps team put a strong emphasis on backwards-compatibility,
but also realized there would be cases where we might need to force users to
stop using a particular released version.&lt;/p&gt;
&lt;p&gt;Is there any way to bypass this update prompt?&lt;/p&gt;
&lt;h2 id=&#34;apktool-to-the-rescue&#34;&gt;&lt;code&gt;apktool&lt;/code&gt; to the rescue&lt;/h2&gt;
&lt;p&gt;We can disassemble the archived Google Maps APKs using the open-source
&lt;a href=&#34;https://ibotpeaches.github.io/Apktool/&#34;&gt;&lt;code&gt;apktool&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell-session&#34; data-lang=&#34;shell-session&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ apktool d -r com.google.android.apps.maps_7.0.2-700022123_minAPI18&lt;span style=&#34;color:#ae81ff&#34;&gt;\(&lt;/span&gt;320dpi&lt;span style=&#34;color:#ae81ff&#34;&gt;\)&lt;/span&gt;_apkmirror.com.apk -o maps-7.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Using Apktool 2.7.0 on com.google.android.apps.maps_7.0.2-700022123_minAPI18(320dpi)_apkmirror.com.apk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Copying raw resources...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Baksmaling classes.dex...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Copying assets and libs...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Copying unknown files...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Copying original files...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(The &lt;code&gt;-r&lt;/code&gt; option avoids decoding resources, as &lt;code&gt;apktool&lt;/code&gt; seemed to have some
issues with the resources.)&lt;/p&gt;
&lt;p&gt;The resulting output includes a folder named &amp;ldquo;smali&amp;rdquo; containing all of the
disassembled Java class bytecode. Many of the class names have been obfuscated,
but we&amp;rsquo;re in luck: there&amp;rsquo;s a class named &lt;code&gt;KillSwitchFragment&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can see one of its methods finds and initializes a WebView, so it certainly
looks like we&amp;rsquo;re on the right track:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-smali&#34; data-lang=&#34;smali&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;.method&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;()&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;android/view/&lt;span style=&#34;color:#a6e22e&#34;&gt;View&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.locals&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.prologue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.line&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;83&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sget v0, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;;-&amp;gt;dv:&lt;span style=&#34;color:#66d9ef&#34;&gt;I&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    const/4 v1, &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-virtual {p0, v0, v1}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/terms/&lt;span style=&#34;color:#a6e22e&#34;&gt;KillSwitchFragment&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;IL&lt;/span&gt;android/view/&lt;span style=&#34;color:#a6e22e&#34;&gt;ViewGroup&lt;/span&gt;;)&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;android/view/&lt;span style=&#34;color:#a6e22e&#34;&gt;View&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    move-result-object v0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.line&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;84&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sget v1, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/&lt;span style=&#34;color:#a6e22e&#34;&gt;g&lt;/span&gt;;-&amp;gt;gN:&lt;span style=&#34;color:#66d9ef&#34;&gt;I&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-virtual {v0, v1}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;android/view/&lt;span style=&#34;color:#a6e22e&#34;&gt;View&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;findViewById&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;I&lt;/span&gt;)&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;android/view/&lt;span style=&#34;color:#a6e22e&#34;&gt;View&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    move-result-object v0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    check-cast v0, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;android/webkit/&lt;span style=&#34;color:#a6e22e&#34;&gt;WebView&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.line&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;86&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new-instance v1, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/terms/&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-direct {v1, p0}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/terms/&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;lt;init&amp;gt;&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/terms/&lt;span style=&#34;color:#a6e22e&#34;&gt;KillSwitchFragment&lt;/span&gt;;)&lt;span style=&#34;color:#66d9ef&#34;&gt;V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-virtual {v0, v1}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;android/webkit/&lt;span style=&#34;color:#a6e22e&#34;&gt;WebView&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;setWebViewClient&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;android/webkit/&lt;span style=&#34;color:#a6e22e&#34;&gt;WebViewClient&lt;/span&gt;;)&lt;span style=&#34;color:#66d9ef&#34;&gt;V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.line&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;98&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    iget-object v1, p0, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/terms/&lt;span style=&#34;color:#a6e22e&#34;&gt;KillSwitchFragment&lt;/span&gt;;-&amp;gt;b:&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;java/lang/&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-virtual {v0, v1}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;android/webkit/&lt;span style=&#34;color:#a6e22e&#34;&gt;WebView&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;loadUrl&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;java/lang/&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;;)&lt;span style=&#34;color:#66d9ef&#34;&gt;V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.line&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return-object v0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;.end&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;method&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This isn&amp;rsquo;t the easiest to read. A quick primer to help make sense of it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Method parameter values are referred to as &lt;code&gt;p0&lt;/code&gt;, &lt;code&gt;p1&lt;/code&gt;, &lt;code&gt;p2&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;For an instance method, parameter &lt;code&gt;p0&lt;/code&gt; is the &lt;code&gt;this&lt;/code&gt; reference.&lt;/li&gt;
&lt;li&gt;Local variables are referred to as &lt;code&gt;v0&lt;/code&gt;, &lt;code&gt;v1&lt;/code&gt;, &lt;code&gt;v2&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Java classes are referenced with an &lt;code&gt;L&lt;/code&gt;, followed by their fully-qualified
class name (with all &lt;code&gt;.&lt;/code&gt; characters replaced by &lt;code&gt;/&lt;/code&gt; characters), followed by
a &lt;code&gt;;&lt;/code&gt; (e.g. &lt;code&gt;android.webkit.WebView&lt;/code&gt; becomes &lt;code&gt;Landroid/webkit/WebView;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Other types include &lt;code&gt;I&lt;/code&gt; (int), &lt;code&gt;Z&lt;/code&gt; (boolean), and &lt;code&gt;V&lt;/code&gt; (void).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;invoke-&lt;/code&gt; mnemonics represent a method call.&lt;/li&gt;
&lt;li&gt;Method references consist of the class name followed by &lt;code&gt;-&amp;gt;&lt;/code&gt;, the method
name, the types of the method parameters (in parentheses), and the method
return type.&lt;/li&gt;
&lt;li&gt;Constructors are referred to like a void method with the name &lt;code&gt;&amp;lt;init&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So this method may have looked something like this in the source code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; View &lt;span style=&#34;color:#a6e22e&#34;&gt;createView&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    View view &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;inflateLayout&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;some_layout_id&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    WebView webView &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;WebView&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; view&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;findViewById&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;some_view_id&lt;span style=&#34;color:#f92672&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    WebViewClient client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; KillSwitchWebViewClient&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    webView&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;setWebViewClient&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;client&lt;span style=&#34;color:#f92672&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    webView&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;loadUrl&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; webView&lt;span style=&#34;color:#f92672&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(I&amp;rsquo;m guessing at some of the method names based on the signatures.)&lt;/p&gt;
&lt;p&gt;We can search through all the disassembled classes to look for references
to &lt;code&gt;KillSwitchFragment&lt;/code&gt;, and after tracing a few method call chains, I came
to this line in a class named &lt;code&gt;GmmActivity&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-smali&#34; data-lang=&#34;smali&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-static {p0, v0}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/terms/&lt;span style=&#34;color:#a6e22e&#34;&gt;KillSwitchFragment&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;GmmActivity&lt;/span&gt;;&lt;span style=&#34;color:#66d9ef&#34;&gt;Z&lt;/span&gt;)&lt;span style=&#34;color:#66d9ef&#34;&gt;V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s just comment that line out with a &lt;code&gt;#&lt;/code&gt; and see what happens&amp;hellip;.&lt;/p&gt;
&lt;h2 id=&#34;building-a-patched-apk&#34;&gt;Building a patched APK&lt;/h2&gt;
&lt;p&gt;We can reassemble an APK by running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell-session&#34; data-lang=&#34;shell-session&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ apktool b maps-7.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Using Apktool 2.7.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Checking whether sources has changed...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Smaling smali folder into classes.dex...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Checking whether resources has changed...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Copying raw resources...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Building apk file...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Copying unknown files/dir...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I: Built apk into: maps-7.0/dist/com.google.android.apps.maps_7.0.2-700022123_minAPI18(320dpi)_apkmirror.com.apk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However in order to install to a device (or emulator), we need to sign the APK.
We can generate a dummy signing key for this with &lt;code&gt;keytool&lt;/code&gt; (part of the Java
runtime):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell-session&#34; data-lang=&#34;shell-session&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ keytool -genkey -keystore dummy.keystore -alias dummy -keyalg RSA -keysize &lt;span style=&#34;color:#ae81ff&#34;&gt;2048&lt;/span&gt; -validity &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After following through the prompts, we can then use &lt;code&gt;apksigner&lt;/code&gt; (from the
Android SDK build tools) to sign our patched APK with the dummy key:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell-session&#34; data-lang=&#34;shell-session&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ apksigner sign --ks dummy.keystore maps-7.0/dist/com.google.android.apps.maps_7.0.2-700022123_minAPI18&lt;span style=&#34;color:#ae81ff&#34;&gt;\(&lt;/span&gt;320dpi&lt;span style=&#34;color:#ae81ff&#34;&gt;\)&lt;/span&gt;_apkmirror.com.apk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And then we can launch a device emulator and finally install our patched APK
using &lt;code&gt;adb&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell-session&#34; data-lang=&#34;shell-session&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ adb install -r maps-7.0/dist/com.google.android.apps.maps_7.0.2-700022123_minAPI18&lt;span style=&#34;color:#ae81ff&#34;&gt;\(&lt;/span&gt;320dpi&lt;span style=&#34;color:#ae81ff&#34;&gt;\)&lt;/span&gt;_apkmirror.com.apk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And we get this&amp;hellip;&lt;/p&gt;

&lt;figure style=&#34;width: 15em; margin: auto;&#34;&gt;
  &lt;video controls muted style=&#34;max-width:100%;&#34;&gt;
    &lt;source src=&#34;https://kennethjenkins.net/video/GMM 7.0 crash.webm&#34; type=&#34;video/webm&#34;&gt;
    Video format not supported on this browser.
  &lt;/video&gt;
  &lt;figcaption&gt;Unfortunately, Maps has stopped.&lt;/figcaption&gt;
&lt;/figure&gt;


&lt;p&gt;Hmm. Progress?&lt;/p&gt;
&lt;h2 id=&#34;more-patching&#34;&gt;More patching&lt;/h2&gt;
&lt;p&gt;Well, at least we can use &lt;code&gt;adb logcat&lt;/code&gt; to look for crash information, and
what do you know:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;FATAL EXCEPTION: main&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Process: com.google.android.apps.maps, PID: 3012&lt;/code&gt;&lt;br&gt;
&lt;code&gt;java.lang.SecurityException: GoogleCertificatesRslt: not whitelisted: pkg=com.google.android.apps.maps, sha1=9459608ef2083805c6f6075779366c5831df2f8f, atk=true, ver=202414010.true (go/gsrlt)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Well yes, our patched APK would fail whatever hash-based verification it
might be subjected to, wouldn&amp;rsquo;t it.&lt;/p&gt;
&lt;p&gt;The logcat output also has a stack trace to work with, and there&amp;rsquo;s only one
stack frame matching the &lt;code&gt;com.google.android.apps.gmm&lt;/code&gt; package name:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--------- beginning of crash
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): FATAL EXCEPTION: main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): Process: com.google.android.apps.maps, PID: 3012
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): java.lang.SecurityException: GoogleCertificatesRslt: not whitelisted: pkg=com.google.android.apps.maps, sha1=9459608ef2083805c6f6075779366c5831df2f8f, atk=true, ver=202414010.true (go/gsrlt)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.os.Parcel.readException(Parcel.java:1540)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.os.Parcel.readException(Parcel.java:1493)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at com.google.android.gms.internal.v.c(Unknown Source)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at com.google.android.gms.internal.aS.a(Unknown Source)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at com.google.android.gms.internal.g.b(Unknown Source)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at com.google.android.gms.internal.k.onServiceConnected(Unknown Source)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at com.google.android.gms.internal.m.a(Unknown Source)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at com.google.android.gms.internal.g.c(Unknown Source)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at com.google.android.gms.location.reporting.c.a(Unknown Source)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at com.google.android.apps.gmm.ulr.UlrPromoFragment.onResume(SourceFile:89)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.app.Fragment.performResume(Fragment.java:2096)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:928)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1067)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.app.BackStackRecord.popFromBackStack(BackStackRecord.java:1595)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1504)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.app.FragmentManagerImpl$2.run(FragmentManager.java:490)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1452)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.app.FragmentManagerImpl$1.run(FragmentManager.java:447)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.os.Handler.handleCallback(Handler.java:739)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.os.Handler.dispatchMessage(Handler.java:95)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.os.Looper.loop(Looper.java:135)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at android.app.ActivityThread.main(ActivityThread.java:5221)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at java.lang.reflect.Method.invoke(Native Method)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at java.lang.reflect.Method.invoke(Method.java:372)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;E/AndroidRuntime( 3012): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;W/ActivityManager( 1178):   Force finishing activity com.google.android.apps.maps/com.google.android.maps.MapsActivity
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s see if we can do without the &lt;code&gt;UlrPromoFragment&lt;/code&gt; as well. After some more
spelunking through the disassembled classes, I found this section in the class
&lt;code&gt;com/google/android/apps/gmm/base/activities/d&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-smali&#34; data-lang=&#34;smali&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-static {v0}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/ulr/&lt;span style=&#34;color:#a6e22e&#34;&gt;UlrPromoFragment&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;GmmActivity&lt;/span&gt;;)&lt;span style=&#34;color:#66d9ef&#34;&gt;Z&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    move-result v0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    if-eqz v0, :cond_0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.line&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;566&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    iget-object v0, p0, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;;-&amp;gt;a:&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;GmmActivity&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new-instance v1, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/ulr/&lt;span style=&#34;color:#a6e22e&#34;&gt;UlrPromoFragment&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-direct {v1}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/ulr/&lt;span style=&#34;color:#a6e22e&#34;&gt;UlrPromoFragment&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;lt;init&amp;gt;&lt;/span&gt;()&lt;span style=&#34;color:#66d9ef&#34;&gt;V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-virtual {v0, v1}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;GmmActivity&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/fragments/&lt;span style=&#34;color:#a6e22e&#34;&gt;GmmActivityFragment&lt;/span&gt;;)&lt;span style=&#34;color:#66d9ef&#34;&gt;V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.line&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;568&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    :cond_0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    iget-object v0, p0, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;;-&amp;gt;a:&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;GmmActivity&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a call to a static method on &lt;code&gt;UlrPromoFragment&lt;/code&gt;, and then depending
on the return value from that static method, we may or may not initialize
a &lt;code&gt;UlrPromoFragment&lt;/code&gt; instance and do something with it.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s just make that an unconditional jump to &lt;code&gt;:cond_0&lt;/code&gt; instead, so we never
create a &lt;code&gt;UlrPromoFragment&lt;/code&gt; instance here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-smali&#34; data-lang=&#34;smali&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-static {v0}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/ulr/&lt;span style=&#34;color:#a6e22e&#34;&gt;UlrPromoFragment&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;GmmActivity&lt;/span&gt;;)&lt;span style=&#34;color:#66d9ef&#34;&gt;Z&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    move-result v0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    goto :cond_0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.line&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;566&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    iget-object v0, p0, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;;-&amp;gt;a:&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;GmmActivity&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    new-instance v1, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/ulr/&lt;span style=&#34;color:#a6e22e&#34;&gt;UlrPromoFragment&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-direct {v1}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/ulr/&lt;span style=&#34;color:#a6e22e&#34;&gt;UlrPromoFragment&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;lt;init&amp;gt;&lt;/span&gt;()&lt;span style=&#34;color:#66d9ef&#34;&gt;V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invoke-virtual {v0, v1}, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;GmmActivity&lt;/span&gt;;-&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/fragments/&lt;span style=&#34;color:#a6e22e&#34;&gt;GmmActivityFragment&lt;/span&gt;;)&lt;span style=&#34;color:#66d9ef&#34;&gt;V&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;.line&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;568&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    :cond_0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    iget-object v0, p0, &lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;;-&amp;gt;a:&lt;span style=&#34;color:#66d9ef&#34;&gt;L&lt;/span&gt;com/google/android/apps/gmm/base/activities/&lt;span style=&#34;color:#a6e22e&#34;&gt;GmmActivity&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Repeat the same dance as before: &lt;code&gt;apktool b&lt;/code&gt;, &lt;code&gt;apksigner sign&lt;/code&gt;, uninstall and
then &lt;code&gt;adb install&lt;/code&gt;, and then&amp;hellip;&lt;/p&gt;

&lt;figure style=&#34;width: 15em; margin: auto;&#34;&gt;
  &lt;video controls muted style=&#34;max-width:100%;&#34;&gt;
    &lt;source src=&#34;https://kennethjenkins.net/video/GMM 7.0 search.webm&#34; type=&#34;video/webm&#34;&gt;
    Video format not supported on this browser.
  &lt;/video&gt;
  &lt;figcaption&gt;Searching for restaurants in Mountain View like it&#39;s
    2013&lt;/figcaption&gt;
&lt;/figure&gt;


&lt;p&gt;The map renders! Search works! The Street View thumbnails don&amp;rsquo;t seem to load
anymore, but other than that it seems totally functional.&lt;/p&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/JesusFreke/smali/wiki/TypesMethodsAndFields&#34;&gt;smali: Types, Methods, and Fields&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://source.android.com/docs/core/runtime/dalvik-bytecode&#34;&gt;Dalvik bytecode documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
     </item>
   
     <item>
       <title>Adventures in consumer electronics repair: GE clock radio</title>
       <link>https://kennethjenkins.net/posts/clock-radio-repair/</link>
       <pubDate>Tue, 11 Apr 2023 16:20:35 -0700</pubDate>
       
       <guid>https://kennethjenkins.net/posts/clock-radio-repair/</guid>
       <description>&lt;figure&gt;
  &lt;img src=&#34;radio-1.jpg&#34; alt=&#34;Photo of a 90&amp;rsquo;s era GE clock radio&#34;&gt;
  &lt;figcaption&gt;GE clock radio, model number 7-4613B. Likely manufactured in
1994.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I&amp;rsquo;ve had this GE clock radio ever since I was little. I took it with me to
college, and then after I graduated I brought it with me across the country.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s been a faithful companion over the years, but it&amp;rsquo;s starting to show its
age. The main slider for selecting between off / on / alarm / music doesn&amp;rsquo;t
work reliably. The radio also has a 9-volt battery backup for keeping the time
when there&amp;rsquo;s a power interruption, but lately it seems to run fast when it&amp;rsquo;s on
backup power.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;d been having power outages fairly often, unfortunately. After the latest
outage I tried to reset it to the current time, but to my dismay I found the
&amp;ldquo;MIN&amp;rdquo; button no longer functioned at all.&lt;/p&gt;
&lt;p&gt;Having developed quite the sentimental attraction to this clock, I thought I&amp;rsquo;d
take it apart and see if I couldn&amp;rsquo;t fix it.&lt;/p&gt;
&lt;p&gt;After removing some screws and pulling off some knobs, I was finally able to
coax the cover off. What did I find, but a set of 6mm tactile switches!&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;radio-2.jpg&#34; alt=&#34;Photo of the clock radio internals&#34;&gt;
  &lt;figcaption&gt;Clock radio internals. Note the four push-button switches in
the lower left.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Rummaging through an old shoebox of electronics doodads, I found I had a little
baggy full of identical-looking switches. Judging from the part number, I must
have bought these from &lt;a href=&#34;https://www.adafruit.com/product/367&#34;&gt;Adafruit&lt;/a&gt;,
probably when I was messing around with Arduino stuff.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;switches.jpg&#34; alt=&#34;Photo of a packet of 6mm switches&#34;&gt;&lt;/p&gt;
&lt;p&gt;Lifting out the circuit board to examine the underside, I found that only three
of the switch&amp;rsquo;s four pins were soldered. (And one of the three that is soldered
doesn&amp;rsquo;t appear to connect to any traces, so I assume that was just for
structural purposes.)&lt;/p&gt;
&lt;p&gt;Luckily for me, that made desoldering a bit easier. I managed it with some
desoldering wick and the pencil tip that was already in my soldering iron, but
it was a bit clumsy.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&#34;radio-3.jpg&#34; alt=&#34;Close-up of the underside of the circuit board&#34;&gt;
  &lt;figcaption&gt;Underside of the circuit board (leads of the switch in question
highlighted in magenta). There&amp;rsquo;s pretty clearly been some water damage,
though I don&amp;rsquo;t have a distinct memory of how or when this might have
happened.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Using a multimeter in continuity mode and one of the other working switches, I
confirmed that the switches I had on hand behaved the same as the ones on the
board (normally open, momentary).&lt;/p&gt;
&lt;p&gt;As I&amp;rsquo;m finding is often the case with repair work, installing the new part was
much easier than removing the old.&lt;/p&gt;
&lt;p&gt;Success! With the new switch installed, I could once again advance the clock&amp;rsquo;s
minute value.&lt;/p&gt;
&lt;p&gt;The only difference I noticed was that the new switch is much &amp;ldquo;click&amp;rdquo;-ier than
the old ones. I&amp;rsquo;m not sure if that&amp;rsquo;s a difference in specification, or
something to do with wear over time. For something that I interact with
infrequently, I&amp;rsquo;m not bothered by it.&lt;/p&gt;
&lt;p&gt;Maybe one of these days I will tackle replacing that main slider switch.&lt;/p&gt;
</description>
     </item>
   
     <item>
       <title>Advent of Code 2020: Day 19 reflection</title>
       <link>https://kennethjenkins.net/posts/advent-2020-19/</link>
       <pubDate>Tue, 30 Nov 2021 20:55:00 -0800</pubDate>
       
       <guid>https://kennethjenkins.net/posts/advent-2020-19/</guid>
       <description>&lt;p&gt;&lt;a href=&#34;https://adventofcode.com/&#34;&gt;Advent of Code&lt;/a&gt; 2021 starts tonight! But before
then, I have one more reflection from last year&amp;rsquo;s challenge to share.&lt;/p&gt;
&lt;p&gt;This one is about the day 19 puzzle, which, to be perfectly honest, I did
not particularly enjoy.&lt;/p&gt;
&lt;p&gt;From the puzzle description:&lt;/p&gt;
&lt;figure&gt;
  &lt;blockquote&gt;
&lt;p&gt;The rules for valid messages (the top part of your puzzle input) are numbered
and build upon each other. For example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;0: 1 2
1: &amp;#34;a&amp;#34;
2: 1 3 | 3 1
3: &amp;#34;b&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Some rules, like &lt;code&gt;3: &amp;quot;b&amp;quot;&lt;/code&gt;, simply match a single character (in this case,
&lt;code&gt;b&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The remaining rules list the sub-rules that must be followed; for example,
the rule &lt;code&gt;0: 1 2&lt;/code&gt; means that to match rule &lt;code&gt;0&lt;/code&gt;, the text being checked must
match rule &lt;code&gt;1&lt;/code&gt;, and the text after the part that matched rule &lt;code&gt;1&lt;/code&gt; must then
match rule &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Some of the rules have multiple lists of sub-rules separated by a pipe (&lt;code&gt;|&lt;/code&gt;).
This means that at least one list of sub-rules must match. (The ones that
match might be different each time the rule is encountered.) For example, the
rule &lt;code&gt;2: 1 3 | 3 1&lt;/code&gt; means that to match rule &lt;code&gt;2&lt;/code&gt;, the text being checked must
match rule &lt;code&gt;1&lt;/code&gt; followed by rule &lt;code&gt;3&lt;/code&gt; or it must match rule &lt;code&gt;3&lt;/code&gt; followed by
rule &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Fortunately, there are no loops in the rules, so the list of possible matches
will be finite.&lt;/p&gt;
&lt;p&gt;[&amp;hellip;]&lt;/p&gt;
&lt;p&gt;The received messages (the bottom part of your puzzle input) need to be
checked against the rules so you can determine which are valid and which are
corrupted.&lt;/p&gt;
&lt;p&gt;[&amp;hellip;]&lt;/p&gt;
&lt;p&gt;Your goal is to determine the number of messages that completely match rule
&lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

  &lt;figcaption&gt;(from &lt;a href=&#34;https://adventofcode.com/2020/day/19&#34;&gt;https://adventofcode.com/2020/day/19&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Dear reader, the &lt;code&gt;&amp;quot;a&amp;quot;&lt;/code&gt; and &lt;code&gt;&amp;quot;b&amp;quot;&lt;/code&gt; from the example were not &lt;em&gt;just&lt;/em&gt; an example.
In my puzzle input file, there were in fact only &lt;code&gt;&amp;quot;a&amp;quot;&lt;/code&gt; and &lt;code&gt;&amp;quot;b&amp;quot;&lt;/code&gt; character
rules, followed by an unwieldy number of &amp;ldquo;messages&amp;rdquo; that looked like lots of
&lt;code&gt;ababbbab&lt;/code&gt; and &lt;code&gt;aabaaaba&lt;/code&gt; and &lt;code&gt;baaabbaa&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I thought this was a bit too much.&lt;/p&gt;
&lt;h2 id=&#34;my-original-approach&#34;&gt;My original approach&lt;/h2&gt;
&lt;p&gt;In my first attempt at this puzzle, I thought, &amp;ldquo;&lt;a href=&#34;http://regex.info/blog/2006-09-15/247&#34;&gt;I know, I&amp;rsquo;ll use regular
expressions&lt;/a&gt;.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The input rules format seemed half way to regular expression syntax anyway,
so I thought, why not translate rule &lt;code&gt;0&lt;/code&gt; into one giant regular expression and
run all the messages through it?&lt;/p&gt;
&lt;p&gt;So the example rule set would turn into something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;/a/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;/b/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;/(ab|ba)/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;/a(ab|ba)/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This strategy worked just fine for part 1 of the puzzle, but looking back, I
will concede that it was unnecessarily complicated.&lt;/p&gt;
&lt;p&gt;(It would probably have been better even to build a set of all possible valid
messages, and use that to query whether each message in the input file was
present in the set of valid messages.)&lt;/p&gt;
&lt;p&gt;But when it came to part 2&amp;hellip;&lt;/p&gt;
&lt;figure&gt;
  &lt;blockquote&gt;
&lt;p&gt;As you look over the list of messages, you realize your matching rules aren&amp;rsquo;t
quite right. To fix them, completely replace rules &lt;code&gt;8: 42&lt;/code&gt; and &lt;code&gt;11: 42 31&lt;/code&gt;
with the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;8: 42 | 42 8
11: 42 31 | 42 11 31
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This small change has a big impact: now, the rules &lt;em&gt;do&lt;/em&gt; contain loops, and
the list of messages they could hypothetically match is infinite. You&amp;rsquo;ll need
to determine how these changes affect which messages are valid.&lt;/p&gt;
&lt;p&gt;Fortunately, many of the rules are unaffected by this change; it might help
to start by looking at which rules always match the same set of values and
how those rules (especially rules &lt;code&gt;42&lt;/code&gt; and &lt;code&gt;31&lt;/code&gt;) are used by the new versions
of rules &lt;code&gt;8&lt;/code&gt; and &lt;code&gt;11&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;(Remember, you only need to handle the rules you have; building a solution
that could handle any hypothetical combination of rules would be
&lt;a href=&#34;https://en.wikipedia.org/wiki/Formal_grammar&#34;&gt;significantly more difficult&lt;/a&gt;.)&lt;/p&gt;
&lt;/blockquote&gt;

  &lt;figcaption&gt;(from &lt;a href=&#34;https://adventofcode.com/2020/day/19&#34;&gt;https://adventofcode.com/2020/day/19&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The new rule &lt;code&gt;8&lt;/code&gt; can still be expressed as a regular expression: it&amp;rsquo;s just &amp;ldquo;1
or more&amp;rdquo; copies of rule &lt;code&gt;42&lt;/code&gt; (we can use the regex &lt;code&gt;+&lt;/code&gt; symbol for this).&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s rule &lt;code&gt;11&lt;/code&gt; that throws a monkey wrench into the regular expression
strategy. In words, we could describe this rule as &amp;ldquo;one or more matches of rule
&lt;code&gt;42&lt;/code&gt;, followed by an equal number of matches of rule &lt;code&gt;31&lt;/code&gt;&amp;rdquo;. Sadly this &amp;ldquo;equal
number of matches&amp;rdquo; can&amp;rsquo;t be expressed as a regular expression.&lt;/p&gt;
&lt;p&gt;In fact, this is literally the first non-example of a regular language in the
&lt;a href=&#34;https://en.wikipedia.org/wiki/Regular_language#Examples&#34;&gt;wikipedia article&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;blockquote&gt;
&lt;p&gt;A simple example of a language that is not regular is the set of strings
\( \{ a^nb^n \mid n ≥ 0 \} \). Intuitively, it cannot be recognized with
a finite automaton, since a finite automaton has finite memory and it cannot
remember the exact number of a&amp;rsquo;s.&lt;/p&gt;
&lt;/blockquote&gt;

  &lt;figcaption&gt;(from &lt;a href=&#34;https://en.wikipedia.org/wiki/Regular_language&#34;&gt;https://en.wikipedia.org/wiki/Regular_language&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I still hacked together a solution out of my existing work for part 1, but
it relied on a couple important details:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Neither rule &lt;code&gt;42&lt;/code&gt; nor rule &lt;code&gt;31&lt;/code&gt; (or any rules referenced by them) contained
any reference to rule &lt;code&gt;8&lt;/code&gt; or &lt;code&gt;11&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;As it turned out, all of the possible matches for both rule &lt;code&gt;42&lt;/code&gt; and rule
&lt;code&gt;31&lt;/code&gt; had the same length (8 characters).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The other crucial detail is that the actual rule &lt;code&gt;0&lt;/code&gt; (at least in my puzzle
input) was this: &lt;code&gt;0: 8 11&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Combining rules &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;8&lt;/code&gt;, and &lt;code&gt;11&lt;/code&gt; then, essentially what I was looking for was
&amp;ldquo;two or more matches of rule &lt;code&gt;42&lt;/code&gt;, followed by some smaller (but non-zero)
number of matches of rule &lt;code&gt;31&lt;/code&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Putting all this together, it meant I could examine a message in chunks
of 8 characters. If a message wasn&amp;rsquo;t a multiple of 8 characters, I could
discard it right away; otherwise, I could see how many chunks (starting from
the beginning) would match rule &lt;code&gt;42&lt;/code&gt;, and how many chunks (now starting from
the end) would match rule &lt;code&gt;31&lt;/code&gt;. Let&amp;rsquo;s say the whole message was \( n \)
chunks in length. If we had \( x \) chunks matching rule &lt;code&gt;42&lt;/code&gt;, and \( y \)
chunks matching rule &lt;code&gt;31&lt;/code&gt;, then as long as there was some \( k \) that could
satisfy all of&lt;/p&gt;
 $$
\begin{gather*}
 2 \le k \le x \\
 1 \le (n - k) \le y \\
 k &gt; n - k \\
\end{gather*}
$$ 

&lt;p&gt;this would indicate a valid match.&lt;/p&gt;
&lt;p&gt;Like I said, I eventually got this to work, after a fair bit of trial and error.&lt;/p&gt;
&lt;p&gt;But this all left me wondering, what&amp;rsquo;s the &amp;ldquo;right way&amp;rdquo; to do something like
this, in the general case? Is there some existing parsing library that&amp;rsquo;s
already solved this problem for us?&lt;/p&gt;
&lt;h2 id=&#34;gll-parsing-to-the-rescue&#34;&gt;GLL parsing to the rescue&lt;/h2&gt;
&lt;p&gt;I recently came across the &lt;a href=&#34;https://github.com/robey/packrattle&#34;&gt;&lt;code&gt;packrattle&lt;/code&gt;&lt;/a&gt;
parsing library, which can do just this! (I&amp;rsquo;m sure there are others that
would work too, but this was one in a language I felt reasonably confident
that I could use.)&lt;/p&gt;
&lt;p&gt;The underlying parsing algorithm is fairly magical to me, but the library
is simple to use.&lt;/p&gt;
&lt;p&gt;The core of my &lt;code&gt;packrattle&lt;/code&gt;-based solution looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;packrattle&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parseRule&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;line&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; [&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;line&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;: &amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parseInt(&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;alt&lt;/span&gt;(...&lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; | &amp;#39;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;seq&lt;/span&gt;(...&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parseInt(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; () =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }))));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This function can be used to build up an object &lt;code&gt;p&lt;/code&gt; of rule parsers (keyed
by the rule ID number), using these &lt;code&gt;packrattle&lt;/code&gt; functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;string()&lt;/code&gt; matches a fixed input string (in our case, either &lt;code&gt;&amp;quot;a&amp;quot;&lt;/code&gt; or &lt;code&gt;&amp;quot;b&amp;quot;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;alt()&lt;/code&gt; matches any one of a list of alternatives&lt;/li&gt;
&lt;li&gt;&lt;code&gt;seq()&lt;/code&gt; matches a list of things in sequence&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The rest of the solution reads from standard input, parsing rules until it
reaches a blank line, and then switches to feeding input lines to the final
&lt;code&gt;p[0]&lt;/code&gt; parser, keeping a running count of valid matches.&lt;/p&gt;
&lt;p&gt;This certainly works, but can we go a little meta? Why are we messing around
with &lt;code&gt;split()&lt;/code&gt; to parse the input rules? Why not construct a parser that
itself returns parsers?&lt;/p&gt;
&lt;p&gt;Behold:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ruleParser&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ruleId&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;regex&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;/\d+/&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; =&amp;gt; parseInt(&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;character&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;regex&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;/&amp;#34;(.)&amp;#34;/&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ruleSeq&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;repeatSeparated&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ruleId&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ruleAlts&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;repeatSeparated&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ruleSeq&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; | &amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ruleDelim&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;: &amp;#39;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;drop&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;seq&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ruleId&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ruleDelim&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;alt&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;character&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ruleAlts&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parseRule&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;line&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; [&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ruleParser&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;run&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;line&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;string&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;alt&lt;/span&gt;(...&lt;span style=&#34;color:#a6e22e&#34;&gt;rule&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;ids&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;packrattle&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;seq&lt;/span&gt;(...&lt;span style=&#34;color:#a6e22e&#34;&gt;ids&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;map&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt; =&amp;gt; () =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;]))));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Is this any more readable that the last version? Not really, no.&lt;/p&gt;
&lt;p&gt;But this was a fun exercise in using a parsing library!&lt;/p&gt;
</description>
     </item>
   
     <item>
       <title>Advent of Code 2020: Day 18 reflection</title>
       <link>https://kennethjenkins.net/posts/advent-2020-18/</link>
       <pubDate>Sun, 28 Nov 2021 20:10:00 -0800</pubDate>
       
       <guid>https://kennethjenkins.net/posts/advent-2020-18/</guid>
       <description>&lt;p&gt;The day 18 puzzle from last year&amp;rsquo;s &lt;a href=&#34;https://adventofcode.com/&#34;&gt;Advent of Code&lt;/a&gt;
involved parsing and evaluating arithmetic expressions, but with a twist:&lt;/p&gt;
&lt;figure&gt;
  &lt;blockquote&gt;
&lt;p&gt;The homework (your puzzle input) consists of a series of expressions that
consist of addition (&lt;code&gt;+&lt;/code&gt;), multiplication (&lt;code&gt;*&lt;/code&gt;), and parentheses (&lt;code&gt;(...)&lt;/code&gt;).
Just like normal math, parentheses indicate that the expression inside must
be evaluated before it can be used by the surrounding expression. Addition
still finds the sum of the numbers on both sides of the operator, and
multiplication still finds the product.&lt;/p&gt;
&lt;p&gt;However, the rules of operator precedence have changed. Rather than
evaluating multiplication before addition, the operators have the same
precedence, and are evaluated left-to-right regardless of the order in which
they appear.&lt;/p&gt;
&lt;p&gt;These expressions consisted of integers, addition, multiplication, and
parentheses to group sub-expressions.&lt;/p&gt;
&lt;/blockquote&gt;

  &lt;figcaption&gt;(from &lt;a href=&#34;https://adventofcode.com/2020/day/18&#34;&gt;https://adventofcode.com/2020/day/18&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Part 2 is similar, but instead of having the same precedence, addition is
evaluated before multiplication.&lt;/p&gt;
&lt;p&gt;When I first went about solving this puzzle, I wrote some Python code to look
for parentheses, and split expressions at operators, and then evaluate those
expressions recursively.&lt;/p&gt;
&lt;p&gt;But afterwards I got curious&amp;hellip; this seems like an awful lot of work. Can we
find a lazier approach?&lt;/p&gt;
&lt;p&gt;What if we could use a programming language where we can directly redefine the
&lt;code&gt;+&lt;/code&gt; and &lt;code&gt;*&lt;/code&gt; functions to have a different precedence?&lt;/p&gt;
&lt;h2 id=&#34;operator-precedence&#34;&gt;Operator precedence&lt;/h2&gt;
&lt;p&gt;There may be other languages we could use for this, but the one that came to
mind for me was &lt;a href=&#34;https://www.haskell.org/&#34;&gt;Haskell&lt;/a&gt;. Haskell allows you to
define your own operators, and to specify their precedence levels.&lt;/p&gt;
&lt;aside class=&#34;notice&#34;&gt;
  &lt;i class=&#34;fas fa-info-circle&#34;&gt;&lt;/i&gt;
  &lt;div&gt;If you&amp;rsquo;d like to follow along on your own machine, you can install &lt;code&gt;ghc&lt;/code&gt;
following the instructions at &lt;a href=&#34;https://www.haskell.org/ghcup/&#34;&gt;https://www.haskell.org/ghcup/&lt;/a&gt;.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;We can use GHC&amp;rsquo;s interactive environment to check the default operator
precedence for &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;*&lt;/code&gt;, using the &lt;code&gt;:info&lt;/code&gt; command:&lt;/p&gt;

&lt;pre style=&#34;color: #f8f8f2; background-color: #272822;&#34;&gt;
&lt;code&gt;ken$ ghci
GHCi, version 8.8.2: https://www.haskell.org/ghc/  :? for help
Prelude&gt; :info +
class Num a where
  (+) :: a -&gt; a -&gt; a
  ...
  	-- Defined in ‘GHC.Num’
infixl 6 +
Prelude&gt; :info *
class Num a where
  ...
  (*) :: a -&gt; a -&gt; a
  ...
  	-- Defined in ‘GHC.Num’
infixl 7 *&lt;/code&gt;
&lt;/pre&gt;


&lt;p&gt;The line &lt;code&gt;infixl 6 +&lt;/code&gt; tells us that the &lt;code&gt;+&lt;/code&gt; operator has precedence level 6.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;
Likewise, the &lt;code&gt;*&lt;/code&gt; operator has precedence level 7. Because &lt;code&gt;*&lt;/code&gt; has a higher
precedence level, it will be evaluated before &lt;code&gt;+&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, we can just redefine the precedence for &lt;code&gt;+&lt;/code&gt; so that it is the same as for
&lt;code&gt;*&lt;/code&gt;:&lt;/p&gt;

&lt;pre style=&#34;color: #f8f8f2; background-color: #272822;&#34;&gt;
&lt;code&gt;Prelude&gt; infixl 7 +

&lt;span style=&#34;color:#fff;font-weight:bold&#34;&gt;&amp;lt;interactive&amp;gt;:1:10: &lt;span style=&#34;color:#f33&#34;&gt;error:&lt;/span&gt;
    The fixity signature for ‘+’ lacks an accompanying binding
      (The fixity signature must be given where ‘+’ is declared)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Well, OK, not quite like that&amp;hellip; it looks like we have to redefine the &lt;code&gt;+&lt;/code&gt;
operator itself at the same time if we want to assign a new operator
precedence. We can do that with a no-op definition, using a qualified reference
to the original &lt;code&gt;+&lt;/code&gt; operator:&lt;/p&gt;

&lt;pre style=&#34;color: #f8f8f2; background-color: #272822;&#34;&gt;
&lt;code&gt;Prelude&gt; (+) = (Prelude.+); infixl 7 +
Prelude&gt; 1 + 2 * 3
9
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;But hey, look at that! Now &lt;code&gt;1 + 2 * 3&lt;/code&gt; equals &lt;code&gt;9&lt;/code&gt;!&lt;/p&gt;
&lt;h2 id=&#34;a-complete-solution&#34;&gt;A complete solution&lt;/h2&gt;
&lt;p&gt;The puzzle asks for the sum of all the expressions in the input file (which
has one expression per line).&lt;/p&gt;
&lt;p&gt;So, if we take our input file, stick the redefinition of &lt;code&gt;+&lt;/code&gt; at the top, and
run the whole lot through &lt;code&gt;ghci&lt;/code&gt;, we can evaluate all of the expressions in the
input.&lt;/p&gt;
&lt;p&gt;Something like this command line:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(+) = (Prelude.+); infixl 7 +
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;cat input18.txt&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | ghci -v0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;-v0&lt;/code&gt; option allows us to suppress the prompt output from &lt;code&gt;ghci&lt;/code&gt;, so we
get just the results of each expression as our output, one per line.&lt;/p&gt;
&lt;p&gt;Once we have the expressions evaluated, we still need to get the sum of all
the values. We could use some &lt;code&gt;awk&lt;/code&gt; for this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(+) = (Prelude.+); infixl 7 +
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;cat input18.txt&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | ghci -v0 | awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{sum+=$1} END {print sum}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can do a very similar thing for part 2, where we need to swap the
precedence of addition and multiplication:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(+) = (Prelude.+); infixl 7 +; (*) = (Prelude.*); infixl 6 *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;cat input18.txt&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | ghci -v0 | awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{sum+=$1} END {print sum}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And there you have it! No need to balance parentheses or parse expressions.&lt;/p&gt;
&lt;p&gt;If there were a contest for &amp;ldquo;most oddball solution&amp;rdquo;, this would be my entry.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;This line is known as a &lt;em&gt;fixity declaration&lt;/em&gt;. The &lt;strong&gt;&lt;code&gt;l&lt;/code&gt;&lt;/strong&gt; in
&lt;code&gt;infix&lt;/code&gt;&lt;strong&gt;&lt;code&gt;l&lt;/code&gt;&lt;/strong&gt; specifies that the &lt;code&gt;+&lt;/code&gt; operator is &lt;strong&gt;l&lt;/strong&gt;eft associative.
There&amp;rsquo;s also &lt;code&gt;infix&lt;/code&gt;&lt;strong&gt;&lt;code&gt;r&lt;/code&gt;&lt;/strong&gt; for &lt;strong&gt;r&lt;/strong&gt;ight associativity, and a plain &lt;code&gt;infix&lt;/code&gt;
for non-associative operators. (See
&lt;a href=&#34;https://www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-820004.4.2&#34;&gt;§4.4.2&lt;/a&gt;
of &lt;em&gt;The Haskell 2010 Language&lt;/em&gt; for more info.)&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
     </item>
   
     <item>
       <title>Advent of Code 2020: Day 13 reflection</title>
       <link>https://kennethjenkins.net/posts/advent-2020-13/</link>
       <pubDate>Fri, 26 Nov 2021 23:15:00 -0800</pubDate>
       
       <guid>https://kennethjenkins.net/posts/advent-2020-13/</guid>
       <description>&lt;p&gt;The day 13 puzzle from last year&amp;rsquo;s &lt;a href=&#34;https://adventofcode.com&#34;&gt;Advent of Code&lt;/a&gt;
was the first one that left me really flummoxed. Part 1 (the warm-up) was
straight-forward enough, but part 2 required left me feeling like there was
some really useful math that I didn&amp;rsquo;t know.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://adventofcode.com/2020/day/13&#34;&gt;The puzzle&lt;/a&gt; was about a set of buses
that all take different amounts of time to complete their routes, and keep
looping around forever.&lt;/p&gt;
&lt;p&gt;After getting past all the flavor text, part 2 of the puzzle boiled down to
this: given a set of tuples \( \{(k_i, n_i)\} \), find the smallest
non-negative \( t \) such that \[  t \equiv -k_i \pmod{n_i} \] for all
\(i\). The problem also gave the hint that \(t\) would exceed
\(10^{14}\).&lt;/p&gt;
&lt;p&gt;(I think the intention of this hint was to discourage anyone from trying
the brute-force solution of checking all possible \( t \) values one by one.
Or maybe just a hint that using 32-bit integers would not be sufficient?)&lt;/p&gt;
&lt;p&gt;The puzzle description gave a few example problems (with solutions,
thankfully). The first one was the set&lt;/p&gt;
&lt;p&gt;\[ \{(0,7), (1,13), (4,59), (6,31), (7,19)\} \]&lt;/p&gt;
&lt;p&gt;and gave the solution as \( t = 1068781 \).&lt;/p&gt;
&lt;p&gt;The modular arithmetic stirred a vague memory in my mind of &lt;a href=&#34;https://en.wikipedia.org/wiki/Fermat%27s_little_theorem&#34;&gt;Fermat&amp;rsquo;s
little theorem&lt;/a&gt;,
which also made me wonder if all the \(n_i\) in my puzzle input were prime.
The problem didn&amp;rsquo;t specify that this would be the case, but glancing through
my input file it seemed that this was indeed true (at least for the puzzle
input that I got).  But I spent much too long thinking about whether this was
useful for the problem, because I couldn&amp;rsquo;t see that it helped at all.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t really make any progress until I started looking for ways to reduce
the problem to a smaller one.&lt;/p&gt;
&lt;p&gt;If the input set consisted of just one tuple, that would be easy enough to
solve! If we had just the first tuple \( (0, 7) \) to worry about, I think
it&amp;rsquo;s simple to see that \( t = 0 \) is the solution.&lt;/p&gt;
&lt;p&gt;And even adding in the second tuple \( (1, 13) \), it&amp;rsquo;s not too bad: as long
as \( t \) is a multiple of \( 7 \), it will satisfy the condition from the
first tuple. So we could start checking all the multiples of \( 7 \), looking
for one where \( t \equiv -1 \equiv 12 \pmod{13} \):&lt;/p&gt;
 $$
\begin{align*}

 7 \equiv 7 \pmod{13} \\
14 \equiv 1 \pmod{13} \\
21 \equiv 8 \pmod{13} \\
28 \equiv 2 \pmod{13} \\
⋮ \qquad \qquad \\
70 \equiv 5 \pmod{13} \\
77 \equiv 12 \pmod{13} \\

\end{align*}
$$ 

&lt;p&gt;But here I got stuck. How do we add in the third tuple \( (4, 59) \)? How
can we find candidate solutions that still satisfy the constraints from the
first two tuples?&lt;/p&gt;
&lt;p&gt;I think I ended up confusing myself here. I was too fixated on the pattern
of looking at multiples of the previous candidate solution. But I couldn&amp;rsquo;t just
start looking at multiples of \( 77 \), as they wouldn&amp;rsquo;t all have the same
remainders modulo \( 13 \) any more.&lt;/p&gt;
&lt;p&gt;With the benefit of hindsight, obviously I should have focused on &lt;em&gt;addition&lt;/em&gt;
rather than &lt;em&gt;multiplication&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We can start adding multiples of \( 91 \) &amp;mdash; because \( 91 \) is
divisible by both \( 7 \) and \( 13 \), this won&amp;rsquo;t affect congruence
\( \gdef\Mod#1{(\mathrm{mod}\ #1)} \Mod{7} \) or \( \Mod{13} \). So we can
continue our search like so, looking for a \( t \equiv -4 \equiv 55 \pmod{59}
\):&lt;/p&gt;
 $$
\begin{align*}

 77 \equiv 18 \pmod{59} \\
168 \equiv 50 \pmod{59} \\
259 \equiv 23 \pmod{59} \\
350 \equiv 55 \pmod{59} \\

\end{align*}
$$ 

&lt;p&gt;So \( t = 350 \) satisfies all of the first three constraints.&lt;/p&gt;
&lt;p&gt;And we can continue on like this. In Python, this approach could look
something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;search&lt;/span&gt;(start, increment, target, modulus):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; start
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; t &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; modulus &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; target:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        t &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; increment
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; t
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;solve&lt;/span&gt;(tuples):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ans &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    increment &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; k, n &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; tuples:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ans &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; search(ans, increment, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;k &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; n, n)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        increment &lt;span style=&#34;color:#f92672&#34;&gt;*=&lt;/span&gt; n
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; ans
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the &lt;code&gt;increment *= n&lt;/code&gt; line probably isn&amp;rsquo;t correct in general. I think
this works only if the set of \( n_i \)s are &lt;a href=&#34;https://en.wikipedia.org/wiki/Coprime_integers&#34;&gt;relatively
prime&lt;/a&gt;? This should probably
be something like &lt;code&gt;increment = math.lcm(increment, n)&lt;/code&gt; in the general case.&lt;/p&gt;
&lt;p&gt;Otherwise, this appears to work just fine for all of the examples in the puzzle
description!&lt;/p&gt;
&lt;h2 id=&#34;too-clever-by-half&#34;&gt;Too clever by half&lt;/h2&gt;
&lt;p&gt;For some reason I couldn&amp;rsquo;t come up with this solution when I was first working
through this problem. Perhaps I had convinced myself this approach wouldn&amp;rsquo;t be
efficient enough?&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know why; my puzzle input had a total of 9 tuples with a maximum \( n
\) value of \( 787 \). That should put us well under 10,000 iterations of
the inner &lt;code&gt;while&lt;/code&gt; loop in the worst case, which should be no problem
(especially if all the values fit in one 64-bit integer, so that there&amp;rsquo;s no
slowdown as we start doing arithmetic with larger and larger numbers).&lt;/p&gt;
&lt;p&gt;But no, after much struggle I eventually ended up with a much more complicated
solution. I&amp;rsquo;ll include the core of it here, but I don&amp;rsquo;t even remember now
exactly what I was thinking:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;solve&lt;/span&gt;(tuples):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    accum &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [(&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;k &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; n, n) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; k, n &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; tuples]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(len(fs)):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        accum&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(fs[i])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [(factor(fs[i][&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;], b[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;], (b[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; fs[i][&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]) &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; b[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]), b[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; fs]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; evaluate(accum)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;factor&lt;/span&gt;(a, d, q):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, d&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; d &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; q:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; i
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;evaluate&lt;/span&gt;(accum):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; accum:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; accum[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;][&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; accum[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;][&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; evaluate(accum[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I think I was trying to express the solution in the form&lt;/p&gt;
&lt;p&gt;\[ t = a + n_1(b + n_2(c + n_3(&amp;hellip;))) \]&lt;/p&gt;
&lt;p&gt;I start with values for the \( a, b, c, &amp;hellip; \) coefficients based on the
\( k_i \) values from the input tuples, and then keep adjusting them for
each of the \( n_i \).&lt;/p&gt;
&lt;p&gt;Compared to the simpler solution from above, the &lt;code&gt;factor()&lt;/code&gt; method is still
doing a linear search for a specific remainder \( \Mod{n_i} \), so it&amp;rsquo;s not
really any more efficient. The only possible advantage I can think of is that
we&amp;rsquo;re working with smaller numbers until the very end, when the &lt;code&gt;evaluate()&lt;/code&gt;
method computes a final answer based on all the final coefficients. But, it
turns out that doesn&amp;rsquo;t really matter, because for this puzzle as the values
fit into 64-bit integers just fine.&lt;/p&gt;
&lt;p&gt;Is there a moral to this story? I&amp;rsquo;m not sure.&lt;/p&gt;
&lt;p&gt;Perhaps it&amp;rsquo;s this: sometimes it&amp;rsquo;s crucial to have a specific insight for
a problem, and you can&amp;rsquo;t really force insight. It&amp;rsquo;s fine to take
a break and come back to the problem later.&lt;/p&gt;
</description>
     </item>
   
     <item>
       <title>Advent of Code 2020: Day 5 reflection</title>
       <link>https://kennethjenkins.net/posts/advent-2020-05/</link>
       <pubDate>Wed, 24 Nov 2021 20:00:00 -0800</pubDate>
       
       <guid>https://kennethjenkins.net/posts/advent-2020-05/</guid>
       <description>&lt;p&gt;The day 5 puzzle from last year&amp;rsquo;s &lt;a href=&#34;https://adventofcode.com/&#34;&gt;Advent of Code&lt;/a&gt;
talks about airplane seat numbering using &amp;ldquo;binary space partitioning&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;From the puzzle description:&lt;/p&gt;
&lt;figure&gt;
  &lt;blockquote&gt;
&lt;p&gt;Instead of zones or groups, this airline uses binary space partitioning to
seat people. A seat might be specified like &lt;code&gt;FBFBBFFRLR&lt;/code&gt;, where &lt;code&gt;F&lt;/code&gt; means
&amp;ldquo;front&amp;rdquo;, &lt;code&gt;B&lt;/code&gt; means &amp;ldquo;back&amp;rdquo;, &lt;code&gt;L&lt;/code&gt; means &amp;ldquo;left&amp;rdquo;, and &lt;code&gt;R&lt;/code&gt; means &amp;ldquo;right&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The first 7 characters will either be &lt;code&gt;F&lt;/code&gt; or &lt;code&gt;B&lt;/code&gt;; these specify exactly one of
the 128 rows on the plane (numbered 0 through 127). Each letter tells you
which half of a region the given seat is in. Start with the whole list of
rows; the first letter indicates whether the seat is in the front (0 through
63) or the back (64 through 127). The next letter indicates which half of
that region the seat is in, and so on until you&amp;rsquo;re left with exactly one row.&lt;/p&gt;
&lt;p&gt;[&amp;hellip;]&lt;/p&gt;
&lt;p&gt;The last three characters will be either &lt;code&gt;L&lt;/code&gt; or &lt;code&gt;R&lt;/code&gt;; these specify exactly
one of the 8 columns of seats on the plane (numbered 0 through 7). The same
process as above proceeds again, this time with only three steps. L means to
keep the lower half, while R means to keep the upper half.&lt;/p&gt;
&lt;p&gt;[&amp;hellip;]&lt;/p&gt;
&lt;p&gt;Every seat also has a unique seat ID: multiply the row by 8, then add the
column.&lt;/p&gt;
&lt;/blockquote&gt;

  &lt;figcaption&gt;(from &lt;a href=&#34;https://adventofcode.com/2020/day/5&#34;&gt;https://adventofcode.com/2020/day/5&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;After absorbing all of that explanation, it turns out that parsing a &amp;ldquo;seat
specification&amp;rdquo; is way less complicated than it might appear. Converting to
row and column is just like parsing a binary number: the first character is
just like the most significant bit of a 7-bit binary number (the 64s place, if
you will). As the rows are numbered with 0 in the front and 127 in the back, an
&lt;code&gt;F&lt;/code&gt; is like a 0 bit and a &lt;code&gt;B&lt;/code&gt; like a 1.&lt;/p&gt;
&lt;p&gt;Similarly, the last three characters are the binary representation of the
column number, with &lt;code&gt;L&lt;/code&gt; like a 0 and &lt;code&gt;R&lt;/code&gt; like a 1.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take the example &lt;code&gt;FBFBBFFRLR&lt;/code&gt;. The row and column break down like so:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://kennethjenkins.net/img/seat-description.svg&#34; alt=&#34;seat description example&#34;&gt;&lt;/p&gt;
&lt;p&gt;And then the final seat ID is 44 × 8 + 5 = 357.&lt;/p&gt;
&lt;p&gt;But multiplying by 8 is the same as shifting the binary digits left by three
places. (The 1s place turns into the 8s place, the 2s place into the 16s
place, and so on.)&lt;/p&gt;
&lt;p&gt;So there&amp;rsquo;s no need to distinguish between row and column at all! We can just
replace all the &lt;code&gt;F&lt;/code&gt;s and &lt;code&gt;L&lt;/code&gt;s with 0s, and all the &lt;code&gt;B&lt;/code&gt;s and &lt;code&gt;R&lt;/code&gt;s with 1s, and
then parse the whole thing as an 11 bit binary number.&lt;/p&gt;
&lt;p&gt;(And in Python, parsing a string &lt;code&gt;s&lt;/code&gt; representation of a binary number is as
simple as &lt;code&gt;int(s, 2)&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;So my quick Python code to parse a list of seat descriptions looked something
like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(lines):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [int(l&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;F&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;B&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;L&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;R&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;), &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; l &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; lines]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This worked just fine, but all those &lt;code&gt;replace()&lt;/code&gt; calls feel a little silly.
I thought that there must be a less-verbose way to do this.&lt;/p&gt;
&lt;p&gt;And sure enough, there is!&lt;/p&gt;
&lt;h2 id=&#34;translate-and-maketrans&#34;&gt;&lt;code&gt;translate()&lt;/code&gt; and &lt;code&gt;maketrans()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The Python &lt;a href=&#34;https://docs.python.org/3/library/stdtypes.html#str.translate&#34;&gt;&lt;code&gt;str.translate()&lt;/code&gt;&lt;/a&gt;
method does just this:&lt;/p&gt;
&lt;figure&gt;
  &lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;str.translate(table)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Return a copy of the string in which each character has been mapped through
the given translation table.&lt;/p&gt;
&lt;p&gt;[&amp;hellip;]&lt;/p&gt;
&lt;p&gt;You can use &lt;code&gt;str.maketrans()&lt;/code&gt; to create a translation map from
character-to-character mappings in different formats.&lt;/p&gt;
&lt;/blockquote&gt;

  &lt;figcaption&gt;(from &lt;a href=&#34;https://docs.python.org/3/library/stdtypes.html#str.translate&#34;&gt;https://docs.python.org/3/library/stdtypes.html#str.translate&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And from the description of &lt;a href=&#34;https://docs.python.org/3/library/stdtypes.html#str.maketrans&#34;&gt;&lt;code&gt;str.maketrans()&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;static&lt;/em&gt; &lt;code&gt;str.maketrans(x[, y[, z]])&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This static method returns a translation table usable for &lt;code&gt;str.translate()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;[&amp;hellip;]&lt;/p&gt;
&lt;p&gt;If there are two arguments, they must be strings of equal length, and in the
resulting dictionary, each character in x will be mapped to the character at
the same position in y.&lt;/p&gt;
&lt;/blockquote&gt;

  &lt;figcaption&gt;(from &lt;a href=&#34;https://docs.python.org/3/library/stdtypes.html#str.maketrans&#34;&gt;https://docs.python.org/3/library/stdtypes.html#str.maketrans&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;We can use these methods to simplify our &lt;code&gt;parse()&lt;/code&gt; method like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; str&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;maketrans(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;FBLR&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0101&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(lines):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [int(l&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;translate(t), &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; l &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; lines]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then part 1 of the puzzle (finding the highest seat ID in the input file)
is trivial:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;part1&lt;/span&gt;(ids):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; max(ids)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But the puzzle isn&amp;rsquo;t really the point of this post.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s more that I hadn&amp;rsquo;t ever used Python&amp;rsquo;s &lt;code&gt;translate()&lt;/code&gt; method before, so I
wanted to share in case it&amp;rsquo;s new to others as well.&lt;/p&gt;
&lt;h2 id=&#34;addendum&#34;&gt;Addendum&lt;/h2&gt;
&lt;p&gt;Fun tidbit: the Python &lt;code&gt;translate()&lt;/code&gt; method is basically what the Unix
&lt;a href=&#34;https://www.freebsd.org/cgi/man.cgi?tr&#34;&gt;&lt;code&gt;tr&lt;/code&gt;&lt;/a&gt; command does. We can solve
part 1 of the puzzle (find the highest seat ID out of all the input seat
descriptions) in a one-liner like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ &amp;lt;input05.txt tr FBLR &lt;span style=&#34;color:#ae81ff&#34;&gt;0101&lt;/span&gt; | sort | tail -n1 | echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ibase=2; &lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;cat&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | bc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because all the seat descriptions are the same length, we can sort them as
text strings and we&amp;rsquo;ll get the same order as if we sorted as numbers.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; Then
we can use &lt;code&gt;tail -n1&lt;/code&gt; to take just the last line (the maximum value), and run it
through &lt;code&gt;bc&lt;/code&gt; (the &lt;a href=&#34;https://en.wikipedia.org/wiki/Bc_(programming_language)&#34;&gt;&lt;strong&gt;b&lt;/strong&gt;asic
&lt;strong&gt;c&lt;/strong&gt;alculator&lt;/a&gt;) to
convert from binary to decimal. (We just need to prepend an &lt;code&gt;ibase=2&lt;/code&gt; to get
&lt;code&gt;bc&lt;/code&gt; to parse its input as binary.)&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;If we tried to use plain &lt;code&gt;sort&lt;/code&gt; after converting from binary to decimal,
we could run into problems like &lt;code&gt;99&lt;/code&gt; sorting after &lt;code&gt;100&lt;/code&gt;. But, we can use the
&lt;code&gt;-n&lt;/code&gt; option to sort the input numerically, like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ &amp;lt;input05.txt tr FBLR &lt;span style=&#34;color:#ae81ff&#34;&gt;0101&lt;/span&gt; | echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ibase=2; &lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;cat&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | bc | sort -n | tail -n1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
     </item>
   
     <item>
       <title>Advent of Code 2020: Introduction and Day 4 reflection</title>
       <link>https://kennethjenkins.net/posts/advent-2020-04/</link>
       <pubDate>Mon, 22 Nov 2021 22:10:00 -0800</pubDate>
       
       <guid>https://kennethjenkins.net/posts/advent-2020-04/</guid>
       <description>&lt;p&gt;This year&amp;rsquo;s &lt;a href=&#34;https://adventofcode.com/&#34;&gt;Advent of Code&lt;/a&gt; is just around the
corner! I had a lot of fun with these puzzles last year, so in honor of the
occasion I thought I would share some of the things I learned over the course
of the challenge.&lt;/p&gt;
&lt;p&gt;For the unfamiliar, Advent of Code is an &lt;a href=&#34;https://en.wikipedia.org/wiki/Advent_calendar&#34;&gt;Advent
calendar&lt;/a&gt; where a new
programming puzzle unlocks each day of December (from the 1st through the 25th).
Each puzzle has a part 1 and a part 2, where part 2 is usually a more
complicated variation on the first part. Puzzles involve writing some code
to compute an answer (usually based on some input file data).&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t intend to talk about every single puzzle, or even to give complete
solutions to a few, but rather to share a few things I found interesting.&lt;/p&gt;
&lt;aside class=&#34;notice&#34;&gt;
  &lt;i class=&#34;fas fa-info-circle&#34;&gt;&lt;/i&gt;
  &lt;div&gt;If you &lt;em&gt;do&lt;/em&gt; want to see a complete walkthrough from an expert programmer, I
should mention Peter Norvig has a &lt;a href=&#34;https://github.com/norvig/pytudes/blob/main/ipynb/Advent-2020.ipynb&#34;&gt;Python
noteback&lt;/a&gt;
documenting his entire puzzle-solving code.)&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;I used Python for all of last year&amp;rsquo;s puzzles. As the high-level structure of
each puzzle was fairly similar (read input, compute something for part 1,
compute something else for part 2), I got in the habit of starting each day
from a skeleton file that looked something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(lines):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;part1&lt;/span&gt;(p):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;part2&lt;/span&gt;(p):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  parsed_input &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parse(sys&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stdin&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;readlines())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  print(part1(parsed_input))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  print(part2(parsed_input))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Usually, I would want to take the raw input and translate it into some other
format that would be more convenient, so this is what the &lt;code&gt;parse()&lt;/code&gt; method was
for: maybe I needed to convert from strings to numbers, or maybe build a map
of values indexed by (X, Y) coordinates.&lt;/p&gt;
&lt;p&gt;When I got to &lt;a href=&#34;https://adventofcode.com/2020/day/4&#34;&gt;Day 4&lt;/a&gt;, I stumbled a bit
with the &lt;code&gt;parse()&lt;/code&gt; method. Most of the input files so far involved a list of
items, with one item per line. This puzzle&amp;rsquo;s input, however, groups of
&lt;code&gt;key:value&lt;/code&gt; pairs that could span multiple lines, with a blank line in between
separate items.&lt;/p&gt;
&lt;p&gt;This was the example input:&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ecl:gry pid:860033327 eyr:2020 hcl:#fffffd
byr:1937 iyr:2017 cid:147 hgt:183cm

iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884
hcl:#cfa07d byr:1929

hcl:#ae17e1 iyr:2013
eyr:2024
ecl:brn pid:760753108 byr:1931
hgt:179cm

hcl:#cfa07d eyr:2025 pid:166559648
iyr:2011 ecl:brn hgt:59in
&lt;/code&gt;&lt;/pre&gt;&lt;!-- raw HTML omitted --&gt;
&lt;p&gt;(These were supposed to represent passport data fields: &lt;code&gt;ecl&lt;/code&gt; was &amp;ldquo;eye color&amp;rdquo;,
&lt;code&gt;pid&lt;/code&gt; was &amp;ldquo;passport ID number&amp;rdquo;, and so on.)&lt;/p&gt;
&lt;p&gt;To make the actual puzzle solving code easier, I wanted to represent each
passport as a dictionary, mapping keys (e.g. &lt;code&gt;&#39;ecl&#39;&lt;/code&gt;, &lt;code&gt;&#39;pid&#39;&lt;/code&gt;) to their values
(&lt;code&gt;&#39;gry&#39;&lt;/code&gt;, &lt;code&gt;&#39;860033327&#39;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;My &lt;code&gt;parse()&lt;/code&gt; method started with a list of input lines, but I needed to make
sure to group these lines together up to the next blank line. I found myself
wanting to use something like Python&amp;rsquo;s string
&lt;a href=&#34;https://docs.python.org/3/library/stdtypes.html#str.split&#34;&gt;&lt;code&gt;split()&lt;/code&gt;&lt;/a&gt; method.
This method takes a string and &amp;ldquo;splits&amp;rdquo; it at any whitespace, returning
a new list of strings.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Well, hello there&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Well,&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;there&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I wanted to take my list of input lines and &amp;ldquo;split&amp;rdquo; it at every blank line;
something like &lt;code&gt;lines.split(&#39;\n&#39;)&lt;/code&gt;. But &lt;code&gt;lines&lt;/code&gt; was a list, not a string, and
I didn&amp;rsquo;t see any method like this in the Python standard library.&lt;/p&gt;
&lt;p&gt;After some Googling, I found this &lt;a href=&#34;https://stackoverflow.com/questions/14529523/python-split-for-lists&#34;&gt;Stack Overflow
question&lt;/a&gt;
wondering the same thing: is there a &lt;code&gt;split()&lt;/code&gt; for lists?&lt;/p&gt;
&lt;p&gt;One of the answers pointed out that we can use &lt;code&gt;itertools.groupby()&lt;/code&gt; for this,
something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;listsplit&lt;/span&gt;(l, delim):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [list(group) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; is_delim, group &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            itertools&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;groupby(l, key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; e: e &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; delim) &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; is_delim]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It took me a minute to understand how this worked, so I thought I&amp;rsquo;d try to
explain it here.&lt;/p&gt;
&lt;h2 id=&#34;what-does-itertoolsgroupby-do&#34;&gt;What does &lt;code&gt;itertools.groupby()&lt;/code&gt; do?&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&#34;https://docs.python.org/3/library/itertools.html#itertools.groupby&#34;&gt;Python documentation&lt;/a&gt;
has this to say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;itertools.groupby(iterable, key=None)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Make an iterator that returns consecutive keys and groups from the iterable.
The key is a function computing a key value for each element. If not
specified or is &lt;code&gt;None&lt;/code&gt;, key defaults to an identity function and returns the
element unchanged. Generally, the iterable needs to already be sorted on the
same key function.&lt;/p&gt;
&lt;p&gt;The operation of &lt;code&gt;groupby()&lt;/code&gt; is similar to the &lt;code&gt;uniq&lt;/code&gt; filter in Unix. It
generates a break or new group every time the value of the key function
changes (which is why it is usually necessary to have sorted the data using
the same key function). That behavior differs from SQL’s GROUP BY which
aggregates common elements regardless of their input order.&lt;/p&gt;
&lt;p&gt;The returned group is itself an iterator that shares the underlying iterable
with &lt;code&gt;groupby()&lt;/code&gt;. Because the source is shared, when the &lt;code&gt;groupby()&lt;/code&gt; object is
advanced, the previous group is no longer visible. So, if that data is needed
later, it should be stored as a list.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I found this a little hard to follow, especially because for our use case
we don&amp;rsquo;t need or want to sort the input.&lt;/p&gt;
&lt;p&gt;If we ignore the &lt;code&gt;key&lt;/code&gt; parameter for now, the &lt;code&gt;groupby()&lt;/code&gt; method essentially
takes a sequence of elements and looks for consecutive elements that are the
same. It returns an iterator of these individual groups.&lt;/p&gt;
&lt;aside class=&#34;notice&#34;&gt;
  &lt;i class=&#34;fas fa-info-circle&#34;&gt;&lt;/i&gt;
  &lt;div&gt;&lt;p&gt;What&amp;rsquo;s an &lt;strong&gt;iterator&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;An iterator is an object that returns successive elements one by one.
In Python, this is any object that defines a special &lt;code&gt;__next__()&lt;/code&gt; method
according to a certain contract.&lt;/p&gt;
&lt;p&gt;An &lt;strong&gt;iterable&lt;/strong&gt; is an object (often a collection, like a list or map) that
can return an &lt;strong&gt;iterator&lt;/strong&gt; of its elements.&lt;/p&gt;
&lt;p&gt;For more details see the &lt;a href=&#34;https://docs.python.org/3/glossary.html#term-iterable&#34;&gt;Python glossary
entries&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;A diagram might help. Say we start with a list with some repeated elements.
These could be strings, numbers, or what have you, so I&amp;rsquo;ll use labeled boxes
to represent the elements:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://kennethjenkins.net/img/groupby-diagram-1.svg&#34; alt=&#34;diagram of input list&#34;&gt;&lt;/p&gt;
&lt;p&gt;Calling &lt;code&gt;groupby()&lt;/code&gt; with this list would group the elements like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://kennethjenkins.net/img/groupby-diagram-2.svg&#34; alt=&#34;groups from input list&#34;&gt;&lt;/p&gt;
&lt;p&gt;(Here I&amp;rsquo;ve used angle brackets to represent an iterator that will return the
enclosed items.)&lt;/p&gt;
&lt;p&gt;Note that even though B appears twice, because they aren&amp;rsquo;t consecutive in the
input, they are part of separate groups in the output.&lt;/p&gt;
&lt;p&gt;However, this isn&amp;rsquo;t quite an accurate picture. The &lt;code&gt;groupby()&lt;/code&gt; method doesn&amp;rsquo;t
just return an iterator of each group of elements. It actually returns an
iterator of tuples, where each tuple contains the grouped element followed by
the group iterator itself. So I guess the output diagram should really look
something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://kennethjenkins.net/img/groupby-diagram-3.svg&#34; alt=&#34;example groupby() output&#34;&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a lot of brackets&amp;hellip;.&lt;/p&gt;
&lt;p&gt;But let&amp;rsquo;s come back to that &lt;code&gt;key&lt;/code&gt; function. This function lets us group
elements in different ways. Let&amp;rsquo;s say we had a function that returned 1 for
a C element and 0 for any other element. If we called &lt;code&gt;groupby()&lt;/code&gt; with this
key function, it would apply this function to each element and then group
according to the results. We could represent that something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://kennethjenkins.net/img/groupby-diagram-4.svg&#34; alt=&#34;groupby() example with key function&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now the first B is grouped with all of the A&amp;rsquo;s, because the key function gave
the same result for all of these elements. (And note that the first element of
each tuple is now the 0 or 1 from the key function.)&lt;/p&gt;
&lt;h2 id=&#34;back-to-listsplit&#34;&gt;Back to &lt;code&gt;listsplit()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;For the list-splitting case, the key insight (ha!) is that we can use a key
function that returns &lt;code&gt;True&lt;/code&gt; if an element matches our delimiter, and &lt;code&gt;False&lt;/code&gt;
otherwise. This way &lt;code&gt;groupby()&lt;/code&gt; will return all consecutive non-delimiter
elements in one chunk.&lt;/p&gt;
&lt;p&gt;Unfortunately, all of our delimiters get returned as an output group too.
But, we can filter out these group by checking the key function output values.&lt;/p&gt;
&lt;p&gt;Finally, we also need to capture all of the elements from the group iterators,
which we can do by making a new list out of each iterator.&lt;/p&gt;
&lt;p&gt;Putting it all together, this brings us to the &lt;code&gt;listsplit()&lt;/code&gt; method from before:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://kennethjenkins.net/img/listsplit-annotated-export.svg&#34; alt=&#34;annoated listsplit() definition&#34;&gt;&lt;/p&gt;
&lt;p&gt;And then using this method, the passport parsing routine is a snap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(lines):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [parse_passport(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(p)) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; p &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; listsplit(lines, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_passport&lt;/span&gt;(p):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; dict(kv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;:&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; kv &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; p&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have a list of dictionaries that are easy to query for individual
data fields.&lt;/p&gt;
&lt;h2 id=&#34;addendum&#34;&gt;Addendum&lt;/h2&gt;
&lt;p&gt;The real moral of this story might be that it&amp;rsquo;s important not to get too
focused on one approach.&lt;/p&gt;
&lt;p&gt;As I was writing up this reflection, I realized that I don&amp;rsquo;t really even need a
&lt;code&gt;listsplit()&lt;/code&gt; method at all.&lt;/p&gt;
&lt;p&gt;If I hadn&amp;rsquo;t had &lt;code&gt;readlines()&lt;/code&gt; in my solution skeleton already, I might have seen
that we can go straight to the input chunks I wanted by reading the entire
file at once and using &lt;code&gt;split(&#39;\n\n&#39;)&lt;/code&gt; to divide the input at blank lines.&lt;/p&gt;
&lt;p&gt;I could replace the whole &lt;code&gt;parse()&lt;/code&gt; method with something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse&lt;/span&gt;(input):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [dict(kv&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;:&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; kv &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; p&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split()) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; p &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; input&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
     </item>
   
     <item>
       <title>A misleading error when using gRPC with Go and nginx</title>
       <link>https://kennethjenkins.net/posts/go-nginx-grpc/</link>
       <pubDate>Mon, 28 Dec 2020 15:57:00 -0800</pubDate>
       
       <guid>https://kennethjenkins.net/posts/go-nginx-grpc/</guid>
       <description>&lt;p&gt;I wanted to document an issue I ran into while trying to write a gRPC service
in Go, in case someone else just so happens to run into the same problem.&lt;/p&gt;
&lt;p&gt;The scenario was this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I had a web service written in Go&lt;/li&gt;
&lt;li&gt;serving both HTTP traffic and &lt;a href=&#34;https://grpc.io/about/&#34;&gt;gRPC&lt;/a&gt; traffic in
plaintext on the same port&lt;/li&gt;
&lt;li&gt;using nginx as a reverse proxy to provide TLS.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This might seem a bit contrived, but it came about fairly naturally. I was
working on a web service written in Go, and then I wanted to add a gRPC API to
it as well, for use by a mobile client. Rather than setting up a new sub-domain
or messing with firewall rules to expose another port, I had the Go service
accept gRPC requests on the same port that it was already listening on. Then,
some time later I decided it would be easier to let nginx handle TLS rather
than figuring out how to set up certificate rotation in the Go service itself,
and so I had nginx terminate the TLS connection and proxy requests to the Go
service over plain HTTP.&lt;/p&gt;
&lt;aside class=&#34;notice&#34;&gt;
  &lt;i class=&#34;fas fa-info-circle&#34;&gt;&lt;/i&gt;
  &lt;div&gt;If you&amp;rsquo;d like to follow along on your own machine, I&amp;rsquo;ve set up a toy example you
can run via Docker here:
&lt;a href=&#34;https://github.com/kenjenkins/code-samples/tree/master/go-nginx-grpc&#34;&gt;https://github.com/kenjenkins/code-samples/tree/master/go-nginx-grpc&lt;/a&gt;. You&amp;rsquo;ll
need to have docker and openssl (to generate a self-signed TLS certificate).&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;the-error-message&#34;&gt;The error message&lt;/h2&gt;
&lt;p&gt;However, when I attempted this, all my gRPC requests stopped working, and I
started seeing errors something like this in the nginx logs:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;2020/12/13 01:14:04 [error] 29#29: *3 upstream sent too large http2 frame:
4740180 while reading response header from upstream, client: 172.21.0.1,
server: , request: &amp;#34;POST /Service/Method HTTP/2.0&amp;#34;, upstream:
&amp;#34;grpc://172.21.0.2:8080&amp;#34;, host: &amp;#34;localhost:8080&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;My attempts to Google this error message didn&amp;rsquo;t turn up much. I found a
&lt;a href=&#34;https://andrewlock.net/fixing-nginx-upstream-sent-too-big-header-error-when-running-an-ingress-controller-in-kubernetes/&#34;&gt;blog post&lt;/a&gt;
about a similar-sounding error, but I wasn&amp;rsquo;t using Kubernetes, and changing the
&lt;code&gt;proxy_buffers&lt;/code&gt; settings in nginx (the recommended fix) didn&amp;rsquo;t seem to have any
effect.&lt;/p&gt;
&lt;p&gt;So what was going on here?&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t make much progress towards understanding this, until I came across the
&lt;a href=&#34;https://golang.org/pkg/net/http/httputil/#DumpRequest&#34;&gt;&lt;code&gt;httputil.DumpRequest()&lt;/code&gt;&lt;/a&gt;
method. When I used this method to log an incoming request in my main handler
method, I saw this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;PRI * HTTP/2.0
Connection: close
 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I&amp;rsquo;d never heard of the PRI method, but at least this was something easy enough
to search for online. I found that this comes from the HTTP/2 &amp;ldquo;&lt;a href=&#34;https://httpwg.org/specs/rfc7540.html#ConnectionHeader&#34;&gt;client
connection preface&lt;/a&gt;&amp;rdquo;.
The preface is sent by the client at the start of an HTTP/2 connection, and it
begins with a fixed byte sequence consisting of:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;(where &lt;code&gt;\r&lt;/code&gt; and &lt;code&gt;\n&lt;/code&gt; indicate the carriage return and newline characters,
respectively). This byte sequence was specifically chosen to look like an
invalid HTTP/1.1 request, so that servers which don&amp;rsquo;t understand HTTP/2 will
error out right away.&lt;/p&gt;
&lt;p&gt;But why did my Go service start treating this as a request in its own right?&lt;/p&gt;
&lt;h2 id=&#34;http2-and-go&#34;&gt;HTTP/2 and Go&lt;/h2&gt;
&lt;p&gt;When I added the gRPC service on the same port as the rest of the web service, I
followed a technique mentioned in the &lt;a href=&#34;https://pkg.go.dev/google.golang.org/grpc#Server.ServeHTTP&#34;&gt;grpc-go
documentation&lt;/a&gt;. It
comes down to a check that the incoming request is using HTTP/2 (as gRPC
is implemented using HTTP/2), and has a &lt;code&gt;Content-Type&lt;/code&gt; header whose value
begins with &lt;code&gt;application/grpc&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;blockquote&gt;
&lt;p&gt;To share one port (such as 443 for https) between gRPC and an existing
http.Handler, use a root http.Handler such as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ProtoMajor&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strings&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;HasPrefix&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Header&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/grpc&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;grpcServer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ServeHTTP&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;w&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;yourMux&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ServeHTTP&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;w&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;r&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/blockquote&gt;

  &lt;figcaption&gt;(from &lt;a href=&#34;https://pkg.go.dev/google.golang.org/grpc#Server.ServeHTTP&#34;&gt;https://pkg.go.dev/google.golang.org/grpc#Server.ServeHTTP&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Unfortunately, I either skipped right over the previous paragraph in the
documentation, or I simply forgot about it by the time I tried to move the TLS
handling into the nginx reverse proxy:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The provided HTTP request must have arrived on an HTTP/2 connection. When
using the Go standard library&amp;rsquo;s server, practically this means that the
Request must also have arrived over TLS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the core of the issue. The HTTP server in Go&amp;rsquo;s standard library does
not support plaintext HTTP/2 by default.&lt;/p&gt;
&lt;p&gt;(Why doesn&amp;rsquo;t Go support this by default?  Well, HTTP/2 on the web effectively
requires encryption, in the sense that currently all major web browsers will
use HTTP/2 only with https:// URLs.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; If Go&amp;rsquo;s standard library HTTP server is
mainly used for writing web services, then this seems like a reasonable design
decision.)&lt;/p&gt;
&lt;p&gt;But what does it mean to &amp;ldquo;support plaintext HTTP/2&amp;rdquo;? Why does the HTTP/2
protocol care if the underlying transport is encrypted or not?&lt;/p&gt;
&lt;h2 id=&#34;starting-an-http2-connection&#34;&gt;Starting an HTTP/2 connection&lt;/h2&gt;
&lt;p&gt;Before all of this, I was only vaguely aware that HTTP/2 existed. I had a basic
understanding of HTTP/1 from a networking course in college, but HTTP/2 is quite
different. To begin with, while HTTP/1 is a text-based protocol, HTTP/2 is a
binary protocol. One implication of this is that HTTP/2 is not
backwards-compatible with HTTP/1, and so clients and servers need some way to
signal to each other that they support HTTP/2. There are three main ways to do
this.&lt;/p&gt;
&lt;p&gt;The first way is specific to https. As part of any https connection, the client
and server first engage in something called a &amp;ldquo;TLS handshake&amp;rdquo; to agree on
encryption parameters and other connection settings. When HTTP/2 was
standardized, an extension was also added to the TLS protocol called
&amp;ldquo;Application-Layer Protocol Negotiation&amp;rdquo; (ALPN) to let the client and server
decide which protocol to use for the connection.  The client can use this
extension to indicate that it supports both HTTP/1 and HTTP/2, and then the
server will indicate back to the client which of these options it has decided
to use.&lt;/p&gt;
&lt;p&gt;However, for plaintext http, there is no corresponding negotiation step when
starting a connection. This brings us to the second way to signal support for
HTTP/2: using the connection &amp;ldquo;upgrade&amp;rdquo; feature of HTTP/1.1. A client can send
an HTTP/1 request that includes the header &lt;code&gt;Upgrade: h2c&lt;/code&gt; (along with a few
other required headers&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;) to signal that it can speak HTTP/2.&lt;/p&gt;
&lt;aside class=&#34;notice&#34;&gt;
  &lt;i class=&#34;fas fa-info-circle&#34;&gt;&lt;/i&gt;
  &lt;div&gt;&lt;code&gt;h2c&lt;/code&gt; is an &amp;ldquo;upgrade token&amp;rdquo; (signifying &lt;strong&gt;H&lt;/strong&gt;TTP/&lt;strong&gt;2&lt;/strong&gt; in &lt;strong&gt;c&lt;/strong&gt;leartext). You
can see all the registered upgrade tokens at &lt;a href=&#34;https://www.iana.org/assignments/http-upgrade-tokens/http-upgrade-tokens.xhtml&#34;&gt;https://www.iana.org/assignments/http-upgrade-tokens/http-upgrade-tokens.xhtml&lt;/a&gt;.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;The third way to use HTTP/2 is for a client to have &amp;ldquo;prior knowledge&amp;rdquo; that
a particular server supports it. Essentially, if some out-of-band signaling
mechanism exists, a client can start speaking HTTP/2 directly.&lt;/p&gt;
&lt;p&gt;This last method is the one that nginx uses for gRPC requests. As the gRPC
protocol is implemented using HTTP/2, this constitutes &amp;ldquo;prior knowledge&amp;rdquo; that
any gRPC endpoint must support HTTP/2. When nginx connects to the Go service,
it immediately begins speaking HTTP/2, starting with the client connection
preface.&lt;/p&gt;
&lt;p&gt;However, because the Go standard library HTTP supports only the first method
(ALPN), when it receives the HTTP/2 client connection preface, it parses this
as an HTTP/1 request, and responds with an HTTP/1 response. When nginx receives
this HTTP/1 response, this leads to the original error.&lt;/p&gt;
&lt;p&gt;But why does the error message read &amp;ldquo;upstream sent too large http2 frame&amp;rdquo;?
What&amp;rsquo;s that about?&lt;/p&gt;
&lt;h2 id=&#34;http2-message-framing&#34;&gt;HTTP/2 message framing&lt;/h2&gt;
&lt;p&gt;To answer that question, we need to understand just a little bit more about
the HTTP/2 protocol:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP/2 messages consist of a number of &amp;ldquo;frames.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Each frame begin with a fixed-length header.&lt;/li&gt;
&lt;li&gt;The first 3 bytes of this header is a Length field.&lt;/li&gt;
&lt;li&gt;Unless otherwise indicated, the maximum value of this Length field is 16,384.
(cf. &lt;a href=&#34;https://httpwg.org/specs/rfc7540.html#FrameHeader&#34;&gt;RFC 7540 §4.1&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When nginx starts reading the erroneous HTTP/1 response from the Go service,
it will try to start parsing it as an HTTP/2 message frame. Now, the first line
of an HTTP/1 response starts contains the HTTP version and status code, for
example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;HTTP/1.1 400 Bad Request&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Trying to parse these bytes as the start of an HTTP/2 frame results in reading
the first three bytes (&amp;ldquo;HTT&amp;rdquo;) as the frame length. In hexadecimal, these bytes
are 0x485454, or 4,740,180 in decimal. Obviously this exceeds the maximum frame
length of 16,384, and so this explains why nginx thinks the frame is too large.&lt;/p&gt;
&lt;p&gt;The number 4740180 even shows up in the error message, and this could have been
an important clue, but I did not realize its significance at the time.  (This
is the same exact idea as the &lt;a href=&#34;https://rachelbythebay.com/w/2016/02/21/malloc/&#34;&gt;magic number
1213486160&lt;/a&gt;, but with only 3
bytes instead of all 4 of &amp;ldquo;HTTP&amp;rdquo;.)&lt;/p&gt;
&lt;h2 id=&#34;putting-it-all-together&#34;&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;To recap:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When nginx connects to an upstream gRPC server, it begins speaking HTTP/2
directly, starting with the HTTP/2 client connection preface. (The use of
gRPC is the &amp;ldquo;prior knowledge&amp;rdquo; that the server must support HTTP/2.)&lt;/li&gt;
&lt;li&gt;The connection preface begins with a fixed byte sequence chosen to look
like like an HTTP/1 request, but with an invalid method and version.&lt;/li&gt;
&lt;li&gt;The Go &lt;code&gt;http&lt;/code&gt; standard library package does not recognize plaintext HTTP/2
requests, and so the Go service misinterprets the client connection
preface as the start of an HTTP/1 request, and responds with an HTTP/1
response.&lt;/li&gt;
&lt;li&gt;Then nginx attempts to parse the HTTP/1 response as an HTTP/2 frame, which
leads to the original error message.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The nginx error message is misleading because it talks about an HTTP/2 frame
being too large, but the real problem is that the Go service wasn&amp;rsquo;t speaking
HTTP/2 at all.&lt;/p&gt;
&lt;p&gt;So what is the fix?&lt;/p&gt;
&lt;p&gt;Luckily, there&amp;rsquo;s an easy way to add plaintext HTTP/2 support to the Go
standard library HTTP server, using the
&lt;a href=&#34;https://pkg.go.dev/golang.org/x/net/http2/h2c&#34;&gt;golang.org/x/net/http2/h2c&lt;/a&gt;
package.&lt;/p&gt;
&lt;p&gt;If the default &lt;a href=&#34;https://pkg.go.dev/golang.org/x/net/http2#Server&#34;&gt;&lt;code&gt;http2.Server&lt;/code&gt;&lt;/a&gt;
options are acceptable, this can be as simple as replacing something like
this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;http&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ListenAndServe&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;addr&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;handler&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;with this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;http&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ListenAndServe&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;addr&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;h2c&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewHandler&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;handler&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;http2&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Server&lt;/span&gt;{}))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Of course, another option would be to move the gRPC portion of the Go service
onto a different port, and use the gRPC library&amp;rsquo;s built-in server (which does
recognize plaintext HTTP/2 requests just fine). If I had simply done that to
begin with, I wouldn&amp;rsquo;t have run in to this issue.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Further reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developers.google.com/web/fundamentals/performance/http2&#34;&gt;&amp;ldquo;Introduction to HTTP/2&amp;rdquo;&lt;/a&gt; from Google&amp;rsquo;s Web Fundamentals series&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://http2.github.io/faq/&#34;&gt;HTTP/2 Frequently Asked Questions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://httpwg.org/specs/rfc7540.html&#34;&gt;RFC 7540&lt;/a&gt;
(the full HTTP/2 specification)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://tools.ietf.org/html/rfc7301&#34;&gt;RFC 7301&lt;/a&gt; (the TLS ALPN extension
specification)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/&#34;&gt;&amp;ldquo;What Happens in a TLS Handshake?&amp;rdquo;&lt;/a&gt;
from Cloudflare&lt;/li&gt;
&lt;li&gt;What&amp;rsquo;s with the letters &amp;lsquo;PRI&amp;rsquo; and &amp;lsquo;SM&amp;rsquo; in the client connection preface? &lt;br&gt;
&lt;a href=&#34;https://blog.jgc.org/2015/11/the-secret-message-hidden-in-every.html&#34;&gt;&amp;ldquo;The secret message hidden in every HTTP/2 connection&amp;rdquo;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;cf. &lt;a href=&#34;https://en.wikipedia.org/wiki/HTTP/2#Encryption&#34;&gt;https://en.wikipedia.org/wiki/HTTP/2#Encryption&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Specifically, the &lt;code&gt;HTTP2-Settings&lt;/code&gt; and &lt;code&gt;Connection&lt;/code&gt; headers (cf. &lt;a href=&#34;https://httpwg.org/specs/rfc7540.html#discover-http&#34;&gt;RFC
7540 §3.2&lt;/a&gt;).&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
     </item>
   
 </channel>
</rss>
