<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="349921" data-post-id="349921">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="al2o3cr" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/al2o3cr/120/3457_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  al2o3cr
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>This one took longer than it should, because it turned up <a href="https://github.com/erlang/otp/issues/9191" rel="nofollow">a possible bug in <code>:digraph_utils</code></a>.</p>
<p>Day 1:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule PrintQueue do
  def read(filename) do
    filename
    |&gt; File.read!()
    |&gt; String.split("\n\n")
    |&gt; then(fn [rules, pages] -&gt;
      {
        parse_rules(rules),
        parse_pages(pages)
      }
    end)
  end

  defp parse_rules(rules) do
    g = :digraph.new()

    rules
    |&gt; String.split("\n")
    |&gt; Enum.each(fn line -&gt;
      line
      |&gt; String.split("|")
      |&gt; Enum.map(&amp;String.to_integer/1)
      |&gt; then(fn [v1, v2] -&gt;
        :digraph.add_vertex(g, v1)
        :digraph.add_vertex(g, v2)
        :digraph.add_edge(g, v1, v2)
      end)
    end)

    g
  end

  defp parse_pages(pages) do
    pages
    |&gt; String.trim()
    |&gt; String.split("\n")
    |&gt; Enum.map(fn line -&gt;
      line
      |&gt; String.split(",")
      |&gt; Enum.map(&amp;String.to_integer/1)
    end)
  end

  def valid?(page, rules) do
    subgraph = fixed_subgraph(rules, page)

    page
    |&gt; triangle()
    |&gt; Enum.to_list()
    |&gt; Enum.each(fn {v1, v2} -&gt;
      :digraph.add_edge(subgraph, v1, v2)
    end)

    result = :digraph_utils.topsort(subgraph)

    :digraph.delete(subgraph)
 
    result
  end

  defp triangle([_]), do: []
  defp triangle([h | rest]) do
    rest
    |&gt; Enum.map(&amp;{h, &amp;1})
    |&gt; Stream.concat(triangle(rest))
  end

  def edges(g) do
    :digraph.edges(g)
    |&gt; Enum.map(&amp;:digraph.edge(g, &amp;1))
    |&gt; Enum.map(fn {_, a, b, _} -&gt; {a, b} end)
    |&gt; Enum.sort()
  end

  defp fixed_subgraph(g, vs) do
    sg = :digraph_utils.subgraph(g, vs)

    [{_, last_eid}] = :ets.lookup(elem(g, 3), :"$eid")
    ntab = elem(sg, 3)
    :ets.delete(ntab, :"$eid")
    :ets.insert(ntab, {:"$eid", last_eid})

    sg
  end

  def middle(vs) do
    len = length(vs)

    Enum.at(vs, div(len, 2))
  end
end

{rules, pages} = PrintQueue.read("input.txt")

pages
|&gt; Enum.map(&amp;PrintQueue.valid?(&amp;1, rules))
|&gt; Enum.filter(&amp; &amp;1)
|&gt; Enum.map(&amp;PrintQueue.middle/1)
|&gt; Enum.sum()
|&gt; IO.inspect()
</code></pre>
<p>The idea:</p>
<ul>
<li>turn the rules into a <code>:digraph</code> of “x precedes y”</li>
<li>for a list of pages, get a subgraph that only has those pages as vertices</li>
<li>compute all the “x precedes y” relations from the given page list and add them to the subgraph</li>
<li>check the subgraph for a cycle; a rule violation (eg putting 75 before 97 with the rule <code>97|75</code>) will have one</li>
</ul>
<p>Hilariously, part 2 requires LESS effort than part 1 - the “correctly ordered” updates are just a <code>:digraph_utils.topsort</code> of the subgraph!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="349921" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/advent-of-code-2024-day-5/67893/52">Post #51</a>
	                </div>
	            </div>
              <div id="likers-container-349921" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="349921"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #51"></div>
  </section>
</div>
    <div class="postbit" id="349940" data-post-id="349940">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="stevensonmt" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/stevensonmt/120/20503_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  stevensonmt
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Is it correct that the short path must be just the two vertices? I think this is only true for the case where all possible pairs are defined. If you have a data set with just rules “a|b” and “b|c”, the update “a,c” should be valid even though the shortest path on a graph would be [a,b,c]. Am I incorrect?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="349940" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/advent-of-code-2024-day-5/67893/53">Post #52</a>
	                </div>
	            </div>
              <div id="likers-container-349940" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="349940"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #52"></div>
  </section>
</div>
    <div class="postbit" id="349942" data-post-id="349942">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Aetherus" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Aetherus/120/17203_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Aetherus
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>It turned out that though the whole graph is cyclic, the minimal subgraph containing all the vertices in each “update” is acyclic.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="349942" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/advent-of-code-2024-day-5/67893/54">Post #53</a>
	                </div>
	            </div>
              <div id="likers-container-349942" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="349942"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #53"></div>
  </section>
</div>
    <div class="postbit" id="349953" data-post-id="349953">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="stevensonmt" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/stevensonmt/120/20503_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  stevensonmt
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thanks for the tip. I had it in my head the whole graph would have to be acyclic and this caused some edges to get silently rejected.</p>
<p>Turns out <code>:digraph_utils.subgraph</code> and <code>:digraph_utils.topsort</code> made part 2 trivially easy.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="349953" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/advent-of-code-2024-day-5/67893/55">Post #54</a>
	                </div>
	            </div>
              <div id="likers-container-349953" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="349953"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #54"></div>
  </section>
