Zulip Chat Archive

Stream: general

Topic: Advent of Code 2024 - Day 5


Marcelo Fornet (Dec 05 2024 at 09:56):

Here's my solution. Please post your own solution!

https://github.com/mfornet/advent-of-code-2024/blob/main/Aoc2024/Day05.lean

The solution finds the topological sort of the given graph, but this was not necessary (it was only clear in hindsight, I don't think the text gives any hint about the structure of the test cases).

Marcelo Fornet (Dec 05 2024 at 09:57):

Cross-posting from discord

I want to prove topological sort always terminate, i.e remove the partial modifier from here: https://github.com/mfornet/advent-of-code-2024/blob/main/Aoc2024/Day05.lean#L68-L76

The condition that this always decrease is the pair <sum of indegrees, size of the queue>. Can anyone help me get started on how do I indicate Lean that this is the decreasing relationship, I'll provide the proof myself, it is just about how to get the statement.

I can't fully grasp how to do it by seeing examples of termination_by.

Matt Russell (Dec 05 2024 at 15:03):

https://github.com/mdr/aoc-2024/blob/main/Aoc2024/Day05/Solve.lean

Ayhon (Dec 06 2024 at 23:20):

Ok, this time it took me a bit longer than usual. I missunderstood the problem statement and programmed something more complicated than needed. It's still not the cleanest, but it definitely works

import Std.Internal.Parsec
import Std.Internal.Parsec.String

namespace Day5
open Std.Internal.Parsec
open Std.Internal.Parsec.String

structure Input where
  edges: List (Nat × Nat)
  reports: Array (Array Nat)
  deriving Repr, Inhabited

def p_order := do
  let lesser <- digits
  skipChar '|'
  let greater <- digits
  return (lesser, greater)

def p_report := do
  let p_optional_comma := attempt (skipChar ',') <|> pure ()
  let list <- many (digits <* p_optional_comma)
  return list

def parse_input(content: String): Option (Input) :=
  let parser := do
    let orders <- many (p_order <* skipChar '\n')
    skipChar '\n'
    let reports <- many (p_report <* skipChar '\n')
    eof
    return {
      edges := orders.toList,
      reports := reports.filter (not  Array.isEmpty)
    : Input}

  match parser content.iter with
  | .success _ res => some res
  | .error _ _ => none

namespace Part1
  def Array.toSlidingWindow[Inhabited α](width: Nat)(ls: Array α): Array (Vector α width) :=
    if ls.size < width then
      #[]
    else
      let n := ls.size + 1 - width
      Array.ofFn fun (start, _⟩: Fin n) =>
        Vector.ofFn fun (offset, _⟩: Fin width) =>
            ls[start + offset]

  def valid(edges: List (Nat × Nat))(report: Array Nat): Bool :=
    open Part1 in
    report.toSlidingWindow 2
      |>.all fun
        | #v[u,v] => ¬ edges.contains (v,u)
        | _ => false

  def middle(report: Array Nat): Nat := report[report.size/2]!

  def solve'(info: Input): Int :=
    info.reports
      |>.toList
      |>.filter (valid info.edges ·)
      |>.map middle
      |>.sum

  def solve: Option (Input) -> Int
  | .some input => solve' input
  | .none => -1
end Part1


namespace Part2
  def fix(edges: List (Nat × Nat))(report: Array Nat): Array Nat := Id.run do
    let mut res := report
    for i in [1:report.size] do
      for j in [0:i] do
        if edges.contains (res[j]!, res[i]!) then
          res := res.swapIfInBounds i j
    return res

  def solve'(info: Input): Int :=
    info.reports
      |>.filter (¬ Part1.valid info.edges ·)
      |>.map (Part1.middle  fix info.edges)
      |>.toList
      |>.sum

  def solve: Option (Input) -> Int
  | .some input => solve' input
  | .none => -1
end Part2

Ayhon (Dec 06 2024 at 23:22):

I do have some questions on how to simplify my Part1.valid function. I would assume that Lean can assume that a Vector a 2 has only 2 entries, why do I need to specify the other branch though?

  def valid(edges: List (Nat × Nat))(report: Array Nat): Bool :=
    open Part1 in
    report.toSlidingWindow 2
      |>.all fun
        | #v[u,v] => ¬ edges.contains (v,u)
        | _ => false

At least I couldn't manage to make it work without


Last updated: May 02 2025 at 03:31 UTC