
* bernoulli -> bernoulli-number * farey -> farey-sequence * fibonacci/mod -> modular-fibonacci * order -> unit-group-order * orders -> unit-group-orders Documented `make-fibonacci' and `make-modular-fibonacci' Reworked text about loading external libraries in docs for `math/bigfloat' Removed type aliases like Z, Q, Prime (I like them, but TR was printing them in unexpected places like array return types)
181 lines
6.9 KiB
Racket
181 lines
6.9 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
|
|
unit-group-order
|
|
unit-group-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))]))
|
|
|
|
(: unit-group-order : Integer Integer -> Positive-Integer)
|
|
(define (unit-group-order g n)
|
|
(cond [(g . <= . 0) (raise-argument-error 'unit-group-order "Positive-Integer" 0 g n)]
|
|
[(n . <= . 0) (raise-argument-error 'unit-group-order "Positive-Integer" 1 g n)]
|
|
[(not (coprime? g n))
|
|
(error 'unit-group-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))])))]))
|
|
|
|
(: unit-group-orders : Integer -> (Listof Positive-Integer))
|
|
(define (unit-group-orders n)
|
|
(cond [(n . <= . 0) (raise-argument-error 'unit-group-orders "Positive-Integer" n)]
|
|
[else (map (λ: ([m : Positive-Integer]) (unit-group-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)
|
|
(= (unit-group-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 (= (unit-group-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))]))]))
|