theorem prop_NStoogeSort2IsSort (xs: List Nat) : nstoogesort2' xs == isort xs:= by sorry

import Mathlib

def third : Nat → Nat
| 0 => 0
| 1 => 0
| 2 => 0
| n + 3 => 1 + (third n)

def insert' : Nat → List Nat → List Nat
| x, [] => [x]
| x, y::xs => if x <= y then x::y::xs else y::(insert x xs)

def isort: List Nat → List Nat
| [] => []
| x::xs => insert' x (isort xs)

def splitAt : Nat → List Nat → (List Nat × List Nat)
| _n, [] => ([], [])
| 0, xs => ([], xs)
| n + 1, x::xs => match splitAt n xs with
  | (l1, l2) => (x::l1, l2)

def reverse : List Nat → List Nat
| [] => []
| x::xs => (reverse xs) ++ [x]

lemma len_rev_eq_len {l: List Nat} : (reverse l).length = l.length := by
  induction l with
  | nil => simp [reverse]
  | cons head tail ih => unfold reverse; simp [ih];

lemma splitAt_len_le : (splitAt n xs).2.length ≤ xs.length := by
  induction xs generalizing n with
  | nil => unfold splitAt; simp;
  | cons head tail ih =>
    cases n with
    | zero => unfold splitAt; simp;
    | succ nm1 => unfold splitAt; simp; apply Nat.le.step; exact ih;

lemma splitAt_second_len_lt (n: Nat): (splitAt n.succ (x::xs)).2.length < (x::xs).length := by
  unfold splitAt;
  simp;
  calc List.length (splitAt n xs).2 ≤ (List.length xs) := splitAt_len_le
       _                          < Nat.succ (List.length xs) := Nat.lt_succ_self _

lemma splitAt_second_len_lt' (n: Nat) (hlen: xs.length > 0): (splitAt n.succ xs).2.length < xs.length := by
  cases xs with
  | nil => simp at hlen;
  | cons x xs => exact splitAt_second_len_lt _;

