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


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="the_wildgoose" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  the_wildgoose
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I would be interested to see your implementation of Performance.memoize()?</p>
<p>Do you need to do anything to avoid problems when running say unit tests across many days/years of problems? Say in my case whenever I run mix test, it actually runs all the tests for all the tests I’ve been playing. I’m wondering how you partition your cache?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="349886" 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-11/68028/32">Post #31</a>
	                </div>
	            </div>
              <div id="likers-container-349886" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="349886"
                     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 #31"></div>
  </section>
</div>
    <div class="postbit" id="349888" data-post-id="349888">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="the_wildgoose" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  the_wildgoose
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>My previous was meant to be a reply to antoine-duchenet</p>
<p>I’m obviously an idiot, I have clicked reply on his post twice and it doesn’t seem to cause a reply to his actual post, nor can I figure out how to delete this second comment??</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="349888" 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-11/68028/33">Post #32</a>
	                </div>
	            </div>
              <div id="likers-container-349888" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="349888"
                     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 #32"></div>
  </section>
</div>
    <div class="postbit" id="349889" data-post-id="349889">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I’m using ETS to memoize.</p>
<p>first I create a named ETS table</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">:ets.new(:my_table, [:named_table])
</code></pre>
<p>then the memoize function</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def memoize(key, fun) do
  case :ets.lookup(@table, key) do
    [{^key, val}] -&gt; val
    [] -&gt; fun.() |&gt; tap(&amp;:ets.insert(@table, {key, &amp;1}))
  end
end
</code></pre>
<p>which I use like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def count_stones(stone, n) do
  memoize({stone, n}, fn -&gt;
    case blink(stone) do
      [s1, s2] -&gt; count_stones(s1, n - 1) + count_stones(s2, n - 1)
      stone -&gt; count_stones(stone, n - 1)
    end
  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="349889" 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-11/68028/34">Post #33</a>
	                </div>
	            </div>
              <div id="likers-container-349889" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="349889"
                     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 #33"></div>
  </section>
</div>
    <div class="postbit" id="349928" data-post-id="349928">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="antoine-duchenet" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/antoine-duchenet/120/27136_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  antoine-duchenet
                    <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">
								<blockquote>
<p>I would be interested to see your implementation of Performance.memoize()?</p>
</blockquote>
<p>Here it is :</p>
<pre data-code-wrap="ex"><code class="lang-ex">defmodule Performance do
  def memoize(key, f) do
    with nil &lt;- Process.get(key), do: tap(f.(), &amp;Process.put(key, &amp;1))
  end
end
</code></pre>
<p>I’m only using this implementation for AoC exercises, I don’t use it in production. It uses the process dictionary, which will have flaws if you need to share this cache between multiple BEAM processes.</p>
<p>You can still wrap it in a agent or use ETS if you need concurrency, but I never needed concurrency to solve AoC challenges (IMHO, if you do, it probably means that you missed something more elaborate than raw concurrency <img src="https://forum.elixirforum.com/images/emoji/apple/smiley_cat.png?v=15" title=":smiley_cat:" class="emoji" alt=":smiley_cat:" loading="lazy" width="20" height="20"> ).</p>
<p>Please note that this is a lazy solution, I could use a Map instead but this gives me a quick and dirty way to add a “global” (to the process) state that I can read and write anywhere !</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="349928" 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-11/68028/35">Post #34</a>
	                </div>
	            </div>
              <div id="likers-container-349928" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="349928"
                     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 #34"></div>
  </section>
</div>
    <div class="postbit" id="351688" data-post-id="351688">
  <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>I hate dynamic programming problems. Didn’t do anything interesting here. ETS for the memoization.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Day11 do
  require Integer

  @test "125 17"

  @real File.read!(__DIR__ &lt;&gt; "/input.txt") |&gt; String.trim()

  @run1 25
  @run2 75

  defp rules(0), do: 1

  defp rules(n) do
    x = :math.log10(n) |&gt; floor()

    if Integer.is_odd(x) do
      divisor = Integer.pow(10, div(x + 1, 2))
      [div(n, divisor), Integer.mod(n, divisor)]
    else
      n * 2024
    end
  end

  def run(mode) do
    data = mode |&gt; input() |&gt; parse()
    :ets.new(:memo, [:named_table, :set, :public])

    part_1(data) |&gt; IO.inspect(label: :part_1)
    part_2(data) |&gt; IO.inspect(label: :part_2)

    :ets.delete(:memo)
  end

  defp input(:test), do: @test
  defp input(:real), do: @real
  defp input(_), do: raise("Please use :test or :real as modes to run.")

  defp parse(input) do
    input
    |&gt; String.split(" ", trim: true)
    |&gt; Enum.map(&amp;String.to_integer/1)
  end

  defp part_1(stones) do
    count_stones(stones, @run1)
  end

  defp part_2(stones) do
    count_stones(stones, @run2)
  end

  defp count_stones(stones, runs) when is_list(stones) do
    stones
    |&gt; Enum.reduce(0, fn stone, acc -&gt; count_stones(stone, runs) + acc end)
  end

  defp count_stones(stone, runs) do
    case :ets.lookup(:memo, {stone, runs}) do
      [] -&gt;
        apply_rules(stone, runs) |&gt; tap(fn res -&gt; :ets.insert(:memo, {{stone, runs}, res}) end)

      [{_, res}] -&gt;
        res
    end
  end

  defp apply_rules(stones, 0) when is_list(stones), do: length(stones)
  defp apply_rules(_, 0), do: 1

  defp apply_rules(stones, runs) when is_list(stones) do
    stones
    |&gt; Enum.reduce(0, fn stone, acc -&gt; acc + count_stones(stone, runs - 1) end)
  end

  defp apply_rules(stone, runs) do
    count_stones(rules(stone), runs - 1)
  end
end

Day11.run(:real)
</code></pre>
<p><a class="mention" href="/u/antoine-duchenet" rel="nofollow">@antoine-duchenet</a> re: not needing concurrency, it’s probably true, but I did test with splitting the stones into chunks to create worker pools in parallel and got 50-60% improvement in performance by <code>:timer.tc</code> excluding the time for input parsing and starting the ETS cache. So, not necessary but can still be useful.</p>
<p>For grins I also tested whether it made a difference what type of table ETS used (set, ordered set, or bag) and it did not seem to make a difference for my implementation. Ordered set may have trended a few percent slower but meaningfully so.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="351688" 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-11/68028/36">Post #35</a>
	                </div>
	            </div>
              <div id="likers-container-351688" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="351688"
                     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>