Zulip Chat Archive
Stream: new members
Topic: Implementing floor division (for ℕ) from scratch
Isak Colboubrani (May 28 2024 at 22:14):
I'm encountering some issues with the following implementation of (floor) division for natural numbers:
def nat.div (x y : nat) : nat :=
if 0 < y ∧ y ≤ x then
nat.div (x - y) (y + 1)
else
0
decreasing_by sorry
example : nat.div (100 : nat) (10 : nat) = (10 : nat) := by rfl -- fails
The example fails with a "type mismatch" error that I can't resolve.
Consider the following:
- I'm using my own
natclass for learning purposes. See the #mwe below for details. Am I missing something in the setup ofnatthat preventsrflfrom working as expected? - I'm using
decreasing_by sorryto avoid having to prove thatnat.divterminates. Although this is a temporary workaround and needs to be addressed, it shouldn't affect the outcome in this case, right? - The
force_decidable(Decidable (0 < y ∧ y ≤ x)) approach is quite hacky. Could this be the cause of the issue? If so, I will need to read up onDecidablesince I don't fully understand the exact circumstances under which it is needed.
Here is the #mwe:
inductive nat
| zero : nat
| succ (n : nat) : nat
deriving DecidableEq
instance : OfNat nat 0 where ofNat := nat.zero
instance {n : Nat} [OfNat nat n] : OfNat nat (n + 1) where ofNat := nat.succ (OfNat.ofNat n)
def nat.add (n1 n2 : nat) : nat := match n1, n2 with
| n1, 0 => n1
| n1, nat.succ n2 => nat.succ (add n1 n2)
instance : Add nat where add := nat.add
def nat.pred : nat → nat
| 0 => 0
| nat.succ a => a
example : nat.pred (2 : nat) = (1 : nat) := by rfl
def nat.sub : nat → nat → nat
| a, 0 => a
| a, nat.succ b => nat.pred (nat.sub a b)
instance : Sub nat where sub := nat.sub
inductive nat.le (n : nat) : nat → Prop
| refl : nat.le n n
| step {m} : nat.le n m → nat.le n (nat.succ m)
instance : LE nat where le := nat.le
def nat.lt (n m : nat) : Prop :=
nat.le (nat.succ n) m
instance : LT nat where lt := nat.lt
instance force_decidable (y x : nat) : Decidable (0 < y ∧ y ≤ x) := by sorry
def nat.div (x y : nat) : nat :=
if 0 < y ∧ y ≤ x then
nat.div (x - y) (y + 1)
else
0
decreasing_by sorry
instance : Div nat where div := nat.div
example : (100 : nat) / (10 : nat) = (10 : nat) := by rfl -- fails
Andrew Yang (May 28 2024 at 22:17):
force_decidable will not work. Decidable is essentially an algorithm that gets used by rfl in if ... then ... else and lean cannot hallucinate one if it is sorried.
Isak Colboubrani (May 28 2024 at 22:20):
Oh, thanks! It looks like I have some reading to do! Do you have any good recommendations?
Andrew Yang (May 28 2024 at 22:20):
Maybe #tpil ?
Andrew Yang (May 28 2024 at 22:21):
In particular this section
Andrew Yang (May 28 2024 at 22:23):
(not sure why the link to the section doesn't work. I mean the "decidable propositions" section in the 10. type class chapter)
Andrew Yang (May 28 2024 at 22:31):
Also I believe the definition should be nat.div (x - y) y + 1?
Isak Colboubrani (May 28 2024 at 23:42):
Thanks Andrew!
I believe I have resolved this issue. The missing components were:
instance nat.decLe (n m : nat) : Decidable (LE.le n m) :=
dite (Eq (nat.ble n m) true) (fun h => isTrue (nat.le_of_ble_eq_true h)) (fun h => isFalse (nat.not_le_of_not_ble_eq_true h))
instance nat.decLt (n m : nat) : Decidable (LT.lt n m) :=
nat.decLe (nat.succ n) m
… along with def nat.ble : nat → nat → Bool, and the six theorems directly or indirectly referenced: not_le_of_not_ble_eq_true, ble_eq_true_of_le, ble_succ_eq_true, ble_self_eq_true, le_of_ble_eq_true, zero_le and succ_le_succ.
Decidability indeed seems somewhat... cumbersome!
Below is the updated #mwe. Can it be improved or simplified? Are there any shortcuts, or is this level of complexity necessary for decidability? :cold_sweat:
inductive nat
| zero : nat
| succ (n : nat) : nat
deriving DecidableEq
instance : OfNat nat 0 where ofNat := nat.zero
instance {n : Nat} [OfNat nat n] : OfNat nat (n + 1) where ofNat := nat.succ (OfNat.ofNat n)
def nat.add (n1 n2 : nat) : nat := match n1, n2 with
| n1, 0 => n1
| n1, nat.succ n2 => nat.succ (add n1 n2)
instance : Add nat where add := nat.add
def nat.pred : nat → nat
| 0 => 0
| nat.succ a => a
def nat.sub : nat → nat → nat
| a, 0 => a
| a, nat.succ b => nat.pred (nat.sub a b)
instance : Sub nat where sub := nat.sub
inductive nat.le (n : nat) : nat → Prop
| refl : nat.le n n
| step {m} : nat.le n m → nat.le n (nat.succ m)
instance : LE nat where le := nat.le
def nat.lt (n m : nat) : Prop :=
nat.le (nat.succ n) m
instance : LT nat where lt := nat.lt
def nat.ble : nat → nat → Bool
| nat.zero, nat.zero => true
| nat.zero, nat.succ _ => true
| nat.succ _, nat.zero => false
| nat.succ n, nat.succ m => nat.ble n m
theorem nat.succ_le_succ {n m : nat} : LE.le n m → LE.le (nat.succ n) (nat.succ m)
| nat.le.refl => nat.le.refl
| nat.le.step h => nat.le.step (nat.succ_le_succ h)
theorem nat.zero_le : (n : nat) → LE.le 0 n
| zero => nat.le.refl
| succ n => nat.le.step (nat.zero_le n)
theorem nat.le_of_ble_eq_true {n m : nat} (h : Eq (nat.ble n m) true) : LE.le n m :=
match n, m with
| 0, _ => nat.zero_le _
| succ _, succ _ => nat.succ_le_succ (nat.le_of_ble_eq_true h)
theorem nat.ble_self_eq_true : (n : nat) → Eq (nat.ble n n) true
| 0 => rfl
| nat.succ n => nat.ble_self_eq_true n
theorem nat.ble_succ_eq_true : {n m : nat} → Eq (nat.ble n m) true → Eq (nat.ble n (nat.succ m)) true
| 0, _, _ => rfl
| succ n, succ _, h => nat.ble_succ_eq_true (n := n) h
theorem nat.ble_eq_true_of_le {n m : nat} (h : LE.le n m) : Eq (nat.ble n m) true :=
match h with
| nat.le.refl => nat.ble_self_eq_true n
| nat.le.step h => nat.ble_succ_eq_true (nat.ble_eq_true_of_le h)
theorem nat.not_le_of_not_ble_eq_true {n m : nat} (h : Not (Eq (nat.ble n m) true)) : Not (LE.le n m) :=
fun h' => absurd (nat.ble_eq_true_of_le h') h
instance nat.decLe (n m : nat) : Decidable (LE.le n m) :=
dite (Eq (nat.ble n m) true) (fun h => isTrue (nat.le_of_ble_eq_true h)) (fun h => isFalse (nat.not_le_of_not_ble_eq_true h))
instance nat.decLt (n m : nat) : Decidable (LT.lt n m) :=
nat.decLe (nat.succ n) m
def nat.div (x y : nat) : nat :=
if 0 < y ∧ y ≤ x then
nat.div (x - y) y + 1
else
0
decreasing_by sorry
example : nat.div (100 : nat) (10 : nat) = (10 : nat) := by rfl
instance : Div nat where div := nat.div
example : (100 : Nat) / (10 : Nat) = (10 : Nat) := by rfl
Last updated: May 02 2025 at 03:31 UTC