module Data.Nat.Prime where

Prime and composite numbersπŸ”—

A number is prime when it is greater than two (which we split into being positive and being apart from 1), and, if is any other number that divides then either or Since we’ve assumed these cases are disjoint, so being a prime is a proposition.

record is-prime (n : Nat) : Type where
  field
    ⦃ positive ⦄ : Positive n
    prime≠1   : n ≠ 1
    primality : βˆ€ d β†’ d ∣ n β†’ (d ≑ 1) ⊎ (d ≑ n)

We note that a prime number has no proper divisors, in that, if and then does not divide

  Β¬proper-divisor : βˆ€ {d} β†’ d β‰  1 β†’ d β‰  n β†’ Β¬ d ∣ n
  ¬proper-divisor ¬d=1 ¬d=n d∣n with primality _ d∣n
  ... | inl d=1 = Β¬d=1 d=1
  ... | inr d=n = Β¬d=n d=n

Conversely, if is a number that is not prime, it is called composite. Instead of defining is-composite to be the literal negation of is-prime, we instead define this notion positively: To say that a number is composite is to give a prime and a number such that and is the smallest divisor of

record is-composite (n : Nat) : Type where
  field
    {p q} : Nat

    p-prime  : is-prime p
    q-proper : 1 < q

    factors : q * p ≑ n
    least : βˆ€ p' β†’ 1 < p' β†’ p' ∣ n β†’ p ≀ p'

Note that the assumption that is a prime is simply for convenience: the least proper divisor of any number is always a prime.

least-divisor→is-prime
  : βˆ€ p n β†’ 1 < p β†’ p ∣ n β†’ (βˆ€ p' β†’ 1 < p' β†’ p' ∣ n β†’ p ≀ p') β†’ is-prime p
