theorem prop_BSortPermutes (xs: List Nat) : isPermutation (bsort 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))

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 ℕ
⊢ (isPermutation (bsort xs) xs == true) = true


theorem prop_BSortIsSort (xs: List Nat) : bsort 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)

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 ℕ
⊢ (bsort xs == isort xs) = true


theorem prop_QSortSorts (xs: List Nat) : ordered (qsort xs) == true:= by sorry

import Mathlib

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

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

lemma filter_len_le {f: Nat → Bool} {xs: List Nat}: (filter xs f).length <= xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl generalizing xs with
  | zero => rw [List.length_eq_zero] at hxsl; rw [hxsl]; unfold filter; simp;
  | succ n ih =>
    cases xs with
    | nil => unfold filter; simp;
    | cons head tail =>
      rw [List.length_cons] at hxsl; simp at hxsl;
      unfold filter; split_ifs with h1;
      case pos;
        rw [List.length_cons];
        exact Nat.pred_le_iff.mp (ih hxsl)
      case neg;
        exact Nat.le.step (ih hxsl)

lemma qsort_term (x:Nat) (xs: List Nat) : List.length (filter xs fun y => decide (y ≤ x)) < Nat.succ (List.length xs) := by
  exact Nat.lt_succ_of_le (filter_len_le);

lemma qsort_term2 (x:Nat) (xs: List Nat) : List.length (filter xs fun y => decide (y > x)) < Nat.succ (List.length xs) := by
  exact Nat.lt_succ_of_le (filter_len_le);

def qsort : List Nat → List Nat
| [] => []
| x::xs =>
  have _h:= qsort_term x xs
  have _h2:= qsort_term2 x xs
  (qsort (filter xs (fun y => y <= x))) ++ [x] ++ (qsort (filter xs (fun y => y > x)))
termination_by xs => xs.length

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


