1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancements. ;;;;;
5 ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
6 ;;; All rights reserved ;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; (c) Copyright 1981 Massachusetts Institute of Technology ;;;
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13 ;;; *****************************************************************
14 ;;; ***** NUMTH ******* VARIOUS NUMBER THEORY FUNCTIONS *************
15 ;;; *****************************************************************
17 (macsyma-module numth
)
19 (declare-top (special modulus
))
21 (load-macsyma-macros rzmac
)
23 ;;; Sum of divisors and Totient functions
25 ;; compute the sum of the k'th powers of the divisors of the absolute
28 (defmfun $divsum
(n &optional
(k 1))
29 (if (and (integerp k
) (integerp n
))
32 (list '(rat) (divsum n
(- k
)) (expt n
(- k
)))
34 (list '($divsum
) n k
)))
36 ;; divsum assumes its arguments to be non-negative integers.
39 (declare (type (integer 0) n k
) (optimize (speed 3)))
40 (let (($intfaclim nil
)) ;get-factor-list returns list
41 ;of (p_rime e_xponent) pairs
44 ((zerop k
) (reduce #'* (get-factor-list n
) ; product over e+1
45 :key
#'(lambda (pe) (1+ (the fixnum
(cadr pe
))))))
46 (t (reduce #'* (get-factor-list n
) ; product over ((p^k)^(e+1)-1)/(p^k-1)
48 (let ((p (car pe
)) (e (cadr pe
)))
49 (declare (type (integer 2) p
)
50 (type (integer 1 (#.most-positive-fixnum
)) e
))
51 (let ((tmp (expt p k
)))
52 (truncate (1- (expt tmp
(1+ e
)))
55 ;; totient computes the euler totient function
56 ;; i.e. the count of numbers relatively prime to n between 1 and n.
61 (list '($totient
) n
)))
63 ;; totient assumes its argument to be an integer.
64 ;; the exponents in the prime factorization of n are assumed to be
65 ;; fixnums (anything else would exceed memory).
68 (declare (type (integer 0) n
) (optimize (speed 3)))
69 (let (($intfaclim nil
))
72 (reduce #'* (get-factor-list n
)
74 (let ((p (car pe
)) (e (cadr pe
)))
75 (declare (type (integer 2) p
)
76 (type (integer 1 (#.most-positive-fixnum
)) e
))
77 (* (1- p
) (expt p
(1- e
)))))))))
79 ;;; JACOBI symbol and Gaussian factoring
81 ;; $jacobi/jacobi implement the Kronecker-Jacobi symbol, a light
82 ;; generalization of the Legendre/Jacobi symbols.
83 ;; (for a prime b, $jacobi(a b) is the Legendre symbol).
85 ;; The implementation follows algorithm 1.4.10 in 'A Course in
86 ;; Computational Algebraic Number Theory' by H. Cohen
88 (defmfun $jacobi
(a b
)
89 (if (and (integerp a
) (integerp b
))
94 (declare (integer a b
) (optimize (speed 3)))
96 (if (= 1 (abs a
)) 1 0))
97 ((and (evenp a
) (evenp b
))
101 (tab2 (make-array 8 :element-type
'(integer -
1 1)
102 :initial-contents
#(0 1 0 -
1 0 -
1 0 1))))
103 (declare (type (integer -
1 1) k
))
105 (loop for v fixnum
= 0 then
(1+ v
) ;remove 2's from b
106 while
(evenp b
) do
(setf b
(ash b -
1))
107 finally
(when (oddp v
)
108 (setf k
(aref tab2
(logand a
7))))) ; (-1)^(a^2-1)/8
117 (return-from jacobi
(if (> b
1) 0 k
)))
119 (loop for v fixnum
= 0 then
(1+ v
)
120 while
(evenp a
) do
(setf a
(ash a -
1))
121 finally
(when (oddp v
)
122 (when (minusp (aref tab2
(logand b
7))); (-1)^(b^2-1)/8
125 (when (plusp (logand a b
2)) ;compute (-1)^(a-1)(b-1)/4
133 ;; factor over the gaussian primes
135 (declaim (inline gctimes gctime1
))
137 (defmfun $gcfactor
(n)
138 (let ((n (cdr ($totaldisrep
($bothcoef
($rat
($rectform n
) '$%i
) '$%i
)))))
139 (if (not (and (integerp (first n
)) (integerp (second n
))))
140 (gcdisp (nreverse n
))
141 (loop for
(term exp
) on
(gcfactor (second n
) (first n
)) by
#'cddr
145 (list '(mexpt simp
) (gcdisp term
) exp
))
148 (return (cond ((null res
)
151 (if (mexptp (car res
))
152 `((mexpt simp gcfactored
) ,@(cdar res
))
155 `((mtimes simp gcfactored
) ,@(nreverse res
)))))))))
158 (declare (integer p
) (optimize (speed 3)))
159 (cond ((not (= (rem p
4) 1)) nil
)
160 ((= (rem p
8) 5) (power-mod 2 (ash (1- p
) -
2) p
))
161 ((= (rem p
24) 17) (power-mod 3 (ash (1- p
) -
2) p
)) ;p=2(mod 3)
162 (t (do ((i 5 (+ i j
)) ;p=1(mod 24)
164 ((= (jacobi i p
) -
1) (power-mod i
(ash (1- p
) -
2) p
))
165 (declare (integer i
) (fixnum j
))))))
168 (declare (integer p
) (optimize (speed 3)))
177 ((not (> r1 sp
)) (list r1 r2
))
178 (declare (integer r1 r2
)))))))
180 (defun gctimes (a b c d
)
181 (declare (integer a b c d
) (optimize (speed 3)))
182 (list (- (* a c
) (* b d
))
183 (+ (* a d
) (* b c
))))
188 (let ((rp (car term
))
190 (setq ip
(if (eql ip
1) '$%i
`((mtimes) ,ip $%i
)))
193 `((mplus) ,rp
,ip
)))))
195 (defun gcfactor (a b
)
196 (declare (integer a b
) (optimize (speed 3)))
197 (when (and (zerop a
) (zerop b
))
198 (return-from gcfactor
(list 0 1)))
199 (let* (($intfaclim nil
)
207 (nl (cfactorw (+ (* a a
) (* b b
))))
213 (declare (integer e1 e2 econt g a b
))
214 (when (= 1 (the integer
(car gl
))) (setq gl nil
))
215 (when (= 1 (the integer
(car nl
))) (setq nl nil
))
225 (setq p
(max (the integer
(car gl
))
226 (the integer
(car nl
))))))
229 (setq plis
(list* p
(cadr gl
) plis
))
233 (cond ((zerop (rem (+ (* a
(the integer
(car cd
))) ; gcremainder
234 (* b
(the integer
(cadr cd
))))
235 (the integer p
))) ; remainder(real((a+bi)cd~),p)
236 ; z~ is complex conjugate
241 (setq dc
(reverse cd
))))
242 (setq dc
(gcexpt dc
(the integer
(cadr nl
)))
243 dc
(gctimes a b
(the integer
(car dc
)) (- (the integer
(cadr dc
))))
244 a
(truncate (the integer
(car dc
)) (the integer p
))
245 b
(truncate (the integer
(cadr dc
)) (the integer p
))
247 (when (equal p
(car gl
))
248 (incf econt
(the integer
(cadr gl
)))
250 (incf e1
(* 2 (the integer
(cadr gl
)))))
252 (incf e1
(the integer
(cadr gl
)))
253 (incf e2
(the integer
(cadr gl
)))))
256 (setq ans
(list* cd e1 ans
))
259 (setq ans
(list* (reverse cd
) e2 ans
))
264 (let* ((cd (gcexpt (list 0 -
1) (rem econt
4)))
267 (l (mapcar #'signum
(gctimes a b rcd icd
)))
270 (declare (integer r i rcd icd
))
271 (when (or (= r -
1) (= i -
1))
272 (setq plis
(list* -
1 1 plis
)))
274 (setq ans
(list* '(0 1) 1 ans
)))
275 (return-from gcfactor
(append plis ans
))))))
278 (declare (optimize (speed 3)))
281 (declare (integer r i
))
282 (list (+ (* r r
) (* i i
)) (ash (* r i
) 1))))
285 (gctimes (car a
) (cadr a
) (car b
) (cadr b
)))
288 (cond ((zerop n
) '(1 0))
290 ((evenp n
) (gcexpt (gcsqr a
) (ash n -
1)))
291 (t (gctime1 a
(gcexpt (gcsqr a
) (ash n -
1))))))
293 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
295 ;; Maxima functions in (Z/nZ)*
297 ;; zn_order, zn_primroot_p, zn_primroot, zn_log,
298 ;; chinese, zn_characteristic_factors, zn_factor_generators, zn_nth_root,
299 ;; zn_add_table, zn_mult_table, zn_power_table
301 ;; 2012 - 2020, Volker van Nek
304 ;; Maxima option variables:
305 (defmvar $zn_primroot_limit
1000 "Upper bound for `zn_primroot'." fixnum
)
306 (defmvar $zn_primroot_verbose nil
"Print message when `zn_primroot_limit' is reached." boolean
)
307 (defmvar $zn_primroot_pretest nil
"`zn_primroot' pretests whether (Z/nZ)* is cyclic." boolean
)
309 (declaim (inline zn-quo
))
310 (defun zn-quo (n d p
)
311 (declare (integer n d p
) (optimize (speed 3)))
312 (mod (* n
(inv-mod d p
)) p
) )
314 ;; compute the order of x in (Z/nZ)*
316 ;; optional argument: ifactors of totient(n) as returned in Maxima by
317 ;; block([factors_only:false], ifactors(totient(n)))
318 ;; e.g. [[2, 3], [3, 1], ... ]
320 (defmfun $zn_order
(x n
&optional fs-phi
)
321 (unless (and (integerp x
) (integerp n
))
322 (return-from $zn_order
324 (list '($zn_order
) x n fs-phi
)
325 (list '($zn_order
) x n
) )))
329 ((= 1 x
) (if (= n
1) nil
1))
330 ((/= 1 (gcd x n
)) nil
)
333 (if (and ($listp fs-phi
) ($listp
(cadr fs-phi
)))
335 (setq fs-phi
(mapcar #'cdr
(cdr fs-phi
))) ; Lispify fs-phi
336 (setq fs-phi
(cons (totient-from-factors fs-phi
) fs-phi
)) )
337 (gf-merror (intl:gettext
338 "Third argument to `zn_order' must be of the form [[p1, e1], ..., [pk, ek]]." )))
339 (setq fs-phi
(totient-with-factors n
)) )
343 (cdr fs-phi
)) ))) ;; factors of phi with multiplicity
345 (defun zn_order (x n phi fs-phi
)
346 (format t
"`zn_order' is deprecated. ~%Please use `zn-order' instead.~%" )
347 (zn-order x n phi fs-phi
) )
349 ;; compute order of x as a divisor of the known group order
351 (defun zn-order (x n ord fs-ord
)
353 (dolist (f fs-ord ord
)
354 (setq p
(car f
) e
(cadr f
)
355 ord
(truncate ord
(expt p e
))
356 z
(power-mod x ord n
) )
357 ;; ord(z) = p^i, i from [0,e]
358 ;; replace p^e in ord by p^i : x^(ord*p^i/p^e) = 1
362 (when (= e
1) (return))
364 (setq z
(power-mod z p n
)) ))))
367 ;; compute totient (euler-phi) of n and its factors in one function
369 ;; returns a list of the form (phi ((p1 e1) ... (pk ek)))
371 (defun totient-with-factors (n)
372 (let (($intfaclim
) (phi 1) fs-n
(fs) p e
(fs-phi) g
)
373 (setq fs-n
(get-factor-list n
))
375 (setq p
(car f
) e
(cadr f
))
376 (setq phi
(* phi
(1- p
) (expt p
(1- e
))))
377 (when (> e
1) (setq fs
(cons `(,p
,(1- e
)) fs
)))
378 (setq fs
(append (get-factor-list (1- p
)) fs
)) )
379 (setq fs
(copy-tree fs
)) ;; this deep copy is a workaround to avoid references
380 ;; to the list returned by ifactor.lisp/get-factor-list.
382 (setq fs
(sort fs
#'< :key
#'car
))
384 (dolist (f (cdr fs
) (cons phi
(reverse (cons g fs-phi
))))
385 (if (= (car f
) (car g
))
386 (incf (cadr g
) (cadr f
)) ;; assignment
388 (setq fs-phi
(cons g fs-phi
))
391 ;; recompute totient from given factors
393 ;; fs-phi: factors of totient with multiplicity: ((p1 e1) ... (pk ek))
395 (defun totient-from-factors (fs-phi)
397 (dolist (f fs-phi phi
)
398 (setq p
(car f
) e
(cadr f
))
399 (setq phi
(* phi
(expt p e
))) )))
402 ;; for n > 2 is x a primitive root modulo n
403 ;; when n does not divide x
404 ;; and for all prime factors p of phi = totient(n)
405 ;; x^(phi/p) mod n # 1
407 ;; optional argument: ifactors of totient(n)
409 (defmfun $zn_primroot_p
(x n
&optional fs-phi
)
410 (unless (and (integerp x
) (integerp n
))
411 (return-from $zn_primroot_p
413 (list '($zn_primroot_p
) x n fs-phi
)
414 (list '($zn_primroot_p
) x n
) )))
418 ((= 1 x
) (if (= n
2) t nil
))
422 (if (and ($listp fs-phi
) ($listp
(cadr fs-phi
)))
424 (setq fs-phi
(mapcar #'cdr
(cdr fs-phi
))) ; Lispify fs-phi
425 (setq fs-phi
(cons (totient-from-factors fs-phi
) fs-phi
)) )
426 (gf-merror (intl:gettext
427 "Third argument to `zn_primroot_p' must be of the form [[p1, e1], ..., [pk, ek]]." )))
428 (setq fs-phi
(totient-with-factors n
)) )
431 (mapcar #'car
(cdr fs-phi
))) ))) ;; factors only (omitting multiplicity)
433 (defun zn-primroot-p (x n phi fs-phi
)
434 (unless (= 1 (gcd x n
))
435 (return-from zn-primroot-p nil
) )
437 (when (= 1 (power-mod x
(truncate phi p
) n
))
438 (return-from zn-primroot-p nil
) )))
441 ;; find the smallest primitive root modulo n
443 ;; optional argument: ifactors of totient(n)
445 (defmfun $zn_primroot
(n &optional fs-phi
)
447 (return-from $zn_primroot
449 (list '($zn_primroot
) n fs-phi
)
450 (list '($zn_primroot
) n
) )))
455 (when $zn_primroot_pretest
457 (return-from $zn_primroot nil
) ))
459 (if (and ($listp fs-phi
) ($listp
(cadr fs-phi
)))
461 (setq fs-phi
(mapcar #'cdr
(cdr fs-phi
))) ; Lispify fs-phi
462 (setq fs-phi
(cons (totient-from-factors fs-phi
) fs-phi
)) )
463 (gf-merror (intl:gettext
464 "Second argument to `zn_primroot' must be of the form [[p1, e1], ..., [pk, ek]]." )))
465 (setq fs-phi
(totient-with-factors n
)) )
468 (mapcar #'car
(cdr fs-phi
))) ))) ;; factors only (omitting multiplicity)
470 ;; (Z/nZ)* is cyclic if n = 2, 4, p^k or 2*p^k where p prime > 2
473 (when (< n
2) (return))
474 (when (< n
8) (return t
)) ;; 2,3,4,5,2*3,7
475 (when (evenp n
) ;; 2*p^k
476 (setq n
(ash n -
1)) ;; -> p^k
477 (when (evenp n
) (return)) )
478 (let (($intfaclim
) fs
(len 0))
479 (multiple-value-setq (n fs
) (get-small-factors n
))
480 (when fs
(setq len
(length fs
)))
481 (when (= 1 n
) (return (= 1 len
)))
482 (when (> len
0) (return))
483 (when (primep n
) (return t
))
484 (setq fs
(convert-list (get-large-factors n
)))
485 (return (= 1 (length fs
))) )))
487 (defun zn-primroot (n phi fs-phi
)
490 (when (zn-primroot-p i n phi fs-phi
)
492 (when (= i $zn_primroot_limit
)
493 (when $zn_primroot_verbose
494 (format t
"`zn_primroot' stopped at zn_primroot_limit = ~A~%" $zn_primroot_limit
) )
498 ;; Chinese Remainder Theorem
500 (defmfun $chinese
(rems mods
&optional
(return-lcm? nil
))
502 ((not (and ($listp rems
) ($listp mods
)))
503 (list '($chinese
) rems mods
) )
504 ((let ((lr ($length rems
)) (lm ($length mods
)))
505 (or (= 0 lr
) (= 0 lm
) (/= lr lm
)) )
506 (gf-merror (intl:gettext
507 "Unsuitable arguments to `chinese': ~m ~m" ) rems mods
))
508 ((notevery #'integerp
(setq rems
(cdr rems
)))
509 (list '($chinese
) (cons '(mlist simp
) rems
) mods
) )
510 ((notevery #'integerp
(setq mods
(cdr mods
)))
511 (list '($chinese
) (cons '(mlist simp
) rems
) (cons '(mlist simp
) mods
)) )
512 ((eql return-lcm?
'$lcm
)
513 (cons '(mlist simp
) (chinese rems mods
)) )
515 (car (chinese rems mods
)) )))
517 (defun chinese (rems mods
)
518 (if (= 1 (length mods
))
519 (list (car rems
) (car mods
))
520 (let ((rp (car rems
))
522 (rq-q (chinese (cdr rems
) (cdr mods
))) )
524 (let* ((rq (car rq-q
))
528 (c (cadr gc
)) ) ;; CRT-coefficient
530 ((= 1 g
) ;; coprime moduli
531 (let* ((h (mod (* (- rp rq
) c
) p
))
534 ((= 0 (mod (- rp rq
) g
)) ;; ensures unique solution
535 (let* ((h (* (- rp rq
) c
))
538 (list (mod (+ rq
(* h q
/g
)) lcm-pq
) lcm-pq
) ))))))))
540 ;; (zn-gcdex2 x y) returns `(,g ,c) where c*x + d*y = g = gcd(x,y)
542 (defun zn-gcdex2 (x y
)
543 (let ((x1 1) (y1 0) q r
)
544 (do ()((= 0 y
) (list x x1
))
545 (multiple-value-setq (q r
) (truncate x y
))
547 (psetf x1 y1 y1
(- x1
(* q y1
))) )))
550 ;; discrete logarithm:
551 ;; solve g^x = a mod n, where g is a generator of (Z/nZ)* or of a subgroup containing a
553 ;; see: lecture notes 'Grundbegriffe der Kryptographie' - Eike Best
554 ;; http://theoretica.informatik.uni-oldenburg.de/~best/publications/kry-Mai2005.pdf
556 ;; optional argument: ifactors of zn_order(g,n)
558 (defmfun $zn_log
(a g n
&optional fs-ord
)
559 (unless (and (integerp a
) (integerp g
) (integerp n
))
562 (list '($zn_log
) a g n fs-ord
)
563 (list '($zn_log
) a g n
) )))
564 (when (minusp a
) (setq a
(mod a n
)))
566 ((or (= 0 a
) (>= a n
)) nil
)
569 ((> (gcd a n
) 1) nil
)
572 (if (and ($listp fs-ord
) ($listp
(cadr fs-ord
)))
574 (setq fs-ord
(mapcar #'cdr
(cdr fs-ord
))) ; Lispify fs-ord
575 (setq fs-ord
(cons (totient-from-factors fs-ord
) fs-ord
)) ) ; totient resp. order in general
576 (gf-merror (intl:gettext
577 "Fourth argument to `zn_log' must be of the form [[p1, e1], ..., [pk, ek]]." )))
578 (let (($intfaclim
) (ord ($zn_order g n
)))
579 (setq fs-ord
(cons ord
(get-factor-list ord
))) ))
581 ((= 0 (mod (- a
(* g g
)) n
))
583 ((= 1 (mod (* a g
) n
))
584 (mod -
1 (car fs-ord
)) )
587 (car fs-ord
) ;; order
588 (cdr fs-ord
) ) ))))) ;; factors with multiplicity
590 ;; Pohlig-Hellman-reduction:
592 ;; Solve g^x = a mod n.
593 ;; Assume, that a is an element of (Z/nZ)*
594 ;; and g is a generator of (Z/nZ)* or of a subgroup containing a.
596 (defun zn-dlog (a g n ord fs-ord
)
597 (let (p e ord
/p om xp xk mods dlogs
(g-inv (inv-mod g n
)))
599 (setq p
(car f
) e
(cadr f
)
600 ord
/p
(truncate ord p
)
601 om
(power-mod g ord
/p n
) ;; om is a generator of prime order p
603 ;; Let op = ord/p^e, gp = g^op (mod n), ap = a^op (mod n) and
605 ;; gp is of order p^e and therefore
606 ;; (*) gp^xp = ap (mod n).
607 (do ((b a
) (k 0) (pk 1) (acc g-inv
) (e1 (1- e
))) (()) ;; Solve (*) by solving e logs ..
608 (setq xk
(dlog-rho (power-mod b ord
/p n
) om p n
)) ;; .. in subgroups of order p.
611 (when (= k e
) (return)) ;; => xp = x_0+x_1*p+x_2*p^2+...+x_{e-1}*p^{e-1} < p^e
612 (setq ord
/p
(truncate ord
/p p
)
614 (when (/= xk
0) (setq b
(mod (* b
(power-mod acc xk n
)) n
)))
615 (when (/= k e1
) (setq acc
(power-mod acc p n
))) )
616 (push (expt p e
) mods
)
618 (car (chinese dlogs mods
)) )) ;; Find x (mod ord) with x = xp (mod p^e) for all p,e.
620 ;; baby-steps-giant-steps:
622 (defun dlog-baby-giant (a g p n
) ;; g is generator of order p mod n
623 (let* ((m (1+ (isqrt p
)))
624 (s (floor (* 1.3 m
)))
628 (make-hash-table :size s
:test
#'eql
:rehash-threshold
0.9) )
633 (return-from dlog-baby-giant r
) )
634 (setf (gethash b babies
) r
)
636 (when (= r m
) (return))
637 (setq b
(mod (* gi b
) n
)) )
638 (setq g^m
(power-mod g m n
))
640 (bb 1 (mod (* g^m bb
) n
))
642 (when (setq r
(gethash bb babies
))
644 (return (+ (* rr m
) r
)) )) ))
648 (defun dlog-naive (a g n
)
649 (do ((i 0 (1+ i
)) (gi 1 (mod (* gi g
) n
)))
652 ;; Pollard rho for dlog computation (Brents variant of collision detection)
654 (defun dlog-rho (a g p n
) ;; g is generator of prime order p mod n
658 ((= 0 (mod (- a
(* g g
)) n
)) 2)
659 ((= 1 (mod (* a g
) n
)) (1- p
))
660 ((< p
512) (dlog-naive a g n
))
661 ((< p
65536) (dlog-baby-giant a g p n
))
663 (prog ((b 1) (y 0) (z 0) ;; b = g^y * a^z
664 (bb 1) (yy 0) (zz 0) ;; bb = g^yy * a^zz
667 (do ((i 0)(j 1)) (()) (declare (fixnum i j
))
668 (multiple-value-setq (b y z
) (dlog-f b y z a g p n
))
669 (when (equal b bb
) (return)) ;; g^y * a^z = g^yy * a^zz
672 (setq j
(1+ (ash j
1)))
673 (setq bb b yy y zz z
) ))
674 (setq dy
(mod (- y yy
) p
) dz
(mod (- zz z
) p
)) ;; g^dy = a^dz = g^(x*dz)
675 (when (= 1 (gcd dz p
))
676 (return (zn-quo dy dz p
)) ) ;; x = dy/dz mod p (since g is generator of order p)
680 yy
(1+ (random (1- p
)))
681 zz
(1+ (random (1- p
)))
682 bb
(mod (* (power-mod g yy n
) (power-mod a zz n
)) n
) )
685 ;; iteration for Pollard rho: b = g^y * a^z in each step
687 (defun dlog-f (b y z a g ord n
)
691 (values (mod (* b b
) n
) (mod (ash y
1) ord
) (mod (ash z
1) ord
)) )
692 ((= 1 m
) ;; avoid stationary case b=1 => b*b=1
693 (values (mod (* a b
) n
) y
(mod (+ z
1) ord
) ) )
695 (values (mod (* g b
) n
) (mod (+ y
1) ord
) z
) ) )))
698 ;; characteristic factors:
700 (defmfun $zn_characteristic_factors
(m)
701 (unless (and (integerp m
) (> m
1)) ;; according to Shanks no result for m = 1
702 (gf-merror (intl:gettext
703 "`zn_characteristic_factors': Argument must be an integer greater than 1." )))
704 (cons '(mlist simp
) (zn-characteristic-factors m
)) )
706 (defmfun $zn_carmichael_lambda
(m)
709 (if (= m
1) 1 (zn-characteristic-factors m t
)) )
710 (t (gf-merror (intl:gettext
711 "`zn_carmichael_lambda': Argument must be a positive integer." )))))
713 ;; D. Shanks - Solved and unsolved problems in number theory, 2. ed
714 ;; definition 29 and 30 (p. 92 - 94)
716 ;; (zn-characteristic-factors 104) --> (2 2 12)
717 ;; => Z104* is isomorphic to M2 x M2 x M12
718 ;; the direct product of modulo multiplication groups of order 2 resp. 12
720 (defun zn-characteristic-factors (m &optional lambda-only
) ;; m > 1
722 (pe-list (get-factor-list m
)) ;; def. 29 part A
723 (shanks-phi ;; part D
725 (apply #'nconc
(mapcar #'zn-shanks-phi-step-bc pe-list
))
728 (do ((todo shanks-phi
(nreverse left
))
729 (p 0 0) (f 1 1) (left nil nil
)
733 (setq q
(car qd
) d
(cadr qd
))
736 (setq p q f
(* f
(expt q d
))) ))
737 (when lambda-only
(return-from zn-characteristic-factors f
))
740 ;; definition 29 parts B,C (p. 92)
741 (defun zn-shanks-phi-step-bc (pe)
742 (let ((p (car pe
)) (e (cadr pe
)) qd
)
745 (setq qd
(list (if (> e
1) '(2 1) '(1 1))))
746 (when (> e
2) (setq qd
(nconc qd
(list `(2 ,(- e
2)))))) )
748 (setq qd
(let (($intfaclim
)) (get-factor-list (1- p
))))
750 (setq qd
(nconc qd
(list `(,p
,(1- e
))))) )))
754 (cond ((> (car a
) (car b
)) t
)
755 ((< (car a
) (car b
)) nil
)
756 (t (> (cadr a
) (cadr b
))) ))
759 ;; factor generators:
761 (defmfun $zn_factor_generators
(m)
762 (unless (and (integerp m
) (> m
1))
763 (gf-merror (intl:gettext
764 "`zn_factor_generators': Argument must be an integer greater or equal 2." )))
765 (cons '(mlist simp
) (zn-factor-generators m
)) )
767 ;; D. Shanks - Solved and unsolved problems in number theory, 2. ed
768 ;; Theorem 44, page 94
770 ;; zn_factor_generators(104); --> [79,27,89]
771 ;; zn_characteristic_factors(104); --> [2,2,12]
772 ;; map(lambda([g], zn_order(g,104)), [79,27,89]); --> [2,2,12]
774 ;; Every element in Z104* can be expressed as
775 ;; 79^i * 27^j * 89^k (mod m) where 0 <= i,j < 2 and 0 <= k < 12
777 ;; The proof of theorem 44 contains the construction of the factor generators.
779 (defun zn-factor-generators (m) ;; m > 1
781 (fs (sort (get-factor-list m
) #'< :key
#'car
))
783 (p (car pe
)) (e (cadr pe
))
785 phi fs-phi ga gs ords fs-ords pegs
)
786 ;; lemma 1, page 98 :
787 ;; (Z/mZ)* is cyclic when m =
789 (return-from zn-factor-generators
(list 1)) )
790 (when (or (< m
8) ;; 3,4,5,6,7
791 (and (> p
2) (null (cdr fs
))) ;; p^k, p#2
792 (and (= 2 p
) (= 1 e
) (null (cddr fs
))) ) ;; 2*p^k, p#2
793 (setq phi
(totient-by-fs-n fs
)
794 fs-phi
(sort (mapcar #'car
(get-factor-list phi
)) #'<)
795 ga
(zn-primroot m phi fs-phi
) )
796 (return-from zn-factor-generators
(list ga
)) )
800 (unless (and (= e
1) (cdr fs
)) ;; phi(2*m) = phi(m) if m#1 is odd
801 (push (1- a
) gs
) ) ;; a = 2^e
802 (when (> e
1) (setq ords
(list 2) fs-ords
(list '((2 1)))))
804 (push 3 gs
) (push (expt 2 (- e
2)) ords
) (push `((2 ,(- e
2))) fs-ords
) ))
805 ;; lemma 2, page 100 :
807 (setq phi
(* (1- p
) (expt p
(1- e
)))
808 fs-phi
(sort (get-factor-list (1- p
)) #'< :key
#'car
) )
809 (when (> e
1) (setq fs-phi
(nconc fs-phi
(list `(,p
,(1- e
))))))
810 (setq ga
(zn-primroot a phi
(mapcar #'car fs-phi
)) ;; factors only
813 fs-ords
(list fs-phi
) )))
817 (setq pe
(car fs
) fs
(cdr fs
)
818 p
(car pe
) e
(cadr pe
)
819 phi
(* (1- p
) (expt p
(1- e
)))
820 fs-phi
(sort (get-factor-list (1- p
)) #'< :key
#'car
) )
821 (when (> e
1) (setq fs-phi
(nconc fs-phi
(list `(,p
,(1- e
))))))
823 gb
(zn-primroot b phi
(mapcar #'car fs-phi
))
824 c
(mod (* (inv-mod b a
) (- 1 gb
)) a
) ;; CRT: h = gb mod b
825 h
(+ (* b c
) gb
) ;; CRT: h = 1 mod a
827 gs
(mapcar #'(lambda (g) (+ (* a
(mod (* ia
(- 1 g
)) b
)) g
)) gs
)
830 fs-ords
(cons fs-phi fs-ords
)
832 ;; lemma 3, page 101 :
834 (mapcar #'(lambda (g ord f
)
835 (mapcar #'(lambda (pe)
837 (list (power-mod g
(truncate ord
(apply #'expt pe
)) m
)) ))
840 (setq pegs
(sort (apply #'append pegs
) #'zn-pe
>))
841 (do ((todo pegs
(nreverse left
))
842 (q 0 0) (fg 1 1) (left nil nil
)
846 (setq p
(car peg
) g
(caddr peg
))
849 (setq q p fg
(mod (* fg g
) m
)) ))
853 ;; r-th roots --------------------------------------------------------------- ;;
855 ;; If the residue class a is an r-th power modulo n and contained in a multiplication
856 ;; subgroup of (Z/nZ), return all r-th roots from this subgroup and false otherwise.
858 (defmfun $zn_nth_root
(a r n
&optional fs-n
)
859 (unless (and (integerp a
) (integerp r
) (integerp n
))
860 (gf-merror (intl:gettext
861 "`zn_nth_root' needs three integer arguments. Found ~m, ~m, ~m." ) a r n
))
862 (unless (and (> r
0) (> n
0))
863 (gf-merror (intl:gettext
864 "`zn_nth_root': Second and third arg must be pos integers. Found ~m, ~m." ) r n
))
866 (if (and ($listp fs-n
) ($listp
(cadr fs-n
)))
867 (setq fs-n
(mapcar #'cdr
(cdr fs-n
))) ;; Lispify fs-n
868 (gf-merror (intl:gettext
869 "`zn_nth_root': The opt fourth arg must be of the form [[p1, e1], ..., [pk, ek]]." ))))
870 (let ((rts (zn-nrt a r n fs-n
)))
871 (when rts
(cons '(mlist simp
) rts
)) ))
873 (defun zn-nrt (a r n
&optional fs-n
)
874 (let (g n
/g c p q aq ro ord qs rt rts rems res
)
875 (unless fs-n
(setq fs-n
(let (($intfaclim
)) (get-factor-list n
))))
878 ((every #'onep
(mapcar #'second fs-n
)) ;; RSA-like case (n is squarefree)
879 (when (= a
0) (return-from zn-nrt
(list 0))) ) ;; n = 1: exit here
881 ;; Handle residue classes not coprime to n (n is not squarefree):
882 ;; Use Theorems 49 and 50 from
883 ;; Shanks - Solved and Unsolved Problems in Number Theory
884 (setq g
(gcd a n
) n
/g
(truncate n g
))
885 (when (/= (gcd g n
/g
) 1) ;; a is not contained in any mult. subgroup (Th. 50):
886 (return-from zn-nrt nil
) ) ;; exit here
887 (when (= a
0) (return-from zn-nrt
(list 0)))
888 ;; g = gcd(a,n). Now assume gcd(g,n/g) = 1.
889 ;; There are totient(n/g) multiples of g, i*g, with gcd(i,n/g) = 1,
890 ;; which form a modulo multiplication subgroup of (Z/nZ),
891 ;; isomorphic to (Z/mZ)*, where m = n/g.
892 ;; a is one of these multiples of g.
893 ;; Find the r-th roots of a resp. mod(a,m) in (Z/mZ)* and then
894 ;; by using CRT all corresponding r-th roots of a in (Z/nZ).
897 c
(inv-mod g n
/g
) ;; CRT-coeff
898 ;; isomorphic mapping (Th. 49):
899 ;; (use CRT with x = 0 mod g and x = rt mod n/g)
900 res
(mapcar #'(lambda (rt) (* g
(mod (* c rt
) n
/g
))) rts
) )
901 (return-from zn-nrt
(sort res
#'<)) ))
903 ;; for every prime power factor of n
904 ;; reduce a and r if possible and call zq-nrt:
909 ord
(* (1- p
) (truncate q p
)) )
912 (setq ro
(mod r ord
))
913 (when (= ro
0) (setq ro ord
)) )
917 (if (or (= p q
) (= ro
1))
919 (return-from zn-nrt nil
) ))
921 (setq rt
(list aq
)) )
923 (setq rt
(zq-nrt aq ro p q
))
924 (unless rt
(return-from zn-nrt nil
)) ))
927 ;; CRT in case n splits into more than one factor:
928 (if (= 1 (length fs-n
))
929 (setq res rt
) ;; n is a prime power
930 (setq qs
(nreverse qs
)
931 rems
(zn-distrib-lists (nreverse rts
))
932 res
(mapcar #'(lambda (rs) (car (chinese rs qs
))) rems
) ))
935 ;; return all possible combinations containing one entry per list:
936 ;; (zn-distrib-lists '((1 2 3) (4) (5 6)))
937 ;; --> ((1 4 5) (1 4 6) (2 4 5) (2 4 6) (3 4 5) (3 4 6))
939 (defun zn-distrib-lists (ll)
940 (let ((res (car ll
)) tmp e
)
941 (dolist (l (cdr ll
) res
)
945 (setq e
(if (listp r
) (copy-list r
) (list r
)))
946 (push (nconc e
(list n
)) tmp
) ))
947 (setq res
(nreverse tmp
)) )))
949 ;; handle composite r (which are not coprime to totient(q)):
950 ;; e.g. r=x*x*y*z, then a^(1/r) = (((a^(1/x))^(1/x))^(1/y))^(1/z)
952 (defun zq-nrt (a r p q
) ;; prime power q = p^e
953 ;; assume a < q, r <= q
956 ((or (= 1 r
) (primep r
))
957 (setq rts
(zq-amm a r p q
)) )
958 ((and (= (gcd r
(1- p
)) 1) (or (= p q
) (= (gcd r p
) 1))) ;; r is coprime to totient(q):
959 (let ((ord (* (1- p
) (truncate q p
))))
960 (setq rts
(list (power-mod a
(inv-mod r ord
) q
))) )) ;; unique solution
962 (let* (($intfaclim
) (rs (get-factor-list r
)))
963 (setq rs
(sort rs
#'< :key
#'car
))
967 #'(lambda (pe) (apply #'(lambda (p e
) (make-list e
:initial-element p
)) pe
))
969 (setq rts
(zq-amm a
(car rs
) p q
))
971 (setq rts
(apply #'nconc
(mapcar #'(lambda (a) (zq-amm a r p q
)) rts
))) ))))
972 (if (and (= p
2) (> q
2) (evenp r
)) ;; this case needs a postprocess (see below)
973 (nconc (mapcar #'(lambda (rt) (- q rt
)) rts
) rts
) ;; add negative solutions
976 ;; Computing r-th roots modulo a prime power p^n, where r is a prime
979 ;; Bach,Shallit - Algorithmic Number Theory, Theorem 7.3.2
981 ;; Shanks - Solved and Unsolved Problems in Number Theory, Th. 46, Lemma 1 to Th. 44
983 ;; The algorithm AMM (Adleman, Manders, Miller) is essentially based on
984 ;; properties of cyclic groups and with the exception of q = 2^n, n > 2
985 ;; it can be applied to any multiplicative group (Z/qZ)* where q = p^n.
987 ;; Doing so, the order q-1 of Fq* in Th. 7.3.2 has to be replaced by the
988 ;; group order totient(q) of (Z/qZ)*.
990 ;; But how to include q = 8,16,32,... ?
991 ;; r > 2: r is prime. There exists a unique solution for all a in (Z/qZ)*.
992 ;; r = 2 (the square root case):
993 ;; - (Z/qZ)* has k = 2 characteristic factors [2,q/4] with [-1,3] as possible
994 ;; factor generators (see Shanks, Lemma 1 to Th. 44).
995 ;; I.e. 3 is of order q/4 and 3^2 = 9 of order q/8.
996 ;; - (Z/qZ)* has totient/2^k = q/8 quadratic residues with 2^k = 4 roots each
997 ;; (see Shanks, Th. 46).
998 ;; - It follows that the subgroup <3> generated by 3 contains all quadratic
999 ;; residues of (Z/qZ)* (which must be all the powers of 9 located in <3>).
1000 ;; - We apply the algorithm AMM for cyclic groups to <3> and compute two
1001 ;; square roots x,y.
1002 ;; - The numbers -x and -y, obviously roots as well, both lie in (-1)*<3>
1003 ;; and therefore they differ from x,y and complete the set of 4 roots.
1005 (defun zq-amm (a r p q
) ;; r,p prime, q = p^n
1006 ;; assume a < q, r <= q
1009 ((= 2 q
) (when (= 1 a
) (list 1)))
1010 ((= 4 q
) (when (or (= 1 a
) (and (= 3 a
) (oddp r
))) (list a
)))
1012 (let ((ord (* (1- p
) (truncate q p
))) ;; group order: totient(q)
1016 (when (/= 1 (mod a
8)) (return-from zq-amm nil
)) ;; a must be a power of 9
1018 ((/= 1 ($jacobi
(mod a p
) p
))
1019 (return-from zq-amm nil
) )
1021 (setq rt
(power-mod a
(ash (+ ord
2) -
2) q
))
1022 (return-from zq-amm
`(,rt
,(- q rt
))) )
1023 ((and (= p q
) (= 5 (mod p
8)))
1024 (let* ((x (ash a
1))
1025 (y (power-mod x
(ash (- p
5) -
3) p
))
1026 (i (mod (* x y y
) p
))
1027 (rt (mod (* a y
(1- i
)) p
)) )
1028 (return-from zq-amm
`(,rt
,(- p rt
))) )))))
1029 (when (= 2 p
) ;; q = 8,16,32,..
1030 (setq ord
(ash ord -
1)) ) ;; factor generator 3 is of order ord/2
1031 (multiple-value-setq (s m
) (truncate ord r
))
1032 (when (and (= 0 m
) (/= 1 (power-mod a s q
))) (return-from zq-amm nil
))
1033 ;; r = 3, first 2 cases:
1036 ((= 1 (setq m
(mod ord
3))) ;; unique solution
1038 `(,(power-mod a
(truncate (1+ (ash ord
1)) 3) q
)) ))
1039 ((= 2 m
) ;; unique solution
1041 `(,(power-mod a
(truncate (1+ ord
) 3) q
)) ))))
1042 ;; compute u,e with ord = u*r^e and r,u coprime:
1045 (multiple-value-setq (u1 m
) (truncate u1 r
))
1046 (when (/= 0 m
) (return))
1047 (setq u u1 e
(1+ e
)) )
1050 (setq rt
(power-mod a
(inv-mod r u
) q
)) ;; unique solution, see Bach,Shallit 7.3.1
1052 (t ;; a is an r-th power
1054 ;; find generator of order r^e:
1055 (if (= p
2) ;; p = 2: then r = 2 (other r: e = 0)
1057 (do ((n 2 ($next_prime n
)))
1058 ((and (= 1 (gcd n q
)) (/= 1 (power-mod n s q
))) ;; n is no r-th power
1059 (setq g
(power-mod n u q
)) )))
1061 om
(power-mod g
(truncate re r
) q
) ) ;; r-th root of unity
1063 ((or (/= 3 r
) (= 0 (setq m
(mod ord
9))))
1064 (let (ar au br bu k ab alpha beta
)
1065 ;; map a from Zq* to C_{r^e} x C_u:
1066 (setq ar
(power-mod a u q
) ;; in C_{r^e}
1067 au
(power-mod a re q
) ) ;; in C_u
1068 ;; compute direct factors of rt:
1069 ;; (the loop in algorithm AMM is effectively a Pohlig-Hellman-reduction, equivalent to zn-dlog)
1070 (setq k
(zn-dlog ar g q re
`((,r
,e
))) ;; g^k = ar, where r|k
1071 br
(power-mod g
(truncate k r
) q
) ;; br^r = g^k (factor of rt in C_{r^e})
1072 bu
(power-mod au
(inv-mod r u
) q
) ) ;; bu^r = au (factor of rt in C_u)
1073 ;; mapping from C_{r^e} x C_u back to Zq*:
1074 (setq ab
(cdr (zn-gcdex1 u re
))
1077 (if (< alpha
0) (incf alpha ord
) (incf beta ord
))
1078 (setq rt
(mod (* (power-mod br alpha q
) (power-mod bu beta q
)) q
)) ))
1079 ;; r = 3, remaining cases:
1081 (setq rt
(power-mod a
(truncate (+ (ash ord
1) 3) 9) q
)) )
1083 (setq rt
(power-mod a
(truncate (+ ord
3) 9) q
)) ))
1084 ;; mult with r-th roots of unity:
1085 (do ((i 1 (1+ i
)) (j 1) (res (list rt
)))
1087 (setq j
(mod (* j om
) q
))
1088 (push (mod (* rt j
) q
) res
) ))))))))
1090 ;; -------------------------------------------------------------------------- ;;
1093 ;; Two variants of gcdex:
1095 ;; returns gcd as first entry:
1096 ;; (zn-gcdex1 12 45) --> (3 4 -1), so 4*12 + -1*45 = 3
1097 (defun zn-gcdex1 (x y
)
1098 (let ((x1 1) (x2 0) (y1 0) (y2 1) q r
)
1099 (do ()((= 0 y
) (list x x1 x2
))
1100 (multiple-value-setq (q r
) (truncate x y
))
1102 (psetf x1 y1 y1
(- x1
(* q y1
)))
1103 (psetf x2 y2 y2
(- x2
(* q y2
))) )))
1105 ;; returns gcd as last entry:
1106 ;; (zn-gcdex 12 45 21) --> (4 -1 0 3), so 4*12 + -1*45 + 0*21 = 3
1107 (defun zn-gcdex (&rest args
)
1108 (let* ((ex (zn-gcdex1 (car args
) (cadr args
)))
1111 (dolist (a (cddr args
) (nconc cs
(list g
)))
1112 (setq ex
(zn-gcdex1 g a
)
1116 cs
(nconc (mapcar #'(lambda (c) (* c c1
)) cs
) (cdr ex
)) ))))
1119 ;; for educational puposes: tables of small residue class rings
1121 (defun zn-table-errchk (n fun
)
1122 (unless (and (fixnump n
) (< 1 n
))
1123 (gf-merror (intl:gettext
1124 "Argument to `~m' must be a small fixnum greater than 1." ) fun
)))
1126 (defmfun $zn_add_table
(n)
1127 (zn-table-errchk n
"zn_add_table")
1128 (do ((i 0 (1+ i
)) res
)
1130 (cons '($matrix simp
) (nreverse res
)) )
1131 (push (mfuncall '$makelist
`((mod) (+ ,i $j
) ,n
) '$j
0 (1- n
)) res
) ))
1133 ;; multiplication table modulo n
1135 ;; The optional g allows to choose subsets of (Z/nZ). Show i with gcd(i,n) = g resp. all i#0.
1136 ;; If n # 1 add row and column headings for better readability.
1138 (defmfun $zn_mult_table
(n &optional g
)
1139 (zn-table-errchk n
"zn_mult_table")
1140 (let ((i0 1) all header choice res
)
1142 ((not g
) (setq g
1))
1143 ((equal g
'$all
) (setq all t
))
1145 (gf-merror (intl:gettext
1146 "`zn_mult_table': The opt second arg must be `all' or a small fixnum." )))
1148 (when (= n g
) (setq i0
0))
1149 (push 1 choice
) ;; creates the headers
1153 (setq choice
(cons '(mlist simp
) (nreverse choice
))) )
1154 (when (or all
(= g
(gcd i n
))) (push i choice
)) )
1155 (when (and header
(= (length choice
) 2))
1156 (return-from $zn_mult_table
) )
1157 (dolist (i (cdr choice
))
1158 (push (mfuncall '$makelist
`((mod) (* ,i $j
) ,n
) '$j choice
) res
) )
1159 (setq res
(nreverse res
))
1160 (when header
(rplaca (cdar res
) "*"))
1161 (cons '($matrix simp
) res
) ))
1163 ;; power table modulo n
1165 ;; The optional g allows to choose subsets of (Z/nZ). Show i with gcd(i,n) = g resp. all i.
1167 (defmfun $zn_power_table
(n &optional g e
)
1168 (zn-table-errchk n
"zn_power_table")
1171 ((not g
) (setq g
1))
1172 ((equal g
'$all
) (setq all t
))
1174 (gf-merror (intl:gettext
1175 "`zn_power_table': The opt second arg must be `all' or a small fixnum." ))))
1177 ((not e
) (setq e
(zn-characteristic-factors n t
)))
1179 (gf-merror (intl:gettext
1180 "`zn_power_table': The opt third arg must be a small fixnum." ))))
1181 (do ((i 0 (1+ i
)) res
)
1183 (when res
(cons '($matrix simp
) (nreverse res
))) )
1184 (when (or all
(= g
(gcd i n
)))
1185 (push (mfuncall '$makelist
`((power-mod) ,i $j
,n
) '$j
1 e
) res
) ))))
1188 ;; $zn_invert_by_lu (m p)
1189 ;; $zn_determinant (m p)
1190 ;; see below: --> galois fields--> interfaces to linearalgebra/lu.lisp
1193 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1196 ;; -----------------------------------------------------------------------------
1197 ;; *** GALOIS FIELDS ***
1199 ;; The following is a revision and improvement of the first part of share/
1200 ;; contrib/gf/gf.mac by Alasdair McAndrew, Fabrizio Caruso and Jacopo D'Aurizio
1201 ;; released under terms of the GPLv2 in 2007.
1203 ;; I would like to thank the original authors for their contribution to Maxima
1204 ;; which allowed me to study, improve and extend the source code.
1206 ;; I would also like to thank Camm Maguire who helped me coding compiler macros
1209 ;; 2012 - 2014, Volker van Nek
1211 (declare-top (special *gf-char
* *gf-exp
* *ef-arith?
*)) ;; modulus $intfaclim see above
1213 (defvar *gf-rat-header
* nil
"header of internal CRE representation")
1215 (defvar *ef-arith?
* nil
"Should extension field arithmetic be used?")
1218 (defvar *gf-char
* 0 "characteristic p")
1219 (defvar *gf-exp
* 0 "exponent n, degree of the reduction polynomial")
1220 (defvar *gf-ord
* 0 "group order, number of units")
1221 (defvar *gf-card
* 0 "cardinality, ring order")
1222 (defvar *gf-red
* nil
"reduction polynomial")
1223 (defvar *gf-prim
* nil
"primitive element")
1224 (defvar *gf-fs-ord
* nil
"ifactors of *gf-ord*")
1225 (defvar *gf-fsx
* nil
"extended factors of *gf-ord*")
1226 (defvar *gf-fsx-base-p
* nil
"*gf-fsx* in base p")
1227 (defvar *gf-x^p-powers
* nil
"x^p^i, i=0,..,n-1")
1229 (defvar *f2-red
* 0 "reduction polynomial") ;; used by ef coeff arith over binary fields
1231 (declaim (fixnum *gf-exp
* *ef-exp
*))
1234 (defvar *ef-exp
* 0 "exponent m, degree of the reduction polynomial")
1235 (defvar *ef-ord
* 0 "group order, number of units")
1236 (defvar *ef-card
* 0 "cardinality, ring order")
1237 (defvar *ef-red
* nil
"reduction polynomial")
1238 (defvar *ef-prim
* nil
"primitive element")
1239 (defvar *ef-fs-ord
* nil
"ifactors of *ef-ord*")
1240 (defvar *ef-fsx
* nil
"extended factors of *ef-ord*")
1241 (defvar *ef-fsx-base-q
* nil
"*ef-fsx* in base q = p^n")
1242 (defvar *ef-x^q-powers
* nil
"x^q^i, i=0,..,m-1")
1244 (defvar *gf-char?
* nil
"Was the characteristic defined?")
1245 (defvar *gf-red?
* nil
"Was the reduction polynomial defined?")
1246 (defvar *gf-irred?
* nil
"Is the reduction polynomial irreducible?")
1247 (defvar *gf-data?
* nil
"gf_set_data called?")
1249 (defvar *ef-red?
* nil
"Was the reduction polynomial defined?")
1250 (defvar *ef-irred?
* nil
"Is the reduction polynomial irreducible?")
1251 (defvar *ef-data?
* nil
"ef_set_data called?")
1253 (defmvar $gf_rat nil
"Return values are rational expressions?" boolean
)
1255 (defmvar $gf_symmetric nil
"A symmetric modulus should be used?" boolean
) ;; deprecated
1256 (defmvar $gf_balanced nil
"A balanced modulus should be used?" boolean
) ;; there is ec_balanced in elliptic_curves.lisp
1258 (putprop '$gf_symmetric
'gf-balanced-info
'assign
)
1260 (defun gf-balanced-info (assign-var arg
)
1261 (declare (ignore assign-var
))
1262 (setq $gf_balanced arg
)
1263 (format t
"`gf_symmetric' is deprecated and replaced by `gf_balanced'.~%The value is bound to `gf_balanced'.") )
1264 ;; temporarily print this message
1267 (defmvar $gf_coeff_limit
256
1268 "`gf_coeff_limit' limits the coeffs when searching for irreducible and primitive polynomials." fixnum
)
1270 (putprop '$gf_coeff_limit
'gf-coeff-check
'assign
)
1272 (defun gf-coeff-check (assign-var arg
)
1273 (declare (ignore assign-var
))
1274 (unless (and (integerp arg
) (> arg
1))
1275 (gf-merror (intl:gettext
1276 "`gf_coeff_limit': Assignment ignored. Value must be an integer greater than 1.~%" ))))
1278 (defmvar $gf_cantor_zassenhaus t
"Should the Cantor-Zassenhaus algorithm be used?" boolean
)
1280 (defmvar $ef_coeff_mult nil
)
1281 (defmvar $ef_coeff_add nil
)
1282 (defmvar $ef_coeff_inv nil
)
1283 (defmvar $ef_coeff_exp nil
)
1285 (defmvar $gf_powers nil
)
1286 (defmvar $gf_logs nil
)
1287 (defmvar $gf_zech_logs nil
)
1288 (defvar *gf-powers
* nil
"alpha^i, i=0,..,ord-1 where alpha is a primitive element")
1289 (defvar *gf-logs?
* nil
"Were the power and log tables calculated?")
1292 ;; contains parts of merror.lisp/merror but avoids "To debug this ...".
1294 (defun gf-merror (sstring &rest l
)
1295 (setq $error
`((mlist) ,sstring
,@ l
))
1296 (and $errormsg
($errormsg
))
1297 (fresh-line *standard-output
*)
1298 (format t
(intl:gettext
"~& -- an error.~%"))
1299 (throw 'macsyma-quit
'maxima-error
) )
1302 (defun gf-char?
(fun)
1304 (gf-merror (intl:gettext
"`~m': The characteristic is not defined yet.") fun
) ))
1306 (defun gf-red?
(fun)
1308 (gf-merror (intl:gettext
"`~m': The reduction polynomial is not defined yet.") fun
) ))
1310 (defun gf-data?
(fun)
1312 (gf-merror (intl:gettext
"`~m': gf_set_data called?") fun
) ))
1314 (defun gf-field?
(fun)
1315 (if (and (gf-data? fun
) *gf-irred?
*) t
1316 (gf-merror (intl:gettext
"`~m': The reduction polynomial is not irreducible.") fun
) ))
1319 (defun ef-gf-field?
(fun)
1320 (if (and *gf-data?
* *gf-irred?
*) t
1321 (gf-merror (intl:gettext
"`~m': The base field is not defined yet.") fun
) ))
1323 (defun ef-red?
(fun)
1324 (if (and (ef-gf-field? fun
) *ef-red?
*) t
1325 (gf-merror (intl:gettext
"`~m': The reduction polynomial is not defined yet.") fun
) ))
1327 (defun ef-data?
(fun)
1328 (if (and (ef-gf-field? fun
) *ef-data?
*) t
1329 (gf-merror (intl:gettext
"`~m': ef_set_data called?") fun
) ))
1331 (defun ef-field?
(fun)
1332 (if (and (ef-data? fun
) *ef-irred?
*) t
1333 (gf-merror (intl:gettext
"`~m': The extension is no field.") fun
) ))
1335 ;; -----------------------------------------------------------------------------
1338 ;; basic coefficient arithmetic ------------------------------------------------
1341 ;; optimize the fixnum cases
1343 (defmacro maybe-char-is-fixnum-let
(binds &body body
)
1344 `(if (or (and (not *ef-arith?
*) (typep *gf-char
* 'fixnum
))
1345 (and *ef-arith?
* (typep *gf-card
* 'fixnum
)) )
1347 (declare (fixnum ,@(mapcar #'(lambda (x) (car x
)) binds
)))
1350 (declare (integer ,@(mapcar #'(lambda (x) (car x
)) binds
)))
1353 ;; basic coefficient functions and compiler macros
1355 ;; gf coefficient arith :
1357 ;; *ef-arith?* controls coefficient arithmetic. If *ef-arith?* is false,
1358 ;; coeffs are elements of Zp, where p is the defined characteristic *gf-char*.
1359 ;; If *ef-arith?* is true, coeffs are interpreted as the integer representation
1360 ;; of a polynomial over Zp[x] reduced by the irreducible polynomial *gf-red*.
1365 (maybe-char-is-fixnum-let ((c c
))
1367 ((= 0 c
) (gf-merror (intl:gettext
"gf coefficient inversion: Quotient by zero")))
1368 (t (inv-mod c
*gf-char
*)) )))) ; *gf-char* is prime
1370 (defun gf-cpow (c n
)
1373 (maybe-char-is-fixnum-let ((c c
))
1374 (power-mod c n
*gf-char
*) )))
1379 (maybe-char-is-fixnum-let ((c c
))
1380 (mod c
*gf-char
*) )))
1382 (defun gf-ctimes (a b
)
1385 (maybe-char-is-fixnum-let ((a a
)(b b
))
1386 (mod (* a b
) *gf-char
*) )))
1388 (defun gf-cplus-b (a b
) ;; assumes that both 0 <= a,b < *gf-char*
1390 (*ef-arith?
* (ef-cplus-b a b
))
1391 (t (maybe-char-is-fixnum-let ((a a
)(b b
))
1393 (if (< (the integer s
) *gf-char
*)
1395 (- (the integer s
) *gf-char
*) ))))))
1397 (defun gf-cminus-b (c) ;; assumes that 0 <= c < *gf-char*
1401 (*ef-arith?
* (ef-cminus-b c
))
1402 (t (maybe-char-is-fixnum-let ((c c
))
1403 (- *gf-char
* c
) ))))
1405 ;; ef coefficient arith :
1408 (declare (integer c
))
1410 ((= 0 c
) (gf-merror (intl:gettext
"ef coefficient inversion: Quotient by zero")))
1411 ($ef_coeff_inv
(mfuncall '$ef_coeff_inv c
))
1412 (*gf-logs?
* (ef-cinv-by-table c
))
1413 ((= 2 *gf-char
*) (f2-inv c
))
1414 (t (let ((*ef-arith?
*))
1415 (gf-x2n (gf-inv (gf-n2x c
) *gf-red
*)) ))))
1417 (defun ef-cpow (c n
)
1419 ($ef_coeff_exp
(mfuncall '$ef_coeff_exp c n
))
1420 (*gf-logs?
* (ef-cpow-by-table c n
))
1421 ((= 2 *gf-char
*) (f2-pow c n
))
1422 (t (let ((*ef-arith?
*))
1423 (gf-x2n (gf-pow (gf-n2x c
) n
*gf-red
*)) ))))
1426 (declare (integer c
))
1431 ((= 2 *gf-char
*) (f2-red c
))
1432 (t (let ((*ef-arith?
*))
1433 (gf-x2n (gf-nred (gf-n2x c
) *gf-red
*)) ))))
1435 (setq c
(ef-cmod (abs c
)))
1436 (let ((*ef-arith?
* t
)) (gf-ctimes (1- *gf-char
*) c
)) )))
1438 (defun ef-ctimes (a b
)
1440 ($ef_coeff_mult
(mfuncall '$ef_coeff_mult a b
))
1441 (*gf-logs?
* (ef-ctimes-by-table a b
))
1442 ((= 2 *gf-char
*) (f2-times a b
))
1443 (t (let ((*ef-arith?
*))
1444 (gf-x2n (gf-times (gf-n2x a
) (gf-n2x b
) *gf-red
*)) ))))
1446 (defun ef-cplus-b (a b
)
1448 ((= 2 *gf-char
*) (logxor a b
))
1449 ($ef_coeff_add
(mfuncall '$ef_coeff_add a b
))
1450 (*gf-logs?
* (ef-cplus-by-table a b
))
1451 (t (let ((*ef-arith?
*))
1452 (gf-x2n (gf-nplus (gf-n2x a
) (gf-n2x b
))) ))))
1454 (defun ef-cminus-b (a)
1458 ($ef_coeff_mult
(mfuncall '$ef_coeff_mult
(1- *gf-char
*) a
))
1459 (*gf-logs?
* (ef-cminus-by-table a
))
1460 (t (let ((*ef-arith?
*))
1461 (gf-x2n (gf-nminus (gf-n2x a
))) ))))
1463 ;; ef coefficient arith by lookup:
1465 (defun ef-ctimes-by-table (c d
)
1466 (declare (fixnum c d
))
1468 ((or (= 0 c
) (= 0 d
)) 0)
1469 (t (let ((cd (+ (the fixnum
(svref $gf_logs c
))
1470 (the fixnum
(svref $gf_logs d
)) )))
1471 (svref $gf_powers
(if (< (the integer cd
) *gf-ord
*) cd
(- cd
*gf-ord
*))) ))))
1473 (defun ef-cminus-by-table (c)
1474 (declare (fixnum c
))
1478 (t (let ((e (ash *gf-ord
* -
1))) (declare (fixnum e
))
1479 (setq c
(svref $gf_logs c
))
1480 (svref $gf_powers
(the fixnum
(if (< c e
) (+ c e
) (- c e
)))) ))))
1482 (defun ef-cinv-by-table (c)
1483 (declare (fixnum c
))
1485 ((= 0 c
) (gf-merror (intl:gettext
"ef coefficient inversion: Quotient by zero")))
1486 (t (svref $gf_powers
(- *gf-ord
* (the fixnum
(svref $gf_logs c
))))) ))
1488 (defun ef-cplus-by-table (c d
)
1489 (declare (fixnum c d
))
1493 (t (setq c
(svref $gf_logs c
) d
(aref $gf_logs d
))
1494 (let ((z (svref $gf_zech_logs
(the fixnum
(if (< d c
) (+ *gf-ord
* (- d c
)) (- d c
))))))
1497 (svref $gf_powers
(the fixnum
(if (> z
*gf-ord
*) (- z
*gf-ord
*) z
))) )
1500 (defun ef-cpow-by-table (c n
)
1501 (declare (fixnum c n
))
1505 (t (svref $gf_powers
1506 (mod (* n
(the fixnum
(svref $gf_logs c
))) *gf-ord
*) )) ))
1509 (defun gf-pow-by-table (x n
) ;; table lookup uses current *gf-red* for reduction
1510 (declare (fixnum n
))
1512 ((= 0 n
) (list 0 1))
1514 (t (svref *gf-powers
*
1515 (mod (* n
(the fixnum
(svref $gf_logs
(gf-x2n x
)))) *gf-ord
*) )) ))
1518 ;; ef coefficient arith for binary base fields:
1521 (declare (optimize (speed 3) (safety 0)))
1522 (let* ((red *f2-red
*)
1523 (ilen (integer-length red
))
1525 (declare (fixnum e ilen
))
1527 (setq e
(- (integer-length a
) ilen
))
1528 (when (< e
0) (return a
))
1529 (setq a
(logxor a
(ash red e
))) )))
1531 (defun f2-times (a b
)
1532 (declare (optimize (speed 3) (safety 0)))
1533 (let* ((ilen (integer-length b
))
1534 (a1 (ash a
(1- ilen
)))
1536 (do ((i (- ilen
2) (1- i
)) (k 0))
1537 ((< i
0) (f2-red ab
))
1538 (declare (fixnum i k
))
1545 (defun f2-pow (a n
) ;; assume n >= 0
1546 (declare (optimize (speed 3) (safety 0))
1553 (setq res
(if res
(f2-times a res
) a
))
1555 (return-from f2-pow res
) ))
1557 a
(f2-times a a
)) ))))
1560 (declare (optimize (speed 3) (safety 0)))
1562 (gf-merror (intl:gettext
"f2 arithmetic: Quotient by zero")) )
1563 (let ((b1 1) (a *f2-red
*) (a1 0) q r
)
1566 (multiple-value-setq (q r
) (f2-divide a b
))
1568 (psetf a1 b1 b1
(logxor (f2-times q b1
) a1
)) )))
1570 (defun f2-divide (a b
)
1571 (declare (optimize (speed 3) (safety 0)))
1574 (gf-merror (intl:gettext
"f2 arithmetic: Quotient by zero")) )
1575 ((= a
0) (values 0 0))
1577 (let ((ilen (integer-length b
))
1580 (declare (fixnum e ilen
))
1581 (do () ((= a
0) (values q
0))
1582 (setq e
(- (integer-length a
) ilen
))
1583 (when (< e
0) (return (values q a
)))
1584 (setq q
(logxor q
(ash 1 e
)))
1585 (setq a
(logxor a
(ash b e
))) )))))
1587 ;; -------------------------------------------------------------------------- ;;
1590 #-gcl
(eval-when (:compile-toplevel
:load-toplevel
:execute
)
1592 (define-compiler-macro gf-cmod
(a)
1596 ((and (typep *gf-char
* 'fixnum
) (typep ,a
'fixnum
)) ;; maybe a > *gf-char*
1597 (let ((x ,a
) (z *gf-char
*)) (declare (fixnum x z
))
1598 (the fixnum
(mod x z
)) ))
1600 (mod (the integer
,a
) *gf-char
*) )))
1602 (define-compiler-macro gf-ctimes
(a b
)
1606 ((typep *gf-char
* 'fixnum
)
1607 (let ((x ,a
) (y ,b
) (z *gf-char
*)) (declare (fixnum x y z
))
1608 (the fixnum
(mod (* x y
) z
)) ))
1610 (mod (* (the integer
,a
) (the integer
,b
)) *gf-char
*) )))
1612 (define-compiler-macro gf-cplus-b
(a b
) ;; assumes that both 0 <= a,b < *gf-char*
1615 (ef-cplus-b ,a
,b
) )
1616 ((typep *gf-char
* 'fixnum
)
1617 (let ((x ,a
) (y ,b
) (z *gf-char
*) (s 0)) (declare (fixnum x y z
) (integer s
))
1618 (setq s
(the integer
(+ x y
)))
1619 (if (< s z
) s
(- s z
)) ))
1621 (let ((x (+ (the integer
,a
) (the integer
,b
)))) (declare (integer x
))
1622 (if (< x
*gf-char
*) x
(- x
*gf-char
*)) ))))
1624 (define-compiler-macro gf-cminus-b
(a) ;; assumes that 0 <= a < *gf-char*
1629 ((typep *gf-char
* 'fixnum
)
1630 (let ((x ,a
) (z *gf-char
*)) (declare (fixnum x z
))
1631 (the fixnum
(- z x
)) ))
1633 (- *gf-char
* (the integer
,a
)) )))
1636 #+gcl
(eval-when (compile load eval
)
1638 (push '((fixnum fixnum
) fixnum
#.
(compiler::flags compiler
::rfa
)
1639 "(fixnum)(((long long)(#0))%((long long)(#1)))" )
1640 (get 'i%
'compiler
::inline-always
) )
1641 (push '((fixnum fixnum fixnum
) fixnum
#.
(compiler::flags compiler
::rfa
)
1642 "(fixnum)((((long long)(#0))*((long long)(#1)))%((long long)(#2)))" )
1643 (get '*%
'compiler
::inline-always
) )
1644 (push '((fixnum fixnum fixnum
) fixnum
#.
(compiler::flags compiler
::rfa compiler
::set
)
1645 "@02;({long long _t=((long long)(#0))+((long long)(#1)),_w=((long long)(#2));_t<_w ? (fixnum)_t : (fixnum)(_t - _w);})" )
1646 (get '+%b
'compiler
::inline-always
) )
1647 (push '((fixnum fixnum
) fixnum
#.
(compiler::flags compiler
::rfa
)
1648 "(fixnum)(((long long)(#1))-((long long)(#0)))" )
1649 (get 'neg%b
'compiler
::inline-always
) )
1651 (setf (get 'i%
'compiler
::return-type
) t
)
1652 (setf (get '*%
'compiler
::return-type
) t
)
1653 (setf (get '+%b
'compiler
::return-type
) t
)
1654 (setf (get 'neg%b
'compiler
::return-type
) t
)
1656 (si::define-compiler-macro gf-cmod
(a)
1660 ((and (typep *gf-char
* 'fixnum
) (typep ,a
'fixnum
) (plusp ,a
)) ;; maybe a > *gf-char*
1661 (let ((x ,a
) (z *gf-char
*)) (declare (fixnum x z
))
1664 (mod (the integer
,a
) *gf-char
*) )))
1666 (si::define-compiler-macro gf-ctimes
(a b
) ;; assume that 0 <= a,b :
1671 ',(if (< (integer-length most-positive-fixnum
) 32) `fixnum
`(signed-byte 32)) )
1672 (let ((x ,a
) (y ,b
) (z *gf-char
*)) (declare (fixnum x y z
))
1675 (mod (* (the integer
,a
) (the integer
,b
)) *gf-char
*) )))
1677 (si::define-compiler-macro gf-cplus-b
(a b
) ;; assume that both 0 <= a,b < *gf-char* :
1680 (ef-cplus-b ,a
,b
) )
1682 ',(if (< (integer-length most-positive-fixnum
) 63) `fixnum
`(signed-byte 63)) )
1683 (let ((x ,a
) (y ,b
) (z *gf-char
*)) (declare (fixnum x y z
))
1686 (let ((x (+ (the integer
,a
) (the integer
,b
)))) (declare (integer x
))
1687 (if (< x
*gf-char
*) x
(- x
*gf-char
*)) ))))
1689 (si::define-compiler-macro gf-cminus-b
(a) ;; assume that 0 <= a < *gf-char* :
1694 ((typep *gf-char
* 'fixnum
)
1695 (let ((x ,a
) (z *gf-char
*)) (declare (fixnum x z
))
1698 (- *gf-char
* (the integer
,a
)) )))
1701 ;; -----------------------------------------------------------------------------
1704 ;; setting the finite field and retrieving basic informations ------------------
1707 (defmfun $gf_set
(p &optional a1 a2 a3
) ;; deprecated
1708 (format t
"`gf_set' is deprecated. ~%~\
1709 The user is asked to use `gf_set_data' instead.~%" )
1711 (format t
"In future versions `gf_set_data' will only accept two arguments.~%") )
1712 ($gf_set_data p a1 a2 a3
) )
1715 (defmfun $gf_set_data
(p &optional a1 a2 a3
) ;; opt: *gf-exp*, *gf-red*, *gf-fs-ord*
1716 (declare (ignore a2 a3
)) ;; remove a2 a3 in next versions
1717 (let ((*ef-arith?
*))
1718 (unless (and (integerp p
) (primep p
))
1719 (gf-merror (intl:gettext
"`gf_set_data': Field characteristic must be a prime number.")) )
1723 (when a1
;; exponent or reduction poly
1726 (unless (and (fixnump a1
) (plusp a1
))
1727 (gf-merror (intl:gettext
"`gf_set_data': The exponent must be a positive fixnum.")) )
1728 (setq *gf-exp
* a1
) )
1730 (setq *gf-red
* (gf-p2x-red a1
"gf_set_data")
1731 *gf-exp
* (car *gf-red
*)
1732 *gf-irred?
* (gf-irr-p *gf-red
* *gf-char
* *gf-exp
*) )) ))
1734 (gf-set-rat-header) ;; CRE-headers
1736 (unless *gf-red
* ;; find irreducible reduction poly:
1737 (setq *gf-red
* (if (= 1 *gf-exp
*) (list 1 1) (gf-irr p
*gf-exp
*))
1740 (when (= *gf-char
* 2) (setq *f2-red
* (gf-x2n *gf-red
*)))
1742 (setq *gf-card
* (expt p
*gf-exp
*)) ;; cardinality #(F)
1744 (setq *gf-ord
* ;; group order #(F*)
1746 ((= 1 *gf-exp
*) (1- p
))
1747 ((not *gf-irred?
*) (gf-group-order *gf-char
* *gf-red
*))
1748 (t (1- (expt p
*gf-exp
*))) ))
1750 (fs (get-factor-list *gf-ord
*)) )
1751 (setq *gf-fs-ord
* (sort fs
#'< :key
#'car
)) ) ;; .. [pi, ei] ..
1753 (when *gf-irred?
* (gf-precomp))
1755 (setq *gf-prim
* ;; primitive element
1758 (if (= 2 *gf-char
*) (list 0 1)
1759 (list 0 (zn-primroot p
*gf-ord
* (mapcar #'car
*gf-fs-ord
*))) )) ;; .. pi .. (factors_only:true)
1761 (if *gf-irred?
* (gf-prim) '$unknown
) )))
1763 (setq *gf-char?
* t
*gf-red?
* t
*gf-data?
* t
) ;; global flags
1764 ($gf_get_data
) )) ;; data structure
1767 (defun gf-set-rat-header ()
1769 (setq *gf-rat-header
* (car ($rat
'$x
))) ))
1771 (defun gf-p2x-red (p fun
)
1772 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
1773 (let* ((modulus) (x (car (prep1 p
))))
1774 (unless (and (listp x
)
1775 (every #'numberp
(setq x
(cdr x
))) )
1776 (gf-merror (intl:gettext
"`~m': Not suitable as reduction polynomial: ~m") fun p
) )
1778 (unless (and (typep (car x
) 'fixnum
) (plusp (car x
)))
1779 (gf-merror (intl:gettext
"`~m': The exponent must be a positive fixnum.") fun
) )
1780 (unless (eql 1 (cadr x
))
1781 (gf-merror (intl:gettext
"`~m': A monic reduction polynomial is assumed.") fun
) )
1785 (defmfun $ef_set_data
(red)
1786 (ef-gf-field?
"ef_set_data")
1788 (let ((*ef-arith?
* t
))
1789 (setq *ef-red
* (gf-p2x-red red
"ef_set_data")
1790 *ef-exp
* (car *ef-red
*)
1791 *ef-card
* (expt *gf-card
* *ef-exp
*)
1792 *ef-irred?
* (gf-irr-p *ef-red
* *gf-card
* *ef-exp
*)
1793 *ef-ord
* (if *ef-irred?
*
1795 (gf-group-order *gf-card
* *ef-red
*) ))
1797 (fs (get-factor-list *ef-ord
*)) )
1798 (setq *ef-fs-ord
* (sort fs
#'< :key
#'car
)) )
1799 (when *ef-irred?
* (ef-precomp))
1802 *ef-prim
* (if (= 1 *ef-exp
*)
1803 (list 0 (let ((*ef-arith?
*)) (gf-x2n *gf-prim
*)))
1804 (if *ef-irred?
* (ef-prim) '$unknown
) )))
1808 (defstruct (gf-data (:print-function gf-data-short-print
))
1809 char exp red prim card
1810 ord fs-ord fsx fsx-base-p x^p-powers
)
1812 (defun gf-data-short-print (struct stream i
)
1813 (declare (ignore struct i
))
1814 (format stream
"Structure [GF-DATA]") ) ;; wxMaxima returns this
1815 ;; terminal should return this too
1817 ;; returns a struct containing all data necessary to use gf_set_again (see below)
1818 (defmfun $gf_get_data
()
1819 (gf-data?
"gf_get_data")
1821 :char
*gf-char
* ; characteristic
1822 :exp
*gf-exp
* ; exponent
1823 :red
*gf-red
* ; reduction
1824 :prim
*gf-prim
* ; primitive
1825 :card
*gf-card
* ; cardinality
1826 :ord
*gf-ord
* ; order
1827 :fs-ord
*gf-fs-ord
* ; factors of order
1828 :fsx
*gf-fsx
* ; extended factors of order
1829 :fsx-base-p
*gf-fsx-base-p
* ; extended factors in base p
1830 :x^p-powers
*gf-x^p-powers
* )) ; pre-calculated powers
1832 (defstruct (ef-data (:print-function ef-data-short-print
))
1834 ord fs-ord fsx fsx-base-q x^q-powers
)
1836 (defun ef-data-short-print (struct stream i
)
1837 (declare (ignore struct i
))
1838 (format stream
"Structure [EF-DATA]") )
1840 (defmfun $ef_get_data
()
1841 (ef-data?
"ef_get_data")
1843 :exp
*ef-exp
* ; exponent
1844 :red
*ef-red
* ; reduction
1845 :prim
*ef-prim
* ; primitive
1846 :card
*ef-card
* ; cardinality
1847 :ord
*ef-ord
* ; order
1848 :fs-ord
*ef-fs-ord
* ; factors of order
1849 :fsx
*ef-fsx
* ; extended factors of order
1850 :fsx-base-q
*ef-fsx-base-q
* ; extended factors in base q
1851 :x^q-powers
*ef-x^q-powers
* )) ; pre-calculated powers
1853 (defmfun $gf_info
(&optional
(t? t
))
1854 (gf-data?
"gf_info")
1855 (let ((no-prim (or (null *gf-prim
*) (equal *gf-prim
* '$unknown
)))
1858 "characteristic = ~a~:[, ~;~%~]~\
1859 reduction polynomial = ~a~:[, ~;~%~]~\
1860 primitive element = ~a~:[, ~;~%~]~\
1861 nr of elements = ~a~:[, ~;~%~]~\
1862 nr of units = ~a~:[, ~;~]~\
1863 ~:[~;~%nr of primitive elements = ~a~] ~%"
1865 (mfuncall '$string
(gf-x2p *gf-red
*)) t?
1869 (gf-x2p *gf-prim
*) )) t?
1871 *gf-ord
* (or t? no-prim
) (not no-prim
)
1872 (totient-by-fs-n *gf-fs-ord
*) )))
1874 (defun totient-by-fs-n (fs-n)
1876 (dolist (f fs-n phi
)
1877 (setq p
(car f
) e
(cadr f
))
1878 (setq phi
(* phi
(1- p
) (expt p
(1- e
)))) )))
1880 (defmfun $gf_infolist
() ;; enables testing gf_set_data in rtest
1881 (gf-data?
"gf_infolist")
1882 (let ((*ef-arith?
*))
1886 ,(if (or (null *gf-prim
*) (equal *gf-prim
* '$unknown
))
1888 (gf-x2p *gf-prim
*) )
1892 (defmfun $ef_info
(&optional
(t? t
))
1893 (ef-data?
"ef_info")
1894 (let ((no-prim (or (null *ef-prim
*) (equal *ef-prim
* '$unknown
)))
1897 "reduction polynomial = ~a~:[, ~;~%~]~\
1898 primitive element = ~a~:[, ~;~%~]~\
1899 nr of elements = ~a~:[, ~;~%~]~\
1900 nr of units = ~a~:[, ~;~]~\
1901 ~:[~;~%nr of primitive elements = ~a~] ~%"
1902 (mfuncall '$string
(gf-x2p *ef-red
*)) t?
1906 (gf-x2p *ef-prim
*) )) t?
1908 *ef-ord
* (or t? no-prim
) (not no-prim
)
1909 (totient-by-fs-n *ef-fs-ord
*) )))
1911 (defmfun $ef_infolist
() ;; enables testing ef_set_data in rtest
1912 (ef-data?
"ef_infolist")
1913 (let ((*ef-arith?
* t
))
1916 ,(if (or (null *ef-prim
*) (equal *ef-prim
* '$unknown
))
1918 (gf-x2p *ef-prim
*) )
1923 (defmfun $gf_unset
()
1924 (setq $gf_powers nil $gf_logs nil $gf_zech_logs nil
*gf-powers
* nil
*gf-logs?
* nil
1926 $ef_coeff_mult nil $ef_coeff_add nil $ef_coeff_inv nil $ef_coeff_exp nil
1927 *gf-rat-header
* nil
*gf-char
* 0
1928 *gf-exp
* 1 *gf-ord
* 0 *gf-card
* 0 ;; *gf-exp* = 1 when gf_set_data has no optional arg
1929 *gf-red
* nil
*f2-red
* 0 *gf-prim
* nil
1930 *gf-fs-ord
* nil
*gf-fsx
* nil
*gf-fsx-base-p
* nil
*gf-x^p-powers
* nil
1931 *gf-char?
* nil
*gf-red?
* nil
*gf-irred?
* nil
*gf-data?
* nil
)
1934 (defmfun $ef_unset
()
1935 (setq *ef-exp
* 0 *ef-ord
* 0 *ef-card
* 0
1936 *ef-red
* nil
*ef-prim
* nil
1937 *ef-fs-ord
* nil
*ef-fsx
* nil
*ef-fsx-base-q
* nil
*ef-x^q-powers
* nil
1938 *ef-red?
* nil
*ef-irred?
* nil
*ef-data?
* nil
)
1943 ;; Just set characteristic and reduction poly to allow basic arithmetics on the fly.
1944 (defmfun $gf_minimal_set
(p &optional
(red))
1945 (unless (and (integerp p
) (primep p
))
1946 (gf-merror (intl:gettext
"First argument to `gf_minimal_set' must be a prime number.")) )
1951 (let ((*ef-arith?
*))
1953 (setq *gf-red
* (gf-p2x-red red
"gf_minimal_set")
1955 *gf-exp
* (car *gf-red
*) ))
1956 (format nil
"characteristic = ~a, reduction polynomial = ~a"
1958 (if red
(mfuncall '$string
(gf-x2p *gf-red
*)) "false") )))
1961 (defmfun $ef_minimal_set
(red)
1962 (ef-gf-field?
"ef_minimal_set")
1964 (let ((*ef-arith?
* t
))
1966 (setq *ef-red
* (gf-p2x-red red
"ef_minimal_set")
1967 *ef-exp
* (car *ef-red
*)
1969 (format nil
"reduction polynomial = ~a"
1970 (if red
(mfuncall '$string
(gf-x2p *ef-red
*)) "false") )))
1973 (defmfun $gf_characteristic
()
1974 (gf-char?
"gf_characteristic")
1977 (defmfun $gf_exponent
()
1978 (gf-red?
"gf_exponent")
1981 (defmfun $gf_reduction
()
1982 (gf-red?
"gf_reduction")
1983 (when *gf-red
* (let ((*ef-arith?
*)) (gf-x2p *gf-red
*))) )
1985 (defmfun $gf_cardinality
()
1986 (gf-data?
"gf_cardinality")
1990 (defmfun $ef_exponent
()
1991 (ef-red?
"ef_exponent")
1994 (defmfun $ef_reduction
()
1995 (ef-red?
"ef_reduction")
1996 (when *ef-red
* (let ((*ef-arith?
* t
)) (gf-x2p *ef-red
*))) )
1998 (defmfun $ef_cardinality
()
1999 (ef-data?
"ef_cardinality")
2003 ;; Reuse data and results from a previous gf_set_data
2004 (defmfun $gf_set_again
(data)
2005 (unless (gf-data-p data
)
2006 (gf-merror (intl:gettext
2007 "Argument to `gf_set_again' must be a return value of `gf_set_data'." )))
2010 (setq *gf-char
* (gf-data-char data
)
2011 *gf-exp
* (gf-data-exp data
)
2012 *gf-red
* (gf-data-red data
)
2013 *gf-prim
* (gf-data-prim data
)
2014 *gf-card
* (gf-data-card data
)
2015 *gf-ord
* (gf-data-ord data
)
2016 *gf-fs-ord
* (gf-data-fs-ord data
)
2017 *gf-fsx
* (gf-data-fsx data
)
2018 *gf-fsx-base-p
* (gf-data-fsx-base-p data
)
2019 *gf-x^p-powers
* (gf-data-x^p-powers data
)
2020 *gf-irred?
* (= *gf-ord
* (1- *gf-card
*))
2025 (defmfun $ef_set_again
(data)
2026 (ef-gf-field?
"ef_set_again")
2027 (unless (ef-data-p data
)
2028 (gf-merror (intl:gettext
2029 "Argument to `ef_set_again' must be a return value of `ef_set_data'." )))
2031 (setq *ef-exp
* (ef-data-exp data
)
2032 *ef-red
* (ef-data-red data
)
2033 *ef-prim
* (ef-data-prim data
)
2034 *ef-card
* (ef-data-card data
)
2035 *ef-ord
* (ef-data-ord data
)
2036 *ef-fs-ord
* (ef-data-fs-ord data
)
2037 *ef-fsx
* (ef-data-fsx data
)
2038 *ef-fsx-base-q
* (ef-data-fsx-base-q data
)
2039 *ef-x^q-powers
* (ef-data-x^q-powers data
)
2040 *ef-irred?
* (= *ef-ord
* (1- *ef-card
*))
2044 ;; -----------------------------------------------------------------------------
2047 ;; lookup tables ---------------------------------------------------------------
2050 (defmfun $gf_make_arrays
()
2051 (format t
"`gf_make_arrays' is deprecated. ~%~\
2052 The user is asked to use `gf_make_logs' instead.~%" )
2055 (defmfun $gf_make_logs
() ;; also zech-logs and antilogs
2056 (gf-field?
"gf_make_logs")
2057 (let ((*ef-arith?
*)) (gf-make-logs)) )
2059 (defun gf-make-logs ()
2060 (unless (typep *gf-ord
* 'fixnum
)
2061 (gf-merror (intl:gettext
"`gf_make_logs': group order must be a fixnum.")) )
2062 (let ((x (list 0 1)) (ord *gf-ord
*) (primx *gf-prim
*) (red *gf-red
*))
2063 (declare (fixnum ord
))
2065 ;; power table of the field, where the i-th element is (the numerical
2066 ;; equivalent of) the field element e^i, where e is a primitive element
2068 (setq $gf_powers
(make-array (1+ ord
) :element-type
'integer
)
2069 *gf-powers
* (make-array (1+ ord
) :element-type
'list
:initial-element nil
) )
2070 (setf (svref $gf_powers
0) 1
2071 (svref *gf-powers
* 0) (list 0 1) )
2074 (declare (fixnum i
))
2075 (setq x
(gf-times x primx red
))
2076 (setf (svref $gf_powers i
) (gf-x2n x
)
2077 (svref *gf-powers
* i
) x
))
2079 ;; log table: the inverse lookup of the power table
2081 (setq $gf_logs
(make-array (1+ ord
) :initial-element nil
))
2084 (declare (fixnum i
))
2085 (setf (svref $gf_logs
(svref $gf_powers i
)) i
) )
2087 ;; zech-log table: lookup table for efficient addition
2089 (setq $gf_zech_logs
(make-array (1+ ord
) :initial-element nil
))
2090 (do ((i 0 (1+ i
)) (one (list 0 1)))
2092 (declare (fixnum i
))
2093 (setf (svref $gf_zech_logs i
)
2094 (svref $gf_logs
(gf-x2n (gf-plus (svref *gf-powers
* i
) one
))) ))
2097 `((mlist simp
) ,$gf_powers
,$gf_logs
,$gf_zech_logs
) ))
2099 (defun gf-clear-tables ()
2100 (setq $gf_powers nil
2105 ;; -----------------------------------------------------------------------------
2108 ;; converting to/from internal representation ----------------------------------
2110 ;; user level <---> internal
2112 ;; integer # 0 (0 integer') where integer' = mod(integer, *gf-char*)
2114 ;; x^4 + 3*x^2 + 4 (4 1 2 3 0 4)
2116 ;; This representation uses the term part of the internal CRE representation.
2117 ;; The coeffcients are exclusively positive: 1, 2, ..., (*gf-char* -1)
2118 ;; Header informations are stored in *gf-rat-header*.
2120 ;; gf_set_data(5, 4)$
2121 ;; :lisp `(,*gf-char* ,*gf-exp*)
2123 ;; p : x^4 + 3*x^2 - 1$
2125 ;; ((MRAT SIMP ($X) (X33303)) (X33303 4 1 2 3 0 -1) . 1)
2126 ;; :lisp (gf-p2x $p)
2128 ;; :lisp *gf-rat-header*
2129 ;; (MRAT SIMP ($X) (X33303))
2131 ;; Remark: I compared the timing results of the arithmetic functions using this
2132 ;; data structure to arithmetics using an array implementation and in case of
2133 ;; modulus 2 to an implementation using bit-arithmetics over integers.
2134 ;; It turns out that in all cases the timing advantages of other data structures
2135 ;; were consumed by conversions from/to the top-level.
2136 ;; So for sparse polynomials the CRE representation seems to fit best.
2140 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2141 (setq p
(car (let ((modulus)) (prep1 p
))))
2146 (t (setq p
(gf-cmod p
))
2147 (if (= p
0) nil
(list 0 p
)) )))
2149 (setq p
(gf-mod (cdr p
)))
2150 (if (typep (car p
) 'fixnum
)
2152 (gf-merror (intl:gettext
"Exponents are limited to fixnums.")) ))))
2155 ;; version of gf-p2x that doesn't apply mod reduction
2157 (defun gf-p2x-raw (p)
2158 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2159 (setq p
(car (let ((modulus)) (prep1 p
))))
2161 ((integerp p
) (if (= 0 p
) nil
(list 0 p
)))
2163 (unless (every #'numberp p
)
2164 (gf-merror (intl:gettext
"gf: polynomials must be univariate.")) )
2169 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2173 ((= 0 (the fixnum
(car x
))) (gf-cp2smod (cadr x
)))
2174 (t (gf-np2smod x
)) ))
2179 ;; depending on $gf_rat gf-x2p returns a CRE or a ratdisrepped expression
2182 #+ (or ccl ecl gcl sbcl
) (declare (optimize (speed 3) (safety 0)))
2184 `(,*gf-rat-header
* ,x .
1)
2185 `(,*gf-rat-header
* ,(cons (caar (cdddr *gf-rat-header
*)) x
) .
1) ))
2187 (defun gf-disrep (x &optional
(var '$x
))
2188 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2190 (maybe-char-is-fixnum-let ((c 0))
2191 (do ((not-plus?
(null (cddr x
))) p
(e 0))
2192 ((null x
) (if not-plus?
(car p
) (cons '(mplus simp
) p
)))
2193 (declare (fixnum e
))
2194 (setq e
(car x
) c
(cadr x
) x
(cddr x
)
2201 (cons `((mtimes simp
) ,c
,var
) p
) ))
2203 (cons `((mexpt simp
) ,var
,e
) p
) )
2205 (cons `((mtimes simp
) ,c
((mexpt simp
) ,var
,e
)) p
) )))))))
2207 ;; -----------------------------------------------------------------------------
2210 ;; evaluation and adjustment ---------------------------------------------------
2213 ;; an arbitrary polynomial is evaluated in a given field
2215 (defmfun $gf_eval
(a)
2216 (gf-char?
"gf_eval")
2217 (let ((*ef-arith?
*)) (gf-eval a
*gf-red
* "gf_eval")) )
2219 (defmfun $ef_eval
(a)
2220 (ef-gf-field?
"ef_eval")
2221 (let ((*ef-arith?
* t
))
2222 (unless (equal a
($expand a
))
2223 (gf-merror (intl:gettext
"`ef_eval': The argument must be an expanded polynomial.")) )
2224 (gf-eval a
*ef-red
* "ef_eval") ))
2226 (defun gf-eval (a red fun
)
2227 (setq a
(let ((modulus)) (car (prep1 a
))))
2229 ((integerp a
) (gf-cmod a
))
2231 (setq a
(gf-mod (cdr a
)))
2232 (and a
(not (typep (car a
) 'fixnum
))
2233 (gf-merror (intl:gettext
"`~m': The exponent is expected to be a fixnum.") fun
) )
2234 (gf-x2p (gf-nred a red
)) )))
2237 ;; gf-mod adjusts arbitrary integer coefficients (pos, neg or unbounded)
2240 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2242 (maybe-char-is-fixnum-let ((c 0))
2243 (do ((r x
(cddr r
)) res
)
2244 ((null r
) (nreverse res
))
2245 (unless (numberp (cadr r
))
2246 (gf-merror (intl:gettext
"gf: polynomials must be univariate.")) )
2247 (setq c
(gf-cmod (cadr r
)))
2248 (unless (= c
0) (setq res
(cons c
(cons (car r
) res
)))) ))))
2250 ;; positive 2 symmetric mod:
2252 (defun gf-np2smod (x) ;; modifies x
2255 ((not $gf_balanced
) x
)
2257 (*f-np2smod x
*gf-card
* #'(lambda (c) (neg (gf-ctimes (1- *gf-char
*) c
)))) )
2259 (*f-np2smod x
*gf-char
* #'(lambda (c) (- (the integer c
) *gf-char
*))) )))
2261 (defun *f-np2smod
(x p cp2smod-fn
)
2263 (maybe-char-is-fixnum-let ((p2 (ash p -
1)))
2264 (do ((r (cdr x
) (cddr r
))) (())
2265 (when (> (the integer
(car r
)) p2
)
2266 (rplaca r
(funcall cp2smod-fn
(car r
))) )
2267 (when (null (cdr r
)) (return x
)) ))))
2269 ;; adjust a coefficient to a symmetric modulus:
2270 (defun gf-cp2smod (c)
2272 ((not $gf_balanced
) c
)
2274 (if (> c
(ash *gf-card
* -
1)) (neg (gf-ctimes c
(1- *gf-char
*))) c
) )
2276 (if (> c
(ash *gf-char
* -
1)) (- (the integer c
) *gf-char
*) c
) )))
2278 ;; -----------------------------------------------------------------------------
2281 ;; arithmetic in Galois Fields - Maxima level functions ------------------------
2286 (defmfun $gf_neg
(a)
2288 (let ((*ef-arith?
*))
2289 (gf-x2p (gf-nminus (gf-p2x a
))) ))
2291 (defmfun $gf_add
(&rest args
)
2293 (let ((*ef-arith?
*))
2294 (setq args
(mapcar #'gf-p2x args
))
2295 (gf-x2p (reduce #'gf-plus args
)) ))
2297 (defmfun $gf_sub
(&rest args
)
2299 (let ((*ef-arith?
*))
2300 (setq args
(mapcar #'gf-p2x args
))
2301 (gf-x2p (gf-plus (car args
) (gf-minus (reduce #'gf-plus
(cdr args
))))) ))
2303 (defmfun $gf_mult
(&rest args
)
2304 (gf-char?
"gf_mult")
2305 (let ((*ef-arith?
*))
2306 (setq args
(mapcar #'gf-p2x args
))
2308 (not (some #'null args
))
2309 (not (typep (apply #'+ (mapcar #'car args
)) 'fixnum
))
2310 (gf-merror (intl:gettext
"`gf_mult': Resulting exponent won't be a fixnum.")) )
2311 (gf-x2p (reduce #'(lambda (x y
) (gf-times x y
*gf-red
*)) args
)) ))
2313 (defmfun $gf_reduce
(a b
)
2314 (gf-char?
"gf_reduce")
2315 (let ((*ef-arith?
*))
2316 (gf-x2p (gf-nrem (gf-p2x a
) (gf-p2x b
))) ))
2318 (defmfun $gf_inv
(a)
2320 (let ((*ef-arith?
*))
2321 (setq a
(gf-inv (gf-p2x a
) *gf-red
*))
2322 (when a
(gf-x2p a
)) )) ;; a is nil in case the inverse does not exist
2324 (defmfun $gf_div
(&rest args
)
2327 (gf-merror (intl:gettext
"`gf_div' needs at least two arguments." )) )
2328 (let* ((*ef-arith?
*)
2329 (a2 (mapcar #'gf-p2x args
))
2330 (a2 (cons (car a2
) (mapcar #'(lambda (x) (gf-inv x
*gf-red
*)) (cdr a2
)))) )
2332 ((some #'null
(cdr a2
)) ;; but check if exact division is possible ..
2333 (let ((q (gf-p2x (car args
))) r
)
2334 (setq args
(cdr args
))
2335 (do ((d (car args
) (car args
)))
2336 ((null d
) (gf-x2p q
))
2337 (multiple-value-setq (q r
) (gf-divide q
(gf-p2x d
)))
2338 (when r
(return)) ;; .. in case it is not return false
2339 (setq args
(cdr args
)) )))
2340 (t ;; a / b = a * b^-1 :
2341 (gf-x2p (reduce #'(lambda (x y
) (gf-times x y
*gf-red
*)) a2
)) ))))
2343 (defmfun $gf_exp
(a n
)
2345 (let ((*ef-arith?
*))
2348 (gf-merror (intl:gettext
"`gf_exp' needs two arguments.")) )
2350 (gf-merror (intl:gettext
"Second argument to `gf_exp' must be an integer.")) )
2351 ((< (the integer n
) 0)
2353 (gf-merror (intl:gettext
"`gf_exp': Unknown reduction polynomial.")) )
2354 (setq a
(gf-inv (gf-p2x a
) *gf-red
*))
2355 (when a
($gf_exp
(gf-x2p a
) (neg n
))) ) ;; a is nil in case the inverse does not exist
2357 (gf-x2p (gf-pow-by-table (gf-p2x a
) n
)) )
2358 ((and *gf-irred?
* *gf-x^p-powers
*)
2359 (gf-x2p (gf-pow$
(gf-p2x a
) n
*gf-red
*)) )
2364 (not (typep (* n
(car a
)) 'fixnum
))
2365 (gf-merror (intl:gettext
"`gf_exp': Resulting exponent won't be a fixnum.")) )
2366 (gf-x2p (gf-pow a n
*gf-red
*)) ))))
2370 (defmfun $ef_neg
(a)
2371 (ef-gf-field?
"ef_neg")
2372 (let ((*ef-arith?
* t
))
2373 (gf-x2p (gf-nminus (gf-p2x a
))) ))
2375 (defmfun $ef_add
(&rest args
)
2376 (ef-gf-field?
"ef_add")
2377 (let ((*ef-arith?
* t
))
2378 (setq args
(mapcar #'gf-p2x args
))
2379 (gf-x2p (reduce #'gf-plus args
)) ))
2381 (defmfun $ef_sub
(&rest args
)
2382 (ef-gf-field?
"ef_sub")
2383 (let ((*ef-arith?
* t
))
2384 (setq args
(mapcar #'gf-p2x args
))
2385 (gf-x2p (gf-plus (car args
) (gf-minus (reduce #'gf-plus
(cdr args
))))) ))
2387 (defmfun $ef_mult
(&rest args
)
2388 (ef-gf-field?
"ef_mult")
2389 (let ((*ef-arith?
* t
)
2391 (setq args
(mapcar #'gf-p2x args
))
2393 (not (some #'null args
))
2394 (not (typep (apply #'+ (mapcar #'car args
)) 'fixnum
))
2395 (gf-merror (intl:gettext
"`ef_mult': Resulting exponent won't be a fixnum.")) )
2396 (gf-x2p (reduce #'(lambda (x y
) (gf-times x y red
)) args
)) ))
2398 (defmfun $ef_reduce
(a b
)
2399 (ef-gf-field?
"ef_reduce")
2400 (let ((*ef-arith?
* t
))
2401 (gf-x2p (gf-nrem (gf-p2x a
) (gf-p2x b
))) ))
2403 (defmfun $ef_inv
(a)
2405 (let ((*ef-arith?
* t
))
2406 (setq a
(gf-inv (gf-p2x a
) *ef-red
*))
2407 (when a
(gf-x2p a
)) ))
2409 (defmfun $ef_div
(&rest args
)
2412 (gf-merror (intl:gettext
"`ef_div' needs at least two arguments." )) )
2413 (let ((*ef-arith?
* t
)
2415 (setq args
(mapcar #'gf-p2x args
))
2417 (cons (car args
) (mapcar #'(lambda (x) (gf-inv x red
)) (cdr args
))) )
2419 ((null (car args
)) 0)
2420 ((some #'null
(cdr args
)) nil
)
2421 (t (gf-x2p (reduce #'(lambda (x y
) (gf-times x y red
)) args
))) )))
2423 (defmfun $ef_exp
(a n
)
2424 (ef-gf-field?
"ef_exp")
2425 (let ((*ef-arith?
* t
))
2427 ((< (the integer n
) 0)
2429 (gf-merror (intl:gettext
"`ef_exp': Unknown reduction polynomial.")) )
2430 (setq a
(gf-inv (gf-p2x a
) *ef-red
*))
2431 (when a
($ef_exp
(gf-x2p a
) (neg n
))) )
2432 ((and *ef-irred?
* *ef-x^q-powers
*)
2433 (gf-x2p (gf-pow$
(gf-p2x a
) n
*ef-red
*)) )
2438 (not (typep (* n
(car a
)) 'fixnum
))
2439 (gf-merror (intl:gettext
"`ef_exp': Resulting exponent won't be a fixnum.")) )
2440 (gf-x2p (gf-pow a n
*ef-red
*)) ))))
2442 ;; -----------------------------------------------------------------------------
2445 ;; arithmetic in Galois Fields - Lisp level functions --------------------------
2448 ;; Both gf (base field) and ef (extension field) Maxima level functions use
2449 ;; this Lisp level functions. The switch *ef-arith?* controls how the coefficients
2450 ;; were treated. The coefficient functions gf-ctimes and friends behave
2451 ;; differently depending on *ef-arith?*. See above definitions.
2453 ;; Remark: A prefixed character 'n' indicates a destructive function.
2457 (defun gf-xctimes (x c
)
2458 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2459 (maybe-char-is-fixnum-let ((c c
))
2460 (if (or (= 0 c
) (null x
)) nil
2461 (do* ((res (list (car x
) (gf-ctimes c
(cadr x
))))
2462 (r (cdr res
) (cddr r
))
2463 (rx (cddr x
) (cddr rx
)) )
2465 (rplacd r
(list (car rx
) (gf-ctimes c
(cadr rx
)))) ))))
2467 (defun gf-nxctimes (x c
) ;; modifies x
2468 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2469 (maybe-char-is-fixnum-let ((c c
))
2470 (if (or (= 0 c
) (null x
)) nil
2471 (do ((r (cdr x
) (cddr r
)))
2473 (rplaca r
(gf-ctimes c
(car r
))) ))))
2477 (defun gf-xectimes (x e c
)
2478 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2479 (declare (fixnum e
))
2480 (maybe-char-is-fixnum-let ((c c
))
2481 (if (or (= 0 c
) (null x
)) nil
2482 (do* ((res (list (+ e
(the fixnum
(car x
))) (gf-ctimes c
(cadr x
))))
2483 (r (cdr res
) (cddr r
))
2484 (rx (cddr x
) (cddr rx
)) )
2486 (rplacd r
(list (+ e
(the fixnum
(car rx
))) (gf-ctimes c
(cadr rx
)))) ))))
2490 (defun gf-nxetimes (x e
) ;; modifies x
2492 (do ((r x
(cddr r
)))
2494 (rplaca r
(+ e
(car r
))) )))
2499 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2500 (if (or (null x
) (= 2 *gf-char
*)) x
2501 (do* ((res (list (car x
) (gf-cminus-b (cadr x
))))
2502 (r (cdr res
) (cddr r
))
2503 (rx (cddr x
) (cddr rx
)) )
2505 (rplacd r
(list (car rx
) (gf-cminus-b (cadr rx
)))) )))
2507 (defun gf-nminus (x) ;; modifies x
2508 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2509 (if (or (null x
) (= 2 *gf-char
*)) x
2510 (do ((r (cdr x
) (cddr r
))) (())
2511 (rplaca r
(gf-cminus-b (car r
)))
2512 (when (null (cdr r
)) (return x
)) )))
2516 (defun gf-plus (x y
)
2520 (t (gf-nplus (copy-list x
) y
)) ))
2524 (defun gf-nplus (x y
) ;; modifies x
2525 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2530 (maybe-char-is-fixnum-let ((cy 0)(c 0))
2531 (prog ((ex 0)(ey 0) r
) (declare (fixnum ex ey
))
2533 (setq ex
(car x
) ey
(car y
) cy
(cadr y
))
2536 (setq x
(cons ey
(cons cy x
)) y
(cddr y
)) )
2538 (setq c
(gf-cplus-b (cadr x
) cy
) y
(cddr y
))
2541 (when (null (setq x
(cddr x
))) (return y
))
2542 (when (null y
) (return x
))
2544 (t (rplaca (cdr x
) c
)) ))
2545 (t (setq r
(cdr x
)) (go b
)) )
2548 (when (null y
) (return x
))
2549 (setq ey
(car y
) cy
(cadr y
))
2551 (while (and (cdr r
) (> (the fixnum
(cadr r
)) ey
))
2554 ((null (cdr r
)) (rplacd r y
) (return x
))
2555 ((> ey
(the fixnum
(cadr r
)))
2556 (rplacd r
(cons ey
(cons cy
(cdr r
))))
2557 (setq r
(cddr r
) y
(cddr y
)) )
2559 (setq c
(gf-cplus-b (caddr r
) cy
) y
(cddr y
))
2561 (rplacd r
(cdddr r
))
2562 (rplaca (setq r
(cddr r
)) c
) )) )
2567 (defun gf-xyecplus (x y e c
)
2570 ((null x
) (gf-xectimes y e c
))
2571 (t (gf-nxyecplus (copy-list x
) y e c
) )))
2573 ;; merge c*v^e*y into x
2575 (defun gf-nxyecplus (x y e c
) ;; modifies x
2576 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2579 ((null x
) (gf-xectimes y e c
))
2581 (maybe-char-is-fixnum-let ((cy 0) (cc 0))
2582 (prog ((e e
) (ex 0) (ey 0) r
) (declare (fixnum e ex ey
))
2584 (setq ey
(+ (the fixnum
(car y
)) e
)
2585 cy
(gf-ctimes c
(cadr y
))
2589 (setq x
(cons ey
(cons cy x
)) y
(cddr y
)) )
2591 (setq cc
(gf-cplus-b (cadr x
) cy
) y
(cddr y
))
2594 (when (null (setq x
(cddr x
))) (return (gf-xectimes y e c
)))
2595 (when (null y
) (return x
))
2597 (t (rplaca (cdr x
) cc
)) ))
2598 (t (setq r
(cdr x
)) (go b
)) )
2601 (when (null y
) (return x
))
2602 (setq ey
(+ (the fixnum
(car y
)) e
)
2603 cy
(gf-ctimes c
(cadr y
)) )
2605 (when (null (cdr r
)) (go d
))
2609 (rplacd r
(cons ey
(cons cy
(cdr r
))))
2610 (setq r
(cddr r
) y
(cddr y
))
2613 (setq cc
(gf-cplus-b (caddr r
) cy
))
2615 (rplacd r
(cdddr r
))
2616 (rplaca (setq r
(cddr r
)) cc
) )
2619 (t (setq r
(cddr r
)) (go b
)) )
2622 (setq x
(nconc x
(list (+ (the fixnum
(car y
)) e
) (gf-ctimes c
(cadr y
))))
2628 ;; For sparse polynomials (in Galois Fields) with not too high degrees
2629 ;; simple school multiplication is faster than Karatsuba.
2631 ;; x * y = (x1 + x2 + ... + xk) * (y1 + y2 + ... + yn)
2632 ;; = x1 * (y1 + y2 + ... + yn) + x2 * (y1 + y2 + ... + yn) + ...
2634 ;; where e.g. xi = ci*v^ei
2636 (defun gf-times (x y red
)
2637 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2638 (if (or (null x
) (null y
)) nil
2639 (maybe-char-is-fixnum-let ((c 0) (cx 0))
2640 (do* ((res (gf-xectimes y
(car x
) (cadr x
))) ;; x1 * (y1 + y2 + ... + yn). summands in res are sorted. res is a new list.
2641 (r1 (cdr res
)) ;; r1 marks the place of xi*y1 in res. x[i+1]*y1 will be smaller.
2642 ry
;; ry iterates over y
2643 (x (cddr x
) (cddr x
)) ;; each loop: res += xi * (y1 + y2 + ... + yn)
2645 ((or (null x
)(null y
)) (gf-nred res red
))
2646 (declare (fixnum e ex
))
2647 (setq ry y
;; start with y1 again
2648 ex
(car x
) cx
(cadr x
) ;; xi = ci*v^ei
2649 e
(+ ex
(the fixnum
(car ry
))) ;; c*v^e = xi*y1
2650 c
(gf-ctimes (cadr ry
) cx
) ) ;; zero divisor free mult in Fp^n
2652 (while (and (cdr r1
) (< e
(the fixnum
(cadr r1
))))
2653 (setq r1
(cddr r1
)) ) ;; mark the position of xi*y1
2655 (do ((r r1
)) (()) ;; merge xi*y1 into res and then xi*y2, etc...
2657 ((or (null (cdr r
)) (> e
(the fixnum
(cadr r
))))
2658 (rplacd r
(cons e
(cons c
(cdr r
))))
2660 ((= 0 (setq c
(gf-cplus-b (caddr r
) c
)))
2661 (rplacd r
(cdddr r
)) )
2662 (t (rplaca (setq r
(cddr r
)) c
)) )
2664 (when (null (setq ry
(cddr ry
))) (return))
2665 (setq e
(+ ex
(the fixnum
(car ry
)))
2666 c
(gf-ctimes (cadr ry
) cx
) )
2668 (while (and (cdr r
) (< e
(the fixnum
(cadr r
))))
2669 (setq r
(cddr r
)) ) )) )))
2673 ;; x * x = (x_1 + x_2 + ... + x_k) * (x_1 + x_2 + ... + x_k)
2675 ;; = x_1^2 + 2*x_1*x_2 + 2*x_1*x_3 + ... + x_2^2 + 2*x_2*x_3 + 2*x_2*x_4 + ...
2677 ;; = x_k^2 + x_{k-1}^2 + 2*x_{k-1}*xk + x_{k-2}^2 + 2*x_{k-2}*x_{k-1} + 2*x_{k-2}*xk + ...
2679 ;; The reverse needs some additional consing but is slightly faster.
2681 (defun gf-sq (x red
)
2682 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2685 ((and (not *ef-arith?
*) (eql *gf-char
* 2)) ;; the mod 2 case degrades to x_1^2 + x_2^2 + ... + x_k^2
2687 ((null x
) (gf-nred (nreverse res
) red
))
2688 (setq res
(cons 1 (cons (ash (car x
) 1) res
))
2691 (maybe-char-is-fixnum-let ((ci 0)(*2ci
0)(c 0))
2692 (setq x
(reverse x
)) ;; start with x_k
2693 (prog (res ;; result
2694 r
;; insertion marker in res
2695 acc
;; acc accumulates previous x_i
2696 r1
;; r1 iterates in each loop over acc
2697 (e 0) (ei 0) ) (declare (fixnum e ei
))
2699 (setq ci
(car x
) ei
(cadr x
) ;; x_i = ci*v^ei
2700 *2ci
(gf-cplus-b ci ci
) ;; 2*ci (2*ci # 0 when *gf-char* # 2)
2701 res
(cons (+ ei ei
) (cons (gf-ctimes ci ci
) res
)) ;; res += x_i^2 (ci^2 # 0, no zero divisors)
2702 r
(cdr res
) ;; place insertion marker behind x_i^2
2705 (when (or (null r1
) (= 0 *2ci
)) ;; in ef *2ci might be 0 !
2706 (when (null (setq x
(cddr x
))) (return (gf-nred res red
)))
2707 (setq acc
(cons ei
(cons ci acc
))) ;; cons previous x_i to acc ..
2708 (go a1
) ) ;; .. and start next loop
2710 (setq e
(+ ei
(the fixnum
(car r1
)))
2711 c
(gf-ctimes *2ci
(cadr r1
))
2714 (while (< e
(the fixnum
(cadr r
)))
2717 ((> e
(the fixnum
(cadr r
)))
2718 (rplacd r
(cons e
(cons c
(cdr r
))))
2721 (setq c
(gf-cplus-b c
(caddr r
)))
2723 (rplacd r
(cdddr r
))
2724 (rplaca (setq r
(cddr r
)) c
) ) ))
2729 (defun gf-pow (x n red
) ;; assume 0 <= n
2730 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2731 (declare (integer n
))
2733 ((= 0 n
) (list 0 1))
2737 (setq res
(if res
(gf-times x res red
) x
))
2739 (return-from gf-pow res
) ))
2741 x
(gf-sq x red
)) ))))
2743 ;; in a field use precomputed *gf-x^p-powers* resp. *ef-x^q-powers*
2745 (defun gf-pow$
(x n red
)
2748 (*f-pow$ x n red
*gf-card
* *ef-card
* *ef-x^q-powers
*)
2751 (*f-pow$ x n red
*gf-char
* *gf-card
* *gf-x^p-powers
*)
2752 (gf-pow x n red
) )))
2754 (defun *f-pow$
(x n red p card x^p-powers
)
2755 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2756 (declare (integer n p card
))
2758 ((= 0 n
) (list 0 1))
2760 ((>= n card
) (gf-pow x n red
))
2762 (let ((prod (list 0 1))
2764 (do (quo r
) ((= 0 n
))
2765 (multiple-value-setq (quo r
) (truncate n p
))
2768 (dolist (ni (nreverse n-base-p
))
2769 (setq y
(gf-compose (svref x^p-powers j
) x red
)
2771 prod
(gf-times prod y red
)
2776 ;; x - quotient(x, y) * y
2780 (gf-merror (intl:gettext
"~m arithmetic: Quotient by zero") (if *ef-arith?
* "ef" "gf")) )
2782 (gf-nrem (copy-list x
) y
) ))
2784 (defun gf-nrem (x y
) ;; modifies x
2785 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2787 (gf-merror (intl:gettext
"~m arithmetic: Quotient by zero") (if *ef-arith?
* "ef" "gf")) )
2789 (maybe-char-is-fixnum-let ((c 0) (lcx 0) (lcy-inv (gf-cinv (cadr y
))))
2790 (let ((e 0) (ley (car y
)))
2791 (declare (fixnum e ley
))
2792 (setq lcy-inv
(gf-cminus-b lcy-inv
))
2795 (setq e
(- (the fixnum
(car x
)) ley
))
2796 (when (< e
0) (return x
))
2798 c
(gf-ctimes lcx lcy-inv
)
2799 x
(gf-nxyecplus (cddr x
) y e c
)) )))))
2803 ;; assume lc(red) = 1, reduction poly is monic
2805 (defun gf-nred (x red
) ;; modifies x
2806 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2807 (if (or (null x
) (null red
)) x
2808 (let ((e 0) (le-red (car red
)))
2809 (declare (fixnum e le-red
))
2810 (setq red
(cddr red
))
2812 (setq e
(- (the fixnum
(car x
)) le-red
))
2813 (when (< e
0) (return x
))
2814 (setq x
(gf-nxyecplus (cddr x
) red e
(gf-cminus-b (cadr x
)))) ))))
2819 #+ (or ccl ecl gcl sbcl
) (declare (optimize (speed 3) (safety 0)))
2825 (if (eql 0 (car x
)) (list 0 1)
2826 (gf-xctimes x
(gf-cinv (cadr x
))) ))
2827 (setq r
(gf-rem x y
))
2828 (psetf x y y r
) )))))
2830 ;; (monic) extended gcd
2832 (defun gf-gcdex (x y red
)
2833 #+ (or ccl ecl gcl sbcl
) (declare (optimize (speed 3) (safety 0)))
2834 (let ((x1 (list 0 1)) x2 y1
(y2 (list 0 1)) q r
)
2836 (let ((inv (gf-cinv (cadr x
))))
2837 (mapcar #'(lambda (a) (gf-xctimes a inv
)) (list x1 x2 x
)) ))
2838 (multiple-value-setq (q r
) (gf-divide x y
))
2841 y1
(gf-nplus (gf-nminus (gf-times q y1 red
)) x1
)
2844 y2
(gf-nplus (gf-nminus (gf-times q y2 red
)) x2
)
2849 ;; in case the inverse does not exist it returns nil
2850 ;; (might happen when reduction poly isn't irreducible)
2852 (defun gf-inv (y red
)
2853 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2855 (gf-merror (intl:gettext
"~m arithmetic: Quotient by zero") (if *ef-arith?
* "ef" "gf")) )
2856 (let ((y1 (list 0 1)) (x red
) x1 q r
)
2857 (setq y
(copy-list y
))
2859 (when (= 0 (car x
)) ;; gcd = 1 (const)
2860 (gf-nxctimes x1
(gf-cinv (cadr x
))) ))
2861 (multiple-value-setq (q r
) (gf-divide x y
))
2865 y1
(gf-nplus (gf-nminus (gf-times q y1 red
)) x1
) )) ))
2867 ;; quotient and remainder
2869 (defun gf-divide (x y
)
2870 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2873 (gf-merror (intl:gettext
"~m arithmetic: Quotient by zero") (if *ef-arith?
* "ef" "gf")) )
2874 ((null x
) (values nil nil
))
2876 (maybe-char-is-fixnum-let ((c 0) (lcx 0) (lcyi (gf-cinv (cadr y
))))
2877 (let ((e 0) (ley (car y
)))
2878 (declare (fixnum e ley
))
2879 (setq x
(copy-list x
))
2880 (do (q (y (cddr y
)))
2881 ((null x
) (values (nreverse q
) x
))
2882 (setq e
(- (the fixnum
(car x
)) ley
))
2884 (return (values (nreverse q
) x
)) )
2887 c
(gf-ctimes lcx lcyi
) )
2888 (unless (null y
) (setq x
(gf-nxyecplus x y e
(gf-cminus-b c
))))
2889 (setq q
(cons c
(cons e q
))) ))))))
2891 ;; -----------------------------------------------------------------------------
2894 ;; polynomial/number/list - conversions ----------------------------------------
2899 (defmfun $ef_p2n
(p)
2901 (let ((*ef-arith?
* t
)) (gf-x2n (gf-p2x p
))) )
2903 (defmfun $gf_p2n
(p &optional gf-char
)
2904 (let ((*ef-arith?
*))
2907 (let ((*gf-char
* gf-char
)) (gf-x2n (gf-p2x p
))) )
2909 (gf-x2n (gf-p2x p
)) )
2911 (gf-merror (intl:gettext
"`gf_p2n': missing modulus.")) ))))
2914 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2916 (maybe-char-is-fixnum-let ((m *gf-char
*))
2917 (when *ef-arith?
* (setq m
*gf-card
*))
2921 (return (* n
(expt m
(the fixnum
(car x
)))))
2922 (setq n
(* n
(expt m
(- (the fixnum
(car x
)) (the fixnum
(caddr x
)))))) )
2923 (setq x
(cddr x
)) ))))
2927 (defun gf-n2p-errchk (fun n
)
2928 (unless (integerp n
)
2929 (gf-merror (intl:gettext
"`~m': Not an integer: ~m") fun n
) ))
2931 (defmfun $gf_n2p
(n &optional gf-char
)
2932 (gf-n2p-errchk "gf_n2p" n
)
2933 (let ((*ef-arith?
*))
2937 (let ((*gf-char
* gf-char
)) (gf-x2p (gf-n2x n
))) )
2939 (gf-x2p (gf-n2x n
)) )
2941 (gf-merror (intl:gettext
"`gf_n2p': missing modulus.")) ))))
2943 (defmfun $ef_n2p
(n)
2945 (gf-n2p-errchk "ef_n2p" n
)
2946 (let ((*ef-arith?
* t
)) (gf-x2p (gf-n2x n
))) )
2949 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
2950 (declare (integer n
))
2951 (maybe-char-is-fixnum-let ((r 0) (m *gf-char
*))
2952 (let ((e 0)) (declare (fixnum e
))
2953 (when *ef-arith?
* (setq m
*gf-card
*))
2956 (multiple-value-setq (n r
) (truncate n m
))
2958 (setq x
(cons e
(cons r x
))) )
2963 (defmfun $ef_p2l
(p &optional
(len 0))
2964 (declare (fixnum len
))
2965 (let ((*ef-arith?
* t
))
2966 (cons '(mlist simp
) (gf-x2l (gf-p2x-raw p
) len
)) )) ;; more flexibility ...
2968 (defmfun $gf_p2l
(p &optional
(len 0)) ;; len = 0 : no padding
2969 (declare (fixnum len
))
2970 (let ((*ef-arith?
*))
2971 (cons '(mlist simp
) (gf-x2l (gf-p2x-raw p
) len
)) )) ;; ... by omitting mod reduction
2973 (defun gf-x2l (x len
)
2974 #+ (or ccl ecl gcl sbcl
) (declare (optimize (speed 3) (safety 0)))
2975 (declare (fixnum len
))
2976 (do* ((e (if x
(the fixnum
(car x
)) 0))
2977 (k (max e
(1- len
)) (1- k
))
2979 ((< k
0) (nreverse l
))
2980 (declare (fixnum e k
))
2982 ((or (null x
) (> k e
))
2987 (unless (null x
) (setq e
(the fixnum
(car x
)))) ))))
2991 (defmfun $ef_l2p
(l)
2992 (gf-l2p-errchk l
"ef_l2p")
2993 (let ((*ef-arith?
* t
)) (gf-x2p (gf-l2x (cdr l
)))) )
2995 (defmfun $gf_l2p
(l)
2996 (gf-l2p-errchk l
"gf_l2p")
2997 (let ((*ef-arith?
*)) (gf-x2p (gf-l2x (cdr l
)))) )
2999 (defun gf-l2p-errchk (l fun
)
3001 (gf-merror (intl:gettext
"`~m': Argument must be a list of integers.") fun
) ))
3004 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3005 (setq l
(reverse l
))
3006 (maybe-char-is-fixnum-let ((c 0))
3007 (let ((e 0)) (declare (fixnum e
))
3010 (unless (= 0 (setq c
(car l
)))
3011 (setq x
(cons e
(cons c x
))) )
3017 (defmfun $gf_l2n
(l)
3019 (gf-l2p-errchk l
"gf_l2n")
3020 (let ((*ef-arith?
*)) (gf-l2n (cdr l
))) )
3022 (defmfun $ef_l2n
(l)
3024 (gf-l2p-errchk l
"ef_l2n")
3025 (let ((*ef-arith?
* t
)) (gf-l2n (cdr l
))) )
3028 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3029 (maybe-char-is-fixnum-let ((m *gf-char
*) (c1 (car l
)) (c 0))
3030 (when *ef-arith?
* (setq m
*gf-card
*))
3031 (setq l
(reverse (cdr l
)))
3033 ((null l
) (+ (* c1 b
) n
))
3034 (declare (integer n b
))
3035 (unless (= 0 (setq c
(car l
))) (incf n
(* c b
)))
3036 (setq b
(* b m
) l
(cdr l
)) )))
3040 (defmfun $gf_n2l
(n &optional
(len 0)) ;; in case of len = 0 the list isn't padded or truncated
3041 (declare (integer n
) (fixnum len
))
3043 (gf-n2p-errchk "gf_n2l" n
)
3045 (let ((*ef-arith?
*))
3046 (if (= 0 len
) (gf-n2l n
) (gf-n2l-twoargs n len
)) )))
3048 (defmfun $ef_n2l
(n &optional
(len 0)) ;; in case of len = 0 the list isn't padded or truncated
3049 (declare (integer n
) (fixnum len
))
3051 (gf-n2p-errchk "ef_n2l" n
)
3053 (let ((*ef-arith?
* t
))
3054 (if (= 0 len
) (gf-n2l n
) (gf-n2l-twoargs n len
)) )))
3056 (defun gf-n2l (n) ;; this version is frequently called by gf-precomp, keep it simple
3057 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3058 (declare (integer n
))
3059 (maybe-char-is-fixnum-let ((m *gf-char
*) (r 0))
3060 (when *ef-arith?
* (setq m
*gf-card
*))
3062 (multiple-value-setq (n r
) (truncate n m
))
3063 (setq l
(cons r l
)) )))
3065 (defun gf-n2l-twoargs (n len
)
3066 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3067 (declare (integer n
) (fixnum len
))
3068 (maybe-char-is-fixnum-let ((m *gf-char
*) (r 0))
3069 (when *ef-arith?
* (setq m
*gf-card
*))
3070 (do (l) ((= 0 len
) l
)
3071 (multiple-value-setq (n r
) (truncate n m
))
3075 ;; -----------------------------------------------------------------------------
3078 ;; irreducibility (Ben-Or algorithm) -------------------------------------------
3081 (defmfun $gf_irreducible_p
(a &optional p
)
3083 (p (unless (and (integerp p
) (primep p
))
3084 (gf-merror (intl:gettext
3085 "`gf_irreducible_p': Second argument must be a prime number." )) ))
3086 (t (gf-char?
"gf_irreducible_p")
3087 (setq p
*gf-char
*) ))
3088 (let* ((*ef-arith?
*)
3090 (x (gf-p2x a
)) n
) ;; gf-p2x depends on *gf-char*
3093 ((= 0 (setq n
(car x
))) nil
)
3095 (t (gf-irr-p x p
(car x
))) )))
3097 (defmfun $ef_irreducible_p
(a)
3098 (ef-gf-field?
"ef_irreducible_p")
3099 (let ((*ef-arith?
* t
))
3101 (gf-irr-p a
*gf-card
* (car a
)) ))
3103 ;; is y irreducible of degree n over Fq[x] ?
3106 (defun gf-irr-p (y q n
) ;; gf-irr-p is independent from any settings
3107 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3108 (declare (integer q
) (fixnum n
))
3109 (let* ((*gf-char
* (car (cfactorw q
)))
3111 (mx (gf-minus x
)) ;; gf-minus needs *gf-char*
3114 (setq y
(gf-xctimes y
(gf-cinv lc
))) ) ;; monicize y
3115 (do ((i 1 (1+ i
)) (xq x
) (n2 (ash n -
1)))
3117 (declare (fixnum i n2
))
3118 (setq xq
(gf-pow xq q y
))
3119 (unless (= 0 (car (gf-gcd y
(gf-plus xq mx
))))
3122 ;; find an irreducible element
3124 ;; gf_irreducible is independent from any settings
3126 (defmfun $gf_irreducible
(p n
) ;; p,n : desired characteristic and degree
3127 (unless (and (integerp p
) (primep p
) (integerp n
))
3128 (gf-merror (intl:gettext
"`gf_irreducible' needs a prime number and an integer.")) )
3130 (let* ((*gf-char
* p
)
3132 (irr (gf-irr p n
)) )
3133 (when irr
(gf-x2p irr
)) ))
3135 (defmfun $ef_irreducible
(n) ;; n : desired degree
3136 (ef-gf-field?
"ef_irreducible")
3137 (unless (integerp n
)
3138 (gf-merror (intl:gettext
"`ef_irreducible' needs an integer.")) )
3139 (let* ((*ef-arith?
* t
)
3141 (when irr
(gf-x2p irr
)) ))
3148 (*f-irr
*gf-card
* n
) )
3151 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3153 (return-from *f-irr
(list 1 1)) )
3154 (let* ((inc (min $gf_coeff_limit q
))
3155 (i-lim (expt inc n
))
3159 (gf-merror (intl:gettext
"No irreducible polynomial found.~%~\
3160 `gf_coeff_limit' might be too small.~%" )))
3161 (setq x
(let ((*gf-char
* inc
)) (gf-n2x i
)))
3162 (when (= 0 (car (last x
2)))
3163 (setq x
(cons n
(cons 1 x
)))
3164 (when (gf-irr-p x q n
) (return-from *f-irr x
)) ))))
3166 ;; -----------------------------------------------------------------------------
3169 ;; Primitive elements ----------------------------------------------------------
3172 ;; Tests if an element is primitive in the field
3174 (defmfun $gf_primitive_p
(a)
3175 (gf-data?
"gf_primitive_p") ;; --> precomputations are performed
3176 (let* ((*ef-arith?
*)
3180 ((or (= 0 n
) (>= n
*gf-card
*)) nil
)
3182 (zn-primroot-p n
*gf-char
* *gf-ord
* (mapcar #'car
*gf-fs-ord
*)) )
3186 (defmfun $ef_primitive_p
(a)
3187 (ef-data?
"ef_primitive_p") ;; --> precomputations are performed
3188 (let ((*ef-arith?
* t
))
3192 ((>= (car a
) *ef-exp
*) nil
)
3195 (let ((*ef-arith?
*)) (gf-prim-p (gf-n2x (cadr a
))))
3197 (t (ef-prim-p a
)) )))
3200 ;; Testing primitivity in (Fq^n)*:
3202 ;; We check f(x)^ei # 1 (ei = ord/pi) for all prime factors pi of ord.
3204 ;; With ei = sum(aij*q^j, j,0,m) in base q and using f(x)^q = f(x^q) we get
3206 ;; f(x)^ei = f(x)^sum(aij*q^j, j,0,m) = prod(f(x^q^j)^aij, j,0,m).
3209 ;; Special case: red is irreducible, f(x) = x+c and pi|ord and pi|q-1.
3211 ;; Then ei = (q^n-1)/(q-1) * (q-1)/pi = sum(q^j, j,0,n-1) * (q-1)/pi.
3213 ;; With ai = (q-1)/pi and using red(z) = prod(z - x^q^j, j,0,n-1) we get
3215 ;; f(x)^ei = f(x)^sum(ai*q^j, j,0,n-1) = (prod(f(x)^q^j, j,0,n-1))^ai
3217 ;; = (prod(x^q^j + c, j,0,n-1))^ai = ((-1)^n * prod(-c - x^q^j, j,0,n-1))^ai
3219 ;; = ((-1)^n * red(-c))^ai
3222 (defun gf-prim-p (x)
3225 (*f-prim-p-2 x
*gf-char
* *gf-red
* *gf-fsx
* *gf-fsx-base-p
* *gf-x^p-powers
*) )
3226 ((gf-unit-p x
*gf-red
*)
3227 (*f-prim-p-1 x
*gf-red
* *gf-ord
* *gf-fs-ord
*) )
3230 (defun ef-prim-p (x)
3233 (*f-prim-p-2 x
*gf-card
* *ef-red
* *ef-fsx
* *ef-fsx-base-q
* *ef-x^q-powers
*) )
3234 ((gf-unit-p x
*ef-red
*)
3235 (*f-prim-p-1 x
*ef-red
* *ef-ord
* *ef-fs-ord
*) )
3240 (defun *f-prim-p-1
(x red ord fs-ord
)
3241 (dolist (pe fs-ord t
)
3242 (when (equal '(0 1) (gf-pow x
(truncate ord
(car pe
)) red
)) (return)) ))
3244 ;; *f-prim-p-2 uses precomputations and exponentiation by composition
3246 (defun *f-prim-p-2
(x q red fs fs-base-q x^q-powers
)
3247 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3248 (unless (or (= 2 *gf-char
*) (= -
1 (gf-jacobi x red q
))) ;; red is assumed to be irreducible
3249 (return-from *f-prim-p-2
) )
3250 (let ((exponent (car red
))
3251 (x+c?
(and (= (car x
) 1) (= (cadr x
) 1)))
3253 (do ((i 0 (1+ i
)) (j 0 0) (lf (array-dimension fs
0)))
3255 (declare (fixnum i j lf
))
3257 ((and x
+c?
(cadr (svref fs i
))) ;; linear and pi|ord and pi|p-1
3258 (setq -c
(if (= 2 (length x
)) 0 (gf-cminus-b (car (last x
))))
3259 z
(list 0 (gf-at red -c
)) )
3260 (when (oddp exponent
) (setq z
(gf-minus z
))) ;; (-1)^n * red(-c)
3261 (setq z
(gf-pow z
(caddr (svref fs i
)) red
)) ;; ((-1)^n * red(-c))^ai
3262 (when (or (null z
) (equal z
'(0 1)))
3265 (setq prod
(list 0 1))
3266 (dolist (aij (svref fs-base-q i
))
3267 (setq y
(gf-compose (svref x^q-powers j
) x red
) ;; f(x^q^j)
3268 y
(gf-pow y aij red
) ;; f(x^q^j)^aij
3269 prod
(gf-times prod y red
)
3271 (when (or (null prod
) (equal prod
'(0 1))) ;; prod(f(x^q^j)^aij, j,0,m)
3272 (return nil
) )) ))))
3275 ;; generalized Jacobi-symbol (Bach-Shallit, Theorem 6.7.1)
3277 (defmfun $gf_jacobi
(a b
)
3278 (gf-char?
"gf_jacobi")
3279 (let* ((*ef-arith?
*)
3283 (if (null (gf-rem x y
)) 0 1)
3284 (gf-jacobi x y
*gf-char
*) )))
3286 (defmfun $ef_jacobi
(a b
)
3287 (ef-gf-field?
"ef_jacobi")
3288 (let* ((*ef-arith?
* t
)
3291 (if (= 2 (car (cfactorw *gf-card
*)))
3292 (if (null (gf-rem x y
)) 0 1)
3293 (gf-jacobi x y
*gf-card
*) )))
3295 (defun gf-jacobi (u v q
)
3296 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3297 (if (null (setq u
(gf-rem u v
))) 0
3299 (s (if (evenp (car v
)) 1 (gf-cjacobi c
))) )
3303 (setq u
(gf-xctimes u
(gf-cinv c
)))
3304 (when (every #'oddp
(list (ash (1- q
) -
1) (car u
) (car v
)))
3306 (* s
(gf-jacobi v u q
)) )))))
3308 (defun gf-cjacobi (c)
3310 (let ((*ef-arith?
*)) (gf-jacobi (gf-n2x c
) *gf-red
* *gf-char
*))
3311 ($jacobi c
*gf-char
*) ))
3314 ;; modular composition (uses Horner and square and multiply)
3317 (defmfun $gf_compose
(a b
)
3318 (gf-red?
"gf_compose")
3319 (let ((*ef-arith?
*))
3320 (gf-x2p (gf-compose (gf-p2x a
) (gf-p2x b
) *gf-red
*)) ))
3322 (defmfun $ef_compose
(a b
)
3323 (ef-red?
"ef_compose")
3324 (let ((*ef-arith?
* t
))
3325 (gf-x2p (gf-compose (gf-p2x a
) (gf-p2x b
) *ef-red
*)) ))
3327 (defun gf-compose (x y red
)
3328 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3330 ((or (null x
) (null y
)) nil
)
3333 (let ((n (gf-at y
(cadr x
))))
3334 (if (= 0 n
) nil
(list 0 n
)) ))
3337 (setq res
(gf-nplus res
(list 0 (cadr y
))))
3338 (when (null (cddr y
))
3339 (return (gf-times res
(gf-pow x
(car y
) red
) red
)) )
3340 (setq res
(gf-times res
(gf-pow x
(- (car y
) (caddr y
)) red
) red
)
3343 ;; a(n) with poly a and integer n
3345 (defun gf-at-errchk (n fun
)
3346 (unless (integerp n
)
3347 (gf-merror (intl:gettext
"`~m': Second argument must be an integer.") fun
) ))
3349 (defmfun $gf_at
(a n
) ;; integer n
3351 (gf-at-errchk n
"gf_at")
3352 (let ((*ef-arith?
*))
3353 (gf-at (gf-p2x a
) n
) ))
3355 (defmfun $ef_at
(a n
) ;; poly a, integer n
3356 (ef-gf-field?
"ef_at")
3357 (gf-at-errchk n
"ef_at")
3358 (let ((*ef-arith?
* t
))
3359 (gf-at (gf-p2x a
) n
) ))
3361 (defun gf-at (x n
) ;; Horner and square and multiply
3362 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3363 (if (or (null x
) (integerp x
)) x
3364 (maybe-char-is-fixnum-let ((n n
))
3366 (setq i
(gf-cplus-b i
(cadr x
)))
3367 (when (null (cddr x
))
3368 (setq i1
(gf-cpow n
(the fixnum
(car x
))))
3369 (return (gf-ctimes i i1
)) )
3370 (setq i1
(gf-cpow n
(- (the fixnum
(car x
)) (the fixnum
(caddr x
))))
3374 ;; find a primitive element:
3376 (defmfun $gf_primitive
()
3377 (gf-data?
"gf_primitive")
3378 (let ((*ef-arith?
*))
3380 ((null *gf-prim
*) nil
)
3381 ((equal *gf-prim
* '$unknown
)
3382 (setq *gf-prim
* (gf-prim))
3383 (unless (null *gf-prim
*) (gf-x2p *gf-prim
*)) )
3384 (t (gf-x2p *gf-prim
*)) )))
3386 (defmfun $ef_primitive
()
3387 (ef-data?
"ef_primitive")
3388 (let ((*ef-arith?
* t
))
3390 ((null *ef-prim
*) nil
)
3391 ((equal *ef-prim
* '$unknown
)
3394 (setq *ef-prim
* (let ((*ef-arith?
*)) (gf-x2n *gf-prim
*))) )
3396 (setq *ef-prim
* (ef-prim))
3397 (unless (null *ef-prim
*) (gf-x2p *ef-prim
*)) )))
3398 (t (gf-x2p *ef-prim
*)) )))
3402 (*f-prim
*gf-char
* *gf-exp
* #'gf-prim-p
) )
3405 (*f-prim
*gf-card
* *ef-exp
* #'ef-prim-p
) )
3407 (defun *f-prim
(inc e prim-p-fn
)
3408 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3409 (setq inc
(min $gf_coeff_limit inc
))
3411 (n-lim (expt inc e
))
3414 (when (= $gf_coeff_limit inc
) '$unknown
) )
3415 (setq x
(let ((*gf-char
* inc
)) (gf-n2x n
)))
3418 (setq n
(1- (* (ash n -
1) inc
))) ) ;; go to next monic poly
3419 ((funcall prim-p-fn x
)
3423 ;; precomputation for *f-prim-p:
3425 (defun gf-precomp ()
3426 (*f-precomp
(1- *gf-char
*) *gf-ord
* *gf-fs-ord
*) )
3428 (defun ef-precomp ()
3429 (*f-precomp
(1- *gf-card
*) *ef-ord
* *ef-fs-ord
*) )
3431 (defun *f-precomp
(q-1 ord fs-ord
)
3432 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3437 (sort (get-factor-list q-1
) #'< :key
#'car
) ) ;; .. [pi, ei] ..
3439 (setq fs-ord
(remove-if #'(lambda (sj) (= (car fj
) (car sj
))) fs-ord
:count
1)) )
3441 (mapcar #'(lambda (pe) (list (car pe
) t
(truncate q-1
(car pe
)))) fs-q-1
) );; .. [pi, true, (p-1)/pi] ..
3443 (mapcar #'(lambda (pe) (list (car pe
) nil
)) fs-ord
) ) ;; .. [pi, false] ..
3445 (merge 'list fs-q-1 fs-ord
#'(lambda (a b
) (< (car a
) (car b
)))) )
3448 (setq *ef-fsx
* (apply #'vector fs-list
))
3449 (setq *ef-fsx-base-q
*
3451 (mapcar #'(lambda (pe) (nreverse (gf-n2l (truncate ord
(car pe
))))) ;; qi = ord/pi = sum(aij*p^j, j,0,m)
3453 (setq *ef-x^q-powers
* (gf-x^p-powers
*gf-card
* *ef-exp
* *ef-red
*)) ) ;; x^p^j
3455 (setq *gf-fsx
* (apply #'vector fs-list
))
3456 (setq *gf-fsx-base-p
*
3458 (mapcar #'(lambda (pe) (nreverse (gf-n2l (truncate ord
(car pe
))))) ;; qi = ord/pi = sum(aij*p^j, j,0,m)
3460 (setq *gf-x^p-powers
* (gf-x^p-powers
*gf-char
* *gf-exp
* *gf-red
*)) )))) ;; x^p^j
3462 ;; returns an array of polynomials x^p^j, j = 0, 1, .. , (n-1), where n = *gf-exp*
3464 (defun gf-x^p-powers
(q n red
)
3465 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3466 (declare (integer q
) (fixnum n
))
3467 (let ((a (make-array n
:element-type
'list
:initial-element nil
)) )
3468 (setf (svref a
0) (list 1 1)) ;; x
3471 (declare (fixnum j
))
3472 (setf (svref a j
) (gf-pow (svref a
(1- j
)) q red
)) )))
3474 ;; -----------------------------------------------------------------------------
3477 ;; Primitive polynomials -------------------------------------------------------
3480 ;; test if a is a primitive polynomial over Fq
3482 (defmfun $gf_primitive_poly_p
(a &optional p
)
3484 (p (unless (and (integerp p
) (primep p
))
3485 (gf-merror (intl:gettext
"`gf_primitive_poly_p': ~m is not a prime number.") p
) ))
3486 (t (gf-char?
"gf_primitive_poly_p")
3487 (setq p
*gf-char
*) ))
3488 (let* ((*ef-arith?
*)
3490 (y (gf-p2x a
)) ;; gf-p2x depends on *gf-char*
3492 (gf-primpoly-p y p n
) ))
3494 (defmfun $ef_primitive_poly_p
(y)
3495 (ef-gf-field?
"ef_primitive_poly_p")
3496 (let ((*ef-arith?
* t
))
3498 (gf-primpoly-p y
*gf-card
* (car y
)) ))
3502 ;; TOM HANSEN AND GARY L. MULLEN
3503 ;; PRIMITIVE POLYNOMIALS OVER FINITE FIELDS
3504 ;; (gf-primpoly-p performs a full irreducibility check
3505 ;; and therefore doesn't check whether x^((q^n-1)/(q-1)) = (-1)^n * y(0) mod y)
3507 (defun gf-primpoly-p (y q n
)
3508 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3509 (declare (fixnum n
))
3510 (unless (= 1 (cadr y
)) ;; monic poly assumed
3511 (return-from gf-primpoly-p
) )
3512 (prog* ((fs-q (cfactorw q
))
3513 (*gf-char
* (car fs-q
))
3514 (*gf-exp
* (if *ef-arith?
* (cadr fs-q
) n
))
3518 ;; the constant part ...
3519 (unless (= 0 (car const
)) (return nil
))
3520 (setq const
(cadr const
))
3521 (when (oddp n
) (setq const
(gf-cminus-b const
))) ;; (-1)^n*const
3522 ;; ... must be primitive in Fq:
3523 (unless (if (and *ef-arith?
* (> *gf-exp
* 1))
3524 (let ((*ef-arith?
*)) (gf-prim-p (gf-n2x const
)))
3526 (setq fs-q-1
(sort (mapcar #'car
(get-factor-list q-1
)) #'<))
3527 (zn-primroot-p const q q-1 fs-q-1
) ))
3530 (when (= n
1) (return t
))
3531 ;; y must be irreducible:
3532 (unless (gf-irr-p y q n
) (return nil
))
3533 ;; check for all prime factors fi of r = (q^n-1)/(q-1) which do not divide q-1
3534 ;; that x^(r/fi) mod y is not an integer:
3535 (let (x^q-powers r fs-r fs-r-base-q
)
3537 (setq x^q-powers
(gf-x^p-powers q n y
)
3538 r
(truncate (1- (expt q n
)) q-1
)
3539 fs-r
(sort (mapcar #'car
(get-factor-list r
)) #'<) )
3541 (setq fs-q-1
(sort (mapcar #'car
(get-factor-list q-1
)) #'<)) )
3543 (setq fs-r
(delete-if #'(lambda (sj) (= fj sj
)) fs-r
:count
1)) )
3545 (let ((*gf-char
* q
))
3547 (mapcar #'(lambda (f) (nreverse (gf-n2l (truncate r f
)))) fs-r
) )))
3549 (return (gf-primpoly-p-exit y fs-r-base-q x^q-powers
)) )))
3551 ;; uses exponentiation by pre-computation
3552 (defun gf-primpoly-p-exit (y fs-r-base-q x^q-powers
)
3553 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3554 (do ((i 0 (1+ i
)) (j 0 0) (dim (array-dimension fs-r-base-q
0)) z zz
)
3556 (declare (fixnum i j dim
))
3557 (setq zz
(list 0 1))
3558 (dolist (aij (svref fs-r-base-q i
)) ;; fi = sum(aij*q^j, j,0,n-1)
3559 (setq z
(gf-pow (svref x^q-powers j
) aij y
)
3560 zz
(gf-times zz z y
)
3562 (when (= 0 (car zz
)) (return nil
)) ))
3565 ;; find a primitive polynomial
3567 (defmfun $gf_primitive_poly
(p n
)
3568 (unless (and (integerp p
) (primep p
) (integerp n
))
3569 (gf-merror (intl:gettext
"`gf_primitive_poly' needs a prime number and an integer.")) )
3571 (let ((*ef-arith?
*) (*gf-char
* p
)) ;; gf-x2p needs *gf-char*
3572 (gf-x2p (gf-primpoly p n
)) ))
3574 (defmfun $ef_primitive_poly
(n)
3575 (ef-gf-field?
"ef_primitive_poly")
3576 (let ((*ef-arith?
* t
))
3577 (gf-x2p (gf-primpoly *gf-card
* n
)) ))
3580 (defun gf-primpoly (q n
)
3581 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3582 (declare (fixnum n
))
3583 (let* ((fs-q (cfactorw q
))
3584 (*gf-char
* (car fs-q
))
3585 (*gf-exp
* (if *ef-arith?
* (cadr fs-q
) n
))
3588 (fs-q-1 (sort (mapcar #'car
(get-factor-list q-1
)) #'<))
3589 r fs-r fs-r-base-q
)
3592 (let ((prt (if (= q
2) 1 (zn-primroot q q-1 fs-q-1
))))
3593 (return-from gf-primpoly
3594 (list 1 1 0 (gf-cminus-b prt
)) )))
3595 ;; pre-computation part 1:
3596 (setq r
(truncate (1- (expt q n
)) q-1
)
3597 fs-r
(sort (mapcar #'car
(get-factor-list r
)) #'<) )
3599 (setq fs-r
(delete-if #'(lambda (sj) (= fj sj
)) fs-r
:count
1)) )
3601 (let ((*gf-char
* q
))
3603 (mapcar #'(lambda (f) (nreverse (gf-n2l (truncate r f
)))) fs-r
) )))
3605 (let* ((inc (min $gf_coeff_limit q
))
3606 (i-lim (expt inc n
))
3608 (do ((i (1+ inc
) (1+ i
)))
3610 (gf-merror (intl:gettext
"No primitive polynomial found.~%~\
3611 `gf_coeff_limit' might be too small.~%" )) )
3612 (setq x
(let ((*gf-char
* inc
)) (gf-n2x i
))
3613 x
(cons n
(cons 1 x
)) )
3614 (when (gf-primpoly-p2 x
*gf-char
* *gf-exp
* q n fs-q-1 fs-r-base-q
)
3617 (defun gf-primpoly-p2 (y p e q n fs-q-1 fs-r-base-q
)
3618 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3619 (declare (fixnum e n
))
3620 (when (= 1 (cadr y
)) ;; monic poly assumed
3621 (prog* ((*gf-char
* p
) (*gf-exp
* e
) (q-1 (1- q
))
3622 (const (last y
2)) )
3623 ;; the constant part ...
3624 (unless (= 0 (car const
)) (return nil
))
3625 (setq const
(cadr const
))
3626 (when (oddp n
) (setq const
(gf-cminus-b const
))) ;; (-1)^n*const
3627 ;; ... must be primitive in Fq:
3628 (unless (if (and *ef-arith?
* (> *gf-exp
* 1))
3629 (let ((*ef-arith?
*)) (gf-prim-p (gf-n2x const
)))
3630 (zn-primroot-p const q q-1 fs-q-1
) )
3632 ;; y must be irreducible:
3633 (unless (gf-irr-p y q n
) (return nil
))
3634 ;; y dependend pre-computation and final check:
3635 (return (gf-primpoly-p-exit y fs-r-base-q
(gf-x^p-powers q n y
))) )))
3637 ;; -----------------------------------------------------------------------------
3640 ;; random elements -------------------------------------------------------------
3643 ;; Produces a random element within the given environment
3645 (defmfun $gf_random
(&optional p n
)
3646 (let ((*ef-arith?
* t
))
3648 (n (let ((*gf-char
* p
))
3649 (unless *gf-red?
* (gf-set-rat-header))
3650 (gf-x2p (gf-random p n
)) ))
3651 (t (gf-data?
"gf_random")
3652 (gf-x2p (gf-random *gf-char
* *gf-exp
*)) ))))
3654 (defmfun $ef_random
(&optional q n
)
3655 (let ((*ef-arith?
* t
))
3657 (n (let ((*gf-char
* q
)) (gf-x2p (gf-random q n
))))
3658 (t (ef-data?
"ef_random")
3659 (gf-x2p (gf-random *gf-card
* *ef-exp
*)) ))))
3661 (defun gf-random (q n
)
3662 (do ((e 0 (1+ e
)) c x
)
3666 (setq x
(cons e
(cons c x
))) )))
3668 ;; -----------------------------------------------------------------------------
3671 ;; factoring -------------------------------------------------------------------
3674 (defmfun $gf_factor
(a &optional p
) ;; set p to switch to another modulus
3676 (p (unless (and (integerp p
) (primep p
))
3677 (gf-merror (intl:gettext
"`gf_factor': Second argument must be a prime number.")) )
3678 (gf-set-rat-header) )
3679 (t (gf-char?
"gf_factor")
3680 (setq p
*gf-char
*) ))
3681 (let* ((*gf-char
* p
)
3682 (modulus p
) (a ($rat a
))
3684 (when (> (length (caddar a
)) 1)
3685 (gf-merror (intl:gettext
"`gf_factor': Polynomial must be univariate.")) )
3688 ((integerp a
) (mod a
*gf-char
*))
3690 (if $gf_cantor_zassenhaus
3691 (gf-factor (gf-mod (cdr a
)) p
)
3692 (gf-ns2pmod-factors (pfactor a
)) ))
3693 (setq a
(gf-disrep-factors a
))
3694 (and (consp a
) (consp (car a
)) (equal (caar a
) 'mtimes
)
3695 (setq a
(simplifya (cons '(mtimes) (cdr a
)) nil
)) )
3698 ;; adjust results from rat3d/pfactor to a positive modulus if $gf_balanced = false
3699 (defun gf-ns2pmod-factors (fs) ;; modifies fs
3701 (maybe-char-is-fixnum-let ((m *gf-char
*))
3702 (do ((r fs
(cddr r
)))
3704 (if (integerp (car r
))
3705 (when (< (the integer
(car r
)) 0)
3706 (incf (car r
) m
) ) ;; only in the case *ef-arith?* = false
3707 (rplaca r
(gf-ns2pmod-factor (cdar r
) m
)) )))))
3709 (defun gf-ns2pmod-factor (fac m
)
3710 (do ((r (cdr fac
) (cddr r
))) (())
3711 (when (< (the integer
(car r
)) 0)
3713 (when (null (cdr r
)) (return fac
)) ))
3715 (defun gf-disrep-factors (fs)
3717 ((integerp fs
) (gf-cp2smod fs
))
3719 (setq fs
(nreverse fs
))
3721 ((null fs
) (cons '(mtimes simp factored
) p
))
3722 (declare (fixnum e
))
3723 (setq e
(the fixnum
(car fs
))
3727 ((integerp fac
) (cons (gf-cp2smod fac
) p
))
3728 ((= 1 e
) (cons (gf-disrep (gf-np2smod fac
)) p
))
3729 (t (cons `((mexpt simp
) ,(gf-disrep (gf-np2smod fac
)) ,e
) p
)) ))))))
3731 (defmfun $ef_factor
(a)
3732 (ef-gf-field?
"ef_factor")
3733 (let ((*ef-arith?
* t
))
3734 (setq a
(let ((modulus)) ($rat a
)))
3735 (when (> (length (caddar a
)) 1)
3736 (gf-merror (intl:gettext
"`ef_factor': Polynomial must be univariate.")) )
3739 ((integerp a
) (ef-cmod a
))
3742 (gf-factor (gf-mod (cdr a
)) *gf-card
*) ))
3743 (and (consp a
) (consp (car a
)) (equal (caar a
) 'mtimes
)
3744 (setq a
(simplifya (cons '(mtimes) (cdr a
)) nil
)) )
3747 (defun gf-factor (x q
) ;; non-integer x
3748 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3749 (let ((lc (cadr x
)) z
)
3751 (setq x
(gf-xctimes x
(gf-cinv lc
))) ) ;; monicize x
3752 (if (gf-irr-p x q
(car x
))
3754 (let ((sqfr (gf-square-free x
)) e y
)
3758 y
(gf-distinct-degree-factors y q
) )
3760 (setq z
(nconc (gf-equal-degree-factors w q e
) z
)) ))))
3761 (if (= 1 lc
) z
(cons lc
(cons 1 z
))) ))
3764 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3766 (maybe-char-is-fixnum-let ((m *gf-char
*))
3767 (do ((rx x
(cddr rx
)) res c
)
3768 ((or (null rx
) (= 0 (car rx
))) (nreverse res
))
3769 (setq c
(gf-ctimes (mod (the fixnum
(car rx
)) m
) (cadr rx
)))
3771 (push (1- (car rx
)) res
)
3775 (defun ef-pth-croot (c)
3776 (let ((p *gf-char
*) (*ef-arith?
* t
))
3777 (dotimes (i (1- *gf-exp
*) c
)
3778 (setq c
(gf-cpow c p
)) )))
3780 (defun gf-pth-root (x)
3781 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3782 (maybe-char-is-fixnum-let ((p *gf-char
*))
3784 (do ((rx x
(cddr rx
)) res c
)
3785 ((null rx
) (nreverse res
))
3786 (push (truncate (the fixnum
(car rx
)) p
) res
)
3788 (when *ef-arith?
* ;; p # q
3789 (setq c
(ef-pth-croot c
)) )
3792 (defun gf-gcd-cofactors (x dx
)
3793 (let ((g (gf-gcd x dx
)))
3794 (values g
(gf-divide x g
) (gf-divide dx g
)) ))
3796 (defun gf-square-free (x) ;; monic x
3797 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3798 (let (f fs
(r (gf-diff x
)) g
)
3800 ((equal '(0 1) (setq g
(gf-gcd x r
))) `((1 ,x
)))
3803 (setq r
(gf-divide x g
)
3807 (declare (fixnum m
))
3808 (multiple-value-setq (r f x
) (gf-gcd-cofactors r x
))
3809 (unless (equal '(0 1) f
)
3810 (push (list m f
) fs
) )))
3811 (unless (equal '(0 1) x
)
3813 (append (mapcar #'(lambda (v) (rplaca v
(* (car v
) *gf-char
*)))
3814 (gf-square-free (gf-pth-root x
)) )
3818 (defun gf-distinct-degree-factors (x q
)
3819 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3820 (let ((w '(1 1)) f fs
(*gf-char
* (car (cfactorw q
))) )
3822 ((equal '(0 1) x
) fs
)
3823 (declare (fixnum n
))
3824 (when (> (ash n
1) (car x
))
3825 (setq fs
(cons (list x
(car x
)) fs
))
3827 (setq w
(gf-nred w x
)
3829 f
(gf-gcd (gf-plus w
(gf-nminus (list 1 1))) x
) )
3830 (unless (equal '(0 1) f
)
3831 (setq fs
(cons (list f n
) fs
)
3832 x
(gf-divide x f
) )))
3835 (defun gf-nonconst-random (q q^n
)
3837 (setq r
(random q^n
))
3838 (when (>= r q
) (return (let ((*gf-char
* q
)) (gf-n2x r
)))) ))
3840 ;; computes Tm(x) = x^2^(m-1) + x^2^(m-2) + .. + x^4 + x^2 + x in F2[x]
3842 (defun gf-trace-poly-f2 (x m red
) ;; m > 0
3843 (let ((tm (gf-nred x red
)))
3846 (declare (fixnum i
))
3847 (setq x
(gf-sq x red
)
3848 tm
(gf-plus tm x
) ))))
3850 ;; Cantor and Zassenhaus' algorithm
3852 (defun gf-equal-degree-factors (x-and-d q mult
)
3853 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3854 (let* ((x (car x-and-d
)) (d (cadr x-and-d
))
3856 (declare (fixnum d n
))
3858 ((= n d
) (list x mult
))
3860 (let* ((p^k
(cfactorw q
)) (p (car p^k
)) (k (cadr p^k
)) (*gf-char
* p
)
3861 (f '(0 1)) (q^n
(expt q n
)) m e r r^e
)
3863 (setq m
(* k d
)) ;; q = 2^k
3864 (setq e
(ash (1- (expt q d
)) -
1)) )
3866 (do () ((and (not (equal '(0 1) f
)) (not (equal x f
))))
3867 (setq r
(gf-nonconst-random q q^n
)
3869 (when (equal '(0 1) f
)
3871 (if (= 2 p
) (gf-trace-poly-f2 r m x
) ;; q = 2^k
3872 (gf-pow r e x
) )) ;; q is odd prime power
3873 (setq f
(gf-gcd x
(gf-nplus r^e
(gf-nminus (list 0 1))))) ))
3875 (append (gf-equal-degree-factors (list (gf-divide x f
) d
) q mult
)
3876 (gf-equal-degree-factors (list f d
) q mult
) ))))))
3878 ;; -----------------------------------------------------------------------------
3881 ;; gcd, gcdex and test of invertibility ----------------------------------------
3884 (defmfun $ef_gcd
(a b
)
3885 (ef-gf-field?
"ef_gcd")
3886 (let ((*ef-arith?
* t
))
3887 (gf-x2p (gf-gcd (gf-p2x a
) (gf-p2x b
))) ))
3889 (defmfun $gf_gcd
(a b
&optional p
)
3890 (let ((*ef-arith?
*))
3892 (p (unless (and (integerp p
) (primep p
))
3893 (gf-merror (intl:gettext
"`gf_gcd': ~m is not a prime number.") p
) )
3895 (let* ((*gf-char
* p
)
3897 (vars (caddar ($rat a
))) )
3898 (when (> (length vars
) 1)
3899 (gf-merror (intl:gettext
"`gf_gcd': Polynomials must be univariate.")) )
3900 (gf-x2p (gf-gcd (gf-p2x a
) (gf-p2x b
))) ))
3901 (t (gf-char?
"gf_gcd")
3902 (gf-x2p (gf-gcd (gf-p2x a
) (gf-p2x b
))) ))))
3905 (defmfun $gf_gcdex
(a b
)
3906 (gf-red?
"gf_gcdex")
3907 (let ((*ef-arith?
*))
3909 (mapcar #'gf-x2p
(gf-gcdex (gf-p2x a
) (gf-p2x b
) *gf-red
*)) )))
3911 (defmfun $ef_gcdex
(a b
)
3912 (ef-red?
"ef_gcdex")
3913 (let ((*ef-arith?
* t
))
3915 (mapcar #'gf-x2p
(gf-gcdex (gf-p2x a
) (gf-p2x b
) *gf-red
*)) )))
3918 (defmfun $gf_unit_p
(a)
3919 (gf-red?
"gf_unit_p")
3920 (let ((*ef-arith?
*))
3921 (gf-unit-p (gf-p2x a
) *gf-red
*) ))
3923 (defmfun $ef_unit_p
(a)
3924 (ef-red?
"ef_unit_p")
3925 (let ((*ef-arith?
* t
))
3926 (gf-unit-p (gf-p2x a
) *ef-red
*) ))
3928 (defun gf-unit-p (x red
)
3929 (= 0 (car (gf-gcd x red
))) )
3931 ;; -----------------------------------------------------------------------------
3934 ;; order -----------------------------------------------------------------------
3937 ;; group/element order
3939 (defmfun $gf_order
(&optional a
)
3940 (gf-data?
"gf_order")
3942 (a (let ((*ef-arith?
*))
3944 (when (and a
(or *gf-irred?
* (gf-unit-p a
*gf-red
*)))
3945 (gf-ord a
*gf-ord
* *gf-fs-ord
* *gf-red
*) )))
3948 (defmfun $ef_order
(&optional a
)
3949 (ef-data?
"ef_order")
3951 (a (let ((*ef-arith?
* t
))
3953 (when (and a
(or *ef-irred?
* (gf-unit-p a
*ef-red
*)))
3954 (gf-ord a
*ef-ord
* *ef-fs-ord
* *ef-red
*) )))
3957 ;; find the lowest value k for which a^k = 1
3959 (defun gf-ord (x ord fs-ord red
) ;; assume x # 0
3960 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3962 (declare (fixnum e
))
3963 (dolist (pe fs-ord ord
)
3965 e
(the fixnum
(cadr pe
))
3966 ord
(truncate ord
(expt p e
))
3967 z
(gf-pow$ x ord red
) ) ;; use exponentiation by precomputation
3970 (setq ord
(* ord p
))
3971 (when (= e
1) (return))
3973 (setq z
(gf-pow$ z p red
)) ))))
3975 (defun gf-ord-by-table (x)
3976 (let ((index (svref $gf_logs
(gf-x2n x
))))
3977 (truncate *gf-ord
* (gcd *gf-ord
* index
)) ))
3980 ;; Fq^n = F[x]/(f) is no field <=> f splits into factors
3982 ;; f = f1^e1 * ... * fk^ek where fi are irreducible of degree ni.
3984 ;; We compute the order of the group (F[x]/(fi^ei))* by
3986 ;; ((q^ni)^ei - (q^ni)^(ei-1)) = ((q^ni) - 1) * (q^ni)^(ei-1)
3988 ;; and ord((Fq^n)*) with help of the Chinese Remainder Theorem.
3990 (defun gf-group-order (q red
)
3991 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
3992 (let (ne-list q^n
(e 0) (ord 1))
3993 (declare (fixnum e
))
3994 (do ((x (gf-factor red q
))) ;; red is assumed to be a monic poly
3996 (push (list (caar x
) (cadr x
)) ne-list
) ;; (list ni ei), f = prod(fi^ei) with fi of degree ni
3999 (setq q^n
(expt q
(the fixnum
(car a
)))
4000 e
(the fixnum
(cadr a
))
4001 ord
(* ord
(1- q^n
) (expt q^n
(the fixnum
(1- e
)))) ))
4004 ;; -----------------------------------------------------------------------------
4007 ;; degree, minimal polynomial, trace and norm ----------------------------------
4011 ;; degree: Find the lowest value d for which x^(q^d) = x
4013 (defun gf-degree-errchk (a n fun
)
4014 (when (and (not (null a
)) (>= (car a
) n
))
4015 (gf-merror (intl:gettext
"`~m': Leading exponent must be smaller than ~m.") fun n
) ))
4017 (defmfun $gf_degree
(a)
4018 (gf-field?
"gf_degree")
4019 (let ((*ef-arith?
*))
4021 (gf-degree-errchk a
*gf-exp
* "gf_degree")
4022 (*f-deg a
*gf-exp
* *gf-red
* *gf-x^p-powers
*) ))
4024 (defmfun $ef_degree
(a)
4025 (ef-field?
"ef_degree")
4026 (let ((*ef-arith?
* t
))
4028 (gf-degree-errchk a
*ef-exp
* "ef_degree")
4029 (*f-deg a
*ef-exp
* *ef-red
* *ef-x^q-powers
*) ))
4031 (defun *f-deg
(x n red x^q-powers
)
4034 (declare (fixnum d
))
4035 (when (equal x
(gf-compose (svref x^q-powers d
) x red
)) ;; f(x)^q = f(x^q)
4039 ;; produce the minimal polynomial
4041 (defmfun $gf_minimal_poly
(a)
4042 (gf-field?
"gf_minimal_poly")
4043 (let ((*ef-arith?
*))
4045 (gf-degree-errchk a
*gf-exp
* "gf_minimal_poly")
4046 (gf-minpoly a
*gf-red
* *gf-x^p-powers
*) ))
4048 (defmfun $ef_minimal_poly
(a)
4049 (ef-field?
"ef_minimal_poly")
4050 (let ((*ef-arith?
* t
))
4052 (gf-degree-errchk a
*ef-exp
* "ef_minimal_poly")
4053 (gf-minpoly a
*ef-red
* *ef-x^q-powers
*) ))
4057 ;; f(z) = (z - x) (z - x ) (z - x ) ... (z - x ) , where d = degree(x)
4059 (defun gf-minpoly (x red x^q-powers
)
4062 (powers (list (gf-minus x
)))
4063 (prod (list 0 (list 0 1)))
4064 xqi zx cx
) (declare (fixnum n
))
4066 ((= i n
)) (declare (fixnum i
))
4067 (setq xqi
(gf-compose (svref x^q-powers i
) x red
))
4068 (when (equal x xqi
) (return))
4069 (push (gf-nminus xqi
) powers
) )
4070 (dolist (pow powers
)
4071 (setq zx
(gf-zx prod
)
4072 cx
(gf-ncx pow prod red
)
4073 prod
(gf-nzx+cx zx cx
)) )
4074 ($substitute
'$z
'$x
(gf-x2p (gf-nxx2x prod
))) )))
4076 (defun gf-zx (x) ;; (gf-zx '(3 (5 1 3 1) 2 (4 1))) -> (4 (5 1 3 1) 3 (4 1))
4077 ;; 3 5 3 2 4 4 5 3 3 4
4078 ;; z (x + x ) + z x -> z (x + x ) + z x
4079 (do* ((res (list (1+ (car x
)) (cadr x
)))
4080 (r (cdr res
) (cddr r
))
4081 (rx (cddr x
) (cddr rx
)) )
4083 (rplacd r
(list (1+ (car rx
)) (cadr rx
))) ))
4085 (defun gf-ncx (c x red
) ;; modifies x
4086 ;; (gf-ncx '(1 1) '(3 (4 1 3 1) 2 (2 1)) '(6 1))
4087 ;; -> (3 (5 1 4 1) 2 (3 1))
4089 (do ((r (cdr x
) (cddr r
)))
4091 (rplaca r
(gf-times c
(car r
) red
)) )))
4093 (defun gf-nzx+cx
(zx cx
) ;; modifies zx
4095 (s (cdr cx
) (cddr s
)) r
+s
)
4096 ((null (cdr r
)) (nconc zx
(list 0 (car s
))))
4097 (setq r
+s
(gf-plus (caddr r
) (car s
)))
4099 (rplaca (setq r
(cddr r
)) r
+s
)
4100 (rplacd r
(cdddr r
)) )))
4102 (defun gf-nxx2x (xx) ;; modifies xx
4103 ;; (gf-nxx2x '(4 (0 3) 2 (0 1))) -> (4 3 2 1)
4104 (do ((r (cdr xx
) (cddr r
)))
4106 (rplaca r
(cadar r
)) ))
4111 ;; trace(a) = a + a + a + .. + a
4113 (defmfun $gf_trace
(a)
4114 (gf-field?
"gf_trace")
4115 (let ((*ef-arith?
*))
4116 (gf-trace (gf-p2x a
) *gf-red
* *gf-x^p-powers
*) ))
4118 (defmfun $ef_trace
(a)
4119 (ef-field?
"ef_trace")
4120 (let ((*ef-arith?
* t
))
4121 (gf-trace (gf-p2x a
) *ef-red
* *ef-x^q-powers
*) ))
4123 (defun gf-trace (x red x^q-powers
)
4127 ((= i n
) (gf-x2p su
)) (declare (fixnum i
))
4128 (setq su
(gf-plus su
(gf-compose (svref x^q-powers i
) x red
))) )))
4131 ;; 2 (n-1) n 2 (n-1)
4132 ;; 1 + q + q + .. + q (q - 1)/(q - 1) q q q
4133 ;; norm(a) = a = a = a * a * a * .. * a
4135 (defmfun $gf_norm
(a)
4136 (gf-field?
"gf_norm")
4137 (let ((*ef-arith?
*))
4138 (gf-norm (gf-p2x a
) *gf-red
* *gf-x^p-powers
*) ))
4140 (defmfun $ef_norm
(a)
4141 (ef-field?
"ef_norm")
4142 (let ((*ef-arith?
* t
))
4143 (gf-norm (gf-p2x a
) *ef-red
* *ef-x^q-powers
*) ))
4145 (defun gf-norm (x red x^q-powers
)
4149 ((= i n
) (gf-x2p prod
)) (declare (fixnum i
))
4150 (setq prod
(gf-times prod
(gf-compose (svref x^q-powers i
) x red
) red
)) )))
4152 ;; -----------------------------------------------------------------------------
4155 ;; normal elements and normal basis --------------------------------------------
4158 ;; Tests if an element is normal
4160 (defmfun $gf_normal_p
(a)
4161 (gf-field?
"gf_normal_p")
4162 (let ((*ef-arith?
*)) (gf-normal-p (gf-p2x a
))) )
4164 (defun gf-normal-p (x)
4166 (let ((modulus *gf-char
*)
4167 (mat (gf-maybe-normal-basis x
)) )
4168 (equal ($rank mat
) *gf-exp
*) )))
4170 (defmfun $ef_normal_p
(a)
4171 (ef-field?
"ef_normal_p")
4172 (let ((*ef-arith?
* t
)) (ef-normal-p (gf-p2x a
))) )
4174 (defun ef-normal-p (x)
4176 (let ((mat (ef-maybe-normal-basis x
)) )
4177 (/= 0 ($ef_determinant mat
)) )))
4180 ;; Finds a normal element e in the field; that is,
4181 ;; an element for which the list [e, e^q, e^(q^2), ... , e^(q^(n-1))] is a basis
4183 (defmfun $gf_normal
()
4184 (gf-field?
"gf_normal")
4185 (let ((*ef-arith?
*))
4186 (gf-x2p (gf-normal *gf-char
* *gf-exp
* #'gf-normal-p
)) ))
4188 (defmfun $ef_normal
()
4189 (ef-field?
"ef_normal")
4190 (let ((*ef-arith?
* t
))
4191 (gf-x2p (gf-normal *gf-card
* *ef-exp
* #'ef-normal-p
)) ))
4193 (defun gf-normal (q n normal-p-fn
)
4194 (let* ((inc (min $gf_coeff_limit q
))
4195 (i-lim (expt inc n
))
4197 (do ((i inc
(1+ i
)))
4199 (gf-merror (intl:gettext
"No normal element found.~%~\
4200 `gf_coeff_limit' might be too small.~%" )) )
4201 (setq x
(let ((*gf-char
* inc
)) (gf-n2x i
)))
4202 (when (funcall normal-p-fn x
) (return-from gf-normal x
)) )))
4205 ;; Finds a normal element in the field by producing random elements and checking
4206 ;; if each one is normal
4208 (defmfun $gf_random_normal
()
4209 (gf-field?
"gf_random_normal")
4210 (let ((*ef-arith?
*)) (gf-x2p (gf-random-normal))) )
4212 (defun gf-random-normal ()
4213 (do ((x (gf-random *gf-char
* *gf-exp
*) (gf-random *gf-char
* *gf-exp
*)))
4214 ((gf-normal-p x
) x
) ))
4216 (defmfun $ef_random_normal
()
4217 (ef-field?
"ef_random_normal")
4218 (let ((*ef-arith?
* t
)) (gf-x2p (ef-random-normal))) )
4220 (defun ef-random-normal ()
4221 (do ((x (gf-random *gf-card
* *ef-exp
*) (gf-random *gf-card
* *ef-exp
*)))
4222 ((ef-normal-p x
) x
) ))
4224 ;; Produces a normal basis as a matrix;
4225 ;; the columns are the coefficients of the powers e^(q^i) of the normal element
4227 (defmfun $gf_normal_basis
(a)
4228 (gf-field?
"gf_normal_basis")
4229 (let* ((*ef-arith?
*)
4232 (mat (gf-maybe-normal-basis x
)) )
4233 (unless (equal ($rank mat
) *gf-exp
*)
4234 (gf-merror (intl:gettext
"Argument to `gf_normal_basis' must be a normal element.")) )
4237 (defmfun $ef_normal_basis
(a)
4238 (ef-field?
"ef_normal_basis")
4239 (let* ((*ef-arith?
* t
)
4240 (mat (ef-maybe-normal-basis (gf-p2x a
))) )
4241 (unless (/= 0 ($ef_determinant mat
))
4242 (merror (intl:gettext
"Argument to `ef_normal_basis' must be a normal element." )) )
4245 (defun gf-maybe-normal-basis (x)
4246 (*f-maybe-normal-basis x
*gf-x^p-powers
* *gf-exp
* *gf-red
*) )
4248 (defun ef-maybe-normal-basis (x)
4249 (*f-maybe-normal-basis x
*ef-x^q-powers
* *ef-exp
* *ef-red
*) )
4251 (defun *f-maybe-normal-basis
(x x^q-powers e red
)
4252 #+ (or ccl ecl gcl sbcl
) (declare (optimize (speed 3) (safety 0)))
4253 (declare (fixnum e
))
4254 (let ((e-1 (- e
1))) (declare (fixnum e-1
))
4257 #'(lambda (i j
) (declare (fixnum i j
))
4259 (gf-compose (svref x^q-powers
(1- i
)) x red
) e-1
) (1- j
)))
4262 ;; coefficients as an array of designated length
4264 (defun gf-x2array (x len
)
4265 #+ (or ccl ecl gcl sbcl
) (declare (optimize (speed 3) (safety 0)))
4266 (declare (fixnum len
))
4267 (let ((cs (make-array (1+ len
) :initial-element
0)))
4268 (do ((k len
)) ((null x
) cs
) (declare (fixnum k
))
4270 ((> k
(the fixnum
(car x
)))
4272 ((= k
(the fixnum
(car x
)))
4273 (setf (svref cs
(- len k
)) (cadr x
))
4277 (setq x
(cddr x
)) ) ))))
4279 ;; Produces the normal representation of an element as a list of coefficients
4280 ;; Needs the inverted normal basis as the second argument
4281 ;; (the inversion might be time consuming)
4283 (defmfun $gf_normal_basis_rep
(a m-inv
)
4284 (gf-field?
"gf_normal_basis_rep")
4285 (let ((*ef-arith?
*))
4286 (gf-normal-basis-rep (gf-p2x a
) m-inv
*gf-exp
* '$gf_matmult
) ))
4288 (defmfun $ef_normal_basis_rep
(a m-inv
)
4289 (ef-field?
"ef_normal_basis_rep")
4290 (let ((*ef-arith?
* t
))
4291 (gf-normal-basis-rep (gf-p2x a
) m-inv
*ef-exp
* '$ef_matmult
) ))
4293 (defun gf-normal-basis-rep (x m-inv e matmult-fn
)
4294 (let* ((cs (cons '(mlist simp
) (gf-x2l x e
)))
4295 (nbrep (mfuncall matmult-fn m-inv cs
)) )
4296 (cons '(mlist simp
) (mapcar #'cadr
(cdr nbrep
))) ))
4298 ;; -----------------------------------------------------------------------------
4301 ;; functions for matrices ------------------------------------------------------
4304 (defmfun $gf_matneg
(m)
4305 (gf-char?
"gf_matneg")
4306 (mfuncall '$matrixmap
'$gf_neg m
) )
4308 (defmfun $ef_matneg
(m)
4309 (ef-gf-field?
"ef_matneg")
4310 (mfuncall '$matrixmap
'$ef_neg m
) )
4313 ;; matrix addition (convenience: mat, list or poly possible as argument)
4315 (defmfun $gf_matadd
(&rest args
)
4316 (let ((*ef-arith?
*))
4317 (reduce #'(lambda (m1 m2
) (gf-matadd m1 m2
'$gf_add
)) args
) ))
4319 (defmfun $ef_matadd
(&rest args
)
4320 (gf-data?
"ef_matadd")
4321 (let ((*ef-arith?
* t
))
4322 (reduce #'(lambda (m1 m2
) (gf-matadd m1 m2
'$ef_add
)) args
) ))
4324 (defun gf-matadd (m1 m2 add-fn
)
4325 (when ($listp m1
) (setq m1
($transpose m1
)))
4326 (when ($listp m2
) (setq m2
($transpose m2
)))
4328 ((and ($matrixp m1
) ($matrixp m2
))
4329 (gf-matadd2 m1 m2 add-fn
) )
4331 (gf-matadd1 m1 m2 add-fn
) ) ;; assumed without checking: m2 is poly
4333 (gf-matadd1 m2 m1 add-fn
) )
4335 (mfuncall add-fn m1 m2
) ) ))
4337 (defun gf-matadd1 (m poly add-fn
)
4338 (do ((r (cdr m
) (cdr r
)) new
)
4339 ((null r
) (cons '($matrix simp
) (nreverse new
)))
4340 (push (cons '(mlist simp
)
4341 (mapcar #'(lambda (p) (mfuncall add-fn p poly
)) (cdar r
)) )
4344 (defun gf-matadd2-error ()
4346 (intl:gettext
"Arguments to `~m' must have same formal structure.")
4347 (if *ef-arith?
* "ef_matadd" "gf_matadd") ))
4349 (defun gf-matadd2 (m1 m2 add-fn
)
4350 (setq m1
(cdr m1
) m2
(cdr m2
))
4351 (unless (= (length (car m1
)) (length (car m2
)))
4352 (gf-matadd2-error) )
4353 (do ((r1 m1
(cdr r1
)) (r2 m2
(cdr r2
)) new
)
4354 ((or (null r1
) (null r2
))
4355 (unless (and (null r1
) (null r2
))
4356 (gf-matadd2-error) )
4357 (cons '($matrix simp
) (nreverse new
)) )
4358 (push (cons '(mlist simp
) (mapcar add-fn
(cdar r1
) (cdar r2
))) new
) ))
4361 ;; matrix multiplication (convenience: mat, list or poly possible as argument)
4363 (defmfun $gf_matmult
(&rest args
)
4364 (gf-red?
"gf_matmult")
4365 (let ((*ef-arith?
*))
4366 (apply #'*f-matmult
`($gf_mult
,@args
)) ))
4368 (defmfun $ef_matmult
(&rest args
)
4369 (ef-red?
"ef_matmult")
4370 (let ((*ef-arith?
* t
))
4371 (apply #'*f-matmult
`($ef_mult
,@args
)) ))
4373 (defun *f-matmult
(mult-fn &rest args
)
4375 #'(lambda (m1 m2
) (gf-matmult m1 m2 mult-fn
))
4376 (cons '(mlist simp
) args
) ))
4378 (defun gf-matmult (m1 m2 mult-fn
)
4379 (when ($listp m1
) (setq m1
(list '($matrix simp
) m1
)))
4380 (when ($listp m2
) (setq m2
($transpose m2
)))
4382 ((and ($matrixp m1
) ($matrixp m2
))
4383 (gf-matmult2 m1 m2
) )
4385 (gf-matmult1 m1 m2 mult-fn
) ) ;; assumed without checking: m2 is poly
4387 (gf-matmult1 m2 m1 mult-fn
) )
4389 (mfuncall mult-fn m1 m2
) ) ))
4391 (defun gf-matmult1 (m poly mult-fn
)
4392 (do ((r (cdr m
) (cdr r
)) new
)
4393 ((null r
) (cons '($matrix simp
) (nreverse new
)))
4394 (push (cons '(mlist simp
)
4395 (mapcar #'(lambda (p) (mfuncall mult-fn p poly
)) (cdar r
)) )
4398 (defun gf-matmult2 (m1 m2
)
4399 (setq m1
(cdr m1
) m2
(cdr ($transpose m2
)))
4400 (unless (= (length (car m1
)) (length (car m2
)))
4402 (intl:gettext
"`~m': attempt to multiply non conformable matrices.")
4403 (if *ef-arith?
* "ef_matmult" "gf_matmult") ))
4404 (let ((red (if *ef-arith?
* *ef-red
* *gf-red
*)) )
4405 (do ((r1 m1
(cdr r1
)) new-mat
)
4407 (if (and (not (eq nil $scalarmatrixp
))
4408 (= 1 (length new-mat
)) (= 1 (length (cdar new-mat
))) )
4410 (cons '($matrix simp
) (nreverse new-mat
)) ))
4411 (do ((r2 m2
(cdr r2
)) new-row
)
4413 (push (cons '(mlist simp
) (nreverse new-row
)) new-mat
) )
4416 (mapcar #'(lambda (a b
) (gf-times (gf-p2x a
) (gf-p2x b
) red
))
4417 (cdar r1
) (cdar r2
) )))
4420 ;; -----------------------------------------------------------------------------
4423 ;; invert and determinant by lu ------------------------------------------------
4426 (defun zn-p-errchk (p fun pos
)
4427 (unless (and p
(integerp p
) (primep p
))
4428 (gf-merror (intl:gettext
"`~m': ~m argument must be prime number.") fun pos
) ))
4432 ;; returns nil when LU-decomposition fails
4433 (defun try-lu-and-call (fun m ring
)
4434 (let ((old-out *standard-output
*)
4435 (redirect (make-string-output-stream))
4437 (unwind-protect ;; protect against lu failure
4438 (setq *standard-output
* redirect
;; discard error messages from lu-factor
4439 res
(mfuncall fun m ring
) )
4441 (setq *standard-output
* old-out
)
4443 (return-from try-lu-and-call res
) )))
4445 (defmfun $zn_invert_by_lu
(m p
)
4446 (zn-p-errchk p
"zn_invert_by_lu" "Second")
4447 (let ((*ef-arith?
*) (*gf-char
* p
))
4448 (try-lu-and-call '$invert_by_lu m
'gf-coeff-ring
) ))
4450 (defmfun $gf_matinv
(m)
4451 (format t
"`gf_matinv' is deprecated. ~%~\
4452 The user is asked to use `gf_invert_by_lu' instead.~%" )
4453 ($gf_invert_by_lu m
) )
4455 (defmfun $gf_invert_by_lu
(m)
4456 (gf-field?
"gf_invert_by_lu")
4457 (let ((*ef-arith?
*)) (try-lu-and-call '$invert_by_lu m
'gf-ring
)) )
4459 (defmfun $ef_invert_by_lu
(m)
4460 (ef-field?
"ef_invert_by_lu")
4461 (let ((*ef-arith?
* t
)) (try-lu-and-call '$invert_by_lu m
'ef-ring
)) )
4465 (defmfun $zn_determinant
(m p
)
4466 (zn-p-errchk p
"zn_determinant" "Second")
4467 (let* ((*ef-arith?
*)
4469 (det (try-lu-and-call '$determinant_by_lu m
'gf-coeff-ring
)) )
4471 (mod (mfuncall '$determinant m
) p
) )))
4473 (defmfun $gf_determinant
(m)
4474 (gf-field?
"gf_determinant")
4475 (let* ((*ef-arith?
*)
4476 (det (try-lu-and-call '$determinant_by_lu m
'gf-ring
)) )
4478 (let (($matrix_element_mult
'$gf_mult
) ($matrix_element_add
'$gf_add
))
4479 (mfuncall '$determinant m
) ))))
4481 (defmfun $ef_determinant
(m)
4482 (ef-field?
"ef_determinant")
4483 (let* ((*ef-arith?
* t
)
4484 (det (try-lu-and-call '$determinant_by_lu m
'ef-ring
)) )
4486 (let (($matrix_element_mult
'$ef_mult
) ($matrix_element_add
'$ef_add
))
4487 (mfuncall '$determinant m
) ))))
4489 ;; -----------------------------------------------------------------------------
4492 ;; discrete logarithm ----------------------------------------------------------
4495 ;; solve g^x = a in Fq^n, where g a generator of (Fq^n)*
4497 (defmfun $gf_index
(a)
4498 (gf-data?
"gf_index")
4499 (gf-log-errchk1 *gf-prim
* "gf_index")
4500 (let ((*ef-arith?
*))
4502 ($zn_log a
(gf-x2n *gf-prim
*) *gf-char
*)
4503 (gf-dlog (gf-p2x a
)) )))
4505 (defmfun $ef_index
(a)
4506 (ef-data?
"ef_index")
4507 (gf-log-errchk1 *ef-prim
* "ef_index")
4508 (let ((*ef-arith?
* t
))
4511 (let ((*ef-arith?
*)) (gf-dlog (gf-n2x (cadr a
))))
4514 (defmfun $gf_log
(a &optional b
)
4516 (gf-log-errchk1 *gf-prim
* "gf_log")
4517 (let ((*ef-arith?
*))
4520 ($zn_log a
(if b b
(gf-x2n *gf-prim
*)) *gf-char
*) ) ;; $zn_log checks if b is primitive
4523 (and b
(setq b
(gf-p2x b
)) (gf-log-errchk2 b
#'gf-prim-p
"gf_log"))
4528 (defun gf-log-errchk1 (prim fun
)
4530 (gf-merror (intl:gettext
"`~m': there is no primitive element.") fun
) )
4531 (when (equal prim
'$unknown
)
4532 (gf-merror (intl:gettext
"`~m': a primitive element is not known.") fun
) ))
4534 (defun gf-log-errchk2 (x prim-p-fn fun
)
4535 (unless (funcall prim-p-fn x
)
4536 (gf-merror (intl:gettext
4537 "Second argument to `~m' must be a primitive element." ) fun
)))
4539 (defmfun $ef_log
(a &optional b
)
4541 (gf-log-errchk1 *ef-prim
* "ef_log")
4542 (let ((*ef-arith?
* t
))
4544 (and b
(setq b
(gf-p2x b
)) (gf-log-errchk2 b
#'ef-prim-p
"ef_log")) )
4547 (let ((*ef-arith?
*))
4548 (setq a
(gf-n2x (cadr a
)))
4550 (gf-dlogb a
(gf-n2x (cadr b
)))
4553 (let ((*ef-arith?
* t
))
4558 (defun gf-dlogb (a b
)
4559 (*f-dlogb a b
#'gf-dlog
*gf-ord
*) )
4561 (defun ef-dlogb (a b
)
4562 (*f-dlogb a b
#'ef-dlog
*ef-ord
*) )
4564 (defun *f-dlogb
(a b dlog-fn ord
)
4565 (let* ((a-ind (funcall dlog-fn a
)) (b-ind (funcall dlog-fn b
))
4566 (d (gcd (gcd a-ind b-ind
) ord
))
4567 (m (truncate ord d
)) )
4568 (zn-quo (truncate a-ind d
) (truncate b-ind d
) m
) ))
4570 ;; Pohlig and Hellman reduction
4573 (*f-dlog a
*gf-prim
* *gf-red
* *gf-ord
* *gf-fs-ord
*) )
4576 (*f-dlog a
*ef-prim
* *ef-red
* *ef-ord
* *ef-fs-ord
*) )
4578 (defun *f-dlog
(a g red ord fs-ord
)
4579 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
4581 ((or (null a
) (null g
)) nil
)
4582 ((>= (car a
) (car red
)) nil
)
4583 ((equal '(0 1) a
) 0)
4585 ((not (gf-unit-p a red
)) nil
)
4587 (let (p (e 0) ord
/p om xp xk dlogs mods
(g-inv (gf-inv g red
)))
4588 (declare (fixnum e
))
4590 (setq p
(car f
) e
(cadr f
)
4591 ord
/p
(truncate ord p
)
4592 om
(gf-pow g ord
/p red
)
4594 (do ((b a
) (k 0) (pk 1) (acc g-inv
) (e1 (1- e
)))
4595 (()) (declare (fixnum k
))
4596 (setq xk
(gf-dlog-rho-brent (gf-pow b ord
/p red
) om p red
))
4599 (when (= k e
) (return))
4600 (setq ord
/p
(truncate ord
/p p
)
4602 (when (/= xk
0) (setq b
(gf-times b
(gf-pow acc xk red
) red
)))
4603 (when (/= k e1
) (setq acc
(gf-pow acc p red
))) )
4604 (push (expt p e
) mods
)
4606 (car (chinese dlogs mods
)) ))))
4608 ;; iteration for Pollard rho: b = g^y * a^z in each step
4610 (defun gf-dlog-f (b y z a g p red
)
4611 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
4612 (let ((m (mod (cadr b
) 3))) (declare (fixnum m
))
4615 (values (gf-sq b red
) (mod (ash y
1) p
) (mod (ash z
1) p
)) )
4616 ((= 1 m
) ;; avoid stationary case b=1 => b^2=1
4617 (values (gf-times g b red
) (mod (+ y
1) p
) z
) )
4619 (values (gf-times a b red
) y
(mod (+ z
1) p
) ) ) )))
4623 (defun gf-dlog-naive (a g red
)
4625 (gi '(0 1) (gf-times gi g red
)) )
4628 ;; baby-steps-giant-steps:
4630 (defun gf-dlog-baby-giant (a g p red
) ;; g is generator of order prime p
4631 (let* ((m (1+ (isqrt p
)))
4632 (s (floor (* 1.3 m
)))
4636 (make-hash-table :size s
:test
#'equal
:rehash-threshold
0.9) )
4639 (when (equal '(0 1) b
)
4641 (return-from gf-dlog-baby-giant r
) )
4642 (setf (gethash b babies
) r
)
4644 (when (= r m
) (return))
4645 (setq b
(gf-times gi b red
)) )
4646 (setq g^m
(gf-pow g m red
))
4648 (bb (list 0 1) (gf-times g^m bb red
))
4650 (when (setq r
(gethash bb babies
))
4652 (return (+ r
(* rr m
))) )) ))
4654 ;; Pollard rho for dlog computation (Brents variant of collision detection)
4656 (defun gf-dlog-rho-brent (a g p red
) ;; g is generator of order p
4657 #+ (or ccl ecl gcl
) (declare (optimize (speed 3) (safety 0)))
4659 ((equal '(0 1) a
) 0)
4661 ((equal a
(gf-sq g red
)) 2)
4662 ((equal '(0 1) (gf-times a g red
)) (1- p
))
4663 ((< p
32) (gf-dlog-naive a g red
))
4664 ((< p
1024) (gf-dlog-baby-giant a g p red
))
4666 (prog ((b (list 0 1)) (y 0) (z 0) ;; b = g^y * a^z
4667 (bb (list 0 1)) (yy 0) (zz 0) ;; bb = g^yy * a^zz
4670 (do ((i 0)(j 1)) (())
4671 (declare (fixnum i j
))
4672 (multiple-value-setq (b y z
) (gf-dlog-f b y z a g p red
))
4673 (when (equal b bb
) (return)) ;; g^y * a^z = g^yy * a^zz
4676 (setq j
(1+ (ash j
1)))
4677 (setq bb b yy y zz z
) ))
4678 (setq dy
(mod (- yy y
) p
) dz
(mod (- z zz
) p
)) ;; g^dy = a^dz = g^(x*dz)
4679 (when (= 1 (gcd dz p
))
4680 (return (zn-quo dy dz p
)) ) ;; x = dy/dz mod p (since g is generator of order p)
4684 yy
(1+ (random (1- p
)))
4685 zz
(1+ (random (1- p
)))
4686 bb
(gf-times (gf-pow g yy red
) (gf-pow a zz red
) red
) )
4689 ;; -----------------------------------------------------------------------------
4692 ;; nth root in Galois Fields ---------------------------------------------------
4695 (defmfun $ef_nth_root
(a r
)
4696 (ef-field?
"ef_nth_root")
4697 (unless (and (integerp r
) (> r
0))
4698 (gf-merror (intl:gettext
"Second argument to `ef_nth_root' must be a positive integer. Found ~m.") a r
) )
4699 (let* ((*ef-arith?
* t
)
4700 (rts (gf-nrt (gf-p2x a
) r
*ef-red
* *ef-ord
*)) )
4701 (gf-nrt-exit rts
) ))
4703 (defmfun $gf_nth_root
(a r
)
4704 (gf-field?
"gf_nth_root")
4705 (unless (and (integerp r
) (> r
0))
4706 (gf-merror (intl:gettext
"Second argument to `gf_nth_root' must be a positive integer. Found ~m.") a r
) )
4707 (let* ((*ef-arith?
*)
4708 (rts (gf-nrt (gf-p2x a
) r
*gf-red
* *gf-ord
*)) )
4709 (gf-nrt-exit rts
) ))
4711 (defun gf-nrt-exit (rts)
4713 (setq rts
(mapcar #'gf-n2x
(sort (mapcar #'gf-x2n rts
) #'<)))
4714 (cons '(mlist simp
) (mapcar #'gf-x2p rts
)) ))
4716 ;; e.g. r=i*i*j*k, then x^(1/r) = (((x^(1/i))^(1/i))^(1/j))^(1/k)
4717 (defun gf-nrt (x r red ord
)
4718 (setq x
(gf-nred x red
))
4722 ((or (= 1 r
) (primep r
)) (setq rts
(gf-amm x r red ord
)))
4724 (let* (($intfaclim
) (rs (get-factor-list r
)))
4725 (setq rs
(sort rs
#'< :key
#'car
))
4729 #'(lambda (pe) (apply #'(lambda (p e
) (make-list e
:initial-element p
)) pe
))
4731 (setq rts
(gf-amm x
(car rs
) red ord
))
4732 (dolist (r (cdr rs
))
4733 (setq rts
(apply #'nconc
(mapcar #'(lambda (y) (gf-amm y r red ord
)) rts
))) ))))
4736 ;; inspired by Bach,Shallit 7.3.2
4737 (defun gf-amm (x r red ord
) ;; r prime, red irreducible
4740 (let (k n e u s m re xr xu om g br bu ab alpha beta rt
)
4743 ((and (= 0 (setq m
(mod ord
2)))
4744 (/= 1 (gf-jacobi x red
(if *ef-arith?
* *gf-card
* *gf-char
*))) )
4745 (return-from gf-amm nil
) )
4746 ((= 1 m
) ;; q = 2^n : unique solution
4748 `(,(gf-pow x
(ash (+ (ash ord
1) 2) -
2) red
)) ))
4750 (setq rt
(gf-pow x
(ash (+ ord
2) -
2) red
))
4751 (return-from gf-amm
`(,rt
,(gf-minus rt
))) )
4753 (let* ((twox (gf-plus x x
))
4754 (y (gf-pow twox
(ash (- ord
4) -
3) red
))
4755 (i (gf-times twox
(gf-times y y red
) red
))
4756 (rt (gf-times x
(gf-times y
(gf-nplus i
`(0 ,(gf-cminus-b 1))) red
) red
)) )
4757 (return-from gf-amm
`(,rt
,(gf-minus rt
))) ))))
4758 (multiple-value-setq (s m
) (truncate ord r
))
4759 (when (and (= 0 m
) (not (equal '(0 1) (gf-pow x s red
))))
4760 (return-from gf-amm nil
))
4761 ;; r = 3, first 2 cases:
4764 ((= 1 (setq m
(mod ord
3))) ;; unique solution
4766 `(,(gf-pow x
(truncate (1+ (ash ord
1)) 3) red
)) ))
4767 ((= 2 m
) ;; unique solution
4769 `(,(gf-pow x
(truncate (1+ ord
) 3) red
)) ))))
4770 ;; compute u,e with ord = u*r^e and r,u coprime:
4773 (multiple-value-setq (u1 m
) (truncate u1 r
))
4774 (when (/= 0 m
) (return))
4775 (setq u u1 e
(1+ e
)) )
4778 (setq rt
(gf-pow x
(inv-mod r u
) red
)) ;; unique solution, see Bach,Shallit 7.3.1
4782 (do () ((not (equal '(0 1) (gf-pow n s red
)))) ;; n is no r-th power
4783 (setq n
(gf-n2x (1+ (gf-x2n n
)))) )
4784 (setq g
(gf-pow n u red
)
4786 om
(gf-pow g
(truncate re r
) red
) ) ;; r-th root of unity
4788 ((or (/= 3 r
) (= 0 (setq m
(mod ord
9))))
4789 (setq xr
(gf-pow x u red
)
4790 xu
(gf-pow x re red
)
4791 k
(*f-dlog xr g red re
`((,r
,e
))) ;; g^k = xr
4792 br
(gf-pow g
(truncate k r
) red
) ;; br^r = xr
4793 bu
(gf-pow xu
(inv-mod r u
) red
) ;; bu^r = xu
4794 ab
(cdr (zn-gcdex1 u re
))
4797 (if (< alpha
0) (incf alpha ord
) (incf beta ord
))
4798 (setq rt
(gf-times (gf-pow br alpha red
) (gf-pow bu beta red
) red
)) )
4799 ;; r = 3, remaining cases:
4801 (setq rt
(gf-pow x
(truncate (+ (ash ord
1) 3) 9) red
)) )
4803 (setq rt
(gf-pow x
(truncate (+ ord
3) 9) red
)) ))
4804 (do ((i 1 (1+ i
)) (j (list 0 1)) (res (list rt
)))
4806 (setq j
(gf-times j om red
))
4807 (push (gf-times rt j red
) res
) ))))))
4809 ;; -----------------------------------------------------------------------------
4812 ;; tables of small fields ------------------------------------------------------
4815 (defmfun $gf_add_table
()
4816 (gf-data?
"gf_add_table")
4817 (let ((*ef-arith?
*)) (gf-add-table *gf-card
*)) )
4819 (defmfun $ef_add_table
()
4820 (ef-data?
"ef_add_table")
4821 (let ((*ef-arith?
* t
)) (gf-add-table *ef-card
*)) )
4823 (defun gf-add-table (card)
4826 (gf-x2n (gf-plus (gf-n2x (1- i
)) (gf-n2x (1- j
)))) )
4830 (defmfun $gf_mult_table
(&optional all?
)
4831 (gf-data?
"gf_mult_table")
4832 (let ((*ef-arith?
*))
4833 (gf-mult-table *gf-red
* *gf-irred?
* *gf-card
* all?
) ))
4835 (defmfun $ef_mult_table
(&optional all?
)
4836 (ef-data?
"ef_mult_table")
4837 (let ((*ef-arith?
* t
))
4838 (gf-mult-table *ef-red
* *ef-irred?
* *ef-card
* all?
) ))
4840 (defun gf-mult-table (red irred? card all?
)
4843 ((or irred?
;; field
4844 (equal all?
'$all
) )
4847 (gf-x2n (gf-times (gf-n2x i
) (gf-n2x j
) red
)))
4851 (do ((i 1 (1+ i
)) x
)
4854 (when (gf-unit-p x red
) (push x units
)) )
4855 (dolist (x units
(cons '($matrix simp
) (nreverse res
)))
4858 (mapcar #'(lambda (y) (gf-x2n (gf-times x y red
))) units
) )
4861 (defmfun $gf_power_table
(&rest args
)
4862 (gf-data?
"gf_power_table")
4863 (let ((*ef-arith?
*) all? cols
)
4864 (multiple-value-setq (all? cols
)
4865 (apply #'gf-power-table-args
(cons "gf_power_table" args
)) )
4867 (setq cols
*gf-ord
*)
4868 (when all?
(incf cols
)) )
4869 (gf-power-table *gf-red
* *gf-irred?
* *gf-card
* cols all?
) ))
4871 (defmfun $ef_power_table
(&rest args
)
4872 (ef-data?
"ef_power_table")
4873 (let ((*ef-arith?
* t
) all? cols
)
4874 (multiple-value-setq (all? cols
)
4875 (apply #'gf-power-table-args
(cons "ef_power_table" args
)) )
4877 (setq cols
*ef-ord
*)
4878 (when all?
(incf cols
)) )
4879 (gf-power-table *ef-red
* *ef-irred?
* *ef-card
* cols all?
) ))
4881 (defun gf-power-table-args (&rest args
)
4882 (let ((str (car args
)) all? cols
(x (cadr args
)) (y (caddr args
)))
4885 ((integerp x
) (setq cols x
))
4886 ((equal x
'$all
) (setq all? t
))
4887 (t (gf-merror (intl:gettext
4888 "First argument to `~m' must be `all' or a small fixnum." ) str
))))
4891 ((and (integerp x
) (equal y
'$all
)) (setq all? t
))
4892 ((and (equal x
'$all
) (integerp y
)) (setq cols y
))
4893 (t (format t
"Only the first argument to `~a' has been used.~%" str
) )))
4894 (values all? cols
) ))
4896 (defun gf-power-table (red irred? card cols all?
)
4900 #'(lambda (i j
) (gf-x2n (gf-pow (gf-n2x i
) j red
)))
4904 (do ((i 1 (1+ i
)) x res
)
4905 ((= i card
) (cons '($matrix simp
) (nreverse res
)))
4907 (when (gf-unit-p x red
)
4910 (mapcar #'(lambda (j) (gf-x2n (gf-pow x j red
)))
4911 (cdr (mfuncall '$makelist
'$j
'$j
1 cols
)) ))
4914 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;