l3lab/ntp-mathlib huggingface dataset:
theorem list_get? : Primrec₂ (@List.get? α) :=
  let F (l : List α) (n : ℕ) :=
    l.foldl
      (fun (s : Sum ℕ α) (a : α) =>
        Sum.casesOn s (@Nat.casesOn (fun _ => Sum ℕ α) · (Sum.inr a) Sum.inl) Sum.inr)
      (Sum.inl n)
  have hF : Primrec₂ F :=
    (list_foldl fst (sum_inl.comp snd)
      ((sum_casesOn fst (nat_casesOn snd (sum_inr.comp <| snd.comp fst) (sum_inl.comp snd).to₂).to₂
              (sum_inr.comp snd).to₂).comp
          snd).to₂).to₂
  have :
    @Primrec _ (Option α) _ _ fun p : List α × ℕ => Sum.casesOn (F p.1 p.2) (fun _ => none) some :=
    sum_casesOn hF (const none).to₂ (option_some.comp snd).to₂
  this.to₂.of_eq fun l n => by
    dsimp; symm
    induction' l with a l IH generalizing n; · rfl
    cases' n with n
    · dsimp
      clear IH
      induction' l with _ l IH <;> simp [*]
    · 

Proof state:
case cons.succ
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
F : List α → ℕ → ℕ ⊕ α :=
  fun l n => List.foldl (fun s a => Sum.casesOn s (fun x => Nat.casesOn x (Sum.inr a) Sum.inl) Sum.inr) (Sum.inl n) l
hF : Primrec₂ F
this : Primrec fun p => Sum.casesOn (F p.1 p.2) (fun x => none) some
a : α
l : List α
IH :
  ∀ (n : ℕ),
    List.get? l n =
      Sum.rec (fun val => none) (fun val => some val)
        (List.foldl
          (fun s a => Sum.rec (fun val => Nat.rec (Sum.inr a) (fun n n_ih => Sum.inl n) val) (fun val => Sum.inr val) s)
          (Sum.inl n) l)
n : ℕ
⊢ List.get? (a :: l) (Nat.succ n) =
    Sum.rec (fun val => none) (fun val => some val)
      (List.foldl
        (fun s a => Sum.rec (fun val => Nat.rec (Sum.inr a) (fun n n_ih => Sum.inl n) val) (fun val => Sum.inr val) s)
        (Sum.inl (Nat.succ n)) (a :: l))

Next tactic:
apply IH
theorem list_getD (d : α) : Primrec₂ fun l n => List.getD l n d := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
d : α
⊢ Primrec₂ fun l n => List.getD l n d

Next tactic:
simp only [List.getD_eq_getD_get?]
theorem list_getD (d : α) : Primrec₂ fun l n => List.getD l n d := by
  simp only [List.getD_eq_getD_get?]
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
d : α
⊢ Primrec₂ fun l n => Option.getD (List.get? l n) d

Next tactic:
exact option_getD.comp₂ list_get? (const _)
theorem list_append : Primrec₂ ((· ++ ·) : List α → List α → List α) :=
  (list_foldr fst snd <| to₂ <| comp (@list_cons α _) snd).to₂.of_eq fun l₁ l₂ => by
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
l₁ l₂ : List α
⊢ List.foldr (fun b s => ((l₁, l₂), b, s).2.1 :: ((l₁, l₂), b, s).2.2) (l₁, l₂).2 (l₁, l₂).1 = l₁ ++ l₂

Next tactic:
induction l₁
theorem list_append : Primrec₂ ((· ++ ·) : List α → List α → List α) :=
  (list_foldr fst snd <| to₂ <| comp (@list_cons α _) snd).to₂.of_eq fun l₁ l₂ => by
    induction l₁ <;> 

Proof state:
case nil
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
l₂ : List α
⊢ List.foldr (fun b s => (([], l₂), b, s).2.1 :: (([], l₂), b, s).2.2) ([], l₂).2 ([], l₂).1 = [] ++ l₂

Next tactic:
simp [*]
theorem list_append : Primrec₂ ((· ++ ·) : List α → List α → List α) :=
  (list_foldr fst snd <| to₂ <| comp (@list_cons α _) snd).to₂.of_eq fun l₁ l₂ => by
    induction l₁ <;> 

Proof state:
case cons
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
l₂ : List α
head✝ : α
tail✝ : List α
tail_ih✝ :
  List.foldr (fun b s => ((tail✝, l₂), b, s).2.1 :: ((tail✝, l₂), b, s).2.2) (tail✝, l₂).2 (tail✝, l₂).1 = tail✝ ++ l₂
