|
| 1 | +/- |
| 2 | +Copyright (c) 2023 Chris Hughes. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Chris Hughes, Scott Morrison |
| 5 | +-/ |
| 6 | +import Archive.Examples.IfNormalization.Statement |
| 7 | +import Mathlib.Data.List.AList |
| 8 | + |
| 9 | +/-! |
| 10 | +# A variant of Chris Hughes' solution for the if normalization challenge. |
| 11 | +
|
| 12 | +In this variant we eschew the use of `aesop`, and instead write out the proofs. |
| 13 | +
|
| 14 | +(In order to avoid duplicated names with `Result.lean`, |
| 15 | +we put primes on the declarations in the file.) |
| 16 | +-/ |
| 17 | + |
| 18 | +set_option autoImplicit true |
| 19 | + |
| 20 | +namespace IfExpr |
| 21 | + |
| 22 | +attribute [local simp] eval normalized hasNestedIf hasConstantIf hasRedundantIf disjoint vars |
| 23 | + List.disjoint max_add_add_right max_mul_mul_left Nat.lt_add_one_iff le_add_of_le_right |
| 24 | + |
| 25 | +-- A copy of Lean's `decide_eq_true_eq` which unifies the `Decidable` instance |
| 26 | +-- rather than finding it by typeclass search. |
| 27 | +-- See https://github.com/leanprover/lean4/pull/2816 |
| 28 | +@[simp] theorem decide_eq_true_eq' {i : Decidable p} : (@decide p i = true) = p := |
| 29 | + _root_.decide_eq_true_eq |
| 30 | + |
| 31 | +theorem eval_ite_ite' : |
| 32 | + (ite (ite a b c) d e).eval f = (ite a (ite b d e) (ite c d e)).eval f := by |
| 33 | + cases h : eval f a <;> simp_all |
| 34 | + |
| 35 | +/-- Custom size function for if-expressions, used for proving termination. -/ |
| 36 | +@[simp] def normSize' : IfExpr → ℕ |
| 37 | + | lit _ => 0 |
| 38 | + | var _ => 1 |
| 39 | + | .ite i t e => 2 * normSize' i + max (normSize' t) (normSize' e) + 1 |
| 40 | + |
| 41 | +/-- Normalizes the expression at the same time as assigning all variables in |
| 42 | +`e` to the literal booleans given by `l` -/ |
| 43 | +def normalize' (l : AList (fun _ : ℕ => Bool)) : |
| 44 | + (e : IfExpr) → { e' : IfExpr // |
| 45 | + (∀ f, e'.eval f = e.eval (fun w => (l.lookup w).elim (f w) (fun b => b))) |
| 46 | + ∧ e'.normalized |
| 47 | + ∧ ∀ (v : ℕ), v ∈ vars e' → l.lookup v = none } |
| 48 | + | lit b => ⟨lit b, by simp⟩ |
| 49 | + | var v => |
| 50 | + match h : l.lookup v with |
| 51 | + | none => ⟨var v, by simp_all⟩ |
| 52 | + | some b => ⟨lit b, by simp_all⟩ |
| 53 | + | .ite (lit true) t e => |
| 54 | + have ⟨t', ht'⟩ := normalize' l t |
| 55 | + ⟨t', by simp_all⟩ |
| 56 | + | .ite (lit false) t e => |
| 57 | + have ⟨e', he'⟩ := normalize' l e |
| 58 | + ⟨e', by simp_all⟩ |
| 59 | + | .ite (.ite a b c) d e => |
| 60 | + have ⟨t', ht₁, ht₂⟩ := normalize' l (.ite a (.ite b d e) (.ite c d e)) |
| 61 | + ⟨t', fun f => by rw [ht₁, eval_ite_ite'], ht₂⟩ |
| 62 | + | .ite (var v) t e => |
| 63 | + match h : l.lookup v with |
| 64 | + | none => |
| 65 | + have ⟨t', ht₁, ht₂, ht₃⟩ := normalize' (l.insert v true) t |
| 66 | + have ⟨e', he₁, he₂, he₃⟩ := normalize' (l.insert v false) e |
| 67 | + ⟨if t' = e' then t' else .ite (var v) t' e', by |
| 68 | + refine ⟨fun f => ?_, ?_, fun w b => ?_⟩ |
| 69 | + · simp only [eval, apply_ite, ite_eq_iff'] |
| 70 | + cases hfv : f v |
| 71 | + · simp (config := {contextual := true}) only [cond_false, h, he₁] |
| 72 | + refine ⟨fun _ => ?_, fun _ => ?_⟩ |
| 73 | + · congr |
| 74 | + ext w |
| 75 | + by_cases w = v <;> rename_i x |
| 76 | + · substs h |
| 77 | + simp_all |
| 78 | + · simp_all |
| 79 | + · congr |
| 80 | + ext w |
| 81 | + by_cases w = v <;> rename_i x |
| 82 | + · substs h |
| 83 | + simp_all |
| 84 | + · simp_all |
| 85 | + · simp only [cond_true, h, ht₁] |
| 86 | + refine ⟨fun _ => ?_, fun _ => ?_⟩ |
| 87 | + · congr |
| 88 | + ext w |
| 89 | + by_cases w = v <;> rename_i x |
| 90 | + · substs h |
| 91 | + simp_all |
| 92 | + · simp_all |
| 93 | + · congr |
| 94 | + ext w |
| 95 | + by_cases w = v <;> rename_i x |
| 96 | + · substs h |
| 97 | + simp_all |
| 98 | + · simp_all |
| 99 | + · have := ht₃ v |
| 100 | + have := he₃ v |
| 101 | + simp_all? says simp_all only [Option.elim, ne_eq, normalized, Bool.and_eq_true, |
| 102 | + Bool.not_eq_true', AList.lookup_insert] |
| 103 | + obtain ⟨⟨⟨tn, tc⟩, tr⟩, td⟩ := ht₂ |
| 104 | + split <;> rename_i h' |
| 105 | + · subst h' |
| 106 | + simp_all |
| 107 | + · simp_all? says simp_all only [ne_eq, hasNestedIf, Bool.or_self, hasConstantIf, |
| 108 | + and_self, hasRedundantIf, Bool.or_false, beq_eq_false_iff_ne, not_false_eq_true, |
| 109 | + disjoint, List.disjoint, Bool.and_true, Bool.and_eq_true, decide_eq_true_eq', |
| 110 | + true_and] |
| 111 | + constructor <;> assumption |
| 112 | + · have := ht₃ w |
| 113 | + have := he₃ w |
| 114 | + by_cases w = v |
| 115 | + · subst h; simp_all |
| 116 | + · simp_all? says simp_all only [Option.elim, ne_eq, normalized, Bool.and_eq_true, |
| 117 | + Bool.not_eq_true', not_false_eq_true, AList.lookup_insert_ne] |
| 118 | + obtain ⟨⟨⟨en, ec⟩, er⟩, ed⟩ := he₂ |
| 119 | + split at b <;> rename_i h' |
| 120 | + · subst h'; simp_all |
| 121 | + · simp_all only [ne_eq, vars, List.singleton_append, List.cons_append, |
| 122 | + Bool.not_eq_true, List.mem_cons, List.mem_append, false_or] |
| 123 | + cases b <;> simp_all⟩ |
| 124 | + | some b => |
| 125 | + have ⟨e', he'⟩ := normalize' l (.ite (lit b) t e) |
| 126 | + ⟨e', by simp_all⟩ |
| 127 | + termination_by normalize' e => e.normSize' |
| 128 | + |
| 129 | +example : IfNormalization := |
| 130 | + ⟨fun e => (normalize' ∅ e).1, |
| 131 | + fun e => ⟨(normalize' ∅ e).2.2.1, by simp [(normalize' ∅ e).2.1]⟩⟩ |
0 commit comments