summaryrefslogtreecommitdiff
path: root/Common
diff options
context:
space:
mode:
authorAndreas Grois <andi@grois.info>2024-12-22 23:17:05 +0100
committerAndreas Grois <andi@grois.info>2024-12-22 23:17:05 +0100
commit406c23a9f6c6a4c7e03f9aad4131921e589623bc (patch)
treeef4917104f505eb7fda1af9f7b3390c78bc38148 /Common
parentb37fc3089eb0ca8fc37a82ae6d6071b0accbca19 (diff)
Day 17, part 1 (hopefully)
Diffstat (limited to 'Common')
-rw-r--r--Common/Finite.lean81
-rw-r--r--Common/Function.lean3
-rw-r--r--Common/HashSet.lean118
-rw-r--r--Common/Parsing.lean18
4 files changed, 209 insertions, 11 deletions
diff --git a/Common/Finite.lean b/Common/Finite.lean
index 25041a8..95f90a0 100644
--- a/Common/Finite.lean
+++ b/Common/Finite.lean
@@ -1,4 +1,5 @@
import Std.Data.HashSet
+import Common.Nat
class Finite (α : Type u) where
cardinality : Nat
@@ -23,6 +24,68 @@ theorem Finite.injective {α : Type u} [Finite α] {a b : α} : enumerate a = en
have h₆ := Function.comp_apply.subst (h₄ b).symm
h₅.substr $ h₆.substr h₃
+def Finite.tuple_enumerate {α : Type u} [Finite α] {β : Type v} [Finite β] (x : α × β) : Fin ((Finite.cardinality α) * (Finite.cardinality β)) :=
+ let (a, b) := x
+ let idxa := (Finite.enumerate a)
+ let idxb := (Finite.enumerate b)
+ let idx := idxa.val + (Finite.cardinality α) * idxb.val
+ have h : idx < (Finite.cardinality α) * (Finite.cardinality β) :=
+ Nat.two_d_coordinate_to_index_lt_size idxa.isLt idxb.isLt
+ ⟨idx,h⟩
+
+def Finite.tuple_nth {α : Type u} [Finite α] {β : Type v} [Finite β] (idx : Fin ((Finite.cardinality α) * (Finite.cardinality β))) :=
+ let idxav := idx % (Finite.cardinality α)
+ let idxbv := idx / (Finite.cardinality α)
+ have h₁ : Finite.cardinality α > 0 :=
+ if h : 0 = Finite.cardinality α then
+ have : cardinality α * cardinality β = 0 := h.subst (motive := λx↦x*cardinality β = 0) $ Nat.zero_mul (cardinality β)
+ (Fin.cast this idx).elim0
+ else
+ Nat.pos_of_ne_zero (Ne.symm h)
+ let idxa : Fin (Finite.cardinality α) := ⟨idxav, Nat.mod_lt _ h₁⟩
+ let idxb : Fin (Finite.cardinality β):= ⟨idxbv, Nat.div_lt_of_lt_mul idx.isLt⟩
+ (Finite.nth idxa, Finite.nth idxb)
+
+theorem Finite.tuple_nth_inverse_enumerate {α : Type u} [Finite α] {β : Type v} [Finite β] : Finite.tuple_nth (α := α) (β := β) ∘ Finite.tuple_enumerate (α := α) (β := β) = id := by
+ unfold Finite.tuple_enumerate Finite.tuple_nth
+ funext
+ simp
+ congr
+ case h.e_fst x =>
+ simp[Nat.mod_eq_of_lt]
+ rw[←Function.comp_apply (f := Finite.nth) (x := x.fst), Finite.nth_inverse_enumerate]
+ rfl
+ case h.e_snd x =>
+ have h₁ : (↑(Finite.enumerate x.fst) + (Finite.cardinality α) * ↑(Finite.enumerate x.snd)) / Finite.cardinality α = ↑(Finite.enumerate x.snd) := by
+ rw[Nat.add_mul_div_left]
+ simp[Nat.div_eq_of_lt]
+ exact Nat.zero_lt_of_lt (Finite.enumerate x.fst).isLt
+ simp[h₁]
+ rw[←Function.comp_apply (f := Finite.nth) (x := x.snd), Finite.nth_inverse_enumerate]
+ rfl
+
+theorem Finite.tuple_enumerate_inerse_nth {α : Type u} [Finite α] {β : Type v} [Finite β] : Finite.tuple_enumerate (α := α) (β := β) ∘ Finite.tuple_nth (α := α) (β := β) = id := by
+ funext
+ unfold Finite.tuple_enumerate Finite.tuple_nth
+ simp
+ rename_i x
+ rw[Fin.eq_mk_iff_val_eq]
+ simp
+ rw[←Function.comp_apply (f := Finite.enumerate), Finite.enumerate_inverse_nth]
+ rw[←Function.comp_apply (f := Finite.enumerate), Finite.enumerate_inverse_nth]
+ simp[Nat.mod_add_div]
+
+instance {α : Type u} [Finite α] {β : Type v} [Finite β] : Finite (Prod α β) where
+ cardinality := (Finite.cardinality α) * (Finite.cardinality β)
+ enumerate := Finite.tuple_enumerate
+ nth := Finite.tuple_nth
+ enumerate_inverse_nth := Finite.tuple_enumerate_inerse_nth
+ nth_inverse_enumerate := Finite.tuple_nth_inverse_enumerate
+
+theorem Finite.forall_nth {α : Type u} [Finite α] (p : α → Prop) (h₁ : ∀(o : Fin (Finite.cardinality α)), p (Finite.nth o)) : ∀(a : α), p a := λa↦
+ have : p ((nth ∘ enumerate) a) := Function.comp_apply.substr $ h₁ (Finite.enumerate a)
+ Finite.nth_inverse_enumerate.subst (motive := λx ↦ p (x a)) this
+
def Finite.set (α : Type u) [Finite α] [BEq α] [Hashable α] : Std.HashSet α :=
match h: (Finite.cardinality α) with
| 0 => Std.HashSet.empty
@@ -65,15 +128,15 @@ protected theorem Finite.set_worker_contains (α : Type u) [Finite α] [BEq α]
exact hi (oldSet.insert (nth ⟨↑(enumerate a) + (p + 1), h₁⟩)) this
theorem Finite.set_contains (α : Type u) [Finite α] [BEq α] [Hashable α] [LawfulBEq α] : ∀ (a : α), (Finite.set α).contains a := λa ↦ by
- unfold set
- split
- case h_1 h => exact (Fin.cast h $ Finite.enumerate a).elim0
- case h_2 l h =>
- let o := l - enumerate a
- have h₁ : (Finite.enumerate a).val + o = l := by omega
- have h₂ := Finite.set_worker_contains _ a Std.HashSet.empty o (by omega)
- simp[h₁] at h₂
- assumption
+ unfold set
+ split
+ case h_1 h => exact (Fin.cast h $ Finite.enumerate a).elim0
+ case h_2 l h =>
+ let o := l - enumerate a
+ have h₁ : (Finite.enumerate a).val + o = l := by omega
+ have h₂ := Finite.set_worker_contains _ a Std.HashSet.empty o (by omega)
+ simp[h₁] at h₂
+ assumption
protected theorem Finite.set_worker_size (α : Type u) [Finite α] [BEq α] [Hashable α] [LawfulBEq α]
: ∀(set : Std.HashSet α) (n : Fin (Finite.cardinality α)) (_ : ∀(x : Fin (Finite.cardinality α)) (_ : x ≤ n),
diff --git a/Common/Function.lean b/Common/Function.lean
new file mode 100644
index 0000000..f497baa
--- /dev/null
+++ b/Common/Function.lean
@@ -0,0 +1,3 @@
+theorem Function.comp_assoc (f : γ → δ) (g : β → γ) (h : α → β) : (f∘g)∘h = f∘g∘h := rfl
+theorem Function.comp_id (f : α → β) : f ∘ id = f := rfl
+theorem Function.id_comp (f : α → β) : id ∘ f = f := rfl
diff --git a/Common/HashSet.lean b/Common/HashSet.lean
index 1845443..3271121 100644
--- a/Common/HashSet.lean
+++ b/Common/HashSet.lean
@@ -1,9 +1,123 @@
import Std.Data.HashSet
import Common.Finite
+
open Std.HashSet
namespace Std.HashSet
-theorem finite_size {α : Type u} [Finite α] [BEq α] [LawfulBEq α] [Hashable α] (set : Std.HashSet α) : set.size ≤ Finite.cardinality α := by
+theorem erase_not_mem {α : Type u} [BEq α] [Hashable α] [LawfulBEq α] (set : HashSet α) (a : α) : a ∉ (set.erase a) := by
+ simp only [HashSet.mem_iff_contains, HashSet.contains_erase, beq_self_eq_true, Bool.not_true, Bool.false_and, Bool.false_eq_true, not_false_eq_true]
+
+theorem erase_not_mem_of_not_mem {α : Type u} [BEq α] [Hashable α] [LawfulBEq α] (set : HashSet α) {a : α} (b : α) (h₁ : a ∉ set) : a ∉ (set.erase b) := by
+ intro h₂
+ rw[HashSet.mem_erase] at h₂
+ exact absurd h₂.right h₁
+
+protected theorem size_le_finite_worker_size (α : Type u) [Finite α] [BEq α] [Hashable α] [LawfulBEq α] : ∀ (set : Std.HashSet α) (n : Fin (Finite.cardinality α)) (_h₁ : ∀(o : Fin (Finite.cardinality α)) (_h : o > n), Finite.nth o ∉ set), set.size ≤ (Finite.set.set_worker α HashSet.empty n).size := by
+ intros set n h₁
+ cases n
+ case mk n h₂ =>
+ cases n
+ case zero =>
+ have h₃ : (Finite.set.set_worker α empty ⟨0,h₂⟩).size = 1 := by simp[Finite.set.set_worker, HashSet.size_insert]
+ rw[h₃]
+ cases h₄ : set.contains (Finite.nth ⟨0,h₂⟩)
+ case false =>
+ have h₅ : ∀(o : Fin (Finite.cardinality α)), Finite.nth o ∉ set := by
+ simp[←Bool.not_eq_true, ←HashSet.mem_iff_contains] at h₄
+ intro o
+ cases o
+ case mk o ho =>
+ cases o
+ case zero => exact h₄
+ case succ oo => exact h₁ ⟨oo+1, ho⟩ (Nat.succ_pos oo)
+ have h₆ : ∀(a : α), a∉set := Finite.forall_nth (λx ↦ x∉set) h₅
+ have h₇ : set.isEmpty = true := HashSet.isEmpty_iff_forall_not_mem.mpr h₆
+ have h₈ : true = (set.size == 0) := HashSet.isEmpty_eq_size_eq_zero.subst h₇.symm
+ have h₈ : set.size = 0 := beq_iff_eq.mp h₈.symm
+ rw[h₈]
+ exact Nat.zero_le _
+ case true =>
+ --show that the set from which we erase (nth 0) has size 0, just like in case false.
+ --then show that, since we removed one element, set.size equals 1.
+ let rset := set.erase $ Finite.nth ⟨0,h₂⟩
+ have h₅ : (Finite.nth ⟨0,h₂⟩) ∉ rset := erase_not_mem set (Finite.nth ⟨0,h₂⟩)
+ have h₆ : ∀(o : Fin (Finite.cardinality α)), Finite.nth o ∉ rset := by
+ intro o
+ cases o
+ case mk o ho =>
+ cases o
+ case zero => exact h₅
+ case succ oo => exact erase_not_mem_of_not_mem set _ $ h₁ ⟨oo+1, ho⟩ (Nat.succ_pos oo)
+ have h₇ : ∀(a : α), a∉rset := Finite.forall_nth (λx ↦ x∉rset) h₆
+ have h₈ : rset.isEmpty = true := HashSet.isEmpty_iff_forall_not_mem.mpr h₇
+ have h₉ : true = (rset.size == 0) := HashSet.isEmpty_eq_size_eq_zero.subst h₈.symm
+ have h₉ : rset.size = 0 := beq_iff_eq.mp h₉.symm
+ have h₁₀ : set.size ≤ rset.size + 1 := HashSet.size_le_size_erase
+ rw[h₉, Nat.zero_add] at h₁₀
+ assumption
+ case succ m =>
+ --erase (nth m) from the set and recurse.
+ --then show that the current set can only be larger by 1, and therefore fulfills the goal
+ --HashSet.size_le_size_erase
+ let rset := set.erase $ Finite.nth ⟨m+1,h₂⟩
+ have h₃ : (Finite.nth ⟨m+1,h₂⟩) ∉ rset := erase_not_mem set (Finite.nth ⟨m+1,h₂⟩)
+ have h₄ : ∀(o : Fin (Finite.cardinality α)) (h₄ : o > ⟨m,Nat.lt_of_succ_lt h₂⟩), Finite.nth o ∉ rset := by
+ intros o h₄
+ cases o
+ case mk o h₅ =>
+ cases h₆ : o == (m+1) <;> simp at h₆
+ case true =>
+ simp only[h₆]
+ exact erase_not_mem set $ Finite.nth ⟨m+1, h₂⟩
+ case false =>
+ have h₆ : o > m+1 := by simp at h₄; omega
+ exact erase_not_mem_of_not_mem _ _ (h₁ ⟨o,h₅⟩ h₆)
+ have h₅ := Std.HashSet.size_le_finite_worker_size α rset ⟨m, Nat.lt_of_succ_lt h₂⟩ h₄
+ have h₆ : (Finite.set.set_worker α empty ⟨m, Nat.lt_of_succ_lt h₂⟩).size = empty.size + m + 1 :=
+ Finite.set_worker_size α empty ⟨m, Nat.lt_of_succ_lt h₂⟩ (λa _ ↦Bool.eq_false_iff.mp $ HashSet.contains_empty (a := Finite.nth a))
+ have h₇ : (Finite.set.set_worker α empty ⟨m+1,h₂⟩).size = empty.size + (m + 1) + 1 :=
+ Finite.set_worker_size α empty ⟨m+1,h₂⟩ (λa _ ↦Bool.eq_false_iff.mp $ HashSet.contains_empty (a := Finite.nth a))
+ have h₈ := HashSet.size_le_size_erase (m := set) (k:=Finite.nth ⟨m+1,h₂⟩)
+ simp[h₆] at h₅
+ simp[h₇]
+ exact Nat.le_trans h₈ (Nat.succ_le_succ h₅)
+
+theorem size_le_finite_set_size {α : Type u} [Finite α] [BEq α] [LawfulBEq α] [Hashable α] (set : Std.HashSet α) : set.size ≤ (Finite.set α).size := by
+ unfold Finite.set
+ split
+ case h_1 h₁ =>
+ if h : set.isEmpty then
+ have := HashSet.isEmpty_eq_size_eq_zero (m := set)
+ simp[h] at this
+ simp[this]
+ else
+ have := HashSet.isEmpty_eq_false_iff_exists_mem.mp (Bool.eq_false_iff.mpr h)
+ cases this
+ case intro x hx =>
+ let xx := Finite.enumerate x
+ exact (Fin.cast h₁ xx).elim0
+ case h_2 l h =>
+ exact Std.HashSet.size_le_finite_worker_size α set ⟨l,h.substr $ Nat.lt_succ_self l⟩ (λa hx ↦
+ by
+ have h₁ := a.isLt
+ simp_arith[h] at h₁
+ have h₂ : a > l := Fin.lt_iff_val_lt_val.mp hx
+ exact absurd h₁ (Nat.not_le.mpr h₂)
+ )
+
+theorem size_le_finite_cardinality {α : Type u} [Finite α] [BEq α] [LawfulBEq α] [Hashable α] (set : Std.HashSet α) : set.size ≤ Finite.cardinality α :=
+ (Finite.set_size_eq_cardinality α).subst (size_le_finite_set_size set)
- sorry
+theorem size_lt_finite_cardinality_of_not_mem {α : Type u} [Finite α] [BEq α] [LawfulBEq α] [Hashable α] (set : Std.HashSet α) (h₁ : ∃(a : α), a ∉ set) : set.size < Finite.cardinality α := by
+ have h₂ : set.size ≤ Finite.cardinality α := size_le_finite_cardinality set
+ cases h₁
+ case intro e h₁ =>
+ let iset := set.insert e
+ have h₂ : iset.size = set.size + 1 := by
+ have := HashSet.size_insert (m := set) (k := e)
+ simp[h₁] at this
+ assumption
+ have h₃ : iset.size ≤ Finite.cardinality α := size_le_finite_cardinality iset
+ rw[h₂] at h₃
+ exact Nat.lt_of_succ_le h₃
diff --git a/Common/Parsing.lean b/Common/Parsing.lean
index ebc5589..d878639 100644
--- a/Common/Parsing.lean
+++ b/Common/Parsing.lean
@@ -2,6 +2,7 @@
import Common.List
import Common.Nat
+import Common.Finite
namespace Parsing
@@ -59,6 +60,23 @@ def RectangularGrid.Coordinate.ofIndex {grid : RectangularGrid Element} (index :
theorem RectangularGrid.Coordinate.toIndex_inv_fromIndex {grid : RectangularGrid Element} (index : Fin (grid.width * grid.height)) : RectangularGrid.Coordinate.toIndex (RectangularGrid.Coordinate.ofIndex index) = index := by
simp only [RectangularGrid.Coordinate.toIndex, RectangularGrid.Coordinate.ofIndex, Nat.mod_add_div, Fin.eta]
+theorem RectangularGrid.Coordinate.fromIndex_inv_toIndex {grid : RectangularGrid Element} (c : grid.Coordinate) : RectangularGrid.Coordinate.ofIndex (RectangularGrid.Coordinate.toIndex c) = c := by
+ unfold RectangularGrid.Coordinate.toIndex RectangularGrid.Coordinate.ofIndex
+ simp only [Nat.add_mul_mod_self_left]
+ congr
+ case e_x.e_val => simp only [Fin.is_lt, Nat.mod_eq_of_lt]
+ case e_y.e_val =>
+ rw[Nat.add_mul_div_left]
+ simp[Nat.div_eq_of_lt]
+ exact grid.not_empty.left
+
+instance {grid : RectangularGrid Element} : Finite grid.Coordinate where
+ cardinality := grid.width * grid.height
+ enumerate := RectangularGrid.Coordinate.toIndex
+ nth := RectangularGrid.Coordinate.ofIndex
+ enumerate_inverse_nth := funext RectangularGrid.Coordinate.toIndex_inv_fromIndex
+ nth_inverse_enumerate := funext RectangularGrid.Coordinate.fromIndex_inv_toIndex
+
def RectangularGrid.Get {grid : RectangularGrid Element} (coordinate : grid.Coordinate) : Element :=
grid.elements[coordinate.toIndex]'(grid.size_valid.substr coordinate.toIndex.isLt)