theorem prop_01 (n: Nat) (xs: List α) :
 List.take n xs ++ List.drop n xs = xs:= by sorry

α : Type u_1
n : ℕ
xs : List α
⊢ List.take n xs ++ List.drop n xs = xs


theorem prop_02 (n: Nat) (xs: List Nat) (ys: List Nat) :
 List.count n xs + List.count n ys = List.count n (xs ++ ys):= by sorry

n : ℕ
xs ys : List ℕ
⊢ List.count n xs + List.count n ys = List.count n (xs ++ ys)


theorem prop_03 (n: Nat) (xs: List Nat) (ys: List Nat) :
 List.count n xs <= List.count n (xs ++ ys):= by sorry

n : ℕ
xs ys : List ℕ
⊢ List.count n xs ≤ List.count n (xs ++ ys)


theorem prop_04 (n: Nat) (xs: List Nat) :
  (List.count n xs).succ = List.count n (n :: xs):= by sorry

n : ℕ
xs : List ℕ
⊢ (List.count n xs).succ = List.count n (n :: xs)


theorem prop_05 (n: Nat) (x: Nat) (xs: List Nat) :
 (n = x) →  (List.count n xs).succ = List.count n (x :: xs):= by sorry

n x : ℕ
xs : List ℕ
⊢ n = x → (List.count n xs).succ = List.count n (x :: xs)


theorem prop_06 (n: Nat) (m: Nat) :
 (n - (n + m) = 0):= by sorry


theorem prop_07 (n: Nat) (m: Nat) :
 ((n + m) - n = m):= by sorry


theorem prop_08 (k:Nat) (m: Nat) (n: Nat) :
  ((k + m) - (k + n) = m - n):= by sorry


theorem prop_09 (i: Nat) (j: Nat) (k: Nat) :
  ((i - j) - k = i - (j + k)):= by sorry


theorem prop_10 (m: Nat) :
 (m - m = 0):= by sorry


theorem prop_11 (xs: List α) :
   (List.drop 0 xs = xs):= by sorry

α : Type u_1
xs : List α
⊢ List.drop 0 xs = xs


theorem prop_12 (n: Nat) (f: α → α) (xs: List α) :
   (List.drop n (List.map f xs) = List.map f (List.drop n xs)):= by sorry

α : Type u_1
n : ℕ
f : α → α
xs : List α
⊢ List.drop n (List.map f xs) = List.map f (List.drop n xs)


theorem prop_13 (n: Nat) (x: α) (xs: List α) :
 (List.drop n.succ (x :: xs) = List.drop n xs):= by sorry

α : Type u_1
n : ℕ
x : α
xs : List α
⊢ List.drop n.succ (x :: xs) = List.drop n xs


theorem prop_14 (p: α → Bool) (xs: List α) (ys: List α) :
  (List.filter p (xs ++ ys) = (List.filter p xs) ++ (List.filter p ys)):= by sorry

α : Type u_1
p : α → Bool
xs ys : List α
⊢ List.filter p (xs ++ ys) = List.filter p xs ++ List.filter p ys


theorem prop_15 (x: Nat) (xs: List Nat) :
  (List.length (ins x xs)) = (List.length xs).succ:= by sorry

import Mathlib

def ins: Nat → List Nat → List Nat
  | n, []  =>  [n]
  | n, x::xs => if (n < x) then n :: x :: xs else x :: (ins n xs)

x : ℕ
xs : List ℕ
⊢ (ins x xs).length = xs.length.succ


theorem prop_16 (x: Nat) (xs: List Nat) :
 xs = [] →  last (x::xs) = x:= by sorry

import Mathlib

def last: List Nat → Nat
  | [] => 0
  | [x] => x
  | _x::xs => (last xs)

x : ℕ
xs : List ℕ
⊢ xs = [] → last (x :: xs) = x


theorem prop_17 (n: Nat) :
  n <= 0 ↔ n = 0:= by sorry


theorem prop_18 i (m: Nat) :
   i < (i + m).succ:= by sorry


theorem prop_19 (n: Nat) (xs: List Nat) :
 (List.length (List.drop n xs) = List.length xs - n):= by sorry

n : ℕ
xs : List ℕ
⊢ (List.drop n xs).length = xs.length - n


theorem prop_20 (xs: List Nat) :
  (List.length (sort xs) = List.length xs):= by sorry

import Mathlib

def insort : Nat → List Nat → List Nat
  | n, [] => [n]
  | n, x::xs => if n <= x then n::x::xs else x::(insort n xs)

def sort : List Nat → List Nat
  | [] => []
  | x::xs => insort x (sort xs)

xs : List ℕ
⊢ (sort xs).length = xs.length


theorem prop_21 (n: Nat) (m: Nat) :
  n <= (n + m):= by sorry


theorem prop_22 (a: Nat) (b: Nat) (c: Nat) :
  (max (max a b) c = max a (max b c)):= by sorry

a b c : ℕ
⊢ max (max a b) c = max a (max b c)


theorem prop_23 (a: Nat) (b: Nat) :
  (max a b = max b a):= by sorry


theorem prop_24 (a: Nat) (b: Nat) :
 (((max a b) = a) ↔ b <= a):= by sorry


theorem prop_25 (a: Nat) (b: Nat) :
  (((max a b) = b) ↔ a <= b):= by sorry


theorem prop_26 (x: α) (xs: List α) (ys: List α) :
  x ∈ xs →  x ∈ (xs ++ ys):= by sorry

α : Type u_1
x : α
xs ys : List α
⊢ x ∈ xs → x ∈ xs ++ ys


theorem prop_27 (x: α) (xs: List α) (ys: List α) :
 x ∈ ys → x ∈ (xs ++ ys):= by sorry

α : Type u_1
x : α
xs ys : List α
⊢ x ∈ ys → x ∈ xs ++ ys


theorem prop_28 (x: α) (xs: List α) :
  x ∈ (xs ++ [x]):= by sorry

α : Type u_1
x : α
xs : List α
⊢ x ∈ xs ++ [x]


theorem prop_29 (x: Nat) (xs: List Nat) :
  x ∈ ins1 x xs:= by sorry

import Mathlib

def ins1 : Nat → List Nat → List Nat
  | n, [] => [n]
  | n, x::xs => if n == x then x::xs else x::(ins1 n xs)


theorem prop_30 (x: Nat) (xs: List Nat) :
  x ∈ ins x xs:= by sorry


theorem prop_31 (a: Nat) (b: Nat) (c: Nat) :
  min (min a b) c = min a (min b c):= by sorry

a b c : ℕ
⊢ min (min a b) c = min a (min b c)


theorem prop_32 (a: Nat) (b: Nat) :
  min a b = min b a:= by sorry


theorem prop_33 (a: Nat) (b: Nat) :
  min a b = a ↔ a <= b:= by sorry


theorem prop_34 (a: Nat) (b: Nat) :
  min a b = b ↔ b <= a:= by sorry


theorem prop_35 (xs: List α) :
  dropWhile (fun _ => False) xs = xs:= by sorry

import Mathlib

def dropWhile : (α → Bool) → List α → List α
  | _, [] => []
  | p, x::xs => if p x then dropWhile p xs else x::xs

α : Type u_1
xs : List α
⊢ dropWhile (fun x => decide False) xs = xs


theorem prop_36 (xs: List α) :
  takeWhile (fun _ => True) xs = xs:= by sorry

import Mathlib

def takeWhile : (α → Bool) → List α → List α
  | _, [] => []
  | p, x::xs => if p x then x ::(takeWhile p xs) else []

α : Type u_1
xs : List α
⊢ takeWhile (fun x => decide True) xs = xs


theorem prop_37 (x: Nat) (xs: List Nat) :
  not (x ∈ delete x xs):= by sorry

import Mathlib

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

x : ℕ
xs : List ℕ
⊢ (!decide (x ∈ delete x xs)) = true


theorem prop_38 (n: Nat) (xs: List Nat) :
  List.count n (xs ++ [n]) = (List.count n xs).succ:= by sorry

n : ℕ
xs : List ℕ
⊢ List.count n (xs ++ [n]) = (List.count n xs).succ


theorem prop_39 (n: Nat) (x: Nat) (xs: List Nat) :
  List.count n [x] + List.count n xs = List.count n (x::xs):= by sorry

n x : ℕ
xs : List ℕ
⊢ List.count n [x] + List.count n xs = List.count n (x :: xs)


theorem prop_40 (xs: List α) :
 (List.take 0 xs = []):= by sorry

α : Type u_1
xs : List α
⊢ List.take 0 xs = []


theorem prop_41 (n: Nat) (f: α → α) (xs: List α) :
  (List.take n (List.map f xs) = List.map f (List.take n xs)):= by sorry

α : Type u_1
n : ℕ
f : α → α
xs : List α
⊢ List.take n (List.map f xs) = List.map f (List.take n xs)


theorem prop_42 (n: Nat) (x: α) (xs: List α) :
  (List.take n.succ (x::xs) = x :: (List.take n xs)):= by sorry

α : Type u_1
n : ℕ
x : α
xs : List α
⊢ List.take n.succ (x :: xs) = x :: List.take n xs


theorem prop_43 (p: Nat → Bool) (xs: List Nat) :
  (takeWhile p xs ++ dropWhile p xs = xs):= by sorry

import Mathlib

def dropWhile : (α → Bool) → List α → List α
  | _, [] => []
  | p, x::xs => if p x then dropWhile p xs else x::xs

def takeWhile : (α → Bool) → List α → List α
  | _, [] => []
  | p, x::xs => if p x then x ::(takeWhile p xs) else []

p : ℕ → Bool
xs : List ℕ
⊢ takeWhile p xs ++ dropWhile p xs = xs


theorem prop_44 (x: α) (xs: List α) (ys: List β) :
  zip' (x::xs) ys = zipConcat x xs ys:= by sorry

import Mathlib

def zip' : List α → List β → List (α × β)
  | [], _ => []
  | _, [] => []
  | x::xs, y::ys => ⟨x, y⟩ :: zip' xs ys

def zipConcat : α → List α → List β → List (α × β)
  | _, _, [] => []
  | x, xs, y::ys => ⟨x, y⟩ :: zip' xs ys

α : Type u_1
β : Type u_2
x : α
xs : List α
ys : List β
⊢ zip' (x :: xs) ys = zipConcat x xs ys


theorem prop_45 (x: α) (y: β) (xs: List α) (ys: List β) :
  zip' (x::xs) (y::ys) = (x, y) :: zip' xs ys:= by sorry

import Mathlib

def zip' : List α → List β → List (α × β)
  | [], _ => []
  | _, [] => []
  | x::xs, y::ys => ⟨x, y⟩ :: zip' xs ys

α : Type u_1
β : Type u_2
x : α
y : β
xs : List α
ys : List β
⊢ zip' (x :: xs) (y :: ys) = (x, y) :: zip' xs ys


theorem prop_46 {α β: Type} (xs: List β) :
  zip' ([]: List α) xs = []:= by sorry

α β : Type
xs : List β
⊢ zip' [] xs = []


theorem prop_47 (a: MyTree α) :
  (height' (mirror a) = height' a):= by sorry

import Mathlib

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

def height' : MyTree α → ℕ
  | .leaf => 0
  | .node l _x r => (max (height' l) (height' r)).succ

def mirror : MyTree α → MyTree α
  | MyTree.leaf => MyTree.leaf
  | MyTree.node l x r => MyTree.node r x l

α : Type
a : MyTree α
⊢ height' (mirror a) = height' a


theorem prop_48 (xs: List Nat) :
  not (null xs) → butlast xs ++ [last xs] = xs:= by sorry

import Mathlib

def last: List Nat → Nat
  | [] => 0
  | [x] => x
  | _x::xs => (last xs)

def null : List α → Bool
  | [] => True
  | _ => False

def butlast : List α → List α
  | [] => []
  | [_x] => []
  | x::xs => x::(butlast xs)

xs : List ℕ
⊢ (!null xs) = true → butlast xs ++ [last xs] = xs


theorem prop_49 (xs: List Nat) (ys: List Nat) :
  (butlast (xs ++ ys) = butlastConcat xs ys):= by sorry

import Mathlib

def butlast : List α → List α
  | [] => []
  | [_x] => []
  | x::xs => x::(butlast xs)

