Rename *ll* and *ul* to ll and ul in method-by-limits
[maxima.git] / src / numth.lisp
blob7d37bd35a77cbe3becccd1309302ac84c681a27e
1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancements. ;;;;;
4 ;;; ;;;;;
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
11 (in-package :maxima)
13 ;;; *****************************************************************
14 ;;; ***** NUMTH ******* VARIOUS NUMBER THEORY FUNCTIONS *************
15 ;;; *****************************************************************
17 (macsyma-module numth)
19 (load-macsyma-macros rzmac)
21 ;;; Sum of divisors and Totient functions
23 ;; compute the sum of the k'th powers of the divisors of the absolute
24 ;; value of n
26 (defmfun $divsum (n &optional (k 1))
27 (if (and (integerp k) (integerp n))
28 (let ((n (abs n)))
29 (if (minusp k)
30 (list '(rat) (divsum n (- k)) (expt n (- k)))
31 (divsum n k)))
32 (list '($divsum) n k)))
34 ;; divsum assumes its arguments to be non-negative integers.
36 (defun divsum (n k)
37 (declare (type (integer 0) n k) (optimize (speed 3)))
38 (let (($intfaclim nil)) ;get-factor-list returns list
39 ;of (p_rime e_xponent) pairs
40 ;e is a fixnum
41 (cond ((<= n 1) 1)
42 ((zerop k) (reduce #'* (get-factor-list n) ; product over e+1
43 :key #'(lambda (pe) (1+ (the fixnum (cadr pe))))))
44 (t (reduce #'* (get-factor-list n) ; product over ((p^k)^(e+1)-1)/(p^k-1)
45 :key #'(lambda (pe)
46 (let ((p (car pe)) (e (cadr pe)))
47 (declare (type (integer 2) p)
48 (type (integer 1 (#.most-positive-fixnum)) e))
49 (let ((tmp (expt p k)))
50 (truncate (1- (expt tmp (1+ e)))
51 (1- tmp))))))))))
53 ;; totient computes the euler totient function
54 ;; i.e. the count of numbers relatively prime to n between 1 and n.
56 (defmfun $totient (n)
57 (if (integerp n)
58 (totient (abs n))
59 (list '($totient) n)))
61 ;; totient assumes its argument to be an integer.
62 ;; the exponents in the prime factorization of n are assumed to be
63 ;; fixnums (anything else would exceed memory).
65 (defun totient (n)
66 (declare (type (integer 0) n) (optimize (speed 3)))
67 (let (($intfaclim nil))
68 (if (< n 2)
70 (reduce #'* (get-factor-list n)
71 :key #'(lambda (pe)
72 (let ((p (car pe)) (e (cadr pe)))
73 (declare (type (integer 2) p)
74 (type (integer 1 (#.most-positive-fixnum)) e))
75 (* (1- p) (expt p (1- e)))))))))
77 ;;; JACOBI symbol and Gaussian factoring
79 ;; $jacobi/jacobi implement the Kronecker-Jacobi symbol, a light
80 ;; generalization of the Legendre/Jacobi symbols.
81 ;; (for a prime b, $jacobi(a b) is the Legendre symbol).
83 ;; The implementation follows algorithm 1.4.10 in 'A Course in
84 ;; Computational Algebraic Number Theory' by H. Cohen
86 (defmfun $jacobi (a b)
87 (if (and (integerp a) (integerp b))
88 (jacobi a b)
89 `(($jacobi) ,a ,b)))
91 (defun jacobi (a b)
92 (declare (integer a b) (optimize (speed 3)))
93 (cond ((zerop b)
94 (if (= 1 (abs a)) 1 0))
95 ((and (evenp a) (evenp b))
98 (let ((k 1)
99 (tab2 (make-array 8 :element-type '(integer -1 1)
100 :initial-contents #(0 1 0 -1 0 -1 0 1))))
101 (declare (type (integer -1 1) k))
103 (loop for v fixnum = 0 then (1+ v) ;remove 2's from b
104 while (evenp b) do (setf b (ash b -1))
105 finally (when (oddp v)
106 (setf k (aref tab2 (logand a 7))))) ; (-1)^(a^2-1)/8
108 (when (minusp b)
109 (setf b (- b))
110 (when (minusp a)
111 (setf k (- k))))
113 (loop
114 (when (zerop a)
115 (return-from jacobi (if (> b 1) 0 k)))
117 (loop for v fixnum = 0 then (1+ v)
118 while (evenp a) do (setf a (ash a -1))
119 finally (when (oddp v)
120 (when (minusp (aref tab2 (logand b 7))); (-1)^(b^2-1)/8
121 (setf k (- k)))))
123 (when (plusp (logand a b 2)) ;compute (-1)^(a-1)(b-1)/4
124 (setf k (- k)))
126 (let ((r (abs a)))
127 (setf a (rem b r))
128 (setf b r)))))))
131 ;; factor over the gaussian primes
133 (declaim (inline gctimes gctime1))
135 (defmfun $gcfactor (n)
136 (let ((n (cdr ($totaldisrep ($bothcoef ($rat ($rectform n) '$%i) '$%i)))))
137 (if (not (and (integerp (first n)) (integerp (second n))))
138 (gcdisp (nreverse n))
139 (loop for (term exp) on (gcfactor (second n) (first n)) by #'cddr
140 with res = () do
141 (push (if (= exp 1)
142 (gcdisp term)
143 (list '(mexpt simp) (gcdisp term) exp))
144 res)
145 finally
146 (return (cond ((null res)
148 ((null (cdr res))
149 (if (mexptp (car res))
150 `((mexpt simp gcfactored) ,@(cdar res))
151 (car res)))
153 `((mtimes simp gcfactored) ,@(nreverse res)))))))))
155 (defun imodp (p)
156 (declare (integer p) (optimize (speed 3)))
157 (cond ((not (= (rem p 4) 1)) nil)
158 ((= (rem p 8) 5) (power-mod 2 (ash (1- p) -2) p))
159 ((= (rem p 24) 17) (power-mod 3 (ash (1- p) -2) p)) ;p=2(mod 3)
160 (t (do ((i 5 (+ i j)) ;p=1(mod 24)
161 (j 2 (- 6 j)))
162 ((= (jacobi i p) -1) (power-mod i (ash (1- p) -2) p))
163 (declare (integer i) (fixnum j))))))
165 (defun psumsq (p)
166 (declare (integer p) (optimize (speed 3)))
167 (if (= p 2)
168 (list 1 1)
169 (let ((x (imodp p)))
170 (if (null x)
172 (do ((sp (isqrt p))
173 (r1 p r2)
174 (r2 x (rem r1 r2)))
175 ((not (> r1 sp)) (list r1 r2))
176 (declare (integer r1 r2)))))))
178 (defun gctimes (a b c d)
179 (declare (integer a b c d) (optimize (speed 3)))
180 (list (- (* a c) (* b d))
181 (+ (* a d) (* b c))))
183 (defun gcdisp (term)
184 (if (atom term)
185 term
186 (let ((rp (car term))
187 (ip (cadr term)))
188 (setq ip (if (eql ip 1) '$%i `((mtimes) ,ip $%i)))
189 (if (eql 0 rp)
191 `((mplus) ,rp ,ip)))))
193 (defun gcfactor (a b)
194 (declare (integer a b) (optimize (speed 3)))
195 (when (and (zerop a) (zerop b))
196 (return-from gcfactor (list 0 1)))
197 (let* (($intfaclim nil)
198 (e1 0)
199 (e2 0)
200 (econt 0)
201 (g (gcd a b))
202 (a (truncate a g))
203 (b (truncate b g))
204 (p 0)
205 (nl (cfactorw (+ (* a a) (* b b))))
206 (gl (cfactorw g))
207 (cd nil)
208 (dc nil)
209 (ans nil)
210 (plis nil))
211 (declare (integer e1 e2 econt g a b))
212 (when (= 1 (the integer (car gl))) (setq gl nil))
213 (when (= 1 (the integer (car nl))) (setq nl nil))
214 (tagbody
215 loop
216 (cond ((null gl)
217 (if (null nl)
218 (go ret)
219 (setq p (car nl))))
220 ((null nl)
221 (setq p (car gl)))
223 (setq p (max (the integer (car gl))
224 (the integer (car nl))))))
225 (setq cd (psumsq p))
226 (cond ((null cd)
227 (setq plis (list* p (cadr gl) plis))
228 (setq gl (cddr gl))
229 (go loop))
230 ((equal p (car nl))
231 (cond ((zerop (rem (+ (* a (the integer (car cd))) ; gcremainder
232 (* b (the integer (cadr cd))))
233 (the integer p))) ; remainder(real((a+bi)cd~),p)
234 ; z~ is complex conjugate
235 (setq e1 (cadr nl))
236 (setq dc cd))
238 (setq e2 (cadr nl))
239 (setq dc (reverse cd))))
240 (setq dc (gcexpt dc (the integer (cadr nl)))
241 dc (gctimes a b (the integer (car dc)) (- (the integer (cadr dc))))
242 a (truncate (the integer (car dc)) (the integer p))
243 b (truncate (the integer (cadr dc)) (the integer p))
244 nl (cddr nl))))
245 (when (equal p (car gl))
246 (incf econt (the integer (cadr gl)))
247 (cond ((equal p 2)
248 (incf e1 (* 2 (the integer (cadr gl)))))
250 (incf e1 (the integer (cadr gl)))
251 (incf e2 (the integer (cadr gl)))))
252 (setq gl (cddr gl)))
253 (unless (zerop e1)
254 (setq ans (list* cd e1 ans))
255 (setq e1 0))
256 (unless (zerop e2)
257 (setq ans (list* (reverse cd) e2 ans))
258 (setq e2 0))
259 (go loop)
261 ret
262 (let* ((cd (gcexpt (list 0 -1) (rem econt 4)))
263 (rcd (first cd))
264 (icd (second cd))
265 (l (mapcar #'signum (gctimes a b rcd icd)))
266 (r (first l))
267 (i (second l)))
268 (declare (integer r i rcd icd))
269 (when (or (= r -1) (= i -1))
270 (setq plis (list* -1 1 plis)))
271 (when (zerop r)
272 (setq ans (list* '(0 1) 1 ans)))
273 (return-from gcfactor (append plis ans))))))
275 (defun gcsqr (a)
276 (declare (optimize (speed 3)))
277 (let ((r (first a))
278 (i (second a)))
279 (declare (integer r i))
280 (list (+ (* r r) (* i i)) (ash (* r i) 1))))
282 (defun gctime1 (a b)
283 (gctimes (car a) (cadr a) (car b) (cadr b)))
285 (defun gcexpt (a n)
286 (cond ((zerop n) '(1 0))
287 ((= n 1) a)
288 ((evenp n) (gcexpt (gcsqr a) (ash n -1)))
289 (t (gctime1 a (gcexpt (gcsqr a) (ash n -1))))))
291 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
293 ;; Maxima functions in (Z/nZ)*
295 ;; zn_order, zn_primroot_p, zn_primroot, zn_log,
296 ;; solve_congruences, zn_characteristic_factors, zn_factor_generators, zn_nth_root,
297 ;; zn_add_table, zn_mult_table, zn_power_table
299 ;; 2012 - 2020, Volker van Nek
302 ;; Maxima option variables:
303 (defmvar $zn_primroot_limit 1000 "Upper bound for `zn_primroot'." fixnum)
304 (defmvar $zn_primroot_verbose nil "Print message when `zn_primroot_limit' is reached." boolean)
305 (defmvar $zn_primroot_pretest nil "`zn_primroot' pretests whether (Z/nZ)* is cyclic." boolean)
307 (declaim (inline zn-quo))
308 (defun zn-quo (n d p)
309 (declare (integer n d p) (optimize (speed 3)))
310 (mod (* n (inv-mod d p)) p) )
312 ;; compute the order of x in (Z/nZ)*
314 ;; optional argument: ifactors of totient(n) as returned in Maxima by
315 ;; block([factors_only:false], ifactors(totient(n)))
316 ;; e.g. [[2, 3], [3, 1], ... ]
318 (defmfun $zn_order (x n &optional fs-phi)
319 (unless (and (integerp x) (integerp n))
320 (return-from $zn_order
321 (if fs-phi
322 (list '($zn_order) x n fs-phi)
323 (list '($zn_order) x n) )))
324 (setq x (mod x n))
325 (cond
326 ((= 0 x) nil)
327 ((= 1 x) (if (= n 1) nil 1))
328 ((/= 1 (gcd x n)) nil)
330 (if fs-phi
331 (if (and ($listp fs-phi) ($listp (cadr fs-phi)))
332 (progn
333 (setq fs-phi (mapcar #'cdr (cdr fs-phi))) ; Lispify fs-phi
334 (setq fs-phi (cons (totient-from-factors fs-phi) fs-phi)) )
335 (gf-merror (intl:gettext
336 "Third argument to `zn_order' must be of the form [[p1, e1], ..., [pk, ek]]." )))
337 (setq fs-phi (totient-with-factors n)) )
338 (zn-order x
340 (car fs-phi) ;; phi
341 (cdr fs-phi)) ))) ;; factors of phi with multiplicity
343 (defun zn_order (x n phi fs-phi)
344 (format t "`zn_order' is deprecated. ~%Please use `zn-order' instead.~%" )
345 (zn-order x n phi fs-phi) )
347 ;; compute order of x as a divisor of the known group order
349 (defun zn-order (x n ord fs-ord)
350 (let (p e z)
351 (dolist (f fs-ord ord)
352 (setq p (car f) e (cadr f)
353 ord (truncate ord (expt p e))
354 z (power-mod x ord n) )
355 ;; ord(z) = p^i, i from [0,e]
356 ;; replace p^e in ord by p^i : x^(ord*p^i/p^e) = 1
357 (do ()
358 ((= z 1))
359 (setq ord (* ord p))
360 (when (= e 1) (return))
361 (decf e)
362 (setq z (power-mod z p n)) ))))
365 ;; compute totient (euler-phi) of n and its factors in one function
367 ;; returns a list of the form (phi ((p1 e1) ... (pk ek)))
369 (defun totient-with-factors (n)
370 (let (($intfaclim) (phi 1) fs-n (fs) p e (fs-phi) g)
371 (setq fs-n (get-factor-list n))
372 (dolist (f fs-n fs)
373 (setq p (car f) e (cadr f))
374 (setq phi (* phi (1- p) (expt p (1- e))))
375 (when (> e 1) (setq fs (cons `(,p ,(1- e)) fs)))
376 (setq fs (append (get-factor-list (1- p)) fs)) )
377 (setq fs (copy-tree fs)) ;; this deep copy is a workaround to avoid references
378 ;; to the list returned by ifactor.lisp/get-factor-list.
379 ;; see bug 3510983
380 (setq fs (sort fs #'< :key #'car))
381 (setq g (car fs))
382 (dolist (f (cdr fs) (cons phi (reverse (cons g fs-phi))))
383 (if (= (car f) (car g))
384 (incf (cadr g) (cadr f)) ;; assignment
385 (progn
386 (setq fs-phi (cons g fs-phi))
387 (setq g f) ))) ))
389 ;; recompute totient from given factors
391 ;; fs-phi: factors of totient with multiplicity: ((p1 e1) ... (pk ek))
393 (defun totient-from-factors (fs-phi)
394 (let ((phi 1) p e)
395 (dolist (f fs-phi phi)
396 (setq p (car f) e (cadr f))
397 (setq phi (* phi (expt p e))) )))
400 ;; for n > 2 is x a primitive root modulo n
401 ;; when n does not divide x
402 ;; and for all prime factors p of phi = totient(n)
403 ;; x^(phi/p) mod n # 1
405 ;; optional argument: ifactors of totient(n)
407 (defmfun $zn_primroot_p (x n &optional fs-phi)
408 (unless (and (integerp x) (integerp n))
409 (return-from $zn_primroot_p
410 (if fs-phi
411 (list '($zn_primroot_p) x n fs-phi)
412 (list '($zn_primroot_p) x n) )))
413 (setq x (mod x n))
414 (cond
415 ((= 0 x) nil)
416 ((= 1 x) (if (= n 2) t nil))
417 ((<= n 2) nil)
419 (if fs-phi
420 (if (and ($listp fs-phi) ($listp (cadr fs-phi)))
421 (progn
422 (setq fs-phi (mapcar #'cdr (cdr fs-phi))) ; Lispify fs-phi
423 (setq fs-phi (cons (totient-from-factors fs-phi) fs-phi)) )
424 (gf-merror (intl:gettext
425 "Third argument to `zn_primroot_p' must be of the form [[p1, e1], ..., [pk, ek]]." )))
426 (setq fs-phi (totient-with-factors n)) )
427 (zn-primroot-p x n
428 (car fs-phi) ;; phi
429 (mapcar #'car (cdr fs-phi))) ))) ;; factors only (omitting multiplicity)
431 (defun zn-primroot-p (x n phi fs-phi)
432 (unless (= 1 (gcd x n))
433 (return-from zn-primroot-p nil) )
434 (dolist (p fs-phi t)
435 (when (= 1 (power-mod x (truncate phi p) n))
436 (return-from zn-primroot-p nil) )))
439 ;; find the smallest primitive root modulo n
441 ;; optional argument: ifactors of totient(n)
443 (defmfun $zn_primroot (n &optional fs-phi)
444 (unless (integerp n)
445 (return-from $zn_primroot
446 (if fs-phi
447 (list '($zn_primroot) n fs-phi)
448 (list '($zn_primroot) n) )))
449 (cond
450 ((<= n 1) nil)
451 ((= n 2) 1)
453 (when $zn_primroot_pretest
454 (unless (cyclic-p n)
455 (return-from $zn_primroot nil) ))
456 (if fs-phi
457 (if (and ($listp fs-phi) ($listp (cadr fs-phi)))
458 (progn
459 (setq fs-phi (mapcar #'cdr (cdr fs-phi))) ; Lispify fs-phi
460 (setq fs-phi (cons (totient-from-factors fs-phi) fs-phi)) )
461 (gf-merror (intl:gettext
462 "Second argument to `zn_primroot' must be of the form [[p1, e1], ..., [pk, ek]]." )))
463 (setq fs-phi (totient-with-factors n)) )
464 (zn-primroot n
465 (car fs-phi) ;; phi
466 (mapcar #'car (cdr fs-phi))) ))) ;; factors only (omitting multiplicity)
468 ;; (Z/nZ)* is cyclic if n = 2, 4, p^k or 2*p^k where p prime > 2
469 (defun cyclic-p (n)
470 (prog ()
471 (when (< n 2) (return))
472 (when (< n 8) (return t)) ;; 2,3,4,5,2*3,7
473 (when (evenp n) ;; 2*p^k
474 (setq n (ash n -1)) ;; -> p^k
475 (when (evenp n) (return)) )
476 (let (($intfaclim) fs (len 0))
477 (multiple-value-setq (n fs) (get-small-factors n))
478 (when fs (setq len (length fs)))
479 (when (= 1 n) (return (= 1 len)))
480 (when (> len 0) (return))
481 (when (primep n) (return t))
482 (setq fs (convert-list (get-large-factors n)))
483 (return (= 1 (length fs))) )))
485 (defun zn-primroot (n phi fs-phi)
486 (do ((i 2 (1+ i)))
487 ((= i n) nil)
488 (when (zn-primroot-p i n phi fs-phi)
489 (return i) )
490 (when (= i $zn_primroot_limit)
491 (when $zn_primroot_verbose
492 (format t "`zn_primroot' stopped at zn_primroot_limit = ~A~%" $zn_primroot_limit) )
493 (return nil) )))
496 ;; Chinese Remainder Theorem
499 (defmfun ($chinese :deprecated-p $solve_congruences) (&rest a)
500 (apply '$solve_congruences a))
502 (defmfun $solve_congruences (rems mods &optional (return-lcm? nil))
503 (cond
504 ((not (and ($listp rems) ($listp mods)))
505 (list '($solve_congruences) rems mods) )
506 ((let ((lr ($length rems)) (lm ($length mods)))
507 (or (= 0 lr) (= 0 lm) (/= lr lm)) )
508 (gf-merror (intl:gettext
509 "Unsuitable arguments to `solve_congruences': ~m ~m" ) rems mods ))
510 ((notevery #'integerp (setq rems (cdr rems)))
511 (list '($solve_congruences) (cons '(mlist simp) rems) mods) )
512 ((notevery #'integerp (setq mods (cdr mods)))
513 (list '($solve_congruences) (cons '(mlist simp) rems) (cons '(mlist simp) mods)) )
514 ((eql return-lcm? '$lcm)
515 (cons '(mlist simp) (solve-congruences rems mods)) )
517 (car (solve-congruences rems mods)) )))
519 (defun solve-congruences (rems mods)
520 (if (= 1 (length mods))
521 (list (car rems) (car mods))
522 (let ((rp (car rems))
523 (p (car mods))
524 (rq-q (solve-congruences (cdr rems) (cdr mods))) )
525 (when rq-q
526 (let* ((rq (car rq-q))
527 (q (cadr rq-q))
528 (gc (zn-gcdex2 q p))
529 (g (car gc)) ;; gcd
530 (c (cadr gc)) ) ;; CRT-coefficient
531 (cond
532 ((= 1 g) ;; coprime moduli
533 (let* ((h (mod (* (- rp rq) c) p))
534 (x (+ (* h q) rq)) )
535 (list x (* p q)) ))
536 ((= 0 (mod (- rp rq) g)) ;; ensures unique solution
537 (let* ((h (* (- rp rq) c))
538 (q/g (truncate q g))
539 (lcm-pq (* p q/g)) )
540 (list (mod (+ rq (* h q/g)) lcm-pq) lcm-pq) ))))))))
542 ;; (zn-gcdex2 x y) returns `(,g ,c) where c*x + d*y = g = gcd(x,y)
544 (defun zn-gcdex2 (x y)
545 (let ((x1 1) (y1 0) q r)
546 (do ()((= 0 y) (list x x1))
547 (multiple-value-setq (q r) (truncate x y))
548 (psetf x y y r)
549 (psetf x1 y1 y1 (- x1 (* q y1))) )))
552 ;; discrete logarithm:
553 ;; solve g^x = a mod n, where g is a generator of (Z/nZ)* or of a subgroup containing a
555 ;; see: lecture notes 'Grundbegriffe der Kryptographie' - Eike Best
556 ;; http://theoretica.informatik.uni-oldenburg.de/~best/publications/kry-Mai2005.pdf
558 ;; optional argument: ifactors of zn_order(g,n)
560 (defmfun $zn_log (a g n &optional fs-ord)
561 (unless (and (integerp a) (integerp g) (integerp n))
562 (return-from $zn_log
563 (if fs-ord
564 (list '($zn_log) a g n fs-ord)
565 (list '($zn_log) a g n) )))
566 (when (minusp a) (setq a (mod a n)))
567 (cond
568 ((or (= 0 a) (>= a n)) nil)
569 ((= 1 a) 0)
570 ((= g a) 1)
571 ((> (gcd a n) 1) nil)
573 (if fs-ord
574 (if (and ($listp fs-ord) ($listp (cadr fs-ord)))
575 (progn
576 (setq fs-ord (mapcar #'cdr (cdr fs-ord))) ; Lispify fs-ord
577 (setq fs-ord (cons (totient-from-factors fs-ord) fs-ord)) ) ; totient resp. order in general
578 (gf-merror (intl:gettext
579 "Fourth argument to `zn_log' must be of the form [[p1, e1], ..., [pk, ek]]." )))
580 (let (($intfaclim) (ord ($zn_order g n)))
581 (setq fs-ord (cons ord (get-factor-list ord))) ))
582 (cond
583 ((= 0 (mod (- a (* g g)) n))
585 ((= 1 (mod (* a g) n))
586 (mod -1 (car fs-ord)) )
588 (zn-dlog a g n
589 (car fs-ord) ;; order
590 (cdr fs-ord) ) ))))) ;; factors with multiplicity
592 ;; Pohlig-Hellman-reduction:
594 ;; Solve g^x = a mod n.
595 ;; Assume, that a is an element of (Z/nZ)*
596 ;; and g is a generator of (Z/nZ)* or of a subgroup containing a.
598 (defun zn-dlog (a g n ord fs-ord)
599 (let (p e ord/p om xp xk mods dlogs (g-inv (inv-mod g n)))
600 (dolist (f fs-ord)
601 (setq p (car f) e (cadr f)
602 ord/p (truncate ord p)
603 om (power-mod g ord/p n) ;; om is a generator of prime order p
604 xp 0 )
605 ;; Let op = ord/p^e, gp = g^op (mod n), ap = a^op (mod n) and
606 ;; xp = x (mod p^e).
607 ;; gp is of order p^e and therefore
608 ;; (*) gp^xp = ap (mod n).
609 (do ((b a) (k 0) (pk 1) (acc g-inv) (e1 (1- e))) (()) ;; Solve (*) by solving e logs ..
610 (setq xk (dlog-rho (power-mod b ord/p n) om p n)) ;; .. in subgroups of order p.
611 (incf xp (* xk pk))
612 (incf k)
613 (when (= k e) (return)) ;; => xp = x_0+x_1*p+x_2*p^2+...+x_{e-1}*p^{e-1} < p^e
614 (setq ord/p (truncate ord/p p)
615 pk (* pk p) )
616 (when (/= xk 0) (setq b (mod (* b (power-mod acc xk n)) n)))
617 (when (/= k e1) (setq acc (power-mod acc p n))) )
618 (push (expt p e) mods)
619 (push xp dlogs) )
620 (car (solve-congruences dlogs mods)) )) ;; Find x (mod ord) with x = xp (mod p^e) for all p,e.
622 ;; baby-steps-giant-steps:
624 (defun dlog-baby-giant (a g p n) ;; g is generator of order p mod n
625 (let* ((m (1+ (isqrt p)))
626 (s (floor (* 1.3 m)))
627 (gi (inv-mod g n))
628 g^m babies )
629 (setf babies
630 (make-hash-table :size s :test #'eql :rehash-threshold 0.9) )
631 (do ((r 0) (b a))
632 (())
633 (when (= 1 b)
634 (clrhash babies)
635 (return-from dlog-baby-giant r) )
636 (setf (gethash b babies) r)
637 (incf r)
638 (when (= r m) (return))
639 (setq b (mod (* gi b) n)) )
640 (setq g^m (power-mod g m n))
641 (do ((rr 0 (1+ rr))
642 (bb 1 (mod (* g^m bb) n))
643 r ) (())
644 (when (setq r (gethash bb babies))
645 (clrhash babies)
646 (return (+ (* rr m) r)) )) ))
648 ;; brute-force:
650 (defun dlog-naive (a g n)
651 (do ((i 0 (1+ i)) (gi 1 (mod (* gi g) n)))
652 ((= gi a) i) ))
654 ;; Pollard rho for dlog computation (Brents variant of collision detection)
656 (defun dlog-rho (a g p n) ;; g is generator of prime order p mod n
657 (cond
658 ((= 1 a) 0)
659 ((= g a) 1)
660 ((= 0 (mod (- a (* g g)) n)) 2)
661 ((= 1 (mod (* a g) n)) (1- p))
662 ((< p 512) (dlog-naive a g n))
663 ((< p 65536) (dlog-baby-giant a g p n))
665 (prog ((b 1) (y 0) (z 0) ;; b = g^y * a^z
666 (bb 1) (yy 0) (zz 0) ;; bb = g^yy * a^zz
667 dy dz )
669 (do ((i 0)(j 1)) (()) (declare (fixnum i j))
670 (multiple-value-setq (b y z) (dlog-f b y z a g p n))
671 (when (equal b bb) (return)) ;; g^y * a^z = g^yy * a^zz
672 (incf i)
673 (when (= i j)
674 (setq j (1+ (ash j 1)))
675 (setq bb b yy y zz z) ))
676 (setq dy (mod (- y yy) p) dz (mod (- zz z) p)) ;; g^dy = a^dz = g^(x*dz)
677 (when (= 1 (gcd dz p))
678 (return (zn-quo dy dz p)) ) ;; x = dy/dz mod p (since g is generator of order p)
679 (setq y 0
682 yy (1+ (random (1- p)))
683 zz (1+ (random (1- p)))
684 bb (mod (* (power-mod g yy n) (power-mod a zz n)) n) )
685 (go rho) ))))
687 ;; iteration for Pollard rho: b = g^y * a^z in each step
689 (defun dlog-f (b y z a g ord n)
690 (let ((m (mod b 3)))
691 (cond
692 ((= 0 m)
693 (values (mod (* b b) n) (mod (ash y 1) ord) (mod (ash z 1) ord)) )
694 ((= 1 m) ;; avoid stationary case b=1 => b*b=1
695 (values (mod (* a b) n) y (mod (+ z 1) ord) ) )
697 (values (mod (* g b) n) (mod (+ y 1) ord) z ) ) )))
700 ;; characteristic factors:
702 (defmfun $zn_characteristic_factors (m)
703 (unless (and (integerp m) (> m 1)) ;; according to Shanks no result for m = 1
704 (gf-merror (intl:gettext
705 "`zn_characteristic_factors': Argument must be an integer greater than 1." )))
706 (cons '(mlist simp) (zn-characteristic-factors m)) )
708 (defmfun $zn_carmichael_lambda (m)
709 (cond
710 ((integerp m)
711 (if (= m 1) 1 (zn-characteristic-factors m t)) )
712 (t (gf-merror (intl:gettext
713 "`zn_carmichael_lambda': Argument must be a positive integer." )))))
715 ;; D. Shanks - Solved and unsolved problems in number theory, 2. ed
716 ;; definition 29 and 30 (p. 92 - 94)
718 ;; (zn-characteristic-factors 104) --> (2 2 12)
719 ;; => Z104* is isomorphic to M2 x M2 x M12
720 ;; the direct product of modulo multiplication groups of order 2 resp. 12
722 (defun zn-characteristic-factors (m &optional lambda-only) ;; m > 1
723 (let* (($intfaclim)
724 (pe-list (get-factor-list m)) ;; def. 29 part A
725 (shanks-phi ;; part D
726 (sort
727 (apply #'nconc (mapcar #'zn-shanks-phi-step-bc pe-list))
728 #'zn-pe> )))
729 ;; def. 30 :
730 (do ((todo shanks-phi (nreverse left))
731 (p 0 0) (f 1 1) (left nil nil)
732 fs q d )
733 ((null todo) fs)
734 (dolist (qd todo)
735 (setq q (car qd) d (cadr qd))
736 (if (= q p)
737 (push qd left)
738 (setq p q f (* f (expt q d))) ))
739 (when lambda-only (return-from zn-characteristic-factors f))
740 (push f fs) )))
742 ;; definition 29 parts B,C (p. 92)
743 (defun zn-shanks-phi-step-bc (pe)
744 (let ((p (car pe)) (e (cadr pe)) qd)
745 (cond
746 ((= 2 p)
747 (setq qd (list (if (> e 1) '(2 1) '(1 1))))
748 (when (> e 2) (setq qd (nconc qd (list `(2 ,(- e 2)))))) )
750 (setq qd (let (($intfaclim)) (get-factor-list (1- p))))
751 (when (> e 1)
752 (setq qd (nconc qd (list `(,p ,(1- e))))) )))
753 qd ))
755 (defun zn-pe> (a b)
756 (cond ((> (car a) (car b)) t)
757 ((< (car a) (car b)) nil)
758 (t (> (cadr a) (cadr b))) ))
761 ;; factor generators:
763 (defmfun $zn_factor_generators (m)
764 (unless (and (integerp m) (> m 1))
765 (gf-merror (intl:gettext
766 "`zn_factor_generators': Argument must be an integer greater or equal 2." )))
767 (cons '(mlist simp) (zn-factor-generators m)) )
769 ;; D. Shanks - Solved and unsolved problems in number theory, 2. ed
770 ;; Theorem 44, page 94
772 ;; zn_factor_generators(104); --> [79,27,89]
773 ;; zn_characteristic_factors(104); --> [2,2,12]
774 ;; map(lambda([g], zn_order(g,104)), [79,27,89]); --> [2,2,12]
776 ;; Every element in Z104* can be expressed as
777 ;; 79^i * 27^j * 89^k (mod m) where 0 <= i,j < 2 and 0 <= k < 12
779 ;; The proof of theorem 44 contains the construction of the factor generators.
781 (defun zn-factor-generators (m) ;; m > 1
782 (let* (($intfaclim)
783 (fs (sort (get-factor-list m) #'< :key #'car))
784 (pe (car fs))
785 (p (car pe)) (e (cadr pe))
786 (a (expt p e))
787 phi fs-phi ga gs ords fs-ords pegs )
788 ;; lemma 1, page 98 :
789 ;; (Z/mZ)* is cyclic when m =
790 (when (= m 2) ;; 2
791 (return-from zn-factor-generators (list 1)) )
792 (when (or (< m 8) ;; 3,4,5,6,7
793 (and (> p 2) (null (cdr fs))) ;; p^k, p#2
794 (and (= 2 p) (= 1 e) (null (cddr fs))) ) ;; 2*p^k, p#2
795 (setq phi (totient-by-fs-n fs)
796 fs-phi (sort (mapcar #'car (get-factor-list phi)) #'<)
797 ga (zn-primroot m phi fs-phi) )
798 (return-from zn-factor-generators (list ga)) )
799 (setq fs (cdr fs))
800 (cond
801 ((= 2 p)
802 (unless (and (= e 1) (cdr fs)) ;; phi(2*m) = phi(m) if m#1 is odd
803 (push (1- a) gs) ) ;; a = 2^e
804 (when (> e 1) (setq ords (list 2) fs-ords (list '((2 1)))))
805 (when (> e 2)
806 (push 3 gs) (push (expt 2 (- e 2)) ords) (push `((2 ,(- e 2))) fs-ords) ))
807 ;; lemma 2, page 100 :
809 (setq phi (* (1- p) (expt p (1- e)))
810 fs-phi (sort (get-factor-list (1- p)) #'< :key #'car) )
811 (when (> e 1) (setq fs-phi (nconc fs-phi (list `(,p ,(1- e))))))
812 (setq ga (zn-primroot a phi (mapcar #'car fs-phi)) ;; factors only
813 gs (list ga)
814 ords (list phi)
815 fs-ords (list fs-phi) )))
817 (do (b gb c h ia)
818 ((null fs))
819 (setq pe (car fs) fs (cdr fs)
820 p (car pe) e (cadr pe)
821 phi (* (1- p) (expt p (1- e)))
822 fs-phi (sort (get-factor-list (1- p)) #'< :key #'car) )
823 (when (> e 1) (setq fs-phi (nconc fs-phi (list `(,p ,(1- e))))))
824 (setq b (expt p e)
825 gb (zn-primroot b phi (mapcar #'car fs-phi))
826 c (mod (* (inv-mod b a) (- 1 gb)) a) ;; CRT: h = gb mod b
827 h (+ (* b c) gb) ;; CRT: h = 1 mod a
828 ia (inv-mod a b)
829 gs (mapcar #'(lambda (g) (+ (* a (mod (* ia (- 1 g)) b)) g)) gs)
830 gs (cons h gs)
831 ords (cons phi ords)
832 fs-ords (cons fs-phi fs-ords)
833 a (* a b) ))
834 ;; lemma 3, page 101 :
835 (setq pegs
836 (mapcar #'(lambda (g ord f)
837 (mapcar #'(lambda (pe)
838 (append pe
839 (list (power-mod g (truncate ord (apply #'expt pe)) m)) ))
840 f ))
841 gs ords fs-ords ))
842 (setq pegs (sort (apply #'append pegs) #'zn-pe>))
843 (do ((todo pegs (nreverse left))
844 (q 0 0) (fg 1 1) (left nil nil)
845 g fgs )
846 ((null todo) fgs)
847 (dolist (peg todo)
848 (setq p (car peg) g (caddr peg))
849 (if (= p q)
850 (push peg left)
851 (setq q p fg (mod (* fg g) m)) ))
852 (push fg fgs) )))
855 ;; r-th roots --------------------------------------------------------------- ;;
857 ;; If the residue class a is an r-th power modulo n and contained in a multiplication
858 ;; subgroup of (Z/nZ), return all r-th roots from this subgroup and false otherwise.
860 (defmfun $zn_nth_root (a r n &optional fs-n)
861 (unless (and (integerp a) (integerp r) (integerp n))
862 (gf-merror (intl:gettext
863 "`zn_nth_root' needs three integer arguments. Found ~m, ~m, ~m." ) a r n))
864 (unless (and (> r 0) (> n 0))
865 (gf-merror (intl:gettext
866 "`zn_nth_root': Second and third arg must be pos integers. Found ~m, ~m." ) r n))
867 (when fs-n
868 (if (and ($listp fs-n) ($listp (cadr fs-n)))
869 (setq fs-n (mapcar #'cdr (cdr fs-n))) ;; Lispify fs-n
870 (gf-merror (intl:gettext
871 "`zn_nth_root': The opt fourth arg must be of the form [[p1, e1], ..., [pk, ek]]." ))))
872 (let ((rts (zn-nrt a r n fs-n)))
873 (when rts (cons '(mlist simp) rts)) ))
875 (defun zn-nrt (a r n &optional fs-n)
876 (let (g n/g c p q aq ro ord qs rt rts rems res)
877 (unless fs-n (setq fs-n (let (($intfaclim)) (get-factor-list n))))
878 (setq a (mod a n))
879 (cond
880 ((every #'onep (mapcar #'second fs-n)) ;; RSA-like case (n is squarefree)
881 (when (= a 0) (return-from zn-nrt (list 0))) ) ;; n = 1: exit here
882 ((/= (gcd a n) 1)
883 ;; Handle residue classes not coprime to n (n is not squarefree):
884 ;; Use Theorems 49 and 50 from
885 ;; Shanks - Solved and Unsolved Problems in Number Theory
886 (setq g (gcd a n) n/g (truncate n g))
887 (when (/= (gcd g n/g) 1) ;; a is not contained in any mult. subgroup (Th. 50):
888 (return-from zn-nrt nil) ) ;; exit here
889 (when (= a 0) (return-from zn-nrt (list 0)))
890 ;; g = gcd(a,n). Now assume gcd(g,n/g) = 1.
891 ;; There are totient(n/g) multiples of g, i*g, with gcd(i,n/g) = 1,
892 ;; which form a modulo multiplication subgroup of (Z/nZ),
893 ;; isomorphic to (Z/mZ)*, where m = n/g.
894 ;; a is one of these multiples of g.
895 ;; Find the r-th roots of a resp. mod(a,m) in (Z/mZ)* and then
896 ;; by using CRT all corresponding r-th roots of a in (Z/nZ).
897 (setq a (mod a n/g)
898 rts (zn-nrt a r n/g)
899 c (inv-mod g n/g) ;; CRT-coeff
900 ;; isomorphic mapping (Th. 49):
901 ;; (use CRT with x = 0 mod g and x = rt mod n/g)
902 res (mapcar #'(lambda (rt) (* g (mod (* c rt) n/g))) rts) )
903 (return-from zn-nrt (sort res #'<)) ))
905 ;; for every prime power factor of n
906 ;; reduce a and r if possible and call zq-nrt:
907 (dolist (pe fs-n)
908 (setq p (car pe)
909 q (apply #'expt pe)
910 aq (mod a q)
911 ord (* (1- p) (truncate q p)) )
912 (cond
913 ((> r ord)
914 (setq ro (mod r ord))
915 (when (= ro 0) (setq ro ord)) )
916 (t (setq ro r)) )
917 (cond
918 ((= aq 0)
919 (if (or (= p q) (= ro 1))
920 (setq rt (list 0))
921 (return-from zn-nrt nil) ))
922 ((= ro 1)
923 (setq rt (list aq)) )
925 (setq rt (zq-nrt aq ro p q))
926 (unless rt (return-from zn-nrt nil)) ))
927 (push q qs)
928 (push rt rts) )
929 ;; CRT in case n splits into more than one factor:
930 (if (= 1 (length fs-n))
931 (setq res rt) ;; n is a prime power
932 (setq qs (nreverse qs)
933 rems (zn-distrib-lists (nreverse rts))
934 res (mapcar #'(lambda (rs) (car (solve-congruences rs qs))) rems) ))
935 (sort res #'<) ))
937 ;; return all possible combinations containing one entry per list:
938 ;; (zn-distrib-lists '((1 2 3) (4) (5 6)))
939 ;; --> ((1 4 5) (1 4 6) (2 4 5) (2 4 6) (3 4 5) (3 4 6))
941 (defun zn-distrib-lists (ll)
942 (let ((res (car ll)) tmp e)
943 (dolist (l (cdr ll) res)
944 (setq tmp nil)
945 (dolist (r res)
946 (dolist (n l)
947 (setq e (if (listp r) (copy-list r) (list r)))
948 (push (nconc e (list n)) tmp) ))
949 (setq res (nreverse tmp)) )))
951 ;; handle composite r (which are not coprime to totient(q)):
952 ;; e.g. r=x*x*y*z, then a^(1/r) = (((a^(1/x))^(1/x))^(1/y))^(1/z)
954 (defun zq-nrt (a r p q) ;; prime power q = p^e
955 ;; assume a < q, r <= q
956 (let (rts)
957 (cond
958 ((or (= 1 r) (primep r))
959 (setq rts (zq-amm a r p q)) )
960 ((and (= (gcd r (1- p)) 1) (or (= p q) (= (gcd r p) 1))) ;; r is coprime to totient(q):
961 (let ((ord (* (1- p) (truncate q p))))
962 (setq rts (list (power-mod a (inv-mod r ord) q))) )) ;; unique solution
964 (let* (($intfaclim) (rs (get-factor-list r)))
965 (setq rs (sort rs #'< :key #'car))
966 (setq rs
967 (apply #'nconc
968 (mapcar
969 #'(lambda (pe) (apply #'(lambda (p e) (make-list e :initial-element p)) pe))
970 rs )))
971 (setq rts (zq-amm a (car rs) p q))
972 (dolist (r (cdr rs))
973 (setq rts (apply #'nconc (mapcar #'(lambda (a) (zq-amm a r p q)) rts))) ))))
974 (if (and (= p 2) (> q 2) (evenp r)) ;; this case needs a postprocess (see below)
975 (nconc (mapcar #'(lambda (rt) (- q rt)) rts) rts) ;; add negative solutions
976 rts )))
978 ;; Computing r-th roots modulo a prime power p^n, where r is a prime
980 ;; inspired by
981 ;; Bach,Shallit - Algorithmic Number Theory, Theorem 7.3.2
982 ;; and
983 ;; Shanks - Solved and Unsolved Problems in Number Theory, Th. 46, Lemma 1 to Th. 44
985 ;; The algorithm AMM (Adleman, Manders, Miller) is essentially based on
986 ;; properties of cyclic groups and with the exception of q = 2^n, n > 2
987 ;; it can be applied to any multiplicative group (Z/qZ)* where q = p^n.
989 ;; Doing so, the order q-1 of Fq* in Th. 7.3.2 has to be replaced by the
990 ;; group order totient(q) of (Z/qZ)*.
992 ;; But how to include q = 8,16,32,... ?
993 ;; r > 2: r is prime. There exists a unique solution for all a in (Z/qZ)*.
994 ;; r = 2 (the square root case):
995 ;; - (Z/qZ)* has k = 2 characteristic factors [2,q/4] with [-1,3] as possible
996 ;; factor generators (see Shanks, Lemma 1 to Th. 44).
997 ;; I.e. 3 is of order q/4 and 3^2 = 9 of order q/8.
998 ;; - (Z/qZ)* has totient/2^k = q/8 quadratic residues with 2^k = 4 roots each
999 ;; (see Shanks, Th. 46).
1000 ;; - It follows that the subgroup <3> generated by 3 contains all quadratic
1001 ;; residues of (Z/qZ)* (which must be all the powers of 9 located in <3>).
1002 ;; - We apply the algorithm AMM for cyclic groups to <3> and compute two
1003 ;; square roots x,y.
1004 ;; - The numbers -x and -y, obviously roots as well, both lie in (-1)*<3>
1005 ;; and therefore they differ from x,y and complete the set of 4 roots.
1007 (defun zq-amm (a r p q) ;; r,p prime, q = p^n
1008 ;; assume a < q, r <= q
1009 (cond
1010 ((= 1 r) (list a))
1011 ((= 2 q) (when (= 1 a) (list 1)))
1012 ((= 4 q) (when (or (= 1 a) (and (= 3 a) (oddp r))) (list a)))
1014 (let ((ord (* (1- p) (truncate q p))) ;; group order: totient(q)
1015 rt s m e u )
1016 (when (= 2 r)
1017 (if (= 2 p)
1018 (when (/= 1 (mod a 8)) (return-from zq-amm nil)) ;; a must be a power of 9
1019 (cond
1020 ((/= 1 ($jacobi (mod a p) p))
1021 (return-from zq-amm nil) )
1022 ((= 2 (mod ord 4))
1023 (setq rt (power-mod a (ash (+ ord 2) -2) q))
1024 (return-from zq-amm `(,rt ,(- q rt))) )
1025 ((and (= p q) (= 5 (mod p 8)))
1026 (let* ((x (ash a 1))
1027 (y (power-mod x (ash (- p 5) -3) p))
1028 (i (mod (* x y y) p))
1029 (rt (mod (* a y (1- i)) p)) )
1030 (return-from zq-amm `(,rt ,(- p rt))) )))))
1031 (when (= 2 p) ;; q = 8,16,32,..
1032 (setq ord (ash ord -1)) ) ;; factor generator 3 is of order ord/2
1033 (multiple-value-setq (s m) (truncate ord r))
1034 (when (and (= 0 m) (/= 1 (power-mod a s q))) (return-from zq-amm nil))
1035 ;; r = 3, first 2 cases:
1036 (when (= 3 r)
1037 (cond
1038 ((= 1 (setq m (mod ord 3))) ;; unique solution
1039 (return-from zq-amm
1040 `(,(power-mod a (truncate (1+ (ash ord 1)) 3) q)) ))
1041 ((= 2 m) ;; unique solution
1042 (return-from zq-amm
1043 `(,(power-mod a (truncate (1+ ord) 3) q)) ))))
1044 ;; compute u,e with ord = u*r^e and r,u coprime:
1045 (setq u ord e 0)
1046 (do ((u1 u)) (())
1047 (multiple-value-setq (u1 m) (truncate u1 r))
1048 (when (/= 0 m) (return))
1049 (setq u u1 e (1+ e)) )
1050 (cond
1051 ((= 0 e)
1052 (setq rt (power-mod a (inv-mod r u) q)) ;; unique solution, see Bach,Shallit 7.3.1
1053 (list rt) )
1054 (t ;; a is an r-th power
1055 (let (g re om)
1056 ;; find generator of order r^e:
1057 (if (= p 2) ;; p = 2: then r = 2 (other r: e = 0)
1058 (setq g 3)
1059 (do ((n 2 ($next_prime n)))
1060 ((and (= 1 (gcd n q)) (/= 1 (power-mod n s q))) ;; n is no r-th power
1061 (setq g (power-mod n u q)) )))
1062 (setq re (expt r e)
1063 om (power-mod g (truncate re r) q) ) ;; r-th root of unity
1064 (cond
1065 ((or (/= 3 r) (= 0 (setq m (mod ord 9))))
1066 (let (ar au br bu k ab alpha beta)
1067 ;; map a from Zq* to C_{r^e} x C_u:
1068 (setq ar (power-mod a u q) ;; in C_{r^e}
1069 au (power-mod a re q) ) ;; in C_u
1070 ;; compute direct factors of rt:
1071 ;; (the loop in algorithm AMM is effectively a Pohlig-Hellman-reduction, equivalent to zn-dlog)
1072 (setq k (zn-dlog ar g q re `((,r ,e))) ;; g^k = ar, where r|k
1073 br (power-mod g (truncate k r) q) ;; br^r = g^k (factor of rt in C_{r^e})
1074 bu (power-mod au (inv-mod r u) q) ) ;; bu^r = au (factor of rt in C_u)
1075 ;; mapping from C_{r^e} x C_u back to Zq*:
1076 (setq ab (cdr (zn-gcdex1 u re))
1077 alpha (car ab)
1078 beta (cadr ab) )
1079 (if (< alpha 0) (incf alpha ord) (incf beta ord))
1080 (setq rt (mod (* (power-mod br alpha q) (power-mod bu beta q)) q)) ))
1081 ;; r = 3, remaining cases:
1082 ((= 3 m)
1083 (setq rt (power-mod a (truncate (+ (ash ord 1) 3) 9) q)) )
1084 ((= 6 m)
1085 (setq rt (power-mod a (truncate (+ ord 3) 9) q)) ))
1086 ;; mult with r-th roots of unity:
1087 (do ((i 1 (1+ i)) (j 1) (res (list rt)))
1088 ((= i r) res)
1089 (setq j (mod (* j om) q))
1090 (push (mod (* rt j) q) res) ))))))))
1092 ;; -------------------------------------------------------------------------- ;;
1095 ;; Two variants of gcdex:
1097 ;; returns gcd as first entry:
1098 ;; (zn-gcdex1 12 45) --> (3 4 -1), so 4*12 + -1*45 = 3
1099 (defun zn-gcdex1 (x y)
1100 (let ((x1 1) (x2 0) (y1 0) (y2 1) q r)
1101 (do ()((= 0 y) (list x x1 x2))
1102 (multiple-value-setq (q r) (truncate x y))
1103 (psetf x y y r)
1104 (psetf x1 y1 y1 (- x1 (* q y1)))
1105 (psetf x2 y2 y2 (- x2 (* q y2))) )))
1107 ;; returns gcd as last entry:
1108 ;; (zn-gcdex 12 45 21) --> (4 -1 0 3), so 4*12 + -1*45 + 0*21 = 3
1109 (defun zn-gcdex (&rest args)
1110 (let* ((ex (zn-gcdex1 (car args) (cadr args)))
1111 (g (car ex))
1112 (cs (cdr ex)) c1 )
1113 (dolist (a (cddr args) (nconc cs (list g)))
1114 (setq ex (zn-gcdex1 g a)
1115 g (car ex)
1116 ex (cdr ex)
1117 c1 (car ex)
1118 cs (nconc (mapcar #'(lambda (c) (* c c1)) cs) (cdr ex)) ))))
1121 ;; for educational puposes: tables of small residue class rings
1123 (defun zn-table-errchk (n fun)
1124 (unless (and (fixnump n) (< 1 n))
1125 (gf-merror (intl:gettext
1126 "Argument to `~m' must be a small fixnum greater than 1." ) fun )))
1128 (defmfun $zn_add_table (n)
1129 (zn-table-errchk n "zn_add_table")
1130 (do ((i 0 (1+ i)) res)
1131 ((= i n)
1132 (cons '($matrix simp) (nreverse res)) )
1133 (push (mfuncall '$makelist `((mod) (+ ,i $j) ,n) '$j 0 (1- n)) res) ))
1135 ;; multiplication table modulo n
1137 ;; The optional g allows to choose subsets of (Z/nZ). Show i with gcd(i,n) = g resp. all i#0.
1138 ;; If n # 1 add row and column headings for better readability.
1140 (defmfun $zn_mult_table (n &optional g)
1141 (zn-table-errchk n "zn_mult_table")
1142 (let ((i0 1) all header choice res)
1143 (cond
1144 ((not g) (setq g 1))
1145 ((equal g '$all) (setq all t))
1146 ((not (fixnump g))
1147 (gf-merror (intl:gettext
1148 "`zn_mult_table': The opt second arg must be `all' or a small fixnum." )))
1150 (when (= n g) (setq i0 0))
1151 (push 1 choice) ;; creates the headers
1152 (setq header t) ))
1153 (do ((i i0 (1+ i)))
1154 ((= i n)
1155 (setq choice (cons '(mlist simp) (nreverse choice))) )
1156 (when (or all (= g (gcd i n))) (push i choice)) )
1157 (when (and header (= (length choice) 2))
1158 (return-from $zn_mult_table) )
1159 (dolist (i (cdr choice))
1160 (push (mfuncall '$makelist `((mod) (* ,i $j) ,n) '$j choice) res) )
1161 (setq res (nreverse res))
1162 (when header (rplaca (cdar res) "*"))
1163 (cons '($matrix simp) res) ))
1165 ;; power table modulo n
1167 ;; The optional g allows to choose subsets of (Z/nZ). Show i with gcd(i,n) = g resp. all i.
1169 (defmfun $zn_power_table (n &optional g e)
1170 (zn-table-errchk n "zn_power_table")
1171 (let (all)
1172 (cond
1173 ((not g) (setq g 1))
1174 ((equal g '$all) (setq all t))
1175 ((not (fixnump g))
1176 (gf-merror (intl:gettext
1177 "`zn_power_table': The opt second arg must be `all' or a small fixnum." ))))
1178 (cond
1179 ((not e) (setq e (zn-characteristic-factors n t)))
1180 ((not (fixnump e))
1181 (gf-merror (intl:gettext
1182 "`zn_power_table': The opt third arg must be a small fixnum." ))))
1183 (do ((i 0 (1+ i)) res)
1184 ((= i n)
1185 (when res (cons '($matrix simp) (nreverse res))) )
1186 (when (or all (= g (gcd i n)))
1187 (push (mfuncall '$makelist `((power-mod) ,i $j ,n) '$j 1 e) res) ))))
1190 ;; $zn_invert_by_lu (m p)
1191 ;; $zn_determinant (m p)
1192 ;; see below: --> galois fields--> interfaces to linearalgebra/lu.lisp
1195 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1198 ;; -----------------------------------------------------------------------------
1199 ;; *** GALOIS FIELDS ***
1201 ;; The following is a revision and improvement of the first part of share/
1202 ;; contrib/gf/gf.mac by Alasdair McAndrew, Fabrizio Caruso and Jacopo D'Aurizio
1203 ;; released under terms of the GPLv2 in 2007.
1205 ;; I would like to thank the original authors for their contribution to Maxima
1206 ;; which allowed me to study, improve and extend the source code.
1208 ;; I would also like to thank Camm Maguire who helped me coding compiler macros
1209 ;; for GCL.
1211 ;; 2012 - 2014, Volker van Nek
1213 (declare-top (special *gf-char* *gf-exp* *ef-arith?*)) ;; modulus $intfaclim see above
1215 (defvar *gf-rat-header* nil "header of internal CRE representation")
1217 (defvar *ef-arith?* nil "Should extension field arithmetic be used?")
1219 ;; base field:
1220 (defvar *gf-char* 0 "characteristic p")
1221 (defvar *gf-exp* 0 "exponent n, degree of the reduction polynomial")
1222 (defvar *gf-ord* 0 "group order, number of units")
1223 (defvar *gf-card* 0 "cardinality, ring order")
1224 (defvar *gf-red* nil "reduction polynomial")
1225 (defvar *gf-prim* nil "primitive element")
1226 (defvar *gf-fs-ord* nil "ifactors of *gf-ord*")
1227 (defvar *gf-fsx* nil "extended factors of *gf-ord*")
1228 (defvar *gf-fsx-base-p* nil "*gf-fsx* in base p")
1229 (defvar *gf-x^p-powers* nil "x^p^i, i=0,..,n-1")
1231 (defvar *f2-red* 0 "reduction polynomial") ;; used by ef coeff arith over binary fields
1233 (declaim (fixnum *gf-exp* *ef-exp*))
1235 ;; extension:
1236 (defvar *ef-exp* 0 "exponent m, degree of the reduction polynomial")
1237 (defvar *ef-ord* 0 "group order, number of units")
1238 (defvar *ef-card* 0 "cardinality, ring order")
1239 (defvar *ef-red* nil "reduction polynomial")
1240 (defvar *ef-prim* nil "primitive element")
1241 (defvar *ef-fs-ord* nil "ifactors of *ef-ord*")
1242 (defvar *ef-fsx* nil "extended factors of *ef-ord*")
1243 (defvar *ef-fsx-base-q* nil "*ef-fsx* in base q = p^n")
1244 (defvar *ef-x^q-powers* nil "x^q^i, i=0,..,m-1")
1246 (defvar *gf-char?* nil "Was the characteristic defined?")
1247 (defvar *gf-red?* nil "Was the reduction polynomial defined?")
1248 (defvar *gf-irred?* nil "Is the reduction polynomial irreducible?")
1249 (defvar *gf-data?* nil "gf_set_data called?")
1251 (defvar *ef-red?* nil "Was the reduction polynomial defined?")
1252 (defvar *ef-irred?* nil "Is the reduction polynomial irreducible?")
1253 (defvar *ef-data?* nil "ef_set_data called?")
1255 (defmvar $gf_rat nil "Return values are rational expressions?" boolean)
1257 (defmvar $gf_symmetric nil "A symmetric modulus should be used?" boolean) ;; deprecated
1258 (defmvar $gf_balanced nil "A balanced modulus should be used?" boolean) ;; there is ec_balanced in elliptic_curves.lisp
1260 (putprop '$gf_symmetric 'gf-balanced-info 'assign)
1262 (defun gf-balanced-info (assign-var arg)
1263 (declare (ignore assign-var))
1264 (setq $gf_balanced arg)
1265 (format t "`gf_symmetric' is deprecated and replaced by `gf_balanced'.~%The value is bound to `gf_balanced'.") )
1266 ;; temporarily print this message
1269 (defmvar $gf_coeff_limit 256
1270 "`gf_coeff_limit' limits the coeffs when searching for irreducible and primitive polynomials." fixnum)
1272 (putprop '$gf_coeff_limit 'gf-coeff-check 'assign)
1274 (defun gf-coeff-check (assign-var arg)
1275 (declare (ignore assign-var))
1276 (unless (and (integerp arg) (> arg 1))
1277 (gf-merror (intl:gettext
1278 "`gf_coeff_limit': Assignment ignored. Value must be an integer greater than 1.~%" ))))
1280 (defmvar $gf_cantor_zassenhaus t "Should the Cantor-Zassenhaus algorithm be used?" boolean)
1282 (defmvar $ef_coeff_mult nil)
1283 (defmvar $ef_coeff_add nil)
1284 (defmvar $ef_coeff_inv nil)
1285 (defmvar $ef_coeff_exp nil)
1287 (defmvar $gf_powers nil)
1288 (defmvar $gf_logs nil)
1289 (defmvar $gf_zech_logs nil)
1290 (defvar *gf-powers* nil "alpha^i, i=0,..,ord-1 where alpha is a primitive element")
1291 (defvar *gf-logs?* nil "Were the power and log tables calculated?")
1294 ;; contains parts of merror.lisp/merror but avoids "To debug this ...".
1296 (defun gf-merror (sstring &rest l)
1297 (setq $error `((mlist) ,sstring ,@ l))
1298 (and $errormsg ($errormsg))
1299 (fresh-line *standard-output*)
1300 (format t (intl:gettext "~& -- an error.~%"))
1301 (throw 'macsyma-quit 'maxima-error) )
1304 (defun gf-char? (fun)
1305 (if *gf-char?* t
1306 (gf-merror (intl:gettext "`~m': The characteristic is not defined yet.") fun) ))
1308 (defun gf-red? (fun)
1309 (if *gf-red?* t
1310 (gf-merror (intl:gettext "`~m': The reduction polynomial is not defined yet.") fun) ))
1312 (defun gf-data? (fun)
1313 (if *gf-data?* t
1314 (gf-merror (intl:gettext "`~m': gf_set_data called?") fun) ))
1316 (defun gf-field? (fun)
1317 (if (and (gf-data? fun) *gf-irred?*) t
1318 (gf-merror (intl:gettext "`~m': The reduction polynomial is not irreducible.") fun) ))
1321 (defun ef-gf-field? (fun)
1322 (if (and *gf-data?* *gf-irred?*) t
1323 (gf-merror (intl:gettext "`~m': The base field is not defined yet.") fun) ))
1325 (defun ef-red? (fun)
1326 (if (and (ef-gf-field? fun) *ef-red?*) t
1327 (gf-merror (intl:gettext "`~m': The reduction polynomial is not defined yet.") fun) ))
1329 (defun ef-data? (fun)
1330 (if (and (ef-gf-field? fun) *ef-data?*) t
1331 (gf-merror (intl:gettext "`~m': ef_set_data called?") fun) ))
1333 (defun ef-field? (fun)
1334 (if (and (ef-data? fun) *ef-irred?*) t
1335 (gf-merror (intl:gettext "`~m': The extension is no field.") fun) ))
1337 ;; -----------------------------------------------------------------------------
1340 ;; basic coefficient arithmetic ------------------------------------------------
1343 ;; optimize the fixnum cases
1345 (defmacro maybe-char-is-fixnum-let (binds &body body)
1346 `(if (or (and (not *ef-arith?*) (typep *gf-char* 'fixnum))
1347 (and *ef-arith?* (typep *gf-card* 'fixnum)) )
1348 (let ,binds
1349 (declare (fixnum ,@(mapcar #'(lambda (x) (car x)) binds)))
1350 ,@body)
1351 (let ,binds
1352 (declare (integer ,@(mapcar #'(lambda (x) (car x)) binds)))
1353 ,@body )))
1355 ;; basic coefficient functions and compiler macros
1357 ;; gf coefficient arith :
1359 ;; *ef-arith?* controls coefficient arithmetic. If *ef-arith?* is false,
1360 ;; coeffs are elements of Zp, where p is the defined characteristic *gf-char*.
1361 ;; If *ef-arith?* is true, coeffs are interpreted as the integer representation
1362 ;; of a polynomial over Zp[x] reduced by the irreducible polynomial *gf-red*.
1364 (defun gf-cinv (c)
1365 (if *ef-arith?*
1366 (ef-cinv c)
1367 (maybe-char-is-fixnum-let ((c c))
1368 (cond
1369 ((= 0 c) (gf-merror (intl:gettext "gf coefficient inversion: Quotient by zero")))
1370 (t (inv-mod c *gf-char*)) )))) ; *gf-char* is prime
1372 (defun gf-cpow (c n)
1373 (if *ef-arith?*
1374 (ef-cpow c n)
1375 (maybe-char-is-fixnum-let ((c c))
1376 (power-mod c n *gf-char*) )))
1378 (defun gf-cmod (c)
1379 (if *ef-arith?*
1380 (ef-cmod c)
1381 (maybe-char-is-fixnum-let ((c c))
1382 (mod c *gf-char*) )))
1384 (defun gf-ctimes (a b)
1385 (if *ef-arith?*
1386 (ef-ctimes a b)
1387 (maybe-char-is-fixnum-let ((a a)(b b))
1388 (mod (* a b) *gf-char*) )))
1390 (defun gf-cplus-b (a b) ;; assumes that both 0 <= a,b < *gf-char*
1391 (cond
1392 (*ef-arith?* (ef-cplus-b a b))
1393 (t (maybe-char-is-fixnum-let ((a a)(b b))
1394 (let ((s (+ a b)))
1395 (if (< (the integer s) *gf-char*)
1397 (- (the integer s) *gf-char*) ))))))
1399 (defun gf-cminus-b (c) ;; assumes that 0 <= c < *gf-char*
1400 (cond
1401 ((= 0 c) 0)
1402 ((= 2 *gf-char*) c)
1403 (*ef-arith?* (ef-cminus-b c))
1404 (t (maybe-char-is-fixnum-let ((c c))
1405 (- *gf-char* c) ))))
1407 ;; ef coefficient arith :
1409 (defun ef-cinv (c)
1410 (declare (integer c))
1411 (cond
1412 ((= 0 c) (gf-merror (intl:gettext "ef coefficient inversion: Quotient by zero")))
1413 ($ef_coeff_inv (mfuncall '$ef_coeff_inv c))
1414 (*gf-logs?* (ef-cinv-by-table c))
1415 ((= 2 *gf-char*) (f2-inv c))
1416 (t (let ((*ef-arith?*))
1417 (gf-x2n (gf-inv (gf-n2x c) *gf-red*)) ))))
1419 (defun ef-cpow (c n)
1420 (cond
1421 ($ef_coeff_exp (mfuncall '$ef_coeff_exp c n))
1422 (*gf-logs?* (ef-cpow-by-table c n))
1423 ((= 2 *gf-char*) (f2-pow c n))
1424 (t (let ((*ef-arith?*))
1425 (gf-x2n (gf-pow (gf-n2x c) n *gf-red*)) ))))
1427 (defun ef-cmod (c)
1428 (declare (integer c))
1429 (cond
1430 ((plusp c)
1431 (cond
1432 ((< c *gf-ord*) c)
1433 ((= 2 *gf-char*) (f2-red c))
1434 (t (let ((*ef-arith?*))
1435 (gf-x2n (gf-nred (gf-n2x c) *gf-red*)) ))))
1437 (setq c (ef-cmod (abs c)))
1438 (let ((*ef-arith?* t)) (gf-ctimes (1- *gf-char*) c)) )))
1440 (defun ef-ctimes (a b)
1441 (cond
1442 ($ef_coeff_mult (mfuncall '$ef_coeff_mult a b))
1443 (*gf-logs?* (ef-ctimes-by-table a b))
1444 ((= 2 *gf-char*) (f2-times a b))
1445 (t (let ((*ef-arith?*))
1446 (gf-x2n (gf-times (gf-n2x a) (gf-n2x b) *gf-red*)) ))))
1448 (defun ef-cplus-b (a b)
1449 (cond
1450 ((= 2 *gf-char*) (logxor a b))
1451 ($ef_coeff_add (mfuncall '$ef_coeff_add a b))
1452 (*gf-logs?* (ef-cplus-by-table a b))
1453 (t (let ((*ef-arith?*))
1454 (gf-x2n (gf-nplus (gf-n2x a) (gf-n2x b))) ))))
1456 (defun ef-cminus-b (a)
1457 (cond
1458 ((= 0 a) 0)
1459 ((= 2 *gf-char*) a)
1460 ($ef_coeff_mult (mfuncall '$ef_coeff_mult (1- *gf-char*) a))
1461 (*gf-logs?* (ef-cminus-by-table a))
1462 (t (let ((*ef-arith?*))
1463 (gf-x2n (gf-nminus (gf-n2x a))) ))))
1465 ;; ef coefficient arith by lookup:
1467 (defun ef-ctimes-by-table (c d)
1468 (declare (fixnum c d))
1469 (cond
1470 ((or (= 0 c) (= 0 d)) 0)
1471 (t (let ((cd (+ (the fixnum (svref $gf_logs c))
1472 (the fixnum (svref $gf_logs d)) )))
1473 (svref $gf_powers (if (< (the integer cd) *gf-ord*) cd (- cd *gf-ord*))) ))))
1475 (defun ef-cminus-by-table (c)
1476 (declare (fixnum c))
1477 (cond
1478 ((= 0 c) 0)
1479 ((= 2 *gf-char*) c)
1480 (t (let ((e (ash *gf-ord* -1))) (declare (fixnum e))
1481 (setq c (svref $gf_logs c))
1482 (svref $gf_powers (the fixnum (if (< c e) (+ c e) (- c e)))) ))))
1484 (defun ef-cinv-by-table (c)
1485 (declare (fixnum c))
1486 (cond
1487 ((= 0 c) (gf-merror (intl:gettext "ef coefficient inversion: Quotient by zero")))
1488 (t (svref $gf_powers (- *gf-ord* (the fixnum (svref $gf_logs c))))) ))
1490 (defun ef-cplus-by-table (c d)
1491 (declare (fixnum c d))
1492 (cond
1493 ((= 0 c) d)
1494 ((= 0 d) c)
1495 (t (setq c (svref $gf_logs c) d (aref $gf_logs d))
1496 (let ((z (svref $gf_zech_logs (the fixnum (if (< d c) (+ *gf-ord* (- d c)) (- d c))))))
1497 (cond
1498 (z (incf z c)
1499 (svref $gf_powers (the fixnum (if (> z *gf-ord*) (- z *gf-ord*) z))) )
1500 (t 0) )))))
1502 (defun ef-cpow-by-table (c n)
1503 (declare (fixnum c n))
1504 (cond
1505 ((= 0 n) 1)
1506 ((= 0 c) 0)
1507 (t (svref $gf_powers
1508 (mod (* n (the fixnum (svref $gf_logs c))) *gf-ord*) )) ))
1511 (defun gf-pow-by-table (x n) ;; table lookup uses current *gf-red* for reduction
1512 (declare (fixnum n))
1513 (cond
1514 ((= 0 n) (list 0 1))
1515 ((null x) nil)
1516 (t (svref *gf-powers*
1517 (mod (* n (the fixnum (svref $gf_logs (gf-x2n x)))) *gf-ord*) )) ))
1520 ;; ef coefficient arith for binary base fields:
1522 (defun f2-red (a)
1523 (declare (optimize (speed 3) (safety 0)))
1524 (let* ((red *f2-red*)
1525 (ilen (integer-length red))
1526 (e 0) )
1527 (declare (fixnum e ilen))
1528 (do () ((= a 0) 0)
1529 (setq e (- (integer-length a) ilen))
1530 (when (< e 0) (return a))
1531 (setq a (logxor a (ash red e))) )))
1533 (defun f2-times (a b)
1534 (declare (optimize (speed 3) (safety 0)))
1535 (let* ((ilen (integer-length b))
1536 (a1 (ash a (1- ilen)))
1537 (ab a1) )
1538 (do ((i (- ilen 2) (1- i)) (k 0))
1539 ((< i 0) (f2-red ab))
1540 (declare (fixnum i k))
1541 (decf k)
1542 (when (logbitp i b)
1543 (setq a1 (ash a1 k)
1544 ab (logxor ab a1)
1545 k 0 )))))
1547 (defun f2-pow (a n) ;; assume n >= 0
1548 (declare (optimize (speed 3) (safety 0))
1549 (integer n) )
1550 (cond
1551 ((= n 0) 1)
1552 ((= a 0) 0)
1553 (t (do (res) (())
1554 (when (oddp n)
1555 (setq res (if res (f2-times a res) a))
1556 (when (= 1 n)
1557 (return-from f2-pow res) ))
1558 (setq n (ash n -1)
1559 a (f2-times a a)) ))))
1561 (defun f2-inv (b)
1562 (declare (optimize (speed 3) (safety 0)))
1563 (when (= b 0)
1564 (gf-merror (intl:gettext "f2 arithmetic: Quotient by zero")) )
1565 (let ((b1 1) (a *f2-red*) (a1 0) q r)
1566 (do ()
1567 ((= b 0) a1)
1568 (multiple-value-setq (q r) (f2-divide a b))
1569 (psetf a b b r)
1570 (psetf a1 b1 b1 (logxor (f2-times q b1) a1)) )))
1572 (defun f2-divide (a b)
1573 (declare (optimize (speed 3) (safety 0)))
1574 (cond
1575 ((= b 0)
1576 (gf-merror (intl:gettext "f2 arithmetic: Quotient by zero")) )
1577 ((= a 0) (values 0 0))
1579 (let ((ilen (integer-length b))
1580 (e 0)
1581 (q 0) )
1582 (declare (fixnum e ilen))
1583 (do () ((= a 0) (values q 0))
1584 (setq e (- (integer-length a) ilen))
1585 (when (< e 0) (return (values q a)))
1586 (setq q (logxor q (ash 1 e)))
1587 (setq a (logxor a (ash b e))) )))))
1589 ;; -------------------------------------------------------------------------- ;;
1592 #-gcl (eval-when (:compile-toplevel :load-toplevel :execute)
1593 (progn
1594 (define-compiler-macro gf-cmod (a)
1595 `(cond
1596 (*ef-arith?*
1597 (ef-cmod ,a) )
1598 ((and (typep *gf-char* 'fixnum) (typep ,a 'fixnum)) ;; maybe a > *gf-char*
1599 (let ((x ,a) (z *gf-char*)) (declare (fixnum x z))
1600 (the fixnum (mod x z)) ))
1602 (mod (the integer ,a) *gf-char*) )))
1604 (define-compiler-macro gf-ctimes (a b)
1605 `(cond
1606 (*ef-arith?*
1607 (ef-ctimes ,a ,b) )
1608 ((typep *gf-char* 'fixnum)
1609 (let ((x ,a) (y ,b) (z *gf-char*)) (declare (fixnum x y z))
1610 (the fixnum (mod (* x y) z)) ))
1612 (mod (* (the integer ,a) (the integer ,b)) *gf-char*) )))
1614 (define-compiler-macro gf-cplus-b (a b) ;; assumes that both 0 <= a,b < *gf-char*
1615 `(cond
1616 (*ef-arith?*
1617 (ef-cplus-b ,a ,b) )
1618 ((typep *gf-char* 'fixnum)
1619 (let ((x ,a) (y ,b) (z *gf-char*) (s 0)) (declare (fixnum x y z) (integer s))
1620 (setq s (the integer (+ x y)))
1621 (if (< s z) s (- s z)) ))
1623 (let ((x (+ (the integer ,a) (the integer ,b)))) (declare (integer x))
1624 (if (< x *gf-char*) x (- x *gf-char*)) ))))
1626 (define-compiler-macro gf-cminus-b (a) ;; assumes that 0 <= a < *gf-char*
1627 `(cond
1628 ((= 0 ,a) 0)
1629 (*ef-arith?*
1630 (ef-cminus-b ,a) )
1631 ((typep *gf-char* 'fixnum)
1632 (let ((x ,a) (z *gf-char*)) (declare (fixnum x z))
1633 (the fixnum (- z x)) ))
1635 (- *gf-char* (the integer ,a)) )))
1638 #+gcl (eval-when (compile load eval)
1639 (progn
1640 (push '((fixnum fixnum) fixnum #.(compiler::flags compiler::rfa)
1641 "(fixnum)(((long long)(#0))%((long long)(#1)))" )
1642 (get 'i% 'compiler::inline-always) )
1643 (push '((fixnum fixnum fixnum) fixnum #.(compiler::flags compiler::rfa)
1644 "(fixnum)((((long long)(#0))*((long long)(#1)))%((long long)(#2)))" )
1645 (get '*% 'compiler::inline-always) )
1646 (push '((fixnum fixnum fixnum) fixnum #.(compiler::flags compiler::rfa compiler::set)
1647 "@02;({long long _t=((long long)(#0))+((long long)(#1)),_w=((long long)(#2));_t<_w ? (fixnum)_t : (fixnum)(_t - _w);})" )
1648 (get '+%b 'compiler::inline-always) )
1649 (push '((fixnum fixnum) fixnum #.(compiler::flags compiler::rfa)
1650 "(fixnum)(((long long)(#1))-((long long)(#0)))" )
1651 (get 'neg%b 'compiler::inline-always) )
1653 (setf (get 'i% 'compiler::return-type) t)
1654 (setf (get '*% 'compiler::return-type) t)
1655 (setf (get '+%b 'compiler::return-type) t)
1656 (setf (get 'neg%b 'compiler::return-type) t)
1658 (si::define-compiler-macro gf-cmod (a)
1659 `(cond
1660 (*ef-arith?*
1661 (ef-cmod ,a) )
1662 ((and (typep *gf-char* 'fixnum) (typep ,a 'fixnum) (plusp ,a)) ;; maybe a > *gf-char*
1663 (let ((x ,a) (z *gf-char*)) (declare (fixnum x z))
1664 (i% x z) ))
1666 (mod (the integer ,a) *gf-char*) )))
1668 (si::define-compiler-macro gf-ctimes (a b) ;; assume that 0 <= a,b :
1669 `(cond
1670 (*ef-arith?*
1671 (ef-ctimes ,a ,b) )
1672 ((typep *gf-char*
1673 ',(if (< (integer-length most-positive-fixnum) 32) `fixnum `(signed-byte 32)) )
1674 (let ((x ,a) (y ,b) (z *gf-char*)) (declare (fixnum x y z))
1675 (*% x y z) ))
1677 (mod (* (the integer ,a) (the integer ,b)) *gf-char*) )))
1679 (si::define-compiler-macro gf-cplus-b (a b) ;; assume that both 0 <= a,b < *gf-char* :
1680 `(cond
1681 (*ef-arith?*
1682 (ef-cplus-b ,a ,b) )
1683 ((typep *gf-char*
1684 ',(if (< (integer-length most-positive-fixnum) 63) `fixnum `(signed-byte 63)) )
1685 (let ((x ,a) (y ,b) (z *gf-char*)) (declare (fixnum x y z))
1686 (+%b x y z) ))
1688 (let ((x (+ (the integer ,a) (the integer ,b)))) (declare (integer x))
1689 (if (< x *gf-char*) x (- x *gf-char*)) ))))
1691 (si::define-compiler-macro gf-cminus-b (a) ;; assume that 0 <= a < *gf-char* :
1692 `(cond
1693 ((= 0 ,a) 0)
1694 (*ef-arith?*
1695 (ef-cminus-b ,a) )
1696 ((typep *gf-char* 'fixnum)
1697 (let ((x ,a) (z *gf-char*)) (declare (fixnum x z))
1698 (neg%b x z) ))
1700 (- *gf-char* (the integer ,a)) )))
1703 ;; -----------------------------------------------------------------------------
1706 ;; setting the finite field and retrieving basic informations ------------------
1709 (defmfun $gf_set (p &optional a1 a2 a3) ;; deprecated
1710 (format t "`gf_set' is deprecated. ~%~\
1711 The user is asked to use `gf_set_data' instead.~%" )
1712 (when a2
1713 (format t "In future versions `gf_set_data' will only accept two arguments.~%") )
1714 ($gf_set_data p a1 a2 a3) )
1717 (defmfun $gf_set_data (p &optional a1 a2 a3) ;; opt: *gf-exp*, *gf-red*, *gf-fs-ord*
1718 (declare (ignore a2 a3)) ;; remove a2 a3 in next versions
1719 (let ((*ef-arith?*))
1720 (unless (and (integerp p) (primep p))
1721 (gf-merror (intl:gettext "`gf_set_data': Field characteristic must be a prime number.")) )
1722 ($gf_unset)
1723 (setq *gf-char* p)
1725 (when a1 ;; exponent or reduction poly
1726 (cond
1727 ((integerp a1)
1728 (unless (and (fixnump a1) (plusp a1))
1729 (gf-merror (intl:gettext "`gf_set_data': The exponent must be a positive fixnum.")) )
1730 (setq *gf-exp* a1) )
1732 (setq *gf-red* (gf-p2x-red a1 "gf_set_data")
1733 *gf-exp* (car *gf-red*)
1734 *gf-irred?* (gf-irr-p *gf-red* *gf-char* *gf-exp*) )) ))
1736 (gf-set-rat-header) ;; CRE-headers
1738 (unless *gf-red* ;; find irreducible reduction poly:
1739 (setq *gf-red* (if (= 1 *gf-exp*) (list 1 1) (gf-irr p *gf-exp*))
1740 *gf-irred?* t ))
1742 (when (= *gf-char* 2) (setq *f2-red* (gf-x2n *gf-red*)))
1744 (setq *gf-card* (expt p *gf-exp*)) ;; cardinality #(F)
1746 (setq *gf-ord* ;; group order #(F*)
1747 (cond
1748 ((= 1 *gf-exp*) (1- p))
1749 ((not *gf-irred?*) (gf-group-order *gf-char* *gf-red*))
1750 (t (1- (expt p *gf-exp*))) ))
1751 (let* (($intfaclim)
1752 (fs (get-factor-list *gf-ord*)) )
1753 (setq *gf-fs-ord* (sort fs #'< :key #'car)) ) ;; .. [pi, ei] ..
1755 (when *gf-irred?* (gf-precomp))
1757 (setq *gf-prim* ;; primitive element
1758 (cond
1759 ((= 1 *gf-exp*)
1760 (if (= 2 *gf-char*) (list 0 1)
1761 (list 0 (zn-primroot p *gf-ord* (mapcar #'car *gf-fs-ord*))) )) ;; .. pi .. (factors_only:true)
1763 (if *gf-irred?* (gf-prim) '$unknown) )))
1765 (setq *gf-char?* t *gf-red?* t *gf-data?* t) ;; global flags
1766 ($gf_get_data) )) ;; data structure
1769 (defun gf-set-rat-header ()
1770 (let ((modulus))
1771 (setq *gf-rat-header* (car ($rat '$x))) ))
1773 (defun gf-p2x-red (p fun)
1774 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
1775 (let* ((modulus) (x (car (prep1 p))))
1776 (unless (and (listp x)
1777 (every #'numberp (setq x (cdr x))) )
1778 (gf-merror (intl:gettext "`~m': Not suitable as reduction polynomial: ~m") fun p) )
1779 (setq x (gf-mod x))
1780 (unless (and (typep (car x) 'fixnum) (plusp (car x)))
1781 (gf-merror (intl:gettext "`~m': The exponent must be a positive fixnum.") fun) )
1782 (unless (eql 1 (cadr x))
1783 (gf-merror (intl:gettext "`~m': A monic reduction polynomial is assumed.") fun) )
1784 x ))
1787 (defmfun $ef_set_data (red)
1788 (ef-gf-field? "ef_set_data")
1789 ($ef_unset)
1790 (let ((*ef-arith?* t))
1791 (setq *ef-red* (gf-p2x-red red "ef_set_data")
1792 *ef-exp* (car *ef-red*)
1793 *ef-card* (expt *gf-card* *ef-exp*)
1794 *ef-irred?* (gf-irr-p *ef-red* *gf-card* *ef-exp*)
1795 *ef-ord* (if *ef-irred?*
1796 (1- *ef-card*)
1797 (gf-group-order *gf-card* *ef-red*) ))
1798 (let* (($intfaclim)
1799 (fs (get-factor-list *ef-ord*)) )
1800 (setq *ef-fs-ord* (sort fs #'< :key #'car)) )
1801 (when *ef-irred?* (ef-precomp))
1802 (setq *ef-data?* t
1803 *ef-red?* t
1804 *ef-prim* (if (= 1 *ef-exp*)
1805 (list 0 (let ((*ef-arith?*)) (gf-x2n *gf-prim*)))
1806 (if *ef-irred?* (ef-prim) '$unknown) )))
1807 ($ef_get_data) )
1810 (defstruct (gf-data (:print-function gf-data-short-print))
1811 char exp red prim card
1812 ord fs-ord fsx fsx-base-p x^p-powers )
1814 (defun gf-data-short-print (struct stream i)
1815 (declare (ignore struct i))
1816 (format stream "Structure [GF-DATA]") ) ;; wxMaxima returns this
1817 ;; terminal should return this too
1819 ;; returns a struct containing all data necessary to use gf_set_again (see below)
1820 (defmfun $gf_get_data ()
1821 (gf-data? "gf_get_data")
1822 (make-gf-data
1823 :char *gf-char* ; characteristic
1824 :exp *gf-exp* ; exponent
1825 :red *gf-red* ; reduction
1826 :prim *gf-prim* ; primitive
1827 :card *gf-card* ; cardinality
1828 :ord *gf-ord* ; order
1829 :fs-ord *gf-fs-ord* ; factors of order
1830 :fsx *gf-fsx* ; extended factors of order
1831 :fsx-base-p *gf-fsx-base-p* ; extended factors in base p
1832 :x^p-powers *gf-x^p-powers* )) ; pre-calculated powers
1834 (defstruct (ef-data (:print-function ef-data-short-print))
1835 exp red prim card
1836 ord fs-ord fsx fsx-base-q x^q-powers )
1838 (defun ef-data-short-print (struct stream i)
1839 (declare (ignore struct i))
1840 (format stream "Structure [EF-DATA]") )
1842 (defmfun $ef_get_data ()
1843 (ef-data? "ef_get_data")
1844 (make-ef-data
1845 :exp *ef-exp* ; exponent
1846 :red *ef-red* ; reduction
1847 :prim *ef-prim* ; primitive
1848 :card *ef-card* ; cardinality
1849 :ord *ef-ord* ; order
1850 :fs-ord *ef-fs-ord* ; factors of order
1851 :fsx *ef-fsx* ; extended factors of order
1852 :fsx-base-q *ef-fsx-base-q* ; extended factors in base q
1853 :x^q-powers *ef-x^q-powers* )) ; pre-calculated powers
1855 (defmfun $gf_info (&optional (t? t))
1856 (gf-data? "gf_info")
1857 (let ((no-prim (or (null *gf-prim*) (equal *gf-prim* '$unknown)))
1858 (*ef-arith?*) )
1859 (format t?
1860 "characteristic = ~a~:[, ~;~%~]~\
1861 reduction polynomial = ~a~:[, ~;~%~]~\
1862 primitive element = ~a~:[, ~;~%~]~\
1863 nr of elements = ~a~:[, ~;~%~]~\
1864 nr of units = ~a~:[, ~;~]~\
1865 ~:[~;~%nr of primitive elements = ~a~] ~%"
1866 *gf-char* t?
1867 (mfuncall '$string (gf-x2p *gf-red*)) t?
1868 (mfuncall '$string
1869 (if no-prim
1870 *gf-prim*
1871 (gf-x2p *gf-prim*) )) t?
1872 *gf-card* t?
1873 *gf-ord* (or t? no-prim) (not no-prim)
1874 (totient-by-fs-n *gf-fs-ord*) )))
1876 (defun totient-by-fs-n (fs-n)
1877 (let ((phi 1) p e)
1878 (dolist (f fs-n phi)
1879 (setq p (car f) e (cadr f))
1880 (setq phi (* phi (1- p) (expt p (1- e)))) )))
1882 (defmfun $gf_infolist () ;; enables testing gf_set_data in rtest
1883 (gf-data? "gf_infolist")
1884 (let ((*ef-arith?*))
1885 `((mlist simp)
1886 ,*gf-char*
1887 ,(gf-x2p *gf-red*)
1888 ,(if (or (null *gf-prim*) (equal *gf-prim* '$unknown))
1889 *gf-prim*
1890 (gf-x2p *gf-prim*) )
1891 ,*gf-card*
1892 ,*gf-ord* )))
1894 (defmfun $ef_info (&optional (t? t))
1895 (ef-data? "ef_info")
1896 (let ((no-prim (or (null *ef-prim*) (equal *ef-prim* '$unknown)))
1897 (*ef-arith?* t) )
1898 (format t?
1899 "reduction polynomial = ~a~:[, ~;~%~]~\
1900 primitive element = ~a~:[, ~;~%~]~\
1901 nr of elements = ~a~:[, ~;~%~]~\
1902 nr of units = ~a~:[, ~;~]~\
1903 ~:[~;~%nr of primitive elements = ~a~] ~%"
1904 (mfuncall '$string (gf-x2p *ef-red*)) t?
1905 (mfuncall '$string
1906 (if no-prim
1907 *ef-prim*
1908 (gf-x2p *ef-prim*) )) t?
1909 *ef-card* t?
1910 *ef-ord* (or t? no-prim) (not no-prim)
1911 (totient-by-fs-n *ef-fs-ord*) )))
1913 (defmfun $ef_infolist () ;; enables testing ef_set_data in rtest
1914 (ef-data? "ef_infolist")
1915 (let ((*ef-arith?* t))
1916 `((mlist simp)
1917 ,(gf-x2p *ef-red*)
1918 ,(if (or (null *ef-prim*) (equal *ef-prim* '$unknown))
1919 *ef-prim*
1920 (gf-x2p *ef-prim*) )
1921 ,*ef-card*
1922 ,*ef-ord* )))
1925 (defmfun $gf_unset ()
1926 (setq $gf_powers nil $gf_logs nil $gf_zech_logs nil *gf-powers* nil *gf-logs?* nil
1927 $gf_rat nil
1928 $ef_coeff_mult nil $ef_coeff_add nil $ef_coeff_inv nil $ef_coeff_exp nil
1929 *gf-rat-header* nil *gf-char* 0
1930 *gf-exp* 1 *gf-ord* 0 *gf-card* 0 ;; *gf-exp* = 1 when gf_set_data has no optional arg
1931 *gf-red* nil *f2-red* 0 *gf-prim* nil
1932 *gf-fs-ord* nil *gf-fsx* nil *gf-fsx-base-p* nil *gf-x^p-powers* nil
1933 *gf-char?* nil *gf-red?* nil *gf-irred?* nil *gf-data?* nil )
1936 (defmfun $ef_unset ()
1937 (setq *ef-exp* 0 *ef-ord* 0 *ef-card* 0
1938 *ef-red* nil *ef-prim* nil
1939 *ef-fs-ord* nil *ef-fsx* nil *ef-fsx-base-q* nil *ef-x^q-powers* nil
1940 *ef-red?* nil *ef-irred?* nil *ef-data?* nil )
1944 ;; Minimal set
1945 ;; Just set characteristic and reduction poly to allow basic arithmetics on the fly.
1946 (defmfun $gf_minimal_set (p &optional (red))
1947 (unless (and (integerp p) (primep p))
1948 (gf-merror (intl:gettext "First argument to `gf_minimal_set' must be a prime number.")) )
1949 ($gf_unset)
1950 (setq *gf-char* p
1951 *gf-char?* t )
1952 (gf-set-rat-header)
1953 (let ((*ef-arith?*))
1954 (when red
1955 (setq *gf-red* (gf-p2x-red red "gf_minimal_set")
1956 *gf-red?* t
1957 *gf-exp* (car *gf-red*) ))
1958 (format nil "characteristic = ~a, reduction polynomial = ~a"
1959 *gf-char*
1960 (if red (mfuncall '$string (gf-x2p *gf-red*)) "false") )))
1963 (defmfun $ef_minimal_set (red)
1964 (ef-gf-field? "ef_minimal_set")
1965 ($ef_unset)
1966 (let ((*ef-arith?* t))
1967 (when red
1968 (setq *ef-red* (gf-p2x-red red "ef_minimal_set")
1969 *ef-exp* (car *ef-red*)
1970 *ef-red?* t ))
1971 (format nil "reduction polynomial = ~a"
1972 (if red (mfuncall '$string (gf-x2p *ef-red*)) "false") )))
1975 (defmfun $gf_characteristic ()
1976 (gf-char? "gf_characteristic")
1977 *gf-char* )
1979 (defmfun $gf_exponent ()
1980 (gf-red? "gf_exponent")
1981 *gf-exp* )
1983 (defmfun $gf_reduction ()
1984 (gf-red? "gf_reduction")
1985 (when *gf-red* (let ((*ef-arith?*)) (gf-x2p *gf-red*))) )
1987 (defmfun $gf_cardinality ()
1988 (gf-data? "gf_cardinality")
1989 *gf-card* )
1992 (defmfun $ef_exponent ()
1993 (ef-red? "ef_exponent")
1994 *ef-exp* )
1996 (defmfun $ef_reduction ()
1997 (ef-red? "ef_reduction")
1998 (when *ef-red* (let ((*ef-arith?* t)) (gf-x2p *ef-red*))) )
2000 (defmfun $ef_cardinality ()
2001 (ef-data? "ef_cardinality")
2002 *ef-card* )
2005 ;; Reuse data and results from a previous gf_set_data
2006 (defmfun $gf_set_again (data)
2007 (unless (gf-data-p data)
2008 (gf-merror (intl:gettext
2009 "Argument to `gf_set_again' must be a return value of `gf_set_data'." )))
2010 ($gf_unset)
2011 (gf-set-rat-header)
2012 (setq *gf-char* (gf-data-char data)
2013 *gf-exp* (gf-data-exp data)
2014 *gf-red* (gf-data-red data)
2015 *gf-prim* (gf-data-prim data)
2016 *gf-card* (gf-data-card data)
2017 *gf-ord* (gf-data-ord data)
2018 *gf-fs-ord* (gf-data-fs-ord data)
2019 *gf-fsx* (gf-data-fsx data)
2020 *gf-fsx-base-p* (gf-data-fsx-base-p data)
2021 *gf-x^p-powers* (gf-data-x^p-powers data)
2022 *gf-irred?* (= *gf-ord* (1- *gf-card*))
2023 *gf-char?* t
2024 *gf-red?* t
2025 *gf-data?* t ))
2027 (defmfun $ef_set_again (data)
2028 (ef-gf-field? "ef_set_again")
2029 (unless (ef-data-p data)
2030 (gf-merror (intl:gettext
2031 "Argument to `ef_set_again' must be a return value of `ef_set_data'." )))
2032 ($ef_unset)
2033 (setq *ef-exp* (ef-data-exp data)
2034 *ef-red* (ef-data-red data)
2035 *ef-prim* (ef-data-prim data)
2036 *ef-card* (ef-data-card data)
2037 *ef-ord* (ef-data-ord data)
2038 *ef-fs-ord* (ef-data-fs-ord data)
2039 *ef-fsx* (ef-data-fsx data)
2040 *ef-fsx-base-q* (ef-data-fsx-base-q data)
2041 *ef-x^q-powers* (ef-data-x^q-powers data)
2042 *ef-irred?* (= *ef-ord* (1- *ef-card*))
2043 *ef-red?* t
2044 *ef-data?* t ))
2046 ;; -----------------------------------------------------------------------------
2049 ;; lookup tables ---------------------------------------------------------------
2052 (defmfun $gf_make_arrays ()
2053 (format t "`gf_make_arrays' is deprecated. ~%~\
2054 The user is asked to use `gf_make_logs' instead.~%" )
2055 ($gf_make_logs) )
2057 (defmfun $gf_make_logs () ;; also zech-logs and antilogs
2058 (gf-field? "gf_make_logs")
2059 (let ((*ef-arith?*)) (gf-make-logs)) )
2061 (defun gf-make-logs ()
2062 (unless (typep *gf-ord* 'fixnum)
2063 (gf-merror (intl:gettext "`gf_make_logs': group order must be a fixnum.")) )
2064 (let ((x (list 0 1)) (ord *gf-ord*) (primx *gf-prim*) (red *gf-red*))
2065 (declare (fixnum ord))
2067 ;; power table of the field, where the i-th element is (the numerical
2068 ;; equivalent of) the field element e^i, where e is a primitive element
2070 (setq $gf_powers (make-array (1+ ord) :element-type 'integer)
2071 *gf-powers* (make-array (1+ ord) :element-type 'list :initial-element nil) )
2072 (setf (svref $gf_powers 0) 1
2073 (svref *gf-powers* 0) (list 0 1) )
2074 (do ((i 1 (1+ i)))
2075 ((> i ord))
2076 (declare (fixnum i))
2077 (setq x (gf-times x primx red))
2078 (setf (svref $gf_powers i) (gf-x2n x)
2079 (svref *gf-powers* i) x ))
2081 ;; log table: the inverse lookup of the power table
2083 (setq $gf_logs (make-array (1+ ord) :initial-element nil))
2084 (do ((i 0 (1+ i)))
2085 ((= i ord))
2086 (declare (fixnum i))
2087 (setf (svref $gf_logs (svref $gf_powers i)) i) )
2089 ;; zech-log table: lookup table for efficient addition
2091 (setq $gf_zech_logs (make-array (1+ ord) :initial-element nil))
2092 (do ((i 0 (1+ i)) (one (list 0 1)))
2093 ((> i ord))
2094 (declare (fixnum i))
2095 (setf (svref $gf_zech_logs i)
2096 (svref $gf_logs (gf-x2n (gf-plus (svref *gf-powers* i) one))) ))
2098 (setq *gf-logs?* t)
2099 `((mlist simp) ,$gf_powers ,$gf_logs ,$gf_zech_logs) ))
2101 (defun gf-clear-tables ()
2102 (setq $gf_powers nil
2103 $gf_logs nil
2104 $gf_zech_logs nil
2105 *gf-logs?* nil ))
2107 ;; -----------------------------------------------------------------------------
2110 ;; converting to/from internal representation ----------------------------------
2112 ;; user level <---> internal
2113 ;; 0 nil
2114 ;; integer # 0 (0 integer') where integer' = mod(integer, *gf-char*)
2115 ;; x (1 1)
2116 ;; x^4 + 3*x^2 + 4 (4 1 2 3 0 4)
2118 ;; This representation uses the term part of the internal CRE representation.
2119 ;; The coeffcients are exclusively positive: 1, 2, ..., (*gf-char* -1)
2120 ;; Header informations are stored in *gf-rat-header*.
2122 ;; gf_set_data(5, 4)$
2123 ;; :lisp `(,*gf-char* ,*gf-exp*)
2124 ;; (5 4)
2125 ;; p : x^4 + 3*x^2 - 1$
2126 ;; :lisp ($rat $p)
2127 ;; ((MRAT SIMP ($X) (X33303)) (X33303 4 1 2 3 0 -1) . 1)
2128 ;; :lisp (gf-p2x $p)
2129 ;; (4 1 2 3 0 4)
2130 ;; :lisp *gf-rat-header*
2131 ;; (MRAT SIMP ($X) (X33303))
2133 ;; Remark: I compared the timing results of the arithmetic functions using this
2134 ;; data structure to arithmetics using an array implementation and in case of
2135 ;; modulus 2 to an implementation using bit-arithmetics over integers.
2136 ;; It turns out that in all cases the timing advantages of other data structures
2137 ;; were consumed by conversions from/to the top-level.
2138 ;; So for sparse polynomials the CRE representation seems to fit best.
2141 (defun gf-p2x (p)
2142 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2143 (setq p (car (let ((modulus)) (prep1 p))))
2144 (cond
2145 ((integerp p)
2146 (cond
2147 ((= p 0) nil)
2148 (t (setq p (gf-cmod p))
2149 (if (= p 0) nil (list 0 p)) )))
2151 (setq p (gf-mod (cdr p)))
2152 (if (typep (car p) 'fixnum)
2154 (gf-merror (intl:gettext "Exponents are limited to fixnums.")) ))))
2157 ;; version of gf-p2x that doesn't apply mod reduction
2159 (defun gf-p2x-raw (p)
2160 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2161 (setq p (car (let ((modulus)) (prep1 p))))
2162 (cond
2163 ((integerp p) (if (= 0 p) nil (list 0 p)))
2164 (t (setq p (cdr p))
2165 (unless (every #'numberp p)
2166 (gf-merror (intl:gettext "gf: polynomials must be univariate.")) )
2167 p )))
2170 (defun gf-x2p (x)
2171 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2172 (setq x
2173 (cond
2174 ((null x) 0)
2175 ((= 0 (the fixnum (car x))) (gf-cp2smod (cadr x)))
2176 (t (gf-np2smod x)) ))
2177 (if (eql $gf_rat t)
2178 (gf-x2cre x)
2179 (gf-disrep x) ))
2181 ;; depending on $gf_rat gf-x2p returns a CRE or a ratdisrepped expression
2183 (defun gf-x2cre (x)
2184 #+ (or ccl ecl gcl sbcl) (declare (optimize (speed 3) (safety 0)))
2185 (if (integerp x)
2186 `(,*gf-rat-header* ,x . 1)
2187 `(,*gf-rat-header* ,(cons (caar (cdddr *gf-rat-header*)) x) . 1) ))
2189 (defun gf-disrep (x &optional (var '$x))
2190 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2191 (if (integerp x) x
2192 (maybe-char-is-fixnum-let ((c 0))
2193 (do ((not-plus? (null (cddr x))) p (e 0))
2194 ((null x) (if not-plus? (car p) (cons '(mplus simp) p)))
2195 (declare (fixnum e))
2196 (setq e (car x) c (cadr x) x (cddr x)
2197 p (cond
2198 ((= 0 e)
2199 (cons c p) )
2200 ((= 1 e)
2201 (if (= 1 c)
2202 (cons var p)
2203 (cons `((mtimes simp) ,c ,var) p) ))
2204 ((= 1 c)
2205 (cons `((mexpt simp) ,var ,e) p) )
2207 (cons `((mtimes simp) ,c ((mexpt simp) ,var ,e)) p) )))))))
2209 ;; -----------------------------------------------------------------------------
2212 ;; evaluation and adjustment ---------------------------------------------------
2215 ;; an arbitrary polynomial is evaluated in a given field
2217 (defmfun $gf_eval (a)
2218 (gf-char? "gf_eval")
2219 (let ((*ef-arith?*)) (gf-eval a *gf-red* "gf_eval")) )
2221 (defmfun $ef_eval (a)
2222 (ef-gf-field? "ef_eval")
2223 (let ((*ef-arith?* t))
2224 (unless (equal a ($expand a))
2225 (gf-merror (intl:gettext "`ef_eval': The argument must be an expanded polynomial.")) )
2226 (gf-eval a *ef-red* "ef_eval") ))
2228 (defun gf-eval (a red fun)
2229 (setq a (let ((modulus)) (car (prep1 a))))
2230 (cond
2231 ((integerp a) (gf-cmod a))
2233 (setq a (gf-mod (cdr a)))
2234 (and a (not (typep (car a) 'fixnum))
2235 (gf-merror (intl:gettext "`~m': The exponent is expected to be a fixnum.") fun) )
2236 (gf-x2p (gf-nred a red)) )))
2239 ;; gf-mod adjusts arbitrary integer coefficients (pos, neg or unbounded)
2241 (defun gf-mod (x)
2242 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2243 (if (null x) nil
2244 (maybe-char-is-fixnum-let ((c 0))
2245 (do ((r x (cddr r)) res)
2246 ((null r) (nreverse res))
2247 (unless (numberp (cadr r))
2248 (gf-merror (intl:gettext "gf: polynomials must be univariate.")) )
2249 (setq c (gf-cmod (cadr r)))
2250 (unless (= c 0) (setq res (cons c (cons (car r) res)))) ))))
2252 ;; positive 2 symmetric mod:
2254 (defun gf-np2smod (x) ;; modifies x
2255 (cond
2256 ((null x) nil)
2257 ((not $gf_balanced) x)
2258 (*ef-arith?*
2259 (*f-np2smod x *gf-card* #'(lambda (c) (neg (gf-ctimes (1- *gf-char*) c)))) )
2261 (*f-np2smod x *gf-char* #'(lambda (c) (- (the integer c) *gf-char*))) )))
2263 (defun *f-np2smod (x p cp2smod-fn)
2264 (if (null x) x
2265 (maybe-char-is-fixnum-let ((p2 (ash p -1)))
2266 (do ((r (cdr x) (cddr r))) (())
2267 (when (> (the integer (car r)) p2)
2268 (rplaca r (funcall cp2smod-fn (car r))) )
2269 (when (null (cdr r)) (return x)) ))))
2271 ;; adjust a coefficient to a symmetric modulus:
2272 (defun gf-cp2smod (c)
2273 (cond
2274 ((not $gf_balanced) c)
2275 (*ef-arith?*
2276 (if (> c (ash *gf-card* -1)) (neg (gf-ctimes c (1- *gf-char*))) c) )
2278 (if (> c (ash *gf-char* -1)) (- (the integer c) *gf-char*) c) )))
2280 ;; -----------------------------------------------------------------------------
2283 ;; arithmetic in Galois Fields - Maxima level functions ------------------------
2286 ;; gf:
2288 (defmfun $gf_neg (a)
2289 (gf-char? "gf_neg")
2290 (let ((*ef-arith?*))
2291 (gf-x2p (gf-nminus (gf-p2x a))) ))
2293 (defmfun $gf_add (&rest args)
2294 (gf-char? "gf_add")
2295 (let ((*ef-arith?*))
2296 (setq args (mapcar #'gf-p2x args))
2297 (gf-x2p (reduce #'gf-plus args)) ))
2299 (defmfun $gf_sub (&rest args)
2300 (gf-char? "gf_sub")
2301 (let ((*ef-arith?*))
2302 (setq args (mapcar #'gf-p2x args))
2303 (gf-x2p (gf-plus (car args) (gf-minus (reduce #'gf-plus (cdr args))))) ))
2305 (defmfun $gf_mult (&rest args)
2306 (gf-char? "gf_mult")
2307 (let ((*ef-arith?*))
2308 (setq args (mapcar #'gf-p2x args))
2309 (and (not *gf-red*)
2310 (not (some #'null args))
2311 (not (typep (apply #'+ (mapcar #'car args)) 'fixnum))
2312 (gf-merror (intl:gettext "`gf_mult': Resulting exponent won't be a fixnum.")) )
2313 (gf-x2p (reduce #'(lambda (x y) (gf-times x y *gf-red*)) args)) ))
2315 (defmfun $gf_reduce (a b)
2316 (gf-char? "gf_reduce")
2317 (let ((*ef-arith?*))
2318 (gf-x2p (gf-nrem (gf-p2x a) (gf-p2x b))) ))
2320 (defmfun $gf_inv (a)
2321 (gf-red? "gf_inv")
2322 (let ((*ef-arith?*))
2323 (setq a (gf-inv (gf-p2x a) *gf-red*))
2324 (when a (gf-x2p a)) )) ;; a is nil in case the inverse does not exist
2326 (defmfun $gf_div (&rest args)
2327 (gf-red? "gf_div")
2328 (unless (cadr args)
2329 (gf-merror (intl:gettext "`gf_div' needs at least two arguments." )) )
2330 (let* ((*ef-arith?*)
2331 (a2 (mapcar #'gf-p2x args))
2332 (a2 (cons (car a2) (mapcar #'(lambda (x) (gf-inv x *gf-red*)) (cdr a2)))) )
2333 (cond
2334 ((some #'null (cdr a2)) ;; but check if exact division is possible ..
2335 (let ((q (gf-p2x (car args))) r)
2336 (setq args (cdr args))
2337 (do ((d (car args) (car args)))
2338 ((null d) (gf-x2p q))
2339 (multiple-value-setq (q r) (gf-divide q (gf-p2x d)))
2340 (when r (return)) ;; .. in case it is not return false
2341 (setq args (cdr args)) )))
2342 (t ;; a / b = a * b^-1 :
2343 (gf-x2p (reduce #'(lambda (x y) (gf-times x y *gf-red*)) a2)) ))))
2345 (defmfun $gf_exp (a n)
2346 (gf-char? "gf_exp")
2347 (let ((*ef-arith?*))
2348 (cond
2349 ((not n)
2350 (gf-merror (intl:gettext "`gf_exp' needs two arguments.")) )
2351 ((not (integerp n))
2352 (gf-merror (intl:gettext "Second argument to `gf_exp' must be an integer.")) )
2353 ((< (the integer n) 0)
2354 (unless *gf-red*
2355 (gf-merror (intl:gettext "`gf_exp': Unknown reduction polynomial.")) )
2356 (setq a (gf-inv (gf-p2x a) *gf-red*))
2357 (when a ($gf_exp (gf-x2p a) (neg n))) ) ;; a is nil in case the inverse does not exist
2358 (*gf-logs?*
2359 (gf-x2p (gf-pow-by-table (gf-p2x a) n)) )
2360 ((and *gf-irred?* *gf-x^p-powers*)
2361 (gf-x2p (gf-pow$ (gf-p2x a) n *gf-red*)) )
2363 (setq a (gf-p2x a))
2364 (and (not *gf-red*)
2365 (not (null a))
2366 (not (typep (* n (car a)) 'fixnum))
2367 (gf-merror (intl:gettext "`gf_exp': Resulting exponent won't be a fixnum.")) )
2368 (gf-x2p (gf-pow a n *gf-red*)) ))))
2370 ;; ef:
2372 (defmfun $ef_neg (a)
2373 (ef-gf-field? "ef_neg")
2374 (let ((*ef-arith?* t))
2375 (gf-x2p (gf-nminus (gf-p2x a))) ))
2377 (defmfun $ef_add (&rest args)
2378 (ef-gf-field? "ef_add")
2379 (let ((*ef-arith?* t))
2380 (setq args (mapcar #'gf-p2x args))
2381 (gf-x2p (reduce #'gf-plus args)) ))
2383 (defmfun $ef_sub (&rest args)
2384 (ef-gf-field? "ef_sub")
2385 (let ((*ef-arith?* t))
2386 (setq args (mapcar #'gf-p2x args))
2387 (gf-x2p (gf-plus (car args) (gf-minus (reduce #'gf-plus (cdr args))))) ))
2389 (defmfun $ef_mult (&rest args)
2390 (ef-gf-field? "ef_mult")
2391 (let ((*ef-arith?* t)
2392 (red *ef-red*) )
2393 (setq args (mapcar #'gf-p2x args))
2394 (and (not red)
2395 (not (some #'null args))
2396 (not (typep (apply #'+ (mapcar #'car args)) 'fixnum))
2397 (gf-merror (intl:gettext "`ef_mult': Resulting exponent won't be a fixnum.")) )
2398 (gf-x2p (reduce #'(lambda (x y) (gf-times x y red)) args)) ))
2400 (defmfun $ef_reduce (a b)
2401 (ef-gf-field? "ef_reduce")
2402 (let ((*ef-arith?* t))
2403 (gf-x2p (gf-nrem (gf-p2x a) (gf-p2x b))) ))
2405 (defmfun $ef_inv (a)
2406 (ef-red? "ef_inv")
2407 (let ((*ef-arith?* t))
2408 (setq a (gf-inv (gf-p2x a) *ef-red*))
2409 (when a (gf-x2p a)) ))
2411 (defmfun $ef_div (&rest args)
2412 (ef-red? "ef_div")
2413 (unless (cadr args)
2414 (gf-merror (intl:gettext "`ef_div' needs at least two arguments." )) )
2415 (let ((*ef-arith?* t)
2416 (red *ef-red*) )
2417 (setq args (mapcar #'gf-p2x args))
2418 (setq args
2419 (cons (car args) (mapcar #'(lambda (x) (gf-inv x red)) (cdr args))) )
2420 (cond
2421 ((null (car args)) 0)
2422 ((some #'null (cdr args)) nil)
2423 (t (gf-x2p (reduce #'(lambda (x y) (gf-times x y red)) args))) )))
2425 (defmfun $ef_exp (a n)
2426 (ef-gf-field? "ef_exp")
2427 (let ((*ef-arith?* t))
2428 (cond
2429 ((< (the integer n) 0)
2430 (unless *ef-red*
2431 (gf-merror (intl:gettext "`ef_exp': Unknown reduction polynomial.")) )
2432 (setq a (gf-inv (gf-p2x a) *ef-red*))
2433 (when a ($ef_exp (gf-x2p a) (neg n))) )
2434 ((and *ef-irred?* *ef-x^q-powers*)
2435 (gf-x2p (gf-pow$ (gf-p2x a) n *ef-red*)) )
2437 (setq a (gf-p2x a))
2438 (and (not *ef-red*)
2439 (not (null a))
2440 (not (typep (* n (car a)) 'fixnum))
2441 (gf-merror (intl:gettext "`ef_exp': Resulting exponent won't be a fixnum.")) )
2442 (gf-x2p (gf-pow a n *ef-red*)) ))))
2444 ;; -----------------------------------------------------------------------------
2447 ;; arithmetic in Galois Fields - Lisp level functions --------------------------
2450 ;; Both gf (base field) and ef (extension field) Maxima level functions use
2451 ;; this Lisp level functions. The switch *ef-arith?* controls how the coefficients
2452 ;; were treated. The coefficient functions gf-ctimes and friends behave
2453 ;; differently depending on *ef-arith?*. See above definitions.
2455 ;; Remark: A prefixed character 'n' indicates a destructive function.
2457 ;; c * x
2459 (defun gf-xctimes (x c)
2460 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2461 (maybe-char-is-fixnum-let ((c c))
2462 (if (or (= 0 c) (null x)) nil
2463 (do* ((res (list (car x) (gf-ctimes c (cadr x))))
2464 (r (cdr res) (cddr r))
2465 (rx (cddr x) (cddr rx)) )
2466 ((null rx) res)
2467 (rplacd r (list (car rx) (gf-ctimes c (cadr rx)))) ))))
2469 (defun gf-nxctimes (x c) ;; modifies x
2470 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2471 (maybe-char-is-fixnum-let ((c c))
2472 (if (or (= 0 c) (null x)) nil
2473 (do ((r (cdr x) (cddr r)))
2474 ((null r) x)
2475 (rplaca r (gf-ctimes c (car r))) ))))
2477 ;; c*v^e * x
2479 (defun gf-xectimes (x e c)
2480 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2481 (declare (fixnum e))
2482 (maybe-char-is-fixnum-let ((c c))
2483 (if (or (= 0 c) (null x)) nil
2484 (do* ((res (list (+ e (the fixnum (car x))) (gf-ctimes c (cadr x))))
2485 (r (cdr res) (cddr r))
2486 (rx (cddr x) (cddr rx)) )
2487 ((null rx) res)
2488 (rplacd r (list (+ e (the fixnum (car rx))) (gf-ctimes c (cadr rx)))) ))))
2490 ;; v^e * x
2492 (defun gf-nxetimes (x e) ;; modifies x
2493 (if (null x) x
2494 (do ((r x (cddr r)))
2495 ((null r) x)
2496 (rplaca r (+ e (car r))) )))
2498 ;; - x
2500 (defun gf-minus (x)
2501 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2502 (if (or (null x) (= 2 *gf-char*)) x
2503 (do* ((res (list (car x) (gf-cminus-b (cadr x))))
2504 (r (cdr res) (cddr r))
2505 (rx (cddr x) (cddr rx)) )
2506 ((null rx) res)
2507 (rplacd r (list (car rx) (gf-cminus-b (cadr rx)))) )))
2509 (defun gf-nminus (x) ;; modifies x
2510 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2511 (if (or (null x) (= 2 *gf-char*)) x
2512 (do ((r (cdr x) (cddr r))) (())
2513 (rplaca r (gf-cminus-b (car r)))
2514 (when (null (cdr r)) (return x)) )))
2516 ;; x + y
2518 (defun gf-plus (x y)
2519 (cond
2520 ((null x) y)
2521 ((null y) x)
2522 (t (gf-nplus (copy-list x) y)) ))
2524 ;; merge y into x
2526 (defun gf-nplus (x y) ;; modifies x
2527 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2528 (cond
2529 ((null x) y)
2530 ((null y) x)
2532 (maybe-char-is-fixnum-let ((cy 0)(c 0))
2533 (prog ((ex 0)(ey 0) r) (declare (fixnum ex ey))
2535 (setq ex (car x) ey (car y) cy (cadr y))
2536 (cond
2537 ((> ey ex)
2538 (setq x (cons ey (cons cy x)) y (cddr y)) )
2539 ((= ey ex)
2540 (setq c (gf-cplus-b (cadr x) cy) y (cddr y))
2541 (cond
2542 ((= 0 c)
2543 (when (null (setq x (cddr x))) (return y))
2544 (when (null y) (return x))
2545 (go a1) )
2546 (t (rplaca (cdr x) c)) ))
2547 (t (setq r (cdr x)) (go b)) )
2548 (setq r (cdr x))
2550 (when (null y) (return x))
2551 (setq ey (car y) cy (cadr y))
2553 (while (and (cdr r) (> (the fixnum (cadr r)) ey))
2554 (setq r (cddr r)) )
2555 (cond
2556 ((null (cdr r)) (rplacd r y) (return x))
2557 ((> ey (the fixnum (cadr r)))
2558 (rplacd r (cons ey (cons cy (cdr r))))
2559 (setq r (cddr r) y (cddr y)) )
2561 (setq c (gf-cplus-b (caddr r) cy) y (cddr y))
2562 (if (= 0 c)
2563 (rplacd r (cdddr r))
2564 (rplaca (setq r (cddr r)) c) )) )
2565 (go a) )))))
2567 ;; x + c*v^e*y
2569 (defun gf-xyecplus (x y e c)
2570 (cond
2571 ((null y) x)
2572 ((null x) (gf-xectimes y e c))
2573 (t (gf-nxyecplus (copy-list x) y e c) )))
2575 ;; merge c*v^e*y into x
2577 (defun gf-nxyecplus (x y e c) ;; modifies x
2578 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2579 (cond
2580 ((null y) x)
2581 ((null x) (gf-xectimes y e c))
2583 (maybe-char-is-fixnum-let ((cy 0) (cc 0))
2584 (prog ((e e) (ex 0) (ey 0) r) (declare (fixnum e ex ey))
2586 (setq ey (+ (the fixnum (car y)) e)
2587 cy (gf-ctimes c (cadr y))
2588 ex (car x) )
2589 (cond
2590 ((> ey ex)
2591 (setq x (cons ey (cons cy x)) y (cddr y)) )
2592 ((= ey ex)
2593 (setq cc (gf-cplus-b (cadr x) cy) y (cddr y))
2594 (cond
2595 ((= 0 cc)
2596 (when (null (setq x (cddr x))) (return (gf-xectimes y e c)))
2597 (when (null y) (return x))
2598 (go a1) )
2599 (t (rplaca (cdr x) cc)) ))
2600 (t (setq r (cdr x)) (go b)) )
2601 (setq r (cdr x))
2603 (when (null y) (return x))
2604 (setq ey (+ (the fixnum (car y)) e)
2605 cy (gf-ctimes c (cadr y)) )
2607 (when (null (cdr r)) (go d))
2608 (setq ex (cadr r))
2609 (cond
2610 ((> ey ex)
2611 (rplacd r (cons ey (cons cy (cdr r))))
2612 (setq r (cddr r) y (cddr y))
2613 (go a) )
2614 ((= ey ex)
2615 (setq cc (gf-cplus-b (caddr r) cy))
2616 (if (= 0 cc)
2617 (rplacd r (cdddr r))
2618 (rplaca (setq r (cddr r)) cc) )
2619 (setq y (cddr y))
2620 (go a) )
2621 (t (setq r (cddr r)) (go b)) )
2623 (do () ((null y))
2624 (setq x (nconc x (list (+ (the fixnum (car y)) e) (gf-ctimes c (cadr y))))
2625 y (cddr y) ))
2626 (return x) ) ))))
2628 ;; x * y
2630 ;; For sparse polynomials (in Galois Fields) with not too high degrees
2631 ;; simple school multiplication is faster than Karatsuba.
2633 ;; x * y = (x1 + x2 + ... + xk) * (y1 + y2 + ... + yn)
2634 ;; = x1 * (y1 + y2 + ... + yn) + x2 * (y1 + y2 + ... + yn) + ...
2636 ;; where e.g. xi = ci*v^ei
2638 (defun gf-times (x y red)
2639 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2640 (if (or (null x) (null y)) nil
2641 (maybe-char-is-fixnum-let ((c 0) (cx 0))
2642 (do* ((res (gf-xectimes y (car x) (cadr x))) ;; x1 * (y1 + y2 + ... + yn). summands in res are sorted. res is a new list.
2643 (r1 (cdr res)) ;; r1 marks the place of xi*y1 in res. x[i+1]*y1 will be smaller.
2644 ry ;; ry iterates over y
2645 (x (cddr x) (cddr x)) ;; each loop: res += xi * (y1 + y2 + ... + yn)
2646 (e 0)(ex 0) )
2647 ((or (null x)(null y)) (gf-nred res red))
2648 (declare (fixnum e ex))
2649 (setq ry y ;; start with y1 again
2650 ex (car x) cx (cadr x) ;; xi = ci*v^ei
2651 e (+ ex (the fixnum (car ry))) ;; c*v^e = xi*y1
2652 c (gf-ctimes (cadr ry) cx) ) ;; zero divisor free mult in Fp^n
2654 (while (and (cdr r1) (< e (the fixnum (cadr r1))))
2655 (setq r1 (cddr r1)) ) ;; mark the position of xi*y1
2657 (do ((r r1)) (()) ;; merge xi*y1 into res and then xi*y2, etc...
2658 (cond
2659 ((or (null (cdr r)) (> e (the fixnum (cadr r))))
2660 (rplacd r (cons e (cons c (cdr r))))
2661 (setq r (cddr r)) )
2662 ((= 0 (setq c (gf-cplus-b (caddr r) c)))
2663 (rplacd r (cdddr r)) )
2664 (t (rplaca (setq r (cddr r)) c)) )
2666 (when (null (setq ry (cddr ry))) (return))
2667 (setq e (+ ex (the fixnum (car ry)))
2668 c (gf-ctimes (cadr ry) cx) )
2670 (while (and (cdr r) (< e (the fixnum (cadr r))))
2671 (setq r (cddr r)) ) )) )))
2673 ;; x^2
2675 ;; x * x = (x_1 + x_2 + ... + x_k) * (x_1 + x_2 + ... + x_k)
2677 ;; = 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 + ...
2679 ;; = 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 + ...
2681 ;; The reverse needs some additional consing but is slightly faster.
2683 (defun gf-sq (x red)
2684 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2685 (cond
2686 ((null x) x)
2687 ((and (not *ef-arith?*) (eql *gf-char* 2)) ;; the mod 2 case degrades to x_1^2 + x_2^2 + ... + x_k^2
2688 (do (res)
2689 ((null x) (gf-nred (nreverse res) red))
2690 (setq res (cons 1 (cons (ash (car x) 1) res))
2691 x (cddr x) ) ))
2693 (maybe-char-is-fixnum-let ((ci 0)(*2ci 0)(c 0))
2694 (setq x (reverse x)) ;; start with x_k
2695 (prog (res ;; result
2696 r ;; insertion marker in res
2697 acc ;; acc accumulates previous x_i
2698 r1 ;; r1 iterates in each loop over acc
2699 (e 0) (ei 0) ) (declare (fixnum e ei))
2701 (setq ci (car x) ei (cadr x) ;; x_i = ci*v^ei
2702 *2ci (gf-cplus-b ci ci) ;; 2*ci (2*ci # 0 when *gf-char* # 2)
2703 res (cons (+ ei ei) (cons (gf-ctimes ci ci) res)) ;; res += x_i^2 (ci^2 # 0, no zero divisors)
2704 r (cdr res) ;; place insertion marker behind x_i^2
2705 r1 acc )
2707 (when (or (null r1) (= 0 *2ci)) ;; in ef *2ci might be 0 !
2708 (when (null (setq x (cddr x))) (return (gf-nred res red)))
2709 (setq acc (cons ei (cons ci acc))) ;; cons previous x_i to acc ..
2710 (go a1) ) ;; .. and start next loop
2712 (setq e (+ ei (the fixnum (car r1)))
2713 c (gf-ctimes *2ci (cadr r1))
2714 r1 (cddr r1) )
2716 (while (< e (the fixnum (cadr r)))
2717 (setq r (cddr r)) )
2718 (cond
2719 ((> e (the fixnum (cadr r)))
2720 (rplacd r (cons e (cons c (cdr r))))
2721 (setq r (cddr r)) )
2723 (setq c (gf-cplus-b c (caddr r)))
2724 (if (= 0 c)
2725 (rplacd r (cdddr r))
2726 (rplaca (setq r (cddr r)) c) ) ))
2727 (go a) ))) ))
2729 ;; x^n mod y
2731 (defun gf-pow (x n red) ;; assume 0 <= n
2732 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2733 (declare (integer n))
2734 (cond
2735 ((= 0 n) (list 0 1))
2736 ((null x) nil)
2737 (t (do (res)(())
2738 (when (oddp n)
2739 (setq res (if res (gf-times x res red) x))
2740 (when (= 1 n)
2741 (return-from gf-pow res) ))
2742 (setq n (ash n -1)
2743 x (gf-sq x red)) ))))
2745 ;; in a field use precomputed *gf-x^p-powers* resp. *ef-x^q-powers*
2747 (defun gf-pow$ (x n red)
2748 (if *ef-arith?*
2749 (if *ef-irred?*
2750 (*f-pow$ x n red *gf-card* *ef-card* *ef-x^q-powers*)
2751 (gf-pow x n red) )
2752 (if *gf-irred?*
2753 (*f-pow$ x n red *gf-char* *gf-card* *gf-x^p-powers*)
2754 (gf-pow x n red) )))
2756 (defun *f-pow$ (x n red p card x^p-powers)
2757 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2758 (declare (integer n p card))
2759 (cond
2760 ((= 0 n) (list 0 1))
2761 ((null x) nil)
2762 ((>= n card) (gf-pow x n red))
2764 (let ((prod (list 0 1))
2765 (j 0) n-base-p y )
2766 (do (quo r) ((= 0 n))
2767 (multiple-value-setq (quo r) (truncate n p))
2768 (push r n-base-p)
2769 (setq n quo) )
2770 (dolist (ni (nreverse n-base-p))
2771 (setq y (gf-compose (svref x^p-powers j) x red)
2772 y (gf-pow y ni red)
2773 prod (gf-times prod y red)
2774 j (1+ j) ))
2775 prod ))))
2777 ;; remainder:
2778 ;; x - quotient(x, y) * y
2780 (defun gf-rem (x y)
2781 (when (null y)
2782 (gf-merror (intl:gettext "~m arithmetic: Quotient by zero") (if *ef-arith?* "ef" "gf")) )
2783 (if (null x) x
2784 (gf-nrem (copy-list x) y) ))
2786 (defun gf-nrem (x y) ;; modifies x
2787 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2788 (when (null y)
2789 (gf-merror (intl:gettext "~m arithmetic: Quotient by zero") (if *ef-arith?* "ef" "gf")) )
2790 (if (null x) x
2791 (maybe-char-is-fixnum-let ((c 0) (lcx 0) (lcy-inv (gf-cinv (cadr y))))
2792 (let ((e 0) (ley (car y)))
2793 (declare (fixnum e ley))
2794 (setq lcy-inv (gf-cminus-b lcy-inv))
2795 (do ((y (cddr y)))
2796 ((null x) x)
2797 (setq e (- (the fixnum (car x)) ley))
2798 (when (< e 0) (return x))
2799 (setq lcx (cadr x)
2800 c (gf-ctimes lcx lcy-inv)
2801 x (gf-nxyecplus (cddr x) y e c)) )))))
2803 ;; reduce x by red
2805 ;; assume lc(red) = 1, reduction poly is monic
2807 (defun gf-nred (x red) ;; modifies x
2808 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2809 (if (or (null x) (null red)) x
2810 (let ((e 0) (le-red (car red)))
2811 (declare (fixnum e le-red))
2812 (setq red (cddr red))
2813 (do () ((null x) x)
2814 (setq e (- (the fixnum (car x)) le-red))
2815 (when (< e 0) (return x))
2816 (setq x (gf-nxyecplus (cddr x) red e (gf-cminus-b (cadr x)))) ))))
2818 ;; (monic) gcd
2820 (defun gf-gcd (x y)
2821 #+ (or ccl ecl gcl sbcl) (declare (optimize (speed 3) (safety 0)))
2822 (cond
2823 ((null x) y)
2824 ((null y) x)
2825 (t (let ((r nil))
2826 (do ()((null y)
2827 (if (eql 0 (car x)) (list 0 1)
2828 (gf-xctimes x (gf-cinv (cadr x))) ))
2829 (setq r (gf-rem x y))
2830 (psetf x y y r) )))))
2832 ;; (monic) extended gcd
2834 (defun gf-gcdex (x y red)
2835 #+ (or ccl ecl gcl sbcl) (declare (optimize (speed 3) (safety 0)))
2836 (let ((x1 (list 0 1)) x2 y1 (y2 (list 0 1)) q r)
2837 (do ()((null y)
2838 (let ((inv (gf-cinv (cadr x))))
2839 (mapcar #'(lambda (a) (gf-xctimes a inv)) (list x1 x2 x)) ))
2840 (multiple-value-setq (q r) (gf-divide x y))
2841 (psetf x y y r)
2842 (psetf
2843 y1 (gf-nplus (gf-nminus (gf-times q y1 red)) x1)
2844 x1 y1 )
2845 (psetf
2846 y2 (gf-nplus (gf-nminus (gf-times q y2 red)) x2)
2847 x2 y2 ) )))
2849 ;; inversion: y^-1
2851 ;; in case the inverse does not exist it returns nil
2852 ;; (might happen when reduction poly isn't irreducible)
2854 (defun gf-inv (y red)
2855 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2856 (when (null y)
2857 (gf-merror (intl:gettext "~m arithmetic: Quotient by zero") (if *ef-arith?* "ef" "gf")) )
2858 (let ((y1 (list 0 1)) (x red) x1 q r)
2859 (setq y (copy-list y))
2860 (do ()((null y)
2861 (when (= 0 (car x)) ;; gcd = 1 (const)
2862 (gf-nxctimes x1 (gf-cinv (cadr x))) ))
2863 (multiple-value-setq (q r) (gf-divide x y))
2864 (psetf x y y r)
2865 (psetf
2866 x1 y1
2867 y1 (gf-nplus (gf-nminus (gf-times q y1 red)) x1) )) ))
2869 ;; quotient and remainder
2871 (defun gf-divide (x y)
2872 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2873 (cond
2874 ((null y)
2875 (gf-merror (intl:gettext "~m arithmetic: Quotient by zero") (if *ef-arith?* "ef" "gf")) )
2876 ((null x) (values nil nil))
2878 (maybe-char-is-fixnum-let ((c 0) (lcx 0) (lcyi (gf-cinv (cadr y))))
2879 (let ((e 0) (ley (car y)))
2880 (declare (fixnum e ley))
2881 (setq x (copy-list x))
2882 (do (q (y (cddr y)))
2883 ((null x) (values (nreverse q) x))
2884 (setq e (- (the fixnum (car x)) ley))
2885 (when (< e 0)
2886 (return (values (nreverse q) x)) )
2887 (setq lcx (cadr x)
2888 x (cddr x)
2889 c (gf-ctimes lcx lcyi) )
2890 (unless (null y) (setq x (gf-nxyecplus x y e (gf-cminus-b c))))
2891 (setq q (cons c (cons e q))) ))))))
2893 ;; -----------------------------------------------------------------------------
2896 ;; polynomial/number/list - conversions ----------------------------------------
2899 ;; poly 2 number:
2901 (defmfun $ef_p2n (p)
2902 (gf-data? "ef_p2n")
2903 (let ((*ef-arith?* t)) (gf-x2n (gf-p2x p))) )
2905 (defmfun $gf_p2n (p &optional gf-char)
2906 (let ((*ef-arith?*))
2907 (cond
2908 (gf-char
2909 (let ((*gf-char* gf-char)) (gf-x2n (gf-p2x p))) )
2910 (*gf-char?*
2911 (gf-x2n (gf-p2x p)) )
2913 (gf-merror (intl:gettext "`gf_p2n': missing modulus.")) ))))
2915 (defun gf-x2n (x)
2916 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2917 (if (null x) 0
2918 (maybe-char-is-fixnum-let ((m *gf-char*))
2919 (when *ef-arith?* (setq m *gf-card*))
2920 (do ((n 0))(())
2921 (incf n (cadr x))
2922 (if (null (cddr x))
2923 (return (* n (expt m (the fixnum (car x)))))
2924 (setq n (* n (expt m (- (the fixnum (car x)) (the fixnum (caddr x)))))) )
2925 (setq x (cddr x)) ))))
2927 ;; number 2 poly:
2929 (defun gf-n2p-errchk (fun n)
2930 (unless (integerp n)
2931 (gf-merror (intl:gettext "`~m': Not an integer: ~m") fun n) ))
2933 (defmfun $gf_n2p (n &optional gf-char)
2934 (gf-n2p-errchk "gf_n2p" n)
2935 (let ((*ef-arith?*))
2936 (cond
2937 (gf-char
2938 (gf-set-rat-header)
2939 (let ((*gf-char* gf-char)) (gf-x2p (gf-n2x n))) )
2940 (*gf-char?*
2941 (gf-x2p (gf-n2x n)) )
2943 (gf-merror (intl:gettext "`gf_n2p': missing modulus.")) ))))
2945 (defmfun $ef_n2p (n)
2946 (gf-data? "ef_n2p")
2947 (gf-n2p-errchk "ef_n2p" n)
2948 (let ((*ef-arith?* t)) (gf-x2p (gf-n2x n))) )
2950 (defun gf-n2x (n)
2951 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
2952 (declare (integer n))
2953 (maybe-char-is-fixnum-let ((r 0) (m *gf-char*))
2954 (let ((e 0)) (declare (fixnum e))
2955 (when *ef-arith?* (setq m *gf-card*))
2956 (do (x)
2957 ((= 0 n) x)
2958 (multiple-value-setq (n r) (truncate n m))
2959 (unless (= 0 r)
2960 (setq x (cons e (cons r x))) )
2961 (incf e) ))))
2963 ;; poly 2 list:
2965 (defmfun $ef_p2l (p &optional (len 0))
2966 (declare (fixnum len))
2967 (let ((*ef-arith?* t))
2968 (cons '(mlist simp) (gf-x2l (gf-p2x-raw p) len)) )) ;; more flexibility ...
2970 (defmfun $gf_p2l (p &optional (len 0)) ;; len = 0 : no padding
2971 (declare (fixnum len))
2972 (let ((*ef-arith?*))
2973 (cons '(mlist simp) (gf-x2l (gf-p2x-raw p) len)) )) ;; ... by omitting mod reduction
2975 (defun gf-x2l (x len)
2976 #+ (or ccl ecl gcl sbcl) (declare (optimize (speed 3) (safety 0)))
2977 (declare (fixnum len))
2978 (do* ((e (if x (the fixnum (car x)) 0))
2979 (k (max e (1- len)) (1- k))
2980 l )
2981 ((< k 0) (nreverse l))
2982 (declare (fixnum e k))
2983 (cond
2984 ((or (null x) (> k e))
2985 (push 0 l) )
2986 ((= k e)
2987 (push (cadr x) l)
2988 (setq x (cddr x))
2989 (unless (null x) (setq e (the fixnum (car x)))) ))))
2991 ;; list 2 poly:
2993 (defmfun $ef_l2p (l)
2994 (gf-l2p-errchk l "ef_l2p")
2995 (let ((*ef-arith?* t)) (gf-x2p (gf-l2x (cdr l)))) )
2997 (defmfun $gf_l2p (l)
2998 (gf-l2p-errchk l "gf_l2p")
2999 (let ((*ef-arith?*)) (gf-x2p (gf-l2x (cdr l)))) )
3001 (defun gf-l2p-errchk (l fun)
3002 (unless ($listp l)
3003 (gf-merror (intl:gettext "`~m': Argument must be a list of integers.") fun) ))
3005 (defun gf-l2x (l)
3006 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3007 (setq l (reverse l))
3008 (maybe-char-is-fixnum-let ((c 0))
3009 (let ((e 0)) (declare (fixnum e))
3010 (do (x)
3011 ((null l) x)
3012 (unless (= 0 (setq c (car l)))
3013 (setq x (cons e (cons c x))) )
3014 (setq l (cdr l))
3015 (incf e) ))))
3017 ;; list 2 number:
3019 (defmfun $gf_l2n (l)
3020 (gf-char? "gf_l2n")
3021 (gf-l2p-errchk l "gf_l2n")
3022 (let ((*ef-arith?*)) (gf-l2n (cdr l))) )
3024 (defmfun $ef_l2n (l)
3025 (gf-data? "ef_l2n")
3026 (gf-l2p-errchk l "ef_l2n")
3027 (let ((*ef-arith?* t)) (gf-l2n (cdr l))) )
3029 (defun gf-l2n (l)
3030 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3031 (maybe-char-is-fixnum-let ((m *gf-char*) (c1 (car l)) (c 0))
3032 (when *ef-arith?* (setq m *gf-card*))
3033 (setq l (reverse (cdr l)))
3034 (do ((n 0) (b 1))
3035 ((null l) (+ (* c1 b) n))
3036 (declare (integer n b))
3037 (unless (= 0 (setq c (car l))) (incf n (* c b)))
3038 (setq b (* b m) l (cdr l)) )))
3040 ;; number 2 list:
3042 (defmfun $gf_n2l (n &optional (len 0)) ;; in case of len = 0 the list isn't padded or truncated
3043 (declare (integer n) (fixnum len))
3044 (gf-char? "gf_n2l")
3045 (gf-n2p-errchk "gf_n2l" n)
3046 (cons '(mlist simp)
3047 (let ((*ef-arith?*))
3048 (if (= 0 len) (gf-n2l n) (gf-n2l-twoargs n len)) )))
3050 (defmfun $ef_n2l (n &optional (len 0)) ;; in case of len = 0 the list isn't padded or truncated
3051 (declare (integer n) (fixnum len))
3052 (gf-data? "ef_n2l")
3053 (gf-n2p-errchk "ef_n2l" n)
3054 (cons '(mlist simp)
3055 (let ((*ef-arith?* t))
3056 (if (= 0 len) (gf-n2l n) (gf-n2l-twoargs n len)) )))
3058 (defun gf-n2l (n) ;; this version is frequently called by gf-precomp, keep it simple
3059 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3060 (declare (integer n))
3061 (maybe-char-is-fixnum-let ((m *gf-char*) (r 0))
3062 (when *ef-arith?* (setq m *gf-card*))
3063 (do (l) ((= 0 n) l)
3064 (multiple-value-setq (n r) (truncate n m))
3065 (setq l (cons r l)) )))
3067 (defun gf-n2l-twoargs (n len)
3068 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3069 (declare (integer n) (fixnum len))
3070 (maybe-char-is-fixnum-let ((m *gf-char*) (r 0))
3071 (when *ef-arith?* (setq m *gf-card*))
3072 (do (l) ((= 0 len) l)
3073 (multiple-value-setq (n r) (truncate n m))
3074 (setq l (cons r l))
3075 (decf len) )))
3077 ;; -----------------------------------------------------------------------------
3080 ;; irreducibility (Ben-Or algorithm) -------------------------------------------
3083 (defmfun $gf_irreducible_p (a &optional p)
3084 (cond
3085 (p (unless (and (integerp p) (primep p))
3086 (gf-merror (intl:gettext
3087 "`gf_irreducible_p': Second argument must be a prime number." )) ))
3088 (t (gf-char? "gf_irreducible_p")
3089 (setq p *gf-char*) ))
3090 (let* ((*ef-arith?*)
3091 (*gf-char* p)
3092 (x (gf-p2x a)) n) ;; gf-p2x depends on *gf-char*
3093 (cond
3094 ((null x) nil)
3095 ((= 0 (setq n (car x))) nil)
3096 ((= 1 n) t)
3097 (t (gf-irr-p x p (car x))) )))
3099 (defmfun $ef_irreducible_p (a)
3100 (ef-gf-field? "ef_irreducible_p")
3101 (let ((*ef-arith?* t))
3102 (setq a (gf-p2x a))
3103 (gf-irr-p a *gf-card* (car a)) ))
3105 ;; is y irreducible of degree n over Fq[x] ?
3107 ;; q,n > 1 !
3108 (defun gf-irr-p (y q n) ;; gf-irr-p is independent from any settings
3109 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3110 (declare (integer q) (fixnum n))
3111 (let* ((*gf-char* (car (cfactorw q)))
3112 (x (list 1 1))
3113 (mx (gf-minus x)) ;; gf-minus needs *gf-char*
3114 (lc (cadr y)) )
3115 (unless (= 1 lc)
3116 (setq y (gf-xctimes y (gf-cinv lc))) ) ;; monicize y
3117 (do ((i 1 (1+ i)) (xq x) (n2 (ash n -1)))
3118 ((> i n2) t)
3119 (declare (fixnum i n2))
3120 (setq xq (gf-pow xq q y))
3121 (unless (= 0 (car (gf-gcd y (gf-plus xq mx))))
3122 (return) ) )))
3124 ;; find an irreducible element
3126 ;; gf_irreducible is independent from any settings
3128 (defmfun $gf_irreducible (p n) ;; p,n : desired characteristic and degree
3129 (unless (and (integerp p) (primep p) (integerp n))
3130 (gf-merror (intl:gettext "`gf_irreducible' needs a prime number and an integer.")) )
3131 (gf-set-rat-header)
3132 (let* ((*gf-char* p)
3133 (*ef-arith?*)
3134 (irr (gf-irr p n)) )
3135 (when irr (gf-x2p irr)) ))
3137 (defmfun $ef_irreducible (n) ;; n : desired degree
3138 (ef-gf-field? "ef_irreducible")
3139 (unless (integerp n)
3140 (gf-merror (intl:gettext "`ef_irreducible' needs an integer.")) )
3141 (let* ((*ef-arith?* t)
3142 (irr (ef-irr n)) )
3143 (when irr (gf-x2p irr)) ))
3146 (defun gf-irr (p n)
3147 (*f-irr p n) )
3149 (defun ef-irr (n)
3150 (*f-irr *gf-card* n) )
3152 (defun *f-irr (q n)
3153 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3154 (when (= 1 n)
3155 (return-from *f-irr (list 1 1)) )
3156 (let* ((inc (min $gf_coeff_limit q))
3157 (i-lim (expt inc n))
3159 (do ((i 1 (1+ i)))
3160 ((>= i i-lim)
3161 (gf-merror (intl:gettext "No irreducible polynomial found.~%~\
3162 `gf_coeff_limit' might be too small.~%" )))
3163 (setq x (let ((*gf-char* inc)) (gf-n2x i)))
3164 (when (= 0 (car (last x 2)))
3165 (setq x (cons n (cons 1 x)))
3166 (when (gf-irr-p x q n) (return-from *f-irr x)) ))))
3168 ;; -----------------------------------------------------------------------------
3171 ;; Primitive elements ----------------------------------------------------------
3174 ;; Tests if an element is primitive in the field
3176 (defmfun $gf_primitive_p (a)
3177 (gf-data? "gf_primitive_p") ;; --> precomputations are performed
3178 (let* ((*ef-arith?*)
3179 (x (gf-p2x a))
3180 (n (gf-x2n x)) )
3181 (cond
3182 ((or (= 0 n) (>= n *gf-card*)) nil)
3183 ((= 1 *gf-exp*)
3184 (zn-primroot-p n *gf-char* *gf-ord* (mapcar #'car *gf-fs-ord*)) )
3186 (gf-prim-p x) ))))
3188 (defmfun $ef_primitive_p (a)
3189 (ef-data? "ef_primitive_p") ;; --> precomputations are performed
3190 (let ((*ef-arith?* t))
3191 (setq a (gf-p2x a))
3192 (cond
3193 ((null a) nil)
3194 ((>= (car a) *ef-exp*) nil)
3195 ((= (car a) 0)
3196 (if (= 1 *ef-exp*)
3197 (let ((*ef-arith?*)) (gf-prim-p (gf-n2x (cadr a))))
3198 nil ))
3199 (t (ef-prim-p a)) )))
3202 ;; Testing primitivity in (Fq^n)*:
3204 ;; We check f(x)^ei # 1 (ei = ord/pi) for all prime factors pi of ord.
3206 ;; With ei = sum(aij*q^j, j,0,m) in base q and using f(x)^q = f(x^q) we get
3208 ;; f(x)^ei = f(x)^sum(aij*q^j, j,0,m) = prod(f(x^q^j)^aij, j,0,m).
3211 ;; Special case: red is irreducible, f(x) = x+c and pi|ord and pi|q-1.
3213 ;; Then ei = (q^n-1)/(q-1) * (q-1)/pi = sum(q^j, j,0,n-1) * (q-1)/pi.
3215 ;; With ai = (q-1)/pi and using red(z) = prod(z - x^q^j, j,0,n-1) we get
3217 ;; f(x)^ei = f(x)^sum(ai*q^j, j,0,n-1) = (prod(f(x)^q^j, j,0,n-1))^ai
3219 ;; = (prod(x^q^j + c, j,0,n-1))^ai = ((-1)^n * prod(-c - x^q^j, j,0,n-1))^ai
3221 ;; = ((-1)^n * red(-c))^ai
3224 (defun gf-prim-p (x)
3225 (cond
3226 (*gf-irred?*
3227 (*f-prim-p-2 x *gf-char* *gf-red* *gf-fsx* *gf-fsx-base-p* *gf-x^p-powers*) )
3228 ((gf-unit-p x *gf-red*)
3229 (*f-prim-p-1 x *gf-red* *gf-ord* *gf-fs-ord*) )
3230 (t nil) ))
3232 (defun ef-prim-p (x)
3233 (cond
3234 (*ef-irred?*
3235 (*f-prim-p-2 x *gf-card* *ef-red* *ef-fsx* *ef-fsx-base-q* *ef-x^q-powers*) )
3236 ((gf-unit-p x *ef-red*)
3237 (*f-prim-p-1 x *ef-red* *ef-ord* *ef-fs-ord*) )
3238 (t nil) ))
3240 ;; *f-prim-p-1
3242 (defun *f-prim-p-1 (x red ord fs-ord)
3243 (dolist (pe fs-ord t)
3244 (when (equal '(0 1) (gf-pow x (truncate ord (car pe)) red)) (return)) ))
3246 ;; *f-prim-p-2 uses precomputations and exponentiation by composition
3248 (defun *f-prim-p-2 (x q red fs fs-base-q x^q-powers)
3249 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3250 (unless (or (= 2 *gf-char*) (= -1 (gf-jacobi x red q))) ;; red is assumed to be irreducible
3251 (return-from *f-prim-p-2) )
3252 (let ((exponent (car red))
3253 (x+c? (and (= (car x) 1) (= (cadr x) 1)))
3254 y prod -c z )
3255 (do ((i 0 (1+ i)) (j 0 0) (lf (array-dimension fs 0)))
3256 ((= i lf) t)
3257 (declare (fixnum i j lf))
3258 (cond
3259 ((and x+c? (cadr (svref fs i))) ;; linear and pi|ord and pi|p-1
3260 (setq -c (if (= 2 (length x)) 0 (gf-cminus-b (car (last x))))
3261 z (list 0 (gf-at red -c)) )
3262 (when (oddp exponent) (setq z (gf-minus z))) ;; (-1)^n * red(-c)
3263 (setq z (gf-pow z (caddr (svref fs i)) red)) ;; ((-1)^n * red(-c))^ai
3264 (when (or (null z) (equal z '(0 1)))
3265 (return nil) ))
3267 (setq prod (list 0 1))
3268 (dolist (aij (svref fs-base-q i))
3269 (setq y (gf-compose (svref x^q-powers j) x red) ;; f(x^q^j)
3270 y (gf-pow y aij red) ;; f(x^q^j)^aij
3271 prod (gf-times prod y red)
3272 j (1+ j) ))
3273 (when (or (null prod) (equal prod '(0 1))) ;; prod(f(x^q^j)^aij, j,0,m)
3274 (return nil) )) ))))
3277 ;; generalized Jacobi-symbol (Bach-Shallit, Theorem 6.7.1)
3279 (defmfun $gf_jacobi (a b)
3280 (gf-char? "gf_jacobi")
3281 (let* ((*ef-arith?*)
3282 (x (gf-p2x a))
3283 (y (gf-p2x b)) )
3284 (if (= 2 *gf-char*)
3285 (if (null (gf-rem x y)) 0 1)
3286 (gf-jacobi x y *gf-char*) )))
3288 (defmfun $ef_jacobi (a b)
3289 (ef-gf-field? "ef_jacobi")
3290 (let* ((*ef-arith?* t)
3291 (x (gf-p2x a))
3292 (y (gf-p2x b)) )
3293 (if (= 2 (car (cfactorw *gf-card*)))
3294 (if (null (gf-rem x y)) 0 1)
3295 (gf-jacobi x y *gf-card*) )))
3297 (defun gf-jacobi (u v q)
3298 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3299 (if (null (setq u (gf-rem u v))) 0
3300 (let* ((c (cadr u))
3301 (s (if (evenp (car v)) 1 (gf-cjacobi c))) )
3302 (cond
3303 ((= 0 (car u)) s)
3305 (setq u (gf-xctimes u (gf-cinv c)))
3306 (when (every #'oddp (list (ash (1- q) -1) (car u) (car v)))
3307 (setq s (neg s)) )
3308 (* s (gf-jacobi v u q)) )))))
3310 (defun gf-cjacobi (c)
3311 (if *ef-arith?*
3312 (let ((*ef-arith?*)) (gf-jacobi (gf-n2x c) *gf-red* *gf-char*))
3313 ($jacobi c *gf-char*) ))
3316 ;; modular composition (uses Horner and square and multiply)
3317 ;; y(x) mod red
3319 (defmfun $gf_compose (a b)
3320 (gf-red? "gf_compose")
3321 (let ((*ef-arith?*))
3322 (gf-x2p (gf-compose (gf-p2x a) (gf-p2x b) *gf-red*)) ))
3324 (defmfun $ef_compose (a b)
3325 (ef-red? "ef_compose")
3326 (let ((*ef-arith?* t))
3327 (gf-x2p (gf-compose (gf-p2x a) (gf-p2x b) *ef-red*)) ))
3329 (defun gf-compose (x y red)
3330 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3331 (cond
3332 ((or (null x) (null y)) nil)
3333 ((= 0 (car y)) y)
3334 ((= 0 (car x))
3335 (let ((n (gf-at y (cadr x))))
3336 (if (= 0 n) nil (list 0 n)) ))
3338 (do (res) (())
3339 (setq res (gf-nplus res (list 0 (cadr y))))
3340 (when (null (cddr y))
3341 (return (gf-times res (gf-pow x (car y) red) red)) )
3342 (setq res (gf-times res (gf-pow x (- (car y) (caddr y)) red) red)
3343 y (cddr y) ) ))))
3345 ;; a(n) with poly a and integer n
3347 (defun gf-at-errchk (n fun)
3348 (unless (integerp n)
3349 (gf-merror (intl:gettext "`~m': Second argument must be an integer.") fun) ))
3351 (defmfun $gf_at (a n) ;; integer n
3352 (gf-char? "gf_at")
3353 (gf-at-errchk n "gf_at")
3354 (let ((*ef-arith?*))
3355 (gf-at (gf-p2x a) n) ))
3357 (defmfun $ef_at (a n) ;; poly a, integer n
3358 (ef-gf-field? "ef_at")
3359 (gf-at-errchk n "ef_at")
3360 (let ((*ef-arith?* t))
3361 (gf-at (gf-p2x a) n) ))
3363 (defun gf-at (x n) ;; Horner and square and multiply
3364 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3365 (if (or (null x) (integerp x)) x
3366 (maybe-char-is-fixnum-let ((n n))
3367 (do ((i 0) i1) (())
3368 (setq i (gf-cplus-b i (cadr x)))
3369 (when (null (cddr x))
3370 (setq i1 (gf-cpow n (the fixnum (car x))))
3371 (return (gf-ctimes i i1)) )
3372 (setq i1 (gf-cpow n (- (the fixnum (car x)) (the fixnum (caddr x))))
3373 i (gf-ctimes i i1)
3374 x (cddr x) )))))
3376 ;; find a primitive element:
3378 (defmfun $gf_primitive ()
3379 (gf-data? "gf_primitive")
3380 (let ((*ef-arith?*))
3381 (cond
3382 ((null *gf-prim*) nil)
3383 ((equal *gf-prim* '$unknown)
3384 (setq *gf-prim* (gf-prim))
3385 (unless (null *gf-prim*) (gf-x2p *gf-prim*)) )
3386 (t (gf-x2p *gf-prim*)) )))
3388 (defmfun $ef_primitive ()
3389 (ef-data? "ef_primitive")
3390 (let ((*ef-arith?* t))
3391 (cond
3392 ((null *ef-prim*) nil)
3393 ((equal *ef-prim* '$unknown)
3394 (cond
3395 ((= 1 *ef-exp*)
3396 (setq *ef-prim* (let ((*ef-arith?*)) (gf-x2n *gf-prim*))) )
3398 (setq *ef-prim* (ef-prim))
3399 (unless (null *ef-prim*) (gf-x2p *ef-prim*)) )))
3400 (t (gf-x2p *ef-prim*)) )))
3403 (defun gf-prim ()
3404 (*f-prim *gf-char* *gf-exp* #'gf-prim-p) )
3406 (defun ef-prim ()
3407 (*f-prim *gf-card* *ef-exp* #'ef-prim-p) )
3409 (defun *f-prim (inc e prim-p-fn)
3410 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3411 (setq inc (min $gf_coeff_limit inc))
3412 (do ((n inc (1+ n))
3413 (n-lim (expt inc e))
3415 ((>= n n-lim)
3416 (when (= $gf_coeff_limit inc) '$unknown) )
3417 (setq x (let ((*gf-char* inc)) (gf-n2x n)))
3418 (cond
3419 ((= 2 (cadr x))
3420 (setq n (1- (* (ash n -1) inc))) ) ;; go to next monic poly
3421 ((funcall prim-p-fn x)
3422 (return x) ) )))
3425 ;; precomputation for *f-prim-p:
3427 (defun gf-precomp ()
3428 (*f-precomp (1- *gf-char*) *gf-ord* *gf-fs-ord*) )
3430 (defun ef-precomp ()
3431 (*f-precomp (1- *gf-card*) *ef-ord* *ef-fs-ord*) )
3433 (defun *f-precomp (q-1 ord fs-ord)
3434 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3435 (let (fs-q-1
3436 fs-list
3437 ($intfaclim) )
3438 (setq fs-q-1
3439 (sort (get-factor-list q-1) #'< :key #'car) ) ;; .. [pi, ei] ..
3440 (dolist (fj fs-q-1)
3441 (setq fs-ord (remove-if #'(lambda (sj) (= (car fj) (car sj))) fs-ord :count 1)) )
3442 (setq fs-q-1
3443 (mapcar #'(lambda (pe) (list (car pe) t (truncate q-1 (car pe)))) fs-q-1) );; .. [pi, true, (p-1)/pi] ..
3444 (setq fs-ord
3445 (mapcar #'(lambda (pe) (list (car pe) nil)) fs-ord) ) ;; .. [pi, false] ..
3446 (setq fs-list
3447 (merge 'list fs-q-1 fs-ord #'(lambda (a b) (< (car a) (car b)))) )
3448 (cond
3449 (*ef-arith?*
3450 (setq *ef-fsx* (apply #'vector fs-list))
3451 (setq *ef-fsx-base-q*
3452 (apply #'vector
3453 (mapcar #'(lambda (pe) (nreverse (gf-n2l (truncate ord (car pe))))) ;; qi = ord/pi = sum(aij*p^j, j,0,m)
3454 fs-list) ))
3455 (setq *ef-x^q-powers* (gf-x^p-powers *gf-card* *ef-exp* *ef-red*)) ) ;; x^p^j
3457 (setq *gf-fsx* (apply #'vector fs-list))
3458 (setq *gf-fsx-base-p*
3459 (apply #'vector
3460 (mapcar #'(lambda (pe) (nreverse (gf-n2l (truncate ord (car pe))))) ;; qi = ord/pi = sum(aij*p^j, j,0,m)
3461 fs-list) ))
3462 (setq *gf-x^p-powers* (gf-x^p-powers *gf-char* *gf-exp* *gf-red*)) )))) ;; x^p^j
3464 ;; returns an array of polynomials x^p^j, j = 0, 1, .. , (n-1), where n = *gf-exp*
3466 (defun gf-x^p-powers (q n red)
3467 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3468 (declare (integer q) (fixnum n))
3469 (let ((a (make-array n :element-type 'list :initial-element nil)) )
3470 (setf (svref a 0) (list 1 1)) ;; x
3471 (do ((j 1 (1+ j)))
3472 ((= j n) a)
3473 (declare (fixnum j))
3474 (setf (svref a j) (gf-pow (svref a (1- j)) q red)) )))
3476 ;; -----------------------------------------------------------------------------
3479 ;; Primitive polynomials -------------------------------------------------------
3482 ;; test if a is a primitive polynomial over Fq
3484 (defmfun $gf_primitive_poly_p (a &optional p)
3485 (cond
3486 (p (unless (and (integerp p) (primep p))
3487 (gf-merror (intl:gettext "`gf_primitive_poly_p': ~m is not a prime number.") p) ))
3488 (t (gf-char? "gf_primitive_poly_p")
3489 (setq p *gf-char*) ))
3490 (let* ((*ef-arith?*)
3491 (*gf-char* p)
3492 (y (gf-p2x a)) ;; gf-p2x depends on *gf-char*
3493 (n (car y)) )
3494 (gf-primpoly-p y p n) ))
3496 (defmfun $ef_primitive_poly_p (y)
3497 (ef-gf-field? "ef_primitive_poly_p")
3498 (let ((*ef-arith?* t))
3499 (setq y (gf-p2x y))
3500 (gf-primpoly-p y *gf-card* (car y)) ))
3503 ;; based on
3504 ;; TOM HANSEN AND GARY L. MULLEN
3505 ;; PRIMITIVE POLYNOMIALS OVER FINITE FIELDS
3506 ;; (gf-primpoly-p performs a full irreducibility check
3507 ;; and therefore doesn't check whether x^((q^n-1)/(q-1)) = (-1)^n * y(0) mod y)
3509 (defun gf-primpoly-p (y q n)
3510 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3511 (declare (fixnum n))
3512 (unless (= 1 (cadr y)) ;; monic poly assumed
3513 (return-from gf-primpoly-p) )
3514 (prog* ((fs-q (cfactorw q))
3515 (*gf-char* (car fs-q))
3516 (*gf-exp* (if *ef-arith?* (cadr fs-q) n))
3517 (q-1 (1- q)) fs-q-1
3518 (const (last y 2))
3519 ($intfaclim) )
3520 ;; the constant part ...
3521 (unless (= 0 (car const)) (return nil))
3522 (setq const (cadr const))
3523 (when (oddp n) (setq const (gf-cminus-b const))) ;; (-1)^n*const
3524 ;; ... must be primitive in Fq:
3525 (unless (if (and *ef-arith?* (> *gf-exp* 1))
3526 (let ((*ef-arith?*)) (gf-prim-p (gf-n2x const)))
3527 (progn
3528 (setq fs-q-1 (sort (mapcar #'car (get-factor-list q-1)) #'<))
3529 (zn-primroot-p const q q-1 fs-q-1) ))
3530 (return nil) )
3531 ;; the linear case:
3532 (when (= n 1) (return t))
3533 ;; y must be irreducible:
3534 (unless (gf-irr-p y q n) (return nil))
3535 ;; check for all prime factors fi of r = (q^n-1)/(q-1) which do not divide q-1
3536 ;; that x^(r/fi) mod y is not an integer:
3537 (let (x^q-powers r fs-r fs-r-base-q)
3538 ;; pre-computation:
3539 (setq x^q-powers (gf-x^p-powers q n y)
3540 r (truncate (1- (expt q n)) q-1)
3541 fs-r (sort (mapcar #'car (get-factor-list r)) #'<) )
3542 (unless fs-q-1
3543 (setq fs-q-1 (sort (mapcar #'car (get-factor-list q-1)) #'<)) )
3544 (dolist (fj fs-q-1)
3545 (setq fs-r (delete-if #'(lambda (sj) (= fj sj)) fs-r :count 1)) )
3546 (setq fs-r-base-q
3547 (let ((*gf-char* q))
3548 (apply #'vector
3549 (mapcar #'(lambda (f) (nreverse (gf-n2l (truncate r f)))) fs-r ) )))
3550 ;; check:
3551 (return (gf-primpoly-p-exit y fs-r-base-q x^q-powers)) )))
3553 ;; uses exponentiation by pre-computation
3554 (defun gf-primpoly-p-exit (y fs-r-base-q x^q-powers)
3555 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3556 (do ((i 0 (1+ i)) (j 0 0) (dim (array-dimension fs-r-base-q 0)) z zz)
3557 ((= i dim) t)
3558 (declare (fixnum i j dim))
3559 (setq zz (list 0 1))
3560 (dolist (aij (svref fs-r-base-q i)) ;; fi = sum(aij*q^j, j,0,n-1)
3561 (setq z (gf-pow (svref x^q-powers j) aij y)
3562 zz (gf-times zz z y)
3563 j (1+ j) ))
3564 (when (= 0 (car zz)) (return nil)) ))
3567 ;; find a primitive polynomial
3569 (defmfun $gf_primitive_poly (p n)
3570 (unless (and (integerp p) (primep p) (integerp n))
3571 (gf-merror (intl:gettext "`gf_primitive_poly' needs a prime number and an integer.")) )
3572 (gf-set-rat-header)
3573 (let ((*ef-arith?*) (*gf-char* p)) ;; gf-x2p needs *gf-char*
3574 (gf-x2p (gf-primpoly p n)) ))
3576 (defmfun $ef_primitive_poly (n)
3577 (ef-gf-field? "ef_primitive_poly")
3578 (let ((*ef-arith?* t))
3579 (gf-x2p (gf-primpoly *gf-card* n)) ))
3582 (defun gf-primpoly (q n)
3583 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3584 (declare (fixnum n))
3585 (let* ((fs-q (cfactorw q))
3586 (*gf-char* (car fs-q))
3587 (*gf-exp* (if *ef-arith?* (cadr fs-q) n))
3588 (q-1 (1- q))
3589 ($intfaclim)
3590 (fs-q-1 (sort (mapcar #'car (get-factor-list q-1)) #'<))
3591 r fs-r fs-r-base-q )
3592 ;; the linear case:
3593 (when (= 1 n)
3594 (let ((prt (if (= q 2) 1 (zn-primroot q q-1 fs-q-1))))
3595 (return-from gf-primpoly
3596 (list 1 1 0 (gf-cminus-b prt)) )))
3597 ;; pre-computation part 1:
3598 (setq r (truncate (1- (expt q n)) q-1)
3599 fs-r (sort (mapcar #'car (get-factor-list r)) #'<) )
3600 (dolist (fj fs-q-1)
3601 (setq fs-r (delete-if #'(lambda (sj) (= fj sj)) fs-r :count 1)) )
3602 (setq fs-r-base-q
3603 (let ((*gf-char* q))
3604 (apply #'vector
3605 (mapcar #'(lambda (f) (nreverse (gf-n2l (truncate r f)))) fs-r ) )))
3606 ;; search:
3607 (let* ((inc (min $gf_coeff_limit q))
3608 (i-lim (expt inc n))
3610 (do ((i (1+ inc) (1+ i)))
3611 ((>= i i-lim)
3612 (gf-merror (intl:gettext "No primitive polynomial found.~%~\
3613 `gf_coeff_limit' might be too small.~%" )) )
3614 (setq x (let ((*gf-char* inc)) (gf-n2x i))
3615 x (cons n (cons 1 x)) )
3616 (when (gf-primpoly-p2 x *gf-char* *gf-exp* q n fs-q-1 fs-r-base-q)
3617 (return x) )))))
3619 (defun gf-primpoly-p2 (y p e q n fs-q-1 fs-r-base-q)
3620 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3621 (declare (fixnum e n))
3622 (when (= 1 (cadr y)) ;; monic poly assumed
3623 (prog* ((*gf-char* p) (*gf-exp* e) (q-1 (1- q))
3624 (const (last y 2)) )
3625 ;; the constant part ...
3626 (unless (= 0 (car const)) (return nil))
3627 (setq const (cadr const))
3628 (when (oddp n) (setq const (gf-cminus-b const))) ;; (-1)^n*const
3629 ;; ... must be primitive in Fq:
3630 (unless (if (and *ef-arith?* (> *gf-exp* 1))
3631 (let ((*ef-arith?*)) (gf-prim-p (gf-n2x const)))
3632 (zn-primroot-p const q q-1 fs-q-1) )
3633 (return nil) )
3634 ;; y must be irreducible:
3635 (unless (gf-irr-p y q n) (return nil))
3636 ;; y dependend pre-computation and final check:
3637 (return (gf-primpoly-p-exit y fs-r-base-q (gf-x^p-powers q n y))) )))
3639 ;; -----------------------------------------------------------------------------
3642 ;; random elements -------------------------------------------------------------
3645 ;; Produces a random element within the given environment
3647 (defmfun $gf_random (&optional p n)
3648 (let ((*ef-arith?* t))
3649 (cond
3650 (n (let ((*gf-char* p))
3651 (unless *gf-red?* (gf-set-rat-header))
3652 (gf-x2p (gf-random p n)) ))
3653 (t (gf-data? "gf_random")
3654 (gf-x2p (gf-random *gf-char* *gf-exp*)) ))))
3656 (defmfun $ef_random (&optional q n)
3657 (let ((*ef-arith?* t))
3658 (cond
3659 (n (let ((*gf-char* q)) (gf-x2p (gf-random q n))))
3660 (t (ef-data? "ef_random")
3661 (gf-x2p (gf-random *gf-card* *ef-exp*)) ))))
3663 (defun gf-random (q n)
3664 (do ((e 0 (1+ e)) c x)
3665 ((= e n) x)
3666 (setq c (random q))
3667 (when (/= 0 c)
3668 (setq x (cons e (cons c x))) )))
3670 ;; -----------------------------------------------------------------------------
3673 ;; factoring -------------------------------------------------------------------
3676 (defmfun $gf_factor (a &optional p) ;; set p to switch to another modulus
3677 (cond
3678 (p (unless (and (integerp p) (primep p))
3679 (gf-merror (intl:gettext "`gf_factor': Second argument must be a prime number.")) )
3680 (gf-set-rat-header) )
3681 (t (gf-char? "gf_factor")
3682 (setq p *gf-char*) ))
3683 (let* ((*gf-char* p)
3684 (modulus p) (a ($rat a))
3685 (*ef-arith?*) )
3686 (when (> (length (caddar a)) 1)
3687 (gf-merror (intl:gettext "`gf_factor': Polynomial must be univariate.")) )
3688 (setq a (cadr a))
3689 (cond
3690 ((integerp a) (mod a *gf-char*))
3691 (t (setq a
3692 (if $gf_cantor_zassenhaus
3693 (gf-factor (gf-mod (cdr a)) p)
3694 (gf-ns2pmod-factors (pfactor a)) ))
3695 (setq a (gf-disrep-factors a))
3696 (and (consp a) (consp (car a)) (equal (caar a) 'mtimes)
3697 (setq a (simplifya (cons '(mtimes) (cdr a)) nil)) )
3698 a ))))
3700 ;; adjust results from rat3d/pfactor to a positive modulus if $gf_balanced = false
3701 (defun gf-ns2pmod-factors (fs) ;; modifies fs
3702 (if $gf_balanced fs
3703 (maybe-char-is-fixnum-let ((m *gf-char*))
3704 (do ((r fs (cddr r)))
3705 ((null r) fs)
3706 (if (integerp (car r))
3707 (when (< (the integer (car r)) 0)
3708 (incf (car r) m) ) ;; only in the case *ef-arith?* = false
3709 (rplaca r (gf-ns2pmod-factor (cdar r) m)) )))))
3711 (defun gf-ns2pmod-factor (fac m)
3712 (do ((r (cdr fac) (cddr r))) (())
3713 (when (< (the integer (car r)) 0)
3714 (incf (car r) m) )
3715 (when (null (cdr r)) (return fac)) ))
3717 (defun gf-disrep-factors (fs)
3718 (cond
3719 ((integerp fs) (gf-cp2smod fs))
3721 (setq fs (nreverse fs))
3722 (do ((e 0) fac p)
3723 ((null fs) (cons '(mtimes simp factored) p))
3724 (declare (fixnum e))
3725 (setq e (the fixnum (car fs))
3726 fac (cadr fs)
3727 fs (cddr fs)
3728 p (cond
3729 ((integerp fac) (cons (gf-cp2smod fac) p))
3730 ((= 1 e) (cons (gf-disrep (gf-np2smod fac)) p))
3731 (t (cons `((mexpt simp) ,(gf-disrep (gf-np2smod fac)) ,e) p)) ))))))
3733 (defmfun $ef_factor (a)
3734 (ef-gf-field? "ef_factor")
3735 (let ((*ef-arith?* t))
3736 (setq a (let ((modulus)) ($rat a)))
3737 (when (> (length (caddar a)) 1)
3738 (gf-merror (intl:gettext "`ef_factor': Polynomial must be univariate.")) )
3739 (setq a (cadr a))
3740 (cond
3741 ((integerp a) (ef-cmod a))
3742 (t (setq a
3743 (gf-disrep-factors
3744 (gf-factor (gf-mod (cdr a)) *gf-card*) ))
3745 (and (consp a) (consp (car a)) (equal (caar a) 'mtimes)
3746 (setq a (simplifya (cons '(mtimes) (cdr a)) nil)) )
3747 a ))))
3749 (defun gf-factor (x q) ;; non-integer x
3750 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3751 (let ((lc (cadr x)) z)
3752 (unless (= 1 lc)
3753 (setq x (gf-xctimes x (gf-cinv lc))) ) ;; monicize x
3754 (if (gf-irr-p x q (car x))
3755 (setq z (list x 1))
3756 (let ((sqfr (gf-square-free x)) e y)
3757 (dolist (v sqfr)
3758 (setq e (car v)
3759 y (cadr v)
3760 y (gf-distinct-degree-factors y q) )
3761 (dolist (w y)
3762 (setq z (nconc (gf-equal-degree-factors w q e) z)) ))))
3763 (if (= 1 lc) z (cons lc (cons 1 z))) ))
3765 (defun gf-diff (x)
3766 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3767 (if (null x) nil
3768 (maybe-char-is-fixnum-let ((m *gf-char*))
3769 (do ((rx x (cddr rx)) res c)
3770 ((or (null rx) (= 0 (car rx))) (nreverse res))
3771 (setq c (gf-ctimes (mod (the fixnum (car rx)) m) (cadr rx)))
3772 (when (/= 0 c)
3773 (push (1- (car rx)) res)
3774 (push c res) )))) )
3776 ;; c -> c^p^(n-1)
3777 (defun ef-pth-croot (c)
3778 (let ((p *gf-char*) (*ef-arith?* t))
3779 (dotimes (i (1- *gf-exp*) c)
3780 (setq c (gf-cpow c p)) )))
3782 (defun gf-pth-root (x)
3783 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3784 (maybe-char-is-fixnum-let ((p *gf-char*))
3785 (if (null x) nil
3786 (do ((rx x (cddr rx)) res c)
3787 ((null rx) (nreverse res))
3788 (push (truncate (the fixnum (car rx)) p) res)
3789 (setq c (cadr rx))
3790 (when *ef-arith?* ;; p # q
3791 (setq c (ef-pth-croot c)) )
3792 (push c res) ))))
3794 (defun gf-gcd-cofactors (x dx)
3795 (let ((g (gf-gcd x dx)))
3796 (values g (gf-divide x g) (gf-divide dx g)) ))
3798 (defun gf-square-free (x) ;; monic x
3799 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3800 (let (f fs (r (gf-diff x)) g)
3801 (cond
3802 ((equal '(0 1) (setq g (gf-gcd x r))) `((1 ,x)))
3804 (when r ;; # 0
3805 (setq r (gf-divide x g)
3806 x g ) ;; d.h. x # 1
3807 (do ((m 1 (1+ m)))
3808 ((equal '(0 1) r))
3809 (declare (fixnum m))
3810 (multiple-value-setq (r f x) (gf-gcd-cofactors r x))
3811 (unless (equal '(0 1) f)
3812 (push (list m f) fs) )))
3813 (unless (equal '(0 1) x)
3814 (setq fs
3815 (append (mapcar #'(lambda (v) (rplaca v (* (car v) *gf-char*)))
3816 (gf-square-free (gf-pth-root x)) )
3817 fs )))
3818 (nreverse fs) ))))
3820 (defun gf-distinct-degree-factors (x q)
3821 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3822 (let ((w '(1 1)) f fs (*gf-char* (car (cfactorw q))) )
3823 (do ((n 1 (1+ n)))
3824 ((equal '(0 1) x) fs)
3825 (declare (fixnum n))
3826 (when (> (ash n 1) (car x))
3827 (setq fs (cons (list x (car x)) fs))
3828 (return) )
3829 (setq w (gf-nred w x)
3830 w (gf-pow w q x)
3831 f (gf-gcd (gf-plus w (gf-nminus (list 1 1))) x) )
3832 (unless (equal '(0 1) f)
3833 (setq fs (cons (list f n) fs)
3834 x (gf-divide x f) )))
3835 (nreverse fs) ))
3837 (defun gf-nonconst-random (q q^n)
3838 (do (r) (())
3839 (setq r (random q^n))
3840 (when (>= r q) (return (let ((*gf-char* q)) (gf-n2x r)))) ))
3842 ;; computes Tm(x) = x^2^(m-1) + x^2^(m-2) + .. + x^4 + x^2 + x in F2[x]
3844 (defun gf-trace-poly-f2 (x m red) ;; m > 0
3845 (let ((tm (gf-nred x red)))
3846 (do ((i 1 (1+ i)))
3847 ((= i m) tm)
3848 (declare (fixnum i))
3849 (setq x (gf-sq x red)
3850 tm (gf-plus tm x) ))))
3852 ;; Cantor and Zassenhaus' algorithm
3854 (defun gf-equal-degree-factors (x-and-d q mult)
3855 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3856 (let* ((x (car x-and-d)) (d (cadr x-and-d))
3857 (n (car x)) )
3858 (declare (fixnum d n))
3859 (cond
3860 ((= n d) (list x mult))
3862 (let* ((p^k (cfactorw q)) (p (car p^k)) (k (cadr p^k)) (*gf-char* p)
3863 (f '(0 1)) (q^n (expt q n)) m e r r^e )
3864 (if (= 2 p)
3865 (setq m (* k d)) ;; q = 2^k
3866 (setq e (ash (1- (expt q d)) -1)) )
3868 (do () ((and (not (equal '(0 1) f)) (not (equal x f))))
3869 (setq r (gf-nonconst-random q q^n)
3870 f (gf-gcd x r) )
3871 (when (equal '(0 1) f)
3872 (setq r^e
3873 (if (= 2 p) (gf-trace-poly-f2 r m x) ;; q = 2^k
3874 (gf-pow r e x) )) ;; q is odd prime power
3875 (setq f (gf-gcd x (gf-nplus r^e (gf-nminus (list 0 1))))) ))
3877 (append (gf-equal-degree-factors (list (gf-divide x f) d) q mult)
3878 (gf-equal-degree-factors (list f d) q mult) ))))))
3880 ;; -----------------------------------------------------------------------------
3883 ;; gcd, gcdex and test of invertibility ----------------------------------------
3886 (defmfun $ef_gcd (a b)
3887 (ef-gf-field? "ef_gcd")
3888 (let ((*ef-arith?* t))
3889 (gf-x2p (gf-gcd (gf-p2x a) (gf-p2x b))) ))
3891 (defmfun $gf_gcd (a b &optional p)
3892 (let ((*ef-arith?*))
3893 (cond
3894 (p (unless (and (integerp p) (primep p))
3895 (gf-merror (intl:gettext "`gf_gcd': ~m is not a prime number.") p) )
3896 (gf-set-rat-header)
3897 (let* ((*gf-char* p)
3898 (modulus p)
3899 (vars (caddar ($rat a))) )
3900 (when (> (length vars) 1)
3901 (gf-merror (intl:gettext "`gf_gcd': Polynomials must be univariate.")) )
3902 (gf-x2p (gf-gcd (gf-p2x a) (gf-p2x b))) ))
3903 (t (gf-char? "gf_gcd")
3904 (gf-x2p (gf-gcd (gf-p2x a) (gf-p2x b))) ))))
3907 (defmfun $gf_gcdex (a b)
3908 (gf-red? "gf_gcdex")
3909 (let ((*ef-arith?*))
3910 (cons '(mlist simp)
3911 (mapcar #'gf-x2p (gf-gcdex (gf-p2x a) (gf-p2x b) *gf-red*)) )))
3913 (defmfun $ef_gcdex (a b)
3914 (ef-red? "ef_gcdex")
3915 (let ((*ef-arith?* t))
3916 (cons '(mlist simp)
3917 (mapcar #'gf-x2p (gf-gcdex (gf-p2x a) (gf-p2x b) *gf-red*)) )))
3920 (defmfun $gf_unit_p (a)
3921 (gf-red? "gf_unit_p")
3922 (let ((*ef-arith?*))
3923 (gf-unit-p (gf-p2x a) *gf-red*) ))
3925 (defmfun $ef_unit_p (a)
3926 (ef-red? "ef_unit_p")
3927 (let ((*ef-arith?* t))
3928 (gf-unit-p (gf-p2x a) *ef-red*) ))
3930 (defun gf-unit-p (x red)
3931 (= 0 (car (gf-gcd x red))) )
3933 ;; -----------------------------------------------------------------------------
3936 ;; order -----------------------------------------------------------------------
3939 ;; group/element order
3941 (defmfun $gf_order (&optional a)
3942 (gf-data? "gf_order")
3943 (cond
3944 (a (let ((*ef-arith?*))
3945 (setq a (gf-p2x a))
3946 (when (and a (or *gf-irred?* (gf-unit-p a *gf-red*)))
3947 (gf-ord a *gf-ord* *gf-fs-ord* *gf-red*) )))
3948 (t *gf-ord*) ))
3950 (defmfun $ef_order (&optional a)
3951 (ef-data? "ef_order")
3952 (cond
3953 (a (let ((*ef-arith?* t))
3954 (setq a (gf-p2x a))
3955 (when (and a (or *ef-irred?* (gf-unit-p a *ef-red*)))
3956 (gf-ord a *ef-ord* *ef-fs-ord* *ef-red*) )))
3957 (t *ef-ord*) ))
3959 ;; find the lowest value k for which a^k = 1
3961 (defun gf-ord (x ord fs-ord red) ;; assume x # 0
3962 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3963 (let (p (e 0) z)
3964 (declare (fixnum e))
3965 (dolist (pe fs-ord ord)
3966 (setq p (car pe)
3967 e (the fixnum (cadr pe))
3968 ord (truncate ord (expt p e))
3969 z (gf-pow$ x ord red) ) ;; use exponentiation by precomputation
3970 (do ()
3971 ((equal z '(0 1)))
3972 (setq ord (* ord p))
3973 (when (= e 1) (return))
3974 (decf e)
3975 (setq z (gf-pow$ z p red)) ))))
3977 (defun gf-ord-by-table (x)
3978 (let ((index (svref $gf_logs (gf-x2n x))))
3979 (truncate *gf-ord* (gcd *gf-ord* index)) ))
3982 ;; Fq^n = F[x]/(f) is no field <=> f splits into factors
3984 ;; f = f1^e1 * ... * fk^ek where fi are irreducible of degree ni.
3986 ;; We compute the order of the group (F[x]/(fi^ei))* by
3988 ;; ((q^ni)^ei - (q^ni)^(ei-1)) = ((q^ni) - 1) * (q^ni)^(ei-1)
3990 ;; and ord((Fq^n)*) with help of the Chinese Remainder Theorem.
3992 (defun gf-group-order (q red)
3993 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
3994 (let (ne-list q^n (e 0) (ord 1))
3995 (declare (fixnum e))
3996 (do ((x (gf-factor red q))) ;; red is assumed to be a monic poly
3997 ((null x))
3998 (push (list (caar x) (cadr x)) ne-list) ;; (list ni ei), f = prod(fi^ei) with fi of degree ni
3999 (setq x (cddr x)) )
4000 (dolist (a ne-list)
4001 (setq q^n (expt q (the fixnum (car a)))
4002 e (the fixnum (cadr a))
4003 ord (* ord (1- q^n) (expt q^n (the fixnum (1- e)))) ))
4004 ord ))
4006 ;; -----------------------------------------------------------------------------
4009 ;; degree, minimal polynomial, trace and norm ----------------------------------
4013 ;; degree: Find the lowest value d for which x^(q^d) = x
4015 (defun gf-degree-errchk (a n fun)
4016 (when (and (not (null a)) (>= (car a) n))
4017 (gf-merror (intl:gettext "`~m': Leading exponent must be smaller than ~m.") fun n) ))
4019 (defmfun $gf_degree (a)
4020 (gf-field? "gf_degree")
4021 (let ((*ef-arith?*))
4022 (setq a (gf-p2x a))
4023 (gf-degree-errchk a *gf-exp* "gf_degree")
4024 (*f-deg a *gf-exp* *gf-red* *gf-x^p-powers*) ))
4026 (defmfun $ef_degree (a)
4027 (ef-field? "ef_degree")
4028 (let ((*ef-arith?* t))
4029 (setq a (gf-p2x a))
4030 (gf-degree-errchk a *ef-exp* "ef_degree")
4031 (*f-deg a *ef-exp* *ef-red* *ef-x^q-powers*) ))
4033 (defun *f-deg (x n red x^q-powers)
4034 (do ((d 1 (1+ d)))
4035 ((= d n) d)
4036 (declare (fixnum d))
4037 (when (equal x (gf-compose (svref x^q-powers d) x red)) ;; f(x)^q = f(x^q)
4038 (return d) ) ))
4041 ;; produce the minimal polynomial
4043 (defmfun $gf_minimal_poly (a)
4044 (gf-field? "gf_minimal_poly")
4045 (let ((*ef-arith?*))
4046 (setq a (gf-p2x a))
4047 (gf-degree-errchk a *gf-exp* "gf_minimal_poly")
4048 (gf-minpoly a *gf-red* *gf-x^p-powers*) ))
4050 (defmfun $ef_minimal_poly (a)
4051 (ef-field? "ef_minimal_poly")
4052 (let ((*ef-arith?* t))
4053 (setq a (gf-p2x a))
4054 (gf-degree-errchk a *ef-exp* "ef_minimal_poly")
4055 (gf-minpoly a *ef-red* *ef-x^q-powers*) ))
4057 ;; 2 (d-1)
4058 ;; q q q
4059 ;; f(z) = (z - x) (z - x ) (z - x ) ... (z - x ) , where d = degree(x)
4061 (defun gf-minpoly (x red x^q-powers)
4062 (if (null x) '$z
4063 (let ((n (car red))
4064 (powers (list (gf-minus x)))
4065 (prod (list 0 (list 0 1)))
4066 xqi zx cx ) (declare (fixnum n))
4067 (do ((i 1 (1+ i)))
4068 ((= i n)) (declare (fixnum i))
4069 (setq xqi (gf-compose (svref x^q-powers i) x red))
4070 (when (equal x xqi) (return))
4071 (push (gf-nminus xqi) powers) )
4072 (dolist (pow powers)
4073 (setq zx (gf-zx prod)
4074 cx (gf-ncx pow prod red)
4075 prod (gf-nzx+cx zx cx)) )
4076 ($substitute '$z '$x (gf-x2p (gf-nxx2x prod))) )))
4078 (defun gf-zx (x) ;; (gf-zx '(3 (5 1 3 1) 2 (4 1))) -> (4 (5 1 3 1) 3 (4 1))
4079 ;; 3 5 3 2 4 4 5 3 3 4
4080 ;; z (x + x ) + z x -> z (x + x ) + z x
4081 (do* ((res (list (1+ (car x)) (cadr x)))
4082 (r (cdr res) (cddr r))
4083 (rx (cddr x) (cddr rx)) )
4084 ((null rx) res)
4085 (rplacd r (list (1+ (car rx)) (cadr rx))) ))
4087 (defun gf-ncx (c x red) ;; modifies x
4088 ;; (gf-ncx '(1 1) '(3 (4 1 3 1) 2 (2 1)) '(6 1))
4089 ;; -> (3 (5 1 4 1) 2 (3 1))
4090 (if (null c) c
4091 (do ((r (cdr x) (cddr r)))
4092 ((null r) x)
4093 (rplaca r (gf-times c (car r) red)) )))
4095 (defun gf-nzx+cx (zx cx) ;; modifies zx
4096 (do ((r (cdr zx))
4097 (s (cdr cx) (cddr s)) r+s )
4098 ((null (cdr r)) (nconc zx (list 0 (car s))))
4099 (setq r+s (gf-plus (caddr r) (car s)))
4100 (if r+s
4101 (rplaca (setq r (cddr r)) r+s)
4102 (rplacd r (cdddr r)) )))
4104 (defun gf-nxx2x (xx) ;; modifies xx
4105 ;; (gf-nxx2x '(4 (0 3) 2 (0 1))) -> (4 3 2 1)
4106 (do ((r (cdr xx) (cddr r)))
4107 ((null r) xx)
4108 (rplaca r (cadar r)) ))
4111 ;; 2 (n-1)
4112 ;; q q q
4113 ;; trace(a) = a + a + a + .. + a
4115 (defmfun $gf_trace (a)
4116 (gf-field? "gf_trace")
4117 (let ((*ef-arith?*))
4118 (gf-trace (gf-p2x a) *gf-red* *gf-x^p-powers*) ))
4120 (defmfun $ef_trace (a)
4121 (ef-field? "ef_trace")
4122 (let ((*ef-arith?* t))
4123 (gf-trace (gf-p2x a) *ef-red* *ef-x^q-powers*) ))
4125 (defun gf-trace (x red x^q-powers)
4126 (let ((n (car red))
4127 (su x) )
4128 (do ((i 1 (1+ i)))
4129 ((= i n) (gf-x2p su)) (declare (fixnum i))
4130 (setq su (gf-plus su (gf-compose (svref x^q-powers i) x red))) )))
4133 ;; 2 (n-1) n 2 (n-1)
4134 ;; 1 + q + q + .. + q (q - 1)/(q - 1) q q q
4135 ;; norm(a) = a = a = a * a * a * .. * a
4137 (defmfun $gf_norm (a)
4138 (gf-field? "gf_norm")
4139 (let ((*ef-arith?*))
4140 (gf-norm (gf-p2x a) *gf-red* *gf-x^p-powers*) ))
4142 (defmfun $ef_norm (a)
4143 (ef-field? "ef_norm")
4144 (let ((*ef-arith?* t))
4145 (gf-norm (gf-p2x a) *ef-red* *ef-x^q-powers*) ))
4147 (defun gf-norm (x red x^q-powers)
4148 (let ((n (car red))
4149 (prod x) )
4150 (do ((i 1 (1+ i)))
4151 ((= i n) (gf-x2p prod)) (declare (fixnum i))
4152 (setq prod (gf-times prod (gf-compose (svref x^q-powers i) x red) red)) )))
4154 ;; -----------------------------------------------------------------------------
4157 ;; normal elements and normal basis --------------------------------------------
4160 ;; Tests if an element is normal
4162 (defmfun $gf_normal_p (a)
4163 (gf-field? "gf_normal_p")
4164 (let ((*ef-arith?*)) (gf-normal-p (gf-p2x a))) )
4166 (defun gf-normal-p (x)
4167 (unless (null x)
4168 (let ((modulus *gf-char*)
4169 (mat (gf-maybe-normal-basis x)) )
4170 (equal ($rank mat) *gf-exp*) )))
4172 (defmfun $ef_normal_p (a)
4173 (ef-field? "ef_normal_p")
4174 (let ((*ef-arith?* t)) (ef-normal-p (gf-p2x a))) )
4176 (defun ef-normal-p (x)
4177 (unless (null x)
4178 (let ((mat (ef-maybe-normal-basis x)) )
4179 (/= 0 ($ef_determinant mat)) )))
4182 ;; Finds a normal element e in the field; that is,
4183 ;; an element for which the list [e, e^q, e^(q^2), ... , e^(q^(n-1))] is a basis
4185 (defmfun $gf_normal ()
4186 (gf-field? "gf_normal")
4187 (let ((*ef-arith?*))
4188 (gf-x2p (gf-normal *gf-char* *gf-exp* #'gf-normal-p)) ))
4190 (defmfun $ef_normal ()
4191 (ef-field? "ef_normal")
4192 (let ((*ef-arith?* t))
4193 (gf-x2p (gf-normal *gf-card* *ef-exp* #'ef-normal-p)) ))
4195 (defun gf-normal (q n normal-p-fn)
4196 (let* ((inc (min $gf_coeff_limit q))
4197 (i-lim (expt inc n))
4199 (do ((i inc (1+ i)))
4200 ((>= i i-lim)
4201 (gf-merror (intl:gettext "No normal element found.~%~\
4202 `gf_coeff_limit' might be too small.~%" )) )
4203 (setq x (let ((*gf-char* inc)) (gf-n2x i)))
4204 (when (funcall normal-p-fn x ) (return-from gf-normal x)) )))
4207 ;; Finds a normal element in the field by producing random elements and checking
4208 ;; if each one is normal
4210 (defmfun $gf_random_normal ()
4211 (gf-field? "gf_random_normal")
4212 (let ((*ef-arith?*)) (gf-x2p (gf-random-normal))) )
4214 (defun gf-random-normal ()
4215 (do ((x (gf-random *gf-char* *gf-exp*) (gf-random *gf-char* *gf-exp*)))
4216 ((gf-normal-p x) x) ))
4218 (defmfun $ef_random_normal ()
4219 (ef-field? "ef_random_normal")
4220 (let ((*ef-arith?* t)) (gf-x2p (ef-random-normal))) )
4222 (defun ef-random-normal ()
4223 (do ((x (gf-random *gf-card* *ef-exp*) (gf-random *gf-card* *ef-exp*)))
4224 ((ef-normal-p x) x) ))
4226 ;; Produces a normal basis as a matrix;
4227 ;; the columns are the coefficients of the powers e^(q^i) of the normal element
4229 (defmfun $gf_normal_basis (a)
4230 (gf-field? "gf_normal_basis")
4231 (let* ((*ef-arith?*)
4232 (x (gf-p2x a))
4233 (modulus *gf-char*)
4234 (mat (gf-maybe-normal-basis x)) )
4235 (unless (equal ($rank mat) *gf-exp*)
4236 (gf-merror (intl:gettext "Argument to `gf_normal_basis' must be a normal element.")) )
4237 mat ))
4239 (defmfun $ef_normal_basis (a)
4240 (ef-field? "ef_normal_basis")
4241 (let* ((*ef-arith?* t)
4242 (mat (ef-maybe-normal-basis (gf-p2x a))) )
4243 (unless (/= 0 ($ef_determinant mat))
4244 (merror (intl:gettext "Argument to `ef_normal_basis' must be a normal element." )) )
4245 mat ))
4247 (defun gf-maybe-normal-basis (x)
4248 (*f-maybe-normal-basis x *gf-x^p-powers* *gf-exp* *gf-red*) )
4250 (defun ef-maybe-normal-basis (x)
4251 (*f-maybe-normal-basis x *ef-x^q-powers* *ef-exp* *ef-red*) )
4253 (defun *f-maybe-normal-basis (x x^q-powers e red)
4254 #+ (or ccl ecl gcl sbcl) (declare (optimize (speed 3) (safety 0)))
4255 (declare (fixnum e))
4256 (let ((e-1 (- e 1))) (declare (fixnum e-1))
4257 ($transpose
4258 ($genmatrix
4259 #'(lambda (i j) (declare (fixnum i j))
4260 (svref (gf-x2array
4261 (gf-compose (svref x^q-powers (1- i)) x red) e-1 ) (1- j)))
4262 e e ))))
4264 ;; coefficients as an array of designated length
4266 (defun gf-x2array (x len)
4267 #+ (or ccl ecl gcl sbcl) (declare (optimize (speed 3) (safety 0)))
4268 (declare (fixnum len))
4269 (let ((cs (make-array (1+ len) :initial-element 0)))
4270 (do ((k len)) ((null x) cs) (declare (fixnum k))
4271 (cond
4272 ((> k (the fixnum (car x)))
4273 (decf k) )
4274 ((= k (the fixnum (car x)))
4275 (setf (svref cs (- len k)) (cadr x))
4276 (setq x (cddr x))
4277 (decf k) )
4279 (setq x (cddr x)) ) ))))
4281 ;; Produces the normal representation of an element as a list of coefficients
4282 ;; Needs the inverted normal basis as the second argument
4283 ;; (the inversion might be time consuming)
4285 (defmfun $gf_normal_basis_rep (a m-inv)
4286 (gf-field? "gf_normal_basis_rep")
4287 (let ((*ef-arith?*))
4288 (gf-normal-basis-rep (gf-p2x a) m-inv *gf-exp* '$gf_matmult) ))
4290 (defmfun $ef_normal_basis_rep (a m-inv)
4291 (ef-field? "ef_normal_basis_rep")
4292 (let ((*ef-arith?* t))
4293 (gf-normal-basis-rep (gf-p2x a) m-inv *ef-exp* '$ef_matmult) ))
4295 (defun gf-normal-basis-rep (x m-inv e matmult-fn)
4296 (let* ((cs (cons '(mlist simp) (gf-x2l x e)))
4297 (nbrep (mfuncall matmult-fn m-inv cs)) )
4298 (cons '(mlist simp) (mapcar #'cadr (cdr nbrep))) ))
4300 ;; -----------------------------------------------------------------------------
4303 ;; functions for matrices ------------------------------------------------------
4306 (defmfun $gf_matneg (m)
4307 (gf-char? "gf_matneg")
4308 (mfuncall '$matrixmap '$gf_neg m) )
4310 (defmfun $ef_matneg (m)
4311 (ef-gf-field? "ef_matneg")
4312 (mfuncall '$matrixmap '$ef_neg m) )
4315 ;; matrix addition (convenience: mat, list or poly possible as argument)
4317 (defmfun $gf_matadd (&rest args)
4318 (let ((*ef-arith?*))
4319 (reduce #'(lambda (m1 m2) (gf-matadd m1 m2 '$gf_add)) args) ))
4321 (defmfun $ef_matadd (&rest args)
4322 (gf-data? "ef_matadd")
4323 (let ((*ef-arith?* t))
4324 (reduce #'(lambda (m1 m2) (gf-matadd m1 m2 '$ef_add)) args) ))
4326 (defun gf-matadd (m1 m2 add-fn)
4327 (when ($listp m1) (setq m1 ($transpose m1)))
4328 (when ($listp m2) (setq m2 ($transpose m2)))
4329 (cond
4330 ((and ($matrixp m1) ($matrixp m2))
4331 (gf-matadd2 m1 m2 add-fn) )
4332 (($matrixp m1)
4333 (gf-matadd1 m1 m2 add-fn) ) ;; assumed without checking: m2 is poly
4334 (($matrixp m2)
4335 (gf-matadd1 m2 m1 add-fn) )
4337 (mfuncall add-fn m1 m2) ) ))
4339 (defun gf-matadd1 (m poly add-fn)
4340 (do ((r (cdr m) (cdr r)) new)
4341 ((null r) (cons '($matrix simp) (nreverse new)))
4342 (push (cons '(mlist simp)
4343 (mapcar #'(lambda (p) (mfuncall add-fn p poly)) (cdar r)) )
4344 new )))
4346 (defun gf-matadd2-error ()
4347 (gf-merror
4348 (intl:gettext "Arguments to `~m' must have same formal structure.")
4349 (if *ef-arith?* "ef_matadd" "gf_matadd") ))
4351 (defun gf-matadd2 (m1 m2 add-fn)
4352 (setq m1 (cdr m1) m2 (cdr m2))
4353 (unless (= (length (car m1)) (length (car m2)))
4354 (gf-matadd2-error) )
4355 (do ((r1 m1 (cdr r1)) (r2 m2 (cdr r2)) new)
4356 ((or (null r1) (null r2))
4357 (unless (and (null r1) (null r2))
4358 (gf-matadd2-error) )
4359 (cons '($matrix simp) (nreverse new)) )
4360 (push (cons '(mlist simp) (mapcar add-fn (cdar r1) (cdar r2))) new) ))
4363 ;; matrix multiplication (convenience: mat, list or poly possible as argument)
4365 (defmfun $gf_matmult (&rest args)
4366 (gf-red? "gf_matmult")
4367 (let ((*ef-arith?*))
4368 (apply #'*f-matmult `($gf_mult ,@args)) ))
4370 (defmfun $ef_matmult (&rest args)
4371 (ef-red? "ef_matmult")
4372 (let ((*ef-arith?* t))
4373 (apply #'*f-matmult `($ef_mult ,@args)) ))
4375 (defun *f-matmult (mult-fn &rest args)
4376 ($rreduce
4377 #'(lambda (m1 m2) (gf-matmult m1 m2 mult-fn))
4378 (cons '(mlist simp) args) ))
4380 (defun gf-matmult (m1 m2 mult-fn)
4381 (when ($listp m1) (setq m1 (list '($matrix simp) m1)))
4382 (when ($listp m2) (setq m2 ($transpose m2)))
4383 (cond
4384 ((and ($matrixp m1) ($matrixp m2))
4385 (gf-matmult2 m1 m2) )
4386 (($matrixp m1)
4387 (gf-matmult1 m1 m2 mult-fn) ) ;; assumed without checking: m2 is poly
4388 (($matrixp m2)
4389 (gf-matmult1 m2 m1 mult-fn) )
4391 (mfuncall mult-fn m1 m2) ) ))
4393 (defun gf-matmult1 (m poly mult-fn)
4394 (do ((r (cdr m) (cdr r)) new)
4395 ((null r) (cons '($matrix simp) (nreverse new)))
4396 (push (cons '(mlist simp)
4397 (mapcar #'(lambda (p) (mfuncall mult-fn p poly)) (cdar r)) )
4398 new )))
4400 (defun gf-matmult2 (m1 m2)
4401 (setq m1 (cdr m1) m2 (cdr ($transpose m2)))
4402 (unless (= (length (car m1)) (length (car m2)))
4403 (gf-merror
4404 (intl:gettext "`~m': attempt to multiply non conformable matrices.")
4405 (if *ef-arith?* "ef_matmult" "gf_matmult") ))
4406 (let ((red (if *ef-arith?* *ef-red* *gf-red*)) )
4407 (do ((r1 m1 (cdr r1)) new-mat)
4408 ((null r1)
4409 (if (and (not (eq nil $scalarmatrixp))
4410 (= 1 (length new-mat)) (= 1 (length (cdar new-mat))) )
4411 (cadar new-mat)
4412 (cons '($matrix simp) (nreverse new-mat)) ))
4413 (do ((r2 m2 (cdr r2)) new-row)
4414 ((null r2)
4415 (push (cons '(mlist simp) (nreverse new-row)) new-mat) )
4416 (push (gf-x2p
4417 (reduce #'gf-nplus
4418 (mapcar #'(lambda (a b) (gf-times (gf-p2x a) (gf-p2x b) red))
4419 (cdar r1) (cdar r2) )))
4420 new-row ) ))))
4422 ;; -----------------------------------------------------------------------------
4425 ;; invert and determinant by lu ------------------------------------------------
4428 (defun zn-p-errchk (p fun pos)
4429 (unless (and p (integerp p) (primep p))
4430 (gf-merror (intl:gettext "`~m': ~m argument must be prime number.") fun pos) ))
4432 ;; invert by lu
4434 ;; returns nil when LU-decomposition fails
4435 (defun try-lu-and-call (fun m ring)
4436 (let ((old-out *standard-output*)
4437 (redirect (make-string-output-stream))
4438 (res nil) )
4439 (unwind-protect ;; protect against lu failure
4440 (setq *standard-output* redirect ;; discard error messages from lu-factor
4441 res (mfuncall fun m ring) )
4442 (progn
4443 (setq *standard-output* old-out)
4444 (close redirect) )
4445 (return-from try-lu-and-call res) )))
4447 (defmfun $zn_invert_by_lu (m p)
4448 (zn-p-errchk p "zn_invert_by_lu" "Second")
4449 (let ((*ef-arith?*) (*gf-char* p))
4450 (try-lu-and-call '$invert_by_lu m 'gf-coeff-ring) ))
4452 (defmfun $gf_matinv (m)
4453 (format t "`gf_matinv' is deprecated. ~%~\
4454 The user is asked to use `gf_invert_by_lu' instead.~%" )
4455 ($gf_invert_by_lu m) )
4457 (defmfun $gf_invert_by_lu (m)
4458 (gf-field? "gf_invert_by_lu")
4459 (let ((*ef-arith?*)) (try-lu-and-call '$invert_by_lu m 'gf-ring)) )
4461 (defmfun $ef_invert_by_lu (m)
4462 (ef-field? "ef_invert_by_lu")
4463 (let ((*ef-arith?* t)) (try-lu-and-call '$invert_by_lu m 'ef-ring)) )
4465 ;; determinant
4467 (defmfun $zn_determinant (m p)
4468 (zn-p-errchk p "zn_determinant" "Second")
4469 (let* ((*ef-arith?*)
4470 (*gf-char* p)
4471 (det (try-lu-and-call '$determinant_by_lu m 'gf-coeff-ring)) )
4472 (if det det
4473 (mod (mfuncall '$determinant m) p) )))
4475 (defmfun $gf_determinant (m)
4476 (gf-field? "gf_determinant")
4477 (let* ((*ef-arith?*)
4478 (det (try-lu-and-call '$determinant_by_lu m 'gf-ring)) )
4479 (if det det
4480 (let (($matrix_element_mult '$gf_mult) ($matrix_element_add '$gf_add))
4481 (mfuncall '$determinant m) ))))
4483 (defmfun $ef_determinant (m)
4484 (ef-field? "ef_determinant")
4485 (let* ((*ef-arith?* t)
4486 (det (try-lu-and-call '$determinant_by_lu m 'ef-ring)) )
4487 (if det det
4488 (let (($matrix_element_mult '$ef_mult) ($matrix_element_add '$ef_add))
4489 (mfuncall '$determinant m) ))))
4491 ;; -----------------------------------------------------------------------------
4494 ;; discrete logarithm ----------------------------------------------------------
4497 ;; solve g^x = a in Fq^n, where g a generator of (Fq^n)*
4499 (defmfun $gf_index (a)
4500 (gf-data? "gf_index")
4501 (gf-log-errchk1 *gf-prim* "gf_index")
4502 (let ((*ef-arith?*))
4503 (if (= 1 *gf-exp*)
4504 ($zn_log a (gf-x2n *gf-prim*) *gf-char*)
4505 (gf-dlog (gf-p2x a)) )))
4507 (defmfun $ef_index (a)
4508 (ef-data? "ef_index")
4509 (gf-log-errchk1 *ef-prim* "ef_index")
4510 (let ((*ef-arith?* t))
4511 (setq a (gf-p2x a))
4512 (if (= 1 *ef-exp*)
4513 (let ((*ef-arith?*)) (gf-dlog (gf-n2x (cadr a))))
4514 (ef-dlog a) )))
4516 (defmfun $gf_log (a &optional b)
4517 (gf-data? "gf_log")
4518 (gf-log-errchk1 *gf-prim* "gf_log")
4519 (let ((*ef-arith?*))
4520 (cond
4521 ((= 1 *gf-exp*)
4522 ($zn_log a (if b b (gf-x2n *gf-prim*)) *gf-char*) ) ;; $zn_log checks if b is primitive
4524 (setq a (gf-p2x a))
4525 (and b (setq b (gf-p2x b)) (gf-log-errchk2 b #'gf-prim-p "gf_log"))
4526 (if b
4527 (gf-dlogb a b)
4528 (gf-dlog a) )))))
4530 (defun gf-log-errchk1 (prim fun)
4531 (when (null prim)
4532 (gf-merror (intl:gettext "`~m': there is no primitive element.") fun) )
4533 (when (equal prim '$unknown)
4534 (gf-merror (intl:gettext "`~m': a primitive element is not known.") fun) ))
4536 (defun gf-log-errchk2 (x prim-p-fn fun)
4537 (unless (funcall prim-p-fn x)
4538 (gf-merror (intl:gettext
4539 "Second argument to `~m' must be a primitive element." ) fun )))
4541 (defmfun $ef_log (a &optional b)
4542 (ef-data? "ef_log")
4543 (gf-log-errchk1 *ef-prim* "ef_log")
4544 (let ((*ef-arith?* t))
4545 (setq a (gf-p2x a))
4546 (and b (setq b (gf-p2x b)) (gf-log-errchk2 b #'ef-prim-p "ef_log")) )
4547 (cond
4548 ((= 1 *ef-exp*)
4549 (let ((*ef-arith?*))
4550 (setq a (gf-n2x (cadr a)))
4551 (if b
4552 (gf-dlogb a (gf-n2x (cadr b)))
4553 (gf-dlog a) )))
4555 (let ((*ef-arith?* t))
4556 (if b
4557 (ef-dlogb a b)
4558 (ef-dlog a) )))))
4560 (defun gf-dlogb (a b)
4561 (*f-dlogb a b #'gf-dlog *gf-ord*) )
4563 (defun ef-dlogb (a b)
4564 (*f-dlogb a b #'ef-dlog *ef-ord*) )
4566 (defun *f-dlogb (a b dlog-fn ord)
4567 (let* ((a-ind (funcall dlog-fn a)) (b-ind (funcall dlog-fn b))
4568 (d (gcd (gcd a-ind b-ind) ord))
4569 (m (truncate ord d)) )
4570 (zn-quo (truncate a-ind d) (truncate b-ind d) m) ))
4572 ;; Pohlig and Hellman reduction
4574 (defun gf-dlog (a)
4575 (*f-dlog a *gf-prim* *gf-red* *gf-ord* *gf-fs-ord*) )
4577 (defun ef-dlog (a)
4578 (*f-dlog a *ef-prim* *ef-red* *ef-ord* *ef-fs-ord*) )
4580 (defun *f-dlog (a g red ord fs-ord)
4581 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
4582 (cond
4583 ((or (null a) (null g)) nil)
4584 ((>= (car a) (car red)) nil)
4585 ((equal '(0 1) a) 0)
4586 ((equal g a) 1)
4587 ((not (gf-unit-p a red)) nil)
4589 (let (p (e 0) ord/p om xp xk dlogs mods (g-inv (gf-inv g red)))
4590 (declare (fixnum e))
4591 (dolist (f fs-ord)
4592 (setq p (car f) e (cadr f)
4593 ord/p (truncate ord p)
4594 om (gf-pow g ord/p red)
4595 xp 0 )
4596 (do ((b a) (k 0) (pk 1) (acc g-inv) (e1 (1- e)))
4597 (()) (declare (fixnum k))
4598 (setq xk (gf-dlog-rho-brent (gf-pow b ord/p red) om p red))
4599 (incf xp (* xk pk))
4600 (incf k)
4601 (when (= k e) (return))
4602 (setq ord/p (truncate ord/p p)
4603 pk (* pk p) )
4604 (when (/= xk 0) (setq b (gf-times b (gf-pow acc xk red) red)))
4605 (when (/= k e1) (setq acc (gf-pow acc p red))) )
4606 (push (expt p e) mods)
4607 (push xp dlogs) )
4608 (car (solve-congruences dlogs mods)) ))))
4610 ;; iteration for Pollard rho: b = g^y * a^z in each step
4612 (defun gf-dlog-f (b y z a g p red)
4613 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
4614 (let ((m (mod (cadr b) 3))) (declare (fixnum m))
4615 (cond
4616 ((= 0 m)
4617 (values (gf-sq b red) (mod (ash y 1) p) (mod (ash z 1) p)) )
4618 ((= 1 m) ;; avoid stationary case b=1 => b^2=1
4619 (values (gf-times g b red) (mod (+ y 1) p) z ) )
4621 (values (gf-times a b red) y (mod (+ z 1) p) ) ) )))
4623 ;; brute-force:
4625 (defun gf-dlog-naive (a g red)
4626 (do ((i 0 (1+ i))
4627 (gi '(0 1) (gf-times gi g red)) )
4628 ((equal gi a) i) ))
4630 ;; baby-steps-giant-steps:
4632 (defun gf-dlog-baby-giant (a g p red) ;; g is generator of order prime p
4633 (let* ((m (1+ (isqrt p)))
4634 (s (floor (* 1.3 m)))
4635 (gi (gf-inv g red))
4636 g^m babies )
4637 (setf babies
4638 (make-hash-table :size s :test #'equal :rehash-threshold 0.9) )
4639 (do ((r 0) (b a))
4640 (())
4641 (when (equal '(0 1) b)
4642 (clrhash babies)
4643 (return-from gf-dlog-baby-giant r) )
4644 (setf (gethash b babies) r)
4645 (incf r)
4646 (when (= r m) (return))
4647 (setq b (gf-times gi b red)) )
4648 (setq g^m (gf-pow g m red))
4649 (do ((rr 0 (1+ rr))
4650 (bb (list 0 1) (gf-times g^m bb red))
4651 r ) (())
4652 (when (setq r (gethash bb babies))
4653 (clrhash babies)
4654 (return (+ r (* rr m))) )) ))
4656 ;; Pollard rho for dlog computation (Brents variant of collision detection)
4658 (defun gf-dlog-rho-brent (a g p red) ;; g is generator of order p
4659 #+ (or ccl ecl gcl) (declare (optimize (speed 3) (safety 0)))
4660 (cond
4661 ((equal '(0 1) a) 0)
4662 ((equal g a) 1)
4663 ((equal a (gf-sq g red)) 2)
4664 ((equal '(0 1) (gf-times a g red)) (1- p))
4665 ((< p 32) (gf-dlog-naive a g red))
4666 ((< p 1024) (gf-dlog-baby-giant a g p red))
4668 (prog ((b (list 0 1)) (y 0) (z 0) ;; b = g^y * a^z
4669 (bb (list 0 1)) (yy 0) (zz 0) ;; bb = g^yy * a^zz
4670 dy dz )
4672 (do ((i 0)(j 1)) (())
4673 (declare (fixnum i j))
4674 (multiple-value-setq (b y z) (gf-dlog-f b y z a g p red))
4675 (when (equal b bb) (return)) ;; g^y * a^z = g^yy * a^zz
4676 (incf i)
4677 (when (= i j)
4678 (setq j (1+ (ash j 1)))
4679 (setq bb b yy y zz z) ))
4680 (setq dy (mod (- yy y) p) dz (mod (- z zz) p)) ;; g^dy = a^dz = g^(x*dz)
4681 (when (= 1 (gcd dz p))
4682 (return (zn-quo dy dz p)) ) ;; x = dy/dz mod p (since g is generator of order p)
4683 (setq y 0
4685 b (list 0 1)
4686 yy (1+ (random (1- p)))
4687 zz (1+ (random (1- p)))
4688 bb (gf-times (gf-pow g yy red) (gf-pow a zz red) red) )
4689 (go rho) ))))
4691 ;; -----------------------------------------------------------------------------
4694 ;; nth root in Galois Fields ---------------------------------------------------
4697 (defmfun $ef_nth_root (a r)
4698 (ef-field? "ef_nth_root")
4699 (unless (and (integerp r) (> r 0))
4700 (gf-merror (intl:gettext "Second argument to `ef_nth_root' must be a positive integer. Found ~m.") a r) )
4701 (let* ((*ef-arith?* t)
4702 (rts (gf-nrt (gf-p2x a) r *ef-red* *ef-ord*)) )
4703 (gf-nrt-exit rts) ))
4705 (defmfun $gf_nth_root (a r)
4706 (gf-field? "gf_nth_root")
4707 (unless (and (integerp r) (> r 0))
4708 (gf-merror (intl:gettext "Second argument to `gf_nth_root' must be a positive integer. Found ~m.") a r) )
4709 (let* ((*ef-arith?*)
4710 (rts (gf-nrt (gf-p2x a) r *gf-red* *gf-ord*)) )
4711 (gf-nrt-exit rts) ))
4713 (defun gf-nrt-exit (rts)
4714 (when rts
4715 (setq rts (mapcar #'gf-n2x (sort (mapcar #'gf-x2n rts) #'<)))
4716 (cons '(mlist simp) (mapcar #'gf-x2p rts)) ))
4718 ;; e.g. r=i*i*j*k, then x^(1/r) = (((x^(1/i))^(1/i))^(1/j))^(1/k)
4719 (defun gf-nrt (x r red ord)
4720 (setq x (gf-nred x red))
4721 (let (rts)
4722 (cond
4723 ((null x) nil)
4724 ((or (= 1 r) (primep r)) (setq rts (gf-amm x r red ord)))
4726 (let* (($intfaclim) (rs (get-factor-list r)))
4727 (setq rs (sort rs #'< :key #'car))
4728 (setq rs
4729 (apply #'nconc
4730 (mapcar
4731 #'(lambda (pe) (apply #'(lambda (p e) (make-list e :initial-element p)) pe))
4732 rs )))
4733 (setq rts (gf-amm x (car rs) red ord))
4734 (dolist (r (cdr rs))
4735 (setq rts (apply #'nconc (mapcar #'(lambda (y) (gf-amm y r red ord)) rts))) ))))
4736 rts ))
4738 ;; inspired by Bach,Shallit 7.3.2
4739 (defun gf-amm (x r red ord) ;; r prime, red irreducible
4740 (if (= 1 r)
4741 (list x)
4742 (let (k n e u s m re xr xu om g br bu ab alpha beta rt)
4743 (when (= 2 r)
4744 (cond
4745 ((and (= 0 (setq m (mod ord 2)))
4746 (/= 1 (gf-jacobi x red (if *ef-arith?* *gf-card* *gf-char*))) )
4747 (return-from gf-amm nil) )
4748 ((= 1 m) ;; q = 2^n : unique solution
4749 (return-from gf-amm
4750 `(,(gf-pow x (ash (+ (ash ord 1) 2) -2) red)) ))
4751 ((= 2 (mod ord 4))
4752 (setq rt (gf-pow x (ash (+ ord 2) -2) red))
4753 (return-from gf-amm `(,rt ,(gf-minus rt))) )
4754 ((= 4 (mod ord 8))
4755 (let* ((twox (gf-plus x x))
4756 (y (gf-pow twox (ash (- ord 4) -3) red))
4757 (i (gf-times twox (gf-times y y red) red))
4758 (rt (gf-times x (gf-times y (gf-nplus i `(0 ,(gf-cminus-b 1))) red) red)) )
4759 (return-from gf-amm `(,rt ,(gf-minus rt))) ))))
4760 (multiple-value-setq (s m) (truncate ord r))
4761 (when (and (= 0 m) (not (equal '(0 1) (gf-pow x s red))))
4762 (return-from gf-amm nil))
4763 ;; r = 3, first 2 cases:
4764 (when (= 3 r)
4765 (cond
4766 ((= 1 (setq m (mod ord 3))) ;; unique solution
4767 (return-from gf-amm
4768 `(,(gf-pow x (truncate (1+ (ash ord 1)) 3) red)) ))
4769 ((= 2 m) ;; unique solution
4770 (return-from gf-amm
4771 `(,(gf-pow x (truncate (1+ ord) 3) red)) ))))
4772 ;; compute u,e with ord = u*r^e and r,u coprime:
4773 (setq u ord e 0)
4774 (do ((u1 u)) (())
4775 (multiple-value-setq (u1 m) (truncate u1 r))
4776 (when (/= 0 m) (return))
4777 (setq u u1 e (1+ e)) )
4778 (cond
4779 ((= 0 e)
4780 (setq rt (gf-pow x (inv-mod r u) red)) ;; unique solution, see Bach,Shallit 7.3.1
4781 (list rt) )
4783 (setq n (gf-n2x 2))
4784 (do () ((not (equal '(0 1) (gf-pow n s red)))) ;; n is no r-th power
4785 (setq n (gf-n2x (1+ (gf-x2n n)))) )
4786 (setq g (gf-pow n u red)
4787 re (expt r e)
4788 om (gf-pow g (truncate re r) red) ) ;; r-th root of unity
4789 (cond
4790 ((or (/= 3 r) (= 0 (setq m (mod ord 9))))
4791 (setq xr (gf-pow x u red)
4792 xu (gf-pow x re red)
4793 k (*f-dlog xr g red re `((,r ,e))) ;; g^k = xr
4794 br (gf-pow g (truncate k r) red) ;; br^r = xr
4795 bu (gf-pow xu (inv-mod r u) red) ;; bu^r = xu
4796 ab (cdr (zn-gcdex1 u re))
4797 alpha (car ab)
4798 beta (cadr ab) )
4799 (if (< alpha 0) (incf alpha ord) (incf beta ord))
4800 (setq rt (gf-times (gf-pow br alpha red) (gf-pow bu beta red) red)) )
4801 ;; r = 3, remaining cases:
4802 ((= 3 m)
4803 (setq rt (gf-pow x (truncate (+ (ash ord 1) 3) 9) red)) )
4804 ((= 6 m)
4805 (setq rt (gf-pow x (truncate (+ ord 3) 9) red)) ))
4806 (do ((i 1 (1+ i)) (j (list 0 1)) (res (list rt)))
4807 ((= i r) res)
4808 (setq j (gf-times j om red))
4809 (push (gf-times rt j red) res) ))))))
4811 ;; -----------------------------------------------------------------------------
4814 ;; tables of small fields ------------------------------------------------------
4817 (defmfun $gf_add_table ()
4818 (gf-data? "gf_add_table")
4819 (let ((*ef-arith?*)) (gf-add-table *gf-card*)) )
4821 (defmfun $ef_add_table ()
4822 (ef-data? "ef_add_table")
4823 (let ((*ef-arith?* t)) (gf-add-table *ef-card*)) )
4825 (defun gf-add-table (card)
4826 ($genmatrix
4827 #'(lambda (i j)
4828 (gf-x2n (gf-plus (gf-n2x (1- i)) (gf-n2x (1- j)))) )
4829 card
4830 card ))
4832 (defmfun $gf_mult_table (&optional all?)
4833 (gf-data? "gf_mult_table")
4834 (let ((*ef-arith?*))
4835 (gf-mult-table *gf-red* *gf-irred?* *gf-card* all?) ))
4837 (defmfun $ef_mult_table (&optional all?)
4838 (ef-data? "ef_mult_table")
4839 (let ((*ef-arith?* t))
4840 (gf-mult-table *ef-red* *ef-irred?* *ef-card* all?) ))
4842 (defun gf-mult-table (red irred? card all?)
4843 (let (units res)
4844 (cond
4845 ((or irred? ;; field
4846 (equal all? '$all) )
4847 ($genmatrix
4848 #'(lambda (i j)
4849 (gf-x2n (gf-times (gf-n2x i) (gf-n2x j) red)))
4850 (1- card)
4851 (1- card) ))
4852 (t ;; units only
4853 (do ((i 1 (1+ i)) x )
4854 ((= i card) )
4855 (setq x (gf-n2x i))
4856 (when (gf-unit-p x red) (push x units)) )
4857 (dolist (x units (cons '($matrix simp) (nreverse res)))
4858 (push
4859 (cons '(mlist simp)
4860 (mapcar #'(lambda (y) (gf-x2n (gf-times x y red))) units) )
4861 res ) )) )))
4863 (defmfun $gf_power_table (&rest args)
4864 (gf-data? "gf_power_table")
4865 (let ((*ef-arith?*) all? cols)
4866 (multiple-value-setq (all? cols)
4867 (apply #'gf-power-table-args (cons "gf_power_table" args)) )
4868 (unless cols
4869 (setq cols *gf-ord*)
4870 (when all? (incf cols)) )
4871 (gf-power-table *gf-red* *gf-irred?* *gf-card* cols all? ) ))
4873 (defmfun $ef_power_table (&rest args)
4874 (ef-data? "ef_power_table")
4875 (let ((*ef-arith?* t) all? cols)
4876 (multiple-value-setq (all? cols)
4877 (apply #'gf-power-table-args (cons "ef_power_table" args)) )
4878 (unless cols
4879 (setq cols *ef-ord*)
4880 (when all? (incf cols)) )
4881 (gf-power-table *ef-red* *ef-irred?* *ef-card* cols all? ) ))
4883 (defun gf-power-table-args (&rest args)
4884 (let ((str (car args)) all? cols (x (cadr args)) (y (caddr args)))
4885 (when x
4886 (cond
4887 ((integerp x) (setq cols x))
4888 ((equal x '$all) (setq all? t))
4889 (t (gf-merror (intl:gettext
4890 "First argument to `~m' must be `all' or a small fixnum." ) str ))))
4891 (when y
4892 (cond
4893 ((and (integerp x) (equal y '$all)) (setq all? t))
4894 ((and (equal x '$all) (integerp y)) (setq cols y))
4895 (t (format t "Only the first argument to `~a' has been used.~%" str) )))
4896 (values all? cols) ))
4898 (defun gf-power-table (red irred? card cols all?)
4899 (cond
4900 ((or irred? all?)
4901 ($genmatrix
4902 #'(lambda (i j) (gf-x2n (gf-pow (gf-n2x i) j red)))
4903 (1- card)
4904 cols ))
4905 (t ;; units only
4906 (do ((i 1 (1+ i)) x res)
4907 ((= i card) (cons '($matrix simp) (nreverse res)))
4908 (setq x (gf-n2x i))
4909 (when (gf-unit-p x red)
4910 (push
4911 (cons '(mlist simp)
4912 (mapcar #'(lambda (j) (gf-x2n (gf-pow x j red)))
4913 (cdr (mfuncall '$makelist '$j '$j 1 cols)) ))
4914 res ) )) )))
4916 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;