incr-set prlevel 1
if #0>=2 START 
incr-set prlevel -1

;;; Usage:
;;;     <annihilator X ann_X [maxdegree]
;;;
;;; computes the annihilator of X by finding the annihilator of
;;; one element at a time.  ann_X comes with a standard basis.
incr-set prlevel 1
jump END
;;;
;;; Parameters:
;;;		X = a module
;;;     maxdegree = a number
;;; Output values:
;;;		ann_X = its annihilator, as an ideal with standard
;;;		basis.
;;;
;;; Discussion:  This code is good because the first generator
;;; usually has annihilator equal to that of the module.  
;;;
;;; Caveat: The work done by the first modulo command, which is often
;;; nearly half of all the work done, is actually superfluous: if
;;; the component comes first in the monomial order, then after
;;; computing a standard basis for #1, the annihilator of the first
;;; generator is generated by the elements in the first row whose 
;;; columns have no other nonzero entries.  However, to make use
;;; of this we would have to be sure that the current
;;; ring had this property.  If we did a copyring to ensure it,
;;; then aside from destroying the block structure of the vars
;;; in the current ring, we would not be able to return a standard
;;; basis of the annihilator.

; created 4/12/91 DE+MS
START:
<ideal #2 1
<idenrowdegs #1 @I
nrows #1 @n
if @n=0 done1

if #0=2 nomaxdegree
  if #3>=0 degok
    <ideal #2
    jump done1
  degok:
    col-degs #1 @coldegs
    max @coldegs @maxcoldeg
    set autocalc 1
    set autodegree @maxcoldeg+(#3)
nomaxdegree:

std #1 @M
submat @I @e
   ;
   1

if #0=2 nomax1
  row-degree @M 1 @degrowi
  set autodegree @degrowi+(#3)
nomax1:

modulo @e @M #2

int @i 1
loop:
  int @i @i+1
  if @i>@n done

  submat @I @e
    ;
    @i

  ; check to see if #2 annihilates the i-th generator
  ncols #2 @nc
  col-degs #2 @coldegs
  <zeromat @nc @i-1 @Jmod
  <zeromat @nc @n-@i @Jmod2
  transpose #2 @K
  concat @Jmod @K @Jmod2
  transpose @Jmod @Jmod
  reduce @M @Jmod @K
  compress @K @K
  ; if this is zero, we already have the annihilator, so go
  ; on to next generator
  ncols @K @m
  if @m=0 loop

  ; compute the annihilator of the i-th generator
  if #0=2 nomaxi
    row-degree @M @i @degrowi
    set autodegree @degrowi+(#3)
  nomaxi:
  
  modulo @e @M @J  

  ; if this ideal does not contain the result ideal #2, intersect
  ; note that @J already is a standard basis
  reduce @J #2 @K
  compress @K @K
  ncols @K @m
  if @m=0 loop
  
  ; otherwise intersect:
  if #0=2 nomaxj
    set autodegree #3
  nomaxj:

  intersect #2 @J #2

  jump loop
done:

row-degree #2 1 @rowshift
dshift #2 -@rowshift
if @n=1 done1row


kill @K @m @Jmod @Jmod2 @nc 
done1row:
;The following awful piece of code ensures that
;these three variables exist at the end, where the kill's happen
int @J 0
int @coldegs 0
int @maxcoldeg 0
int @degrowi 0
kill @J @i @e @M @rowshift @coldegs @maxcoldeg @degrowi 
done1:
kill @I @n 
set autocalc
END:
incr-set prlevel -1
$;;;;;;;; EXAMPLE SECTION ;;;;;;;;;;;;;;;;;;;;;;;;;
;; first try some boundary cases
reset
<annihilator

<ring 4 a-d r
poly f 0
<annihilator f g
type g
betti g

poly f 1
<annihilator f g
type g
betti g

<zeromat 0 10 f
<annihilator f g
type g
betti g

<ring 3 a-z r
cat a m
0 -1
0..3
type m
<annihilator m i
type i
listvars

std m m
elim m em 1
type em
putstd m em
type em

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; try the maxdegree option
<ring 2 a-z r
<ideal j a3 b4
dshift j -6
<annihilator j k 0
type k
betti k

<ring 2 a-z r
<ideal j a3 b4 1
dshift j -6
<annihilator j k 0
type k
betti k
listvars
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

reset
;Comparison of 3 methods:
;In these examples, annihilator2 is usually faster than annihilator1,
;but annihilator is fastest on all the larger problems we have 
;tried, in some cases quite dramatically.
;Note also that annihilator2 introduces new variables which 
;appear to a degree about the number of rows of the original 
;presentation -- impractical when the matrices are large as in 
;one example below.

<ring 11 a-z r
cat a n
0..3
0..8

type n
;<annihilator1 n ann
;type ann
;Annihilator1 did not finish after 6 min on 50Mhz Mac II
set prlevel -1
set timer 1
<annihilator2 n ann
type ann
;about 3:15 min on 50 Mhz Mac*II
<annihilator n ann
type ann
;about 4 min on 50 Mhz Mac*II

; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;Yet an example of the 
;strong form of Huneke's conj:
<ring 3 a-z r
power r 2 r2
res r2 rr
transpose rr.2 t
<wedge_cokernel t 4 t4
betti t4
; total:     70   336 
; --------------------
;   -12:     70   336 
;<annihilator1 t4 i
;About 1 hour on the Decstation 5000 (only 2.7 Mbytes)

<annihilator t4 i
;About 3 minutes on 50 Mhz MacII
type i
;b a c

; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Further examples for strong Huneke conjecture. All but the first
;was too hard with annihilator1, impossible with annihilator2.

<ring 4 a-z r
<ideal i a2 b2 c2 d2 abcd
res i ii
betti ii

<rank_prob ii.1 r1
<wedge_cokernel ii.2 r1+1 w1
betti w1
; total:     10    50 
; --------------------
;     4:      6     - 
;     5:      -    24 
;     6:      4    16 
;     7:      -     6 
;     8:      -     4 
;<annihilator1 w1 i1
;<annihilator2 w1 i1
<annihilator w1 i1
;3 sec

<rank_prob ii.2 r2
<wedge_cokernel ii.3 r2+1 w2
betti w2
; total:    252  2100 
; --------------------
;    20:      6     - 
;    21:     60   150 
;    22:    120   800 
;    23:     60   900 
;    24:      6   240 
;    25:      -    10 
;About 1000 of the columns are minimal generators
;<annihilator1 w2 i2
;<annihilator2 w2 i2
<annihilator w2 i2
;3:25 min on 50 Mhz MacII
type i2
; d2 a2 c2 b2 abcd 

<rank_prob ii.3 r3
<wedge_cokernel ii.4 r3+1 w3
betti w3
; total:    120   840 
; --------------------
;    42:    120   840 
<annihilator w3 i3
;About a minute on 50 Mhz MacII
type i3
; d c a b 

listvars