⊢ List.foldr (fun b s => ((head✝ :: tail✝, l₂), b, s).2.1 :: ((head✝ :: tail✝, l₂), b, s).2.2) (head✝ :: tail✝, l₂).2
      (head✝ :: tail✝, l₂).1 =
    head✝ :: tail✝ ++ l₂

Next tactic:
simp [*]
theorem list_map {f : α → List β} {g : α → β → σ} (hf : Primrec f) (hg : Primrec₂ g) :
    Primrec fun a => (f a).map (g a) :=
  (list_foldr hf (const []) <|
        to₂ <| list_cons.comp (hg.comp fst (fst.comp snd)) (snd.comp snd)).of_eq
    fun a => by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → List β
g : α → β → σ
hf : Primrec f
hg : Primrec₂ g
a : α
⊢ List.foldr (fun b s => g (a, b, s).1 (a, b, s).2.1 :: (a, b, s).2.2) [] (f a) = List.map (g a) (f a)

Next tactic:
induction f a
theorem list_map {f : α → List β} {g : α → β → σ} (hf : Primrec f) (hg : Primrec₂ g) :
    Primrec fun a => (f a).map (g a) :=
  (list_foldr hf (const []) <|
        to₂ <| list_cons.comp (hg.comp fst (fst.comp snd)) (snd.comp snd)).of_eq
    fun a => by induction f a <;> 

Proof state:
case nil
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → List β
g : α → β → σ
hf : Primrec f
hg : Primrec₂ g
a : α
⊢ List.foldr (fun b s => g (a, b, s).1 (a, b, s).2.1 :: (a, b, s).2.2) [] [] = List.map (g a) []

Next tactic:
simp [*]
theorem list_map {f : α → List β} {g : α → β → σ} (hf : Primrec f) (hg : Primrec₂ g) :
    Primrec fun a => (f a).map (g a) :=
  (list_foldr hf (const []) <|
        to₂ <| list_cons.comp (hg.comp fst (fst.comp snd)) (snd.comp snd)).of_eq
    fun a => by induction f a <;> 

Proof state:
case cons
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → List β
g : α → β → σ
hf : Primrec f
hg : Primrec₂ g
a : α
head✝ : β
tail✝ : List β
tail_ih✝ : List.foldr (fun b s => g (a, b, s).1 (a, b, s).2.1 :: (a, b, s).2.2) [] tail✝ = List.map (g a) tail✝
⊢ List.foldr (fun b s => g (a, b, s).1 (a, b, s).2.1 :: (a, b, s).2.2) [] (head✝ :: tail✝) =
    List.map (g a) (head✝ :: tail✝)

