racket/collects/math/private/number-theory/primitive-roots.rkt
Neil Toronto f2dc2027f6 Initial math library commit. The history for these changes is preserved
in the original GitHub fork:

  https://github.com/ntoronto/racket

Some things about this are known to be broken (most egregious is that the
array tests DO NOT RUN because of a problem in typed/rackunit), about half
has no coverage in the tests, and half has no documentation. Fixes and
docs are coming. This is committed now to allow others to find errors and
inconsistency in the things that appear to be working, and to give the
author a (rather incomplete) sense of closure.
2012-11-16 11:39:51 -07:00

180 lines
6.8 KiB
Racket

#lang typed/racket/base
(require racket/list
"divisibility.rkt"
"modular-arithmetic.rkt"
(only-in "number-theory.rkt"
odd-prime-power?
totient
prime-divisors
prime?
odd-prime?
prime-power))
(provide unit-group
order
orders
exists-primitive-root?
primitive-root?
primitive-root
primitive-roots)
; DEFINITION (Order)
; If G is a finite group with identity element e,
; then the order of g in G is the least k>0 such that
; g^k=e
; DEFINITION (Un)
; The group of units in Zn with respect to multiplication
; modulo n is called Un.
(: unit-group : Integer -> (Listof Positive-Integer))
(define (unit-group n)
(cond [(n . <= . 0) (raise-argument-error 'unit-group "Positive-Integer" n)]
[else (filter (λ: ([m : Natural]) (coprime? m n))
(build-list (- n 1) add1))]))
(: order : Integer Integer -> Positive-Integer)
(define (order g n)
(cond [(g . <= . 0) (raise-argument-error 'order "Positive-Integer" 0 g n)]
[(n . <= . 0) (raise-argument-error 'order "Positive-Integer" 1 g n)]
[(not (coprime? g n)) (error 'order "expected coprime arguments; given ~e and ~e" g n)]
[else
(with-modulus n
(let: loop : Positive-Integer ([k : Positive-Integer 1]
[a : Natural g])
(cond [(mod= a 1) k]
[else (loop (+ k 1) (mod* a g))])))]))
(: orders : Integer -> (Listof Positive-Integer))
(define (orders n)
(cond [(n . <= . 0) (raise-argument-error 'orders "Positive-Integer" n)]
[else (map (λ: ([m : Positive-Integer]) (order m n))
(unit-group n))]))
; DEFINITION (Primitive Root)
; A generator g of Un is called a primitive root mod n.
; I.e. order(g)=phi(n) <=> g is primitive
#;
(define (primitive-root? g n)
(if (not (coprime? g n))
(error 'primitive-root? "expected coprime arguments; given ~e and ~e" g n)
(= (order g n) (phi n))))
; THEOREM (Existence of primitive roots)
; Un is cyclic (i.e. have a primitive root)
; <=> n = 1, 2, 4, p^e, 2*p^e where p is an odd prime
(: exists-primitive-root? : Integer -> Boolean)
(define (exists-primitive-root? n)
(cond [(n . <= . 0) (raise-argument-error 'exists-primitive-root? "Positive-Integer" n)]
[(or (= n 1) (= n 2) (= n 4)) #t]
[(odd? n) (odd-prime-power? n)]
[else (odd-prime-power? (quotient n 2))]))
; LEMMA
; a in Un is a primitive root
; <=> phi(n)/q
; a <> 1 in Un for all primes q dividing phi(n)
(: primitive-root? : Integer Integer -> Boolean)
(define (primitive-root? g n)
(cond [(g . <= . 0) (raise-argument-error 'primitive-root? "Positive-Integer" 0 g n)]
[(n . <= . 0) (raise-argument-error 'primitive-root? "Positive-Integer" 1 g n)]
[(not (coprime? g n))
(error 'primitive-root? "expected coprime arguments; given ~e and ~e" g n)]
[else
(define phi-n (totient n))
(with-modulus n
(andmap (λ: ([x : Boolean]) x)
(map (λ: ([q : Natural]) (not (mod= (modexpt g (quotient phi-n q)) 1)))
(prime-divisors phi-n))))]))
; primitive-root : N -> Un
; return primitive root of n if one exists,
; otherwise return #f
#;
(define (primitive-root n)
(and (exists-primitive-root? n)
(let* ([phi-n (phi n)]
[qs (prime-divisors phi-n)])
(define (primitive-root? g)
(with-modulus n (andmap (lambda (x) x)
(map (lambda (q)
(not (= (expt g (/ phi-n q)) 1)))
qs))))
(let loop ([g 1])
(cond
[(= g n) #f]
[(not (coprime? g n)) (loop (+ g 1))]
[(primitive-root? g) g]
[else (loop (+ g 1))])))))
; LEMMA
; If Un has a primitive root, then it has phi(phi(n)) primitive roots
; primitive-roots : integer -> list
; return list of all primitive roots of Un
(: primitive-roots : Integer -> (Listof Natural))
(define (primitive-roots n)
(cond [(n . <= . 0) (raise-argument-error 'primitive-roots "Positive-Integer" n)]
[(not (exists-primitive-root? n)) empty]
[else
(let* ([phi-n (totient n)]
[qs (prime-divisors phi-n)])
(: primitive-root? : Natural -> Boolean)
(define (primitive-root? g)
(with-modulus n
(andmap (λ: ([x : Boolean]) x)
(map (λ: ([q : Natural])
(not (mod= (modexpt g (quotient phi-n q)) 1)))
qs))))
(let: loop : (Listof Natural)
([g : Natural 1]
[roots : (Listof Natural) empty])
(cond
[(= g n) (reverse roots)]
[(not (coprime? g n)) (loop (+ g 1) roots)]
[(primitive-root? g) (loop (+ g 1) (cons g roots))]
[else (loop (+ g 1) roots)])))]))
(: primitive-root : Integer -> (U Natural False))
(define (primitive-root n)
(cond [(n . <= . 0) (raise-argument-error 'primitive-root "Positive-Integer" n)]
[(not (exists-primitive-root? n)) #f]
; U_p^e , p odd
[(and (odd-prime-power? n) (not (prime? n)))
(define pp (prime-power n))
(define p (if pp (first pp) (error 'primitive-root "internal error")))
(define gg (primitive-root p))
(define g (or gg (error 'primitive-root "internal error")))
(if (= (order g (* p p)) (totient (* p p)))
g
(modulo (+ g p) n))]
; U_2p^e , p odd
[(and (even? n) (odd-prime? (quotient n 2)))
(define gg (primitive-root (quotient n 2)))
(define g (or gg (error 'primitive-root "internal error")))
(if (odd? g)
g
(modulo (+ g (quotient n 2)) n))]
; General case
[else
(define phi-n (totient n))
(define qs (prime-divisors phi-n))
(: primitive-root? : Natural -> Boolean)
(define (primitive-root? g)
(with-modulus n
(andmap (λ: ([x : Boolean]) x)
(map (λ: ([q : Natural])
(not (mod= (modexpt g (quotient phi-n q)) 1)))
qs))))
(let: loop : (U Natural False)
([g : Natural 1])
(cond
[(= g n) #f]
[(not (coprime? g n)) (loop (+ g 1))]
[(primitive-root? g) g]
[else (loop (+ g 1))]))]))