Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions Cubical/Data/Int/GCD.agda
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
module Cubical.Data.Int.GCD where

open import Cubical.Data.Empty as ⊥
import Cubical.Data.Nat.GCD as ℕ
open import Cubical.Data.Nat.Divisibility renaming (_∣_ to _∣ℕ_)
open import Cubical.Foundations.Prelude
open import Cubical.Data.Int.Divisibility
open import Cubical.Data.Int

------------------------------------------------------------------------
-- Definition
------------------------------------------------------------------------

gcd : ℤ → ℤ → ℤ
gcd i j = pos (ℕ.gcd (abs i) (abs j))

gcd-def : ∀ i j → gcd i j ≡ pos (ℕ.gcd (abs i) (abs j))
gcd-def i j = refl

gcdSym : ∀ i j → gcd i j ≡ gcd j i
gcdSym i j = cong pos (ℕ.gcdSym (abs i) (abs j))

------------------------------------------------------------------------
-- ℕ and ℤ conversions
------------------------------------------------------------------------

gcd∣ℕ→∣ℤ-lemma : ∀ {i j k : ℤ} → ℕ.gcd (abs i) (abs j) ∣ℕ (abs k) → gcd i j ∣ k
gcd∣ℕ→∣ℤ-lemma {i}{j}{k} gcdij|k = ∣ℕ→∣ gcdij|k

ℕ∣gcd→ℤ∣-lemma : ∀ {i j k : ℤ} → (abs k) ∣ℕ ℕ.gcd (abs i) (abs j) → k ∣ gcd i j
ℕ∣gcd→ℤ∣-lemma {i}{j}{k} k|gcdij = ∣ℕ→∣ k|gcdij

gcd∣ℤ→∣ℕ-lemma : ∀ {i j k : ℤ} → gcd i j ∣ k → ℕ.gcd (abs i) (abs j) ∣ℕ (abs k)
gcd∣ℤ→∣ℕ-lemma {i}{j}{k} gcdij|k = ∣→∣ℕ gcdij|k

ℤ∣gcd→ℕ∣-lemma : ∀ {i j k : ℤ} → k ∣ gcd i j → (abs k) ∣ℕ ℕ.gcd (abs i) (abs j)
ℤ∣gcd→ℕ∣-lemma {i}{j}{k} k|gcdij = ∣→∣ℕ k|gcdij

------------------------------------------------------------------------
-- Properties
------------------------------------------------------------------------

gcd[i,j]∣i : ∀ i j → gcd i j ∣ i
gcd[i,j]∣i i j = gcd∣ℕ→∣ℤ-lemma {i}{j} (ℕ.gcd[m,n]∣m (abs i) (abs j))

gcd[i,j]∣j : ∀ i j → gcd i j ∣ j
gcd[i,j]∣j i j = gcd∣ℕ→∣ℤ-lemma {i}{j} (ℕ.gcd[m,n]∣n (abs i) (abs j))

gcd-greatest : ∀ {i j c} → c ∣ i → c ∣ j → c ∣ gcd i j
gcd-greatest {i}{j}{c} ci cj =
ℕ∣gcd→ℤ∣-lemma {i}{j}{c} (ℕ.gcd-greatest (∣→∣ℕ ci) (∣→∣ℕ cj))

gcd[0,0]≡0 : gcd 0 0 ≡ 0
gcd[0,0]≡0 = cong pos ℕ.gcd[0,0]≡0

gcd[i,j]≡0⇒i≡0 : ∀ {i j} → gcd i j ≡ 0 → i ≡ 0
gcd[i,j]≡0⇒i≡0 {i} {j} eqn = abs≡0 i (ℕ.gcd[m,n]≡0⇒m≡0 {abs i}{abs j} (injPos eqn))

gcd[i,j]≡0⇒j≡0 : ∀ {i j} → gcd i j ≡ 0 → j ≡ 0
gcd[i,j]≡0⇒j≡0 {i}{j} eqn = gcd[i,j]≡0⇒i≡0 {j}{i} (gcdSym j i ∙ eqn)
4 changes: 4 additions & 0 deletions Cubical/Data/Nat/Base.agda
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ isZero : ℕ → Bool
isZero zero = true
isZero (suc n) = false

NonZero : ℕ → Type
NonZero zero = ⊥
NonZero (ℕ.suc n) = Unit

-- exponential

