Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 172 additions & 1 deletion Cslib/Languages/CombinatoryLogic/Recursion.lean
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/-
Copyright (c) 2025 Thomas Waring. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Thomas Waring
Authors: Thomas Waring, Jesse Alama
-/

module

public import Cslib.Languages.CombinatoryLogic.Basic
public import Mathlib.Data.Nat.Pairing

@[expose] public section

Expand All @@ -30,6 +31,13 @@ implies `Rec ⬝ x ⬝ g ⬝ a ↠ x`.
- Unbounded root finding (μ-recursion) : given a term `f` representing a function `fℕ: Nat → Nat`,
which takes on the value 0 a term `RFind` such that (`rFind_correct`) `RFind ⬝ f ↠ a` such that
`IsChurch n a` for `n` the smallest root of `fℕ`.
- Integer square root : a term `Sqrt` so that (`sqrt_correct`)
`IsChurch n a → IsChurch n.sqrt (Sqrt ⬝ a)`.
- Nat pairing : a term `NatPair` so that (`natPair_correct`)
`IsChurch a x → IsChurch b y → IsChurch (Nat.pair a b) (NatPair ⬝ x ⬝ y)`.
- Nat unpairing : terms `NatUnpairLeft` and `NatUnpairRight` so that (`natUnpairLeft_correct`)
`IsChurch n a → IsChurch n.unpair.1 (NatUnpairLeft ⬝ a)` and (`natUnpairRight_correct`)
`IsChurch n a → IsChurch n.unpair.2 (NatUnpairRight ⬝ a)`.

## References

Expand Down Expand Up @@ -405,6 +413,169 @@ theorem le_correct (n m : Nat) (a b : SKI) (ha : IsChurch n a) (hb : IsChurch m
apply isZero_correct
apply sub_correct <;> assumption

/-! ### Integer square root -/

/-- Inner condition for Sqrt: with &0 = n, &1 = k,
computes `if n < (k+1)² then 0 else 1`. -/
def SqrtCondPoly : SKI.Polynomial 2 :=
SKI.Cond ⬝' SKI.Zero ⬝' SKI.One
⬝' (SKI.Neg ⬝' (SKI.LE ⬝' (SKI.Mul ⬝' (SKI.Succ ⬝' &1) ⬝' (SKI.Succ ⬝' &1)) ⬝' &0))

/-- SKI term for the inner condition of Sqrt -/
def SqrtCond : SKI := SqrtCondPoly.toSKI

/-- `SqrtCond ⬝ n ⬝ k` reduces to: return 0 if `(k+1)² > n`, else 1.
Used by `RFind` to locate the smallest such `k`, which is `√n`. -/
theorem sqrtCond_def (cn ck : SKI) :
(SqrtCond ⬝ cn ⬝ ck) ↠
SKI.Cond ⬝ SKI.Zero ⬝ SKI.One ⬝
(SKI.Neg ⬝ (SKI.LE ⬝ (SKI.Mul ⬝ (SKI.Succ ⬝ ck) ⬝ (SKI.Succ ⬝ ck)) ⬝ cn)) :=
SqrtCondPoly.toSKI_correct [cn, ck] (by simp)

/-- Sqrt n = smallest k such that (k+1)² > n, i.e., the integer square root.
Defined as `λ n. RFind (SqrtCond n)`. -/
def SqrtPoly : SKI.Polynomial 1 := RFind ⬝' (SqrtCond ⬝' &0)

/-- SKI term for integer square root -/
def Sqrt : SKI := SqrtPoly.toSKI

/-- `Sqrt ⬝ n` reduces to an `RFind` search for the smallest `k` with `(k+1)² > n`. -/
theorem sqrt_def (cn : SKI) : (Sqrt ⬝ cn) ↠ RFind ⬝ (SqrtCond ⬝ cn) :=
SqrtPoly.toSKI_correct [cn] (by simp)

/-- `Sqrt` correctly computes `Nat.sqrt`. -/
theorem sqrt_correct (n : Nat) (cn : SKI) (hcn : IsChurch n cn) :
IsChurch (Nat.sqrt n) (Sqrt ⬝ cn) := by
apply isChurch_trans _ (sqrt_def cn)
apply RFind_correct (fun k => if n < (k + 1) * (k + 1) then 0 else 1) (SqrtCond ⬝ cn)
· -- SqrtCond ⬝ cn correctly computes the function
intro i y hy
apply isChurch_trans _ (sqrtCond_def cn y)
have hsucc := succ_correct i y hy
have hle := le_correct _ n _ cn (mul_correct hsucc hsucc) hcn
have hneg := neg_correct _ _ hle
apply isChurch_trans _ (cond_correct _ _ _ _ hneg)
by_cases h : n < (i + 1) * (i + 1) <;> grind
· -- fNat (Nat.sqrt n) = 0
simp [Nat.lt_succ_sqrt]
· -- ∀ i < Nat.sqrt n, fNat i ≠ 0
intro i hi
have : (i + 1) * (i + 1) ≤ n := Nat.le_sqrt.mp (by omega)
simp [show ¬(n < (i + 1) * (i + 1)) from by omega]

/-! ### Nat pairing (matching Mathlib's `Nat.pair`) -/

/-- NatPair a b = if a < b then b*b + a else a*a + a + b.
With &0 = a, &1 = b. The condition `a < b` is `¬(b ≤ a)`. -/
def NatPairPoly : SKI.Polynomial 2 :=
SKI.Cond ⬝' (SKI.Add ⬝' (SKI.Mul ⬝' &1 ⬝' &1) ⬝' &0)
⬝' (SKI.Add ⬝' (SKI.Add ⬝' (SKI.Mul ⬝' &0 ⬝' &0) ⬝' &0) ⬝' &1)
⬝' (SKI.Neg ⬝' (SKI.LE ⬝' &1 ⬝' &0))