def butlastConcat : List α → List α → List α
  | xs, [] => butlast xs
  | xs, ys => xs ++ butlast ys

xs ys : List ℕ
⊢ butlast (xs ++ ys) = butlastConcat xs ys


theorem prop_50 (xs: List α) :
  (butlast xs = List.take (List.length xs - 1) xs):= by sorry

import Mathlib

def butlast : List α → List α
  | [] => []
  | [_x] => []
  | x::xs => x::(butlast xs)

α : Type u_1
xs : List α
⊢ butlast xs = List.take (xs.length - 1) xs


theorem prop_51 (xs: List α) (x: α) :
  (butlast (xs ++ [x]) = xs):= by sorry

α : Type u_1
xs : List α
x : α
⊢ butlast (xs ++ [x]) = xs


theorem prop_52 (n: Nat) xs :
  (List.count n xs = List.count n (List.reverse xs)):= by sorry

n : ℕ
xs : List ℕ
⊢ List.count n xs = List.count n xs.reverse


theorem prop_53 (n: Nat) xs :
  (List.count n xs = List.count n (sort xs)):= by sorry

n : ℕ
xs : List ℕ
⊢ List.count n xs = List.count n (sort xs)


theorem prop_54 (n: Nat) (m: Nat) :
  ((m + n) - n = m):= by sorry


theorem prop_55 (n: Nat) (xs: List α) (ys: List α) :
  (List.drop n (xs ++ ys) = List.drop n xs ++ List.drop (n - List.length xs) ys):= by sorry

α : Type u_1
n : ℕ
xs ys : List α
⊢ List.drop n (xs ++ ys) = List.drop n xs ++ List.drop (n - xs.length) ys


theorem prop_56 (n: Nat) (m: Nat) (xs: List α) :
  (List.drop n (List.drop m xs) = List.drop (n + m) xs):= by sorry

α : Type u_1
n m : ℕ
xs : List α
⊢ List.drop n (List.drop m xs) = List.drop (n + m) xs


theorem prop_57 (n: Nat) (m: Nat) (xs: List α) :
  (List.drop n (List.take m xs) = List.take (m - n) (List.drop n xs)):= by sorry

α : Type u_1
n m : ℕ
xs : List α
⊢ List.drop n (List.take m xs) = List.take (m - n) (List.drop n xs)


theorem prop_58 (n: Nat) (xs: List α) (ys: List β) :
  (List.drop n (zip' xs ys) =  zip' (List.drop n xs) (List.drop n ys)):= by sorry

α : Type u_1
β : Type u_2
n : ℕ
xs : List α
ys : List β
⊢ List.drop n (zip' xs ys) = zip' (List.drop n xs) (List.drop n ys)


theorem prop_59 (xs: List Nat) (ys: List Nat) :
  ys = [] →  last (xs ++ ys) = last xs:= by sorry

xs ys : List ℕ
⊢ ys = [] → last (xs ++ ys) = last xs


theorem prop_60 (xs: List Nat) (ys: List Nat) :
  not (null ys) → last (xs ++ ys) = last ys:= by sorry

import Mathlib

def last: List Nat → Nat
  | [] => 0
  | [x] => x
  | _x::xs => (last xs)

def null : List α → Bool
  | [] => True
  | _ => False

xs ys : List ℕ
⊢ (!null ys) = true → last (xs ++ ys) = last ys


theorem prop_61 (xs: List Nat) (ys: List Nat) :
  (last (xs ++ ys) = lastOfTwo xs ys):= by sorry

import Mathlib

def last: List Nat → Nat
  | [] => 0
  | [x] => x
  | _x::xs => (last xs)

def lastOfTwo : List ℕ → List ℕ → ℕ
  | xs, [] => last xs
  | _, ys => last ys

xs ys : List ℕ
⊢ last (xs ++ ys) = lastOfTwo xs ys


theorem prop_62 (xs: List Nat) (x: Nat) :
  not (null xs) → last (x::xs) = last xs:= by sorry

xs : List ℕ
x : ℕ
⊢ (!null xs) = true → last (x :: xs) = last xs


theorem prop_63 (n: Nat) (xs: List Nat) :
  n < List.length xs → last (List.drop n xs) = last xs:= by sorry

n : ℕ
xs : List ℕ
⊢ n < xs.length → last (List.drop n xs) = last xs


theorem prop_64 x xs :
  (last (xs ++ [x]) = x):= by sorry

x : ℕ
xs : List ℕ
⊢ last (xs ++ [x]) = x


theorem prop_65 (i: Nat) (m: Nat) :
  i < (m + i).succ:= by sorry


theorem prop_66 (p: α → Bool) (xs: List α) :
  List.length (List.filter p xs) <= List.length xs:= by sorry

α : Type u_1
p : α → Bool
xs : List α
⊢ (List.filter p xs).length ≤ xs.length


theorem prop_67 (xs: List Nat) :
  List.length (butlast xs) = List.length xs - 1:= by sorry

xs : List ℕ
⊢ (butlast xs).length = xs.length - 1


theorem prop_68 (n: Nat) (xs: List Nat) :
  List.length (delete n xs) <= List.length xs:= by sorry

n : ℕ
xs : List ℕ
⊢ (delete n xs).length ≤ xs.length


theorem prop_69 (n: Nat) (m: Nat) :
  n <= (m + n):= by sorry


theorem prop_70 m (n: Nat) :
  m <= n → m <= n.succ:= by sorry


theorem prop_71 (x:Nat) (y :Nat) (xs: List Nat) :
  (x == y) = False → ((x ∈ (ins y xs)) == (x ∈ xs)):= by sorry

x y : ℕ
xs : List ℕ
⊢ ((x == y) = true) = False → (decide (x ∈ ins y xs) == decide (x ∈ xs)) = true


theorem prop_72 (i: Nat) (xs: List α) :
  (List.reverse (List.drop i xs) = List.take (List.length xs - i) (List.reverse xs)):= by sorry

α : Type u_1
i : ℕ
xs : List α
⊢ (List.drop i xs).reverse = List.take (xs.length - i) xs.reverse


theorem prop_73 (p: α → Bool) (xs: List α) :
  (List.reverse (List.filter p xs) = List.filter p (List.reverse xs)):= by sorry

α : Type u_1
p : α → Bool
xs : List α
⊢ (List.filter p xs).reverse = List.filter p xs.reverse


theorem prop_74 (i: Nat) (xs: List α) :
  (List.reverse (List.take i xs) = List.drop (List.length xs - i) (List.reverse xs)):= by sorry

α : Type u_1
i : ℕ
xs : List α
⊢ (List.take i xs).reverse = List.drop (xs.length - i) xs.reverse


theorem prop_75 (n: Nat) (m: Nat ) (xs: List Nat) :
  (List.count n xs + List.count n [m] = List.count n (m :: xs)):= by sorry

n m : ℕ
xs : List ℕ
⊢ List.count n xs + List.count n [m] = List.count n (m :: xs)


theorem prop_76 (n: Nat) (m: Nat) (xs: List Nat) :
  (n == m) = False → List.count n (xs ++ [m]) = List.count n xs:= by sorry

n m : ℕ
xs : List ℕ
⊢ ((n == m) = true) = False → List.count n (xs ++ [m]) = List.count n xs


theorem prop_77 (x: Nat) (xs: List Nat) :
  sorted xs → sorted (insort x xs):= by sorry

import Mathlib

def insort : Nat → List Nat → List Nat
  | n, [] => [n]
  | n, x::xs => if n <= x then n::x::xs else x::(insort n xs)

def sorted : List Nat → Bool
  | [] => True
  | [_x] => True
  | x::y::xs => and (x <= y) (sorted (y::xs))

x : ℕ
xs : List ℕ
⊢ sorted xs = true → sorted (insort x xs) = true


theorem prop_78 (xs: List Nat) :
  sorted (sort xs):= by sorry

import Mathlib

def insort : Nat → List Nat → List Nat
  | n, [] => [n]
  | n, x::xs => if n <= x then n::x::xs else x::(insort n xs)

def sort : List Nat → List Nat
  | [] => []
  | x::xs => insort x (sort xs)

def sorted : List Nat → Bool
  | [] => True
  | [_x] => True
  | x::y::xs => and (x <= y) (sorted (y::xs))


theorem prop_79 (m: Nat) (n: Nat) (k: Nat) :
  ((m.succ - n) - k.succ = (m - n) - k):= by sorry

m n k : ℕ
⊢ m.succ - n - k.succ = m - n - k


theorem prop_80 (n: Nat) (xs: List α) (ys: List α) :
  (List.take n (xs ++ ys) = List.take n xs ++ List.take (n - List.length xs) ys):= by sorry

α : Type u_1
n : ℕ
xs ys : List α
⊢ List.take n (xs ++ ys) = List.take n xs ++ List.take (n - xs.length) ys


theorem prop_81 (n: Nat) (m: Nat) (xs: List α)  :
  (List.take n (List.drop m xs) = List.drop m (List.take (n + m) xs)):= by sorry

α : Type u_1
n m : ℕ
xs : List α
⊢ List.take n (List.drop m xs) = List.drop m (List.take (n + m) xs)


theorem prop_82 (n: Nat) (xs: List α) (ys: List β) :
  (List.take n (zip' xs ys) = zip' (List.take n xs) (List.take n ys)):= by sorry

α : Type u_1
β : Type u_2
n : ℕ
xs : List α
ys : List β
⊢ List.take n (zip' xs ys) = zip' (List.take n xs) (List.take n ys)


theorem prop_83 (xs: List α) (ys: List α) (zs: List β) :
  (zip' (xs ++ ys) zs =
           zip' xs (List.take (List.length xs) zs) ++ zip' ys (List.drop (List.length xs) zs)):= by sorry

α : Type u_1
β : Type u_2
xs ys : List α
zs : List β
⊢ zip' (xs ++ ys) zs = zip' xs (List.take xs.length zs) ++ zip' ys (List.drop xs.length zs)


theorem prop_84 (xs: List α) (ys: List β) (zs: List β) :
  (zip' xs (ys ++ zs) =
           zip' (List.take (List.length ys) xs) ys ++ zip' (List.drop (List.length ys) xs) zs):= by sorry

α : Type u_1
β : Type u_2
xs : List α
ys zs : List β
⊢ zip' xs (ys ++ zs) = zip' (List.take ys.length xs) ys ++ zip' (List.drop ys.length xs) zs


theorem prop_85 (xs: List α) (ys: List β) :
  (List.length xs = List.length ys) →
    (zip' (List.reverse xs) (List.reverse ys) = List.reverse (zip' xs ys)):= by sorry

α : Type u_1
β : Type u_2
xs : List α
ys : List β
⊢ xs.length = ys.length → zip' xs.reverse ys.reverse = (zip' xs ys).reverse


theorem prop_86 (x: Nat) (y: Nat) (xs: List Nat) :
  x < y → ((x ∈ (ins y xs)) == (x ∈ xs)):= by sorry

import Mathlib

def ins: Nat → List Nat → List Nat
  | n, []  =>  [n]
  | n, x::xs => if (n < x) then n :: x :: xs else x :: (ins n xs)

x y : ℕ
xs : List ℕ
⊢ x < y → (decide (x ∈ ins y xs) == decide (x ∈ xs)) = true


theorem prop_ISortSorts (xs: List Nat) : ordered (isort xs) == True:= by sorry

import Mathlib

def ordered : List Nat -> Bool
| []       => True
| [_x]     => True
| x::y::xs => x <= y && ordered (y::xs)

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)

xs : List ℕ
⊢ (ordered (isort xs) == decide True) = true


theorem prop_ISortCount (x: Nat) (xs: List Nat) : count x (isort xs) == count x xs:= by sorry

import Mathlib

def count [DecidableEq α] : α → List α → Nat
| _x, [] => 0
| x, y::ys => if x == y then 1 + (count x ys) else count x ys

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)

x : ℕ
xs : List ℕ
⊢ (count x (isort xs) == count x xs) = true


theorem prop_ISortPermutes (xs: List Nat) : isPermutation (isort xs) xs == True:= 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))

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)

xs : List ℕ
⊢ (isPermutation (isort xs) xs == decide True) = true


theorem prop_BubSortSorts (xs: List Nat) : ordered (bubsort xs) == true:= by sorry

