Proofs that the mirror function of binary trees is involutive
The tree corresponding to
3 / \ 2 4 / \ 1 5
can be represented by the term
Node 3 (Node 2 (Leaf 1) (Leaf 5)) (Leaf 4)
using the datatype defined by
inductive Tree' (α : Type) : Type | Leaf : α → Tree' α | Node : α → Tree' α → Tree' α → Tree' α
The mirror image of the previous tree is
3 / \ 4 2 / \ 5 1
whose term is
Node 3 (Leaf 4) (Node 2 (Leaf 5) (Leaf 1))
The definition of the function that calculates the mirror image is
def mirror : Tree' α → Tree' α | (Leaf x) => Leaf x | (Node x l r) => Node x (mirror r) (mirror l)
Prove that the mirror function is involutive; that is,
mirror (mirror a) = a
To do this, complete the following Lean4 theory:
import Mathlib.Tactic variable {α : Type} set_option pp.fieldNotation false inductive Tree' (α : Type) : Type | Leaf : α → Tree' α | Node : α → Tree' α → Tree' α → Tree' α namespace Tree' variable (a l r : Tree' α) variable (x : α) def mirror : Tree' α → Tree' α | (Leaf x) => Leaf x | (Node x l r) => Node x (mirror r) (mirror l) example : mirror (mirror a) = a := by sorry end Tree'
1. Natural language proof
From the definition of the mirror function, we have the following lemmas
mirror1 : mirror (Leaf x) = Leaf x mirror2 : mirror (Node x l r) = Node x (mirror r) (mirror l)
We will prove that, for any tree a,
mirror (mirror a) = a
by induction on a.
Base case: Suppose that a = Leaf x. Then,
mirror (mirror a) = mirror (mirror (Leaf x)) = mirror (Leaf x) [por mirror1] = Leaf x [por mirror1] = a
Induction step: Suppose that a = Node x l r and the induction hypotheses hold:
mirror (mirror l) = l (Hl) mirror (mirror r) = r (Hr)
Then,
mirror (mirror a) = mirror (mirror (Node x l r)) = mirror (Node x (mirror r) (mirror l)) [by mirror2] = Node x (mirror (mirror l)) (mirror (mirror r)) [by mirror2] = Node x l (mirror (mirror r)) [by Hl] = Node x l r [by Hr] = a
2. Proofs with Lean4
import Mathlib.Tactic variable {α : Type} set_option pp.fieldNotation false inductive Tree' (α : Type) : Type | Leaf : α → Tree' α | Node : α → Tree' α → Tree' α → Tree' α namespace Tree' variable (a l r : Tree' α) variable (x : α) def mirror : Tree' α → Tree' α | (Leaf x) => Leaf x | (Node x l r) => Node x (mirror r) (mirror l) @[simp] lemma mirror_1 : mirror (Leaf x) = Leaf x := rfl @[simp] lemma mirror_2 : mirror (Node x l r) = Node x (mirror r) (mirror l) := rfl -- Proof 1 -- ======= example : mirror (mirror a) = a := by induction a with | Leaf x => -- x : α -- ⊢ mirror (mirror (Leaf x)) = Leaf x calc mirror (mirror (Leaf x)) = mirror (Leaf x) := congr_arg mirror (mirror_1 x) _ = Leaf x := mirror_1 x | Node x l r Hl Hr => -- x : α -- l r : Tree' α -- Hl : mirror (mirror l) = l -- Hr : mirror (mirror r) = r -- ⊢ mirror (mirror (Node x l r)) = Node x l r calc mirror (mirror (Node x l r)) = mirror (Node x (mirror r) (mirror l)) := congr_arg mirror (mirror_2 l r x) _ = Node x (mirror (mirror l)) (mirror (mirror r)) := mirror_2 (mirror r) (mirror l) x _ = Node x l (mirror (mirror r)) := congrArg (Node x . (mirror (mirror r))) Hl _ = Node x l r := congrArg (Node x l .) Hr -- Proof 2 -- ======= example : mirror (mirror a) = a := by induction a with | Leaf x => -- ⊢ mirror (mirror (Leaf x)) = Leaf x calc mirror (mirror (Leaf x)) = mirror (Leaf x) := congr_arg mirror (mirror_1 x) _ = Leaf x := by rw [mirror_1] | Node x l r Hl Hr => -- x : α -- l r : Tree' α -- Hl : mirror (mirror l) = l -- Hr : mirror (mirror r) = r -- ⊢ mirror (mirror (Node x l r)) = Node x l r calc mirror (mirror (Node x l r)) = mirror (Node x (mirror r) (mirror l)) := congr_arg mirror (mirror_2 l r x) _ = Node x (mirror (mirror l)) (mirror (mirror r)) := by rw [mirror_2] _ = Node x l (mirror (mirror r)) := by rw [Hl] _ = Node x l r := by rw [Hr] -- Proof 3 -- ======= example : mirror (mirror a) = a := by induction a with | Leaf x => -- x : α -- ⊢ mirror (mirror (Leaf x)) = Leaf x calc mirror (mirror (Leaf x)) = mirror (Leaf x) := by simp _ = Leaf x := by simp | Node x l r Hl Hr => -- x : α -- l r : Tree' α -- Hl : mirror (mirror l) = l -- Hr : mirror (mirror r) = r -- ⊢ mirror (mirror (Node x l r)) = Node x l r calc mirror (mirror (Node x l r)) = mirror (Node x (mirror r) (mirror l)) := by simp _ = Node x (mirror (mirror l)) (mirror (mirror r)) := by simp _ = Node x l (mirror (mirror r)) := by simp [Hl] _ = Node x l r := by simp [Hr] -- Proof 4 -- ======= example : mirror (mirror a) = a := by induction a with | Leaf _ => simp | Node _ _ _ Hl Hr => simp [Hl, Hr] -- Proof 5 -- ======= example : mirror (mirror a) = a := by induction a <;> simp [*] -- Proof 6 -- ======= example : mirror (mirror a) = a := by induction a with | Leaf x => -- x : α -- ⊢ mirror (mirror (Leaf x)) = Leaf x rw [mirror_1] -- ⊢ mirror (Leaf x) = Leaf x rw [mirror_1] | Node x l r Hl Hr => -- x : α -- l r : Tree' α -- Hl : mirror (mirror l) = l -- Hr : mirror (mirror r) = r -- ⊢ mirror (mirror (Node x l r)) = Node x l r rw [mirror_2] -- ⊢ mirror (Node x (mirror r) (mirror l)) = Node x l r rw [mirror_2] -- ⊢ Node x (mirror (mirror l)) (mirror (mirror r)) = Node x l r rw [Hl] -- ⊢ Node x l (mirror (mirror r)) = Node x l r rw [Hr] -- Proof 7 -- ======= example : mirror (mirror a) = a := by induction a with | Leaf x => -- x : α -- ⊢ mirror (mirror (Leaf x)) = Leaf x rw [mirror_1, mirror_1] | Node x l r Hl Hr => -- x : α -- l r : Tree' α -- Hl : mirror (mirror l) = l -- Hr : mirror (mirror r) = r -- ⊢ mirror (mirror (Node x l r)) = Node x l r rw [mirror_2, mirror_2, Hl, Hr] -- Proof 8 -- ======= lemma mirror_mirror : ∀ a : Tree' α, mirror (mirror a) = a | (Leaf x) => by simp | (Node x l r) => by simp [mirror_mirror l, mirror_mirror r] end Tree'
You can interact with the previous proofs at Lean 4 Web.
3. Proofs with Isabelle/HOL
theory Proofs_that_the_mirror_function_of_binary_trees_is_involutive imports Main begin datatype 'a tree = Leaf "'a" | Node "'a" "'a tree" "'a tree" fun mirror :: "'a tree ⇒ 'a tree" where "mirror (Leaf x) = (Leaf x)" | "mirror (Node x l r) = (Node x (mirror r) (mirror l))" (* Proof 1 *) lemma fixes a :: "'b tree" shows "mirror (mirror a) = a" (is "?P a") proof (induct a) fix x show "?P (Leaf x)" by (simp only: mirror.simps(1)) next fix x fix l assume h1: "?P l" fix r assume h2: "?P r" show "?P (Node x l r)" proof - have "mirror (mirror (Node x l r)) = mirror (Node x (mirror r) (mirror l))" by (simp only: mirror.simps(2)) also have "… = Node x (mirror (mirror l)) (mirror (mirror r))" by (simp only: mirror.simps(2)) also have "… = Node x l r" by (simp only: h1 h2) finally show ?thesis by this qed qed (* Proof 2 *) lemma fixes a :: "'b tree" shows "mirror (mirror a) = a" (is "?P a") proof (induct a) fix x show "?P (Leaf x)" by simp next fix x fix l assume h1: "?P l" fix r assume h2: "?P r" show "?P (Node x l r)" proof - have "mirror (mirror (Node x l r)) = mirror (Node x (mirror r) (mirror l))" by simp also have "… = Node x (mirror (mirror l)) (mirror (mirror r))" by simp also have "… = Node x l r" using h1 h2 by simp finally show ?thesis . qed qed (* Proof 3 *) lemma "mirror (mirror a ) = a" by (induct a) simp_all end
Note: The code for the previous proofs can be found in the Calculemus repository on GitHub.