Skip to content

Commit 28cdb06

Browse files
authored
Fast binary conversion (#1495)
1 parent e884a94 commit 28cdb06

File tree

4 files changed

+128
-19
lines changed

4 files changed

+128
-19
lines changed

CHANGELOG.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ Bug-fixes
1616
Non-backwards compatible changes
1717
--------------------------------
1818

19+
* Replaced O(n) implementation of `Data.Nat.Binary.fromℕ` with O(log n). The old
20+
implementation is maintained under `Data.Nat.Binary.fromℕ'` and proven to be
21+
equivalent.
22+
1923
Deprecated modules
2024
------------------
2125

@@ -61,7 +65,6 @@ Other minor additions
6165
respʳ-flip : _≈_ Respectsʳ (flip _≈_)
6266
respˡ-flip : _≈_ Respectsˡ (flip _≈_)
6367
```
64-
6568
* Added new function to `Data.Fin.Base`:
6669
```agda
6770
pinch : ∀ {n} → Fin n → Fin (suc n) → Fin n
@@ -72,3 +75,21 @@ Other minor additions
7275
pinch-surjective : ∀ {m} (i : Fin m) → Surjective _≡_ (pinch i)
7376
pinch-mono-≤ : ∀ {m} (i : Fin m) → (pinch i) Preserves _≤_ ⟶ _≤_
7477
```
78+
79+
* Added new proofs to `Data.Nat.Binary.Properties`:
80+
```agda
81+
fromℕ≡fromℕ' : fromℕ ≗ fromℕ'
82+
toℕ-fromℕ' : toℕ ∘ fromℕ' ≗ id
83+
fromℕ'-homo-+ : ∀ m n → fromℕ' (m ℕ.+ n) ≡ fromℕ' m + fromℕ' n
84+
```
85+
86+
* Rewrote proofs in `Data.Nat.Binary.Properties` for new implementation of `fromℕ`:
87+
```agda
88+
toℕ-fromℕ : toℕ ∘ fromℕ ≗ id
89+
fromℕ-homo-+ : ∀ m n → fromℕ (m ℕ.+ n) ≡ fromℕ m + fromℕ n
90+
```
91+
92+
* Added new proof to `Data.Nat.DivMod`:
93+
```agda
94+
m/n≤m : ∀ m n {≢0} → (m / n) {≢0} ≤ m
95+
```

src/Data/Nat/Binary/Base.agda

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
module Data.Nat.Binary.Base where
1414

1515
open import Algebra.Core using (Op₂)
16+
open import Data.Bool.Base using (if_then_else_)
1617
open import Data.Nat.Base as ℕ using (ℕ)
18+
open import Data.Nat.DivMod using (_%_ ; _/_)
1719
open import Data.Sum.Base using (_⊎_)
1820
open import Function.Base using (_on_)
1921
open import Level using (0ℓ)
@@ -112,10 +114,22 @@ toℕ zero = 0
112114
toℕ 2[1+ x ] = 2 ℕ.* (ℕ.suc (toℕ x))
113115
toℕ 1+[2 x ] = ℕ.suc (2 ℕ.* (toℕ x))
114116

115-
-- Costs O(n), could be improved using `_/_` and `_%_`
116117
fromℕ : ℕᵇ
117-
fromℕ 0 = zero
118-
fromℕ (ℕ.suc n) = suc (fromℕ n)
118+
fromℕ n = helper n n
119+
module fromℕ where
120+
helper : ℕᵇ
121+
helper 0 _ = zero
122+
helper (ℕ.suc n) (ℕ.suc w) =
123+
if (n % 2 ℕ.≡ᵇ 0)
124+
then 1+[2 helper (n / 2) w ]
125+
else 2[1+ helper (n / 2) w ]
126+
-- Impossible case
127+
helper _ 0 = zero
128+
129+
-- An alternative slower definition
130+
fromℕ' : ℕᵇ
131+
fromℕ' 0 = zero
132+
fromℕ' (ℕ.suc n) = suc (fromℕ' n)
119133

120134
-- An alternative ordering lifted from ℕ
121135

src/Data/Nat/Binary/Properties.agda

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ open import Algebra.Bundles
1212
open import Algebra.Morphism.Structures
1313
import Algebra.Morphism.MonoidMonomorphism as MonoidMonomorphism
1414
open import Algebra.Consequences.Propositional
15+
open import Data.Bool.Base using (if_then_else_; Bool; true; false)
16+
open import Data.Maybe.Base using (Maybe; just; nothing)
1517
open import Data.Nat.Binary.Base
1618
open import Data.Nat as ℕ using (ℕ; z≤n; s≤s)
19+
open import Data.Nat.DivMod using (_%_; _/_; m/n≤m; +-distrib-/-∣ˡ)
20+
open import Data.Nat.Divisibility using (∣-refl)
1721
import Data.Nat.Properties as ℕₚ
1822
open import Data.Nat.Solver
19-
open import Data.Product using (_,_; proj₁; proj₂; ∃)
23+
open import Data.Product using (_×_; _,_; proj₁; proj₂; ∃)
2024
open import Data.Sum.Base using (_⊎_; inj₁; inj₂)
2125
open import Function.Base using (_∘_; _$_; id)
2226
open import Function.Definitions using (Injective)
@@ -98,15 +102,75 @@ toℕ-pred zero = refl
98102
toℕ-pred 2[1+ x ] = cong ℕ.pred $ sym $ ℕₚ.*-distribˡ-+ 2 1 (toℕ x)
99103
toℕ-pred 1+[2 x ] = toℕ-double x
100104

101-
toℕ-fromℕ : toℕ ∘ fromℕ ≗ id
102-
toℕ-fromℕ 0 = refl
103-
toℕ-fromℕ (ℕ.suc n) = begin
104-
toℕ (fromℕ (ℕ.suc n)) ≡⟨⟩
105-
toℕ (suc (fromℕ n)) ≡⟨ toℕ-suc (fromℕ n) ⟩
106-
ℕ.suc (toℕ (fromℕ n)) ≡⟨ cong ℕ.suc (toℕ-fromℕ n) ⟩
105+
toℕ-fromℕ' : toℕ ∘ fromℕ' ≗ id
106+
toℕ-fromℕ' 0 = refl
107+
toℕ-fromℕ' (ℕ.suc n) = begin
108+
toℕ (fromℕ' (ℕ.suc n)) ≡⟨⟩
109+
toℕ (suc (fromℕ' n)) ≡⟨ toℕ-suc (fromℕ' n) ⟩
110+
ℕ.suc (toℕ (fromℕ' n)) ≡⟨ cong ℕ.suc (toℕ-fromℕ' n) ⟩
107111
ℕ.suc n ∎
108112
where open ≡-Reasoning
109113

114+
fromℕ≡fromℕ' : fromℕ ≗ fromℕ'
115+
fromℕ≡fromℕ' n = fromℕ-helper≡fromℕ' n n ℕₚ.≤-refl
116+
where
117+
split : ℕᵇ Maybe Bool × ℕᵇ
118+
split zero = nothing , zero
119+
split 2[1+ n ] = just false , n
120+
split 1+[2 n ] = just true , n
121+
122+
head = proj₁ ∘ split
123+
tail = proj₂ ∘ split
124+
125+
split-injective : Injective _≡_ _≡_ split
126+
split-injective {zero} {zero} refl = refl
127+
split-injective {2[1+ _ ]} {2[1+ _ ]} refl = refl
128+
split-injective {1+[2 _ ]} {1+[2 _ ]} refl = refl
129+
130+
split-if : x xs split (if x then 1+[2 xs ] else 2[1+ xs ]) ≡ (just x , xs)
131+
split-if false xs = refl
132+
split-if true xs = refl
133+
134+
head-suc : n head (suc (suc (suc n))) ≡ head (suc n)
135+
head-suc zero = refl
136+
head-suc 2[1+ n ] = refl
137+
head-suc 1+[2 n ] = refl
138+
139+
tail-suc : n suc (tail (suc n)) ≡ tail (suc (suc (suc n)))
140+
tail-suc zero = refl
141+
tail-suc 2[1+ n ] = refl
142+
tail-suc 1+[2 n ] = refl
143+
144+
head-homo : n head (suc (fromℕ' n)) ≡ just (n % 2 ℕ.≡ᵇ 0)
145+
head-homo ℕ.zero = refl
146+
head-homo (ℕ.suc ℕ.zero) = refl
147+
head-homo (ℕ.suc (ℕ.suc n)) = trans (head-suc (fromℕ' n)) (head-homo n)
148+
149+
open ≡-Reasoning
150+
151+
tail-homo : n tail (suc (fromℕ' n)) ≡ fromℕ' (n / 2)
152+
tail-homo ℕ.zero = refl
153+
tail-homo (ℕ.suc ℕ.zero) = refl
154+
tail-homo (ℕ.suc (ℕ.suc n)) = begin
155+
tail (suc (fromℕ' (ℕ.suc (ℕ.suc n)))) ≡˘⟨ tail-suc (fromℕ' n) ⟩
156+
suc (tail (suc (fromℕ' n))) ≡⟨ cong suc (tail-homo n) ⟩
157+
fromℕ' (ℕ.suc (n / 2)) ≡˘⟨ cong fromℕ' (+-distrib-/-∣ˡ {2} n ∣-refl) ⟩
158+
fromℕ' (ℕ.suc (ℕ.suc n) / 2) ∎
159+
160+
fromℕ-helper≡fromℕ' : n w n ℕ.≤ w fromℕ.helper n n w ≡ fromℕ' n
161+
fromℕ-helper≡fromℕ' ℕ.zero w p = refl
162+
fromℕ-helper≡fromℕ' (ℕ.suc n) (ℕ.suc w) (s≤s n≤w) =
163+
split-injective (begin
164+
split (fromℕ.helper n (ℕ.suc n) (ℕ.suc w)) ≡⟨ split-if _ _ ⟩
165+
just (n % 2 ℕ.≡ᵇ 0) , fromℕ.helper n (n / 2) w ≡⟨ cong (_ ,_) rec-n/2 ⟩
166+
just (n % 2 ℕ.≡ᵇ 0) , fromℕ' (n / 2) ≡˘⟨ cong₂ _,_ (head-homo n) (tail-homo n) ⟩
167+
head (fromℕ' (ℕ.suc n)) , tail (fromℕ' (ℕ.suc n)) ≡⟨⟩
168+
split (fromℕ' (ℕ.suc n)) ∎)
169+
where rec-n/2 = fromℕ-helper≡fromℕ' (n / 2) w (ℕₚ.≤-trans (m/n≤m n 2) n≤w)
170+
171+
toℕ-fromℕ : toℕ ∘ fromℕ ≗ id
172+
toℕ-fromℕ n rewrite fromℕ≡fromℕ' n = toℕ-fromℕ' n
173+
110174
toℕ-injective : Injective _≡_ _≡_ toℕ
111175
toℕ-injective {zero} {zero} _ = refl
112176
toℕ-injective {2[1+ x ]} {2[1+ y ]} 2[1+xN]≡2[1+yN] = cong 2[1+_] x≡y
@@ -144,7 +208,7 @@ fromℕ-injective {x} {y} f[x]≡f[y] = begin
144208
where open ≡-Reasoning
145209

146210
fromℕ-toℕ : fromℕ ∘ toℕ ≗ id
147-
fromℕ-toℕ = toℕ-injective ∘ toℕ-fromℕ ∘ toℕ
211+
fromℕ-toℕ = toℕ-injective ∘ toℕ-fromℕ ∘ toℕ
148212

149213
fromℕ-pred : n fromℕ (ℕ.pred n) ≡ pred (fromℕ n)
150214
fromℕ-pred n = begin
@@ -654,15 +718,19 @@ suc-+ 1+[2 x ] 1+[2 y ] = refl
654718
1+≗suc : (1ᵇ +_) ≗ suc
655719
1+≗suc = suc-+ zero
656720

657-
fromℕ-homo-+ : m n fromℕ (m ℕ.+ n) ≡ fromℕ m + fromℕ n
658-
fromℕ-homo-+ 0 _ = refl
659-
fromℕ-homo-+ (ℕ.suc m) n = begin
660-
fromℕ ((ℕ.suc m) ℕ.+ n) ≡⟨⟩
661-
suc (fromℕ (m ℕ.+ n)) ≡⟨ cong suc (fromℕ-homo-+ m n) ⟩
721+
fromℕ'-homo-+ : m n fromℕ' (m ℕ.+ n) ≡ fromℕ' m + fromℕ' n
722+
fromℕ'-homo-+ 0 _ = refl
723+
fromℕ'-homo-+ (ℕ.suc m) n = begin
724+
fromℕ' ((ℕ.suc m) ℕ.+ n) ≡⟨⟩
725+
suc (fromℕ' (m ℕ.+ n)) ≡⟨ cong suc (fromℕ'-homo-+ m n) ⟩
662726
suc (a + b) ≡⟨ sym (suc-+ a b) ⟩
663727
(suc a) + b ≡⟨⟩
664-
(fromℕ (ℕ.suc m)) + (fromℕ n) ∎
665-
where open ≡-Reasoning; a = fromℕ m; b = fromℕ n
728+
(fromℕ' (ℕ.suc m)) + (fromℕ' n) ∎
729+
where open ≡-Reasoning; a = fromℕ' m; b = fromℕ' n
730+
731+
fromℕ-homo-+ : m n fromℕ (m ℕ.+ n) ≡ fromℕ m + fromℕ n
732+
fromℕ-homo-+ m n rewrite fromℕ≡fromℕ' (m ℕ.+ n) | fromℕ≡fromℕ' m | fromℕ≡fromℕ' n =
733+
fromℕ'-homo-+ m n
666734

667735
------------------------------------------------------------------------
668736
-- Algebraic properties of _+_

src/Data/Nat/DivMod.agda

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ m/n*n≤m m n@(suc n-1) = begin
185185
m % n + (m / n) * n ≡⟨ sym (m≡m%n+[m/n]*n m n-1) ⟩
186186
m ∎
187187

188+
m/n≤m : m n {≢0} (m / n) {≢0} ≤ m
189+
m/n≤m m n@(suc n-1) = *-cancelʳ-≤ (m / n) m n-1 (begin
190+
(m / n) * n ≤⟨ m/n*n≤m m n ⟩
191+
m ≤⟨ m≤m*n m (s≤s z≤n) ⟩
192+
m * n ∎)
193+
188194
m/n<m : m n {≢0} m ≥ 1 n ≥ 2 (m / n) {≢0} < m
189195
m/n<m m n@(suc n-1) m≥1 n≥2 = *-cancelʳ-< {n} (m / n) m (begin-strict
190196
(m / n) * n ≤⟨ m/n*n≤m m n ⟩

0 commit comments

Comments
 (0)