import Mathlib

def ordered : List Nat -> Bool
| []       => True
| [_x]     => True
| x::y::xs => x <= y && ordered (y::xs)

def bubsort (xs : List Nat) : {l' : List Nat // xs.length = l'.length} :=
  match xs with
  | [] => ⟨[], rfl⟩
  | x :: xs =>
    match bubsort xs with
    | ⟨[], h⟩ => ⟨[x], by simp[h]⟩
    | ⟨y :: ys, h⟩ =>
      if y < x then
        have : Nat.succ (List.length ys) < Nat.succ (List.length xs) := by rw [h, List.length_cons]; apply Nat.lt_succ_self
        let ⟨zs, he⟩ := bubsort (x :: ys)
        ⟨y :: zs, by simp[h, ← he]⟩
      else
        ⟨x :: y :: ys, by simp[h]⟩
termination_by xs.length

xs : List ℕ
⊢ (ordered ↑(bubsort xs) == true) = true


theorem prop_BubSortCount (x: Nat) (xs: List Nat) : count x (bubsort xs) == count x xs:= by sorry

import Mathlib

def count [DecidableEq α] : α → List α → Nat
| _x, [] => 0
| x, y::ys => if x == y then 1 + (count x ys) else count x ys

def bubsort (xs : List Nat) : {l' : List Nat // xs.length = l'.length} :=
  match xs with
  | [] => ⟨[], rfl⟩
  | x :: xs =>
    match bubsort xs with
    | ⟨[], h⟩ => ⟨[x], by simp[h]⟩
    | ⟨y :: ys, h⟩ =>
      if y < x then
        have : Nat.succ (List.length ys) < Nat.succ (List.length xs) := by rw [h, List.length_cons]; apply Nat.lt_succ_self
        let ⟨zs, he⟩ := bubsort (x :: ys)
        ⟨y :: zs, by simp[h, ← he]⟩
      else
        ⟨x :: y :: ys, by simp[h]⟩
termination_by xs.length

x : ℕ
xs : List ℕ
⊢ (count x ↑(bubsort xs) == count x xs) = true


theorem prop_BubSortPermutes (xs: List Nat) : isPermutation (bubsort xs) xs == true:= 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))

def bubsort (xs : List Nat) : {l' : List Nat // xs.length = l'.length} :=
  match xs with
  | [] => ⟨[], rfl⟩
  | x :: xs =>
    match bubsort xs with
    | ⟨[], h⟩ => ⟨[x], by simp[h]⟩
    | ⟨y :: ys, h⟩ =>
      if y < x then
        have : Nat.succ (List.length ys) < Nat.succ (List.length xs) := by rw [h, List.length_cons]; apply Nat.lt_succ_self
        let ⟨zs, he⟩ := bubsort (x :: ys)
        ⟨y :: zs, by simp[h, ← he]⟩
      else
        ⟨x :: y :: ys, by simp[h]⟩
termination_by xs.length

xs : List ℕ
⊢ (isPermutation (↑(bubsort xs)) xs == true) = true


theorem prop_BubSortIsSort (xs: List Nat) : bubblesort xs == isort xs:= by sorry

import Mathlib

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 bubsort (xs : List Nat) : {l' : List Nat // xs.length = l'.length} :=
  match xs with
  | [] => ⟨[], rfl⟩
  | x :: xs =>
    match bubsort xs with
    | ⟨[], h⟩ => ⟨[x], by simp[h]⟩
    | ⟨y :: ys, h⟩ =>
      if y < x then
        have : Nat.succ (List.length ys) < Nat.succ (List.length xs) := by rw [h, List.length_cons]; apply Nat.lt_succ_self
        let ⟨zs, he⟩ := bubsort (x :: ys)
        ⟨y :: zs, by simp[h, ← he]⟩
      else
        ⟨x :: y :: ys, by simp[h]⟩
termination_by xs.length

def bubblesort (xs: List Nat) : List Nat :=
  bubsort xs

xs : List ℕ
⊢ (bubblesort xs == isort xs) = true


theorem prop_HSortSorts (xs: List Nat) : ordered (hsort xs) == True:= by sorry

import Mathlib

def ordered : List Nat -> Bool
| []       => True
| [_x]     => True
| x::y::xs => x <= y && ordered (y::xs)

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

lemma hpairwise_desc (ps: List MyHeap): List.length (hpairwise ps) ≤ List.length ps := by
  generalize hl: ps.length = len
  induction len using Nat.strong_induction_on generalizing ps with
  | h len2 ih =>
    match ps with
      | [] => unfold hpairwise; simp
      | q1::qs1 => match qs1 with
        | [] => unfold hpairwise; rw [←hl]
        | q2::qs2 => unfold hpairwise; rw [← hl, List.length_cons, List.length_cons, List.length_cons,Nat.succ_le_succ_iff]
                     rw [List.length_cons, List.length_cons] at hl
                     have hl3 := Nat.lt_of_succ_lt (Nat.lt_of_succ_le (Nat.le_of_eq hl))
                     exact Nat.le.step (ih (qs2.length) hl3 qs2 rfl)

def hmerging : List MyHeap → MyHeap
| [] => MyHeap.nil
| [p] => p
| p::q::ps =>
    have : List.length (hpairwise (p :: q :: ps)) < Nat.succ (Nat.succ (List.length ps)) := by
      unfold hpairwise
      rw [List.length_cons, Nat.succ_lt_succ_iff, Nat.lt_succ]
      exact hpairwise_desc _
    hmerging (hpairwise (p::q::ps))
termination_by ps => ps.length

def toHeap : List Nat → MyHeap
| xs => hmerging (xs.map (fun x => MyHeap.node MyHeap.nil x MyHeap.nil))

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

