[Agda] gcd, toDecimal performance
Sergei Meshveliani
mechvel at botik.ru
Mon Mar 25 16:53:04 CET 2019
Dear standard library developers,
I wrote recently about the performance of divMod for Nat and rational
arithmetics, and Ulf issued some comments.
But now I discover news, when look into Dat.Nat/
in the
experimental lib branch for Agda-2.6.0-candidate.
There appear _≡ᵇ_, _<ᵇ_, div-mod-helper
which use built-ins from GHC.
Also the attached test shows that mod (_%_) and _div_ are fast enough.
(1) But gcd remains `exponentially' slow.
This is strange. Because gcd is usually found by applying divMod a few
times.
This slow gcd will actually break rational arithmetic.
(2) Printing Nat (in decimal digits) is `exponentially' slow.
This is also strange. Because conversion from unary to decimal system
can be written as something like applying (\n -> divMod n 10) a few
times.
Am I missing something?
Can you, please, fix these two features?
I guess this is not difficult, because standard library currently has
all the needed fast functions.
Please try the _attached test_.
It
reads n : Nat from data.txt (n = 10, 11, 12, ...);
computes fs = factorials n = [0!, 1!, ..., n!]
(12! is not so large);
computes x % n, (x div n) % n for x <- fs;
prints x % n, (x div n) % n for x <- fs.
prints gcd! x n for x <- fs; -- II
prints fs. -- III
For n = 12, all this is done in a moment - except II and III.
II takes very long,
III takes very long.
I guess this illustrates the conclusions (1) and (2).
Does it?
Can you, please, look into the subject?
I have an impressions that these things can be fixed easily.
Thanks,
------
Sergei
-------------- next part --------------
open import Foreign.Haskell
open import IO.Primitive
open import Codata.Musical.Costring using (toCostring)
open import Function using (_∘_; case_of_)
open import Algebra.FunctionProperties using (Op₂)
open import Relation.Nullary using (yes; no)
open import Relation.Unary using (Decidable)
open import Relation.Binary.PropositionalEquality using (_≡_; refl)
open import Data.Product using (_×_; _,_; _,′_; proj₁; proj₂)
open import Data.List using (List; []; _∷_; [_]; reverse; map; foldr; filter)
open import Data.Char as Char using (Char)
open import Data.String as Str using (String) renaming (_++_ to _++s_)
open import Data.Nat using (ℕ; suc; _+_; _∸_; _*_; _≤_; _≤?_)
open import Data.Nat.Show using (show)
open import Data.Nat.GCD using (gcd)
open import Data.Nat.DivMod using (_div_; _%_)
-- Begin-Prelude -------------------------------------------------------------
concatStr : List String → String
concatStr = foldr Str._++_ ""
filterStr : ∀ {p} {P : Char → Set p} → Decidable P → String → String
filterStr P? = Str.fromList ∘ filter P? ∘ Str.toList
-- stringToNat; decimalDigit?
showNats : List ℕ → String
showNats = concatStr ∘ map (\n → "," ++s (show n))
isDecimapDigit : Char → Set
isDecimapDigit c = 48 ≤ n × n ≤ 57 where
n = Char.toNat c
decimalDigit? : Decidable isDecimapDigit
decimalDigit? c =
let n = Char.toNat c
in
case ((48 ≤? n) ,′ (n ≤? 57)) of \
{ (yes le , yes le') → yes (le , le')
; (yes _ , no n≰57) → no (n≰57 ∘ proj₂)
; (no 48≰n , _ ) → no (48≰n ∘ proj₁)
}
charToDecDigitNat : Char → ℕ
charToDecDigitNat c = (Char.toNat c) ∸ 48
readNat : List Char → ℕ -- convert from decimal digit chars
readNat = toN ∘ map charToDecDigitNat ∘ reverse
where
toN : List ℕ → ℕ
toN [] = 0
toN (j ∷ js) = j + (10 * (toN js))
stringToNat : String → ℕ
stringToNat = readNat ∘ Str.toList
-- End-Prelude ----------------------------------------------------------------
factorials : ℕ → List ℕ -- [factorial i | i <- [n .. 0]]
factorials 0 = [ 1 ]
factorials (suc n) = case factorials n of \
{ (p ∷ fs) → ((suc n) * p) ∷ p ∷ fs
; [] → [] -- unaccessible
}
-- test : factorials 4 ≡ 24 ∷ 6 ∷ 2 ∷ 1 ∷ 1 ∷ []
-- test = refl
gcd! : Op₂ ℕ
gcd! n = proj₁ ∘ gcd n
gcds : ℕ → List ℕ
gcds n =
map (gcd! n) (factorials n)
test : ℕ → String
test n =
concatStr
("n = " ∷ show n
∷ "\nrems =\n " ∷ showNats rems
∷ "\nqRems =\n " ∷ showNats qRems
-- ∷ "\nfactorials n =\n " ∷ showNats fs
-- ∷ "\ngcds n =\n " ∷ showNats gs
∷ []
)
where
fs = factorials n
rems = map (\x → x % (suc n)) fs
quots = map (\x → x div (suc n)) fs
qRems = map (\x → x % (suc n)) quots
gs = gcds n
main = (readFiniteFile "data.txt") >>= putStrLn ∘ toCostring ∘ g
where
g : String -> String
g str = test n
where
n = stringToNat (filterStr decimalDigit? str)
-- Put to data.txt n =
-- 9, 10, 11, 12, ...
--
-- and run > time ./NatGCDTest
More information about the Agda
mailing list