/-- SKI term for Nat pairing -/
def NatPair : SKI := NatPairPoly.toSKI

/-- `NatPair ⬝ a ⬝ b` reduces to: if `a < b` then `b² + a`, else `a² + a + b`. -/
theorem natPair_def (ca cb : SKI) :
(NatPair ⬝ ca ⬝ cb) ↠
SKI.Cond ⬝ (SKI.Add ⬝ (SKI.Mul ⬝ cb ⬝ cb) ⬝ ca)
⬝ (SKI.Add ⬝ (SKI.Add ⬝ (SKI.Mul ⬝ ca ⬝ ca) ⬝ ca) ⬝ cb)
⬝ (SKI.Neg ⬝ (SKI.LE ⬝ cb ⬝ ca)) :=
NatPairPoly.toSKI_correct [ca, cb] (by simp)

/-- `NatPair` correctly computes `Nat.pair`. -/
theorem natPair_correct (a b : Nat) (ca cb : SKI)
(ha : IsChurch a ca) (hb : IsChurch b cb) :
IsChurch (Nat.pair a b) (NatPair ⬝ ca ⬝ cb) := by
simp only [Nat.pair]
apply isChurch_trans _ (natPair_def ca cb)
have hcond := neg_correct _ _ (le_correct b a cb ca hb ha)
apply isChurch_trans _ (cond_correct _ _ _ _ hcond)
by_cases hab : a < b
· have hle : ¬(b ≤ a) := Nat.not_le.mpr hab
simp only [decide_eq_true_eq, hab, hle, ↓reduceIte]
exact add_correct (b * b) a _ ca (mul_correct hb hb) ha
· have hle : b ≤ a := Nat.le_of_not_lt hab
simp only [decide_eq_true_eq, hab, hle, ↓reduceIte]
exact add_correct (a * a + a) b _ cb
(add_correct (a * a) a _ ca (mul_correct ha ha) ha) hb

/-! ### Nat unpairing (matching Mathlib's `Nat.unpair`) -/

/-- `NatUnpairLeft n = if n - s² < s then n - s² else s` where `s = Nat.sqrt n`. -/
def NatUnpairLeftPoly : SKI.Polynomial 1 :=
let s := Sqrt ⬝' &0
let s2 := SKI.Mul ⬝' s ⬝' s
let diff := SKI.Sub ⬝' &0 ⬝' s2
let cond := SKI.Neg ⬝' (SKI.LE ⬝' s ⬝' diff)
SKI.Cond ⬝' diff ⬝' s ⬝' cond

/-- SKI term for left projection of Nat.unpair -/
def NatUnpairLeft : SKI := NatUnpairLeftPoly.toSKI

/-- `NatUnpairLeft ⬝ n` reduces to: let `s = √n` and `d = n - s²`;
return `d` if `d < s`, else `s`. -/
theorem natUnpairLeft_def (cn : SKI) :
(NatUnpairLeft ⬝ cn) ↠
SKI.Cond ⬝ (SKI.Sub ⬝ cn ⬝ (SKI.Mul ⬝ (Sqrt ⬝ cn) ⬝ (Sqrt ⬝ cn)))
⬝ (Sqrt ⬝ cn)
⬝ (SKI.Neg ⬝ (SKI.LE ⬝ (Sqrt ⬝ cn)
⬝ (SKI.Sub ⬝ cn ⬝ (SKI.Mul ⬝ (Sqrt ⬝ cn) ⬝ (Sqrt ⬝ cn))))) :=
NatUnpairLeftPoly.toSKI_correct [cn] (by simp)