lemma 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
  have h': numElem (MyHeap.node q x r) = 1 + numElem q + numElem r; rfl
  rw [h'];
  exact ⟨by linarith, by linarith⟩;

lemma merge_elems (p q: MyHeap): numElem p + numElem q = numElem (hmerge p q) := by
  generalize hsp: numElem p = sp
  generalize hsq: numElem q = sq
  generalize hspq: numElem (hmerge p q) = spq
  induction sp using Nat.strong_induction_on generalizing p q sq spq with
  | h sp2 ih =>   induction sq using Nat.strong_induction_on generalizing p q sp2 spq with
    | h sq2 ih2 =>
    rw [←hsp, ← hsq, ← hspq];
    unfold hmerge;
    split;
    case h_1 _ _;
      unfold numElem; rw [Nat.add_comm, Nat.add_zero];
    case h_2 _ _;
      unfold numElem; rw [Nat.add_zero];
    case h_3 _ _ pl x pr ql y qr;
      split;
        unfold numElem;
        suffices h': numElem (hmerge pr (MyHeap.node ql y qr)) = numElem pr + (1 + numElem ql + numElem qr);
          rw[h']; linarith;
        rw [←hsp] at ih;
        exact Eq.symm (ih (numElem pr) (numElem_lt_subHeaps _ _).2 pr (MyHeap.node ql y qr) rfl
          (numElem (MyHeap.node ql y qr)) rfl (numElem (hmerge pr (MyHeap.node ql y qr))) rfl);
        unfold numElem;
        suffices h': numElem (hmerge (MyHeap.node pl x pr) qr) = numElem qr + (1 + numElem pl + numElem pr);
          rw[h']; linarith;
        rw [←hsq] at ih2;
        have h':= ih2 (numElem qr) (numElem_lt_subHeaps _ _).2 sp2 ih (MyHeap.node pl x pr) qr hsp rfl
          (numElem (hmerge (MyHeap.node pl x pr) qr)) rfl;
        rw [←hsp] at h';
        suffices h'': 1 + numElem pl + numElem pr = numElem (MyHeap.node pl x pr);
          rw [h'']; linarith;
        rfl;

lemma numElem_merge_branches_lt (p q: MyHeap) (x: Nat): numElem (hmerge p q) < numElem (MyHeap.node p x q) := by
  rw [←merge_elems _ _];
  have h': numElem (MyHeap.node p x q) = 1 + numElem p + numElem q; rfl
  rw [h']
  linarith;

def toList : MyHeap → List Nat
| MyHeap.nil => []
| MyHeap.node p x q =>
    have _h := numElem_merge_branches_lt p q x
    x :: toList (hmerge p q)
termination_by hp => numElem hp

def hsort : List Nat → List Nat
 | xs => toList (toHeap xs)

xs : List ℕ
⊢ (ordered (hsort xs) == decide True) = true


theorem prop_HSortCount (x: Nat) (xs: List Nat) : count x (hsort xs) == count x xs:= by sorry

import Mathlib

def count [DecidableEq α] : α → List α → Nat
| _x, [] => 0
| x, y::ys => if x == y then 1 + (count x ys) else count x ys

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

lemma hpairwise_desc (ps: List MyHeap): List.length (hpairwise ps) ≤ List.length ps := by
  generalize hl: ps.length = len
  induction len using Nat.strong_induction_on generalizing ps with
  | h len2 ih =>
    match ps with
      | [] => unfold hpairwise; simp
      | q1::qs1 => match qs1 with
        | [] => unfold hpairwise; rw [←hl]
        | q2::qs2 => unfold hpairwise; rw [← hl, List.length_cons, List.length_cons, List.length_cons,Nat.succ_le_succ_iff]
                     rw [List.length_cons, List.length_cons] at hl
                     have hl3 := Nat.lt_of_succ_lt (Nat.lt_of_succ_le (Nat.le_of_eq hl))
                     exact Nat.le.step (ih (qs2.length) hl3 qs2 rfl)

def hmerging : List MyHeap → MyHeap
| [] => MyHeap.nil
| [p] => p
| p::q::ps =>
    have : List.length (hpairwise (p :: q :: ps)) < Nat.succ (Nat.succ (List.length ps)) := by
      unfold hpairwise
      rw [List.length_cons, Nat.succ_lt_succ_iff, Nat.lt_succ]
      exact hpairwise_desc _
    hmerging (hpairwise (p::q::ps))
termination_by ps => ps.length

def toHeap : List Nat → MyHeap
| xs => hmerging (xs.map (fun x => MyHeap.node MyHeap.nil x MyHeap.nil))

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

lemma 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
  have h': numElem (MyHeap.node q x r) = 1 + numElem q + numElem r; rfl
  rw [h'];
  exact ⟨by linarith, by linarith⟩;

lemma merge_elems (p q: MyHeap): numElem p + numElem q = numElem (hmerge p q) := by
  generalize hsp: numElem p = sp
  generalize hsq: numElem q = sq
  generalize hspq: numElem (hmerge p q) = spq
  induction sp using Nat.strong_induction_on generalizing p q sq spq with
  | h sp2 ih =>   induction sq using Nat.strong_induction_on generalizing p q sp2 spq with
    | h sq2 ih2 =>
    rw [←hsp, ← hsq, ← hspq];
    unfold hmerge;
    split;
    case h_1 _ _;
      unfold numElem; rw [Nat.add_comm, Nat.add_zero];
    case h_2 _ _;
      unfold numElem; rw [Nat.add_zero];
    case h_3 _ _ pl x pr ql y qr;
      split;
        unfold numElem;
        suffices h': numElem (hmerge pr (MyHeap.node ql y qr)) = numElem pr + (1 + numElem ql + numElem qr);
          rw[h']; linarith;
        rw [←hsp] at ih;
        exact Eq.symm (ih (numElem pr) (numElem_lt_subHeaps _ _).2 pr (MyHeap.node ql y qr) rfl
          (numElem (MyHeap.node ql y qr)) rfl (numElem (hmerge pr (MyHeap.node ql y qr))) rfl);
        unfold numElem;
        suffices h': numElem (hmerge (MyHeap.node pl x pr) qr) = numElem qr + (1 + numElem pl + numElem pr);
          rw[h']; linarith;
        rw [←hsq] at ih2;
        have h':= ih2 (numElem qr) (numElem_lt_subHeaps _ _).2 sp2 ih (MyHeap.node pl x pr) qr hsp rfl
          (numElem (hmerge (MyHeap.node pl x pr) qr)) rfl;
        rw [←hsp] at h';
        suffices h'': 1 + numElem pl + numElem pr = numElem (MyHeap.node pl x pr);
          rw [h'']; linarith;
        rfl;

lemma numElem_merge_branches_lt (p q: MyHeap) (x: Nat): numElem (hmerge p q) < numElem (MyHeap.node p x q) := by
  rw [←merge_elems _ _];
  have h': numElem (MyHeap.node p x q) = 1 + numElem p + numElem q; rfl
  rw [h']
  linarith;

def toList : MyHeap → List Nat
| MyHeap.nil => []
| MyHeap.node p x q =>
    have _h := numElem_merge_branches_lt p q x
    x :: toList (hmerge p q)
termination_by hp => numElem hp

def hsort : List Nat → List Nat
 | xs => toList (toHeap xs)

x : ℕ
xs : List ℕ
⊢ (count x (hsort xs) == count x xs) = true


theorem prop_HSortPermutes (xs: List Nat) : isPermutation (hsort xs) xs == True:= 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))

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

lemma hpairwise_desc (ps: List MyHeap): List.length (hpairwise ps) ≤ List.length ps := by
  generalize hl: ps.length = len
  induction len using Nat.strong_induction_on generalizing ps with
  | h len2 ih =>
    match ps with
      | [] => unfold hpairwise; simp
      | q1::qs1 => match qs1 with
        | [] => unfold hpairwise; rw [←hl]
        | q2::qs2 => unfold hpairwise; rw [← hl, List.length_cons, List.length_cons, List.length_cons,Nat.succ_le_succ_iff]
                     rw [List.length_cons, List.length_cons] at hl
                     have hl3 := Nat.lt_of_succ_lt (Nat.lt_of_succ_le (Nat.le_of_eq hl))
                     exact Nat.le.step (ih (qs2.length) hl3 qs2 rfl)

def hmerging : List MyHeap → MyHeap
| [] => MyHeap.nil
| [p] => p
| p::q::ps =>
    have : List.length (hpairwise (p :: q :: ps)) < Nat.succ (Nat.succ (List.length ps)) := by
      unfold hpairwise
      rw [List.length_cons, Nat.succ_lt_succ_iff, Nat.lt_succ]
      exact hpairwise_desc _
    hmerging (hpairwise (p::q::ps))
termination_by ps => ps.length

def toHeap : List Nat → MyHeap
| xs => hmerging (xs.map (fun x => MyHeap.node MyHeap.nil x MyHeap.nil))

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

lemma 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
  have h': numElem (MyHeap.node q x r) = 1 + numElem q + numElem r; rfl
  rw [h'];
  exact ⟨by linarith, by linarith⟩;

lemma merge_elems (p q: MyHeap): numElem p + numElem q = numElem (hmerge p q) := by
  generalize hsp: numElem p = sp
  generalize hsq: numElem q = sq
  generalize hspq: numElem (hmerge p q) = spq
  induction sp using Nat.strong_induction_on generalizing p q sq spq with
  | h sp2 ih =>   induction sq using Nat.strong_induction_on generalizing p q sp2 spq with
    | h sq2 ih2 =>
    rw [←hsp, ← hsq, ← hspq];
    unfold hmerge;
    split;
    case h_1 _ _;
      unfold numElem; rw [Nat.add_comm, Nat.add_zero];
    case h_2 _ _;
      unfold numElem; rw [Nat.add_zero];
    case h_3 _ _ pl x pr ql y qr;
      split;
        unfold numElem;
        suffices h': numElem (hmerge pr (MyHeap.node ql y qr)) = numElem pr + (1 + numElem ql + numElem qr);
          rw[h']; linarith;
        rw [←hsp] at ih;
        exact Eq.symm (ih (numElem pr) (numElem_lt_subHeaps _ _).2 pr (MyHeap.node ql y qr) rfl
          (numElem (MyHeap.node ql y qr)) rfl (numElem (hmerge pr (MyHeap.node ql y qr))) rfl);
        unfold numElem;
        suffices h': numElem (hmerge (MyHeap.node pl x pr) qr) = numElem qr + (1 + numElem pl + numElem pr);
          rw[h']; linarith;
        rw [←hsq] at ih2;
        have h':= ih2 (numElem qr) (numElem_lt_subHeaps _ _).2 sp2 ih (MyHeap.node pl x pr) qr hsp rfl
          (numElem (hmerge (MyHeap.node pl x pr) qr)) rfl;
        rw [←hsp] at h';
        suffices h'': 1 + numElem pl + numElem pr = numElem (MyHeap.node pl x pr);
          rw [h'']; linarith;
        rfl;

lemma numElem_merge_branches_lt (p q: MyHeap) (x: Nat): numElem (hmerge p q) < numElem (MyHeap.node p x q) := by
  rw [←merge_elems _ _];
  have h': numElem (MyHeap.node p x q) = 1 + numElem p + numElem q; rfl
  rw [h']
  linarith;

def toList : MyHeap → List Nat
| MyHeap.nil => []
| MyHeap.node p x q =>
    have _h := numElem_merge_branches_lt p q x
    x :: toList (hmerge p q)
termination_by hp => numElem hp

def hsort : List Nat → List Nat
 | xs => toList (toHeap xs)

xs : List ℕ
⊢ (isPermutation (hsort xs) xs == decide True) = true


theorem prop_HSortIsSort (xs: List Nat) : hsort xs == isort xs:= by sorry

import Mathlib

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)

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

lemma hpairwise_desc (ps: List MyHeap): List.length (hpairwise ps) ≤ List.length ps := by
  generalize hl: ps.length = len
  induction len using Nat.strong_induction_on generalizing ps with
  | h len2 ih =>
    match ps with
      | [] => unfold hpairwise; simp
      | q1::qs1 => match qs1 with
        | [] => unfold hpairwise; rw [←hl]
        | q2::qs2 => unfold hpairwise; rw [← hl, List.length_cons, List.length_cons, List.length_cons,Nat.succ_le_succ_iff]
                     rw [List.length_cons, List.length_cons] at hl
                     have hl3 := Nat.lt_of_succ_lt (Nat.lt_of_succ_le (Nat.le_of_eq hl))
                     exact Nat.le.step (ih (qs2.length) hl3 qs2 rfl)

def hmerging : List MyHeap → MyHeap
| [] => MyHeap.nil
| [p] => p
| p::q::ps =>
    have : List.length (hpairwise (p :: q :: ps)) < Nat.succ (Nat.succ (List.length ps)) := by
      unfold hpairwise
      rw [List.length_cons, Nat.succ_lt_succ_iff, Nat.lt_succ]
      exact hpairwise_desc _
    hmerging (hpairwise (p::q::ps))
termination_by ps => ps.length

def toHeap : List Nat → MyHeap
| xs => hmerging (xs.map (fun x => MyHeap.node MyHeap.nil x MyHeap.nil))

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

lemma 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
  have h': numElem (MyHeap.node q x r) = 1 + numElem q + numElem r; rfl
  rw [h'];
  exact ⟨by linarith, by linarith⟩;

lemma merge_elems (p q: MyHeap): numElem p + numElem q = numElem (hmerge p q) := by
  generalize hsp: numElem p = sp
  generalize hsq: numElem q = sq
  generalize hspq: numElem (hmerge p q) = spq
  induction sp using Nat.strong_induction_on generalizing p q sq spq with
  | h sp2 ih =>   induction sq using Nat.strong_induction_on generalizing p q sp2 spq with
    | h sq2 ih2 =>
    rw [←hsp, ← hsq, ← hspq];
    unfold hmerge;
    split;
    case h_1 _ _;
      unfold numElem; rw [Nat.add_comm, Nat.add_zero];
    case h_2 _ _;
      unfold numElem; rw [Nat.add_zero];
    case h_3 _ _ pl x pr ql y qr;
      split;
        unfold numElem;
        suffices h': numElem (hmerge pr (MyHeap.node ql y qr)) = numElem pr + (1 + numElem ql + numElem qr);
          rw[h']; linarith;
        rw [←hsp] at ih;
        exact Eq.symm (ih (numElem pr) (numElem_lt_subHeaps _ _).2 pr (MyHeap.node ql y qr) rfl
          (numElem (MyHeap.node ql y qr)) rfl (numElem (hmerge pr (MyHeap.node ql y qr))) rfl);
        unfold numElem;
        suffices h': numElem (hmerge (MyHeap.node pl x pr) qr) = numElem qr + (1 + numElem pl + numElem pr);
          rw[h']; linarith;
        rw [←hsq] at ih2;
        have h':= ih2 (numElem qr) (numElem_lt_subHeaps _ _).2 sp2 ih (MyHeap.node pl x pr) qr hsp rfl
          (numElem (hmerge (MyHeap.node pl x pr) qr)) rfl;
        rw [←hsp] at h';
        suffices h'': 1 + numElem pl + numElem pr = numElem (MyHeap.node pl x pr);
          rw [h'']; linarith;
        rfl;

lemma numElem_merge_branches_lt (p q: MyHeap) (x: Nat): numElem (hmerge p q) < numElem (MyHeap.node p x q) := by
  rw [←merge_elems _ _];
  have h': numElem (MyHeap.node p x q) = 1 + numElem p + numElem q; rfl
  rw [h']
  linarith;

def toList : MyHeap → List Nat
| MyHeap.nil => []
| MyHeap.node p x q =>
    have _h := numElem_merge_branches_lt p q x
    x :: toList (hmerge p q)
termination_by hp => numElem hp

def hsort : List Nat → List Nat
 | xs => toList (toHeap xs)

xs : List ℕ
⊢ (hsort xs == isort xs) = true


theorem prop_HSort2Sorts (xs: List Nat) : ordered (hsort2 xs) == True:= by sorry

import Mathlib

def ordered : List Nat -> Bool
| []       => True
| [_x]     => True
| x::y::xs => x <= y && ordered (y::xs)

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

lemma 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
  have h': numElem (MyHeap.node q x r) = 1 + numElem q + numElem r; rfl
  rw [h'];
  exact ⟨by linarith, by linarith⟩;

lemma merge_elems (p q: MyHeap): numElem p + numElem q = numElem (hmerge p q) := by
  generalize hsp: numElem p = sp
  generalize hsq: numElem q = sq
  generalize hspq: numElem (hmerge p q) = spq
  induction sp using Nat.strong_induction_on generalizing p q sq spq with
  | h sp2 ih =>   induction sq using Nat.strong_induction_on generalizing p q sp2 spq with
    | h sq2 ih2 =>
    rw [←hsp, ← hsq, ← hspq];
    unfold hmerge;
    split;
    case h_1 _ _;
      unfold numElem; rw [Nat.add_comm, Nat.add_zero];
    case h_2 _ _;
      unfold numElem; rw [Nat.add_zero];
    case h_3 _ _ pl x pr ql y qr;
      split;
        unfold numElem;
        suffices h': numElem (hmerge pr (MyHeap.node ql y qr)) = numElem pr + (1 + numElem ql + numElem qr);
          rw[h']; linarith;
        rw [←hsp] at ih;
        exact Eq.symm (ih (numElem pr) (numElem_lt_subHeaps _ _).2 pr (MyHeap.node ql y qr) rfl
          (numElem (MyHeap.node ql y qr)) rfl (numElem (hmerge pr (MyHeap.node ql y qr))) rfl);
        unfold numElem;
        suffices h': numElem (hmerge (MyHeap.node pl x pr) qr) = numElem qr + (1 + numElem pl + numElem pr);
          rw[h']; linarith;
        rw [←hsq] at ih2;
        have h':= ih2 (numElem qr) (numElem_lt_subHeaps _ _).2 sp2 ih (MyHeap.node pl x pr) qr hsp rfl
          (numElem (hmerge (MyHeap.node pl x pr) qr)) rfl;
        rw [←hsp] at h';
        suffices h'': 1 + numElem pl + numElem pr = numElem (MyHeap.node pl x pr);
          rw [h'']; linarith;
        rfl;

lemma numElem_merge_branches_lt (p q: MyHeap) (x: Nat): numElem (hmerge p q) < numElem (MyHeap.node p x q) := by
  rw [←merge_elems _ _];
  have h': numElem (MyHeap.node p x q) = 1 + numElem p + numElem q; rfl
  rw [h']
  linarith;

def toList : MyHeap → List Nat
| MyHeap.nil => []
| MyHeap.node p x q =>
    have _h := numElem_merge_branches_lt p q x
    x :: toList (hmerge p q)
termination_by hp => numElem hp

def hinsert : Nat → MyHeap → MyHeap
| x, h => hmerge (MyHeap.node MyHeap.nil x MyHeap.nil) h

def toHeap2 : List Nat → MyHeap
| [] => MyHeap.nil
| x::xs => hinsert x (toHeap2 xs)

def hsort2 : List Nat → List Nat
| xs => toList (toHeap2 xs)

xs : List ℕ
⊢ (ordered (hsort2 xs) == decide True) = true


theorem prop_HSort2Count (x: Nat) (xs: List Nat) : count x (hsort2 xs) == count x xs:= by sorry

import Mathlib

def count [DecidableEq α] : α → List α → Nat
| _x, [] => 0
| x, y::ys => if x == y then 1 + (count x ys) else count x ys

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

lemma 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
  have h': numElem (MyHeap.node q x r) = 1 + numElem q + numElem r; rfl
  rw [h'];
  exact ⟨by linarith, by linarith⟩;

lemma merge_elems (p q: MyHeap): numElem p + numElem q = numElem (hmerge p q) := by
  generalize hsp: numElem p = sp
  generalize hsq: numElem q = sq
  generalize hspq: numElem (hmerge p q) = spq
  induction sp using Nat.strong_induction_on generalizing p q sq spq with
  | h sp2 ih =>   induction sq using Nat.strong_induction_on generalizing p q sp2 spq with
    | h sq2 ih2 =>
    rw [←hsp, ← hsq, ← hspq];
    unfold hmerge;
    split;
    case h_1 _ _;
      unfold numElem; rw [Nat.add_comm, Nat.add_zero];
    case h_2 _ _;
      unfold numElem; rw [Nat.add_zero];
    case h_3 _ _ pl x pr ql y qr;
      split;
        unfold numElem;
        suffices h': numElem (hmerge pr (MyHeap.node ql y qr)) = numElem pr + (1 + numElem ql + numElem qr);
          rw[h']; linarith;
        rw [←hsp] at ih;
        exact Eq.symm (ih (numElem pr) (numElem_lt_subHeaps _ _).2 pr (MyHeap.node ql y qr) rfl
          (numElem (MyHeap.node ql y qr)) rfl (numElem (hmerge pr (MyHeap.node ql y qr))) rfl);
        unfold numElem;
        suffices h': numElem (hmerge (MyHeap.node pl x pr) qr) = numElem qr + (1 + numElem pl + numElem pr);
          rw[h']; linarith;
        rw [←hsq] at ih2;
        have h':= ih2 (numElem qr) (numElem_lt_subHeaps _ _).2 sp2 ih (MyHeap.node pl x pr) qr hsp rfl
          (numElem (hmerge (MyHeap.node pl x pr) qr)) rfl;
        rw [←hsp] at h';
        suffices h'': 1 + numElem pl + numElem pr = numElem (MyHeap.node pl x pr);
          rw [h'']; linarith;
        rfl;

lemma numElem_merge_branches_lt (p q: MyHeap) (x: Nat): numElem (hmerge p q) < numElem (MyHeap.node p x q) := by
  rw [←merge_elems _ _];
  have h': numElem (MyHeap.node p x q) = 1 + numElem p + numElem q; rfl
  rw [h']
  linarith;

def toList : MyHeap → List Nat
| MyHeap.nil => []
| MyHeap.node p x q =>
    have _h := numElem_merge_branches_lt p q x
    x :: toList (hmerge p q)
termination_by hp => numElem hp

def hinsert : Nat → MyHeap → MyHeap
| x, h => hmerge (MyHeap.node MyHeap.nil x MyHeap.nil) h

def toHeap2 : List Nat → MyHeap
| [] => MyHeap.nil
| x::xs => hinsert x (toHeap2 xs)

def hsort2 : List Nat → List Nat
| xs => toList (toHeap2 xs)

x : ℕ
xs : List ℕ
⊢ (count x (hsort2 xs) == count x xs) = true


theorem prop_HSort2Permutes (xs: List Nat) : isPermutation (hsort2 xs) xs == True:= 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))

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

lemma 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
  have h': numElem (MyHeap.node q x r) = 1 + numElem q + numElem r; rfl
  rw [h'];
  exact ⟨by linarith, by linarith⟩;

lemma merge_elems (p q: MyHeap): numElem p + numElem q = numElem (hmerge p q) := by
  generalize hsp: numElem p = sp
  generalize hsq: numElem q = sq
  generalize hspq: numElem (hmerge p q) = spq
  induction sp using Nat.strong_induction_on generalizing p q sq spq with
  | h sp2 ih =>   induction sq using Nat.strong_induction_on generalizing p q sp2 spq with
    | h sq2 ih2 =>
    rw [←hsp, ← hsq, ← hspq];
    unfold hmerge;
    split;
    case h_1 _ _;
      unfold numElem; rw [Nat.add_comm, Nat.add_zero];
    case h_2 _ _;
      unfold numElem; rw [Nat.add_zero];
    case h_3 _ _ pl x pr ql y qr;
      split;
        unfold numElem;
        suffices h': numElem (hmerge pr (MyHeap.node ql y qr)) = numElem pr + (1 + numElem ql + numElem qr);
          rw[h']; linarith;
        rw [←hsp] at ih;
        exact Eq.symm (ih (numElem pr) (numElem_lt_subHeaps _ _).2 pr (MyHeap.node ql y qr) rfl
          (numElem (MyHeap.node ql y qr)) rfl (numElem (hmerge pr (MyHeap.node ql y qr))) rfl);
        unfold numElem;
        suffices h': numElem (hmerge (MyHeap.node pl x pr) qr) = numElem qr + (1 + numElem pl + numElem pr);
          rw[h']; linarith;
        rw [←hsq] at ih2;
        have h':= ih2 (numElem qr) (numElem_lt_subHeaps _ _).2 sp2 ih (MyHeap.node pl x pr) qr hsp rfl
          (numElem (hmerge (MyHeap.node pl x pr) qr)) rfl;
        rw [←hsp] at h';
        suffices h'': 1 + numElem pl + numElem pr = numElem (MyHeap.node pl x pr);
          rw [h'']; linarith;
        rfl;

lemma numElem_merge_branches_lt (p q: MyHeap) (x: Nat): numElem (hmerge p q) < numElem (MyHeap.node p x q) := by
  rw [←merge_elems _ _];
  have h': numElem (MyHeap.node p x q) = 1 + numElem p + numElem q; rfl
  rw [h']
  linarith;

def toList : MyHeap → List Nat
| MyHeap.nil => []
| MyHeap.node p x q =>
    have _h := numElem_merge_branches_lt p q x
    x :: toList (hmerge p q)
termination_by hp => numElem hp

def hinsert : Nat → MyHeap → MyHeap
| x, h => hmerge (MyHeap.node MyHeap.nil x MyHeap.nil) h

def toHeap2 : List Nat → MyHeap
| [] => MyHeap.nil
| x::xs => hinsert x (toHeap2 xs)

def hsort2 : List Nat → List Nat
| xs => toList (toHeap2 xs)

xs : List ℕ
⊢ (isPermutation (hsort2 xs) xs == decide True) = true


theorem prop_HSort2IsSort (xs: List Nat) : hsort2 xs == isort xs:= by sorry

import Mathlib

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)

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

lemma 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
  have h': numElem (MyHeap.node q x r) = 1 + numElem q + numElem r; rfl
  rw [h'];
  exact ⟨by linarith, by linarith⟩;

lemma merge_elems (p q: MyHeap): numElem p + numElem q = numElem (hmerge p q) := by
  generalize hsp: numElem p = sp
  generalize hsq: numElem q = sq
  generalize hspq: numElem (hmerge p q) = spq
  induction sp using Nat.strong_induction_on generalizing p q sq spq with
  | h sp2 ih =>   induction sq using Nat.strong_induction_on generalizing p q sp2 spq with
    | h sq2 ih2 =>
    rw [←hsp, ← hsq, ← hspq];
    unfold hmerge;
    split;
    case h_1 _ _;
      unfold numElem; rw [Nat.add_comm, Nat.add_zero];
    case h_2 _ _;
      unfold numElem; rw [Nat.add_zero];
    case h_3 _ _ pl x pr ql y qr;
      split;
        unfold numElem;
        suffices h': numElem (hmerge pr (MyHeap.node ql y qr)) = numElem pr + (1 + numElem ql + numElem qr);
          rw[h']; linarith;
        rw [←hsp] at ih;
        exact Eq.symm (ih (numElem pr) (numElem_lt_subHeaps _ _).2 pr (MyHeap.node ql y qr) rfl
          (numElem (MyHeap.node ql y qr)) rfl (numElem (hmerge pr (MyHeap.node ql y qr))) rfl);
        unfold numElem;
        suffices h': numElem (hmerge (MyHeap.node pl x pr) qr) = numElem qr + (1 + numElem pl + numElem pr);
          rw[h']; linarith;
        rw [←hsq] at ih2;
        have h':= ih2 (numElem qr) (numElem_lt_subHeaps _ _).2 sp2 ih (MyHeap.node pl x pr) qr hsp rfl
          (numElem (hmerge (MyHeap.node pl x pr) qr)) rfl;
        rw [←hsp] at h';
        suffices h'': 1 + numElem pl + numElem pr = numElem (MyHeap.node pl x pr);
          rw [h'']; linarith;
        rfl;

lemma numElem_merge_branches_lt (p q: MyHeap) (x: Nat): numElem (hmerge p q) < numElem (MyHeap.node p x q) := by
  rw [←merge_elems _ _];
  have h': numElem (MyHeap.node p x q) = 1 + numElem p + numElem q; rfl
  rw [h']
  linarith;

def toList : MyHeap → List Nat
| MyHeap.nil => []
| MyHeap.node p x q =>
    have _h := numElem_merge_branches_lt p q x
    x :: toList (hmerge p q)
termination_by hp => numElem hp

def hinsert : Nat → MyHeap → MyHeap
| x, h => hmerge (MyHeap.node MyHeap.nil x MyHeap.nil) h

def toHeap2 : List Nat → MyHeap
| [] => MyHeap.nil
| x::xs => hinsert x (toHeap2 xs)

def hsort2 : List Nat → List Nat
| xs => toList (toHeap2 xs)

xs : List ℕ
⊢ (hsort2 xs == isort xs) = true


theorem prop_MSortBU2Sorts (xs: List Nat) : ordered (msortbu2 xs) == true:= by sorry

import Mathlib

def ordered : List Nat -> Bool
| []       => True
| [_x]     => True
| x::y::xs => x <= y && ordered (y::xs)

def risers : List Nat → List (List Nat)
| [] => []
| [x] => [[x]]
| x::y::xs => if x <= y then
      match (risers (y::xs)) with
      | ys::yss => (x::ys)::yss
      | _ => []
    else
      [x]::(risers (y::xs))

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

lemma len_pairwise (xs: List (List Nat)): 2 * (pairwise xs).length = (if (Odd xs.length) then xs.length + 1 else xs.length) := by
  generalize hxl : xs.length = xl;
  split_ifs with h1;
  case pos;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => simp at h1;
        | cons head2 tail2 =>
          cases tail2 with
          | nil => unfold pairwise; simp at hxl; rw [←hxl]; simp;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _]
            ring_nf;
            simp;
            have hodd: Odd tail3.length := by
              rw [List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.add_assoc, Nat.odd_add] at h1;
              apply h1.2; simp;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl hodd
            ring_nf at tmp;
            rw [tmp];
            ring_nf;
  case neg;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => unfold pairwise; simp [← hxl]
        | cons head2 tail2 =>
          cases tail2 with
          | nil => simp at h1;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
            ring_nf;
            simp;
            simp at h1;
            have heven: Even tail3.length := by
              rw [Nat.add_assoc, Nat.even_add] at h1;
              apply h1.2; simp;
            simp at ih;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl heven
            ring_nf at tmp;
            exact tmp;

def mergingbu2 : List (List Nat) → List Nat
| [] => []
| [xs] => xs
| xs::ys::xss =>
  have _h: (pairwise (xs::ys::xss)).length < (xs::ys::xss).length := by
    suffices h': 2* List.length (pairwise (xs :: ys :: xss)) < 2 * List.length (xs :: ys :: xss)
    case h';
      rw [ len_pairwise _];
      split_ifs with hparity;
      case pos;
        simp;
        ring_nf;
        linarith;
      case neg;
        simp;
    linarith [h'];
  mergingbu2 (pairwise (xs::ys::xss))
termination_by xss => xss.length

def msortbu2 : List Nat → List Nat
| xs => mergingbu2 (risers xs)

xs : List ℕ
⊢ (ordered (msortbu2 xs) == true) = true


theorem prop_MSortBU2Count (x: Nat) (xs: List Nat) : count x (msortbu2 xs) == count x xs:= by sorry

import Mathlib

def count [DecidableEq α] : α → List α → Nat
| _x, [] => 0
| x, y::ys => if x == y then 1 + (count x ys) else count x ys

def risers : List Nat → List (List Nat)
| [] => []
| [x] => [[x]]
| x::y::xs => if x <= y then
      match (risers (y::xs)) with
      | ys::yss => (x::ys)::yss
      | _ => []
    else
      [x]::(risers (y::xs))

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

lemma len_pairwise (xs: List (List Nat)): 2 * (pairwise xs).length = (if (Odd xs.length) then xs.length + 1 else xs.length) := by
  generalize hxl : xs.length = xl;
  split_ifs with h1;
  case pos;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => simp at h1;
        | cons head2 tail2 =>
          cases tail2 with
          | nil => unfold pairwise; simp at hxl; rw [←hxl]; simp;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _]
            ring_nf;
            simp;
            have hodd: Odd tail3.length := by
              rw [List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.add_assoc, Nat.odd_add] at h1;
              apply h1.2; simp;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl hodd
            ring_nf at tmp;
            rw [tmp];
            ring_nf;
  case neg;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => unfold pairwise; simp [← hxl]
        | cons head2 tail2 =>
          cases tail2 with
          | nil => simp at h1;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
            ring_nf;
            simp;
            simp at h1;
            have heven: Even tail3.length := by
              rw [Nat.add_assoc, Nat.even_add] at h1;
              apply h1.2; simp;
            simp at ih;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl heven
            ring_nf at tmp;
            exact tmp;

def mergingbu2 : List (List Nat) → List Nat
| [] => []
| [xs] => xs
| xs::ys::xss =>
  have _h: (pairwise (xs::ys::xss)).length < (xs::ys::xss).length := by
    suffices h': 2* List.length (pairwise (xs :: ys :: xss)) < 2 * List.length (xs :: ys :: xss)
    case h';
      rw [ len_pairwise _];
      split_ifs with hparity;
      case pos;
        simp;
        ring_nf;
        linarith;
      case neg;
        simp;
    linarith [h'];
  mergingbu2 (pairwise (xs::ys::xss))
termination_by xss => xss.length

def msortbu2 : List Nat → List Nat
| xs => mergingbu2 (risers xs)

x : ℕ
xs : List ℕ
⊢ (count x (msortbu2 xs) == count x xs) = true


theorem prop_MSortBU2Permutes (xs: List Nat) : isPermutation (msortbu2 xs) xs == true:= 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))

def risers : List Nat → List (List Nat)
| [] => []
| [x] => [[x]]
| x::y::xs => if x <= y then
      match (risers (y::xs)) with
      | ys::yss => (x::ys)::yss
      | _ => []
    else
      [x]::(risers (y::xs))

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

lemma len_pairwise (xs: List (List Nat)): 2 * (pairwise xs).length = (if (Odd xs.length) then xs.length + 1 else xs.length) := by
  generalize hxl : xs.length = xl;
  split_ifs with h1;
  case pos;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => simp at h1;
        | cons head2 tail2 =>
          cases tail2 with
          | nil => unfold pairwise; simp at hxl; rw [←hxl]; simp;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _]
            ring_nf;
            simp;
            have hodd: Odd tail3.length := by
              rw [List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.add_assoc, Nat.odd_add] at h1;
              apply h1.2; simp;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl hodd
            ring_nf at tmp;
            rw [tmp];
            ring_nf;
  case neg;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => unfold pairwise; simp [← hxl]
        | cons head2 tail2 =>
          cases tail2 with
          | nil => simp at h1;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
            ring_nf;
            simp;
            simp at h1;
            have heven: Even tail3.length := by
              rw [Nat.add_assoc, Nat.even_add] at h1;
              apply h1.2; simp;
            simp at ih;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl heven
            ring_nf at tmp;
            exact tmp;

def mergingbu2 : List (List Nat) → List Nat
| [] => []
| [xs] => xs
| xs::ys::xss =>
  have _h: (pairwise (xs::ys::xss)).length < (xs::ys::xss).length := by
    suffices h': 2* List.length (pairwise (xs :: ys :: xss)) < 2 * List.length (xs :: ys :: xss)
    case h';
      rw [ len_pairwise _];
      split_ifs with hparity;
      case pos;
        simp;
        ring_nf;
        linarith;
      case neg;
        simp;
    linarith [h'];
  mergingbu2 (pairwise (xs::ys::xss))
termination_by xss => xss.length

def msortbu2 : List Nat → List Nat
| xs => mergingbu2 (risers xs)

xs : List ℕ
⊢ (isPermutation (msortbu2 xs) xs == true) = true


theorem prop_MSortBU2IsSort (xs: List Nat) : msortbu2 xs == isort xs:= by sorry

import Mathlib

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 risers : List Nat → List (List Nat)
| [] => []
| [x] => [[x]]
| x::y::xs => if x <= y then
      match (risers (y::xs)) with
      | ys::yss => (x::ys)::yss
      | _ => []
    else
      [x]::(risers (y::xs))

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

lemma len_pairwise (xs: List (List Nat)): 2 * (pairwise xs).length = (if (Odd xs.length) then xs.length + 1 else xs.length) := by
  generalize hxl : xs.length = xl;
  split_ifs with h1;
  case pos;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => simp at h1;
        | cons head2 tail2 =>
          cases tail2 with
          | nil => unfold pairwise; simp at hxl; rw [←hxl]; simp;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _]
            ring_nf;
            simp;
            have hodd: Odd tail3.length := by
              rw [List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.add_assoc, Nat.odd_add] at h1;
              apply h1.2; simp;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl hodd
            ring_nf at tmp;
            rw [tmp];
            ring_nf;
  case neg;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => unfold pairwise; simp [← hxl]
        | cons head2 tail2 =>
          cases tail2 with
          | nil => simp at h1;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
            ring_nf;
            simp;
            simp at h1;
            have heven: Even tail3.length := by
              rw [Nat.add_assoc, Nat.even_add] at h1;
              apply h1.2; simp;
            simp at ih;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl heven
            ring_nf at tmp;
            exact tmp;

def mergingbu2 : List (List Nat) → List Nat
| [] => []
| [xs] => xs
| xs::ys::xss =>
  have _h: (pairwise (xs::ys::xss)).length < (xs::ys::xss).length := by
    suffices h': 2* List.length (pairwise (xs :: ys :: xss)) < 2 * List.length (xs :: ys :: xss)
    case h';
      rw [ len_pairwise _];
      split_ifs with hparity;
      case pos;
        simp;
        ring_nf;
        linarith;
      case neg;
        simp;
    linarith [h'];
  mergingbu2 (pairwise (xs::ys::xss))
termination_by xss => xss.length

def msortbu2 : List Nat → List Nat
| xs => mergingbu2 (risers xs)

xs : List ℕ
⊢ (msortbu2 xs == isort xs) = true


theorem prop_MSortBUSorts (xs: List Nat) : ordered (msortbu xs) == true:= by sorry

import Mathlib

def ordered : List Nat -> Bool
| []       => True
| [_x]     => True
| x::y::xs => x <= y && ordered (y::xs)

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

lemma len_pairwise (xs: List (List Nat)): 2 * (pairwise xs).length = (if (Odd xs.length) then xs.length + 1 else xs.length) := by
  generalize hxl : xs.length = xl;
  split_ifs with h1;
  case pos;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => simp at h1;
        | cons head2 tail2 =>
          cases tail2 with
          | nil => unfold pairwise; simp at hxl; rw [←hxl]; simp;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _]
            ring_nf;
            simp;
            have hodd: Odd tail3.length := by
              rw [List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.add_assoc, Nat.odd_add] at h1;
              apply h1.2; simp;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl hodd
            ring_nf at tmp;
            rw [tmp];
            ring_nf;
  case neg;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => unfold pairwise; simp [← hxl]
        | cons head2 tail2 =>
          cases tail2 with
          | nil => simp at h1;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
            ring_nf;
            simp;
            simp at h1;
            have heven: Even tail3.length := by
              rw [Nat.add_assoc, Nat.even_add] at h1;
              apply h1.2; simp;
            simp at ih;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl heven
            ring_nf at tmp;
            exact tmp;

def mergingbu : List (List Nat) → List Nat
| [] => []
| [xs] => xs
| xs::ys::xss =>
  have _h: (pairwise (xs::ys::xss)).length < (xs::ys::xss).length := by
    suffices h': 2* List.length (pairwise (xs :: ys :: xss)) < 2 * List.length (xs :: ys :: xss)
    case h';
      rw [ len_pairwise _];
      split_ifs with hparity;
      case pos;
        simp;
        ring_nf;
        linarith;
      case neg;
        simp;
    linarith [h'];
  mergingbu (pairwise (xs::ys::xss))
termination_by xss => xss.length

def msortbu : List Nat → List Nat
| xs => mergingbu (xs.map (fun x => [x]))

xs : List ℕ
⊢ (ordered (msortbu xs) == true) = true


theorem prop_MSortBUCount (x: Nat) (xs: List Nat) : count x (msortbu xs) == count x xs:= by sorry

import Mathlib

def count [DecidableEq α] : α → List α → Nat
| _x, [] => 0
| x, y::ys => if x == y then 1 + (count x ys) else count x ys

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

lemma len_pairwise (xs: List (List Nat)): 2 * (pairwise xs).length = (if (Odd xs.length) then xs.length + 1 else xs.length) := by
  generalize hxl : xs.length = xl;
  split_ifs with h1;
  case pos;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => simp at h1;
        | cons head2 tail2 =>
          cases tail2 with
          | nil => unfold pairwise; simp at hxl; rw [←hxl]; simp;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _]
            ring_nf;
            simp;
            have hodd: Odd tail3.length := by
              rw [List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.add_assoc, Nat.odd_add] at h1;
              apply h1.2; simp;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl hodd
            ring_nf at tmp;
            rw [tmp];
            ring_nf;
  case neg;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => unfold pairwise; simp [← hxl]
        | cons head2 tail2 =>
          cases tail2 with
          | nil => simp at h1;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
            ring_nf;
            simp;
            simp at h1;
            have heven: Even tail3.length := by
              rw [Nat.add_assoc, Nat.even_add] at h1;
              apply h1.2; simp;
            simp at ih;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl heven
            ring_nf at tmp;
            exact tmp;

def mergingbu : List (List Nat) → List Nat
| [] => []
| [xs] => xs
| xs::ys::xss =>
  have _h: (pairwise (xs::ys::xss)).length < (xs::ys::xss).length := by
    suffices h': 2* List.length (pairwise (xs :: ys :: xss)) < 2 * List.length (xs :: ys :: xss)
    case h';
      rw [ len_pairwise _];
      split_ifs with hparity;
      case pos;
        simp;
        ring_nf;
        linarith;
      case neg;
        simp;
    linarith [h'];
  mergingbu (pairwise (xs::ys::xss))
termination_by xss => xss.length

def msortbu : List Nat → List Nat
| xs => mergingbu (xs.map (fun x => [x]))

x : ℕ
xs : List ℕ
⊢ (count x (msortbu xs) == count x xs) = true


theorem prop_MSortBUPermutes (xs: List Nat) : isPermutation (msortbu xs) xs == true:= 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))

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

lemma len_pairwise (xs: List (List Nat)): 2 * (pairwise xs).length = (if (Odd xs.length) then xs.length + 1 else xs.length) := by
  generalize hxl : xs.length = xl;
  split_ifs with h1;
  case pos;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => simp at h1;
        | cons head2 tail2 =>
          cases tail2 with
          | nil => unfold pairwise; simp at hxl; rw [←hxl]; simp;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _]
            ring_nf;
            simp;
            have hodd: Odd tail3.length := by
              rw [List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.add_assoc, Nat.odd_add] at h1;
              apply h1.2; simp;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl hodd
            ring_nf at tmp;
            rw [tmp];
            ring_nf;
  case neg;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => unfold pairwise; simp [← hxl]
        | cons head2 tail2 =>
          cases tail2 with
          | nil => simp at h1;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
            ring_nf;
            simp;
            simp at h1;
            have heven: Even tail3.length := by
              rw [Nat.add_assoc, Nat.even_add] at h1;
              apply h1.2; simp;
            simp at ih;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl heven
            ring_nf at tmp;
            exact tmp;

def mergingbu : List (List Nat) → List Nat
| [] => []
| [xs] => xs
| xs::ys::xss =>
  have _h: (pairwise (xs::ys::xss)).length < (xs::ys::xss).length := by
    suffices h': 2* List.length (pairwise (xs :: ys :: xss)) < 2 * List.length (xs :: ys :: xss)
    case h';
      rw [ len_pairwise _];
      split_ifs with hparity;
      case pos;
        simp;
        ring_nf;
        linarith;
      case neg;
        simp;
    linarith [h'];
  mergingbu (pairwise (xs::ys::xss))
termination_by xss => xss.length

def msortbu : List Nat → List Nat
| xs => mergingbu (xs.map (fun x => [x]))

xs : List ℕ
⊢ (isPermutation (msortbu xs) xs == true) = true


theorem prop_MSortBUIsSort (xs: List Nat) : msortbu xs == isort xs:= by sorry

import Mathlib

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 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

lemma len_pairwise (xs: List (List Nat)): 2 * (pairwise xs).length = (if (Odd xs.length) then xs.length + 1 else xs.length) := by
  generalize hxl : xs.length = xl;
  split_ifs with h1;
  case pos;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => simp at h1;
        | cons head2 tail2 =>
          cases tail2 with
          | nil => unfold pairwise; simp at hxl; rw [←hxl]; simp;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _]
            ring_nf;
            simp;
            have hodd: Odd tail3.length := by
              rw [List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.add_assoc, Nat.odd_add] at h1;
              apply h1.2; simp;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl hodd
            ring_nf at tmp;
            rw [tmp];
            ring_nf;
  case neg;
    induction xl using Nat.strong_induction_on generalizing xs with
    | h xls ih =>
        rw [← hxl] at h1;
        cases xs with
        | nil => unfold pairwise; simp [← hxl]
        | cons head2 tail2 =>
          cases tail2 with
          | nil => simp at h1;
          | cons head3 tail3 =>
            unfold pairwise;
            rw [←hxl, List.length_cons, List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
            ring_nf;
            simp;
            simp at h1;
            have heven: Even tail3.length := by
              rw [Nat.add_assoc, Nat.even_add] at h1;
              apply h1.2; simp;
            simp at ih;
            have tmp := ih tail3.length (by rw [←hxl]; simp; linarith;) tail3 rfl heven
            ring_nf at tmp;
            exact tmp;

def mergingbu : List (List Nat) → List Nat
| [] => []
| [xs] => xs
| xs::ys::xss =>
  have _h: (pairwise (xs::ys::xss)).length < (xs::ys::xss).length := by
    suffices h': 2* List.length (pairwise (xs :: ys :: xss)) < 2 * List.length (xs :: ys :: xss)
    case h';
      rw [ len_pairwise _];
      split_ifs with hparity;
      case pos;
        simp;
        ring_nf;
        linarith;
      case neg;
        simp;
    linarith [h'];
  mergingbu (pairwise (xs::ys::xss))
termination_by xss => xss.length

def msortbu : List Nat → List Nat
| xs => mergingbu (xs.map (fun x => [x]))

xs : List ℕ
⊢ (msortbu xs == isort xs) = true


theorem prop_MSortTDSorts (xs: List Nat) : ordered (msorttd xs) == true:= by sorry

import Mathlib

def ordered : List Nat -> Bool
| []       => True
| [_x]     => True
| x::y::xs => x <= y && ordered (y::xs)

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 msorttd : List Nat → List Nat
| [] => []
| [x] => [x]
| x::y::xs =>
  let k:= (x::y::xs).length/2
  have _h: Nat.succ (Nat.succ (List.length xs)) / 2 < Nat.succ (Nat.succ (List.length xs)) := by
    rw [Nat.succ_eq_add_one _];
    ring_nf;
    simp [Nat.succ_eq_add_one _];
    ring_nf;
    calc 1 + xs.length/2 ≤ 1 + xs.length := by simp; exact Nat.div_le_self (List.length xs) 2;
         _               < 2 + xs.length := by simp;
  lmerge (msorttd ((x::y::xs).take k)) (msorttd ((x::y::xs).drop k))
termination_by xs => xs.length

xs : List ℕ
⊢ (ordered (msorttd xs) == true) = true


theorem prop_MSortTDCount (x: Nat) (xs: List Nat) : count x (msorttd xs) == count x xs:= by sorry

import Mathlib

def count [DecidableEq α] : α → List α → Nat
| _x, [] => 0
| x, y::ys => if x == y then 1 + (count x ys) else count x ys

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 msorttd : List Nat → List Nat
| [] => []
| [x] => [x]
| x::y::xs =>
  let k:= (x::y::xs).length/2
  have _h: Nat.succ (Nat.succ (List.length xs)) / 2 < Nat.succ (Nat.succ (List.length xs)) := by
    rw [Nat.succ_eq_add_one _];
    ring_nf;
    simp [Nat.succ_eq_add_one _];
    ring_nf;
    calc 1 + xs.length/2 ≤ 1 + xs.length := by simp; exact Nat.div_le_self (List.length xs) 2;
         _               < 2 + xs.length := by simp;
  lmerge (msorttd ((x::y::xs).take k)) (msorttd ((x::y::xs).drop k))
termination_by xs => xs.length

x : ℕ
xs : List ℕ
⊢ (count x (msorttd xs) == count x xs) = true


theorem prop_MSortTDPermutes (xs: List Nat) : isPermutation (msorttd xs) xs == true:= 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))

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 msorttd : List Nat → List Nat
| [] => []
| [x] => [x]
| x::y::xs =>
  let k:= (x::y::xs).length/2
  have _h: Nat.succ (Nat.succ (List.length xs)) / 2 < Nat.succ (Nat.succ (List.length xs)) := by
    rw [Nat.succ_eq_add_one _];
    ring_nf;
    simp [Nat.succ_eq_add_one _];
    ring_nf;
    calc 1 + xs.length/2 ≤ 1 + xs.length := by simp; exact Nat.div_le_self (List.length xs) 2;
         _               < 2 + xs.length := by simp;
  lmerge (msorttd ((x::y::xs).take k)) (msorttd ((x::y::xs).drop k))
termination_by xs => xs.length

xs : List ℕ
⊢ (isPermutation (msorttd xs) xs == true) = true


theorem prop_MSortTDIsSort (xs: List Nat) : msorttd xs == isort xs:= by sorry

import Mathlib

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 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 msorttd : List Nat → List Nat
| [] => []
| [x] => [x]
| x::y::xs =>
  let k:= (x::y::xs).length/2
  have _h: Nat.succ (Nat.succ (List.length xs)) / 2 < Nat.succ (Nat.succ (List.length xs)) := by
    rw [Nat.succ_eq_add_one _];
    ring_nf;
    simp [Nat.succ_eq_add_one _];
    ring_nf;
    calc 1 + xs.length/2 ≤ 1 + xs.length := by simp; exact Nat.div_le_self (List.length xs) 2;
         _               < 2 + xs.length := by simp;
  lmerge (msorttd ((x::y::xs).take k)) (msorttd ((x::y::xs).drop k))
termination_by xs => xs.length

xs : List ℕ
⊢ (msorttd xs == isort xs) = true


theorem prop_NMSortTDSorts (xs: List Nat) : ordered (nmsorttd xs) == true:= by sorry

import Mathlib

def ordered : List Nat -> Bool
| []       => True
| [_x]     => True
| x::y::xs => x <= y && ordered (y::xs)

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 half : Nat → Nat
| 0 => 0
| 1 => 0
| x + 2 => 1 + (half x)

lemma half_lt: half x ≤ x := by
  induction x using Nat.strong_induction_on with
  | h n ih =>
    cases n with
    | zero => unfold half; simp;
    | succ nm1 =>
      cases nm1 with
      | zero => unfold half; simp;
      | succ nm2 =>
        unfold half;
        have tmp := ih nm2 (by exact Nat.le.step Nat.le.refl);
        ring_nf;
        calc 1 + half nm2 ≤ 1 + nm2  := Nat.add_le_add (@Nat.le.refl 1) tmp
             _           ≤  2 + nm2  := by simp

def nmsorttd : List Nat → List Nat
| [] => []
| [x] => [x]
| x::y::xs =>
  let k:= half ((x::y::xs).length)
  have _h: half (Nat.succ (Nat.succ (List.length xs))) < Nat.succ (Nat.succ (List.length xs)) := by
    rw [Nat.succ_eq_add_one _];
    ring_nf;
    rw [Nat.add_comm];
    unfold half;
    ring_nf;
    calc 1 + half (xs.length) ≤ 1 + xs.length := by simp; exact half_lt;
         _               < 2 + xs.length := by simp;
  have _h': Nat.succ (Nat.succ (List.length xs)) - half (Nat.succ (Nat.succ (List.length xs))) <
  Nat.succ (Nat.succ (List.length xs)) := by
    suffices h': 0 < half (Nat.succ (Nat.succ (List.length xs)))
    case h';
      unfold half;simp;
    refine Nat.sub_lt ?h h'
    simp;
  lmerge (nmsorttd ((x::y::xs).take k)) (nmsorttd ((x::y::xs).drop k))
termination_by xs => xs.length

xs : List ℕ
⊢ (ordered (nmsorttd xs) == true) = true


theorem prop_NMSortTDCount (x: Nat) (xs: List Nat) : count x (nmsorttd xs) == count x xs:= by sorry

import Mathlib

def count [DecidableEq α] : α → List α → Nat
| _x, [] => 0
| x, y::ys => if x == y then 1 + (count x ys) else count x ys

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 half : Nat → Nat
| 0 => 0
| 1 => 0
| x + 2 => 1 + (half x)

lemma half_lt: half x ≤ x := by
  induction x using Nat.strong_induction_on with
  | h n ih =>
    cases n with
    | zero => unfold half; simp;
    | succ nm1 =>
      cases nm1 with
      | zero => unfold half; simp;
      | succ nm2 =>
        unfold half;
        have tmp := ih nm2 (by exact Nat.le.step Nat.le.refl);
        ring_nf;
        calc 1 + half nm2 ≤ 1 + nm2  := Nat.add_le_add (@Nat.le.refl 1) tmp
             _           ≤  2 + nm2  := by simp

def nmsorttd : List Nat → List Nat
| [] => []
| [x] => [x]
| x::y::xs =>
  let k:= half ((x::y::xs).length)
  have _h: half (Nat.succ (Nat.succ (List.length xs))) < Nat.succ (Nat.succ (List.length xs)) := by
    rw [Nat.succ_eq_add_one _];
    ring_nf;
    rw [Nat.add_comm];
    unfold half;
    ring_nf;
    calc 1 + half (xs.length) ≤ 1 + xs.length := by simp; exact half_lt;
         _               < 2 + xs.length := by simp;
  have _h': Nat.succ (Nat.succ (List.length xs)) - half (Nat.succ (Nat.succ (List.length xs))) <
  Nat.succ (Nat.succ (List.length xs)) := by
    suffices h': 0 < half (Nat.succ (Nat.succ (List.length xs)))
    case h';
      unfold half;simp;
    refine Nat.sub_lt ?h h'
    simp;
  lmerge (nmsorttd ((x::y::xs).take k)) (nmsorttd ((x::y::xs).drop k))
termination_by xs => xs.length

x : ℕ
xs : List ℕ
⊢ (count x (nmsorttd xs) == count x xs) = true


theorem prop_NMSortTDPermutes (xs: List Nat) : isPermutation (nmsorttd xs) xs == true:= 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))

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 half : Nat → Nat
| 0 => 0
| 1 => 0
| x + 2 => 1 + (half x)

lemma half_lt: half x ≤ x := by
  induction x using Nat.strong_induction_on with
  | h n ih =>
    cases n with
    | zero => unfold half; simp;
    | succ nm1 =>
      cases nm1 with
      | zero => unfold half; simp;
      | succ nm2 =>
        unfold half;
        have tmp := ih nm2 (by exact Nat.le.step Nat.le.refl);
        ring_nf;
        calc 1 + half nm2 ≤ 1 + nm2  := Nat.add_le_add (@Nat.le.refl 1) tmp
             _           ≤  2 + nm2  := by simp

def nmsorttd : List Nat → List Nat
| [] => []
| [x] => [x]
| x::y::xs =>
  let k:= half ((x::y::xs).length)
  have _h: half (Nat.succ (Nat.succ (List.length xs))) < Nat.succ (Nat.succ (List.length xs)) := by
    rw [Nat.succ_eq_add_one _];
    ring_nf;
    rw [Nat.add_comm];
    unfold half;
    ring_nf;
    calc 1 + half (xs.length) ≤ 1 + xs.length := by simp; exact half_lt;
         _               < 2 + xs.length := by simp;
  have _h': Nat.succ (Nat.succ (List.length xs)) - half (Nat.succ (Nat.succ (List.length xs))) <
  Nat.succ (Nat.succ (List.length xs)) := by
    suffices h': 0 < half (Nat.succ (Nat.succ (List.length xs)))
    case h';
      unfold half;simp;
    refine Nat.sub_lt ?h h'
    simp;
  lmerge (nmsorttd ((x::y::xs).take k)) (nmsorttd ((x::y::xs).drop k))
termination_by xs => xs.length

xs : List ℕ
⊢ (isPermutation (nmsorttd xs) xs == true) = true


theorem prop_NMSortTDIsSort (xs: List Nat) : nmsorttd xs == isort xs:= by sorry

import Mathlib

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 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 half : Nat → Nat
| 0 => 0
| 1 => 0
| x + 2 => 1 + (half x)

lemma half_lt: half x ≤ x := by
  induction x using Nat.strong_induction_on with
  | h n ih =>
    cases n with
    | zero => unfold half; simp;
    | succ nm1 =>
      cases nm1 with
      | zero => unfold half; simp;
      | succ nm2 =>
        unfold half;
        have tmp := ih nm2 (by exact Nat.le.step Nat.le.refl);
        ring_nf;
        calc 1 + half nm2 ≤ 1 + nm2  := Nat.add_le_add (@Nat.le.refl 1) tmp
             _           ≤  2 + nm2  := by simp

def nmsorttd : List Nat → List Nat
| [] => []
| [x] => [x]
| x::y::xs =>
  let k:= half ((x::y::xs).length)
  have _h: half (Nat.succ (Nat.succ (List.length xs))) < Nat.succ (Nat.succ (List.length xs)) := by
    rw [Nat.succ_eq_add_one _];
    ring_nf;
    rw [Nat.add_comm];
    unfold half;
    ring_nf;
    calc 1 + half (xs.length) ≤ 1 + xs.length := by simp; exact half_lt;
         _               < 2 + xs.length := by simp;
  have _h': Nat.succ (Nat.succ (List.length xs)) - half (Nat.succ (Nat.succ (List.length xs))) <
  Nat.succ (Nat.succ (List.length xs)) := by
    suffices h': 0 < half (Nat.succ (Nat.succ (List.length xs)))
    case h';
      unfold half;simp;
    refine Nat.sub_lt ?h h'
    simp;
  lmerge (nmsorttd ((x::y::xs).take k)) (nmsorttd ((x::y::xs).drop k))
termination_by xs => xs.length

xs : List ℕ
⊢ (nmsorttd xs == isort xs) = true


theorem prop_BSortSorts (xs: List Nat) : ordered (bsort xs) == true:= by sorry

import Mathlib

def ordered : List Nat -> Bool
| []       => True
| [_x]     => True
| x::y::xs => x <= y && ordered (y::xs)

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

lemma len_evens_le : (evens xs).length ≤ xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl using Nat.strong_induction_on generalizing xs with
  | h n ih =>
    cases xs with
    | nil => unfold evens; simp;
    | cons head1 tail1 =>
      unfold evens;
      cases tail1 with
      | nil => unfold odds; rw[← hxsl];
      | cons head2 tail2 =>
        unfold odds;
        rw [List.length_cons, List.length_cons] at hxsl;
        rw [← hxsl, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
        simp;
        have h': List.length tail2 < n := by rw [← hxsl, Nat.succ_eq_add_one _]; linarith;
        exact Nat.le.step (ih (List.length tail2) h' rfl);

lemma len_odds_le : (odds xs).length ≤ xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl using Nat.strong_induction_on generalizing xs with
  | h n ih =>
    cases xs with
    | nil => unfold odds; simp;
    | cons head1 tail1 =>
      unfold odds;
      cases tail1 with
      | nil => unfold evens; simp;
      | cons head2 tail2 =>
        unfold evens;
        rw [List.length_cons, List.length_cons] at hxsl;
        rw [← hxsl, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
        simp;
        have h': List.length tail2 < n := by rw [← hxsl, Nat.succ_eq_add_one _]; linarith;
        exact Nat.le.step (ih (List.length tail2) h' rfl);

def sort2 (a b: Nat): List Nat := if a ≤ b then [a,b] else [b, a]

def pairs : List Nat → List Nat → List Nat
| [], ys => ys
| xs, [] => xs
| x::xs, y::ys => (sort2 x y) ++ (pairs xs ys)

def stitch : List Nat → List Nat → List Nat
| [], ys => ys
| x::xs, ys => x::(pairs xs ys)

lemma 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
    unfold evens;
    simp at hlen;
    rw [List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
    ring_nf;
    cases as with
    | nil => cases bs with
      | nil => simp at hlen;
      | cons bhead btail => unfold odds; simp; linarith [@len_evens_le btail];
    | cons ahead atail =>
      rw [add_comm, add_comm (2 + List.length (ahead :: atail)) (List.length bs)]
      refine add_lt_add_of_le_of_lt (@len_odds_le bs) ?h; unfold odds; simp; linarith [@len_evens_le atail];

lemma 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
  unfold odds;
  exact add_lt_add_of_lt_of_lt (Nat.lt_succ_of_le (@len_evens_le xs)) (Nat.lt_succ_of_le (@len_evens_le ys))

def bmerge : List Nat → List Nat → List Nat
| [], bs => bs -- I changed this from TIP. I don't believe this case is ever hit, though.
| as, [] => as
| x::xs, y::ys =>
  if hlen: xs.length == 0 && ys.length == 0 then sort2 x y else
  have _h := bmerge_term x y xs ys hlen;
  have _h2 := bmerge_term2 x y xs ys;
  stitch (bmerge (evens (x::xs)) (evens (y::ys))) (bmerge (odds (x::xs)) (odds (y::ys)))
termination_by xs ys => xs.length + ys.length

lemma bsort_term1 (x y: Nat) (xs: List Nat): List.length (evens (x :: y :: xs)) < Nat.succ (Nat.succ (List.length xs)) := by
  unfold evens; unfold odds;
  rw [List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
  simp;
  exact Nat.lt_succ_of_le (@len_evens_le xs);

lemma bsort_term2 (x y: Nat) (xs: List Nat): List.length (odds (x :: y :: xs)) < Nat.succ (Nat.succ (List.length xs)) := by
  unfold odds; unfold evens;
  rw [List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
  simp;
  exact Nat.lt_succ_of_le (@len_odds_le xs);

def bsort : List Nat → List Nat
| [] => []
| [x] => [x]
| x::y::xs =>
  have _h := bsort_term1 x y xs
  have _h2 := bsort_term2 x y xs
  bmerge (bsort (evens (x::y::xs))) (bsort (odds (x::y::xs)))
termination_by xs => xs.length

xs : List ℕ
⊢ (ordered (bsort xs) == true) = true


theorem prop_BSortCount (x: Nat) (xs: List Nat) : count x (bsort xs) == count x xs:= by sorry

import Mathlib

def count [DecidableEq α] : α → List α → Nat
| _x, [] => 0
| x, y::ys => if x == y then 1 + (count x ys) else count x ys

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

lemma len_evens_le : (evens xs).length ≤ xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl using Nat.strong_induction_on generalizing xs with
  | h n ih =>
    cases xs with
    | nil => unfold evens; simp;
    | cons head1 tail1 =>
      unfold evens;
      cases tail1 with
      | nil => unfold odds; rw[← hxsl];
      | cons head2 tail2 =>
        unfold odds;
        rw [List.length_cons, List.length_cons] at hxsl;
        rw [← hxsl, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
        simp;
        have h': List.length tail2 < n := by rw [← hxsl, Nat.succ_eq_add_one _]; linarith;
        exact Nat.le.step (ih (List.length tail2) h' rfl);

lemma len_odds_le : (odds xs).length ≤ xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl using Nat.strong_induction_on generalizing xs with
  | h n ih =>
    cases xs with
    | nil => unfold odds; simp;
    | cons head1 tail1 =>
      unfold odds;
      cases tail1 with
      | nil => unfold evens; simp;
      | cons head2 tail2 =>
        unfold evens;
        rw [List.length_cons, List.length_cons] at hxsl;
        rw [← hxsl, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
        simp;
        have h': List.length tail2 < n := by rw [← hxsl, Nat.succ_eq_add_one _]; linarith;
        exact Nat.le.step (ih (List.length tail2) h' rfl);

def sort2 (a b: Nat): List Nat := if a ≤ b then [a,b] else [b, a]

def pairs : List Nat → List Nat → List Nat
| [], ys => ys
| xs, [] => xs
| x::xs, y::ys => (sort2 x y) ++ (pairs xs ys)

def stitch : List Nat → List Nat → List Nat
| [], ys => ys
| x::xs, ys => x::(pairs xs ys)

lemma 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
    unfold evens;
    simp at hlen;
    rw [List.length_cons, List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
    ring_nf;
    cases as with
    | nil => cases bs with
      | nil => simp at hlen;
      | cons bhead btail => unfold odds; simp; linarith [@len_evens_le btail];
    | cons ahead atail =>
      rw [add_comm, add_comm (2 + List.length (ahead :: atail)) (List.length bs)]
      refine add_lt_add_of_le_of_lt (@len_odds_le bs) ?h; unfold odds; simp; linarith [@len_evens_le atail];

lemma 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
  unfold odds;
  exact add_lt_add_of_lt_of_lt (Nat.lt_succ_of_le (@len_evens_le xs)) (Nat.lt_succ_of_le (@len_evens_le ys))

def bmerge : List Nat → List Nat → List Nat
| [], bs => bs -- I changed this from TIP. I don't believe this case is ever hit, though.
| as, [] => as
| x::xs, y::ys =>
  if hlen: xs.length == 0 && ys.length == 0 then sort2 x y else
  have _h := bmerge_term x y xs ys hlen;
  have _h2 := bmerge_term2 x y xs ys;
  stitch (bmerge (evens (x::xs)) (evens (y::ys))) (bmerge (odds (x::xs)) (odds (y::ys)))
termination_by xs ys => xs.length + ys.length

lemma bsort_term1 (x y: Nat) (xs: List Nat): List.length (evens (x :: y :: xs)) < Nat.succ (Nat.succ (List.length xs)) := by
  unfold evens; unfold odds;
  rw [List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
  simp;
  exact Nat.lt_succ_of_le (@len_evens_le xs);

lemma bsort_term2 (x y: Nat) (xs: List Nat): List.length (odds (x :: y :: xs)) < Nat.succ (Nat.succ (List.length xs)) := by
  unfold odds; unfold evens;
  rw [List.length_cons, Nat.succ_eq_add_one _, Nat.succ_eq_add_one _];
  simp;
  exact Nat.lt_succ_of_le (@len_odds_le xs);

def bsort : List Nat → List Nat
| [] => []
| [x] => [x]
| x::y::xs =>
  have _h := bsort_term1 x y xs
  have _h2 := bsort_term2 x y xs
  bmerge (bsort (evens (x::y::xs))) (bsort (odds (x::y::xs)))
termination_by xs => xs.length

x : ℕ
xs : List ℕ
⊢ (count x (bsort xs) == count x xs) = true