@Kha I had some unexpected surprises, but it is a good change.
Here is the summary.
1- We could get rid of `a %ₙ b` and `ModN` class. We can use `HMod`
instead. It was a positive surprise since I didn't remember we had
this `ModN` class.
2- Coercions are never used in heterogeneous operators. This is
expected since `a * b` is now notation for `HMul.hMul a b`, and
`a` and `b` may have different types. I manually added instances such
as `HMul Nat Int Int`. However, I did not try to add generic instances
such as
```
instance [Coe a b] [Mul b] : HMul a b b where
hMul x y := mul (coe x) y
```
I will try later.
3- Give `h : cs.size > 0`, I got a type error at
```
let idx : Fin cs.size := ⟨cs.size - 1, Nat.predLt h⟩
```
`Nat.predLt h` has type `Nat.pred cs.size < cs.size`
However, `Nat.pred cs.size` doesn't unify with `cs.size - 1`.
The problem is that we can't synthesize the `HSub` instance until
we apply the default instances.
It worked before because `isDefEq` would force the pending TC
problem `Sub Nat` to be resolved, and after that we would be able
to reduce `cs.size - 1` and establish that it is definitionally
equal to `Nat.pred cs.size`.
I considered two possible workarounds
a) `let idx : Fin cs.size := ⟨cs.size - (1:Nat), Nat.predLt h⟩`
b) `let idx : Fin cs.size := ⟨cs.size - 1, by exact Nat.predLt h⟩`
The first one works because we are not providing enough information
for synthesizing the `HSub` instance. The second works because it
postpones the elaboration of `Nat.predLt h`. The default instances
will be applied before we start applying tactics.
4- The `.` notation is affected too. For example, `(x + 1).toUInt8`
doesn't work since we don't know the type of `x+1` until we apply
default instances. I fixed it by using `(x + (1:Nat)).toUInt8`.
Another possible fix is `Nat.toUInt8 (x + 1)`.
Similarly, `(x+1).fold ...` doesn't work.
5- The following code failed to be elaborated
```
indent (push s!"{ss'}\n") (some (0 - Format.getIndent (← getOptions)))
```
It was working before, but it relied on how the expected type is
propagated. The elaborator process
```
some (0 - Format.getIndent (← getOptions))
```
with expected type `(Option Int)`. So, the `-` is interpreted as
`Int.sub` although `Format.getIndent (← getOptions)` has type `Nat`.
In the new `HSub`, the expected type doesn't really influence TC
resolution since it is an `outparam`. So, we failed with the error
failed to synthesize `HSub Nat Nat Int`.
One possible fix was to add the instance `HSub Nat Nat Int` with
`Int.sub`, but I used the following fix
```
some ((0 : Int) - Format.getIndent (← getOptions))
```
which makes it clear that we want the `Int.sub` operator instead of
`Nat.sub`.
82 lines
2.3 KiB
Text
82 lines
2.3 KiB
Text
#lang lean4
|
||
import Std
|
||
open Std
|
||
|
||
def check (b : Bool) : IO Unit := do
|
||
unless b do IO.println "ERROR"
|
||
|
||
def sz {α β : Type} {lt : α → α → Bool} (m : RBMap α β lt) : Nat :=
|
||
m.fold (fun sz _ _ => sz+1) 0
|
||
|
||
def depth {α β : Type} {lt : α → α → Bool} (m : RBMap α β lt) : Nat :=
|
||
m.depth Nat.max
|
||
|
||
def tst1 : IO Unit :=
|
||
do let Map := RBMap String Nat (fun a b => a < b)
|
||
let m : Map := {}
|
||
let m := m.insert "hello" 0
|
||
let m := m.insert "world" 1
|
||
check (m.find? "hello" == some 0)
|
||
check (m.find? "world" == some 1)
|
||
let m := m.erase "hello"
|
||
check (m.find? "hello" == none)
|
||
check (m.find? "world" == some 1)
|
||
pure ()
|
||
|
||
def tst2 : IO Unit :=
|
||
do let Map := RBMap Nat Nat (fun a b => a < b)
|
||
let m : Map := {}
|
||
let n : Nat := 10000
|
||
let mut m := n.fold (fun i (m : Map) => m.insert i (i*10)) m
|
||
check (m.all (fun k v => v == k*10))
|
||
check (sz m == n)
|
||
IO.println (">> " ++ toString (depth m) ++ ", " ++ toString (sz m))
|
||
for i in [:n/2] do
|
||
m := m.erase (2*i)
|
||
check (m.all (fun k v => v == k*10))
|
||
check (sz m == n / 2)
|
||
IO.println (">> " ++ toString (depth m) ++ ", " ++ toString (sz m))
|
||
pure ()
|
||
|
||
abbrev Map := RBMap Nat Nat (fun a b => a < b)
|
||
|
||
def mkRandMap (max : Nat) : Nat → Map → Array (Nat × Nat) → IO (Map × Array (Nat × Nat))
|
||
| 0, m, a => pure (m, a)
|
||
| n+1, m, a => do
|
||
let k ← IO.rand 0 max
|
||
let v ← IO.rand 0 max
|
||
if m.find? k == none then do
|
||
let m := m.insert k v
|
||
let a := a.push (k, v)
|
||
mkRandMap max n m a
|
||
else
|
||
mkRandMap max n m a
|
||
|
||
def tst3 (seed : Nat) (n : Nat) (max : Nat) : IO Unit :=
|
||
do IO.setRandSeed seed
|
||
let mut (m, a) ← mkRandMap max n {} Array.empty
|
||
check (sz m == a.size)
|
||
check (a.all (fun ⟨k, v⟩ => m.find? k == some v))
|
||
IO.println ("tst3 size: " ++ toString a.size)
|
||
let mut i := 0
|
||
for (k, b) in a do
|
||
if i % 2 == 0 then
|
||
m := m.erase k
|
||
i := i + 1
|
||
check (sz m == a.size / 2)
|
||
let i := 0
|
||
for (k, v) in a do
|
||
if i % 2 == 1 then
|
||
check (m.find? k == some v)
|
||
i := i + 1
|
||
IO.println ("tst3 after, depth: " ++ toString (depth m) ++ ", size: " ++ toString (sz m))
|
||
pure ()
|
||
|
||
def main (xs : List String) : IO Unit :=
|
||
tst1 *> tst2 *>
|
||
tst3 1 1000 20000 *>
|
||
tst3 2 1000 40000 *>
|
||
tst3 3 100 4000 *>
|
||
tst3 4 5000 100000 *>
|
||
tst3 5 1000 40000 *>
|
||
pure ()
|