A quadratic residue mod p is a nonzero number that is a perfect square mod p. Exactly half the nonzero residues are squares. The Legendre symbol encodes which are which, and quadratic reciprocity reveals a deep symmetry between different primes.
Squares mod p
For a prime p, the nonzero residues mod p split evenly: (p-1)/2 are quadratic residues (squares), and (p-1)/2 are non-residues. This is because squaring is two-to-one: a^2 ≡ (-a)^2 (mod p).
Scheme
; Find quadratic residues mod p
(define (quadratic-residues p)
(let loop ((a 1) (qrs (list)))
(if (>= a p)
(sort (delete-duplicates qrs) <)
(loop (+ a 1)
(cons (modulo (* a a) p) qrs)))))
(define (delete-duplicates lst)
(cond ((null? lst) lst)
((member (car lst) (cdr lst))
(delete-duplicates (cdr lst)))
(else (cons (car lst)
(delete-duplicates (cdr lst))))))
(display "QR mod 13: ") (display (quadratic-residues 13)) (newline)
(display "QR mod 7: ") (display (quadratic-residues 7)) (newline)
(display "QR mod 11: ") (display (quadratic-residues 11))
Legendre symbol and Euler's criterion
The Legendre symbol (a/p) equals 1 if a is a QR mod p, -1 if not, and 0 if p divides a. Euler's criterion computes it: (a/p) ≡ a^((p-1)/2) (mod p). This is a fast test for whether a number is a square mod p.
# Legendre symbol via Euler's criteriondef legendre(a, p):
result = pow(a, (p - 1) // 2, p)
return -1if result == p - 1else result
print(f"(2/7) = {legendre(2, 7)}")
print(f"(3/7) = {legendre(3, 7)}")
print(f"(4/13) = {legendre(4, 13)}")
print(f"(5/13) = {legendre(5, 13)}")
# Quadratic residues mod pdef qr(p):
returnsorted(set(pow(a, 2, p) for a inrange(1, p)))
print(f"QR mod 13: {qr(13)}")
Quadratic reciprocity (statement)
For distinct odd primes p and q: (p/q)(q/p) = (-1)^((p-1)/2 * (q-1)/2). In words: p is a square mod q and q is a square mod p, unless both are 3 mod 4, in which case exactly one is. This is the "golden theorem" that Gauss proved eight different ways.
Scheme
; Verify quadratic reciprocity for small primes
(define (mod-pow base exp mod)
(cond ((= exp 0) 1)
((even? exp)
(let ((half (mod-pow base (quotient exp 2) mod)))
(modulo (* half half) mod)))
(else (modulo (* base (mod-pow base (- exp 1) mod)) mod))))
(define (legendre a p)
(let ((r (mod-pow a (quotient (- p 1) 2) p)))
(if (= r (- p 1)) -1 r)))
(define (check-reciprocity p q)
(let* ((lpq (legendre p q))
(lqp (legendre q p))
(sign (if (and (= 3 (modulo p 4)) (= 3 (modulo q 4))) -11)))
(display "(") (display p) (display "/") (display q) (display ")=")
(display lpq) (display " (") (display q) (display "/") (display p)
(display ")=") (display lqp)
(display " product=") (display (* lpq lqp))
(display " predicted=") (display sign) (newline)))
(check-reciprocity 35)
(check-reciprocity 37)
(check-reciprocity 57)
(check-reciprocity 311)
(check-reciprocity 513)