Next tactic:
simp [*]
theorem list_range : Primrec List.range :=
  (nat_rec' .id (const []) ((list_concat.comp snd fst).comp snd).to₂).of_eq fun n => by
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
n : ℕ
⊢ Nat.rec [] (fun n_1 IH => (n, n_1, IH).2.2 ++ [(n, n_1, IH).2.1]) (id n) = List.range n

Next tactic:
simp
theorem list_range : Primrec List.range :=
  (nat_rec' .id (const []) ((list_concat.comp snd fst).comp snd).to₂).of_eq fun n => by
    simp; 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
n : ℕ
⊢ Nat.rec [] (fun n IH => IH ++ [n]) n = List.range n

Next tactic:
induction n
theorem list_range : Primrec List.range :=
  (nat_rec' .id (const []) ((list_concat.comp snd fst).comp snd).to₂).of_eq fun n => by
    simp; induction n <;> 

Proof state:
case zero
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
⊢ Nat.rec [] (fun n IH => IH ++ [n]) Nat.zero = List.range Nat.zero

Next tactic:
simp [*, List.range_succ]
theorem list_range : Primrec List.range :=
  (nat_rec' .id (const []) ((list_concat.comp snd fst).comp snd).to₂).of_eq fun n => by
    simp; induction n <;> 

Proof state:
case succ
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
n✝ : ℕ
n_ih✝ : Nat.rec [] (fun n IH => IH ++ [n]) n✝ = List.range n✝
⊢ Nat.rec [] (fun n IH => IH ++ [n]) (Nat.succ n✝) = List.range (Nat.succ n✝)

Next tactic:
simp [*, List.range_succ]
theorem list_join : Primrec (@List.join α) :=
  (list_foldr .id (const []) <| to₂ <| comp (@list_append α _) snd).of_eq fun l => by
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
l : List (List α)
⊢ List.foldr (fun b s => (fun x x_1 => x ++ x_1) (l, b, s).2.1 (l, b, s).2.2) [] (id l) = List.join l

Next tactic:
dsimp
theorem list_join : Primrec (@List.join α) :=
  (list_foldr .id (const []) <| to₂ <| comp (@list_append α _) snd).of_eq fun l => by
    dsimp; 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
l : List (List α)
⊢ List.foldr (fun b s => b ++ s) [] l = List.join l

Next tactic:
induction l
theorem list_join : Primrec (@List.join α) :=
  (list_foldr .id (const []) <| to₂ <| comp (@list_append α _) snd).of_eq fun l => by
    dsimp; induction l <;> 

Proof state:
case nil
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
⊢ List.foldr (fun b s => b ++ s) [] [] = List.join []

Next tactic:
simp [*]
theorem list_join : Primrec (@List.join α) :=
  (list_foldr .id (const []) <| to₂ <| comp (@list_append α _) snd).of_eq fun l => by
    dsimp; induction l <;> 

Proof state:
case cons
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
head✝ : List α
tail✝ : List (List α)
tail_ih✝ : List.foldr (fun b s => b ++ s) [] tail✝ = List.join tail✝
⊢ List.foldr (fun b s => b ++ s) [] (head✝ :: tail✝) = List.join (head✝ :: tail✝)

Next tactic:
simp [*]
theorem list_length : Primrec (@List.length α) :=
  (list_foldr (@Primrec.id (List α) _) (const 0) <| to₂ <| (succ.comp <| snd.comp snd).to₂).of_eq
    fun l => by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
l : List α
⊢ List.foldr (fun b s => (fun a b => Nat.succ (a, b).2.2) (l, b, s).1 (l, b, s).2) 0 (id l) = List.length l

Next tactic:
dsimp
theorem list_length : Primrec (@List.length α) :=
  (list_foldr (@Primrec.id (List α) _) (const 0) <| to₂ <| (succ.comp <| snd.comp snd).to₂).of_eq
    fun l => by dsimp; 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
l : List α
⊢ List.foldr (fun b s => Nat.succ s) 0 l = List.length l

Next tactic:
induction l
theorem list_length : Primrec (@List.length α) :=
  (list_foldr (@Primrec.id (List α) _) (const 0) <| to₂ <| (succ.comp <| snd.comp snd).to₂).of_eq
    fun l => by dsimp; induction l <;> 

Proof state:
case nil
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
⊢ List.foldr (fun b s => Nat.succ s) 0 [] = List.length []

Next tactic:
simp [*]
theorem list_length : Primrec (@List.length α) :=
  (list_foldr (@Primrec.id (List α) _) (const 0) <| to₂ <| (succ.comp <| snd.comp snd).to₂).of_eq
    fun l => by dsimp; induction l <;> 

Proof state:
case cons
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
head✝ : α
tail✝ : List α
tail_ih✝ : List.foldr (fun b s => Nat.succ s) 0 tail✝ = List.length tail✝
⊢ List.foldr (fun b s => Nat.succ s) 0 (head✝ :: tail✝) = List.length (head✝ :: tail✝)

Next tactic:
simp [*]
theorem list_findIdx {f : α → List β} {p : α → β → Bool}
    (hf : Primrec f) (hp : Primrec₂ p) : Primrec fun a => (f a).findIdx (p a) :=
  (list_foldr hf (const 0) <|
        to₂ <| cond (hp.comp fst <| fst.comp snd) (const 0) (succ.comp <| snd.comp snd)).of_eq
    fun a => by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → List β
p : α → β → Bool
hf : Primrec f
hp : Primrec₂ p
a : α
⊢ List.foldr (fun b s => bif p (a, b, s).1 (a, b, s).2.1 then 0 else Nat.succ (a, b, s).2.2) 0 (f a) =
    List.findIdx (p a) (f a)

Next tactic:
dsimp
theorem list_findIdx {f : α → List β} {p : α → β → Bool}
    (hf : Primrec f) (hp : Primrec₂ p) : Primrec fun a => (f a).findIdx (p a) :=
  (list_foldr hf (const 0) <|
        to₂ <| cond (hp.comp fst <| fst.comp snd) (const 0) (succ.comp <| snd.comp snd)).of_eq
    fun a => by dsimp; 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → List β
p : α → β → Bool
hf : Primrec f
hp : Primrec₂ p
a : α
⊢ List.foldr (fun b s => bif p a b then 0 else Nat.succ s) 0 (f a) = List.findIdx (p a) (f a)

Next tactic:
induction f a
theorem list_findIdx {f : α → List β} {p : α → β → Bool}
    (hf : Primrec f) (hp : Primrec₂ p) : Primrec fun a => (f a).findIdx (p a) :=
  (list_foldr hf (const 0) <|
        to₂ <| cond (hp.comp fst <| fst.comp snd) (const 0) (succ.comp <| snd.comp snd)).of_eq
    fun a => by dsimp; induction f a <;> 

Proof state:
case nil
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → List β
p : α → β → Bool
hf : Primrec f
hp : Primrec₂ p
a : α
⊢ List.foldr (fun b s => bif p a b then 0 else Nat.succ s) 0 [] = List.findIdx (p a) []

Next tactic:
simp [List.findIdx_cons, *]
theorem list_findIdx {f : α → List β} {p : α → β → Bool}
    (hf : Primrec f) (hp : Primrec₂ p) : Primrec fun a => (f a).findIdx (p a) :=
  (list_foldr hf (const 0) <|
        to₂ <| cond (hp.comp fst <| fst.comp snd) (const 0) (succ.comp <| snd.comp snd)).of_eq
    fun a => by dsimp; induction f a <;> 

Proof state:
case cons
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → List β
p : α → β → Bool
hf : Primrec f
hp : Primrec₂ p
a : α
head✝ : β
tail✝ : List β
tail_ih✝ : List.foldr (fun b s => bif p a b then 0 else Nat.succ s) 0 tail✝ = List.findIdx (p a) tail✝
⊢ List.foldr (fun b s => bif p a b then 0 else Nat.succ s) 0 (head✝ :: tail✝) = List.findIdx (p a) (head✝ :: tail✝)

Next tactic:
simp [List.findIdx_cons, *]
theorem nat_strong_rec (f : α → ℕ → σ) {g : α → List σ → Option σ} (hg : Primrec₂ g)
    (H : ∀ a n, g a ((List.range n).map (f a)) = some (f a n)) : Primrec₂ f :=
  suffices Primrec₂ fun a n => (List.range n).map (f a) from
    Primrec₂.option_some_iff.1 <|
      (list_get?.comp (this.comp fst (succ.comp snd)) snd).to₂.of_eq fun a n => by
        simp [List.get?_range (Nat.lt_succ_self n)]
  Primrec₂.option_some_iff.1 <|
    (nat_rec (const (some []))
          (to₂ <|
            option_bind (snd.comp snd) <|
              to₂ <|
                option_map (hg.comp (fst.comp fst) snd)
                  (to₂ <| list_concat.comp (snd.comp fst) snd))).of_eq
      fun a n => by
      

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → ℕ → σ
g : α → List σ → Option σ
hg : Primrec₂ g
H : ∀ (a : α) (n : ℕ), g a (List.map (f a) (List.range n)) = some (f a n)
a : α
n : ℕ
⊢ Nat.rec (some [])
      (fun n IH =>
        Option.bind (a, n, IH).2.2 fun b =>
          Option.map (fun b_1 => (((a, n, IH), b), b_1).1.2 ++ [(((a, n, IH), b), b_1).2])
            (g ((a, n, IH), b).1.1 ((a, n, IH), b).2))
      n =
    some (List.map (f a) (List.range n))

Next tactic:
simp
theorem nat_strong_rec (f : α → ℕ → σ) {g : α → List σ → Option σ} (hg : Primrec₂ g)
    (H : ∀ a n, g a ((List.range n).map (f a)) = some (f a n)) : Primrec₂ f :=
  suffices Primrec₂ fun a n => (List.range n).map (f a) from
    Primrec₂.option_some_iff.1 <|
      (list_get?.comp (this.comp fst (succ.comp snd)) snd).to₂.of_eq fun a n => by
        simp [List.get?_range (Nat.lt_succ_self n)]
  Primrec₂.option_some_iff.1 <|
    (nat_rec (const (some []))
          (to₂ <|
            option_bind (snd.comp snd) <|
              to₂ <|
                option_map (hg.comp (fst.comp fst) snd)
                  (to₂ <| list_concat.comp (snd.comp fst) snd))).of_eq
      fun a n => by
      simp; 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → ℕ → σ
g : α → List σ → Option σ
hg : Primrec₂ g
H : ∀ (a : α) (n : ℕ), g a (List.map (f a) (List.range n)) = some (f a n)
a : α
n : ℕ
⊢ Nat.rec (some []) (fun n IH => Option.bind IH fun b => Option.map (fun b_1 => b ++ [b_1]) (g a b)) n =
    some (List.map (f a) (List.range n))

Next tactic:
induction' n with n IH
theorem nat_strong_rec (f : α → ℕ → σ) {g : α → List σ → Option σ} (hg : Primrec₂ g)
    (H : ∀ a n, g a ((List.range n).map (f a)) = some (f a n)) : Primrec₂ f :=
  suffices Primrec₂ fun a n => (List.range n).map (f a) from
    Primrec₂.option_some_iff.1 <|
      (list_get?.comp (this.comp fst (succ.comp snd)) snd).to₂.of_eq fun a n => by
        simp [List.get?_range (Nat.lt_succ_self n)]
  Primrec₂.option_some_iff.1 <|
    (nat_rec (const (some []))
          (to₂ <|
            option_bind (snd.comp snd) <|
              to₂ <|
                option_map (hg.comp (fst.comp fst) snd)
                  (to₂ <| list_concat.comp (snd.comp fst) snd))).of_eq
      fun a n => by
      simp; induction' n with n IH; · 

Proof state:
case zero
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → ℕ → σ
g : α → List σ → Option σ
hg : Primrec₂ g
H : ∀ (a : α) (n : ℕ), g a (List.map (f a) (List.range n)) = some (f a n)
a : α
⊢ Nat.rec (some []) (fun n IH => Option.bind IH fun b => Option.map (fun b_1 => b ++ [b_1]) (g a b)) Nat.zero =
    some (List.map (f a) (List.range Nat.zero))

Next tactic:
rfl
theorem nat_strong_rec (f : α → ℕ → σ) {g : α → List σ → Option σ} (hg : Primrec₂ g)
    (H : ∀ a n, g a ((List.range n).map (f a)) = some (f a n)) : Primrec₂ f :=
  suffices Primrec₂ fun a n => (List.range n).map (f a) from
    Primrec₂.option_some_iff.1 <|
      (list_get?.comp (this.comp fst (succ.comp snd)) snd).to₂.of_eq fun a n => by
        simp [List.get?_range (Nat.lt_succ_self n)]
  Primrec₂.option_some_iff.1 <|
    (nat_rec (const (some []))
          (to₂ <|
            option_bind (snd.comp snd) <|
              to₂ <|
                option_map (hg.comp (fst.comp fst) snd)
                  (to₂ <| list_concat.comp (snd.comp fst) snd))).of_eq
      fun a n => by
      simp; induction' n with n IH; · rfl
      

Proof state:
case succ
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → ℕ → σ
g : α → List σ → Option σ
hg : Primrec₂ g
H : ∀ (a : α) (n : ℕ), g a (List.map (f a) (List.range n)) = some (f a n)
a : α
n : ℕ
IH :
  Nat.rec (some []) (fun n IH => Option.bind IH fun b => Option.map (fun b_1 => b ++ [b_1]) (g a b)) n =
    some (List.map (f a) (List.range n))
⊢ Nat.rec (some []) (fun n IH => Option.bind IH fun b => Option.map (fun b_1 => b ++ [b_1]) (g a b)) (Nat.succ n) =
    some (List.map (f a) (List.range (Nat.succ n)))

Next tactic:
simp [IH, H, List.range_succ]
theorem nat_strong_rec (f : α → ℕ → σ) {g : α → List σ → Option σ} (hg : Primrec₂ g)
    (H : ∀ a n, g a ((List.range n).map (f a)) = some (f a n)) : Primrec₂ f :=
  suffices Primrec₂ fun a n => (List.range n).map (f a) from
    Primrec₂.option_some_iff.1 <|
      (list_get?.comp (this.comp fst (succ.comp snd)) snd).to₂.of_eq fun a n => by
        

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
σ : Type u_4
inst✝³ : Primcodable α
inst✝² : Primcodable β
inst✝¹ : Primcodable γ
inst✝ : Primcodable σ
f : α → ℕ → σ
g : α → List σ → Option σ
hg : Primrec₂ g
H : ∀ (a : α) (n : ℕ), g a (List.map (f a) (List.range n)) = some (f a n)
this : Primrec₂ fun a n => List.map (f a) (List.range n)
a : α
n : ℕ
⊢ List.get? (List.map (f (a, n).1) (List.range (Nat.succ (a, n).2))) (a, n).2 = some (f a n)

Next tactic:
simp [List.get?_range (Nat.lt_succ_self n)]
/-- A subtype of a primitive recursive predicate is `Primcodable`. -/
def subtype {p : α → Prop} [DecidablePred p] (hp : PrimrecPred p) : Primcodable (Subtype p) :=
  ⟨have : Primrec fun n => (@decode α _ n).bind fun a => Option.guard p a :=
    option_bind .decode (option_guard (hp.comp snd).to₂ snd)
  nat_iff.1 <| (encode_iff.2 this).of_eq fun n =>
    show _ = encode ((@decode α _ n).bind fun a => _) by
      

Proof state:
α : Type u_1
β : Type u_2
inst✝² : Primcodable α
inst✝¹ : Primcodable β
p : α → Prop
inst✝ : DecidablePred p
hp : PrimrecPred p
this : Primrec fun n => Option.bind (decode n) fun a => Option.guard p a
n : ℕ
⊢ encode (Option.bind (decode n) fun a => Option.guard p a) =
    encode
      (Option.bind (decode n) fun a =>
        if h : (fun a => (fun a => p a) a) a then some { val := a, property := h } else none)

Next tactic:
cases' @decode α _ n with a
/-- A subtype of a primitive recursive predicate is `Primcodable`. -/
def subtype {p : α → Prop} [DecidablePred p] (hp : PrimrecPred p) : Primcodable (Subtype p) :=
  ⟨have : Primrec fun n => (@decode α _ n).bind fun a => Option.guard p a :=
    option_bind .decode (option_guard (hp.comp snd).to₂ snd)
  nat_iff.1 <| (encode_iff.2 this).of_eq fun n =>
    show _ = encode ((@decode α _ n).bind fun a => _) by
      cases' @decode α _ n with a; · 

Proof state:
case none
α : Type u_1
β : Type u_2
inst✝² : Primcodable α
inst✝¹ : Primcodable β
p : α → Prop
inst✝ : DecidablePred p
hp : PrimrecPred p
this : Primrec fun n => Option.bind (decode n) fun a => Option.guard p a
n : ℕ
⊢ encode (Option.bind none fun a => Option.guard p a) =
    encode
      (Option.bind none fun a => if h : (fun a => (fun a => p a) a) a then some { val := a, property := h } else none)

Next tactic:
rfl
/-- A subtype of a primitive recursive predicate is `Primcodable`. -/
def subtype {p : α → Prop} [DecidablePred p] (hp : PrimrecPred p) : Primcodable (Subtype p) :=
  ⟨have : Primrec fun n => (@decode α _ n).bind fun a => Option.guard p a :=
    option_bind .decode (option_guard (hp.comp snd).to₂ snd)
  nat_iff.1 <| (encode_iff.2 this).of_eq fun n =>
    show _ = encode ((@decode α _ n).bind fun a => _) by
      cases' @decode α _ n with a; · rfl
      

Proof state:
case some
α : Type u_1
β : Type u_2
inst✝² : Primcodable α
inst✝¹ : Primcodable β
p : α → Prop
inst✝ : DecidablePred p
hp : PrimrecPred p
this : Primrec fun n => Option.bind (decode n) fun a => Option.guard p a
n : ℕ
a : α
⊢ encode (Option.bind (some a) fun a => Option.guard p a) =
    encode
      (Option.bind (some a) fun a =>
        if h : (fun a => (fun a => p a) a) a then some { val := a, property := h } else none)

Next tactic:
dsimp [Option.guard]
/-- A subtype of a primitive recursive predicate is `Primcodable`. -/
def subtype {p : α → Prop} [DecidablePred p] (hp : PrimrecPred p) : Primcodable (Subtype p) :=
  ⟨have : Primrec fun n => (@decode α _ n).bind fun a => Option.guard p a :=
    option_bind .decode (option_guard (hp.comp snd).to₂ snd)
  nat_iff.1 <| (encode_iff.2 this).of_eq fun n =>
    show _ = encode ((@decode α _ n).bind fun a => _) by
      cases' @decode α _ n with a; · rfl
      dsimp [Option.guard]
      

Proof state:
case some
α : Type u_1
β : Type u_2
inst✝² : Primcodable α
inst✝¹ : Primcodable β
p : α → Prop
inst✝ : DecidablePred p
hp : PrimrecPred p
this : Primrec fun n => Option.bind (decode n) fun a => Option.guard p a
n : ℕ
a : α
⊢ encode (if p a then some a else none) = encode (if h : p a then some { val := a, property := h } else none)

Next tactic:
by_cases h : p a