theorem prop_QSortCount (x: Nat) (xs: List Nat) : count x (qsort 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 filter : List Nat → (Nat → Bool) → List Nat
| [], _f => []
| x::xs, f => if f x then x::(filter xs f) else (filter xs f)

lemma filter_len_le {f: Nat → Bool} {xs: List Nat}: (filter xs f).length <= xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl generalizing xs with
  | zero => rw [List.length_eq_zero] at hxsl; rw [hxsl]; unfold filter; simp;
  | succ n ih =>
    cases xs with
    | nil => unfold filter; simp;
    | cons head tail =>
      rw [List.length_cons] at hxsl; simp at hxsl;
      unfold filter; split_ifs with h1;
      case pos;
        rw [List.length_cons];
        exact Nat.pred_le_iff.mp (ih hxsl)
      case neg;
        exact Nat.le.step (ih hxsl)

lemma qsort_term (x:Nat) (xs: List Nat) : List.length (filter xs fun y => decide (y ≤ x)) < Nat.succ (List.length xs) := by
  exact Nat.lt_succ_of_le (filter_len_le);

lemma qsort_term2 (x:Nat) (xs: List Nat) : List.length (filter xs fun y => decide (y > x)) < Nat.succ (List.length xs) := by
  exact Nat.lt_succ_of_le (filter_len_le);

def qsort : List Nat → List Nat
| [] => []
| x::xs =>
  have _h:= qsort_term x xs
  have _h2:= qsort_term2 x xs
  (qsort (filter xs (fun y => y <= x))) ++ [x] ++ (qsort (filter xs (fun y => y > x)))
termination_by xs => xs.length

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


theorem prop_QSortPermutes (xs: List Nat) : isPermutation (qsort 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 filter : List Nat → (Nat → Bool) → List Nat
| [], _f => []
| x::xs, f => if f x then x::(filter xs f) else (filter xs f)

lemma filter_len_le {f: Nat → Bool} {xs: List Nat}: (filter xs f).length <= xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl generalizing xs with
  | zero => rw [List.length_eq_zero] at hxsl; rw [hxsl]; unfold filter; simp;
  | succ n ih =>
    cases xs with
    | nil => unfold filter; simp;
    | cons head tail =>
      rw [List.length_cons] at hxsl; simp at hxsl;
      unfold filter; split_ifs with h1;
      case pos;
        rw [List.length_cons];
        exact Nat.pred_le_iff.mp (ih hxsl)
      case neg;
        exact Nat.le.step (ih hxsl)

lemma qsort_term (x:Nat) (xs: List Nat) : List.length (filter xs fun y => decide (y ≤ x)) < Nat.succ (List.length xs) := by
  exact Nat.lt_succ_of_le (filter_len_le);

lemma qsort_term2 (x:Nat) (xs: List Nat) : List.length (filter xs fun y => decide (y > x)) < Nat.succ (List.length xs) := by
  exact Nat.lt_succ_of_le (filter_len_le);

def qsort : List Nat → List Nat
| [] => []
| x::xs =>
  have _h:= qsort_term x xs
  have _h2:= qsort_term2 x xs
  (qsort (filter xs (fun y => y <= x))) ++ [x] ++ (qsort (filter xs (fun y => y > x)))
termination_by xs => xs.length

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


theorem prop_QSortIsSort (xs: List Nat) : qsort 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 filter : List Nat → (Nat → Bool) → List Nat
| [], _f => []
| x::xs, f => if f x then x::(filter xs f) else (filter xs f)

lemma filter_len_le {f: Nat → Bool} {xs: List Nat}: (filter xs f).length <= xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl generalizing xs with
  | zero => rw [List.length_eq_zero] at hxsl; rw [hxsl]; unfold filter; simp;
  | succ n ih =>
    cases xs with
    | nil => unfold filter; simp;
    | cons head tail =>
      rw [List.length_cons] at hxsl; simp at hxsl;
      unfold filter; split_ifs with h1;
      case pos;
        rw [List.length_cons];
        exact Nat.pred_le_iff.mp (ih hxsl)
      case neg;
        exact Nat.le.step (ih hxsl)

lemma qsort_term (x:Nat) (xs: List Nat) : List.length (filter xs fun y => decide (y ≤ x)) < Nat.succ (List.length xs) := by
  exact Nat.lt_succ_of_le (filter_len_le);

lemma qsort_term2 (x:Nat) (xs: List Nat) : List.length (filter xs fun y => decide (y > x)) < Nat.succ (List.length xs) := by
  exact Nat.lt_succ_of_le (filter_len_le);

def qsort : List Nat → List Nat
| [] => []
| x::xs =>
  have _h:= qsort_term x xs
  have _h2:= qsort_term2 x xs
  (qsort (filter xs (fun y => y <= x))) ++ [x] ++ (qsort (filter xs (fun y => y > x)))
termination_by xs => xs.length

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


theorem prop_SSortSorts (xs: List Nat) : ordered (ssort 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 ordered : List Nat -> Bool
| []       => True
| [_x]     => True
| x::y::xs => x <= y && ordered (y::xs)

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

lemma min_in_list : minimum x xs ∈ (x::xs) := by
  induction xs generalizing x with
  | nil => unfold minimum; simp;
  | cons head tail ih =>
    unfold minimum; split_ifs with h1;
    case pos;
      rw [List.mem_cons]; right; exact ih;
    case neg;
      rw [List.mem_cons];
      cases (List.mem_cons.1 (@ih x)) with
      | inl h2 => left; exact h2;
      | inr h2 => right; rw [List.mem_cons]; right; exact h2;

lemma delete_len_eq {x: Nat} {xs: List Nat} (h: x ∈ xs): (deleteFirst x xs).length + 1 = xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl generalizing xs with
  | zero => rw [List.length_eq_zero] at hxsl; simp [hxsl] at h;
  | succ n ih =>
    cases xs with
    | nil => simp at hxsl;
    | cons head tail =>
      unfold deleteFirst;
      split_ifs with h1;
      case pos;
        simp at hxsl;
        simp [hxsl];
      case neg;
        rw [List.mem_cons] at h;
        simp at h1;
        simp at hxsl;
        simp;
        exact ih ((or_iff_right h1).1 h) hxsl;

def ssort : List Nat → List Nat
| [] => []
| x::xs =>
  let m := minimum x xs
  have _h: List.length (deleteFirst (minimum x xs) (x :: xs)) < Nat.succ (List.length xs) := by
    have tmp := delete_len_eq (@min_in_list x xs)
    simp at tmp;
    simp [tmp];
  m :: ssort (deleteFirst m (x::xs))
termination_by xs => xs.length

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


theorem prop_SSortCount (x: Nat) (xs: List Nat) : count x (ssort xs) == count x xs:= by sorry

import Mathlib

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

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

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

lemma min_in_list : minimum x xs ∈ (x::xs) := by
  induction xs generalizing x with
  | nil => unfold minimum; simp;
  | cons head tail ih =>
    unfold minimum; split_ifs with h1;
    case pos;
      rw [List.mem_cons]; right; exact ih;
    case neg;
      rw [List.mem_cons];
      cases (List.mem_cons.1 (@ih x)) with
      | inl h2 => left; exact h2;
      | inr h2 => right; rw [List.mem_cons]; right; exact h2;

lemma delete_len_eq {x: Nat} {xs: List Nat} (h: x ∈ xs): (deleteFirst x xs).length + 1 = xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl generalizing xs with
  | zero => rw [List.length_eq_zero] at hxsl; simp [hxsl] at h;
  | succ n ih =>
    cases xs with
    | nil => simp at hxsl;
    | cons head tail =>
      unfold deleteFirst;
      split_ifs with h1;
      case pos;
        simp at hxsl;
        simp [hxsl];
      case neg;
        rw [List.mem_cons] at h;
        simp at h1;
        simp at hxsl;
        simp;
        exact ih ((or_iff_right h1).1 h) hxsl;

def ssort : List Nat → List Nat
| [] => []
| x::xs =>
  let m := minimum x xs
  have _h: List.length (deleteFirst (minimum x xs) (x :: xs)) < Nat.succ (List.length xs) := by
    have tmp := delete_len_eq (@min_in_list x xs)
    simp at tmp;
    simp [tmp];
  m :: ssort (deleteFirst m (x::xs))
termination_by xs => xs.length

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


theorem prop_SSortPermutes (xs: List Nat) : isPermutation (ssort 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 minimum : Nat → List Nat → Nat
| x, [] => x
| x, y::ys => if y <= x then minimum y ys else minimum x ys

lemma min_in_list : minimum x xs ∈ (x::xs) := by
  induction xs generalizing x with
  | nil => unfold minimum; simp;
  | cons head tail ih =>
    unfold minimum; split_ifs with h1;
    case pos;
      rw [List.mem_cons]; right; exact ih;
    case neg;
      rw [List.mem_cons];
      cases (List.mem_cons.1 (@ih x)) with
      | inl h2 => left; exact h2;
      | inr h2 => right; rw [List.mem_cons]; right; exact h2;

lemma delete_len_eq {x: Nat} {xs: List Nat} (h: x ∈ xs): (deleteFirst x xs).length + 1 = xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl generalizing xs with
  | zero => rw [List.length_eq_zero] at hxsl; simp [hxsl] at h;
  | succ n ih =>
    cases xs with
    | nil => simp at hxsl;
    | cons head tail =>
      unfold deleteFirst;
      split_ifs with h1;
      case pos;
        simp at hxsl;
        simp [hxsl];
      case neg;
        rw [List.mem_cons] at h;
        simp at h1;
        simp at hxsl;
        simp;
        exact ih ((or_iff_right h1).1 h) hxsl;

def ssort : List Nat → List Nat
| [] => []
| x::xs =>
  let m := minimum x xs
  have _h: List.length (deleteFirst (minimum x xs) (x :: xs)) < Nat.succ (List.length xs) := by
    have tmp := delete_len_eq (@min_in_list x xs)
    simp at tmp;
    simp [tmp];
  m :: ssort (deleteFirst m (x::xs))
termination_by xs => xs.length

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


theorem prop_SSortIsSort (xs: List Nat) : ssort xs == isort xs:= by sorry

import Mathlib

def deleteFirst [DecidableEq α]: α → List α → List α
  | _, [] => []
  | n, x::xs => if n == x then xs else x::(deleteFirst n 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)

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

lemma min_in_list : minimum x xs ∈ (x::xs) := by
  induction xs generalizing x with
  | nil => unfold minimum; simp;
  | cons head tail ih =>
    unfold minimum; split_ifs with h1;
    case pos;
      rw [List.mem_cons]; right; exact ih;
    case neg;
      rw [List.mem_cons];
      cases (List.mem_cons.1 (@ih x)) with
      | inl h2 => left; exact h2;
      | inr h2 => right; rw [List.mem_cons]; right; exact h2;

lemma delete_len_eq {x: Nat} {xs: List Nat} (h: x ∈ xs): (deleteFirst x xs).length + 1 = xs.length := by
  generalize hxsl: xs.length = xsl
  induction xsl generalizing xs with
  | zero => rw [List.length_eq_zero] at hxsl; simp [hxsl] at h;
  | succ n ih =>
    cases xs with
    | nil => simp at hxsl;
    | cons head tail =>
      unfold deleteFirst;
      split_ifs with h1;
      case pos;
        simp at hxsl;
        simp [hxsl];
      case neg;
        rw [List.mem_cons] at h;
        simp at h1;
        simp at hxsl;
        simp;
        exact ih ((or_iff_right h1).1 h) hxsl;

def ssort : List Nat → List Nat
| [] => []
| x::xs =>
  let m := minimum x xs
  have _h: List.length (deleteFirst (minimum x xs) (x :: xs)) < Nat.succ (List.length xs) := by
    have tmp := delete_len_eq (@min_in_list x xs)
    simp at tmp;
    simp [tmp];
  m :: ssort (deleteFirst m (x::xs))
termination_by xs => xs.length

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


theorem prop_TSortSorts (xs: List Nat) : ordered (tsort xs) == true:= by sorry

import Mathlib

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

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

def add : Nat → MyTree → MyTree
| x, .nil => .node .nil x .nil
| x, .node p y q => if x <= y then .node (add x p) y q else .node p y (add x q)

def toTree : List Nat → MyTree
| [] => .nil
| x::xs => add x (toTree xs)

def flatten : MyTree → List Nat → List Nat
| .nil, ys => ys
| .node p x q, ys => flatten p (x :: flatten q ys)

def tsort : List Nat → List Nat
| xs => flatten (toTree xs) []

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


theorem prop_TSortCount (x: Nat) (xs: List Nat) : count x (tsort 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 MyTree where
| nil : MyTree
| node : MyTree → Nat → MyTree → MyTree

def add : Nat → MyTree → MyTree
| x, .nil => .node .nil x .nil
| x, .node p y q => if x <= y then .node (add x p) y q else .node p y (add x q)

def toTree : List Nat → MyTree
| [] => .nil
| x::xs => add x (toTree xs)

def flatten : MyTree → List Nat → List Nat
| .nil, ys => ys
| .node p x q, ys => flatten p (x :: flatten q ys)

def tsort : List Nat → List Nat
| xs => flatten (toTree xs) []

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


theorem prop_TSortPermutes (xs: List Nat) : isPermutation (tsort 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 MyTree where
| nil : MyTree
| node : MyTree → Nat → MyTree → MyTree

def add : Nat → MyTree → MyTree
| x, .nil => .node .nil x .nil
| x, .node p y q => if x <= y then .node (add x p) y q else .node p y (add x q)

def toTree : List Nat → MyTree
| [] => .nil
| x::xs => add x (toTree xs)

def flatten : MyTree → List Nat → List Nat
| .nil, ys => ys
| .node p x q, ys => flatten p (x :: flatten q ys)

def tsort : List Nat → List Nat
| xs => flatten (toTree xs) []

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


theorem prop_TSortIsSort (xs: List Nat) : tsort 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 MyTree where
| nil : MyTree
| node : MyTree → Nat → MyTree → MyTree

def add : Nat → MyTree → MyTree
| x, .nil => .node .nil x .nil
| x, .node p y q => if x <= y then .node (add x p) y q else .node p y (add x q)

def toTree : List Nat → MyTree
| [] => .nil
| x::xs => add x (toTree xs)

def flatten : MyTree → List Nat → List Nat
| .nil, ys => ys
| .node p x q, ys => flatten p (x :: flatten q ys)

def tsort : List Nat → List Nat
| xs => flatten (toTree xs) []

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


theorem prop_StoogeSortSorts (xs: List Nat) : ordered (stoogesort' xs) == true:= by sorry

import Mathlib

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

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

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

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

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

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

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

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

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

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

def stoogesort' (xs: List Nat) := stoogesort ⟨xs, Eq.refl xs.length⟩

xs : List ℕ
⊢ (ordered ↑(stoogesort' xs) == true) = true


theorem prop_StoogeSortCount (x: Nat) (xs: List Nat) : count x (stoogesort' 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 splitAt : Nat → List Nat → (List Nat × List Nat)
| _n, [] => ([], [])
| 0, xs => ([], xs)
| n + 1, x::xs => match splitAt n xs with
  | (l1, l2) => (x::l1, l2)

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

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

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

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

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

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

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

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

def stoogesort' (xs: List Nat) := stoogesort ⟨xs, Eq.refl xs.length⟩

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


theorem prop_StoogeSortPermutes (xs: List Nat) : isPermutation (stoogesort' 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 splitAt : Nat → List Nat → (List Nat × List Nat)
| _n, [] => ([], [])
| 0, xs => ([], xs)
| n + 1, x::xs => match splitAt n xs with
  | (l1, l2) => (x::l1, l2)

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

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

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

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

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

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

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

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

def stoogesort' (xs: List Nat) := stoogesort ⟨xs, Eq.refl xs.length⟩

xs : List ℕ
⊢ (isPermutation (↑(stoogesort' xs)) xs == true) = true


theorem prop_StoogeSortIsSort (xs: List Nat) : stoogesort' 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 splitAt : Nat → List Nat → (List Nat × List Nat)
| _n, [] => ([], [])
| 0, xs => ([], xs)
| n + 1, x::xs => match splitAt n xs with
  | (l1, l2) => (x::l1, l2)

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

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

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

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

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

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

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

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

def stoogesort' (xs: List Nat) := stoogesort ⟨xs, Eq.refl xs.length⟩

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


theorem prop_StoogeSort2Sorts (xs: List Nat) : ordered (stoogesort2' xs) == true:= by sorry

import Mathlib

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

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

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

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

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

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

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

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

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

lemma splitAt_first_len_lt (hn: n < xl) (hlen': xl = xs.length): (splitAt n xs).1.length < xl := by
  suffices heq : (splitAt n xs).1.length = n
  simp [heq, hn]
  induction xs generalizing n xl with
  | nil => simp [hlen'] at hn;
  | cons head tail ih =>
    cases n with
    | zero => simp [splitAt];
    | succ nm1 =>
      simp [splitAt];
      exact @ih nm1 tail.length (by simp [hlen'] at hn; exact hn) rfl

lemma twon_lt (n: Nat): (2*n.succ.succ.succ + 1)/ 3 < n.succ.succ.succ := by
  simp [Nat.succ_eq_add_one];
  ring_nf;
  rw [Nat.div_lt_iff_lt_mul (by simp)];
  ring_nf;
  linarith;

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

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

xs : List ℕ
⊢ (ordered ↑(stoogesort2' xs) == true) = true


theorem prop_StoogeSort2Count (x: Nat) (xs: List Nat) : count x (stoogesort2' 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 splitAt : Nat → List Nat → (List Nat × List Nat)
| _n, [] => ([], [])
| 0, xs => ([], xs)
| n + 1, x::xs => match splitAt n xs with
  | (l1, l2) => (x::l1, l2)

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

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

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

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

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

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

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

lemma splitAt_first_len_lt (hn: n < xl) (hlen': xl = xs.length): (splitAt n xs).1.length < xl := by
  suffices heq : (splitAt n xs).1.length = n
  simp [heq, hn]
  induction xs generalizing n xl with
  | nil => simp [hlen'] at hn;
  | cons head tail ih =>
    cases n with
    | zero => simp [splitAt];
    | succ nm1 =>
      simp [splitAt];
      exact @ih nm1 tail.length (by simp [hlen'] at hn; exact hn) rfl

lemma twon_lt (n: Nat): (2*n.succ.succ.succ + 1)/ 3 < n.succ.succ.succ := by
  simp [Nat.succ_eq_add_one];
  ring_nf;
  rw [Nat.div_lt_iff_lt_mul (by simp)];
  ring_nf;
  linarith;

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

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

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


theorem prop_StoogeSort2Permutes (xs: List Nat) : isPermutation (stoogesort2' 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 splitAt : Nat → List Nat → (List Nat × List Nat)
| _n, [] => ([], [])
| 0, xs => ([], xs)
| n + 1, x::xs => match splitAt n xs with
  | (l1, l2) => (x::l1, l2)

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

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

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

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

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

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

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

lemma splitAt_first_len_lt (hn: n < xl) (hlen': xl = xs.length): (splitAt n xs).1.length < xl := by
  suffices heq : (splitAt n xs).1.length = n
  simp [heq, hn]
  induction xs generalizing n xl with
  | nil => simp [hlen'] at hn;
  | cons head tail ih =>
    cases n with
    | zero => simp [splitAt];
    | succ nm1 =>
      simp [splitAt];
      exact @ih nm1 tail.length (by simp [hlen'] at hn; exact hn) rfl

lemma twon_lt (n: Nat): (2*n.succ.succ.succ + 1)/ 3 < n.succ.succ.succ := by
  simp [Nat.succ_eq_add_one];
  ring_nf;
  rw [Nat.div_lt_iff_lt_mul (by simp)];
  ring_nf;
  linarith;

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

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

xs : List ℕ
⊢ (isPermutation (↑(stoogesort2' xs)) xs == true) = true


theorem prop_StoogeSort2IsSort (xs: List Nat) : stoogesort2' 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 splitAt : Nat → List Nat → (List Nat × List Nat)
| _n, [] => ([], [])
| 0, xs => ([], xs)
| n + 1, x::xs => match splitAt n xs with
  | (l1, l2) => (x::l1, l2)

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

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

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

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

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

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

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

lemma splitAt_first_len_lt (hn: n < xl) (hlen': xl = xs.length): (splitAt n xs).1.length < xl := by
  suffices heq : (splitAt n xs).1.length = n
  simp [heq, hn]
  induction xs generalizing n xl with
  | nil => simp [hlen'] at hn;
  | cons head tail ih =>
    cases n with
    | zero => simp [splitAt];
    | succ nm1 =>
      simp [splitAt];
      exact @ih nm1 tail.length (by simp [hlen'] at hn; exact hn) rfl

lemma twon_lt (n: Nat): (2*n.succ.succ.succ + 1)/ 3 < n.succ.succ.succ := by
  simp [Nat.succ_eq_add_one];
  ring_nf;
  rw [Nat.div_lt_iff_lt_mul (by simp)];
  ring_nf;
  linarith;

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

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

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


theorem prop_NStoogeSortSorts (xs: List Nat) : ordered (nstoogesort' xs) == true:= by sorry

import Mathlib

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

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

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

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

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

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

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

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

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

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

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

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

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

xs : List ℕ
⊢ (ordered ↑(nstoogesort' xs) == true) = true


theorem prop_NStoogeSortCount (x: Nat) (xs: List Nat) : count x (nstoogesort' 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 third : Nat → Nat
| 0 => 0
| 1 => 0
| 2 => 0
| n + 3 => 1 + (third n)

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

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

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

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

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

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

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

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

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

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

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

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


theorem prop_NStoogeSortPermutes (xs: List Nat) : isPermutation (nstoogesort' 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 third : Nat → Nat
| 0 => 0
| 1 => 0
| 2 => 0
| n + 3 => 1 + (third n)

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

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

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

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

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

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

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

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

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

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

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

xs : List ℕ
⊢ (isPermutation (↑(nstoogesort' xs)) xs == true) = true


theorem prop_NStoogeSortIsSort (xs: List Nat) : nstoogesort' xs == isort xs:= by sorry

import Mathlib

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


theorem prop_NStoogeSort2Sorts (xs: List Nat) : ordered (nstoogesort2' xs) == true:= by sorry

import Mathlib

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

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

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

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

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

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

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

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

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

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

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

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

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

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


theorem prop_NStoogeSort2Count (x: Nat) (xs: List Nat) : count x (nstoogesort2' 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 third : Nat → Nat
| 0 => 0
| 1 => 0
| 2 => 0
| n + 3 => 1 + (third n)

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

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

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

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

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

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

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

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

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

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

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

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


theorem prop_NStoogeSort2Permutes (xs: List Nat) : isPermutation (nstoogesort2' 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 third : Nat → Nat
| 0 => 0
| 1 => 0
| 2 => 0
| n + 3 => 1 + (third n)

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

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

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

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

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

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

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

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

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

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

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

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