~ubuntu-branches/debian/squeeze/maxima/squeeze

« back to all changes in this revision

Viewing changes to share/contrib/Grobner/grobner.lisp

  • Committer: Bazaar Package Importer
  • Author(s): Camm Maguire
  • Date: 2006-10-18 14:52:42 UTC
  • mto: (1.1.5 upstream)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20061018145242-vzyrm5hmxr8kiosf
ImportĀ upstreamĀ versionĀ 5.10.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
;;; -*-  Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- 
 
2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
3
;;;                                                                              
 
4
;;;  $Id: grobner.lisp,v 1.1 2006/02/07 04:49:49 robert_dodier Exp $             
 
5
;;;  Copyright (C) 1999, 2002 Marek Rychlik <rychlik@u.arizona.edu>              
 
6
;;;                                                                              
 
7
;;;  This program is free software; you can redistribute it and/or modify        
 
8
;;;  it under the terms of the GNU General Public License as published by        
 
9
;;;  the Free Software Foundation; either version 2 of the License, or           
 
10
;;;  (at your option) any later version.                                         
 
11
;;;                                                                              
 
12
;;;  This program is distributed in the hope that it will be useful,             
 
13
;;;  but WITHOUT ANY WARRANTY; without even the implied warranty of              
 
14
;;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               
 
15
;;;  GNU General Public License for more details.                                
 
16
;;;                                                                              
 
17
;;;  You should have received a copy of the GNU General Public License           
 
18
;;;  along with this program; if not, write to the Free Software                 
 
19
;;;  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
 
20
;;;                                                                              
 
21
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
22
 
 
23
(in-package :maxima)
 
24
(macsyma-module cgb-maxima)
 
25
 
 
26
(eval-when (load eval)
 
27
  (format t "~&Loading maxima-grobner ~a ~a~%"
 
28
          "$Revision: 1.1 $" "$Date: 2006/02/07 04:49:49 $"))
 
29
 
 
30
;;FUNCTS is loaded because it contains the definition of LCM
 
31
($load "functs")
 
32
 
 
33
;; Macros for making lists with iterators - an exammple of GENSYM
 
34
;; MAKELIST-1 makes a list with one iterator, while MAKELIST accepts an
 
35
;; arbitrary number of iterators
 
36
 
 
37
;; Sample usage:
 
38
;; Without a step:
 
39
;; >(makelist-1 (* 2 i) i 0 10)
 
40
;; (0 2 4 6 8 10 12 14 16 18 20)
 
41
;; With a step of 3:
 
42
;; >(makelist-1 (* 2 i) i 0 10 3)
 
43
;; (0 6 12 18)
 
44
 
 
45
;; Generate sums of squares of numbers between 1 and 4:
 
46
;; >(makelist (+ (* i i) (* j j)) (i 1 4) (j 1 i))
 
47
;; (2 5 8 10 13 18 17 20 25 32)
 
48
;; >(makelist (list i j '---> (+ (* i i) (* j j))) (i 1 4) (j 1 i))
 
49
;; ((1 1 ---> 2) (2 1 ---> 5) (2 2 ---> 8) (3 1 ---> 10) (3 2 ---> 13)
 
50
;; (3 3 ---> 18) (4 1 ---> 17) (4 2 ---> 20) (4 3 ---> 25) (4 4 ---> 32))
 
51
 
 
52
;; Evaluate expression expr with variable set to lo, lo+1,... ,hi
 
53
;; and put the results in a list.
 
54
(defmacro makelist-1 (expr var lo hi &optional (step 1))
 
55
  (let ((l (gensym)))
 
56
    `(do ((,var ,lo (+ ,var ,step))
 
57
          (,l nil (cons ,expr ,l)))
 
58
         ((> ,var ,hi) (reverse ,l))
 
59
       (declare (fixnum ,var)))))
 
60
 
 
61
(defmacro makelist (expr (var lo hi &optional (step 1)) &rest more)
 
62
  (if (endp more)
 
63
      `(makelist-1 ,expr ,var ,lo ,hi ,step)
 
64
    (let* ((l (gensym)))
 
65
      `(do ((,var ,lo (+ ,var ,step))
 
66
            (,l nil (nconc ,l `,(makelist ,expr ,@more))))
 
67
           ((> ,var ,hi) ,l)
 
68
         (declare (fixnum ,var))))))
 
69
 
 
70
;;----------------------------------------------------------------
 
71
;; This package implements BASIC OPERATIONS ON MONOMIALS
 
72
;;----------------------------------------------------------------
 
73
;; DATA STRUCTURES: Monomials are represented as lists:
 
74
;;
 
75
;;      monom:  (n1 n2 ... nk) where ni are non-negative integers
 
76
;;
 
77
;; However, lists may be implemented as other sequence types,
 
78
;; so the flexibility to change the representation should be
 
79
;; maintained in the code to use general operations on sequences
 
80
;; whenever possible. The optimization for the actual representation
 
81
;; should be left to declarations and the compiler.
 
82
;;----------------------------------------------------------------
 
83
;; EXAMPLES: Suppose that variables are x and y. Then
 
84
;;
 
85
;;      Monom x*y^2 ---> (1 2)
 
86
;;
 
87
;;----------------------------------------------------------------
 
88
 
 
89
(deftype exponent ()
 
90
  "Type of exponent in a monomial."
 
91
  'fixnum)
 
92
 
 
93
(deftype monom (&optional dim)
 
94
  "Type of monomial."
 
95
  `(simple-array exponent (,dim)))
 
96
 
 
97
(declaim (optimize (speed 3) (safety 0)))
 
98
 
 
99
(declaim (ftype (function (monom) fixnum) monom-dimension monom-sugar)
 
100
         (ftype (function (monom &optional fixnum fixnum) fixnum) monom-total-degree)
 
101
         (ftype (function (monom monom) monom) monom-div monom-mul monom-lcm monom-gcd)
 
102
         (ftype (function (monom monom) (member t nil)) monom-divides-p monom-divisible-by-p monom-rel-prime-p)
 
103
         (ftype (function (monom monom monom) (member t nil)) monom-divides-monom-lcm-p)
 
104
         (ftype (function (monom monom monom monom) (member t nil)) monom-lcm-divides-monom-lcm-p)
 
105
         (ftype (function (monom fixnum) (member t nil)) monom-depends-p)
 
106
         ;;(ftype (function (t monom &optional monom) monom) monom-map)
 
107
         ;;(ftype (function (monom monom) monom) monom-append)
 
108
         )
 
109
 
 
110
(declaim (inline monom-mul monom-div
 
111
                 monom-total-degree monom-divides-p
 
112
                 monom-divisible-by-p monom-rel-prime monom-lcm))
 
113
 
 
114
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
115
;;
 
116
;; Construction of monomials
 
117
;;
 
118
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
119
 
 
120
(defmacro make-monom (dim &key (initial-contents nil initial-contents-supplied-p)
 
121
                               (initial-element 0 initial-element-supplied-p))
 
122
  "Make a monomial with DIM variables. Additional argument
 
123
INITIAL-CONTENTS specifies the list of powers of the consecutive
 
124
variables. The alternative additional argument INITIAL-ELEMENT
 
125
specifies the common power for all variables."
 
126
  (declare (fixnum dim))
 
127
  `(make-array ,dim
 
128
               :element-type 'exponent
 
129
               ,@(when initial-contents-supplied-p `(:initial-contents ,initial-contents))
 
130
               ,@(when initial-element-supplied-p `(:initial-element ,initial-element))))
 
131
 
 
132
 
 
133
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
134
;;
 
135
;; Operations on monomials
 
136
;;
 
137
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
138
 
 
139
(defmacro monom-elt (m index)
 
140
  "Return the power in the monomial M of variable number INDEX."
 
141
  `(elt ,m ,index))
 
142
 
 
143
(defun monom-dimension (m)
 
144
  "Return the number of variables in the monomial M."
 
145
  (length m))
 
146
 
 
147
(defun monom-total-degree (m &optional (start 0) (end (length m)))
 
148
  "Return the todal degree of a monomoal M. Optinally, a range
 
149
of variables may be specified with arguments START and END."
 
150
  (declare (type monom m) (fixnum start end))
 
151
  (reduce #'+ m :start start :end end))
 
152
 
 
153
(defun monom-sugar (m &aux (start 0) (end (length m)))
 
154
  "Return the sugar of a monomial M. Optinally, a range
 
155
of variables may be specified with arguments START and END."
 
156
  (declare (type monom m) (fixnum start end))
 
157
  (monom-total-degree m start end))
 
158
 
 
159
(defun monom-div (m1 m2 &aux (result (copy-seq m1)))
 
160
  "Divide monomial M1 by monomial M2."
 
161
  (declare (type monom m1 m2 result))
 
162
  (map-into result #'- m1 m2))
 
163
 
 
164
(defun monom-mul (m1 m2  &aux (result (copy-seq m1)))
 
165
  "Multiply monomial M1 by monomial M2."
 
166
  (declare (type monom m1 m2 result))
 
167
  (map-into result #'+ m1 m2))
 
168
 
 
169
(defun monom-divides-p (m1 m2)
 
170
  "Returns T if monomial M1 divides monomial M2, NIL otherwise."
 
171
  (declare (type monom m1 m2))
 
172
  (every #'<= m1 m2))
 
173
 
 
174
(defun monom-divides-monom-lcm-p (m1 m2 m3)
 
175
  "Returns T if monomial M1 divides MONOM-LCM(M2,M3), NIL otherwise."
 
176
  (declare (type monom m1 m2 m3))
 
177
  (every #'(lambda (x y z) (declare (type exponent x y z)) (<= x (max y z))) m1 m2 m3))
 
178
 
 
179
(defun monom-lcm-divides-monom-lcm-p (m1 m2 m3 m4)
 
180
  "Returns T if monomial MONOM-LCM(M1,M2) divides MONOM-LCM(M3,M4), NIL otherwise."
 
181
  (declare (type monom m1 m2 m3 m4))
 
182
  (every #'(lambda (x y z w) (declare (type exponent x y z w)) (<= (max x y) (max z w))) m1 m2 m3 m4))
 
183
 
 
184
(defun monom-lcm-equal-monom-lcm-p (m1 m2 m3 m4)
 
185
  "Returns T if monomial MONOM-LCM(M1,M2) equals MONOM-LCM(M3,M4), NIL otherwise."
 
186
  (declare (type monom m1 m2 m3 m4))
 
187
  (every #'(lambda (x y z w) (declare (type exponent x y z w)) (= (max x y) (max z w))) m1 m2 m3 m4))
 
188
 
 
189
(defun monom-divisible-by-p (m1 m2)
 
190
  "Returns T if monomial M1 is divisible by monomial M2, NIL otherwise."
 
191
  (declare (type monom m1 m2))
 
192
   (every #'>= m1 m2))
 
193
 
 
194
(defun monom-rel-prime-p (m1 m2)
 
195
  "Returns T if two monomials M1 and M2 are relatively prime (disjoint)."
 
196
  (declare (type monom m1 m2))
 
197
  (every #'(lambda (x y) (declare (type exponent x y)) (zerop (min x y))) m1 m2))
 
198
 
 
199
(defun monom-equal-p (m1 m2)
 
200
  "Returns T if two monomials M1 and M2 are equal."
 
201
  (declare (type monom m1 m2))
 
202
  (every #'= m1 m2))
 
203
 
 
204
(defun monom-lcm (m1 m2 &aux (result (copy-seq m1)))
 
205
  "Returns least common multiple of monomials M1 and M2."
 
206
  (declare (type monom m1 m2))
 
207
  (map-into result #'max m1 m2))
 
208
 
 
209
(defun monom-gcd (m1 m2 &aux (result (copy-seq m1)))
 
210
  "Returns greatest common divisor of monomials M1 and M2."
 
211
  (declare (type monom m1 m2))
 
212
  (map-into result #'min m1 m2))
 
213
 
 
214
(defun monom-depends-p (m k)
 
215
  "Return T if the monomial M depends on variable number K."
 
216
  (declare (type monom m) (fixnum k))
 
217
  (plusp (elt m k)))
 
218
 
 
219
(defmacro monom-map (fun m &rest ml &aux (result `(copy-seq ,m)))
 