/-- Common Church numeral witnesses for `Nat.sqrt` and the difference `n - (Nat.sqrt n)²`. -/
private theorem natUnpair_church (n : Nat) (cn : SKI) (hcn : IsChurch n cn) :
IsChurch (Nat.sqrt n) (Sqrt ⬝ cn) ∧
IsChurch (n - Nat.sqrt n * Nat.sqrt n)
(SKI.Sub ⬝ cn ⬝ (SKI.Mul ⬝ (Sqrt ⬝ cn) ⬝ (Sqrt ⬝ cn))) := by
have hs := sqrt_correct n cn hcn
exact ⟨hs, sub_correct n _ cn _ hcn (mul_correct hs hs)⟩

/-- `NatUnpairLeft` correctly computes the first component of `Nat.unpair`. -/
theorem natUnpairLeft_correct (n : Nat) (cn : SKI) (hcn : IsChurch n cn) :
IsChurch (Nat.unpair n).1 (NatUnpairLeft ⬝ cn) := by
apply isChurch_trans _ (natUnpairLeft_def cn)
obtain ⟨hs, hdiff⟩ := natUnpair_church n cn hcn
have hcond := neg_correct _ _ (le_correct _ _ _ _ hs hdiff)
apply isChurch_trans _ (cond_correct _ _ _ _ hcond)
simp only [Nat.unpair]
by_cases h : n - Nat.sqrt n * Nat.sqrt n < Nat.sqrt n <;> grind

/-- NatUnpairRight n = let s = sqrt n in if n - s² < s then s else n - s² - s. -/
def NatUnpairRightPoly : SKI.Polynomial 1 :=
let s := Sqrt ⬝' &0
let s2 := SKI.Mul ⬝' s ⬝' s
let diff := SKI.Sub ⬝' &0 ⬝' s2
let cond := SKI.Neg ⬝' (SKI.LE ⬝' s ⬝' diff)
SKI.Cond ⬝' s ⬝' (SKI.Sub ⬝' diff ⬝' s) ⬝' cond

/-- SKI term for right projection of Nat.unpair -/
def NatUnpairRight : SKI := NatUnpairRightPoly.toSKI

/-- `NatUnpairRight ⬝ n` reduces to: let `s = √n` and `d = n - s²`;
return `s` if `d < s`, else `d - s`. -/
theorem natUnpairRight_def (cn : SKI) :
(NatUnpairRight ⬝ cn) ↠
SKI.Cond ⬝ (Sqrt ⬝ cn)
⬝ (SKI.Sub ⬝ (SKI.Sub ⬝ cn ⬝ (SKI.Mul ⬝ (Sqrt ⬝ cn) ⬝ (Sqrt ⬝ cn)))
⬝ (Sqrt ⬝ cn))
⬝ (SKI.Neg ⬝ (SKI.LE ⬝ (Sqrt ⬝ cn)
⬝ (SKI.Sub ⬝ cn ⬝ (SKI.Mul ⬝ (Sqrt ⬝ cn) ⬝ (Sqrt ⬝ cn))))) :=
NatUnpairRightPoly.toSKI_correct [cn] (by simp)

/-- `NatUnpairRight` correctly computes the second component of `Nat.unpair`. -/
theorem natUnpairRight_correct (n : Nat) (cn : SKI) (hcn : IsChurch n cn) :
IsChurch (Nat.unpair n).2 (NatUnpairRight ⬝ cn) := by
apply isChurch_trans _ (natUnpairRight_def cn)
obtain ⟨hs, hdiff⟩ := natUnpair_church n cn hcn
have hcond := neg_correct _ _ (le_correct _ _ _ _ hs hdiff)
apply isChurch_trans _ (cond_correct _ _ _ _ hcond)
simp only [Nat.unpair]
by_cases h : n - Nat.sqrt n * Nat.sqrt n < Nat.sqrt n
· simp only [decide_eq_true_eq, h, Nat.not_le.mpr h, ↓reduceIte]; exact hs
· simp only [decide_eq_true_eq, h, Nat.le_of_not_lt h, ↓reduceIte]
exact sub_correct _ _ _ _ hdiff hs

end SKI

end Cslib
Loading