</div>
    <div class="postbit" id="349954" data-post-id="349954">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="adamu" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adamu/120/31482_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  adamu
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="al2o3cr" data-post="52" data-topic="67893">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/al2o3cr/48/3457_2.png" class="avatar"> al2o3cr:</div>
<blockquote>
<p>compute all the “x precedes y” relations</p>
</blockquote>
</aside>
<p>Indeed, as the input is exhaustive, this is all that’s needed (no need to build a graph).</p>
<aside class="quote no-group" data-username="adamu" data-post="11" data-topic="67893">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adamu/48/31482_2.png" class="avatar"> adamu:</div>
<blockquote>
<ul>
<li><code>group_by</code> the pairs to get a map of greater to lesser numbers</li>
</ul>
</blockquote>
</aside> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="349954" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/advent-of-code-2024-day-5/67893/56">Post #55</a>
	                </div>
	            </div>
              <div id="likers-container-349954" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="349954"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #55"></div>
  </section>
</div>
    <div class="postbit" id="350021" data-post-id="350021">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="stevensonmt" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/stevensonmt/120/20503_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  stevensonmt
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>any benefit to <code>sorted?</code> and <code>greater?</code> being anonymous functions rather than defined, named helper functions?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="350021" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/advent-of-code-2024-day-5/67893/57">Post #56</a>
	                </div>
	            </div>
              <div id="likers-container-350021" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="350021"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #56"></div>
  </section>
</div>
    <div class="postbit" id="350050" data-post-id="350050">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="adamu" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adamu/120/31482_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  adamu
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I would need to pass the map out of the input parsing function, and write inline anonymous functions to <code>Enum.reject</code> and <code>Enum.sort</code> that are closures over the map that call the named functions anyway. So 4 functions and an exposed map, rather than 2 functions.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="350050" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/advent-of-code-2024-day-5/67893/58">Post #57</a>
	                </div>
	            </div>
              <div id="likers-container-350050" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="350050"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #57"></div>
  </section>
</div>
    <div class="postbit" id="350969" data-post-id="350969">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="KeithFrost" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/KeithFrost/120/33263_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  KeithFrost
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>LOL came here well after the fact, and discovered that I had been ambitious with my<br>
solution to Part 2: I wrote my own stable topological sort, using <code>:gb_sets</code> to provide<br>
both the container for the edges, and also a heap-like container for ensuring the<br>
stability of the sort.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Graph do

  def add_edge(edges, pred, succ) do
    edges = :gb_sets.add({:fwd, pred, succ}, edges)
    :gb_sets.add({:rev, succ, pred}, edges)
  end

  def delete_edge(edges, pred, succ) do
    edges = :gb_sets.delete_any({:fwd, pred, succ}, edges)
    :gb_sets.delete_any({:rev, succ, pred}, edges)
  end

  defp collect_while(iter, cont?, acc \\ []) do
    case :gb_sets.next(iter) do
      :none -&gt;
        Enum.reverse(acc)
      {elem, iter_next} -&gt;
        if cont?.(elem) do
          collect_while(iter_next, cont?, [elem | acc])
        else
          Enum.reverse(acc)
        end
    end
  end

  def has_edges?(edges, dir, node) do
    iter = :gb_sets.iterator_from({dir, node, -1}, edges)
    case :gb_sets.next(iter) do
      :none -&gt; 
        false
      {{d, x, _y}, _iter_next} -&gt;
        d == dir and x == node
    end
  end

  def get_edges(edges, dir, node) do
    iter = :gb_sets.iterator_from({dir, node, -1}, edges)
    collect_while(iter, fn {d, x, _y} -&gt;
      d == dir and x == node
    end) |&gt;
      Enum.map(fn {_d, _x, y} -&gt; y end)
  end
  
  def graph_edges(update, rules) do
    nodes = MapSet.new(update)
    Enum.reduce(rules, :gb_sets.empty(), fn {bef, afts}, edges -&gt;
      if MapSet.member?(nodes, bef) do
        Enum.filter(afts, fn aft -&gt; MapSet.member?(nodes, aft) end) |&gt;
          Enum.reduce(edges, fn aft, edges -&gt;
            add_edge(edges, bef, aft)
          end)
      else
        edges
      end
    end)
  end

  def terms_order(update, edges) do
    Enum.with_index(update) |&gt;
      Enum.reduce({:gb_sets.empty(), Map.new()}, fn {node, i}, {terms, order} -&gt;
        if has_edges?(edges, :fwd, node) do
          {terms, Map.put(order, node, i)}
        else
          {:gb_sets.add({i, node}, terms), order}
        end
      end)
  end

  def construct_order(terms, order, edges, acc \\ []) do
    if :gb_sets.is_empty(terms) do
      acc
    else
      {{_i, node}, terms} = :gb_sets.take_largest(terms)
      acc = [node | acc]
      preds = get_edges(edges, :rev, node)
      edges = Enum.reduce(preds, edges, fn pred, edges -&gt;
        delete_edge(edges, pred, node)
      end)
      terms = Enum.filter(preds, fn pred -&gt;
          not has_edges?(edges, :fwd, pred) 
        end) |&gt;
        Enum.reduce(terms, fn pred, terms -&gt;
          i = order[pred]
          :gb_sets.add({i, pred}, terms)
        end)
      construct_order(terms, order, edges, acc)
    end
  end

  def reorder(update, rules) do
    edges = graph_edges(update, rules)
    {terms, order} = terms_order(update, edges)
    construct_order(terms, order, edges)
  end
end
</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="350969" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/advent-of-code-2024-day-5/67893/59">Post #58</a>
	                </div>
	            </div>
              <div id="likers-container-350969" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="350969"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-last-post cat-last-post" title="Last post!"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <span class="all-loaded">— All posts loaded —</span>
</div></template></turbo-stream>