_^_ : ℕ → ℕ → ℕ
Expand Down
6 changes: 6 additions & 0 deletions Cubical/Data/Nat/Coprime.agda
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ open import Cubical.Data.Nat.GCD
areCoprime : ℕ × ℕ → Type₀
areCoprime (m , n) = isGCD m n 1

zeroCoprime : {d-1 : ℕ} → (copr : areCoprime (0 , ℕ.suc d-1)) → d-1 ≡ 0
zeroCoprime {d-1} copr = injSuc (zeroGCD-unique (symGCD copr))

isPropAreCoprime : (x : ℕ) (y : ℕ) → isProp (areCoprime (x , y))
isPropAreCoprime x y = isPropIsGCD

-- Any pair (m , n) can be converted to a coprime pair (m' , n') s.t.
-- m' ∣ m, n' ∣ n if and only if one of m or n is nonzero

Expand Down
69 changes: 62 additions & 7 deletions Cubical/Data/Nat/GCD.agda
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ module Cubical.Data.Nat.GCD where
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism

open import Cubical.Induction.WellFounded

open import Cubical.Data.Sigma as Σ
open import Cubical.Data.Fin as F hiding (_%_ ; _/_)
open import Cubical.Data.NatPlusOne
open import Cubical.Data.Sum
open import Cubical.Data.Fin as F hiding (_%_ ; _/_)

open import Cubical.HITs.PropositionalTruncation as PropTrunc

open import Cubical.Data.Empty as ⊥
open import Cubical.Data.Nat.Base
open import Cubical.Data.Nat.Properties
open import Cubical.Data.Nat.Order
Expand All @@ -27,7 +27,7 @@ open import Cubical.Relation.Nullary

private
variable
m n d : ℕ
m n c d : ℕ

-- common divisors