220
  `(map-into ,result ,fun ,m ,@ml))
 
221
 
 
222
(defmacro monom-append (m1 m2)
 
223
  `(concatenate 'monom ,m1 ,m2))
 
224
 
 
225
(defmacro monom-contract (k m)
 
226
  `(subseq ,m ,k))
 
227
 
 
228
(defun monom-exponents (m)
 
229
  (declare (type monom m))
 
230
  (coerce m 'list))
 
231
 
 
232
 
 
233
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
234
;;
 
235
;; Implementations of various admissible monomial orders
 
236
;;
 
237
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
238
 
 
239
;; pure lexicographic
 
240
(defun lex> (p q &optional (start 0) (end (monom-dimension  p)))
 
241
  "Return T if P>Q with respect to lexicographic order, otherwise NIL.
 
242
The second returned value is T if P=Q, otherwise it is NIL."
 
243
  (declare (type monom p q) (type fixnum start end))
 
244
  (do ((i start (1+ i)))
 
245
      ((>= i end) (values NIL T))
 
246
    (declare (type fixnum i))
 
247
    (cond
 
248
     ((> (monom-elt p i) (monom-elt q i))
 
249
      (return-from lex> (values t nil)))
 
250
     ((< (monom-elt p i) (monom-elt q i))
 
251
      (return-from lex> (values nil nil))))))
 
252
 
 
253
;; total degree order , ties broken by lexicographic
 
254
(defun grlex> (p q &optional (start 0) (end (monom-dimension  p)))
 
255
  "Return T if P>Q with respect to graded lexicographic order, otherwise NIL.
 
256
The second returned value is T if P=Q, otherwise it is NIL."
 
257
  (declare (type monom p q) (type fixnum start end))
 
258
  (let ((d1 (monom-total-degree p start end))
 
259
        (d2 (monom-total-degree q start end)))
 
260
    (cond
 
261
      ((> d1 d2) (values t nil))
 
262
      ((< d1 d2) (values nil nil))
 
263
      (t
 
264
        (lex> p q start end)))))
 
265
 
 
266
 
 
267
;; total degree, ties broken by reverse lexicographic
 
268
(defun grevlex> (p q &optional (start 0) (end (monom-dimension  p)))
 
269
  "Return T if P>Q with respect to graded reverse lexicographic order,
 
270
NIL otherwise. The second returned value is T if P=Q, otherwise it is NIL."
 
271
  (declare (type monom p q) (type fixnum start end))
 
272
  (let ((d1 (monom-total-degree p start end))
 
273
        (d2 (monom-total-degree q start end)))
 
274
    (cond
 
275
     ((> d1 d2) (values t nil))
 
276
     ((< d1 d2) (values nil nil))
 
277
     (t
 
278
      (revlex> p q start end)))))
 
279
 
 
280
 
 
281
;; reverse lexicographic
 
282
(defun revlex> (p q &optional (start 0) (end (monom-dimension  p)))
 
283
  "Return T if P>Q with respect to reverse lexicographic order, NIL
 
284
otherwise.  The second returned value is T if P=Q, otherwise it is
 
285
NIL. This is not and admissible monomial order because some sets do
 
286
not have a minimal element. This order is useful in constructing other
 
287
orders."
 
288
  (declare (type monom p q) (type fixnum start end))
 
289
  (do ((i (1- end) (1- i)))
 
290
      ((< i start) (values NIL T))
 
291
    (declare (type fixnum i))
 
292
    (cond
 
293
     ((< (monom-elt p i) (monom-elt q i))
 
294
      (return-from revlex> (values t nil)))
 
295
     ((> (monom-elt p i) (monom-elt q i))
 
296
      (return-from revlex> (values nil nil))))))
 
297
 
 
298
 
 
299
(defun invlex> (p q &optional (start 0) (end (monom-dimension  p)))
 
300
  "Return T if P>Q with respect to inverse lexicographic order, NIL otherwise
 
301
The second returned value is T if P=Q, otherwise it is NIL."
 
302
  (declare (type monom p q) (type fixnum start end))
 
303
  (do ((i (1- end) (1- i)))
 
304
        ((< i start) (values NIL T))
 
305
    (declare (type fixnum i))
 
306
      (cond
 
307
         ((> (monom-elt p i) (monom-elt q i))
 
308
          (return-from invlex> (values t nil)))
 
309
         ((< (monom-elt p i) (monom-elt q i))
 
310
          (return-from invlex> (values nil nil))))))
 
311
 
 
312
 
 
313
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
314
;;
 
315
;; Order making functions
 
316
;;
 
317
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
318
 
 
319
(declaim (type function *monomial-order* *primary-elimination-order* *secondary-elimination-order*))
 
320
 
 
321
(defvar *monomial-order* #'lex>
 
322
  "Default order for monomial comparisons")
 
323
 
 
324
(defmacro monomial-order (x y)
 
325
  `(funcall *monomial-order* ,x ,y))
 
326
 
 
327
(defun reverse-monomial-order (x y)
 
328
  (monomial-order y x))
 
329
 
 
330
(defvar *primary-elimination-order* #'lex>)
 
331
 
 
332
(defvar *secondary-elimination-order* #'lex>)
 
333
 
 
334
(defvar *elimination-order* nil
 
335
  "Default elimination order used in elimination-based functions.
 
336
If not NIL, it is assumed to be a proper elimination order. If NIL,
 
337
we will construct an elimination order using the values of
 
338
*PRIMARY-ELIMINATION-ORDER* and *SECONDARY-ELIMINATION-ORDER*.")
 
339
 
 
340
(defun elimination-order (k)
 
341
  "Return a predicate which compares monomials according to the
 
342
K-th elimination order. Two variables *PRIMARY-ELIMINATION-ORDER*
 
343
and *SECONDARY-ELIMINATION-ORDER* control the behavior on the first K
 
344
and the remaining variables, respectively."
 
345
  (declare (type fixnum k))
 
346
  #'(lambda (p q &optional (start 0) (end (monom-dimension  p)))
 
347
      (declare (type monom p q) (type fixnum start end))
 
348
      (multiple-value-bind (primary equal)
 
349
           (funcall *primary-elimination-order* p q start k)
 
350
         (if equal
 
351
             (funcall *secondary-elimination-order* p q k end)
 
352
           (values primary nil)))))
 
353
 
 
354
(defun elimination-order-1 (p q &optional (start 0) (end (monom-dimension  p)))
 
355
  "Equivalent to the function returned by the call to (ELIMINATION-ORDER 1)."
 
356
  (declare (type monom p q) (type fixnum start end))
 
357
  (cond
 
358
   ((> (monom-elt p start) (monom-elt q start)) (values t nil))
 
359
   ((< (monom-elt p start) (monom-elt q start)) (values nil nil))
 
360
   (t (funcall *secondary-elimination-order* p q (1+ start) end))))
 
361
 
 
362
 
 
363
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
364
;;
 
365
;; Priority queue stuff
 
366
;;
 
367
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
368
 
 
369
(declaim (integer *priority-queue-allocation-size*))
 
370
 
 
371
(defparameter *priority-queue-allocation-size* 16)
 
372
 
 
373
(defun priority-queue-make-heap (&key (element-type 'fixnum))
 
374
  (make-array *priority-queue-allocation-size* :element-type element-type :fill-pointer 1
 
375
              :adjustable t))
 
376
 
 
377
(defstruct (priority-queue (:constructor priority-queue-construct))
 
378
  (heap (priority-queue-make-heap))
 
379
  test)
 
380
 
 
381
(defun make-priority-queue (&key (element-type 'fixnum)
 
382
                            (test #'<=)
 
383
                            (element-key #'identity))
 
384
  (priority-queue-construct
 
385
   :heap (priority-queue-make-heap :element-type element-type)
 
386
   :test #'(lambda (x y) (funcall test (funcall element-key y) (funcall element-key x)))))
 
387
  
 
388
(defun priority-queue-insert (pq item)
 
389
  (priority-queue-heap-insert (priority-queue-heap pq) item (priority-queue-test pq)))
 
390
 
 
391
(defun priority-queue-remove (pq)
 
392
  (priority-queue-heap-remove (priority-queue-heap pq) (priority-queue-test pq)))
 
393
 
 
394
(defun priority-queue-empty-p (pq)
 
395
  (priority-queue-heap-empty-p (priority-queue-heap pq)))
 
396
 
 
397
(defun priority-queue-size (pq)
 
398
  (fill-pointer (priority-queue-heap pq)))
 
399
 
 
400
(defun priority-queue-upheap (a k
 
401
               &optional
 
402
               (test #'<=)
 
403
               &aux  (v (aref a k)))
 
404
  (declare (fixnum k))
 
405
  (assert (< 0 k (fill-pointer a)))
 
406
  (loop
 
407
   (let ((parent (ash k -1)))
 
408
     (when (zerop parent) (return))
 
409
     (unless (funcall test (aref a parent) v)
 
410
       (return))
 
411
     (setf (aref a k) (aref a parent)
 
412
           k parent)))
 
413
  (setf (aref a k) v)
 
414
  a)
 
415
 
 
416
    
 
417
(defun priority-queue-heap-insert (a item &optional (test #'<=))
 
418
  (vector-push-extend item a)
 
419
  (priority-queue-upheap a (1- (fill-pointer a)) test))
 
420
 
 
421
(defun priority-queue-downheap (a k
 
422
                 &optional
 
423
                 (test #'<=)
 
424
                 &aux  (v (aref a k)) (j 0) (n (fill-pointer a)))
 
425
  (declare (fixnum k n j))
 
426
  (loop
 
427
   (unless (<= k (ash n -1))
 
428
     (return))
 
429
   (setf j (ash k 1))
 
430
   (if (and (< j n) (not (funcall test (aref a (1+ j)) (aref a j))))
 
431
       (incf j))
 
432
   (when (funcall test (aref a j) v)
 
433
     (return))
 
434
   (setf (aref a k) (aref a j)
 
435
         k j))
 
436
  (setf (aref a k) v)
 
437
  a)
 
438
 
 
439
(defun priority-queue-heap-remove (a &optional (test #'<=) &aux (v (aref a 1)))
 
440
  (when (<= (fill-pointer a) 1) (error "Empty queue."))
 
441
  (setf (aref a 1) (vector-pop a))
 
442
  (priority-queue-downheap a 1 test)
 
443
  (values v a))
 
444
 
 
445
(defun priority-queue-heap-empty-p (a)
 
446
  (<= (fill-pointer a) 1))
 
447
 
 
448
 
 
449
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
450
;;
 
451
;; Global switches
 
452
;; (Can be used in Maxima just fine)
 
453
;;
 
454
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
455
 
 
456
(defmvar $poly_monomial_order '$lex
 
457
  "This switch controls which monomial order is used in polynomial
 
458
and Grobner basis calculations. If not set, LEX will be used")
 
459
 
 
460
(defmvar $poly_coefficient_ring '$expression_ring
 
461
  "This switch indicates the coefficient ring of the polynomials
 
462
that will be used in grobner calculations. If not set, Maxima's
 
463
general expression ring will be used. This variable may be set
 
464
to RING_OF_INTEGERS if desired.")
 
465
 
 
466
(defmvar $poly_primary_elimination_order NIL
 
467
  "Name of the default order for eliminated variables in elimination-based functions.
 
468
If not set, LEX will be used.")
 
469
 
 
470
(defmvar $poly_secondary_elimination_order NIL
 
471
  "Name of the default order for kept variables in elimination-based functions.
 
472
If not set, LEX will be used.")
 
473
 
 
474
(defmvar $poly_elimination_order NIL
 
475
  "Name of the default elimination order used in elimination calculations.
 
476
If set, it overrides the settings in variables POLY_PRIMARY_ELIMINATION_ORDER
 
477
and SECONDARY_ELIMINATION_ORDER. The user must ensure that this is a true
 
478
elimination order valid for the number of eliminated variables.")
 
479
 
 
480
(defmvar $poly_return_term_list NIL
 
481
  "If set to T, all functions in this package will return each polynomial as a
 
482
list of terms in the current monomial order rather than a Maxima general expression.")
 
483
 
 
484
(defmvar $poly_grobner_debug nil
 
485
  "If set to TRUE, produce debugging and tracing output.")
 
486
 
 
487
(defmvar $poly_grobner_algorithm '$buchberger
 
488
  "The name of the algorithm used to find grobner bases.")
 
489
 
 
490
(defmvar $poly_top_reduction_only NIL
 
491
  "If not FALSE, use top reduction only whenever possible.
 
492
Top reduction means that division algorithm stops after the first reduction.")
 
493
 
 
494
 
 
495
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
496
;;
 
497
;; Coefficient ring operations
 
498
;;
 
499
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
500
;;
 
501
;; These are ALL operations that are performed on the coefficients by
 
502
;; the package, and thus the coefficient ring can be changed by merely
 
503
;; redefining these operations.
 
504
;;
 
505
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
506
 
 
507
(defstruct (ring)
 
508
  (parse #'identity :type function)
 
509
  (unit #'identity :type function)
 
510
  (zerop #'identity :type function)
 
511
  (add #'identity :type function)
 
512
  (sub #'identity :type function)
 
513
  (uminus #'identity :type function)
 
514
  (mul #'identity :type function)
 
515
  (div #'identity :type function)
 
516
  (lcm #'identity :type function)
 
517
  (ezgcd #'identity :type function)
 
518
  (gcd #'identity :type function))
 
519
 
 
520
(declaim (type ring *RingOfIntegers* *FieldOfRationals*))
 
521
 
 
522
(defparameter *RingOfIntegers*
 
523
    (make-ring
 
524
     :parse #'identity
 
525
     :unit #'(lambda () 1)
 
526
     :zerop #'zerop
 
527
     :add #'+
 
528
     :sub #'-
 
529
     :uminus #'-
 
530
     :mul #'*
 
531
     :div #'/
 
532
     :lcm #'lcm
 
533
     :ezgcd #'(lambda (x y &aux (c (gcd x y))) (values c (/ x c) (/ y c)))
 
534
     :gcd #'gcd)
 
535
  "The ring of integers.")
 
536
 
 
537
 
 
538
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
539
;;
 
540
;; This is how we perform operations on coefficients
 
541
;; using Maxima functions. 
 
542
;;
 
543
;; Functions and macros dealing with internal representation structure
 
544
;;
 
545
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
546
 
 
547
(defstruct (term
 
548
            (:constructor make-term (monom coeff))
 
549
            (:constructor make-term-variable)
 
550
            ;;(:type list)
 
551
            )
 
552
  (monom (make-monom 0) :type monom)
 
553
  (coeff nil))
 
554
 
 
555
(defun make-term-variable (ring nvars pos
 
556
                                &optional
 
557
                                (power 1)
 
558
                                (coeff (funcall (ring-unit ring)))
 
559
                                &aux
 
560
                                (monom (make-monom nvars :initial-element 0)))
 
561
  (declare (fixnum nvars pos power))
 
562
  (incf (monom-elt monom pos) power)
 
563
  (make-term monom coeff))
 
564
 
 
565
(defun term-sugar (term)
 
566
  (monom-sugar (term-monom term)))
 
567
 
 
568
(defun termlist-sugar (p &aux (sugar -1))
 
569
  (declare (fixnum sugar))
 
570
  (dolist (term p sugar)
 
571
    (setf sugar (max sugar (term-sugar term)))))
 
572
 
 
573
 
 
574
 
 
575
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
576
;;
 
577
;; Low-level polynomial arithmetic done on 
 
578
;; lists of terms
 
579
;;
 
580
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
581
 
 
582
(defmacro termlist-lt (p) `(car ,p))
 
583
(defun termlist-lm (p) (term-monom (termlist-lt p)))
 
584
(defun termlist-lc (p) (term-coeff (termlist-lt p)))
 
585
 
 
586
(define-modify-macro scalar-mul (c) coeff-mul)
 
587
 
 
588
(declaim (ftype (function (ring t t) t) scalar-times-termlist))
 
589
 
 
590
(defun scalar-times-termlist (ring c p)
 
591
  "Multiply scalar C by a polynomial P. This function works
 
592
even if there are divisors of 0."
 
593
  (mapcan
 
594
   #'(lambda (term)
 
595
       (let ((c1 (funcall (ring-mul ring) c (term-coeff term))))
 
596
         (unless (funcall (ring-zerop ring) c1)
 
597
           (list (make-term (term-monom term) c1)))))
 
598
   p))
 
599
 
 
600
 
 
601
(declaim (ftype (function (ring term term) list) term-mul))
 
602
 
 
603
(defun term-mul (ring term1 term2)
 
604
  "Returns (LIST TERM) wheter TERM is the product of the terms TERM1 TERM2,
 
605
or NIL when the product is 0. This definition takes care of divisors of 0
 
606
in the coefficient ring."
 
607
  (let ((c (funcall (ring-mul ring) (term-coeff term1) (term-coeff term2))))
 
608
    (unless (funcall (ring-zerop ring) c)
 
609
      (list (make-term (monom-mul (term-monom term1) (term-monom term2)) c)))))
 
610
 
 
611
(declaim (ftype (function (ring term list) list) term-times-termlist))
 
612
 
 
613
(defun term-times-termlist (ring term f)
 
614
  (declare (type ring ring))
 
615
  (mapcan #'(lambda (term-f) (term-mul ring term term-f)) f))
 
616
 
 
617
(declaim (ftype (function (ring list term) list) termlist-times-term))
 
618
 
 
619
(defun termlist-times-term (ring f term)
 
620
  (mapcan #'(lambda (term-f) (term-mul ring term-f term)) f))
 
621
 
 
622
(declaim (ftype (function (monom term) term) monom-times-term))
 
623
 
 
624
(defun monom-times-term (m term)
 
625
  (make-term (monom-mul m (term-monom term)) (term-coeff term)))
 
626
 
 
627
(declaim (ftype (function (monom list) list) monom-times-poly))
 
628
 
 
629
(defun monom-times-termlist (m f)
 
630
  (cond
 
631
   ((null f) nil)
 
632
   (t
 
633
    (mapcar #'(lambda (x) (monom-times-term m x)) f))))
 
634
 
 
635
(declaim (ftype (function (ring list) list) termlist-uminus))
 
636
 
 
637
(defun termlist-uminus (ring f)
 
638
  (mapcar #'(lambda (x)
 
639
              (make-term (term-monom x) (funcall (ring-uminus ring) (term-coeff x))))
 
640
          f))
 
641
 
 
642
(declaim (ftype (function (ring list list) list) termlist-add termlist-sub termlist-mul))
 
643
 
 
644
(defun termlist-add (ring p q)
 
645
  (declare (type list p q))
 
646
  (do (r)
 
647
      ((cond
 
648
        ((endp p)
 
649
         (setf r (revappend r q)) t)
 
650
        ((endp q)
 
651
         (setf r (revappend r p)) t)
 
652
        (t
 
653
         (multiple-value-bind
 
654
             (lm-greater lm-equal)
 
655
             (monomial-order (termlist-lm p) (termlist-lm q))
 
656
           (cond
 
657
            (lm-equal
 
658
             (let ((s (funcall (ring-add ring) (termlist-lc p) (termlist-lc q))))
 
659
               (unless (funcall (ring-zerop ring) s)    ;check for cancellation
 
660
                 (setf r (cons (make-term (termlist-lm p) s) r)))
 
661
               (setf p (cdr p) q (cdr q))))
 
662
            (lm-greater
 
663
             (setf r (cons (car p) r)
 
664
                   p (cdr p)))
 
665
            (t (setf r (cons (car q) r)
 
666
                     q (cdr q)))))
 
667
         nil))
 
668
       r)))
 
669
 
 
670
(defun termlist-sub (ring p q)
 
671
  (declare (type list p q))
 
672
  (do (r)
 
673
      ((cond
 
674
        ((endp p)
 
675
         (setf r (revappend r (termlist-uminus ring q)))
 
676
         t)
 
677
        ((endp q)
 
678
         (setf r (revappend r p))
 
679
         t)
 
680
        (t
 
681
         (multiple-value-bind
 
682
             (mgreater mequal)
 
683
             (monomial-order (termlist-lm p) (termlist-lm q))
 
684
           (cond
 
685
            (mequal
 
686
             (let ((s (funcall (ring-sub ring) (termlist-lc p) (termlist-lc q))))
 
687
               (unless (funcall (ring-zerop ring) s)    ;check for cancellation
 
688
                 (setf r (cons (make-term (termlist-lm p) s) r)))
 
689
               (setf p (cdr p) q (cdr q))))
 
690
            (mgreater
 
691
             (setf r (cons (car p) r)
 
692
                   p (cdr p)))
 
693
            (t (setf r (cons (make-term (termlist-lm q) (funcall (ring-uminus ring) (termlist-lc q))) r)
 
694
                     q (cdr q)))))
 
695
         nil))
 
696
       r)))
 
697
 
 
698
;; Multiplication of polynomials
 
699
;; Non-destructive version
 
700
(defun termlist-mul (ring p q)
 
701
  (cond ((or (endp p) (endp q)) nil)    ;p or q is 0 (represented by NIL)
 
702
        ;; If p=p0+p1 and q=q0+q1 then pq=p0q0+p0q1+p1q
 
703
        ((endp (cdr p))
 
704
         (term-times-termlist ring (car p) q))
 
705
        ((endp (cdr q))
 
706
         (termlist-times-term ring p (car q)))
 
707
        (t
 
708
         (let ((head (term-mul ring (termlist-lt p) (termlist-lt q)))
 
709
               (tail (termlist-add ring (term-times-termlist ring (car p) (cdr q))
 
710
                                   (termlist-mul ring (cdr p) q))))
 
711
           (cond ((null head) tail)
 
712
                 ((null tail) head)
 
713
                 (t (nconc head tail)))))))
 
714
                    
 
715
(defun termlist-unit (ring dimension)
 
716
  (declare (fixnum dimension))
 
717
  (list (make-term (make-monom dimension :initial-element 0)
 
718
                   (funcall (ring-unit ring)))))
 
719
 
 
720
(defun termlist-expt (ring poly n &aux (dim (monom-dimension (termlist-lm poly))))
 
721
  (declare (type fixnum n dim))
 
722
  (cond
 
723
   ((minusp n) (error "termlist-expt: Negative exponent."))
 
724
   ((endp poly) (if (zerop n) (termlist-unit ring dim) nil))
 
725
   (t
 
726
    (do ((k 1 (ash k 1))
 
727
         (q poly (termlist-mul ring q q))       ;keep squaring
 
728
         (p (termlist-unit ring dim) (if (not (zerop (logand k n))) (termlist-mul ring p q) p)))
 
729
        ((> k n) p)
 
730
      (declare (fixnum k))))))
 
731
 
 
732
 
 
733
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
734
;;
 
735
;; Additional structure operations on a list of terms
 
736
;;
 
737
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
738
 
 
739
(defun termlist-contract (p &optional (k 1))
 
740
  "Eliminate first K variables from a polynomial P."
 
741
  (mapcar #'(lambda (term) (make-term (monom-contract k (term-monom term))
 
742
                                      (term-coeff term)))
 
743
          p))
 
744
 
 
745
(defun termlist-extend (p &optional (m (list 0)))
 
746
  "Extend every monomial in a polynomial P by inserting at the
 
747
beginning of every monomial the list of powers M."
 
748
  (mapcar #'(lambda (term) (make-term (monom-append m (term-monom term))
 
749
                                      (term-coeff term)))
 
750
          p))
 
751
 
 
752
(defun termlist-add-variables (p n)
 
753
  "Add N variables to a polynomial P by inserting zero powers
 
754
at the beginning of each monomial."
 
755
  (declare (fixnum n))
 
756
  (mapcar #'(lambda (term)
 
757
              (make-term (monom-append (make-monom n :initial-element 0)
 
758
                                       (term-monom term))
 
759
                         (term-coeff term)))
 
760
          p))
 
761
 
 
762
 
 
763
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
764
;;
 
765
;; Arithmetic on polynomials
 
766
;;
 
767
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
768
 
 
769
(defstruct (poly
 
770
            ;;BOA constructor, by default constructs zero polynomial
 
771
            (:constructor make-poly-from-termlist (termlist &optional (sugar (termlist-sugar termlist))))
 
772
            (:constructor make-poly-zero (&aux (termlist nil) (sugar -1)))
 
773
            ;;Constructor of polynomials representing a variable
 
774
            (:constructor make-variable (ring nvars pos &optional (power 1)
 
775
                                              &aux
 
776
                                              (termlist (list
 
777
                                                         (make-term-variable ring nvars pos power)))
 
778
                                              (sugar power)))
 
779
            (:constructor poly-unit (ring dimension
 
780
                                     &aux
 
781
                                     (termlist (termlist-unit ring dimension))
 
782
                                     (sugar 0))))
 
783
  (termlist nil :type list)
 
784
  (sugar -1 :type fixnum))
 
785
 
 
786
;; Leading term
 
787
(defmacro poly-lt (p) `(car (poly-termlist ,p)))
 
788
 
 
789
;; Second term
 
790
(defmacro poly-second-lt (p) `(cadar (poly-termlist ,p)))
 
791
 
 
792
;; Leading monomial
 
793
(defun poly-lm (p) (term-monom (poly-lt p)))
 
794
 
 
795
;; Second monomial
 
796
(defun poly-second-lm (p) (term-monom (poly-second-lt p)))
 
797
 
 
798
;; Leading coefficient
 
799
(defun poly-lc (p) (term-coeff (poly-lt p)))
 
800
 
 
801
;; Second coefficient
 
802
(defun poly-second-lc (p) (term-coeff (poly-second-lt p)))
 
803
 
 
804
;; Testing for a zero polynomial
 
805
(defun poly-zerop (p) (null (poly-termlist p)))
 
806
 
 
807
;; The number of terms
 
808
(defun poly-length (p) (length (poly-termlist p)))
 
809
 
 
810
(declaim (ftype (function (ring t poly) poly) scalar-times-poly))
 
811
 
 
812
(defun scalar-times-poly (ring c p)
 
813
  (make-poly-from-termlist (scalar-times-termlist ring c (poly-termlist p)) (poly-sugar p)))
 
814
    
 
815
(declaim (ftype (function (monom poly) poly) monom-times-poly))
 
816
 
 
817
(defun monom-times-poly (m p)
 
818
  (make-poly-from-termlist (monom-times-termlist m (poly-termlist p)) (+ (poly-sugar p) (monom-sugar m))))
 
819
 
 
820
(declaim (ftype (function (ring term poly) poly) term-times-poly))
 
821
 
 
822
(defun term-times-poly (ring term p)
 
823
  (make-poly-from-termlist (term-times-termlist ring term (poly-termlist p)) (+ (poly-sugar p) (term-sugar term))))
 
824
 
 
825
(declaim (ftype (function (ring poly poly) poly) poly-add poly-sub poly-mul))
 
826
 
 
827
(defun poly-add (ring p q)
 
828
  (make-poly-from-termlist (termlist-add ring (poly-termlist p) (poly-termlist q)) (max (poly-sugar p) (poly-sugar q))))
 
829
 
 
830
(defun poly-sub (ring p q)
 
831
  (make-poly-from-termlist (termlist-sub ring (poly-termlist p) (poly-termlist q)) (max (poly-sugar p) (poly-sugar q))))
 
832
 
 
833
(declaim (ftype (function (ring poly) poly) poly-uminus))
 
834
 
 
835
(defun poly-uminus (ring p)
 
836
  (make-poly-from-termlist (termlist-uminus ring (poly-termlist p)) (poly-sugar p)))
 
837
 
 
838
(defun poly-mul (ring p q)
 
839
  (make-poly-from-termlist (termlist-mul ring (poly-termlist p) (poly-termlist q)) (+ (poly-sugar p) (poly-sugar q))))
 
840
 
 
841
(declaim (ftype (function (ring poly fixnum) poly) poly-expt))
 
842
 
 
843
(defun poly-expt (ring p n)
 
844
  (make-poly-from-termlist (termlist-expt ring (poly-termlist p) n) (* n (poly-sugar p))))
 
845
 
 
846
(defun poly-append (&rest plist)
 
847
  (make-poly-from-termlist (apply #'append (mapcar #'poly-termlist plist))
 
848
             (apply #'max (mapcar #'poly-sugar plist))))
 
849
 
 
850
(declaim (ftype (function (poly) poly) poly-nreverse))
 
851
 
 
852
(defun poly-nreverse (p)
 
853
  (setf (poly-termlist p) (nreverse (poly-termlist p)))
 
854
  p)
 
855
 
 
856
(declaim (ftype (function (poly &optional fixnum) poly) poly-contract))
 
857
 
 
858
(defun poly-contract (p &optional (k 1))
 
859
  (make-poly-from-termlist (termlist-contract (poly-termlist p) k)
 
860
             (poly-sugar p)))
 
861
 
 
862
(declaim (ftype (function (poly &optional sequence)) poly-extend))
 
863
 
 
864
(defun poly-extend (p &optional (m (list 0)))
 
865
  (make-poly-from-termlist
 
866
   (termlist-extend (poly-termlist p) m)
 
867
   (+ (poly-sugar p) (monom-sugar m))))
 
868
 
 
869
(declaim (ftype (function (poly fixnum)) poly-add-variables))
 
870
 
 
871
(defun poly-add-variables (p k)
 
872
  (setf (poly-termlist p) (termlist-add-variables (poly-termlist p) k))
 
873
  p)
 
874
 
 
875
(defun poly-list-add-variables (plist k)
 
876
  (mapcar #'(lambda (p) (poly-add-variables p k)) plist))
 
877
 
 
878
(defun poly-standard-extension (plist &aux (k (length plist)))
 
879
  "Calculate [U1*P1,U2*P2,...,UK*PK], where PLIST=[P1,P2,...,PK]."
 
880
  (declare (list plist) (fixnum k))
 
881
  (labels ((incf-power (g i)
 
882
             (dolist (x (poly-termlist g))
 
883
               (incf (monom-elt (term-monom x) i)))
 
884
             (incf (poly-sugar g))))
 
885
    (setf plist (poly-list-add-variables plist k))
 
886
    (dotimes (i k plist)
 
887
      (incf-power (nth i plist) i))))
 
888
 
 
889
(defun saturation-extension (ring F plist &aux (k (length plist)) (d (monom-dimension (poly-lm (car plist)))))
 
890
  "Calculate [F, U1*P1-1,U2*P2-1,...,UK*PK-1], where PLIST=[P1,P2,...,PK]."
 
891
  (setf F (poly-list-add-variables F k)
 
892
        plist (mapcar #'(lambda (x)
 
893
                          (setf (poly-termlist x) (nconc (poly-termlist x)
 
894
                                                         (list (make-term (make-monom d :initial-element 0)
 
895
                                                                          (funcall (ring-uminus ring) (funcall (ring-unit ring)))))))
 
896
                          x)
 
897
                      (poly-standard-extension plist)))
 
898
  (append F plist))
 
899
 
 
900
 
 
901
(defun polysaturation-extension (ring F plist &aux (k (length plist))
 
902
                                                   (d (+ k (length (poly-lm (car plist))))))
 
903
  "Calculate [F, U1*P1+U2*P2+...+UK*PK-1], where PLIST=[P1,P2,...,PK]."
 
904
  (setf F (poly-list-add-variables F k)
 
905
        plist (apply #'poly-append (poly-standard-extension plist))
 
906
        (cdr (last (poly-termlist plist))) (list (make-term (make-monom d :initial-element 0)
 
907
                                                            (funcall (ring-uminus ring) (funcall (ring-unit ring))))))
 
908
  (append F (list plist)))
 
909
 
 
910
(defun saturation-extension-1 (ring F p) (polysaturation-extension ring F (list p)))
 
911
 
 
912
 
 
913
 
 
914
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
915
;;
 
916
;; Evaluation of polynomial (prefix) expressions
 
917
;;
 
918
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
919
 
 
920
(defun coerce-coeff (ring expr vars)
 
921
  "Coerce an element of the coefficient ring to a constant polynomial."
 
922
  ;; Modular arithmetic handler by rat
 
923
  (make-poly-from-termlist (list (make-term (make-monom (length vars) :initial-element 0)
 
924
                              (funcall (ring-parse ring) expr)))
 
925
             0))
 
926
 
 
927
(defun poly-eval (ring expr vars &optional (list-marker '[))
 
928
  (labels ((p-eval (arg) (poly-eval ring arg vars))
 
929
           (p-eval-list (args) (mapcar #'p-eval args))
 
930
           (p-add (x y) (poly-add ring x y)))
 
931
    (cond
 
932
     ((eql expr 0) (make-poly-zero))
 
933
     ((member expr vars :test #'equalp)
 
934
      (let ((pos (position expr vars :test #'equalp)))
 
935
        (make-variable ring (length vars) pos)))
 
936
     ((atom expr)
 
937
      (coerce-coeff ring expr vars))
 
938
     ((eq (car expr) list-marker)
 
939
      (cons list-marker (p-eval-list (cdr expr))))
 
940
     (t
 
941
      (case (car expr)
 
942
        (+ (reduce #'p-add (p-eval-list (cdr expr))))
 
943
        (- (case (length expr)
 
944
             (1 (make-poly-zero))
 
945
             (2 (poly-uminus ring (p-eval (cadr expr))))
 
946
             (3 (poly-sub ring (p-eval (cadr expr)) (p-eval (caddr expr))))
 
947
             (otherwise (poly-sub ring (p-eval (cadr expr))
 
948
                                  (reduce #'p-add (p-eval-list (cddr expr)))))))
 
949
        (*
 
950
         (if (endp (cddr expr))         ;unary
 
951
             (p-eval (cdr expr))
 
952
           (reduce #'(lambda (p q) (poly-mul ring p q)) (p-eval-list (cdr expr)))))
 
953
        (expt
 
954
         (cond
 
955
          ((member (cadr expr) vars :test #'equalp)
 
956
           ;;Special handling of (expt var pow)
 
957
           (let ((pos (position (cadr expr) vars :test #'equalp)))
 
958
             (make-variable ring (length vars) pos (caddr expr))))
 
959
          ((not (and (integerp (caddr expr)) (plusp (caddr expr))))
 
960
           ;; Negative power means division in coefficient ring
 
961
           ;; Non-integer power means non-polynomial coefficient
 
962
           (coerce-coeff ring expr vars))
 
963
          (t (poly-expt ring (p-eval (cadr expr)) (caddr expr)))))
 
964
        (otherwise
 
965
         (coerce-coeff ring expr vars)))))))
 
966
 
 
967
 
 
968
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
969
;;
 
970
;; Global optimization/debugging options
 
971
;;
 
972
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
973
 
 
974
 
 
975
;;All inline functions of this module
 
976
(declaim (inline free-of-vars make-pair-queue pair-queue-insert
 
977
                 pair-queue-remove pair-queue-empty-p
 
978
                 pair-queue-remove pair-queue-size Criterion-1
 
979
                 Criterion-2 grobner reduced-grobner sugar-pair-key
 
980
                 sugar-order normal-form normal-form-step grobner-op spoly
 
981
                 equal-test-p
 
982
                 ))
 
983
 
 
984
;;Optimization options
 
985
(declaim (optimize (speed 3) (safety 0)))
 
986
 
 
987
 
 
988
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
989
;;
 
990
;; Debugging/tracing
 
991
;;
 
992
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
993
 
 
994
 
 
995
 
 
996
(defmacro debug-cgb (&rest args)
 
997
  `(when $poly_grobner_debug (format *terminal-io* ,@args)))
 
998
 
 
999
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1000
;;
 
1001
;; An implementation of Grobner basis
 
1002
;;
 
1003
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1004
 
 
1005
(defun spoly (ring f g)
 
1006
  "It yields the S-polynomial of polynomials F and G."
 
1007
  (declare (type poly f g))
 
1008
  (let* ((lcm (monom-lcm (poly-lm f) (poly-lm g)))
 
1009
          (mf (monom-div lcm (poly-lm f)))
 
1010
          (mg (monom-div lcm (poly-lm g))))
 
1011
    (declare (type monom mf mg))
 
1012
    (multiple-value-bind (c cf cg)
 
1013
        (funcall (ring-ezgcd ring) (poly-lc f) (poly-lc g))
 
1014
      (declare (ignore c))
 
1015
      (poly-sub 
 
1016
       ring
 
1017
       (scalar-times-poly ring cg (monom-times-poly mf f))
 
1018
       (scalar-times-poly ring cf (monom-times-poly mg g))))))
 
1019
 
 
1020
 
 
1021
(defun poly-primitive-part (ring p)
 
1022
  "Divide polynomial P with integer coefficients by gcd of its
 
1023
coefficients and return the result."
 
1024
  (declare (type poly p))
 
1025
  (if (poly-zerop p)
 
1026
      (values p 1)
 
1027
    (let ((c (poly-content ring p)))
 
1028
      (values (make-poly-from-termlist (mapcar
 
1029
                          #'(lambda (x)
 
1030
                              (make-term (term-monom x)
 
1031
                                         (funcall (ring-div ring) (term-coeff x) c)))
 
1032
                          (poly-termlist p))
 
1033
                         (poly-sugar p))
 
1034
               c))))
 
1035
 
 
1036
(defun poly-content (ring p)
 
1037
  "Greatest common divisor of the coefficients of the polynomial P. Use the RING structure
 
1038
to compute the greatest common divisor."
 
1039
  (declare (type poly p))
 
1040
  (reduce (ring-gcd ring) (mapcar #'term-coeff (rest (poly-termlist p))) :initial-value (poly-lc p)))
 
1041
 
 
1042
 
 
1043
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1044
;;
 
1045
;; An implementation of the division algorithm
 
1046
;;
 
1047
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1048
 
 
1049
(declaim (ftype (function (ring t t monom poly poly) poly) grobner-op))
 
1050
 
 
1051
(defun grobner-op (ring c1 c2 m f g)
 
1052
  "Returns C2*F-C1*M*G, where F and G are polynomials M is a monomial.
 
1053
Assume that the leading terms will cancel."
 
1054
  #+grobner-check(funcall (ring-zerop ring)
 
1055
                          (funcall (ring-sub ring)
 
1056
                                   (funcall (ring-mul ring) c2 (poly-lc f))
 
1057
                                   (funcall (ring-mul ring) c1 (poly-lc g))))
 
1058
  #+grobner-check(monom-equal-p (poly-lm f) (monom-mul m (poly-lm g)))
 
1059
  (poly-sub ring
 
1060
            (scalar-times-poly ring c2 f)
 
1061
            (scalar-times-poly ring c1 (monom-times-poly m g))))
 
1062
 
 
1063
(defun poly-pseudo-divide (ring f fl)
 
1064
  "Pseudo-divide a polynomial F by the list of polynomials FL. Return
 
1065
multiple values. The first value is a list of quotients A.  The second
 
1066
value is the remainder R. The third argument is a scalar coefficient
 
1067
C, such that C*F can be divided by FL within the ring of coefficients,
 
1068
which is not necessarily a field. Finally, the fourth value is an
 
1069
integer count of the number of reductions performed.  The resulting
 
1070
objects satisfy the equation: C*F= sum A[i]*FL[i] + R."
 
1071
  (declare (type poly f) (list fl))
 
1072
  (do ((r (make-poly-zero))
 
1073
       (c (funcall (ring-unit ring)))
 
1074
       (a (make-list (length fl) :initial-element (make-poly-zero)))
 
1075
       (division-count 0)
 
1076
       (p f))
 
1077
      ((poly-zerop p)
 
1078
       (debug-cgb "~&~3T~d reduction~:p" division-count)
 
1079
       (when (poly-zerop r) (debug-cgb " ---> 0"))
 
1080
       (values (mapcar #'poly-nreverse a) (poly-nreverse r) c division-count))
 
1081
    (declare (fixnum division-count))
 
1082
    (do ((fl fl (rest fl))                              ;scan list of divisors
 
1083
         (b a (rest b)))
 
1084
        ((cond
 
1085
          ((endp fl)                                    ;no division occurred
 
1086
           (push (poly-lt p) (poly-termlist r))         ;move lt(p) to remainder
 
1087
           (setf (poly-sugar r) (max (poly-sugar r) (term-sugar (poly-lt p))))
 
1088
           (pop (poly-termlist p))                      ;remove lt(p) from p
 
1089
           t)
 
1090
          ((monom-divides-p (poly-lm (car fl)) (poly-lm p)) ;division occurred
 
1091
           (incf division-count)
 
1092
           (multiple-value-bind (gcd c1 c2)
 
1093
               (funcall (ring-ezgcd ring) (poly-lc (car fl)) (poly-lc p))
 
1094
             (declare (ignore gcd))
 
1095
             (let ((m (monom-div (poly-lm p) (poly-lm (car fl)))))
 
1096
               ;; Multiply the equation c*f=sum ai*fi+r+p by c1.
 
1097
               (mapl #'(lambda (x)
 
1098
                         (setf (car x) (scalar-times-poly ring c1 (car x))))
 
1099
                     a)
 
1100
               (setf r (scalar-times-poly ring c1 r)
 
1101
                     c (funcall (ring-mul ring) c c1)
 
1102
                     p (grobner-op ring c2 c1 m p (car fl)))
 
1103
               (push (make-term m c2) (poly-termlist (car b))))
 
1104
             t)))))))
 
1105
 
 
1106
(defun poly-exact-divide (ring f g)
 
1107
  "Divide a polynomial F by another polynomial G. Assume that exact division
 
1108
with no remainder is possible. Returns the quotient."
 
1109
  (declare (type poly f g))
 
1110
  (multiple-value-bind (quot rem coeff division-count)
 
1111
      (poly-pseudo-divide ring f (list g))
 
1112
    (declare (ignore division-count coeff)
 
1113
             (type poly quot rem) (type fixnum division-count))
 
1114
    (unless (poly-zerop rem) (error "Exact division failed."))
 
1115
    (car quot)))
 
1116
 
 
1117
 
 
1118
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1119
;;
 
1120
;; An implementation of the normal form
 
1121
;;
 
1122
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1123
 
 
1124
(declaim (ftype (function (ring t poly poly t fixnum)
 
1125
                          (values poly poly t fixnum))
 
1126
                normal-form-step))
 
1127
 
 
1128
(defun normal-form-step (ring fl p r c division-count
 
1129
                         &aux (g (find (poly-lm p) fl
 
1130
                                       :test #'monom-divisible-by-p
 
1131
                                       :key #'poly-lm)))
 
1132
  (cond
 
1133
   (g                                   ;division possible
 
1134
    (incf division-count)
 
1135
    (multiple-value-bind (gcd cg cp)
 
1136
        (funcall (ring-ezgcd ring) (poly-lc g) (poly-lc p))
 
1137
      (declare (ignore gcd))
 
1138
      (let ((m (monom-div (poly-lm p) (poly-lm g))))
 
1139
        ;; Multiply the equation c*f=sum ai*fi+r+p by cg.
 
1140
        (setf r (scalar-times-poly ring cg r)
 
1141
              c (funcall (ring-mul ring) c cg)
 
1142
              p (grobner-op ring cp cg m p g))))
 
1143
    (debug-cgb "/"))
 
1144
   (t                                                   ;no division possible
 
1145
    (push (poly-lt p) (poly-termlist r))                ;move lt(p) to remainder
 
1146
    (setf (poly-sugar r) (max (poly-sugar r) (term-sugar (poly-lt p))))
 
1147
    (pop (poly-termlist p))                             ;remove lt(p) from p
 
1148
    (debug-cgb "+")))
 
1149
  (values p r c division-count))
 
1150
 
 
1151
(declaim (ftype (function (ring poly t &optional t) (values poly t fixnum)) normal-form))
 
1152
 
 
1153
;; Merge it sometime with poly-pseudo-divide
 
1154
(defun normal-form (ring f fl &optional (top-reduction-only $poly_top_reduction_only))
 
1155
  ;; Loop invariant: c*f0=sum ai*fi+r+f, where f0 is the initial value of f
 
1156
  #+grobner-check(when (null fl) (warn "normal-form: empty divisor list."))
 
1157
  (do ((r (make-poly-zero))
 
1158
       (c (funcall (ring-unit ring)))
 
1159
       (division-count 0))
 
1160
      ((or (poly-zerop f)
 
1161
           ;;(endp fl)
 
1162
           (and top-reduction-only (not (poly-zerop r))))
 
1163
       (progn
 
1164
         (debug-cgb "~&~3T~d reduction~:p" division-count)
 
1165
         (when (poly-zerop r)
 
1166
           (debug-cgb " ---> 0")))
 
1167
       (setf (poly-termlist f) (nreconc (poly-termlist r) (poly-termlist f)))
 
1168
       (values f c division-count))
 
1169
    (declare (fixnum division-count)
 
1170
             (type poly r))
 
1171
    (multiple-value-setq (f r c division-count)
 
1172
      (normal-form-step ring fl f r c division-count))))
 
1173
 
 
1174
 
 
1175
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1176
;;
 
1177
;; These are provided mostly for debugging purposes To enable
 
1178
;; verification of grobner bases with BUCHBERGER-CRITERION, do
 
1179
;; (pushnew :grobner-check *features*) and compile/load this file.
 
1180
;; With this feature, the calculations will slow down CONSIDERABLY.
 
1181
;;
 
1182
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1183
 
 
1184
(defun buchberger-criterion (ring G)
 
1185
  "Returns T if G is a Grobner basis, by using the Buchberger
 
1186
criterion: for every two polynomials h1 and h2 in G the S-polynomial
 
1187
S(h1,h2) reduces to 0 modulo G."
 
1188
  (every
 
1189
   #'poly-zerop
 
1190
   (makelist (normal-form ring (spoly ring (elt G i) (elt G j)) G nil)
 
1191
             (i 0 (- (length G) 2))
 
1192
             (j (1+ i) (1- (length G))))))
 
1193
 
 
1194
(defun grobner-test (ring G F)
 
1195
  "Test whether G is a Grobner basis and F is contained in G. Return T
 
1196
upon success and NIL otherwise."
 
1197
  (debug-cgb "~&GROBNER CHECK: ")
 
1198
  (let (($poly_grobner_debug nil)
 
1199
        (stat1 (buchberger-criterion ring G))
 
1200
        (stat2
 
1201
          (every #'poly-zerop
 
1202
                 (makelist (normal-form ring (copy-tree (elt F i)) G nil)
 
1203
                           (i 0 (1- (length F)))))))
 
1204
    (unless stat1 (error "~&Buchberger criterion failed."))
 
1205
    (unless stat2
 
1206
      (error "~&Original polys not in ideal spanned by Grobner.")))
 
1207
  (debug-cgb "~&GROBNER CHECK END")
 
1208
  T)
 
1209
 
 
1210
 
 
1211
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1212
;;
 
1213
;; Pair queue implementation
 
1214
;;
 
1215
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1216
 
 
1217
(defun sugar-pair-key (p q &aux (lcm (monom-lcm (poly-lm p) (poly-lm q)))
 
1218
                                (d (monom-sugar lcm)))
 
1219
  "Returns list (S LCM-TOTAL-DEGREE) where S is the sugar of the S-polynomial of
 
1220
polynomials P and Q, and LCM-TOTAL-DEGREE is the degree of is LCM(LM(P),LM(Q))."
 
1221
  (declare (type poly p q) (type monom lcm) (type fixnum d))
 
1222
  (cons (max 
 
1223
         (+  (- d (monom-sugar (poly-lm p))) (poly-sugar p))
 
1224
         (+  (- d (monom-sugar (poly-lm q))) (poly-sugar q)))
 
1225
        lcm))
 
1226
 
 
1227
(defstruct (pair
 
1228
            (:constructor make-pair (first second
 
1229
                                           &aux
 
1230
                                           (sugar (car (sugar-pair-key first second)))
 
1231
                                           (division-data nil))))
 
1232
  (first nil :type poly)
 
1233
  (second nil :type poly)
 
1234
  (sugar 0 :type fixnum)
 
1235
  (division-data nil :type list))
 
1236
  
 
1237
;;(defun pair-sugar (pair &aux (p (pair-first pair)) (q (pair-second pair)))
 
1238
;;  (car (sugar-pair-key p q)))
 
1239
 
 
1240
(defun sugar-order (x y)
 
1241
  "Pair order based on sugar, ties broken by normal strategy."
 
1242
  (declare (type cons x y))
 
1243
  (or (< (car x) (car y))
 
1244
      (and (= (car x) (car y))
 
1245
           (< (monom-total-degree (cdr x))
 
1246
              (monom-total-degree (cdr y))))))
 
1247
 
 
1248
(defvar *pair-key-function* #'sugar-pair-key
 
1249
  "Function that, given two polynomials as argument, computed the key
 
1250
in the pair queue.")
 
1251
 
 
1252
(defvar *pair-order* #'sugar-order
 
1253
  "Function that orders the keys of pairs.")
 
1254
 
 
1255
(defun make-pair-queue ()
 
1256
  "Constructs a priority queue for critical pairs."
 
1257
  (make-priority-queue
 
1258
   :element-type 'pair
 
1259
   :element-key #'(lambda (pair) (funcall *pair-key-function* (pair-first pair) (pair-second pair)))
 
1260
   :test *pair-order*))
 
1261
 
 
1262
(defun pair-queue-initialize (pq F start
 
1263
                              &aux
 
1264
                              (s (1- (length F)))
 
1265
                              (B (nconc (makelist (make-pair (elt F i) (elt F j))
 
1266
                                                 (i 0 (1- start)) (j start s))
 
1267
                                        (makelist (make-pair (elt F i) (elt F j))
 
1268
                                                 (i start (1- s)) (j (1+ i) s)))))
 
1269
  "Initializes the priority for critical pairs. F is the initial list of polynomials.
 
1270
START is the first position beyond the elements which form a partial
 
1271
grobner basis, i.e. satisfy the Buchberger criterion."
 
1272
  (declare (type priority-queue pq) (type fixnum start))
 
1273
  (dolist (pair B pq)
 
1274
    (priority-queue-insert pq pair)))
 
1275
 
 
1276
(defun pair-queue-insert (B pair)
 
1277
  (priority-queue-insert B pair))
 
1278
 
 
1279
(defun pair-queue-remove (B)
 
1280
  (priority-queue-remove B))
 
1281
 
 
1282
(defun pair-queue-size (B)
 
1283
  (priority-queue-size B))
 
1284
 
 
1285
(defun pair-queue-empty-p (B)
 
1286
  (priority-queue-empty-p B))
 
1287
 
 
1288
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1289
;;
 
1290
;; Buchberger Algorithm Implementation
 
1291
;;
 
1292
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1293
 
 
1294
(defun buchberger (ring F start &optional (top-reduction-only $poly_top_reduction_only)
 
1295
                   &aux B B-done)
 
1296
  "An implementation of the Buchberger algorithm. Return Grobner basis
 
1297
of the ideal generated by the polynomial list F.  Polynomials 0 to
 
1298
START-1 are assumed to be a Grobner basis already, so that certain
 
1299
critical pairs will not be examined. If TOP-REDUCTION-ONLY set, top
 
1300
reduction will be preformed. This function assumes that all polynomials
 
1301
in F are non-zero."
 
1302
  (declare (type fixnum start) (type priority-queue B) (type hash-table B-done))
 
1303
  (when (endp F) (return-from buchberger F)) ;cut startup costs
 
1304
  (debug-cgb "~&GROBNER BASIS - BUCHBERGER ALGORITHM")
 
1305
  (when (plusp start) (debug-cgb "~&INCREMENTAL:~d done" start))
 
1306
  #+grobner-check  (when (plusp start)
 
1307
                     (grobner-test ring (subseq F 0 start) (subseq F 0 start)))
 
1308
  ;;Initialize critical pairs
 
1309
  (setf B (pair-queue-initialize (make-pair-queue)
 
1310
                                 F start)
 
1311
        B-done (make-hash-table :test #'equal))
 
1312
  (dotimes (i (1- start))
 
1313
    (do ((j (1+ i) (1+ j))) ((>= j start))
 
1314
      (setf (gethash (list (elt F i) (elt F j)) B-done) t)))
 
1315
  (do ()
 
1316
      ((pair-queue-empty-p B)
 
1317
       #+grobner-check(grobner-test ring F F)
 
1318
       (debug-cgb "~&GROBNER END")
 
1319
       F)
 
1320
    (let ((pair (pair-queue-remove B)))
 
1321
      (declare (type pair pair))
 
1322
      (cond
 
1323
       ((Criterion-1 pair) nil)
 
1324
       ((Criterion-2 pair B-done F) nil)
 
1325
       (t 
 
1326
        (let ((SP (normal-form ring
 
1327
                   (spoly ring (pair-first pair) (pair-second pair))
 
1328
                   F top-reduction-only)))
 
1329
          (declare (type poly SP))
 
1330
          (cond
 
1331
           ((poly-zerop SP)
 
1332
            nil)
 
1333
           (t
 
1334
            (setf SP (poly-primitive-part ring SP)
 
1335
                  F (nconc F (list SP)))
 
1336
            ;; Add new critical pairs
 
1337
            (dolist (h F)
 
1338
              (pair-queue-insert B (make-pair h SP)))
 
1339
            (debug-cgb "~&Sugar: ~d Polynomials: ~d; Pairs left: ~d; Pairs done: ~d;"
 
1340
                       (pair-sugar pair) (length F) (pair-queue-size B)
 
1341
                       (hash-table-count B-done)))))))
 
1342
      (setf (gethash (list (pair-first pair) (pair-second pair)) B-done) t))))
 
1343
 
 
1344
(defun parallel-buchberger (ring F start &optional (top-reduction-only $poly_top_reduction_only)
 
1345
                            &aux B B-done)
 
1346
  "An implementation of the Buchberger algorithm. Return Grobner basis
 
1347
of the ideal generated by the polynomial list F.  Polynomials 0 to
 
1348
START-1 are assumed to be a Grobner basis already, so that certain
 
1349
critical pairs will not be examined. If TOP-REDUCTION-ONLY set, top
 
1350
reduction will be preformed."
 
1351
  (declare (ignore top-reduction-only)
 
1352
           (type fixnum start)
 
1353
           (type priority-queue B)
 
1354
           (type hash-table B-done))
 
1355
  (when (endp F) (return-from parallel-buchberger F)) ;cut startup costs
 
1356
  (debug-cgb "~&GROBNER BASIS - PARALLEL-BUCHBERGER ALGORITHM")
 
1357
  (when (plusp start) (debug-cgb "~&INCREMENTAL:~d done" start))
 
1358
  #+grobner-check  (when (plusp start)
 
1359
                     (grobner-test ring (subseq F 0 start) (subseq F 0 start)))
 
1360
  ;;Initialize critical pairs
 
1361
  (setf B (pair-queue-initialize (make-pair-queue)
 
1362
                                 F start)
 
1363
        B-done (make-hash-table :test #'equal))
 
1364
  (dotimes (i (1- start))
 
1365
    (do ((j (1+ i) (1+ j))) ((>= j start))
 
1366
      (declare (type fixnum j))
 
1367
      (setf (gethash (list (elt F i) (elt F j)) B-done) t)))
 
1368
  (do ()
 
1369
      ((pair-queue-empty-p B)
 
1370
       #+grobner-check(grobner-test ring F F)
 
1371
       (debug-cgb "~&GROBNER END")
 
1372
       F)
 
1373
    (let ((pair (pair-queue-remove B)))
 
1374
      (when (null (pair-division-data pair))
 
1375
        (setf (pair-division-data pair) (list (spoly ring
 
1376
                                                     (pair-first pair)
 
1377
                                                     (pair-second pair))
 
1378
                                              (make-poly-zero)
 
1379
                                              (funcall (ring-unit ring))
 
1380
                                              0)))
 
1381
      (cond
 
1382
       ((Criterion-1 pair) nil)
 
1383
       ((Criterion-2 pair B-done F) nil)
 
1384
       (t 
 
1385
        (let* ((dd (pair-division-data pair))
 
1386
               (p (first dd))
 
1387
               (SP (second dd))
 
1388
               (c (third dd))
 
1389
               (division-count (fourth dd)))
 
1390
          (cond
 
1391
           ((poly-zerop p)              ;normal form completed
 
1392
            (debug-cgb "~&~3T~d reduction~:p" division-count)
 
1393
            (cond 
 
1394
             ((poly-zerop SP)
 
1395
              (debug-cgb " ---> 0")
 
1396
              nil)
 
1397
             (t
 
1398
              (setf SP (poly-nreverse SP)
 
1399
                    SP (poly-primitive-part ring SP)
 
1400
                    F (nconc F (list SP)))
 
1401
              ;; Add new critical pairs
 
1402
              (dolist (h F)
 
1403
                (pair-queue-insert B (make-pair h SP)))
 
1404
              (debug-cgb "~&Sugar: ~d Polynomials: ~d; Pairs left: ~d; Pairs done: ~d;"
 
1405
                         (pair-sugar pair) (length F) (pair-queue-size B)
 
1406
                         (hash-table-count B-done))))
 
1407
            (setf (gethash (list (pair-first pair) (pair-second pair)) B-done) t))
 
1408
           (t                           ;normal form not complete
 
1409
            (do ()
 
1410
                ((cond
 
1411
                  ((> (poly-sugar SP) (pair-sugar pair))
 
1412
                   (debug-cgb "(~a)?" (poly-sugar SP))
 
1413
                   t)
 
1414
                  ((poly-zerop p)
 
1415
                   (debug-cgb ".")
 
1416
                   t)
 
1417
                  (t nil))
 
1418
                 (setf (first dd) p
 
1419
                       (second dd) SP
 
1420
                       (third dd) c
 
1421
                       (fourth dd) division-count
 
1422
                       (pair-sugar pair) (poly-sugar SP))
 
1423
                 (pair-queue-insert B pair))
 
1424
              (multiple-value-setq (p SP c division-count)
 
1425
                (normal-form-step ring F p SP c division-count)))))))))))
 
1426
 
 
1427
 
 
1428
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1429
;;
 
1430
;; Grobner Criteria
 
1431
;;
 
1432
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1433
 
 
1434
(defun Criterion-1 (pair &aux (f (pair-first pair)) (g (pair-second pair)))
 
1435
  "Returns T if the leading monomials of the two polynomials
 
1436
in G pointed to by the integers in PAIR have disjoint (relatively prime)
 
1437
monomials. This test is known as the first Buchberger criterion."
 
1438
  (declare (type pair pair))
 
1439
  (when (monom-rel-prime-p (poly-lm f) (poly-lm g))
 
1440
    (debug-cgb ":1")
 
1441
    (return-from Criterion-1 t)))
 
1442
 
 
1443
(defun Criterion-2 (pair B-done partial-basis
 
1444
                    &aux (f (pair-first pair)) (g (pair-second pair))
 
1445
                         (place :before))
 
1446
  "Returns T if the leading monomial of some element P of
 
1447
PARTIAL-BASIS divides the LCM of the leading monomials of the two
 
1448
polynomials in the polynomial list PARTIAL-BASIS, and P paired with
 
1449
each of the polynomials pointed to by the the PAIR has already been
 
1450
treated, as indicated by the absence in the hash table B-done."
 
1451
  (declare (type pair pair) (type hash-table B-done)
 
1452
           (type poly f g))
 
1453
  ;; In the code below we assume that pairs are ordered as follows: 
 
1454
  ;; if PAIR is (I J) then I appears before J in the PARTIAL-BASIS.
 
1455
  ;; We traverse the list PARTIAL-BASIS and keep track of where we
 
1456
  ;; are, so that we can produce the pairs in the correct order
 
1457
  ;; when we check whether they have been processed, i.e they
 
1458
  ;; appear in the hash table B-done
 
1459
  (dolist (h partial-basis nil)
 
1460
    (cond
 
1461
     ((eq h f)
 
1462
      #+grobner-check(assert (eq place :before))
 
1463
      (setf place :in-the-middle))
 
1464
     ((eq h g)
 
1465
      #+grobner-check(assert (eq place :in-the-middle))
 
1466
      (setf place :after))
 
1467
     ((and (monom-divides-monom-lcm-p (poly-lm h) (poly-lm f) (poly-lm g))
 
1468
           (gethash (case place
 
1469
                      (:before (list h f))
 
1470
                      ((:in-the-middle :after) (list f h)))
 
1471
                    B-done)
 
1472
           (gethash (case place
 
1473
                      ((:before :in-the-middle) (list h g))
 
1474
                      (:after (list g h)))
 
1475
                    B-done))
 
1476
      (debug-cgb ":2")
 
1477
      (return-from Criterion-2 t)))))
 
1478
 
 
1479
 
 
1480
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1481
;;
 
1482
;; An implementation of the algorithm of Gebauer and Moeller, as
 
1483
;; described in the book of Becker-Weispfenning, p. 232
 
1484
;;
 
1485
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1486
 
 
1487
(defun gebauer-moeller (ring F start &optional (top-reduction-only $poly_top_reduction_only)
 
1488
                        &aux B G F1)
 
1489
  "Compute Grobner basis by using the algorithm of Gebauer and
 
1490
Moeller.  This algorithm is described as BUCHBERGERNEW2 in the book by
 
1491
Becker-Weispfenning entitled ``Grobner Bases''. This function assumes
 
1492
that all polynomials in F are non-zero."
 
1493
  (declare (ignore top-reduction-only)
 
1494
           (type fixnum start)
 
1495
           (type priority-queue B))
 
1496
  (cond
 
1497
   ((endp F) (return-from gebauer-moeller nil))
 
1498
   ((endp (cdr F))
 
1499
    (return-from gebauer-moeller (list (poly-primitive-part ring (car F))))))
 
1500
   (debug-cgb "~&GROBNER BASIS - GEBAUER MOELLER ALGORITHM")
 
1501
   (when (plusp start) (debug-cgb "~&INCREMENTAL:~d done" start))
 
1502
  #+grobner-check  (when (plusp start)
 
1503
                     (grobner-test ring (subseq F 0 start) (subseq F 0 start)))
 
1504
  (setf B (make-pair-queue)
 
1505
        G (subseq F 0 start)
 
1506
        F1 (subseq F start))
 
1507
  (do () ((endp F1))
 
1508
    (multiple-value-setq (G B)
 
1509
      (gebauer-moeller-update G B (poly-primitive-part ring (pop F1)))))
 
1510
  (do () ((pair-queue-empty-p B))
 
1511
    (let* ((pair (pair-queue-remove B))
 
1512
           (g1 (pair-first pair))
 
1513
           (g2 (pair-second pair))
 
1514
           (h (normal-form ring (spoly ring g1 g2)
 
1515
                           G
 
1516
                           nil #| Always fully reduce! |#
 
1517
                           )))
 
1518
      (unless (poly-zerop h)
 
1519
        (setf h (poly-primitive-part ring h))
 
1520
        (multiple-value-setq (G B)
 
1521
          (gebauer-moeller-update G B h))
 
1522
        (debug-cgb "~&Sugar: ~d Polynomials: ~d; Pairs left: ~d~%"
 
1523
                   (pair-sugar pair) (length G) (pair-queue-size B))
 
1524
        )))
 
1525
  #+grobner-check(grobner-test ring G F)
 
1526
  (debug-cgb "~&GROBNER END")
 
1527
  G)
 
1528
 
 
1529
(defun gebauer-moeller-update (G B h
 
1530
                 &aux
 
1531
                 C D E
 
1532
                 (B-new (make-pair-queue))
 
1533
                 G-new
 
1534
                 pair)
 
1535
  "An implementation of the auxillary UPDATE algorithm used by the
 
1536
Gebauer-Moeller algorithm. G is a list of polynomials, B is a list of
 
1537
critical pairs and H is a new polynomial which possibly will be added
 
1538
to G. The naming conventions used are very close to the one used in
 
1539
the book of Becker-Weispfenning."
 
1540
  (declare
 
1541
   #+allegro (dynamic-extent B)
 
1542
   (type poly h)
 
1543
   (type priority-queue B)
 
1544
   (type pair pair))
 
1545
  (setf C G D nil) 
 
1546
  (do (g1) ((endp C))
 
1547
    (declare (type poly g1))
 
1548
    (setf g1 (pop C))
 
1549
    (when (or (monom-rel-prime-p (poly-lm h) (poly-lm g1))
 
1550
              (and
 
1551
               (notany #'(lambda (g2) (monom-lcm-divides-monom-lcm-p
 
1552
                                       (poly-lm h) (poly-lm g2)
 
1553
                                       (poly-lm h) (poly-lm g1)))
 
1554
                       C)
 
1555
               (notany #'(lambda (g2) (monom-lcm-divides-monom-lcm-p
 
1556
                                       (poly-lm h) (poly-lm g2)
 
1557
                                       (poly-lm h) (poly-lm g1)))
 
1558
                       D)))
 
1559
      (push g1 D)))
 
1560
  (setf E nil)
 
1561
  (do (g1) ((endp D))
 
1562
    (declare (type poly g1))
 
1563
    (setf g1 (pop D))
 
1564
    (unless (monom-rel-prime-p (poly-lm h) (poly-lm g1))
 
1565
      (push g1 E)))
 
1566
  (do (g1 g2) ((pair-queue-empty-p B))
 
1567
    (declare (type poly g1 g2))
 
1568
    (setf pair (pair-queue-remove B)
 
1569
          g1 (pair-first pair)
 
1570
          g2 (pair-second pair))
 
1571
    (when (or (not (monom-divides-monom-lcm-p
 
1572
                    (poly-lm h)
 
1573
                    (poly-lm g1) (poly-lm g2)))
 
1574
              (monom-lcm-equal-monom-lcm-p
 
1575
               (poly-lm g1) (poly-lm h)
 
1576
               (poly-lm g1) (poly-lm g2))
 
1577
              (monom-lcm-equal-monom-lcm-p
 
1578
               (poly-lm h) (poly-lm g2)
 
1579
               (poly-lm g1) (poly-lm g2)))
 
1580
      (pair-queue-insert B-new (make-pair g1 g2))))
 
1581
  (dolist (g3 E)
 
1582
    (pair-queue-insert B-new (make-pair h g3)))
 
1583
  (setf G-new nil)
 
1584
  (do (g1) ((endp G))
 
1585
    (declare (type poly g1))
 
1586
    (setf g1 (pop G))
 
1587
    (unless (monom-divides-p (poly-lm h) (poly-lm g1))
 
1588
      (push g1 G-new)))
 
1589
  (push h G-new)
 
1590
  (values G-new B-new))
 
1591
 
 
1592
 
 
1593
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1594
;;
 
1595
;; Standard postprocessing of Grobner bases
 
1596
;;
 
1597
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1598
 
 
1599
(defun reduction (ring plist)
 
1600
  "Reduce a list of polynomials PLIST, so that non of the terms in any of
 
1601
the polynomials is divisible by a leading monomial of another
 
1602
polynomial.  Return the reduced list."
 
1603
  (do ((Q plist)
 
1604
       (found t))
 
1605
      ((not found)
 
1606
       (mapcar #'(lambda (x) (poly-primitive-part ring x)) Q))
 
1607
    ;;Find p in Q such that p is reducible mod Q\{p}
 
1608
    (setf found nil)
 
1609
    (dolist (x Q)
 
1610
      (let ((Q1 (remove x Q)))
 
1611
        (multiple-value-bind (h c div-count)
 
1612
            (normal-form ring x Q1 nil #| not a top reduction! |# )
 
1613
          (declare (ignore c))
 
1614
          (unless (zerop div-count)
 
1615
            (setf found t Q Q1)
 
1616
            (unless (poly-zerop h)
 
1617
              (setf Q (nconc Q1 (list h))))
 
1618
            (return)))))))
 
1619
 
 
1620
(defun minimization (P)
 
1621
  "Returns a sublist of the polynomial list P spanning the same
 
1622
monomial ideal as P but minimal, i.e. no leading monomial
 
1623
of a polynomial in the sublist divides the leading monomial
 
1624
of another polynomial."
 
1625
  (do ((Q P)
 
1626
       (found t))
 
1627
      ((not found) Q)
 
1628
    ;;Find p in Q such that lm(p) is in LM(Q\{p})
 
1629
    (setf found nil
 
1630
          Q (dolist (x Q Q)
 
1631
              (let ((Q1 (remove x Q)))
 
1632
                (when (member-if #'(lambda (p) (monom-divides-p (poly-lm x) (poly-lm p))) Q1)
 
1633
                  (setf found t)
 
1634
                  (return Q1)))))))
 
1635
 
 
1636
(defun poly-normalize (ring p &aux (c (poly-lc p)))
 
1637
  "Divide a polynomial by its leading coefficient. It assumes
 
1638
that the division is possible, which may not always be the
 
1639
case in rings which are not fields. The exact division operator
 
1640
is assumed to be provided by the RING structure of the
 
1641
COEFFICIENT-RING package."
 
1642
  (mapc #'(lambda (term)
 
1643
            (setf (term-coeff term) (funcall (ring-div ring) (term-coeff term) c)))
 
1644
        (poly-termlist p))
 
1645
  p)
 
1646
 
 
1647
(defun poly-normalize-list (ring plist)
 
1648
  "Divide every polynomial in a list PLIST by its leading coefficient. "
 
1649
  (mapcar #'(lambda (x) (poly-normalize ring x)) plist))
 
1650
 
 
1651
 
 
1652
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1653
;;
 
1654
;; Algorithm and Pair heuristic selection
 
1655
;;
 
1656
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1657
 
 
1658
(defun find-grobner-function (algorithm)
 
1659
  "Return a function which calculates Grobner basis, based on its
 
1660
names. Names currently used are either Lisp symbols, Maxima symbols or
 
1661
keywords."
 
1662
  (ecase algorithm
 
1663
    ((buchberger :buchberger $buchberger) #'buchberger)
 
1664
    ((parallel-buchberger :parallel-buchberger $parallel_buchberger) #'parallel-buchberger)
 
1665
    ((gebauer-moeller :gebauer_moeller $gebauer_moeller) #'gebauer-moeller)))
 
1666
 
 
1667
(defun grobner (ring F &optional (start 0) (top-reduction-only nil))
 
1668
  ;;(setf F (sort F #'< :key #'sugar))
 
1669
  (funcall
 
1670
   (find-grobner-function $poly_grobner_algorithm)
 
1671
   ring F start top-reduction-only))
 
1672
 
 
1673
(defun reduced-grobner (ring F &optional (start 0) (top-reduction-only $poly_top_reduction_only))
 
1674
  (reduction ring (grobner ring F start top-reduction-only)))
 
1675
 
 
1676
(defun set-pair-heuristic (method)
 
1677
  "Sets up variables *PAIR-KEY-FUNCTION* and *PAIR-ORDER* used
 
1678
to determine the priority of critical pairs in the priority queue."
 
1679
  (ecase method
 
1680
    ((sugar :sugar $sugar)
 
1681
     (setf *pair-key-function* #'sugar-pair-key
 
1682
           *pair-order* #'sugar-order))
 
1683
;     ((minimal-mock-spoly :minimal-mock-spoly $minimal_mock_spoly)
 
1684
;      (setf *pair-key-function* #'mock-spoly
 
1685
;          *pair-order* #'mock-spoly-order))
 
1686
    ((minimal-lcm :minimal-lcm $minimal_lcm)
 
1687
     (setf *pair-key-function* #'(lambda (p q)
 
1688
                                   (monom-lcm (poly-lm p) (poly-lm q)))
 
1689
           *pair-order* #'reverse-monomial-order))
 
1690
    ((minimal-total-degree :minimal-total-degree $minimal_total_degree)
 
1691
     (setf *pair-key-function* #'(lambda (p q)
 
1692
                                   (monom-total-degree
 
1693
                                    (monom-lcm (poly-lm p) (poly-lm q))))
 
1694
           *pair-order* #'<))
 
1695
    ((minimal-length :minimal-length $minimal_length)
 
1696
     (setf *pair-key-function* #'(lambda (p q)
 
1697
                                   (+ (poly-length p) (poly-length q)))
 
1698
           *pair-order* #'<))))
 
1699
 
 
1700
 
 
1701
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1702
;;
 
1703
;; Operations in ideal theory
 
1704
;;
 
1705
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1706
 
 
1707
;; Does the term depend on variable K?
 
1708
(defun term-depends-p (term k)
 
1709
  "Return T if the term TERM depends on variable number K."
 
1710
  (monom-depends-p (term-monom term) k))
 
1711
 
 
1712
;; Does the polynomial P depend on variable K?
 
1713
(defun poly-depends-p (p k)
 
1714
  "Return T if the term polynomial P depends on variable number K."
 
1715
  (some #'(lambda (term) (term-depends-p term k)) (poly-termlist p)))
 
1716
 
 
1717
(defun ring-intersection (plist k)
 
1718
  "This function assumes that polynomial list PLIST is a Grobner basis
 
1719
and it calculates the intersection with the ring R[x[k+1],...,x[n]], i.e.
 
1720
it discards polynomials which depend on variables x[0], x[1], ..., x[k]."
 
1721
  (dotimes (i k plist)
 
1722
    (setf plist
 
1723
          (remove-if #'(lambda (p)
 
1724
                         (poly-depends-p p i))
 
1725
                     plist))))
 
1726
 
 
1727
(defun elimination-ideal (ring flist k
 
1728
                          &optional (top-reduction-only $poly_top_reduction_only) (start 0)
 
1729
                          &aux (*monomial-order*
 
1730
                                (or *elimination-order*
 
1731
                                    (elimination-order k))))
 
1732
  (ring-intersection (reduced-grobner ring flist start top-reduction-only) k))
 
1733
 
 
1734
(defun colon-ideal (ring F G &optional (top-reduction-only $poly_top_reduction_only))
 
1735
  "Returns the reduced Grobner basis of the colon ideal Id(F):Id(G),
 
1736
where F and G are two lists of polynomials. The colon ideal I:J is
 
1737
defined as the set of polynomials H such that for all polynomials W in
 
1738
J the polynomial W*H belongs to I."
 
1739
  (cond
 
1740
   ((endp G)
 
1741
    ;;Id(G) consists of 0 only so W*0=0 belongs to Id(F)
 
1742
    (if (every #'poly-zerop F)
 
1743
        (error "First ideal must be non-zero.")
 
1744
      (list (make-poly
 
1745
             (list (make-term
 
1746
                    (make-monom (monom-dimension (poly-lm (find-if-not #'poly-zerop F)))
 
1747
                                :initial-element 0)
 
1748
                    (funcall (ring-unit ring))))))))
 
1749
   ((endp (cdr G))
 
1750
    (colon-ideal-1 ring F (car G) top-reduction-only))
 
1751
   (t
 
1752
    (ideal-intersection ring
 
1753
                        (colon-ideal-1 ring F (car G) top-reduction-only)
 
1754
                        (colon-ideal ring F (rest G) top-reduction-only)
 
1755
                        top-reduction-only))))
 
1756
 
 
1757
(defun colon-ideal-1 (ring F g &optional (top-reduction-only $poly_top_reduction_only))
 
1758
  "Returns the reduced Grobner basis of the colon ideal Id(F):Id({G}), where
 
1759
F is a list of polynomials and G is a polynomial."
 
1760
  (mapcar #'(lambda (x) (poly-exact-divide ring x g)) (ideal-intersection ring F (list g) top-reduction-only)))
 
1761
 
 
1762
 
 
1763
(defun ideal-intersection (ring F G &optional (top-reduction-only $poly_top_reduction_only)
 
1764
                           &aux (*monomial-order* (or *elimination-order*
 
1765
                                                      #'elimination-order-1)))
 
1766
  (mapcar #'poly-contract
 
1767
          (ring-intersection
 
1768
           (reduced-grobner
 
1769
            ring
 
1770
            (append (mapcar #'(lambda (p) (poly-extend p (list 1))) F)
 
1771
                    (mapcar #'(lambda (p)
 
1772
                                (poly-append (poly-extend (poly-uminus ring p) (list 1))
 
1773
                                             (poly-extend p)))
 
1774
                            G))
 
1775
            0
 
1776
            top-reduction-only)
 
1777
           1)))
 
1778
 
 
1779
(defun poly-lcm (ring f g)
 
1780
  "Return LCM (least common multiple) of two polynomials F and G.
 
1781
The polynomials must be ordered according to monomial order PRED
 
1782
and their coefficients must be compatible with the RING structure
 
1783
defined in the COEFFICIENT-RING package."
 
1784
  (cond
 
1785
    ((poly-zerop f) f)
 
1786
    ((poly-zerop g) g)
 
1787
    ((and (endp (cdr (poly-termlist f))) (endp (cdr (poly-termlist g))))
 
1788
     (let ((m (monom-lcm (poly-lm f) (poly-lm g))))
 
1789
       (make-poly-from-termlist (list (make-term m (funcall (ring-lcm ring) (poly-lc f) (poly-lc g)))))))
 
1790
    (t
 
1791
     (multiple-value-bind (f f-cont)
 
1792
         (poly-primitive-part ring f)
 
1793
       (multiple-value-bind (g g-cont)
 
1794
           (poly-primitive-part ring g)
 
1795
         (scalar-times-poly
 
1796
          ring
 
1797
          (funcall (ring-lcm ring) f-cont g-cont)
 
1798
          (poly-primitive-part ring (car (ideal-intersection ring (list f) (list g) nil)))))))))
 
1799
 
 
1800
;; Do two Grobner bases yield the same ideal?
 
1801
(defun grobner-equal (ring G1 G2)
 
1802
  "Returns T if two lists of polynomials G1 and G2, assumed to be Grobner bases,
 
1803
generate  the same ideal, and NIL otherwise."
 
1804
  (and (grobner-subsetp ring G1 G2) (grobner-subsetp ring G2 G1)))
 
1805
 
 
1806
(defun grobner-subsetp (ring G1 G2)
 
1807
  "Returns T if a list of polynomials G1 generates
 
1808
an ideal contained in the ideal generated by a polynomial list G2,
 
1809
both G1 and G2 assumed to be Grobner bases. Returns NIL otherwise."
 
1810
  (every #'(lambda (p) (grobner-member ring p G2)) G1))
 
1811
 
 
1812
(defun grobner-member (ring p G)
 
1813
  "Returns T if a polynomial P belongs to the ideal generated by the
 
1814
polynomial list G, which is assumed to be a Grobner basis. Returns NIL otherwise."
 
1815
  (poly-zerop (normal-form ring p G nil)))
 
1816
 
 
1817
;; Calculate F : p^inf
 
1818
(defun ideal-saturation-1 (ring F p start &optional (top-reduction-only $poly_top_reduction_only)
 
1819
                           &aux (*monomial-order* (or *elimination-order*
 
1820
                                                      #'elimination-order-1)))
 
1821
  "Returns the reduced Grobner basis of the saturation of the ideal
 
1822
generated by a polynomial list F in the ideal generated by a single
 
1823
polynomial P. The saturation ideal is defined as the set of
 
1824
polynomials H such for some natural number n (* (EXPT P N) H) is in the ideal
 
1825
F. Geometrically, over an algebraically closed field, this is the set
 
1826
of polynomials in the ideal generated by F which do not identically
 
1827
vanish on the variety of P."
 
1828
  (mapcar
 
1829
   #'poly-contract
 
1830
   (ring-intersection
 
1831
    (reduced-grobner
 
1832
     ring
 
1833
     (saturation-extension-1 ring F p)
 
1834
     start top-reduction-only)
 
1835
    1)))
 
1836
 
 
1837
 
 
1838
 
 
1839
;; Calculate F : p1^inf : p2^inf : ... : ps^inf
 
1840
(defun ideal-polysaturation-1 (ring F plist start &optional (top-reduction-only $poly_top_reduction_only))
 
1841
  "Returns the reduced Grobner basis of the ideal obtained by a
 
1842
sequence of successive saturations in the polynomials
 
1843
of the polynomial list PLIST of the ideal generated by the
 
1844
polynomial list F."
 
1845
  (cond
 
1846
   ((endp plist) (reduced-grobner ring F start top-reduction-only))
 
1847
   (t (let ((G (ideal-saturation-1 ring F (car plist) start top-reduction-only)))
 
1848
        (ideal-polysaturation-1 ring G (rest plist) (length G) top-reduction-only)))))
 
1849
 
 
1850
(defun ideal-saturation (ring F G start &optional (top-reduction-only $poly_top_reduction_only)
 
1851
                         &aux
 
1852
                         (k (length G))
 
1853
                         (*monomial-order* (or *elimination-order*
 
1854
                                               (elimination-order k))))
 
1855
  "Returns the reduced Grobner basis of the saturation of the ideal
 
1856
generated by a polynomial list F in the ideal generated a polynomial
 
1857
list G. The saturation ideal is defined as the set of polynomials H
 
1858
such for some natural number n and some P in the ideal generated by G
 
1859
the polynomial P**N * H is in the ideal spanned by F.  Geometrically,
 
1860
over an algebraically closed field, this is the set of polynomials in
 
1861
the ideal generated by F which do not identically vanish on the
 
1862
variety of G."
 
1863
  (mapcar
 
1864
   #'(lambda (q) (poly-contract q k))
 
1865
   (ring-intersection
 
1866
    (reduced-grobner ring
 
1867
                     (polysaturation-extension ring F G)
 
1868
                     start
 
1869
                     top-reduction-only)
 
1870
    k)))
 
1871
 
 
1872
(defun ideal-polysaturation (ring F ideal-list start &optional (top-reduction-only $poly_top_reduction_only))
 
1873
    "Returns the reduced Grobner basis of the ideal obtained by a
 
1874
successive applications of IDEAL-SATURATION to F and lists of
 
1875
polynomials in the list IDEAL-LIST."
 
1876
  (cond
 
1877
   ((endp ideal-list) F)
 
1878
   (t (let ((H (ideal-saturation ring F (car ideal-list) start top-reduction-only)))
 
1879
        (ideal-polysaturation ring H (rest ideal-list) (length H) top-reduction-only)))))
 
1880
 
 
1881
 
 
1882
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1883
;;
 
1884
;; Set up the coefficients to be polynomials
 
1885
;;
 
1886
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1887
 
 
1888
;; (defun poly-ring (ring vars)
 
1889
;;   (make-ring 
 
1890
;;    :parse #'(lambda (expr) (poly-eval ring expr vars))
 
1891
;;    :unit #'(lambda () (poly-unit ring (length vars)))
 
1892
;;    :zerop #'poly-zerop
 
1893
;;    :add #'(lambda (x y) (poly-add ring x y))
 
1894
;;    :sub #'(lambda (x y) (poly-sub ring x y))
 
1895
;;    :uminus #'(lambda (x) (poly-uminus ring x))
 
1896
;;    :mul #'(lambda (x y) (poly-mul ring x y))
 
1897
;;    :div #'(lambda (x y) (poly-exact-divide ring x y))
 
1898
;;    :lcm #'(lambda (x y) (poly-lcm ring x y))
 
1899
;;    :ezgcd #'(lambda (x y &aux (gcd (poly-gcd ring x y)))
 
1900
;;            (values gcd
 
1901
;;                    (poly-exact-divide ring x gcd)
 
1902
;;                    (poly-exact-divide ring y gcd)))
 
1903
;;    :gcd #'(lambda (x y) (poly-gcd x y))))
 
1904
 
 
1905
 
 
1906
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1907
;;
 
1908
;; Conversion from internal to infix form
 
1909
;;
 
1910
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1911
 
 
1912
(defun coerce-to-infix (poly-type object vars)
 
1913
  (case poly-type
 
1914
    (:termlist
 
1915
     `(+ ,@(mapcar #'(lambda (term) (coerce-to-infix :term term vars)) object)))
 
1916
    (:polynomial
 
1917
     (coerce-to-infix :termlist (poly-termlist object) vars))
 
1918
    (:poly-list
 
1919
     `([ ,@(mapcar #'(lambda (p) (coerce-to-infix :polynomial p vars)) object)))
 
1920
    (:term
 
1921
     `(* ,(term-coeff object)
 
1922
         ,@(mapcar #'(lambda (var power) `(expt ,var ,power))
 
1923
                   vars (monom-exponents (term-monom object)))))
 
1924
    (otherwise
 
1925
     object)))
 
1926
 
 
1927
 
 
1928
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1929
;;
 
1930
;; Maxima expression ring
 
1931
;;
 
1932
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1933
 
 
1934
(defparameter *ExpressionRing*
 
1935
    (make-ring 
 
1936
     ;;(defun coeff-zerop (expr) (meval1 `(($is) (($equal) ,expr 0))))
 
1937
     :parse #'(lambda (expr)
 
1938
                (when modulus (setf expr ($rat expr)))
 
1939
                expr)
 
1940
     :unit #'(lambda () (if modulus ($rat 1) 1))
 
1941
     :zerop #'(lambda (expr)
 
1942
                ;;When is exactly a maxima expression equal to 0?
 
1943
                (cond ((numberp expr)
 
1944
                       (= expr 0))
 
1945
                      ((atom expr) nil)
 
1946
                      (t
 
1947
                       (case (caar expr)
 
1948
                         (mrat (eql ($ratdisrep expr) 0))
 
1949
                         (otherwise (eql ($totaldisrep expr) 0))))))
 
1950
     :add #'(lambda (x y) (m+ x y))
 
1951
     :sub #'(lambda (x y) (m- x y))
 
1952
     :uminus #'(lambda (x) (m- x))
 
1953
     :mul #'(lambda (x y) (m* x y))
 
1954
     ;;(defun coeff-div (x y) (cadr ($divide x y)))
 
1955
     :div #'(lambda (x y) (m// x y))
 
1956
     :lcm #'(lambda (x y) (meval1 `((|$lcm|) ,x ,y)))
 
1957
     :ezgcd #'(lambda (x y) (apply #'values (cdr ($ezgcd x y))))
 
1958
     :gcd #'(lambda (x y) (second ($ezgcd x y)))))
 
1959
 
 
1960
(defvar *MaximaRing* *ExpressionRing*
 
1961
  "The ring of coefficients, over which all polynomials 
 
1962
are assumed to be defined.")
 
1963
 
 
1964
 
 
1965
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1966
;;
 
1967
;; Maxima expression parsing
 
1968
;;
 
1969
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
1970
 
 
1971
(defun equal-test-p (expr1 expr2)
 
1972
  (alike1 expr1 expr2))
 
1973
 
 
1974
(defun coerce-maxima-list (expr)
 
1975
  "Convert a Maxima list to Lisp list."
 
1976
  (cond
 
1977
   ((and (consp (car expr)) (eql (caar expr) 'mlist)) (cdr expr))
 
1978
   (t expr)))
 
1979
 
 
1980
(defun free-of-vars (expr vars) (apply #'$freeof `(,@vars ,expr)))
 
1981
 
 
1982
(defun parse-poly (expr vars &aux (vars (coerce-maxima-list vars)))
 
1983
  "Convert a maxima polynomial expression EXPR in variables VARS to internal form."
 
1984
  (labels ((parse (arg) (parse-poly arg vars))
 
1985
           (parse-list (args) (mapcar #'parse args)))
 
1986
    (cond
 
1987
     ((eql expr 0) (make-poly-zero))
 
1988
     ((member expr vars :test #'equal-test-p)
 
1989
      (let ((pos (position expr vars :test #'equal-test-p)))
 
1990
        (make-variable *MaximaRing* (length vars) pos)))
 
1991
     ((free-of-vars expr vars)
 
1992
      ;;This means that variable-free CRE and Poisson forms will be converted
 
1993
      ;;to coefficients intact
 
1994
      (coerce-coeff *MaximaRing* expr vars))
 
1995
     (t
 
1996
      (case (caar expr)
 
1997
        (mplus (reduce #'(lambda (x y) (poly-add *MaximaRing* x y)) (parse-list (cdr expr))))
 
1998
        (mminus (poly-uminus *MaximaRing* (parse (cadr expr))))
 
1999
        (mtimes
 
2000
         (if (endp (cddr expr))         ;unary
 
2001
             (parse (cdr expr))
 
2002
           (reduce #'(lambda (p q) (poly-mul *MaximaRing* p q)) (parse-list (cdr expr)))))
 
2003
        (mexpt
 
2004
         (cond
 
2005
          ((member (cadr expr) vars :test #'equal-test-p)
 
2006
           ;;Special handling of (expt var pow)
 
2007
           (let ((pos (position (cadr expr) vars :test #'equal-test-p)))
 
2008
             (make-variable *MaximaRing* (length vars) pos (caddr expr))))
 
2009
          ((not (and (integerp (caddr expr)) (plusp (caddr expr))))
 
2010
           ;; Negative power means division in coefficient ring
 
2011
           ;; Non-integer power means non-polynomial coefficient
 
2012
           (mtell "~%Warning: Expression ~%~M~%contains power which is not a positive integer. Parsing as coefficient.~%"
 
2013
                  expr)
 
2014
           (coerce-coeff *MaximaRing* expr vars))
 
2015
          (t (poly-expt *MaximaRing* (parse (cadr expr)) (caddr expr)))))
 
2016
        (mrat (parse ($ratdisrep expr)))
 
2017
        (mpois (parse ($outofpois expr)))
 
2018
        (otherwise
 
2019
         (coerce-coeff *MaximaRing* expr vars)))))))
 
2020
 
 
2021
(defun parse-poly-list (expr vars)
 
2022
  (case (caar expr)
 
2023
    (mlist (mapcar #'(lambda (p) (parse-poly p vars)) (cdr expr)))
 
2024
    (t (merror "Expression ~M is not a list of polynomials in variables ~M."
 
2025
               expr vars))))
 
2026
(defun parse-poly-list-list (poly-list-list vars)
 
2027
  (mapcar #'(lambda (G) (parse-poly-list G vars)) (coerce-maxima-list poly-list-list)))
 
2028
 
 
2029
 
 
2030
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
2031
;;
 
2032
;; Order utilities
 
2033
;;
 
2034
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
2035
(defun find-order (order)
 
2036
  "This function returns the order function bases on its name."
 
2037
  (cond
 
2038
   ((null order) NIL)
 
2039
   ((symbolp order)
 
2040
    (case order
 
2041
      ((lex :lex $lex) #'lex>) 
 
2042
      ((grlex :grlex $grlex) #'grlex>)
 
2043
      ((grevlex :grevlex $grevlex) #'grevlex>)
 
2044
      ((invlex :invlex $invlex) #'invlex>)
 
2045
      ((elimination-order-1 :elimination-order-1 elimination_order_1) #'elimination-order-1)
 
2046
      (otherwise
 
2047
       (mtell "~%Warning: Order ~M not found. Using default.~%" order))))
 
2048
   (t
 
2049
    (mtell "~%Order specification ~M is not recognized. Using default.~%" order)
 
2050
    NIL)))
 
2051
 
 
2052
(defun find-ring (ring)
 
2053
  "This function returns the ring structure bases on input symbol."
 
2054
  (cond
 
2055
   ((null ring) NIL)
 
2056
   ((symbolp ring)
 
2057
    (case ring
 
2058
      ((expression-ring :expression-ring $expression_ring) *ExpressionRing*) 
 
2059
      ((ring-of-integers :ring-of-integers $ring_of_integers) *RingOfIntegers*) 
 
2060
      (otherwise
 
2061
       (mtell "~%Warning: Ring ~M not found. Using default.~%" ring))))
 
2062
   (t
 
2063
    (mtell "~%Ring specification ~M is not recognized. Using default.~%" ring)
 
2064
    NIL)))
 
2065
 
 
2066
(defmacro with-monomial-order ((order) &body body)
 
2067
  "Evaluate BODY with monomial order set to ORDER."
 
2068
  `(let ((*monomial-order* (or (find-order ,order) *monomial-order*)))
 
2069
     . ,body))
 
2070
 
 
2071
(defmacro with-coefficient-ring ((ring) &body body)
 
2072
  "Evaluate BODY with coefficient ring set to RING."
 
2073
  `(let ((*MaximaRing* (or (find-ring ,ring) *MaximaRing*)))
 
2074
     . ,body))
 
2075
 
 
2076
(defmacro with-elimination-orders ((primary secondary elimination-order)
 
2077
                                   &body body)
 
2078
  "Evaluate BODY with primary and secondary elimination orders set to PRIMARY and SECONDARY."
 
2079
  `(let ((*primary-elimination-order* (or (find-order ,primary)  *primary-elimination-order*))
 
2080
         (*secondary-elimination-order* (or (find-order ,secondary) *secondary-elimination-order*))
 
2081
         (*elimination-order* (or (find-order ,elimination-order) *elimination-order*)))
 
2082
     . ,body))
 
2083
 
 
2084
 
 
2085
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
2086
;;
 
2087
;; Conversion from internal form to Maxima general form
 
2088
;;
 
2089
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
2090
 
 
2091
(defun maxima-head ()
 
2092
  (if $poly_return_term_list
 
2093
      '(mlist)
 
2094
    '(mplus)))
 
2095
 
 
2096
(defun coerce-to-maxima (poly-type object vars)
 
2097
  (case poly-type
 
2098
    (:polynomial 
 
2099
     `(,(maxima-head) ,@(mapcar #'(lambda (term) (coerce-to-maxima :term term vars)) (poly-termlist object))))
 
2100
    (:poly-list
 
2101
     `((mlist) ,@(mapcar #'(lambda (p) (coerce-to-maxima :polynomial p vars)) object)))
 
2102
    (:term
 
2103
     `((mtimes) ,(term-coeff object)
 
2104
                ,@(mapcar #'(lambda (var power) `((mexpt) ,var ,power))
 
2105
                          vars (monom-exponents (term-monom object)))))
 
2106
    (otherwise
 
2107
     object)))
 
2108
 
 
2109
 
 
2110
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
2111
;;
 
2112
;; Macro facility for writing Maxima-level wrappers for
 
2113
;; functions operating on internal representation
 
2114
;;
 
2115
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
2116
 
 
2117
(defmacro with-parsed-polynomials (((maxima-vars &optional (maxima-new-vars nil new-vars-supplied-p))
 
2118
                                    &key (polynomials nil)
 
2119
                                         (poly-lists nil)
 
2120
                                         (poly-list-lists nil)
 
2121
                                         (value-type nil))
 
2122
                                   &body body
 
2123
                                   &aux (vars (gensym))
 
2124
                                        (new-vars (gensym)))
 
2125
  `(let ((,vars (coerce-maxima-list ,maxima-vars))
 
2126
         ,@(when new-vars-supplied-p
 
2127
             (list `(,new-vars (coerce-maxima-list ,maxima-new-vars)))))
 
2128
     (coerce-to-maxima
 
2129
      ,value-type
 
2130
      (with-coefficient-ring ($poly_coefficient_ring)
 
2131
        (with-monomial-order ($poly_monomial_order)
 
2132
          (with-elimination-orders ($poly_primary_elimination_order
 
2133
                                    $poly_secondary_elimination_order
 
2134
                                    $poly_elimination_order)
 
2135
            (let ,(let ((args nil))
 
2136
                    (dolist (p polynomials args)
 
2137
                      (setf args (cons `(,p (parse-poly ,p ,vars)) args)))
 
2138
                    (dolist (p poly-lists args)
 
2139
                      (setf args (cons `(,p (parse-poly-list ,p ,vars)) args)))
 
2140
                    (dolist (p poly-list-lists args)
 
2141
                      (setf args (cons `(,p (parse-poly-list-list ,p ,vars)) args))))
 
2142
              . ,body))))
 
2143
      ,(if new-vars-supplied-p
 
2144
           `(append ,vars ,new-vars)
 
2145
         vars))))
 
2146
 
 
2147
(defmacro define-unop (maxima-name fun-name
 
2148
                       &optional (documentation nil documentation-supplied-p))
 
2149
  "Define a MAXIMA-level unary operator MAXIMA-NAME corresponding to unary function FUN-NAME."
 
2150
  `(defun ,maxima-name (p vars
 
2151
                             &aux
 
2152
                             (vars (coerce-maxima-list vars))
 
2153
                             (p (parse-poly p vars)))
 
2154
     ,@(when documentation-supplied-p (list documentation))
 
2155
     (coerce-to-maxima :polynomial (,fun-name *MaximaRing* p) vars)))
 
2156
 
 
2157
(defmacro define-binop (maxima-name fun-name
 
2158
                        &optional (documentation nil documentation-supplied-p))
 
2159
  "Define a MAXIMA-level binary operator MAXIMA-NAME corresponding to binary function FUN-NAME."
 
2160
  `(defmfun ,maxima-name (p q vars
 
2161
                             &aux
 
2162
                             (vars (coerce-maxima-list vars))
 
2163
                             (p (parse-poly p vars))
 
2164
                             (q (parse-poly q vars)))
 
2165
     ,@(when documentation-supplied-p (list documentation))
 
2166
     (coerce-to-maxima :polynomial (,fun-name *MaximaRing* p q) vars)))
 
2167
 
 
2168
 
 
2169
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
2170
;;
 
2171
;; Maxima-level interface functions
 
2172
;;
 
2173
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
2174
 
 
2175
;; Auxillary function for removing zero polynomial
 
2176
(defun remzero (plist) (remove #'poly-zerop plist))
 
2177
 
 
2178
;;Simple operators
 
2179
 
 
2180
(define-binop $poly_add poly-add
 
2181
  "Adds two polynomials P and Q")
 
2182
 
 
2183
(define-binop $poly_subtract poly-sub
 
2184
  "Subtracts a polynomial Q from P.")
 
2185
 
 
2186
(define-binop $poly_multiply poly-mul
 
2187
  "Returns the product of polynomials P and Q.")
 
2188
 
 
2189
(define-binop $poly_s_polynomial spoly
 
2190
  "Returns the syzygy polynomial (S-polynomial) of two polynomials P and Q.")
 
2191
 
 
2192
(define-unop $poly_primitive_part poly-primitive-part
 
2193
  "Returns the polynomial P divided by GCD of its coefficients.")
 
2194
 
 
2195
(define-unop $poly_normalize poly-normalize
 
2196
  "Returns the polynomial P divided by the leading coefficient.")
 
2197
 
 
2198
;;Functions
 
2199
 
 
2200
(defmfun $poly_expand (p vars)
 
2201
  "This function is equivalent to EXPAND(P) if P parses correctly to a polynomial.
 
2202
If the representation is not compatible with a polynomial in variables VARS,
 
2203
the result is an error."
 
2204
  (with-parsed-polynomials ((vars) :polynomials (p)
 
2205
                            :value-type :polynomial)
 
2206
                           p))
 
2207
 
 
2208
(defmfun $poly_expt (p n vars)
 
2209
  (with-parsed-polynomials ((vars) :polynomials (p) :value-type :polynomial)
 
2210
    (poly-expt *MaximaRing* p n)))
 
2211
 
 
2212
(defmfun $poly_content (p vars)
 
2213
  (with-parsed-polynomials ((vars) :polynomials (p))
 
2214
    (poly-content *MaximaRing* p)))
 
2215
 
 
2216
(defmfun $poly_pseudo_divide (f fl vars
 
2217
                            &aux (vars (coerce-maxima-list vars))
 
2218
                                 (f (parse-poly f vars))
 
2219
                                 (fl (parse-poly-list fl vars)))
 
2220
  (multiple-value-bind (quot rem c division-count)
 
2221
      (poly-pseudo-divide *MaximaRing* f fl)
 
2222
    `((mlist)
 
2223
      ,(coerce-to-maxima :poly-list quot vars)
 
2224
      ,(coerce-to-maxima :polynomial rem vars)
 
2225
      ,c
 
2226
      ,division-count)))
 
2227
 
 
2228
(defmfun $poly_exact_divide (f g vars)
 
2229
  (with-parsed-polynomials ((vars) :polynomials (f g) :value-type :polynomial)
 
2230
    (poly-exact-divide *MaximaRing* f g)))
 
2231
 
 
2232
(defmfun $poly_normal_form (f fl vars)
 
2233
  (with-parsed-polynomials ((vars) :polynomials (f)
 
2234
                                   :poly-lists (fl)
 
2235
                                   :value-type :polynomial)
 
2236
    (normal-form *MaximaRing* f (remzero fl) nil)))
 
2237
 
 
2238
(defmfun $poly_buchberger_criterion (G vars)
 
2239
  (with-parsed-polynomials ((vars) :poly-lists (G))
 
2240
    (buchberger-criterion *MaximaRing* G)))
 
2241
 
 
2242
(defmfun $poly_buchberger (fl vars)
 
2243
  (with-parsed-polynomials ((vars) :poly-lists (fl) :value-type :poly-list)
 
2244
    (buchberger *MaximaRing*  (remzero fl) 0 nil)))
 
2245
 
 
2246
(defmfun $poly_reduction (plist vars)
 
2247
  (with-parsed-polynomials ((vars) :poly-lists (plist)
 
2248
                                   :value-type :poly-list)
 
2249
    (reduction *MaximaRing* plist)))
 
2250
 
 
2251
(defmfun $poly_minimization (plist vars)
 
2252
  (with-parsed-polynomials ((vars) :poly-lists (plist)
 
2253
                                   :value-type :poly-list)
 
2254
    (minimization plist)))
 
2255
 
 
2256
(defmfun $poly_normalize_list (plist vars)
 
2257
  (with-parsed-polynomials ((vars) :poly-lists (plist)
 
2258
                                   :value-type :poly-list)
 
2259
    (poly-normalize-list *MaximaRing* plist)))
 
2260
 
 
2261
(defmfun $poly_grobner (F vars)
 
2262
  (with-parsed-polynomials ((vars) :poly-lists (F)
 
2263
                                   :value-type :poly-list)
 
2264
    (grobner *MaximaRing* (remzero F))))
 
2265
 
 
2266
(defmfun $poly_reduced_grobner (F vars)
 
2267
  (with-parsed-polynomials ((vars) :poly-lists (F)
 
2268
                                   :value-type :poly-list)
 
2269
    (reduced-grobner *MaximaRing* (remzero F))))
 
2270
 
 
2271
(defmfun $poly_depends_p (p var mvars
 
2272
                        &aux (vars (coerce-maxima-list mvars))
 
2273
                             (pos (position var vars)))
 
2274
  (if (null pos)
 
2275
      (merror "~%Variable ~M not in the list of variables ~M." var mvars)
 
2276
    (poly-depends-p (parse-poly p vars) pos)))
 
2277
 
 
2278
(defmfun $poly_elimination_ideal (flist k vars)
 
2279
  (with-parsed-polynomials ((vars) :poly-lists (flist)
 
2280
                                   :value-type :poly-list)
 
2281
    (elimination-ideal *MaximaRing* flist k nil 0)))
 
2282
 
 
2283
(defmfun $poly_colon_ideal (F G vars)
 
2284
  (with-parsed-polynomials ((vars) :poly-lists (F G) :value-type :poly-list)
 
2285
    (colon-ideal *MaximaRing* F G nil)))
 
2286
 
 
2287
(defmfun $poly_ideal_intersection (F G vars)
 
2288
  (with-parsed-polynomials ((vars) :poly-lists (F G) :value-type :poly-list)  
 
2289
    (ideal-intersection *MaximaRing* F G nil)))
 
2290
 
 
2291
(defmfun $poly_lcm (f g vars)
 
2292
  (with-parsed-polynomials ((vars) :polynomials (f g) :value-type :polynomial)
 
2293
    (poly-lcm *MaximaRing* f g)))
 
2294
 
 
2295
(defmfun $poly_gcd (f g vars)
 
2296
  ($first ($divide (m* f g) ($poly_lcm f g vars))))
 
2297
 
 
2298
(defmfun $poly_grobner_equal (G1 G2 vars)
 
2299
  (with-parsed-polynomials ((vars) :poly-lists (G1 G2))
 
2300
    (grobner-equal *MaximaRing* G1 G2)))
 
2301
 
 
2302
(defmfun $poly_grobner_subsetp (G1 G2 vars)
 
2303
  (with-parsed-polynomials ((vars) :poly-lists (G1 G2))
 
2304
    (grobner-subsetp *MaximaRing* G1 G2)))
 
2305
 
 
2306
(defmfun $poly_grobner_member (p G vars)
 
2307
  (with-parsed-polynomials ((vars) :polynomials (p) :poly-lists (G))
 
2308
    (grobner-member *MaximaRing* p G)))
 
2309
 
 
2310
(defmfun $poly_ideal_saturation1 (F p vars)
 
2311
  (with-parsed-polynomials ((vars) :poly-lists (F) :polynomials (p)
 
2312
                                   :value-type :poly-list)
 
2313
    (ideal-saturation-1 *MaximaRing* F p)))
 
2314
 
 
2315
(defmfun $poly_saturation_extension (F plist vars new-vars)
 
2316
  (with-parsed-polynomials ((vars new-vars)
 
2317
                            :poly-lists (F plist)
 
2318
                            :value-type :poly-list)
 
2319
    (saturation-extension *MaximaRing* F plist)))
 
2320
 
 
2321
(defmfun $poly_polysaturation_extension (F plist vars new-vars)
 
2322
  (with-parsed-polynomials ((vars new-vars)
 
2323
                            :poly-lists (F plist)
 
2324
                            :value-type :poly-list)
 
2325
    (polysaturation-extension *MaximaRing* F plist)))
 
2326
 
 
2327
(defmfun $poly_ideal_polysaturation1 (F plist vars)
 
2328
  (with-parsed-polynomials ((vars) :poly-lists (F plist)
 
2329
                                   :value-type :poly-list)
 
2330
    (ideal-polysaturation-1 *MaximaRing* F plist 0 nil)))
 
2331
 
 
2332
(defmfun $poly_ideal_saturation (F G vars)
 
2333
  (with-parsed-polynomials ((vars) :poly-lists (F G)
 
2334
                                   :value-type  :poly-list)
 
2335
    (ideal-saturation *MaximaRing* F G 0 nil)))
 
2336
 
 
2337
(defmfun $poly_ideal_polysaturation (F ideal-list vars)
 
2338
  (with-parsed-polynomials ((vars) :poly-lists (F)
 
2339
                                   :poly-list-lists (ideal-list)
 
2340
                                   :value-type :poly-list)
 
2341
    (ideal-polysaturation *MaximaRing* F ideal-list 0 nil)))
 
2342