lemma splitAt_second_len_lt'' (hn: n > 0) (hlen: xs.length > 0) (hlen': xl = xs.length): (splitAt n xs).2.length < xl := by
  cases n with
  | zero => simp at hn;
  | succ nm1 => rw [hlen']; exact splitAt_second_len_lt' _ hlen;

lemma splitAt_sum_preserves_len (n: Nat) (xs: List Nat) (hspl: spl = splitAt n xs):
  (spl.1.length + spl.2.length = xs.length) := by
  induction xs generalizing n spl with
  | nil => simp [splitAt] at hspl; simp [hspl];
  | cons head tail ih => cases n with
    | zero => simp [splitAt] at hspl; simp [hspl];
    | succ nm1 =>
      simp [splitAt] at hspl;
      simp [hspl]
      rw [Nat.succ_add];
      apply Order.succ_eq_succ_iff.2
      exact ih nm1 (by rfl);

lemma third_eq_div_3 : (x/3) = third x := by
  induction x using Nat.strongInductionOn with
  | ind x ih =>
  unfold third
  match x with
  | 0 => simp;
  | 1 => simp;
  | 2 => simp;
  | n + 3 => simp [Nat.succ_eq_add_one]; ring_nf; linarith [ih n (by linarith)]

def nstoogesort (xs : {xs : List Nat // xs.length = n}) : {ys: List Nat // ys.length = n} := match xs with
| ⟨[], h⟩ => ⟨[], h⟩
| ⟨[x], h⟩ => ⟨[x], h⟩
| ⟨[x, y], h⟩ => if x <= y then ⟨[x, y], h⟩ else ⟨[y, x], by simp [←h]⟩
| ⟨x1::x2::x3::xs, hlen⟩ =>
  let yzs1 := splitAt (third (x1::x2::x3::xs).length) (reverse (x1::x2::x3::xs))
  let ⟨tmp2, hlen2⟩ := (@nstoogesort yzs1.2.length ⟨yzs1.2, by rfl⟩)
  let s1s2a := tmp2 ++ (reverse yzs1.1)
  let yzs2 := splitAt (third s1s2a.length) s1s2a
  let ⟨tmp3, hlen3⟩ := (@nstoogesort yzs2.2.length ⟨yzs2.2, by rfl⟩)
  let s1s1 := yzs2.1 ++ tmp3
  let yzs3 := splitAt (third s1s1.length) (reverse s1s1)
  let ⟨tmp4, hlen4⟩ := (@nstoogesort yzs3.2.length ⟨yzs3.2, by rfl⟩)
  ⟨tmp4 ++ (reverse yzs3.1), by
    simp [hlen4, len_rev_eq_len];
    rw [add_comm];
    simp [splitAt_sum_preserves_len _ _ (Eq.refl yzs3), len_rev_eq_len];
    unfold_let s1s1
    simp [hlen3, splitAt_sum_preserves_len _ _ (Eq.refl yzs2)]
    unfold_let s1s2a
    simp [hlen2, len_rev_eq_len];
    rw [add_comm];
    simp [splitAt_sum_preserves_len _ _ (Eq.refl yzs1), len_rev_eq_len, hlen]
    ⟩
termination_by match xs with | ⟨lst, _h⟩ => lst.length
decreasing_by
  simp_wf
  apply splitAt_second_len_lt''
    (by simp [←third_eq_div_3, Nat.succ_eq_add_one, Nat.add_assoc])
    (by simp [len_rev_eq_len, Nat.succ_eq_add_one, Nat.add_assoc])
    (by simp [len_rev_eq_len, hlen])
  simp_wf
  suffices hyzs2 : (splitAt (List.length s1s2a / 3) s1s2a).2.length < n
  rw [List.length_append, third_eq_div_3] at hyzs2
  exact hyzs2;
  unfold_let s1s2a
  suffices hyzs1 : List.length (tmp2 ++ reverse yzs1.1) = List.length xs + 3
  exact splitAt_second_len_lt'' (by simp [hyzs1]) (by simp [hyzs1]) (by simp [hyzs1, ←hlen])
  simp [hlen2, len_rev_eq_len, Nat.add_comm, splitAt_sum_preserves_len _ _ (Eq.refl yzs1)]
  ring_nf
  simp_wf
  suffices hyzs3 : (splitAt (List.length s1s1 / 3) (reverse s1s1)).2.length < n
  unfold_let s1s1 yzs2 s1s2a yzs1 at hyzs3
  simp [List.length_append, third_eq_div_3] at hyzs3;
  exact hyzs3;
  suffices hs1s1: s1s1.length = xs.length + 3
  exact splitAt_second_len_lt'' (by simp [hs1s1]) (by simp [len_rev_eq_len, hs1s1]) (by simp [len_rev_eq_len, hs1s1, ←hlen])
  unfold_let s1s1
  simp [hlen3, splitAt_sum_preserves_len _ _ (Eq.refl yzs2)]
  unfold_let s1s2a
  simp [hlen2, len_rev_eq_len, Nat.add_comm, splitAt_sum_preserves_len _ _ (Eq.refl yzs1)]
  ring_nf

def nstoogesort2' (xs: List Nat) := nstoogesort ⟨xs, (by rfl)⟩

xs : List ℕ
⊢ (↑(nstoogesort2' xs) == isort xs) = true


theorem hpairwise_desc (ps: List MyHeap): List.length (hpairwise ps) ≤ List.length ps := by sorry

import Mathlib

inductive MyHeap where
| nil : MyHeap
| node : MyHeap → Nat → MyHeap  →  MyHeap

def hmerge : MyHeap  → MyHeap  → MyHeap
| MyHeap.nil, q => q
| p, MyHeap.nil => p
| MyHeap.node p x q, MyHeap.node r y s =>
  if x <= y then MyHeap.node (hmerge q (MyHeap.node r y s)) x p
            else MyHeap.node (hmerge (MyHeap.node p x q) s) y r

def hpairwise : List MyHeap → List MyHeap
| p::q::qs => (hmerge p q)::hpairwise qs
| ps => ps

ps : List MyHeap
⊢ (hpairwise ps).length ≤ ps.length


theorem numElem_lt_subHeaps  (q r: MyHeap) {x: Nat}: numElem q < numElem (MyHeap.node q x r) ∧ numElem r < numElem (MyHeap.node q x r) := by sorry

import Mathlib

inductive MyHeap where
| nil : MyHeap
| node : MyHeap → Nat → MyHeap  →  MyHeap

def numElem : MyHeap → Nat
| MyHeap.nil => 0
| MyHeap.node p _x q => 1 + numElem p + numElem q

q r : MyHeap
x : ℕ
⊢ numElem q < numElem (q.node x r) ∧ numElem r < numElem (q.node x r)


theorem merge_elems (p q: MyHeap): numElem p + numElem q = numElem (hmerge p q) := by sorry

import Mathlib

inductive MyHeap where
| nil : MyHeap
| node : MyHeap → Nat → MyHeap  →  MyHeap

def hmerge : MyHeap  → MyHeap  → MyHeap
| MyHeap.nil, q => q
| p, MyHeap.nil => p
| MyHeap.node p x q, MyHeap.node r y s =>
  if x <= y then MyHeap.node (hmerge q (MyHeap.node r y s)) x p
            else MyHeap.node (hmerge (MyHeap.node p x q) s) y r

def numElem : MyHeap → Nat
| MyHeap.nil => 0
| MyHeap.node p _x q => 1 + numElem p + numElem q

p q : MyHeap
⊢ numElem p + numElem q = numElem (hmerge p q)


theorem numElem_merge_branches_lt (p q: MyHeap) (x: Nat): numElem (hmerge p q) < numElem (MyHeap.node p x q) := by sorry

p q : MyHeap
x : ℕ
⊢ numElem (hmerge p q) < numElem (p.node x q)


theorem len_pairwise (xs: List (List Nat)): 2 * (pairwise xs).length = (if (Odd xs.length) then xs.length + 1 else xs.length) := by sorry

import Mathlib

def lmerge : List Nat → List Nat → List Nat
| [], ys => ys
| xs, [] => xs
| x::xs, y::ys => if  x <= y  then
      x::(lmerge xs (y::ys))
    else
      y::(lmerge (x::xs) ys)

def pairwise : List (List Nat) → List (List Nat)
| xs::ys::xss => lmerge xs ys :: pairwise xss
| xss => xss

xs : List (List ℕ)
⊢ 2 * (pairwise xs).length = if Odd xs.length then xs.length + 1 else xs.length


theorem merge_term : (pairwise (xs::ys::xss)).length < (xs::ys::xss).length := by sorry

xs ys : List ℕ
xss : List (List ℕ)
⊢ (pairwise (xs :: ys :: xss)).length < (xs :: ys :: xss).length


import Mathlib

def half : Nat → Nat
| 0 => 0
| 1 => 0
| x + 2 => 1 + (half x)


theorem len_evens_le {xs: List Nat}: (evens xs).length ≤ xs.length := by sorry

import Mathlib

mutual
  def evens : List Nat → List Nat
  | [] => []
  | x::xs => x::(odds xs)
--
  def odds : List Nat → List Nat
  | [] => []
  | _x::xs => evens xs
end

xs : List ℕ
⊢ (evens xs).length ≤ xs.length


theorem len_odds_le {xs: List Nat}: (odds xs).length ≤ xs.length := by sorry

xs : List ℕ
⊢ (odds xs).length ≤ xs.length


theorem bmerge_term (a b: Nat) (as bs: List Nat) (hlen: ¬(List.length as == 0 && List.length bs == 0) = true): List.length (evens (a :: as)) + List.length (evens (b :: bs)) < Nat.succ (List.length as) + Nat.succ (List.length bs) := by sorry

a b : ℕ
as bs : List ℕ
hlen : ¬(as.length == 0 && bs.length == 0) = true
⊢ (evens (a :: as)).length + (evens (b :: bs)).length < as.length.succ + bs.length.succ


theorem bmerge_term2 (x y: Nat) (xs ys: List Nat) : List.length (odds (x :: xs)) + List.length (odds (y :: ys)) < Nat.succ (List.length xs) + Nat.succ (List.length ys) := by sorry

x y : ℕ
xs ys : List ℕ
⊢ (odds (x :: xs)).length + (odds (y :: ys)).length < xs.length.succ + ys.length.succ


theorem bsort_term1 (x y: Nat) (xs: List Nat): List.length (evens (x :: y :: xs)) < Nat.succ (Nat.succ (List.length xs)) := by sorry

x y : ℕ
xs : List ℕ
⊢ (evens (x :: y :: xs)).length < xs.length.succ.succ


theorem bsort_term2 (x y: Nat) (xs: List Nat): List.length (odds (x :: y :: xs)) < Nat.succ (Nat.succ (List.length xs)) := by sorry

x y : ℕ
xs : List ℕ
⊢ (odds (x :: y :: xs)).length < xs.length.succ.succ


theorem filter_len_le {f: Nat → Bool} {xs: List Nat}: (filter xs f).length <= xs.length := by sorry

import Mathlib

def filter : List Nat → (Nat → Bool) → List Nat
| [], _f => []
| x::xs, f => if f x then x::(filter xs f) else (filter xs f)

f : ℕ → Bool
xs : List ℕ
⊢ (filter xs f).length ≤ xs.length


theorem qsort_term (x:Nat) (xs: List Nat) : List.length (filter xs fun y => decide (y ≤ x)) < Nat.succ (List.length xs) := by sorry

x : ℕ
xs : List ℕ
⊢ (filter xs fun y => decide (y ≤ x)).length < xs.length.succ


theorem qsort_term2 (x:Nat) (xs: List Nat) : List.length (filter xs fun y => decide (y > x)) < Nat.succ (List.length xs) := by sorry

x : ℕ
xs : List ℕ
⊢ (filter xs fun y => decide (y > x)).length < xs.length.succ


theorem min_in_list : minimum x xs ∈ (x::xs) := by sorry

import Mathlib

def minimum : Nat → List Nat → Nat
| x, [] => x
| x, y::ys => if y <= x then minimum y ys else minimum x ys

x : ℕ
xs : List ℕ
⊢ minimum x xs ∈ x :: xs


theorem delete_len_eq {x: Nat} {xs: List Nat} (h: x ∈ xs): (deleteFirst x xs).length + 1 = xs.length := by sorry

import Mathlib

def deleteFirst [DecidableEq α]: α → List α → List α
  | _, [] => []
  | n, x::xs => if n == x then xs else x::(deleteFirst n xs)

x : ℕ
xs : List ℕ
h : x ∈ xs
⊢ (deleteFirst x xs).length + 1 = xs.length


theorem len_rev_eq_len {l: List Nat} : (reverse l).length = l.length := by sorry

import Mathlib

def reverse : List Nat → List Nat
| [] => []
| x::xs => (reverse xs) ++ [x]

l : List ℕ
⊢ (reverse l).length = l.length


theorem splitAt_len_le {xs: List Nat}: (splitAt n xs).2.length ≤ xs.length := by sorry

import Mathlib

def splitAt : Nat → List Nat → (List Nat × List Nat)
| _n, [] => ([], [])
| 0, xs => ([], xs)
| n + 1, x::xs => match splitAt n xs with
  | (l1, l2) => (x::l1, l2)

n : ℕ
xs : List ℕ
⊢ (splitAt n xs).2.length ≤ xs.length


theorem splitAt_second_len_lt (n: Nat): (splitAt n.succ (x::xs)).2.length < (x::xs).length := by sorry

x : ℕ
xs : List ℕ
n : ℕ
⊢ (splitAt n.succ (x :: xs)).2.length < (x :: xs).length


theorem splitAt_second_len_lt' {xs: List Nat} (n: Nat) (hlen: xs.length > 0): (splitAt n.succ xs).2.length < xs.length := by sorry

xs : List ℕ
n : ℕ
hlen : xs.length > 0
⊢ (splitAt n.succ xs).2.length < xs.length


theorem splitAt_second_len_lt'' {xs: List Nat} (hn: n > 0) (hlen: xs.length > 0) (hlen': xl = xs.length): (splitAt n xs).2.length < xl := by sorry

n xl : ℕ
xs : List ℕ
hn : n > 0
hlen : xs.length > 0
hlen' : xl = xs.length
⊢ (splitAt n xs).2.length < xl


theorem splitAt_sum_preserves_len (n: Nat) (xs: List Nat) (hspl: spl = splitAt n xs): (spl.1.length + spl.2.length = xs.length) := by sorry

spl : List ℕ × List ℕ
n : ℕ
xs : List ℕ
hspl : spl = splitAt n xs
⊢ spl.1.length + spl.2.length = xs.length


theorem splitAt_first_len_lt {xs: List Nat} (hn: n < xl) (hlen': xl = xs.length): (splitAt n xs).1.length < xl := by sorry

n xl : ℕ
xs : List ℕ
hn : n < xl
hlen' : xl = xs.length
⊢ (splitAt n xs).1.length < xl


theorem twon_lt (n: Nat): (2*n.succ.succ.succ + 1)/ 3 < n.succ.succ.succ := by sorry

n : ℕ
⊢ (2 * n.succ.succ.succ + 1) / 3 < n.succ.succ.succ


theorem third_eq_div_3 : (x/3) = third x := by sorry

import Mathlib

def third : Nat → Nat
| 0 => 0
| 1 => 0
| 2 => 0
| n + 3 => 1 + (third n)


theorem twon_lt' (n: Nat): twoThirds (n.succ.succ.succ) < n.succ.succ.succ := by sorry

import Mathlib

def twoThirds : Nat → Nat
| 0 => 0
| 1 => 0
| 2 => 0
| n + 3 => 2 + (twoThirds n)

n : ℕ
⊢ twoThirds n.succ.succ.succ < n.succ.succ.succ


theorem prop_Select (xs: List α) [DecidableEq α] :
  List.map Prod.fst (select xs) == xs:= by sorry

import Mathlib

def select : List α → List (α × (List α))
  | [] => []
  | x :: xs =>
    ⟨x, xs⟩:: List.map (fun (p: α × (List α))  => (p.1, x::p.2)) (select xs)

α : Type u_1
xs : List α
inst✝ : DecidableEq α
⊢ (List.map Prod.fst (select xs) == xs) = true


theorem prop_SelectPermutations (xs: List α) [DecidableEq α] :
  (List.all
    (List.map
      (fun (p: α × List α) => isPermutation xs (p.1::p.2))
      (select xs)
    )
    (fun x => x)
  ):= by sorry

import Mathlib

def select : List α → List (α × (List α))
  | [] => []
  | x :: xs =>
    ⟨x, xs⟩:: List.map (fun (p: α × (List α))  => (p.1, x::p.2)) (select xs)

def deleteFirst [DecidableEq α]: α → List α → List α
  | _, [] => []
  | n, x::xs => if n == x then xs else x::(deleteFirst n xs)

def isPermutation [DecidableEq α] : List α → List α → Bool
| [], ys => (ys == [])
| x::xs, ys => x ∈ ys && (isPermutation xs (deleteFirst x ys))

α : Type u_1
xs : List α
inst✝ : DecidableEq α
⊢ ((List.map (fun p => isPermutation xs (p.1 :: p.2)) (select xs)).all fun x => x) = true


theorem prop_SelectPermutations' (xs: List α) (z: α) [DecidableEq α] :
  let n := count z xs
  (List.all
    (List.map
      (fun (p: α × List α) => n == (count z (p.1::p.2)))
      (select xs)
    )
    (fun x => x)
  ):= by sorry

import Mathlib

def select : List α → List (α × (List α))
  | [] => []
  | x :: xs =>
    ⟨x, xs⟩:: List.map (fun (p: α × (List α))  => (p.1, x::p.2)) (select xs)

def count [DecidableEq α]: α -> List α -> Nat
  | _z, []    => 0
  | z,  x::xs => if x==z then (count z xs).succ else count z xs

α : Type u_1
xs : List α
z : α
inst✝ : DecidableEq α
⊢ let n := count z xs;
  ((List.map (fun p => n == count z (p.1 :: p.2)) (select xs)).all fun x => x) = true


theorem prop_PairUnpair (xs: List α) [DecidableEq α] :
  Even (xs.length) → ((unpair (pairs xs)) == xs):= by sorry

import Mathlib

def pairs : List α → List (α × α)
  | x::y::xs => (x, y):: (pairs xs)
  | _ => []

def unpair : List (α × α) → List α
  | [] => []
  | (x, y)::xs => x :: y :: (unpair xs)

α : Type u_1
xs : List α
inst✝ : DecidableEq α
⊢ Even xs.length → (unpair (pairs xs) == xs) = true


theorem prop_PairEvens (xs: List α) [DecidableEq α] :
  Even (xs.length) → List.map Prod.fst (pairs xs) == evens xs:= by sorry

import Mathlib

def pairs : List α → List (α × α)
  | x::y::xs => (x, y):: (pairs xs)
  | _ => []

mutual
  def evens : List α → List α
  | [] => []
  | x::xs => x::(odds xs)
  def odds : List α → List α
  | [] => []
  | _x::xs => evens xs
end

α : Type u_1
xs : List α
inst✝ : DecidableEq α
⊢ Even xs.length → (List.map Prod.fst (pairs xs) == evens xs) = true


theorem prop_PairOdds (xs: List α) [DecidableEq α] :
  List.map Prod.snd (pairs xs) == odds xs:= by sorry

α : Type u_1
xs : List α
inst✝ : DecidableEq α
⊢ (List.map Prod.snd (pairs xs) == odds xs) = true


theorem prop_interleave (xs: List α) [DecidableEq α] :
  interleave (evens xs) (odds xs) == xs:= by sorry

import Mathlib

mutual
  def evens : List α → List α
  | [] => []
  | x::xs => x::(odds xs)
  def odds : List α → List α
  | [] => []
  | _x::xs => evens xs
end

def interleave : List α → List α → List α
  | (x::xs), ys => x :: interleave ys xs
  | [],      ys => ys
termination_by xs ys => xs.length + ys.length

α : Type u_1
xs : List α
inst✝ : DecidableEq α
⊢ (interleave (evens xs) (odds xs) == xs) = true


theorem prop_append_inj_1 (xs ys zs: List α) [DecidableEq α] :
  (xs ++ zs == ys ++ zs) → xs == ys:= by sorry

α : Type u_1
xs ys zs : List α
inst✝ : DecidableEq α
⊢ (xs ++ zs == ys ++ zs) = true → (xs == ys) = true


theorem prop_append_inj_2 (xs ys zs: List α) [DecidableEq α] :
  (xs ++ ys == xs ++ zs) → ys == zs:= by sorry

α : Type u_1
xs ys zs : List α
inst✝ : DecidableEq α
⊢ (xs ++ ys == xs ++ zs) = true → (ys == zs) = true


theorem prop_nub_nub (xs: List α) [DecidableEq α] :
  nub (nub xs) == nub xs:= by sorry

import Mathlib

def nub [DecidableEq α]: List α → List α
  | [] => []
  | x::xs => x::(nub (xs.filter (fun y => x != y)))
termination_by xs => xs.length
decreasing_by
  simp_wf
  rw [Nat.lt_succ]
  exact List.length_filter_le _ xs

α : Type u_1
xs : List α
inst✝ : DecidableEq α
⊢ (nub (nub xs) == nub xs) = true


theorem prop_elem_nub_l (x: α) (xs: List α) [DecidableEq α] :
  x ∈ xs → x ∈ nub xs:= by sorry

α : Type u_1
x : α
xs : List α
inst✝ : DecidableEq α
⊢ x ∈ xs → x ∈ nub xs


theorem prop_elem_nub_r (x: α) (xs: List α) [DecidableEq α] :
  x ∈ nub xs → x ∈ xs:= by sorry

α : Type u_1
x : α
xs : List α
inst✝ : DecidableEq α
⊢ x ∈ nub xs → x ∈ xs


theorem prop_count_nub (x: α) (xs: List α) [DecidableEq α] :
  x ∈ xs → (count x (nub xs) == 1):= by sorry

import Mathlib

def count [DecidableEq α]: α -> List α -> Nat
  | _z, []    => 0
  | z,  x::xs => if x==z then (count z xs).succ else count z xs

def nub [DecidableEq α]: List α → List α
  | [] => []
  | x::xs => x::(nub (xs.filter (fun y => x != y)))
termination_by xs => xs.length
decreasing_by
  simp_wf
  rw [Nat.lt_succ]
  exact List.length_filter_le _ xs

α : Type u_1
x : α
xs : List α
inst✝ : DecidableEq α
⊢ x ∈ xs → (count x (nub xs) == 1) = true


theorem prop_perm_trans (xs ys zs: List α) [DecidableEq α] :
  isPermutation xs ys → isPermutation ys zs → isPermutation xs zs:= by sorry

import Mathlib

def deleteFirst [DecidableEq α]: α → List α → List α
  | _, [] => []
  | n, x::xs => if n == x then xs else x::(deleteFirst n xs)

def isPermutation [DecidableEq α] : List α → List α → Bool
| [], ys => (ys == [])
| x::xs, ys => x ∈ ys && (isPermutation xs (deleteFirst x ys))

α : Type u_1
xs ys zs : List α
inst✝ : DecidableEq α
⊢ isPermutation xs ys = true → isPermutation ys zs = true → isPermutation xs zs = true


theorem prop_perm_refl (xs: List α) [DecidableEq α] :
  isPermutation xs xs:= by sorry

α : Type u_1
xs : List α
inst✝ : DecidableEq α
⊢ isPermutation xs xs = true


theorem prop_perm_symm (xs ys: List α) [DecidableEq α] :
  isPermutation xs ys → isPermutation ys xs:= by sorry

α : Type u_1
xs ys : List α
inst✝ : DecidableEq α
⊢ isPermutation xs ys = true → isPermutation ys xs = true


theorem prop_perm_elem (x: α) (xs ys: List α) [DecidableEq α] :
  x ∈ xs → isPermutation xs ys → x ∈ ys:= by sorry

α : Type u_1
x : α
xs ys : List α
inst✝ : DecidableEq α
⊢ x ∈ xs → isPermutation xs ys = true → x ∈ ys


theorem prop_deleteAll_count (x: α) (xs: List α) [DecidableEq α]:
  (delete x xs == deleteFirst x xs) → count x xs <= 1:= by sorry

import Mathlib

def delete [DecidableEq α]: α → List α → List α
  | _, [] => []
  | n, x::xs => if n == x then (delete n xs) else x::(delete n xs)

def count [DecidableEq α]: α -> List α -> Nat
  | _z, []    => 0
  | z,  x::xs => if x==z then (count z xs).succ else count z xs

def deleteFirst [DecidableEq α]: α → List α → List α
  | _, [] => []
  | n, x::xs => if n == x then xs else x::(deleteFirst n xs)

α : Type u_1
x : α
xs : List α
inst✝ : DecidableEq α
⊢ (delete x xs == deleteFirst x xs) = true → count x xs ≤ 1


theorem prop_elem (x: α) (xs: List α) [DecidableEq α] :
  x ∈ xs → ∃i, x == at' xs i:= by sorry

import Mathlib

def at' : List α → Nat → Option α
  | x::_, 0 => x
  | _::xs, n => at' xs (n - 1)
  | [], _ => none

α : Type u_1
x : α
xs : List α
inst✝ : DecidableEq α
⊢ x ∈ xs → ∃ i, (some x == at' xs i) = true


theorem prop_elem_map (y: β) (f: α → β) (xs: List α) [DecidableEq β] :
  y ∈ xs.map f → (∃x, (f x) == y ∧ x ∈ xs):= by sorry

β : Type u_1
α : Type u_2
y : β
f : α → β
xs : List α
inst✝ : DecidableEq β
⊢ y ∈ List.map f xs → ∃ x, (f x == y) = true ∧ x ∈ xs


theorem prop_Flatten1 (p: MyTree α) [DecidableEq α] :
  flatten1 [p] == flatten0 p:= by sorry

import Mathlib

inductive MyTree (α: Type) where
| leaf : MyTree α
| node : MyTree α → α → MyTree α  →  MyTree α

def f1Size : MyTree α → Nat
| MyTree.leaf => 1
| MyTree.node p _x q => f1Size p + f1Size q +
  (match p with
    | MyTree.leaf => 0
    | MyTree.node _a _b _c=> 2)

lemma f1Size_gt_zero (t: MyTree α): f1Size t > 0 := by
  induction t with
    | leaf => simp [f1Size]
    | node p _x q ih1 => simp [f1Size, ih1]

lemma f1Size_lt_subTrees  (q r: MyTree α) {x: α}: f1Size q < f1Size (MyTree.node q x r) ∧ f1Size r < f1Size (MyTree.node q x r) := by
  simp [f1Size]
  exact ⟨by linarith [f1Size_gt_zero r], by linarith [f1Size_gt_zero q]⟩;

def flatten0 : MyTree α → List α
  | MyTree.leaf => []
  | MyTree.node p x q => flatten0 p ++ [x] ++ flatten0 q

def flatten1 : List (MyTree α) → List α
  | []                                => []
  | MyTree.leaf::ps                   => flatten1 ps
  | (MyTree.node MyTree.leaf x q)::ps => x::(flatten1 (q::ps))
  | (MyTree.node (MyTree.node a b c) x q)::ps => flatten1 ((MyTree.node a b c)::(MyTree.node MyTree.leaf x q)::ps)
termination_by ps => List.sum (ps.map (fun (t: MyTree α ) => f1Size t))
decreasing_by
  simp_wf
  simp [f1Size]
  simp_wf
  simp [f1Size_lt_subTrees]
  simp_wf
  simp [f1Size]
  linarith

α : Type
p : MyTree α
inst✝ : DecidableEq α
⊢ (flatten1 [p] == flatten0 p) = true


theorem prop_Flatten1List (ps: List (MyTree α)) [DecidableEq α] :
  flatten1 ps == List.foldl (fun (ps2: List α) (t: MyTree α)  => ps2 ++ (flatten0 t) ) [] ps:= by sorry

α : Type
ps : List (MyTree α)
inst✝ : DecidableEq α
⊢ (flatten1 ps == List.foldl (fun ps2 t => ps2 ++ flatten0 t) [] ps) = true


theorem prop_Flatten2 (p: MyTree α) [DecidableEq α] :
  flatten2 p [] == flatten0 p:= by sorry

import Mathlib

inductive MyTree (α: Type) where
| leaf : MyTree α
| node : MyTree α → α → MyTree α  →  MyTree α

def flatten0 : MyTree α → List α
  | MyTree.leaf => []
  | MyTree.node p x q => flatten0 p ++ [x] ++ flatten0 q

def flatten2 : MyTree α -> List α -> List α
| MyTree.leaf, ys => ys
| MyTree.node p x q, ys => flatten2 p (x:: flatten2 q ys)

α : Type
p : MyTree α
inst✝ : DecidableEq α
⊢ (flatten2 p [] == flatten0 p) = true


theorem prop_Flatten3 (p: MyTree α) [DecidableEq α] :
  flatten3 p == flatten0 p:= by sorry

import Mathlib

inductive MyTree (α: Type) where
| leaf : MyTree α
| node : MyTree α → α → MyTree α  →  MyTree α

def flatten0 : MyTree α → List α
  | MyTree.leaf => []
  | MyTree.node p x q => flatten0 p ++ [x] ++ flatten0 q

def f3Size : MyTree α → Nat
| MyTree.leaf => 1
| MyTree.node p _x q => (f3Size p) * 2 + f3Size q

lemma f3Size_gt_zero (t: MyTree α): f3Size t > 0 := by
  induction t with
  | leaf => simp [f3Size]
  | node p _ q ih1 => simp [f3Size, ih1]

def flatten3 : MyTree α → List α
| MyTree.leaf => []
| MyTree.node (MyTree.node p x q) y r => flatten3 (MyTree.node p x (MyTree.node q y r))
| MyTree.node MyTree.leaf x q => x :: flatten3 q
termination_by t => f3Size t
decreasing_by
  simp_wf
  simp [f3Size]
  linarith [f3Size_gt_zero p]
  simp_wf
  simp [f3Size]

α : Type
p : MyTree α
inst✝ : DecidableEq α
⊢ (flatten3 p == flatten0 p) = true