Expand Down Expand Up @@ -55,7 +55,6 @@ isPropGCD : isProp (GCD m n)
isPropGCD (d , dCD , gr) (d' , d'CD , gr') =
Σ≡Prop (λ _ → isPropIsGCD) (antisym∣ (gr' d dCD) (gr d' d'CD))


symGCD : isGCD m n d → isGCD n m d
symGCD (dCD , gr) = symCD dCD , λ { d' d'CD → gr d' (symCD d'CD) }

Expand All @@ -65,11 +64,21 @@ divsGCD p = (∣-refl refl , p) , λ { d (d∣m , _) → d∣m }
oneGCD : ∀ m → isGCD m 1 1
oneGCD m = symGCD (divsGCD (∣-oneˡ m))


-- The base case of the Euclidean algorithm
zeroGCD : ∀ m → isGCD m 0 m
zeroGCD m = divsGCD (∣-zeroʳ m)

-- zeroGCD-unique lemma: if d is gcd of m and 0, then d ≡ m
zeroGCD-unique : {m d : ℕ} → isGCD m 0 d → m ≡ d
zeroGCD-unique {m} {d} (iscd , greatest) =
let
d∣m = fst iscd
m∣m = ∣-refl (refl {x = m})
m∣0 = ∣-zeroʳ m
m∣d = greatest m (m∣m , m∣0)
in
antisym∣ m∣d d∣m

-- a pair of useful lemmas about ∣ and (efficient) %
private
∣→∣% : ∀ {m n d} → d ∣ m → d ∣ n → d ∣ (m % n)
Expand Down Expand Up @@ -159,7 +168,6 @@ euclid m n@(suc n-1) =
in
r .fst , stepGCD (r .snd)


isContrGCD : ∀ m n → isContr (GCD m n)
isContrGCD m n = euclid m n , isPropGCD _

Expand All @@ -177,6 +185,16 @@ isGCD→gcd≡ dGCD = cong fst (isContrGCD _ _ .snd (_ , dGCD))
gcd≡→isGCD : gcd m n ≡ d → isGCD m n d
gcd≡→isGCD p = subst (isGCD _ _) p (gcdIsGCD _ _)

gcd≡isGCD : ∀ m n d → (gcd m n ≡ d) ≡ (isGCD m n d)
gcd≡isGCD m n d = isoToPath (iso gcd≡→isGCD isGCD→gcd≡
(λ b → isPropIsGCD (gcd≡→isGCD (isGCD→gcd≡ b)) b)
(λ a → isSetℕ (gcd m n) d (isGCD→gcd≡ {m}{n}{d} (gcd≡→isGCD a)) a))

uniqueGCD : ∀ {d'} → isGCD m n d → isGCD m n d' → d ≡ d'
uniqueGCD isgd isgd' = sym (isGCD→gcd≡ isgd) ∙ isGCD→gcd≡ isgd'

gcdSym : (m n : ℕ) → (gcd m n) ≡ (gcd n m)
gcdSym m n = uniqueGCD (gcdIsGCD m n) (symGCD (gcdIsGCD n m))

-- multiplicative properties of the gcd

Expand Down Expand Up @@ -214,6 +232,43 @@ isGCD-multʳ {m} {n} {d} k dGCD = gcd≡→isGCD (gcd-factorʳ m n k ∙ cong (_
where r : gcd m n ≡ d
r = isGCD→gcd≡ dGCD

-- Core properties of gcd

gcd[m,n]∣m : (m n : ℕ) → (gcd m n) ∣ m
gcd[m,n]∣m m n = fst (fst (gcdIsGCD m n))

gcd[m,n]∣n : (m n : ℕ) → (gcd m n) ∣ n
gcd[m,n]∣n m n = transport (cong (λ a → a ∣ n) (gcdSym n m) ) (gcd[m,n]∣m n m)

gcd-greatest : c ∣ m → c ∣ n → c ∣ gcd m n
gcd-greatest c∣m c∣n =
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason why it is proved in this way, instead of currying snd (gcdIsGCD m n) c?
I tested it, and you could even write gcd-greatest = curry (snd (gcdIsGCD _ _) _), with all implicit varibles solved

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

rec2 isPropPropTrunc (λ x y → ∣ gcd-greatestHlp y x ∣₁) c∣n c∣m
where
gcd-greatestHlp : ∀ {m}{n}{c} → (x : Σ ℕ (λ c₁ → c₁ · c ≡ m)) →
(y : Σ ℕ (λ c₁ → c₁ · c ≡ n)) → Σ ℕ (λ c₁ → c₁ · c ≡ gcd m n)
gcd-greatestHlp {m} {n} {c} (m' , m'c≡m) (n' , n'c≡n) =
(gcd m' n') , sym (gcd-factorʳ m' n' c) ∙ cong₂ (λ a b → gcd a b) m'c≡m n'c≡n

-- Other properties

gcd[0,0]≡0 : gcd 0 0 ≡ 0
gcd[0,0]≡0 = antisym∣ (∣-zeroʳ (gcd 0 0) ) (gcd-greatest (∣-zeroʳ 0) (∣-zeroʳ 0))

gcd[m,n]≢0 : ∀ (m n : ℕ) → (¬ (m ≡ 0)) ⊎ (¬ (n ≡ 0)) → ¬ (gcd m n ≡ 0)
gcd[m,n]≢0 m n (inl m≢0) gcd0 =
m≢0 (sym (∣-zeroˡ (transport (cong (λ a → a ∣ m) gcd0) (gcd[m,n]∣m m n))))
gcd[m,n]≢0 m n (inr n≢0) gcd0 =
n≢0 (sym (∣-zeroˡ (transport ((cong (λ a → a ∣ n) gcd0)) (gcd[m,n]∣n m n))))

gcd[m,n]≡0⇒m≡0 : gcd m n ≡ 0 → m ≡ 0
gcd[m,n]≡0⇒m≡0 {zero} {n} gmn = refl
gcd[m,n]≡0⇒m≡0 {suc m} {n} gmn =
⊥.elim {A = λ bot → suc m ≡ 0} (gcd[m,n]≢0 (suc m) n (inl snotz) gmn)

gcd[m,n]≡0⇒n≡0 : ∀ {m n} → gcd m n ≡ 0 → n ≡ 0
gcd[m,n]≡0⇒n≡0 {m}{n} gmn = gcd[m,n]≡0⇒m≡0 {n}{m}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transporting on cong (λ a → a ≡ 0) is the same as composing with the reverse path. In my opinion, it would be better to write gcd[m,n]≡0⇒m≡0 {n} {m} (gcdSym n m ∙ gmn), avoiding unnecessary transports.
More generally, I see you are using multiple times the pattern transport + cong, where you could have used subst instead.
However, I'm not a maintainer; I just noticed a couple of places where the code could be made shorter. Feel free to ignore these comments if not relevant

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I'll look at this shortly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

(transport (cong (λ a → a ≡ 0) (gcdSym m n)) gmn)

-- Inequality for strict divisibility

stDivIneqGCD : ¬ m ≡ 0 → ¬ m ∣ n → (g : GCD m n) → g .fst < m
Expand Down
Loading