incr-set prlevel 1
if #0=3 START 
incr-set prlevel -1

;;; Usage:
;;; 	<double_dual M N f
;;;
;;; N is set equal to the double dual
;;; Hom(M,Hom (M, R)) of M over whatever ring
;;; M is defined.  f is set to the natural map from
;;; M to N (that is, from the free module giving
;;; the generators of M to the one giving the generators
;;; of N.
incr-set prlevel 1
jump END
;;;
;;; Parameters:
;;;		M = module
;;; Output values:
;;;		N =  M^**
;;; 	f : M --> N the natural map (given on the generators)
;;;
;;; The torsion submodule of M is the kernel of the map f;
;;; it can be computed by a call of the form
;;; 	<kernel f M N torsion
;;; 	type torsion
;;; See the script "kernel" for an example.
;;;
;;; Method: Let m be the dual of a presentation map of M.  2 steps of
;;; the resolution of m are computed.  Writing n.-1 for
;;; the dual of the second, the desired presentation of
;;; N is obtained as n.1 , the 3rd map in the resolution
;;; beginning with n.-1.  The map f is obtained by lifting
;;; the dual of m.2 along n.0.
;;;
;;; Caveats:
;;;
;Created 2/89 by D.Eisenbud
START:

transpose #1 @m
res @m @m 3
transpose @m.3 @n

;Compute N
res @n @n 3
copy @n.3 #2

;Compute f
transpose @m.2 @m2
lift @n.2 @m2 #3

kill @m @m2 @n
END:
incr-set prlevel -1


$;;;;;;;; EXAMPLE SECTION ;;;;;;;;;;;;;;;;;;;;;;;;;
reset
;Torsion in the cotangent sheaf of the nodal cubic:
<ring 3 a-z r  ;coord ring of P2
<poly f a2c-b2(b-c)  ;the nodal cubic
cotan f Omega

<double_dual
<double_dual Omega Om f
<kernel f Omega Om tors
<prune tors tors
betti tors
;singular point is a = b = 0.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
<ring 4 a-z r

;something torsion: double dual = 0
ideal m1
1
a
<double_dual
<double_dual m1 n1 f1
type n1
type f1
<kernel f1 m1 n1 tors1
type tors1

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

;something with double dual free of rank 1
ideal m2
2
a
b
transpose m2 m2
<double_dual m2 n2 f2
type n2
betti n2

type f2
; ;;;;;;;;;;;;;;;;;;;;;;;;;

;something reflexive
ideal m3
3
a
b
c
transpose m3 m3
<double_dual m3 n3 f3
type n3
type f3
