module Data.Rational.Base where
Rational numbers🔗
The ring of rational numbers, is the localisation of the ring of integers inverting every positive element. We’ve already done most of the work while implementing localisations:
: Int → Ω
PositiveΩ .∣_∣ = Positive x
PositiveΩ x .is-tr = hlevel 1
PositiveΩ x
private
module L = Loc ℤ-comm PositiveΩ record { has-1 = pos 0 ; has-* = *ℤ-positive }
module ℤ = CRing ℤ-comm hiding (has-is-set ; magma-hlevel)
open Algebra.Ring.Localisation using (module Fraction) public
: Type
Fraction = Algebra.Ring.Localisation.Fraction Positive
Fraction
open Frac ℤ-comm using (Inductive-≈)
open Explicit ℤ-comm
open Fraction renaming (num to ↑ ; denom to ↓) public
open L using (_≈_) renaming (module Fr to ≈) public
open Algebra.Ring.Localisation using (_/_[_]) public
Strictly speaking, we are done: we could simply define to be the ring we just constructed. However, for the sake of implementation hiding, we wrap it as a distinct type constructor. This lets consumers of the type forget that it’s implemented as a localisation.
data ℚ : Type where
: ⌞ L.S⁻¹R ⌟ → ℚ
inc
: Fraction → ℚ
toℚ = inc (inc x)
toℚ x
_+ℚ_ : ℚ → ℚ → ℚ
_+ℚ_ (inc x) (inc y) = inc (x L.+ₗ y)
_*ℚ_ : ℚ → ℚ → ℚ
_*ℚ_ (inc x) (inc y) = inc (x L.*ₗ y)
_ : ℚ → ℚ
-ℚ_ (inc x) = inc (L.-ₗ x) -ℚ
-- Note on the definitions of ℚ/the operations above: we want all the
-- algebraic operations over ℚ to be definitionally injective. This
-- means that every clause has to have a proper match, and return a
-- distinct head symbol.
--
-- The requirement of having a proper match means we can't use a record
-- (even a no-eta pattern record), since Agda doesn't count those as
-- proper. Therefore, we have to use a data type.
private
: ℚ → ⌞ L.S⁻¹R ⌟
unℚ (inc x) = x unℚ
However, clients of this module will need the fact that is a quotient of the type of integer fractions. Therefore, we expose an elimination principle, saying that to show a proposition everywhere over it suffices to do so at the fractions.
ℚ-elim-prop: ∀ {ℓ} {P : ℚ → Type ℓ} (pprop : ∀ x → is-prop (P x))
→ (f : ∀ x → P (toℚ x))
→ ∀ x → P x
(inc (inc x)) = f x
ℚ-elim-prop pprop f (inc (glue r@(x , y , _) i)) = is-prop→pathp (λ i → pprop (inc (glue r i))) (f x) (f y) i
ℚ-elim-prop pprop f (inc (squash x y p q i j)) =
ℚ-elim-prop pprop f
is-prop→squarep(λ i j → pprop (inc (squash x y p q i j)))
(λ i → go (inc x)) (λ i → go (inc (p i))) (λ i → go (inc (q i))) (λ i → go (inc y))
i jwhere go = ℚ-elim-prop pprop f
ℚ-rec: ∀ {ℓ} {P : Type ℓ} ⦃ _ : H-Level P 2 ⦄
→ (f : Fraction → P)
→ (∀ x y → x ≈ y → f x ≡ f y)
→ ℚ → P
(inc x) = Coeq-rec f (λ (x , y , r) → p x y r) x ℚ-rec f p
Next, we show that sameness of fractions implies identity in and the converse is true as well:
abstract
: ∀ {x y} → x ≈ y → toℚ x ≡ toℚ y
quotℚ = ap ℚ.inc (quot p)
quotℚ p
: ∀ {x y} → toℚ x ≡ toℚ y → x ≈ y
unquotℚ = ≈.effective (ap unℚ p) unquotℚ p
Finally, we want to show that the type of rational numbers is discrete. To do this, we have to show that sameness of integer fractions is decidable, i.e. that, given fractions and we can decide whether there exists a with This is not automatic since can range over all integers! However, recall that this is equivalent to Since we know that has no zerodivisors, if this is true, then either or by assumption, it can not be But if then Conversely, if then we can take Therefore, checking sameness of fractions boils down to checking whether they “cross-multiply” to the same thing.
: {x y : Fraction} → x ≈ y → x .↑ *ℤ y .↓ ≡ y .↑ *ℤ x .↓
from-same-rational {x / s [ s≠0 ]} {y / t [ t≠0 ]} p = case L.≈→≈' p of λ where
from-same-rational @(possuc u') (pos u') p → case *ℤ-is-zero u _ p of λ where
u(inl u=0) → absurd (suc≠zero (pos-injective u=0))
(inr xt-ys=0) → ℤ.zero-diff xt-ys=0
: {x y : Fraction} → x .↑ *ℤ y .↓ ≡ y .↑ *ℤ x .↓ → x ≈ y
to-same-rational {x / s [ s≠0 ]} {y / t [ t≠0 ]} p = L.inc 1 (pos 0) (recover (sym (*ℤ-associative 1 x t) ·· ap (1 *ℤ_) p ·· *ℤ-associative 1 y s))
to-same-rational
: (x y : Fraction) → Dec (x ≈ y)
Dec-same-rational @(x / s [ _ ]) f'@(y / t [ _ ]) with x *ℤ t ≡? y *ℤ s
Dec-same-rational f... | yes p = yes (to-same-rational p)
... | no xt≠ys = no λ p → xt≠ys (from-same-rational p)
private
_≡ℚ?_ : (x y : ⌞ L.S⁻¹R ⌟) → Dec (x ≡ y)
= Discrete-quotient L.Fraction-congruence Dec-same-rational {x} {y}
x ≡ℚ? y
instance
: Discrete ℚ
Discrete-ℚ {inc x} {inc y} with x ≡ℚ? y
Discrete-ℚ ... | yes p = yes (ap ℚ.inc p)
... | no ¬p = no (¬p ∘ ap unℚ)
There are a number of other properties of that we can export as properties of however, these are all trivial as above, so we do not remark on them any further.
_-ℚ_ : ℚ → ℚ → ℚ
(inc x) -ℚ (inc y) = inc (x L.+ₗ L.-ₗ y)
infixl 8 _+ℚ_ _-ℚ_
infixl 9 _*ℚ_
infix 10 -ℚ_
_/_ : (x y : Int) ⦃ _ : Positive y ⦄ → ℚ
_/_ x y ⦃ p ⦄ = toℚ (x / y [ p ])
infix 11 _/_
{-# DISPLAY ℚ.inc (Coeq.inc (_/_[_] x y _)) = x / y #-}
{-# DISPLAY _/_ x (Int.pos 1) = x #-}
_/1 : Int → ℚ
= x / 1
x /1
instance
: ∀ {n} → H-Level ℚ (2 + n)
H-Level-ℚ = basic-instance 2 (Discrete→is-set auto)
H-Level-ℚ
: Number ℚ
Number-ℚ .Number.Constraint _ = ⊤
Number-ℚ .Number.fromNat x = pos x /1
Number-ℚ
: Negative ℚ
Negative-ℚ .Negative.Constraint _ = ⊤
Negative-ℚ .Negative.fromNeg 0 = 0
Negative-ℚ .Negative.fromNeg (suc x) = negsuc x /1
Negative-ℚ
Inductive-ℚ: ∀ {ℓ ℓm} {P : ℚ → Type ℓ}
→ ⦃ _ : Inductive ((x : Fraction) → P (toℚ x)) ℓm ⦄
→ ⦃ _ : ∀ {x} → H-Level (P x) 1 ⦄
→ Inductive (∀ x → P x) ℓm
.Inductive.methods = r .Inductive.methods
Inductive-ℚ ⦃ r ⦄ .Inductive.from f = ℚ-elim-prop (λ x → hlevel 1) (r .Inductive.from f)
Inductive-ℚ ⦃ r ⦄
abstract
: ∀ x → 0 +ℚ x ≡ x
+ℚ-idl (inc x) = ap inc (L.+ₗ-idl x)
+ℚ-idl
: ∀ x → x +ℚ 0 ≡ x
+ℚ-idr (inc x) = ap ℚ.inc (CRing.+-idr L.S⁻¹R)
+ℚ-idr
: ∀ x → (-ℚ x) +ℚ x ≡ 0
+ℚ-invl (inc x) = ap ℚ.inc (CRing.+-invl L.S⁻¹R {x})
+ℚ-invl
: ∀ x → x +ℚ (-ℚ x) ≡ 0
+ℚ-invr (inc x) = ap ℚ.inc (L.+ₗ-invr x)
+ℚ-invr
: ∀ x y z → x +ℚ (y +ℚ z) ≡ (x +ℚ y) +ℚ z
+ℚ-associative (inc x) (inc y) (inc z) = ap inc (L.+ₗ-assoc x y z)
+ℚ-associative
: ∀ x y → x +ℚ y ≡ y +ℚ x
+ℚ-commutative (inc x) (inc y) = ap inc (L.+ₗ-comm x y)
+ℚ-commutative
: ∀ x → 1 *ℚ x ≡ x
*ℚ-idl (inc x) = ap inc (L.*ₗ-idl x)
*ℚ-idl
: ∀ x → x *ℚ 1 ≡ x
*ℚ-idr (inc x) = ap ℚ.inc (CRing.*-idr L.S⁻¹R)
*ℚ-idr
: ∀ x y z → x *ℚ (y *ℚ z) ≡ (x *ℚ y) *ℚ z
*ℚ-associative (inc x) (inc y) (inc z) = ap inc (L.*ₗ-assoc x y z)
*ℚ-associative
: ∀ x y → x *ℚ y ≡ y *ℚ x
*ℚ-commutative (inc x) (inc y) = ap inc (L.*ₗ-comm x y)
*ℚ-commutative
: ∀ x → 0 *ℚ x ≡ 0
*ℚ-zerol (inc x) = ap ℚ.inc (CRing.*-zerol L.S⁻¹R {x})
*ℚ-zerol
: ∀ x → x *ℚ 0 ≡ 0
*ℚ-zeror (inc x) = ap ℚ.inc (CRing.*-zeror L.S⁻¹R {x})
*ℚ-zeror
: ∀ x y z → x *ℚ (y +ℚ z) ≡ x *ℚ y +ℚ x *ℚ z
*ℚ-distribl (inc x) (inc y) (inc z) = ap ℚ.inc (L.*ₗ-distribl x y z)
*ℚ-distribl
: ∀ x y z → (y +ℚ z) *ℚ x ≡ y *ℚ x +ℚ z *ℚ x
*ℚ-distribr = *ℚ-commutative (y +ℚ z) x ∙ *ℚ-distribl x y z ∙ ap₂ _+ℚ_ (*ℚ-commutative x y) (*ℚ-commutative x z)
*ℚ-distribr x y z
: is-monoid 0 _+ℚ_
+ℚ-monoid = record { has-is-semigroup = record { has-is-magma = record { has-is-set = hlevel 2 } ; associative = λ {x} {y} {z} → +ℚ-associative x y z } ; idl = +ℚ-idl _ ; idr = +ℚ-idr _ }
+ℚ-monoid
: is-monoid 1 _*ℚ_
*ℚ-monoid = record { has-is-semigroup = record { has-is-magma = record { has-is-set = hlevel 2 } ; associative = λ {x} {y} {z} → *ℚ-associative x y z } ; idl = *ℚ-idl _ ; idr = *ℚ-idr _ }
*ℚ-monoid
: CRing lzero
ℚ-ring .fst ∣ = ℚ
∣ ℚ-ring .fst .is-tr = hlevel 2
ℚ-ring .snd .CRing-on.has-ring-on = to-ring-on mk where
ℚ-ring open make-ring
: make-ring ℚ
mk .ring-is-set = hlevel 2
mk .0R = 0
mk .make-ring._+_ = _+ℚ_
mk = -ℚ_
- mk .+-idl = +ℚ-idl
mk .+-invr = +ℚ-invr
mk .+-assoc = +ℚ-associative
mk .+-comm = +ℚ-commutative
mk .1R = 1
mk .make-ring._*_ = _*ℚ_
mk .*-idl = *ℚ-idl
mk .*-idr = *ℚ-idr
mk .*-assoc = *ℚ-associative
mk .*-distribl = *ℚ-distribl
mk .*-distribr = *ℚ-distribr
mk .snd .CRing-on.*-commutes = *ℚ-commutative _ _ ℚ-ring
As a field🔗
An important property of the ring is that any nonzero rational number is invertible. Since inverses are unique when they exist — the type of inverses is a proposition — it suffices to show this at the more concrete level of integer fractions.
: (x : ℚ) → x ≠ 0 → Σ[ y ∈ ℚ ] (x *ℚ y ≡ 1)
inverseℚ = ℚ-elim-prop is-p go where
inverseℚ abstract
: (x : ℚ) → is-prop (x ≠ 0 → Σ[ y ∈ ℚ ] (x *ℚ y ≡ 1))
is-p = Π-is-hlevel 1 λ _ (y , p) (z , q) → Σ-prop-path! (monoid-inverse-unique *ℚ-monoid x y z (*ℚ-commutative y x ∙ p) q)
is-p x
: ∀ x y → 1 *ℤ (x *ℤ y) *ℤ 1 ≡ 1 *ℤ (y *ℤ x)
rem₁ = ap (_*ℤ 1) (*ℤ-onel (x *ℤ y))
rem₁ x y (x *ℤ y) ∙ *ℤ-commutative x y ∙ sym (*ℤ-onel (y *ℤ x)) ∙ *ℤ-oner
This leaves us with three cases: either the fraction had a denominator of zero, contradicting our assumption, or its numerator is also nonzero (either positive or negative), so that we can form the fraction It’s then routine to show that holds in
: (x : Fraction) → toℚ x ≠ 0 → Σ[ y ∈ ℚ ] (toℚ x *ℚ y ≡ 1)
go (posz / y [ _ ]) nz = absurd (nz (quotℚ (L.inc 1 decide! refl)))
go (x@(possuc x') / y [ _ ]) nz = y / x , quotℚ (L.inc 1 decide! (rem₁ x y))
go (x@(negsuc x') / y [ p ]) nz with y | p
go ... | possuc y | _ = negsuc y / possuc x' , quotℚ (L.inc 1 decide! (rem₁ (negsuc x') (negsuc y)))
... | negsuc y | _ = possuc y / possuc x' , quotℚ (L.inc 1 decide! (rem₁ x (possuc y)))
Reduced fractions🔗
We now show that the quotient defining is split: integer fractions have a well-defined notion of normal form, and similarity of fractions is equivalent to equality under normalisation. The procedure we formalise is the familiar one, sending a fraction to
What remains is proving that this is actually a normalisation procedure, which takes up the remainder of this module.
: Fraction → Fraction
reduce-fraction (x / y [ p ]) = reduced module reduce where
reduce-fraction : GCD (abs x) (abs y)
gcd[x,y] = Euclid.euclid (abs x) (abs y) gcd[x,y]
open Σ gcd[x,y] renaming (fst to g) using () public
open is-gcd (gcd[x,y] .snd)
open Σ (∣→fibre gcd-∣l) renaming (fst to x/g ; snd to x/g*g=x) public
open Σ (∣→fibre gcd-∣r) renaming (fst to y/g ; snd to y/g*g=y) public
Our first obligation, to form the reduced fraction at all, is showing that the denominator is non-zero. This is pretty easy: we know that is nonzero, so if were zero, we would have which is a contradiction. A similar argument shows that is itself nonzero, a fact we’ll need later.
: y/g ≠ 0
rem₁ with y/g | y/g=0 | y/g*g=y
rem₁ y/g=0 ... | zero | y/g=0 | q = positive→nonzero p (abs-positive y (sym q))
... | suc n | y/g=0 | q = absurd (suc≠zero y/g=0)
: g ≠ 0
rem₂ = positive→nonzero p (abs-positive y (sym (sym (*-zeror y/g) ∙ ap (y/g *_) (sym g=0) ∙ y/g*g=y))) rem₂ g=0
Finally, we must quickly mention the issue of signs. Since gcd
produces a natural number, we have to
multiply it by the sign
of
to make sure signs are preserved. Note that the denominator must be
positive.
: Fraction
reduced = assign (sign x) x/g / pos y/g [ pos-positive rem₁ ] reduced
Finally, we can calculate that these fractions are similar.
: (x / y [ p ]) ≈ reduced
related = L.inc (pos g) (pos-positive rem₂) $
related 3 (λ x y z → x :* y :* z ≔ (z :* x) :* y) (pos g) x (pos y/g) refl ⟩
pos g *ℤ x *ℤ pos y/g ≡⟨ solve (pos y/g *ℤ pos g) *ℤ x ≡⟨ ap (_*ℤ x) (ap (assign pos) y/g*g=y) ⟩
(abs y) *ℤ x ≡⟨ ap₂ _*ℤ_ (assign-pos-positive y p) (sym (divides-*ℤ {n = x/g} {g} {x} (*-commutative g x/g ∙ x/g*g=x))) ⟩
assign pos (pos g *ℤ assign (sign x) x/g) ≡⟨ solve 3 (λ y g x → y :* (g :* x) ≔ g :* x :* y) y (pos g) (assign (sign x) x/g) refl ⟩
y *ℤ .* assign (sign x) x/g ℤ.* y ∎ pos g ℤ
: is-gcd x/g y/g 1
coprime .is-gcd.gcd-∣l = ∣-one
coprime .is-gcd.gcd-∣r = ∣-one
coprime .is-gcd.greatest {g'} p q with (p , α) ← ∣→fibre p | (q , β) ← ∣→fibre q =
coprime {g} {g'} {1} ⦃ nonzero→positive rem₂ ⦄ (∣-trans (gcd[x,y] .snd .is-gcd.greatest p1 p2) (subst (g ∣_) (sym (+-zeror g)) ∣-refl))
∣-*-cancelr where
: g' * g ∣ abs x
p1 = fibre→∣ (p , sym (*-associative p g' g) ∙ ap (_* g) α ∙ x/g*g=x)
p1
: g' * g ∣ abs y
p2 = fibre→∣ (q , sym (*-associative q g' g) ∙ ap (_* g) β ∙ y/g*g=y) p2
We’ll now show that reduce-fraction
respects similarity of
fractions. We show this by an intermediate lemma, which says that, given
a non-zero
and a fraction
we have
as integer fractions (rather than rational numbers).
reduce-*r: ∀ x y s (p : Positive y) (q : Positive s)
→ reduce-fraction ((x *ℤ s) / (y *ℤ s) [ *ℤ-positive p q ])
(x / y [ p ])
≡ reduce-fraction = Fraction-path (ap₂ assign sgn num) (ap Int.pos denom) where reduce-*r x y s p q
module m = reduce x y p
module n = reduce (x *ℤ s) (y *ℤ s) (*ℤ-positive p q)
open n renaming (x/g to xs/gcd ; y/g to ys/gcd) using ()
open m renaming (x/g to x/gcd ; y/g to y/gcd) using ()
instance
_ : Data.Nat.Base.Positive (abs s)
_ = nonzero→positive (λ p → positive→nonzero q (abs-positive s p))
_ : Data.Nat.Base.Positive m.g
_ = nonzero→positive m.rem₂
: Int → Int → Nat
gcdℤ = gcd (abs x) (abs y) gcdℤ x y
The first observation is that even when absolute values are involved.1
: gcdℤ (x *ℤ s) (y *ℤ s) ≡ gcdℤ x y * abs s
p1 = ap₂ gcd (abs-*ℤ x s) (abs-*ℤ y s) ∙ gcd-factor (abs x) (abs y) (abs s) p1
This implies that and, because multiplication by is injective, this in turn implies that is also We conclude since both sides multiply with to and this multiplication is also injective. Therefore, our numerators have the same absolute value.
: xs/gcd * gcdℤ x y ≡ abs x
num' = *-injr (abs s) (xs/gcd * m.g) (abs x) $
num' .g (abs s) ⟩
xs/gcd * gcdℤ x y * abs s ≡⟨ *-associative xs/gcd m(gcdℤ x y * abs s) ≡˘⟨ ap (xs/gcd *_) p1 ⟩
xs/gcd * (x *ℤ s) (y *ℤ s) ≡⟨ n.x/g*g=x ⟩
xs/gcd * gcdℤ (x *ℤ s) ≡⟨ abs-*ℤ x s ⟩
abs
abs x * abs s ∎
: xs/gcd ≡ x/gcd
num = *-injr (gcdℤ x y) xs/gcd x/gcd (num' ∙ sym m.x/g*g=x) num
We must still show that the resulting numerators have the same sign. Fortunately, this boils down to a bit of algebra, plus the hyper-specific fact that whenever is nonzero.2
: sign (x *ℤ s) ≡ sign x
sgn = sign-*ℤ-positive x s q sgn
A symmetric calculation shows that the denominators also have the same absolute value, and, since they’re both positive in the resulting fraction, we’re done.
: ys/gcd * gcdℤ x y ≡ abs y
denom' = *-injr (abs s) (ys/gcd * m.g) (abs y) (*-associative ys/gcd m.g (abs s) ∙ sym (ap (ys/gcd *_) p1) ∙ n.y/g*g=y ∙ abs-*ℤ y s)
denom'
: ys/gcd ≡ y/gcd
denom = *-injr (gcdℤ x y) ys/gcd y/gcd (denom' ∙ sym m.y/g*g=y) denom
We can use this to show that reduce-fraction
sends similar fractions
to equal normal forms: If
we have
and we can calculate
using the previous lemma. Thus, by the general logic of split quotients, we conclude that
is equivalent to the set of reduced integer fractions.
: (x y : Fraction) → x ≈ y → reduce-fraction x ≡ reduce-fraction y
reduce-resp @(x / s [ s≠0 ]) f'@(y / t [ t≠0 ]) p =
reduce-resp f(x / s [ s≠0 ]) ≡⟨ sym (reduce-*r x s t s≠0 t≠0) ⟩
reduce-fraction ((x *ℤ t) / (s *ℤ t) [ _ ]) ≡⟨ ap reduce-fraction (Fraction-path {x = _ / _ [ *ℤ-positive s≠0 t≠0 ]} {_ / _ [ *ℤ-positive t≠0 s≠0 ]} (from-same-rational p) (*ℤ-commutative s t)) ⟩
reduce-fraction ((y *ℤ s) / (t *ℤ s) [ _ ]) ≡⟨ reduce-*r y t s t≠0 s≠0 ⟩
reduce-fraction (y / t [ t≠0 ]) ∎
reduce-fraction
: is-split-congruence L.Fraction-congruence
integer-frac-splits = record
integer-frac-splits { has-is-set = hlevel 2
; normalise = reduce-fraction
; represents = elim! reduce.related
; respects = reduce-resp
}
private module split = is-split-congruence integer-frac-splits
: ℚ → Fraction
reduceℚ (inc x) = split.choose x
reduceℚ
: (x : ℚ) → fibre toℚ x
splitℚ (inc x) = record
splitℚ { fst = split.choose x
-- The use of 'recover' here replaces the calculated proof that
-- is-split-congruence returns by an invocation of Discrete-ℚ. This
-- has much shorter normal forms when applied to concrete values.
; snd = recover (ap inc (split.splitting x .snd))
}
abstract
: ∀ x y → reduceℚ x ≡ reduceℚ y → x ≡ y
reduce-injective = elim! (λ x s s≠0 y t t≠0 p → quotℚ (split.reflects _ _ p))
reduce-injective
common-denominator: ∀ n (fs : Fin n → Fraction) → Σ[ c ∈ Int ] Σ[ p ∈ Positive c ] Σ[ n ∈ (Fin n → Int) ] (∀ j → fs j ≈ (n j / c [ p ]))
0 _ = 1 , pos 0 , (λ ()) , (λ ())
common-denominator (suc sz) fs with (c , c≠0 , nums , prfs) ← common-denominator sz (fs ∘ fsuc) | inspect (fs fzero)
common-denominator ... | n / d [ d≠0 ] , prf = c' , c'≠0 , nums' , prfs' where
= d *ℤ c
c' = *ℤ-positive d≠0 c≠0
c'≠0
: Fin (suc sz) → Int
nums' = n *ℤ c
nums' fzero (fsuc n) = nums n *ℤ d
nums'
abstract
: (n : Fin (suc sz)) → fs n ≈ (nums' n / c' [ c'≠0 ])
prfs' = ≈.reflᶜ' prf ≈.∙ᶜ L.inc c c≠0 (solve 3 (λ c n d → c :* n :* (d :* c) ≔ c :* (n :* c) :* d) c n d refl)
prfs' fzero (fsuc n) = prfs n ≈.∙ᶜ L.inc d d≠0 (solve 3 (λ c n d → d :* n :* (d :* c) ≔ d :* (n :* d) :* c) c (nums n) d refl)
prfs'
-- Induction principle for n-tuples of rational numbers which reduces to
-- the case of n fractions /with the same denominator/. The type is
-- pretty noisy since it uses the combinators for finite products, but
-- it specialises at concrete n to what you would expect, e.g.
--
-- ℚ-elim-propⁿ 2
-- : ∀ {P : ℚ → ℚ → Prop}
-- → (∀ d (p : Positive d) a b → P (a / d) (b / d))
-- → ∀ a b → P a b
--
-- This is useful primarily when dealing with the order, since
-- a / d ≤ b / d is just a ≤ b. Algebraic properties of the rationals
-- don't generally get any easier by assuming a common denominator.
ℚ-elim-propⁿ: ∀ (n : Nat) {ℓ} {P : Arrᶠ {n = n} (λ _ → ℚ) (Type ℓ)}
→ ⦃ _ : {as : Πᶠ (λ i → ℚ)} → H-Level (applyᶠ P as) 1 ⦄
→ ( (d : Int) ⦃ p : Positive d ⦄
→ ∀ᶠ n (λ i → Int) (λ as → applyᶠ P (mapₚ (λ i n → toℚ (n / d [ p ])) as)))
→ ∀ᶠ n (λ _ → ℚ) λ as → applyᶠ P as
{P = P} work = curry-∀ᶠ go where abstract
ℚ-elim-propⁿ n : ∀ n → (qs : Fin n → ℚ) → ∥ ((i : Fin n) → fibre toℚ (qs i)) ∥
reps = finite-choice n (λ i → ℚ-elim-prop {P = λ x → ∥ fibre toℚ x ∥} (λ _ → squash) (λ x → inc (x , refl)) (qs i))
reps n qs
: (as : Πᶠ (λ i → ℚ)) → applyᶠ P as
go = ∥-∥-out! do
go as _ (indexₚ as)
fracs' ← reps
let
: Fin n → Fraction
fracs = fracs' i .fst
fracs i
: (i : Fin n) → toℚ (fracs i) ≡ indexₚ as i
same = fracs' i .snd
same i
(d , d≠0 , nums , same') ← pure (common-denominator _ fracs)
let
: Πᶠ (λ i → ℚ)
rats = mapₚ (λ i n → toℚ (n / d [ d≠0 ])) (tabulateₚ nums)
rats
: applyᶠ P rats
p₀ = apply-∀ᶠ (work d ⦃ d≠0 ⦄) (tabulateₚ nums)
p₀
: rats ≡ as
rats=as = extₚ λ i →
rats=as (λ i n → toℚ (n / d [ d≠0 ])) (tabulateₚ nums) i
indexₚ-mapₚ (λ e → toℚ (e / d [ d≠0 ])) (indexₚ-tabulateₚ nums i)
·· ap (quotℚ (same' i)) ∙ same i
·· sym
(subst (applyᶠ P) rats=as p₀)
pure
: Fraction → Fraction → Prop lzero
same-frac @record{} g@record{} = el! (f .↑ *ℤ g .↓ ≡ g .↑ *ℤ f .↓)
same-frac f
private
: ℚ → ℚ → Prop lzero
eqℚ (inc x) (inc y) = Coeq-rec₂ (hlevel 2) same-frac
eqℚ (λ { f@(x / s [ p ]) (g@(y / t [ q ]) , h@(z / u [ r ]) , α) → n-ua (prop-ext!
(λ β → from-same-rational {h} {f} (≈.symᶜ α ≈.∙ᶜ to-same-rational β))
(λ β → from-same-rational {g} {f} (α ≈.∙ᶜ to-same-rational β))) })
(λ { f@(x / s [ p ]) (g@(y / t [ q ]) , h@(z / u [ r ]) , α) → n-ua (prop-ext!
(λ β → from-same-rational {f} {h} (to-same-rational β ≈.∙ᶜ α))
(λ β → from-same-rational {f} {g} (to-same-rational β ≈.∙ᶜ ≈.symᶜ α))) })
x y
open Extensional
instance
: Extensional ℚ lzero
Extensional-ℚ .Pathᵉ x y = ⌞ eqℚ x y ⌟
Extensional-ℚ .reflᵉ = ℚ-elim-prop (λ _ → hlevel 1) λ { record{} → refl }
Extensional-ℚ .idsᵉ .to-path {a} {b} = go a b where
Extensional-ℚ : ∀ a b → ⌞ eqℚ a b ⌟ → a ≡ b
go = ℚ-elim-propⁿ 2 (λ d a b p → quotℚ (to-same-rational p))
go .idsᵉ .to-path-over p = prop!
Extensional-ℚ
record Nonzero (x : ℚ) : Type where
constructor inc
-- It's important that Nonzero is a strict proposition, living in
-- Type, so that it doesn't matter what instance gets selected when we
-- use it in invℚ etc.
--
-- The alternative is to always use it as an irrelevant instance (or
-- to, god forbid, have it in Agda's Prop), but that doesn't play well
-- with the overlap pragmas; and we need those if we're gonna have
-- e.g. Nonzero (p * q) as an instance.
field
.lower : x ≠ 0
instance
: ∀ {x n} → H-Level (Nonzero x) (suc n)
H-Level-Nonzero = prop-instance λ _ _ → refl
H-Level-Nonzero
open Nonzero
abstract
: ∀ {x} ⦃ p : Nonzero x ⦄ → x ≠ 0
from-nonzero = absurd (α p)
from-nonzero ⦃ inc α ⦄ p
: ∀ {x y} {p : Positive y} → Nonzero (toℚ (x / y [ p ])) → x ≠ 0
from-nonzero-frac (inc α) p = absurd (α (quotℚ (to-same-rational (*ℤ-oner _ ∙ p))))
from-nonzero-frac
: ∀ {x y} {p : Positive y} → x ≠ 0 → Nonzero (toℚ (x / y [ p ]))
to-nonzero-frac = inc λ α → p (sym (*ℤ-oner _) ∙ from-same-rational (unquotℚ α))
to-nonzero-frac p
instance
: ∀ {x d} {p : Positive d} → Nonzero (toℚ (negsuc x / d [ p ]))
Nonzero-neg = inc (λ p → absurd (negsuc≠pos (from-same-rational (unquotℚ p))))
Nonzero-neg
: ∀ {x d} {p : Positive d} ⦃ _ : Positive x ⦄ → Nonzero (toℚ (x / d [ p ]))
Nonzero-pos {.(possuc x)} ⦃ pos x ⦄ = inc (λ p → absurd (suc≠zero (pos-injective (from-same-rational (unquotℚ p)))))
Nonzero-pos {-# OVERLAPPABLE Nonzero-pos #-}
-- Since we want invℚ to be injective as well, we re-wrap the result on
-- the RHS, to make sure the clause is headed by a constructor.
: (x : ℚ) ⦃ p : Nonzero x ⦄ → ℚ
invℚ (inc x) ⦃ inc α ⦄ = inc (unℚ (inverseℚ (inc x) (λ p → absurd (α p)) .fst))
invℚ
: {x : ℚ} {p : Nonzero x} → x *ℚ invℚ x ⦃ p ⦄ ≡ 1
*ℚ-invr {inc x} {inc α} with inverseℚ (inc x) (λ p → absurd (α p))
*ℚ-invr ... | (inc x , p) = p
: ∀ x y → x +ℚ (-ℚ y) ≡ x -ℚ y
-ℚ-def (inc x) (inc y) = refl
-ℚ-def
_/ℚ_ : (x y : ℚ) ⦃ p : Nonzero y ⦄ → ℚ
= inc x *ℚ invℚ (inc y)
inc x /ℚ inc y
abstract
: ∀ {x y d} ⦃ p : Positive d ⦄ → x / d ≡ y / d → x ≡ y
from-same-denom {x} {y} {d} p = *ℤ-injectiver d x y (positive→nonzero auto) (from-same-rational (unquotℚ p)) from-same-denom