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
nat
class for learning purposes. See the #mwe below for details. Am I missing something in the setup ofnat
that preventsrfl
from working as expected? - I'm using
decreasing_by sorry
to avoid having to prove thatnat.div
terminates. 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 onDecidable
since 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