least-divisorβ†’is-prime p n gt div least .positive = ≀-trans ≀-ascend gt
least-divisor→is-prime p n gt div least .prime≠1 q = <-irrefl (sym q) gt
least-divisor→is-prime p@(suc p') n gt div least .primality 0 x = absurd (suc≠zero x)
least-divisor→is-prime p@(suc p') n gt div least .primality 1 x = inl refl
least-divisor→is-prime p@(suc p') n gt div least .primality m@(suc (suc k)) x =
  inr (≀-antisym (m∣snβ†’m≀sn x) (least m (s≀s (s≀s 0≀x)) (∣-trans x div)))
open is-composite hiding (p ; q ; factors)

private unquoteDecl eqv' = declare-record-iso eqv' (quote is-composite)

abstract instance
  H-Level-is-composite : βˆ€ {n k} β†’ H-Level (is-composite n) (suc k)
  H-Level-is-composite = prop-instance Ξ» a@record{p = p ; q = q} b@record{p = p' ; q = q'} β†’
    let
      open is-composite using (factors)

      ap=bp : p ≑ p'
      ap=bp = ≀-antisym
        (a .least p' (prime>1 (b .p-prime)) (fibreβ†’βˆ£ (q' , b .factors)))
        (b .least p (prime>1 (a .p-prime)) (fibreβ†’βˆ£ (q , a .factors)))

      aq=bq : q ≑ q'
      aq=bq = *-injl p q q' (*-commutative p q βˆ™ a .factors βˆ™ sym (b .factors) βˆ™ ap (q' *_) (sym ap=bp) βˆ™ *-commutative q' p)
    in Equiv.injective (Iso→Equiv eqv') (Σ-pathp ap=bp (Σ-prop-pathp! aq=bq))

prime-divisor-lt : βˆ€ p q n .⦃ _ : Positive n ⦄ β†’ is-prime p β†’ q * p ≑ n β†’ q < n
prime-divisor-lt p q n pprime div with ≀-strengthen (m∣nβ†’m≀n {q} {n} (fibreβ†’βˆ£ (p , *-commutative p q βˆ™ div)))
... | inr less = less
... | inl same = absurd (pprime .prime≠1 $
  *-injr n p 1 (sym (+-zeror n βˆ™ sym div βˆ™ *-commutative q p βˆ™ ap (p *_) same)))

prime-remainder-positive : βˆ€ p q n .⦃ _ : Positive n ⦄ β†’ Β¬ is-prime n β†’ is-prime p β†’ q * p ≑ n β†’ 1 < q
prime-remainder-positive p 0 n@(suc _) _ _ div = absurd (zero≠suc div)
prime-remainder-positive p 1 n@(suc _) nn pp div = absurd (nn (subst is-prime (sym (+-zeror p) βˆ™ div) pp))
prime-remainder-positive p (suc (suc _)) (suc _) _ _ _ = s≀s (s≀s 0≀x)

distinct-primesβ†’coprime : βˆ€ {a b} β†’ is-prime a β†’ is-prime b β†’ Β¬ a ≑ b β†’ is-gcd a b 1
distinct-primes→coprime {a@(suc a')} {b@(suc b')} apr bpr a≠b = record
  { gcd-∣l = a , *-oner a
  ; gcd-∣r = b , *-oner b
  ; greatest = Ξ» {g'} w2 w β†’ case bpr .primality _ w of Ξ» where
    (inl p) β†’ subst (_∣ 1) (sym p) ∣-refl
    (inr q) β†’ case apr .primality _ w2 of Ξ» where
      (inl p) β†’ subst (_∣ 1) (sym p) ∣-refl
      (inr r) β†’ absurd (aβ‰ b (sym r βˆ™ q))
  }

Primality testingπŸ”—

is-prime-or-composite : βˆ€ n β†’ 1 < n β†’ is-prime n ⊎ is-composite n
is-prime-or-composite n@(suc (suc m)) (s≀s p)
  with Fin-omniscience {n = n} (Ξ» k β†’ 1 < to-nat k Γ— to-nat k ∣ n)
... | inr prime = inl record { primeβ‰ 1 = sucβ‰ zero ∘ suc-inj ; primality = no-divisorsβ†’prime } where
  no-divisorsβ†’prime : βˆ€ d β†’ d ∣ n β†’ d ≑ 1 ⊎ d ≑ n
  no-divisorsβ†’prime d div with d ≑? 1
  ... | yes p = inl p
  ... | no Β¬d=1 with d ≑? n
  ... | yes p = inr p
  ... | no Β¬d=n =
    let
      ord : d < n
      ord = proper-divisor-< Β¬d=n div

      coh : d ≑ to-nat (from-β„•< (d , ord))
      coh = sym (ap fst (to-from-β„•< (d , ord)))

      no = case d return (Ξ» x β†’ Β¬ x ≑ 1 β†’ x ∣ n β†’ 1 < x) of Ξ» where
        0 p1 div β†’ absurd (<-irrefl (sym div) (s≀s 0≀x))
        1 p1 div β†’ absurd (p1 refl)
        (suc (suc n)) p1 div β†’ s≀s (s≀s 0≀x)
    in absurd (prime (from-β„•< (d , ord)) ((subst (1 <_) coh (no Β¬d=1 div)) , subst (_∣ n) coh div))
... | inl (ix , (proper , div) , least)
  = inr record { p-prime = prime ; q-proper = proper' ; factors = path ; least = least' } where
  open Ξ£ (βˆ£β†’fibre div) renaming (fst to quot ; snd to path)

  abstract
    least' : (p' : Nat) β†’ 1 < p' β†’ p' ∣ n β†’ to-nat ix ≀ p'
    least' p' x div with ≀-strengthen (m∣nβ†’m≀n div)
    ... | inl same = ≀-trans ≀-ascend (subst (to-nat ix <_) (sym same) (to-β„•< ix .snd))
    ... | inr less =
      let
        it : β„•< n
        it = p' , less

        coh : p' ≑ to-nat (from-β„•< it)
        coh = sym (ap fst (to-from-β„•< it))

        orig = least (from-β„•< it) (subst (1 <_) coh x , subst (_∣ n) coh div)
      in ≀-trans orig (subst (to-nat (from-β„•< it) ≀_) (sym coh) ≀-refl)

  prime : is-prime (to-nat ix)
  prime = least-divisor→is-prime (to-nat ix) n proper div least'

  proper' : 1 < quot
  proper' with quot | path
  ... | 0 | q = absurd (<-irrefl q (s≀s 0≀x))
  ... | 1 | q = absurd (<-irrefl (sym (+-zeror (to-nat ix)) βˆ™ q) (to-β„•< ix .snd))
  ... | suc (suc n) | p = s≀s (s≀s 0≀x)

record Factorisation (n : Nat) : Type where
  constructor factored
  field
    primes    : List Nat
    factors   : product primes ≑ n
    is-primes : All is-prime primes

  prime-divides : βˆ€ {x} β†’ x βˆˆβ‚— primes β†’ x ∣ n
  prime-divides {x} h = subst (x ∣_) factors (work _ _ h) where
    work : βˆ€ x xs β†’ x βˆˆβ‚— xs β†’ x ∣ product xs
    work x (y ∷ xs) (here p) = subst (Ξ» e β†’ x ∣ e * product xs) p (∣-*l {x} {product xs})
    work x (y ∷ xs) (there p) =
      let
        it = work x xs p
        (div , p) = βˆ£β†’fibre it
      in fibreβ†’βˆ£ (y * div , *-associative y div x βˆ™ ap (y *_) p)

  find-prime-factor : βˆ€ {x} β†’ is-prime x β†’ x ∣ n β†’ x βˆˆβ‚— primes
  find-prime-factor {num} x d =
    work _ primes x (Ξ» _ β†’ all-∈ is-primes) (subst (num ∣_) (sym factors) d)
    where
    work : βˆ€ x xs β†’ is-prime x β†’ (βˆ€ x β†’ x βˆˆβ‚— xs β†’ is-prime x) β†’ x ∣ product xs β†’ x βˆˆβ‚— xs
    work x [] xp ps xd = absurd (primeβ‰ 1 xp (∣-1 xd))
    work x (y ∷ xs) xp ps xd with x ≑? y
    ... | yes x=y = here x=y
    ... | no Β¬p = there $ work x xs xp (Ξ» x w β†’ ps x (there w))
      (|-*-coprime-cancel x y (product xs)
        ⦃ auto ⦄ ⦃ ps y (here refl) .positive ⦄
        xd (distinct-primes→coprime xp (ps y (here refl)) ¬p))

open is-composite using (factors)
open Factorisation

factorisation-unique' : βˆ€ {n} (a b : Factorisation n) β†’ βˆ€ p β†’ p βˆˆβ‚— a .primes β†’ p βˆˆβ‚— b .primes
factorisation-unique' a b p p∈a =
  let
    p∣n = prime-divides a p∈a
  in find-prime-factor b (all-∈ (a .is-primes) p∈a) p∣n

factorisation-worker
  : βˆ€ n β†’ (βˆ€ k β†’ k < n β†’ .⦃ _ : Positive k ⦄ β†’ Factorisation k)
  β†’ .⦃ _ : Positive n ⦄ β†’ Factorisation n
factorisation-worker 1 ind = factored [] refl []
factorisation-worker n@(suc (suc m)) ind with is-prime-or-composite n (s≀s (s≀s 0≀x))
... | inl prime     = factored (n ∷ []) (ap (2 +_) (*-oner m)) (prime ∷ [])
... | inr composite@record{p = p; q = q} =
  let
    instance
      _ : Positive q
      _ = ≀-trans ≀-ascend (composite .q-proper)

    factored ps path primes = ind q $
      prime-divisor-lt p q n (composite .p-prime) (composite .factors)
  in record
    { primes    = p ∷ ps
    ; factors   = ap (p *_) path Β·Β· *-commutative p q Β·Β· composite .factors
    ; is-primes = composite .p-prime ∷ primes
    }

∣-factorial : βˆ€ n {m} β†’ m < n β†’ suc m ∣ factorial n
∣-factorial (suc n) {m} m≀n with suc m ≑? suc n
... | yes m=n = subst (_∣ factorial (suc n)) (sym m=n) (∣-*l {suc n} {factorial n})
... | no m≠n  = |-*l-pres {suc m} {suc n} {factorial n} $
  ∣-factorial n {m} (≀-uncap (suc m) n mβ‰ n m≀n)

factorise : (n : Nat) .⦃ _ : Positive n ⦄ β†’ Factorisation n
factorise = Wf-induction _<_ <-wf _ factorisation-worker

new-prime : (n : Nat) β†’ Ξ£[ p ∈ Nat ] n < p Γ— is-prime p
new-prime n with is-prime-or-composite (suc (factorial n)) (s≀s (factorial-positive n))
new-prime n | inl prime     = suc (factorial n) , s≀s (≀-factorial n) , prime
new-prime n | inr c@record{p = p} with holds? (suc n ≀ p)
new-prime n | inr c@record{p = p} | yes n<p = p , n<p , c .p-prime
new-prime n | inr c@record{p = suc p; q = q} | no Β¬n<p =
  let
    rem = ∣-factorial n (<-from-not-≀ _ _ (Β¬n<p ∘ s≀s))
    rem' : suc p ≑ 1
    rem' = ∣-1 (∣-+-cancel' {n = suc p} {a = 1} {b = factorial n} rem (fibreβ†’βˆ£ (q , c .factors)))
  in absurd (c .p-prime .prime≠1 rem')