newPackage(
    "IntegralClosure",
    Version => "1.10", 
    Date => "31 Dec 2020",
    Authors => {
        {Name => "David Eisenbud", Email => "de@msri.org", HomePage => "http://www.msri.org/~de/"},
        {Name => "Mike Stillman", Email => "mike@math.cornell.edu", HomePage => "http://www.math.cornell.edu/~mike"},
        {Name => "Amelia Taylor", Email => "originalbrickhouse@gmail.com"}
        },
    Headline => "integral closure",
    Keywords => {"Commutative Algebra"},
    PackageImports => { 
        "PrimaryDecomposition",  -- only used for an obscure "rad" function
        "ReesAlgebra" -- used for integral closure of an ideal
        },
    PackageExports => {
        "MinimalPrimes" -- really helps speed up most computations here. Use minprimes.
        },
    DebuggingMode => false,
    AuxiliaryFiles => true
    )

-- The present version (for M2 1.16) is not done being cleaned up,
-- but it does fix a bad bug involving integral closure of ideals.
-- 

importFrom_Core { "generatorSymbols" } -- use as R#generatorSymbols.
importFrom_MinimalPrimes { "rad" } -- a function we seem to be using in integralClosure.

export{
    -- methods
     "integralClosure", 
     "icFractions", 
     "icMap", 
     "isNormal", 
     "conductor", 
     "makeS2",
     "idealizer", 
     "ringFromFractions", 
     -- small characteristic method
     "icFracP", 
     "icPIdeal",
     --mes--"extendIdeal",
     "testHunekeQuestion",
     -- optional argument names
     "Keep",
     "Index",
     --mes--"Denominator",
     "ConductorElement",
     -- strategy options for integralClosure (passed on to its subroutines)
     "AllCodimensions",
     "RadicalCodim1",
     "Radical",
     "StartWithOneMinor",
     "SimplifyFractions",
     "Vasconcelos"
     } 

makeVariable = opts -> (
     s := opts.Variable;
     if instance(s,Symbol) then s else
     if instance(s,String) then getSymbol s else
     error "expected Variable option to provide a string or a symbol"
     )

integralClosure = method(Options=>{
        Variable => "w",
        Limit => infinity,
        Strategy => {}, -- a mix of certain symbols
        Verbosity => 0,
        --mes--Denominator => null, -- if given, should be a nonzero divisor in Jacobian ideal of the ring.
        Keep => null -- list of variables to not simplify away.  Default is all original vars
        }
    )

idealInSingLocus = method(Options => {
	Verbosity => 0,
	Strategy => {}
	})

idealInSingLocus Ring := Ideal => opts -> S -> (
     -- Input: ring S = S'/I, where S' is a flattened poly ring.
     --  Verbosity: if >0 display timing
     --  Strategy: List. If isMember(StartWithOneMinor, opts.Strategy) then 
     -- Output:
     --        ideal in non-normal locus
     -- private subroutine of integralClosure

     if opts.Verbosity >= 1 then (
	  << " [jacobian time " << flush;
	  );
     if member(StartWithOneMinor, opts.Strategy) then 
          t1 := timing (J := ideal nonzeroMinor(codim ideal S, jacobian S))
     else
          t1 = timing (J = minors(codim ideal S, jacobian S));

     if opts.Verbosity >= 1 then (
        << t1#0 << " sec #minors " << numgens J << "]" << endl;
	);
     J
     )

integralClosure(Ring, Ring) := Ring => o -> (B,A) -> (
    (fB,fromB) := flattenRing B;
    fC := integralClosure(fB, o);
    if fB === fC and coefficientRing B === A then return B;
    gensA := generators(A, CoefficientRing => ZZ);
    newvars := drop(gens ambient fC, - #gensA);
    newdegs := drop(degrees ambient fC, - #gensA);
    aC := A (monoid[newvars, Degrees => newdegs, Join => false]);
    L := trim sub(ideal fC, aC);
    C := aC/L;
    B.icFractions = fB.icFractions;
    fCtoC := map(C, fC, gens(C, CoefficientRing => coefficientRing fC));
    B.icMap = fCtoC * fB.icMap * fromB;
    C
    )

integralClosure Ring := Ring => o -> (R) -> (
     -- R: Ring, a reduced affine ring. TODO: can we handle integral closures over ZZ as well?
     --   answer: if we choose J in the non-normal ideal some other way?
     if R.?icMap then return target R.icMap;
     verbosity := o.Verbosity;

     strategies := if instance(o.Strategy, Symbol) then {o.Strategy} else o.Strategy;
     strategyList := {AllCodimensions, RadicalCodim1, Radical, 
             StartWithOneMinor, SimplifyFractions, Vasconcelos};
     if not isSubset(strategies, strategyList) then
       error("expected Strategy option to be either an element of, or a list containing some of, "|toString strategyList);
     if #((set strategies) * set {AllCodimensions, RadicalCodim1, Radical}) >= 2 then
       error " expected Strategy option to include at most one of AllCodimensions, Radical, Radical";

     if ultimate(coefficientRing, R) =!= coefficientRing R
     then return integralClosure(R, coefficientRing R, o);

     (S,F) := flattenRing R;

     -- right here, we will grab the variables to be excluded
     -- this seems a bit awkward, perhaps there is a better way?
     T := ambient S;
     kk := ultimate(coefficientRing,T);
     allgens := generators(T, CoefficientRing => kk);
     keepvars := o.Keep; -- TODO MES: bug? these will not be in the correct ring, bring them over?
       -- TODO MES: check that o.Keep contains a list of variables in the ring R?
     if keepvars === null then keepvars = allgens;

     P := ideal S;
     startingCodim := codim P;
     isCompleteIntersection := (startingCodim == numgens P);
     G := map(frac R, frac S, substitute(F^-1 vars S, frac R));

     isS2 := isCompleteIntersection; -- true means is, false means 'do not know'
     nsteps := 0;
     t1 := null;  -- used for timings
     
     allCodimensionsNotPresent := not member(AllCodimensions, strategies);
     codim1only := not member(AllCodimensions, strategies);
       -- this means: don't bother to compute the S2-ification
       -- and don't try to take only the codim 1 part of the radical
       -- of the jacobian locus.
       
     ------------------------------------------
     -- Step 1: Find ideal in singular locus --
     ------------------------------------------     
     -- other possible things here: make a list of ideals, and we 
     --   will compute End of each in turn.
     --   (b) use discriminant
     J := idealInSingLocus(S, Verbosity => verbosity, Strategy => strategies); 
        -- returns ideal in non-normal locus S
     codimJ := codim J;
     isR1 := (codimJ > 1);

     if verbosity >= 1 then (
     	  << "integral closure nvars " << numgens S;
	  << " numgens " << numgens ideal S;
	  if isS2 then << " is S2";
	  if isR1 then << " is R1";
	  << " codim " << startingCodim;
	  << " codimJ " << startingCodim + codimJ << endl;
       << endl;
       );

     ------------------------------
     -- Step 2: make the ring S2 --
     ------------------------------
     --  unless we are using an option that
     --  doesn't require it.
     if not isS2 and allCodimensionsNotPresent then (
         if verbosity >= 1 then 
         << "   S2-ification " << flush;
         t1 = (timing F'G' := makeS2(target F,Variable=>makeVariable o,Verbosity=>verbosity));
         nsteps = nsteps + 1;
         if verbosity >= 1 then
             << t1#0 << " seconds" << endl;
	 if F'G' === null then (
             << "warning: probabilistic computation of S2-ification failed " << endl;
             << "         reverting to standard algorithm" << endl;
             strategies =  append(strategies, AllCodimensions);
             codim1only = false
	 ) else (
             (F', G') := F'G';
             F = F'*F;
	     G = G*G';
	     -- also extend J to be in this ring
	     J = trim(F' J);
	     isS2 = true;
	     )
         );

     denom := null; --mes--o.Denominator; -- either null (means for the routine to find a possible denominator),
     -- or a nzd in the radical of the ideal J.
     
     -------------------------------------------
     -- Step 3: incrementally add to the ring --
     -------------------------------------------
     if not isR1 or not codim1only then (     
     -- loop (note: F : R --> Rn, G : frac Rn --> frac R)
     while (
	  F1 := F;

	  if verbosity >= 1 then << " [step " << nsteps << ": " << flush;

	  t1 = timing((F,G,J,denom) = integralClosure1(F1,G,J,denom,nsteps,makeVariable o,keepvars,strategies,verbosity));

          if verbosity >= 1 then (
		 if verbosity >= 5 then (
		      << "  time " << t1#0<< " sec  fractions " << first entries G.matrix << "]" << endl << endl;
		      )
		 else (
		      << "  time " << t1#0<< " sec  #fractions " << numColumns G.matrix << "]" << endl;
		      );
		 );

	  nsteps = nsteps + 1;
	  nsteps < o.Limit and target F1 =!= target F
	  ) do (
	  ));
     R.icFractions = first entries G.matrix;
     R.icMap = F;
     target R.icMap
     )

doingMinimalization = true;

--the following finds an element in the intersection of the
--principal ideals generated by the ring elements in a list.
commonDenom = X -> findSmallGen intersect(apply (X, x->ideal x));

-- Compute the list of  minimal primes of J
-- Inputs:
--   J:Ideal (in a ring R0.  R0 is an affine ring, which should be a domain).
--   codim1only: Boolean
--   nsteps: ZZ (currently unused.  If > 0, this was an indication to add in some minors of the 
--       Jacobian ideal of J).
--   strategies: List, containing a subset of: 
--     RadicalCodim1, Radical
--     These mean:
--       Radical: compute radical using command 'radical'
--       RadicalCodim1: compute radical using command 'rad' in PrimaryDecomposition package
--       neither present: compute radical using 'decompose'.
--       AddMinor: unused.  Used to be an indication of whether to add in some Jacobian determinants.
-- Output: List, the list of minimal primes of J
--     of codim <= 1 in R0/J (if codim1only is true, or RadicalCodimi1 is set, or
--     of all codimensions, (otherwise)
--     If codim J > 1 and we are only collecting components of codim 1, then {} is returned.
-- Rational: 
--   why does this function exist, rather than just calling minimalPrimes?
-- What this does differently than minimalPrimes:
--   replaces each generator of (trim J) (flattened ring too), with its squarefree part.
--   in one case, it does call 'rad' in PrimaryDecompositions package.
-- TODO for this function:
--   remove nsteps.
--   radical and decompose in M2 call the same function these days.
--   remove dead code?
radicalJ = (J,codim1only,nsteps,strategies,verbosity) -> (
    -- Old comments, to remove;
    -- J is an ideal in R0.
    -- compute the radical of J, or perhaps a list of 
    -- components of J.  Possibly:
    --  remove components of codim > 1 in R0.
    --  add in new elements of the singular locus of J first, or after
    --  computing the radical.
    -- Choices for the radical:
    --  (a) intersection of decompose
    --  (b) use rad, limiting to codim 1
    --  (c) what else?
     useRadical := false;
     useRadicalCodim1 := false;
     useDecompose := false; --mes-todo-- this is always false here.
     if member(RadicalCodim1, strategies) then useRadicalCodim1 = true;
     if member(Radical, strategies) then useRadical = true;
     
     R0 := ring J;
     J = trim J;
     if codim1only and codim J > 1 then return {};

     if verbosity >= 2 then (
       	  << endl << "      radical " <<
	  (if useRadical then "(use usual radical) "
     	  else if useRadicalCodim1 then "(use codim1radical) "
	  else if useDecompose then "(use decompose) "
          else "(use minprimes) ")
          << flush;
	  );

     Jup := trim first flattenRing J;
     Jup = ideal apply(Jup_*, f -> product apply(apply(toList factor f, toList), first));
     Jup = trim ideal gens gb Jup;
     
     if verbosity >= 5 then (
       << "R0 = " << toExternalString ring Jup << endl;
       << "J0 = " << toString Jup << endl;
       );

     t1 := timing(radJup := 
       if useRadical then {radical Jup}
       else if useRadicalCodim1 then {rad(Jup,0)}
       else if useDecompose then decompose Jup
       else minprimes(Jup, Verbosity => max(verbosity-3,0)));

     radJ := apply(radJup, L -> trim promote(L, R0));
     
     if verbosity >= 4 then << "done computing radical" << endl << flush;     
     if verbosity >= 2 then << t1#0 << " seconds" << endl;

     if codim1only then radJ = select(radJ, J -> codim J == 1);
     
     if verbosity >= 5 then (
	  << "dimension of components: " << apply(radJ, codim) << endl;
	  << "components of radJ: " << endl;
	  << netList radJ 
	  << endl;
	  );

     radJ
     )

-- integralClosure1: the iterative step in the integral closure algorithm.
-- Some rings appearing here:
--     R is an affine  domain, the original ring for which we are computing the integral closure
--     R0 is a partial normalization.
-- Inputs:
--     F:RingMap, F : R -> R0, R0 is assumed to be a domain
--     G:RingMap  G : frac R0 --> frac R (really, the list of fractions).
--     J:Ideal, an ideal in the non-normal ideal of R0
--     denom: either null, or, a RingElement, a nonzero-divisor in R0, in the radical of J.
--        if null, this function will choose an element of this radical.
--     nsteps:ZZ
--     varname:Symbol
--     keepvars:List of variables to keep (where are these variables?) if/when we prune the ring.
--     strategies:List of elements from:
--       AllCodimensions
--       SimplifyFractions
--       doingMinimalization (always true, can't give this: it is currently a local variable set to true).
-- Outputs:
--     F1:RingMap, F1 : R --> R1, R1 is a (potentially) larger partial normalization.
--     G1:RingMap, G1: frac R1 --> frac R (list of fractions, one for each variable in the new R1)
--     J1:Ideal, J1 = radJ R1, the extension of the radical of J to R1.
--     denom1: either null, if denom===null, or 'denom' in the ring R1.
-- Features of the output:
--     The ring R0 is integrally closed (normal) iff target F === target F1.
--     New variables in the ring R1 will be named varname_(nsteps, 0), varnames_(nsteps, 1), ...

integralClosure1 = (F,G,J,denom,nsteps,varname,keepvars,strategies,verbosity) -> (
     codim1only := not member(AllCodimensions, strategies);

     R0 := target F;
     J = trim J;
     radJ := radicalJ(J, codim1only, nsteps, strategies,verbosity);
     if #radJ == 0 then return (F,G,ideal(1_R0),denom);
     radJ = trim intersect radJ;

     f := if denom === null then findSmallGen radJ else denom; -- we assume that f is a non-zero divisor!!
     
     --TODO: put in a test for f a nzd, and an option isDomain => true
     --syz matrix{{f}} ==0

     -- Compute Hom_S(radJ,radJ), using f as the common denominator.

     if verbosity >= 3 then <<"      small gen of radJ: " << f << endl << endl;
     if verbosity >= 6 then << "rad J: " << netList flatten entries gens radJ << endl;
     if verbosity >= 2 then <<"      idlizer1:  " << flush;

     t1 := timing((He,fe) := endomorphisms(radJ,f));

     if verbosity >= 2 then << t1#0 << " seconds" << endl;
     if verbosity >= 6 then << "endomorphisms returns: " << netList flatten entries He << endl;

     --TODO: make verbosity into Verbosity, a passed option
     
     -- here is where we improve or change our fractions
     if He == 0 then (
	  -- there are no new fractions to add, and this process will add no new fractions
	  return (F,G,ideal(1_R0),denom);
	  );

     if verbosity >= 6 then (
	  << "        about to add fractions" << endl;
          << "        " << apply(flatten entries He, g -> G(g/f)) << endl;
	  );

     if verbosity >= 2 then <<"      idlizer2:  " << flush;
     
    -- This is almost always a bad idea:  !!
    -- the issue is that the fraction field operations can be very bad.
       if member(SimplifyFractions, strategies)
       then (He,fe) = (
     	    Hef := apply(flatten entries He, h->h/f);
     	    Henum := Hef/numerator;
     	    Heden := Hef/denominator;
     	    Henumred := apply(#Hef, i-> Henum_i % Heden_i);
     	    fe1 := commonDenom Heden;
     	    multipliers := apply(Heden, H -> fe1//H);
     	    He1 := matrix{
	       	 apply(#Hef, i -> (Henumred#i * multipliers#i))
		 };
	    (He1,fe1));
	     
     if verbosity >= 6 then (
	  << "        reduced fractions: " << endl;
          << "        " << apply(flatten entries He, g -> G(g/fe)) << endl;
	  );

     t1 = timing((F0,G0) := ringFromFractions(He,fe,Variable=>varname,Index=>nsteps));
     
     if verbosity >= 2 then << t1#0 << " seconds" << endl;
     
     -- These would be correct, except that we want to clean up the
     -- presentation
     R1temp := target F0;
     if R1temp === R0 then return(F,G,radJ,denom);

     if doingMinimalization then (
       if verbosity >= 2 then << "      minpres:   " << flush;
       R1tempAmbient := ambient R1temp;
       keepindices := apply(keepvars, x -> index substitute(x, R1tempAmbient));
       t1 = timing(R1 := minimalPresentation(R1temp, Exclude => keepindices));
       if verbosity >= 2 then << t1#0 << " seconds" << endl;
       i := R1temp.minimalPresentationMap; -- R1temp --> R1
       iinv := R1temp.minimalPresentationMapInv.matrix; -- R1 --> R1temp
       iinvfrac := map(frac R1temp , frac R1, substitute(iinv,frac R1temp));
     
       -- We also want to trim the ring     
       F0 = i*F0; -- R0 --> R1
       if denom === null then
          (F0*F,G*G0*iinvfrac,F0 radJ, null)
       else
          (F0*F,G*G0*iinvfrac,F0 radJ, F0 denom)
       )
     else if denom === null then
       (F0,G0,F0 radJ,null)
     else
       (F0,G0,F0 radJ, F0 denom)
     )

---------------------------------------------------
-- Support routines, perhaps should be elsewhere --
---------------------------------------------------

randomMinors = method()
randomMinors(ZZ,ZZ,Matrix) := (n,d,M) -> (
     --produces a list of n distinct randomly chosend d x d minors of M
     r := numrows M;
     c := numcols M;
     if d > min(r,c) then return null;
     if n >= binomial(r,d) * binomial(c,d)
	then return (minors(d,M))_*;
     L := {}; -- L will be a list of minors, specified by the pair of lists "rows" and "cols"
     dets := {}; -- the list of determinants taken so far
     rowlist := toList(0..r-1);
     collist := toList(0..c-1);
     ds := toList(0..d-1);

     for i from 1 to n do (
      -- choose a random set of rows and of columns, add it to L 
      -- only if it doesn't appear already. When a pair is added to L, 
      -- the corresponding minor is added to "dets"
       while ( 
         rows := sort (random rowlist)_ds ;
         cols := sort (random collist)_ds ;
         for p in L do (if (rows,cols) == p then break true);
         false)
        do();
       L = L|{(rows,cols)};
       dets = dets | {det (M^rows_cols)}
       );
     dets
     )

nonzeroMinor = method(Options => {Limit => 100})
nonzeroMinor(ZZ,Matrix) :=  opts -> (d,M) -> (
     --produces one d x d nonzero minor, making up to 100 random tries.
     r := numrows M;
     c := numcols M;
     if d > min(r,c) then return null;
     candidate := 0_(ring M);
     rowlist := toList(0..r-1);
     collist := toList(0..c-1);
     ds := toList(0..d-1);
     for i from 1 to opts.Limit do(
      -- choose a random set of rows and of columns, test the determinant.
         rows := sort (random rowlist)_ds ;
         cols := sort (random collist)_ds ;
         candidate = det (M^rows_cols);
	 if candidate != 0 then return(candidate);
       );
     error("no nonzero minors found");
     )

-------------------------------------------
-- Rings of fractions, finding fractions --
-------------------------------------------
findSmallGen = (J) -> (
     a := toList((numgens ring J):1);
     L := sort apply(J_*, f -> ((weightRange(a,f))_1, size f, f));
     --<< "first choices are " << netList take(L,3) << endl;
--     << "ideal: " << toString J << endl;
     L#0#2
     )

idealizer = method(Options=>{
        Variable => "w", 
        Index => 0, 
        Strategy => {},
        Verbosity => 0
        }
    )

idealizer (Ideal, RingElement) := o -> (J, g) ->  (
     -- J is an ideal in a ring R containing a nonzerodivisor
     -- g is a nonzero divisor in J
     -- compute R1 = Hom(J,J) = (g*J:J)/g
     -- returns a sequence (F,G), where 
     --   F : R --> R1 is the natural inclusion
     --   G : frac R1 --> frac R, 
     -- optional arguments:
     --   o.Variable: base name for new variables added
     --   o.Index: the first subscript to use for such variables
     R := ring J;

     (H, f) := if member(Vasconcelos, o.Strategy) then (
                    vasconcelos (J,g))
                 else 
                    endomorphisms (J,g);
     
     if o.Verbosity >= 5 then << "endomorphism fractions:" << netList prepend(f,flatten entries H) << endl;
     if H == 0 then 
         (id_R, map(frac R, frac R, vars frac R)) -- in this case R is isomorphic to Hom(J,J)
     else 
         ringFromFractions(H, f, Variable=>makeVariable o, Index=>o.Index, Verbosity => o.Verbosity)
     )

endomorphisms = method()
endomorphisms(Ideal,RingElement) := (I,f) -> (
     --computes generators in frac ring I of
     --Hom(I,I)
     --assumes that f is a nonzerodivisor.
     --NOTE: f must be IN THE CONDUCTOR; 
     --else we get only the intersection of Hom(I,I) and f^(-1)*R.
     --returns the answer as a sequence (H,f) where
     --H is a matrix of numerators
     --f = is the denominator.
     if not fInIdeal(f,I) then error "Proposed denominator was not in the ideal.";
     timing(H1 := (f*I):I);
     H := compress ((gens H1) % f);
     (H,f)
     )

vasconcelos = method()
vasconcelos(Ideal,RingElement) := (I,f) -> (
     --computes generators in frac ring I of
     --(I^(-1)*I)^(-1) = Hom(I*I^-1, I*I^-1),
     --which is in general a larger ring than Hom(I,I)
     --(though in a 1-dim local ring, with a radical ideal I = mm,
     --they are the same.)
     --assumes that f is a nonzerodivisor (not necessarily in the conductor).
     --returns the answer as a sequence (H,f) where
     --H is a matrix of numerators
     --f  is the denominator. MUST BE AN ELEMENT OF I.
     if f%I != 0 then error "Proposed denominator was not in the ideal.";
     m := presentation module I;
     timing(n := syz transpose m);
     J := trim ideal flatten entries n;
     timing(H1 := ideal(f):J);
     H := compress ((gens H1) % f);
     (H,f)
     )

ringFromFractions = method(Options=>{
	  Variable => "w", 
	  Index => 0,
	  Verbosity => 0})
ringFromFractions (Matrix, RingElement) := o -> (H, f) ->  (
     -- f is a nonzero divisor in R
     -- H is a (row) matrix of numerators, elements of R
     -- Forms the ring R1 = R[H_0/f, H_1/f, ..].
     -- returns a sequence (F,G), where 
     --   F : R --> R1 is the natural inclusion
     --   G : frac R1 --> frac R, 
     -- optional arguments:
     --   o.Variable: base name for new variables added, defaults to w
     --   o.Index: the first subscript to use for such variables, defaults to 0
     --   so in the default case, the new variables produced are w_{0,0}, w_{0,1}...
     -- MES TODO: possible problem: in the inhomogeneous case, we might generate variables of degree 0.
     --   While this shouldn't be a problem, 'decompose' fails under this situation.
     --   Fix for now: if singly graded, but not homog, if a degree comes out <= 0 then set it to 1.
          isgraded := isHomogeneous H and isHomogeneous f;
          R := ring H;
       	  fractions := apply(first entries H,i->i/f);
          Hf := H | matrix{{f}};
     	  -- Make the new polynomial ring.
     	  n := numgens source H;
     	  newdegs := degrees source H - toList(n:degree f);
          if not isgraded and #newdegs#0 === 1 and any(newdegs, i -> i == {0})
          then newdegs = for d in newdegs list if first d > 0 then d else {1};
     	  degs := join(newdegs, (monoid R).Options.Degrees);
     	  MO := prepend(GRevLex => n, (monoid R).Options.MonomialOrder);
          kk := coefficientRing R;
	  var := makeVariable o;
     	  A := kk(monoid [var_(o.Index,0)..var_(o.Index,n-1), R#generatorSymbols,
		    MonomialOrder=>MO, Degrees => degs]);
     	  I := ideal presentation R;
     	  IA := ideal ((map(A,ring I,(vars A)_{n..numgens R + n-1})) (generators I));
     	  B := A/IA; -- this is sometimes a slow op

     	  -- Make the linear and quadratic relations
     	  varsB := (vars B)_{0..n-1};
     	  RtoB := map(B, R, (vars B)_{n..numgens R + n - 1});
     	  XX := varsB | matrix{{1_B}};
     	  -- Linear relations in the new variables
     	  lins := XX * RtoB syz Hf; 
     	  -- linear equations(in new variables) in the ideal
     	  -- Quadratic relations in the new variables
     	  tails := (symmetricPower(2,H) // f) // Hf;
     	  tails = RtoB tails;
     	  quads := matrix(B, entries (symmetricPower(2,varsB) - XX * tails));
	  both := ideal lins + ideal quads;
	  
	  gb both; -- sometimes slow
	  Bflat := flattenRing (B/both); --sometimes very slow
	  R1 := trim Bflat_0; -- sometimes slow

     	  -- Now construct the trivial maps
     	  F := map(R1, R, (vars R1)_{n..numgens R + n - 1});
	  G := map(frac R, frac R1, matrix{fractions} | vars frac R);

	  (F, G)
     )


fInIdeal = (f,I) -> (
     -- << "warning: fix fInIdeal" << endl;
     if isHomogeneous I -- really want to say: is the ring local?
       then f%I == 0
       else substitute(I:f, ultimate(coefficientRing, ring I)) != 0
     )


-- PURPOSE: check if an affine domain is normal.  
-- INPUT: any quotient ring.  
-- OUTPUT:  true if the ring is normal and false otherwise. 
-- COMMENT: This computes the jacobian of the ring which can be expensive.  
-- However, it first checks the less expensive S2 condition and then 
-- checks R1.  
isNormal = method()     
isNormal(Ring) := Boolean => (R) -> (
     -- 1 argument:  A ring - usually a quotient ring. 
     -- Return: A boolean value, true if the ring is normal and false
     -- otherwise. 
     -- Method:  Check if the Jacobian ideal of R has
     -- codim >= 2, if true then check the codimension 
     -- of Ext^i(S/I,S) where R = S/I and i>codim I. If 
     -- the codimensions are >= i+2 then return true.
     I := ideal (R);
     M := cokernel generators I;
     n := codim I;         
     test := apply((dim ring I)-n-1,i->i);
     if all(test, j -> (codim Ext^(j+n+1)(M,ring M)) >= j+n+3) 
     then ( 
	  Jac := minors(n,jacobian R);
	  d := dim Jac;
	  if d < 0 then d = -infinity;
	  dim R - d >=2)
     else false
     )

--------------------------------------------------------------------
-- MES TODO: don't require homogeneous!!
conductor = method()
conductor RingMap := Ideal => (F) -> (
     --Input:  A ring map where the target is finitely generated as a 
     --module over the source.
     --Output: The conductor of the target into the source.
     --Assumption: if R = source F, then R.icFractions is set
     --  or R is homogeneous
     R := source F;
     if false and R.?icFractions
       then (
            -- MES TODO: why is this commented out?
	    -- here we have a set of fractions which generate the integral closure
	    L := R.icFractions;
	    L = apply(L, h -> {numerator h, denominator h});
	    L = select(L, h -> h#1 != 1);
	    ans := ideal(1_R);
	    scan(L, h -> (
		      L1 := (ideal h#1) : h#0;
		      ans = trim intersect(ans, L1);
		      ));
	    ans
	    )
     else if isHomogeneous (source F)
     	  then(M := presentation pushForward(F, (target F)^1);
     	       P := target M;
     	       intersect apply((numgens P)-1, i->(
	       m:=matrix{P_(i+1)};
	       I:=ideal modulo(m,matrix{P_0}|M))))
	  else error "conductor: expected a homogeneous ideal in a graded ring"
     )
conductor Ring := (R) -> conductor icMap R

icMap = method()
icMap(Ring) := RingMap => R -> (
     -- 1 argument: a ring.  May be a quotient ring, or a tower ring.
     -- Returns: The map from R to the integral closure of R.  
     -- Note:  This is a map where the target is finitely generated as
     -- a module over the source, so it can be used as the input to
     -- conductor and other methods that require this. 
     if R.?icMap then R.icMap
     else if isNormal R then id_R
     else (
	  integralClosure R;
	  R.icMap
	  )
     )

--------------------------------------------------------------------
icFractions = method()
icFractions(Ring) := Matrix => (R) -> (
     if R.?icFractions then R.icFractions
     else if isNormal R then vars R -- MES TODO: is this too expensive?
     else (
	  integralClosure R;
     	  R.icFractions	  
     )
)

--------------------------------------------------------------------

icFracP = method(Options=>{ConductorElement => null, Limit => infinity, Verbosity => 0})
icFracP Ring := List => o -> (R) -> (
     -- 1 argument: a ring whose base field has characteristic p.
     -- Returns: Fractions
     -- MES:
     --  ideal presentation R ==== ideal R
     --  does not seem to handle towers
     --  this ring in the next line can't really be ZZ?
     if ring ideal presentation R === ZZ then (
	  D := 1_R;
	  U := ideal(D);
	  if o.Verbosity > 0 then print ("Number of steps: " | toString 0 | ",  Conductor Element: " | toString 1_R);
	  )    
     else if coefficientRing(R) === ZZ or coefficientRing(R) === QQ then error("Expected coefficient ring to be a finite field")
     else(
	  if o.ConductorElement === null then (
     	       P := ideal presentation R;
     	       c := codim P;
     	       S := ring P;
	       J := promote(jacobian P,R);
	       n := 1;
	       det1 := ideal(0_R);
	       while det1 == ideal(0_R) do (
		    det1 = minors(c, J, Limit => n);
		    n = n+1
		    );
	       D = det1_0;
	       D = (mingens(ideal(D)))_(0,0);
	       )
     	  else D = o.ConductorElement;
     	  p := char(R);
     	  K := ideal(1_R);
     	  U = ideal(0_R);
     	  F := apply(generators R, i-> i^p);
     	  n = 1;
     	  while (U != K) do (
	       U = K;
	       L := U*ideal(D^(p-1));
	       f := map(R/L,R,F);
	       K = intersect(kernel f, U);
	       if (o.Limit < infinity) then (
	       	    if (n >= o.Limit) then U = K;
	       	    );
               n = n+1;
     	       );
     	  if o.Verbosity > 0 then print ("Number of steps: " | toString n | ",  Conductor Element: " | toString D);
     	  );
     U = mingens U;
     if numColumns U == 0 then {1_R}
     else apply(numColumns U, i-> U_(0,i)/D)
     )

icPIdeal = method()
icPIdeal (RingElement, RingElement, ZZ) := Ideal => (a, D, N) -> (
     -- 3 arguments: An element in a ring of characteristic P that
     -- generates the principal ideal we are interested in, a
     -- non-zerodivisor of $ in the conductor, and the number of steps
     -- in icFracP to compute the integral closure of R using the
     -- conductor element given.  
     -- Returns: the integral closure of the ideal generated by the
     -- first argument.  
     n := 1;
     R := ring a;
     p := char(R);
     J := ideal(1_R);
     while (n <= N+1) do (
	F := apply(generators R, i-> i^(p^n));
	U := ideal(a^(p^n)) : D;
        f := map(R/U, R, F);
        J = intersect(J, kernel f);
	n = n+1;
     );
     J
     )

----------------------------------------
-- Integral closure of ideal -----------
----------------------------------------

-* version 2.  Not working yet, Dec 2020, perhaps use something like this in M2 1.18?
integralClosure(Ideal, RingElement, ZZ) := opts -> (I,a,D) -> (
    S := ring I;
    if a % I != 0 then error "The ring element should be an element of the ideal.";
    if ((ideal 0_S):a) != 0 then error "The given ring element must be a nonzerodivisor of the ring.";
    z := local z;
    w := local w;
    I = trim I;
    Reesi := reesAlgebra(I,a,Variable => z);
    Rbar := integralClosure(Reesi, S,  Variable => w); --opts is missing
    T := S[select(gens Rbar, x -> first degree x == 0)];
    J := ideal select((ideal Rbar)_*, f -> first degree f == 0);
    count := -1;
    TRbar := map(T,ring J,for x in gens ring J list 
        if first degree x == 0 
        then (count = count+1; T_count) 
        else 0);
    S' := T/TRbar J;
    S'S := map(S', S);
    M'' := basis({D}, Rbar, SourceRing => S');
    M := coimage M'';
    IS'D := S'S (I^D);
    -- the last generators of M correspond to IS'D, in order (checking on this).
    rg := splice{(numgens M - numgens IS'D)..(numgens M - 1)};
    phi := map(M, module IS'D, M_rg);
    assert(isWellDefined phi);
    I' := extendIdeal phi;
--    error "debug me";
    preimage(map(S', S), I')
    )
*-

integralClosureOfIdeal = (I, a, D, opts) -> (
    -- Assumption: ring I is integrally closed.
    S := ring I;
    if a % I != 0 then error "The ring element should be an element of the ideal.";
    if ((ideal 0_S):a) != 0 then error "The given ring element must be a nonzerodivisor of the ring.";
    z := local z;
    w := local w;
    I = trim I;
    Reesi := (flattenRing reesAlgebra(I,a,Variable => z))_0;
    Rbar := integralClosure(Reesi, opts, Variable => w);
    psi := map(Rbar,S,DegreeMap =>d->prepend(0,d));
    zIdeal := ideal(map(Rbar,Reesi))((vars Reesi)_{0..numgens I -1});
    zIdealD := module zIdeal^D;
    LD := prepend(D,toList(degreeLength S:null));
    degD := image basisOfDegreeD(LD,Rbar); --all gens of first-degree D.
    degsM := apply(degrees cover degD,d->drop(d,1));
    --the following line is ***slow***
    psi' := map(degD,S^(-degsM),psi,id_(cover degD));
    mapback := map(S,Rbar, matrix{{numgens Rbar-numgens S:0_S}}|(vars S), DegreeMap => d -> drop(d, 1));
    pdegD := gens gb presentation degD;
    origVarsInRbar := support sub(vars S, Rbar);
    ind := select(toList(0..numcols pdegD-1), i -> isSubset(support pdegD_{i}, origVarsInRbar));

    M := coker mapback pdegD_ind;

    phi := map(M,module(I^D), mapback matrix inducedMap(degD,zIdealD));
    if isHomogeneous I and isHomogeneous a then assert(isHomogeneous phi);
    assert(isWellDefined phi);
    extendIdeal phi
    )

integralClosure(Ideal, RingElement, ZZ) := opts -> (I,a,D) -> (
    S := ring I;
    if a % I != 0 then error "The ring element should be an element of the ideal.";
    if ((ideal 0_S):a) != 0 then error "The given ring element must be a nonzerodivisor of the ring.";
    Sbar := integralClosure S;
    Ibar := if Sbar === S then I else S.icMap I;
    abar := if Sbar === S then a else S.icMap a;
    Ibar = trim Ibar;
    J := integralClosureOfIdeal(Ibar, abar, D, opts); -- J is now an ideal in Sbar.
    -- now we need to take preimage in S, if needed.
    if Sbar === S then trim J else trim preimage(S.icMap, J)
    )

integralClosure(Ideal,ZZ) := Ideal => o -> (I,D) -> integralClosure(I, I_0, D, o)
integralClosure(Ideal,RingElement) := Ideal => o -> (I,a) -> integralClosure(I, a, 1, o)
integralClosure(Ideal) := Ideal => o -> I -> integralClosure(I, I_0, 1, o)

-*
Theorem (Saito): If R is a formal power series ring over a field of char 0, 
and f\in R has isolated singularity, then f is contained in j(f), the Jacobian ideal iff f is
quasi-homogeneous after a change of variables.

Theorem (Lejeune-Teisser?; see Swanson-Huneke Thm 7.1.5) 
f \in integral closure(ideal apply(numgens R,i-> x_i*df/dx_i))

Conjecture (Huneke: f is never a minimal generator of the integral closure of
ideal apply(numgens R,i-> df/dx_i).
*-
jacobian RingElement := Matrix => f -> jacobian ideal f

testHunekeQuestion = method()
testHunekeQuestion RingElement := Boolean => f -> (
    R := ring f;
    mm := ideal vars R;
    j := ideal jacobian f;
    if f % (j+mm*f) == 0 then (
	<< "power series is crypto-quasi-homogeneous"<<flush<<endl;
	return "yes");
    <<"power series is not crypto-quasi-homogeneous"<<flush<<endl;
    j' := ideal apply(numgens R, i -> R_i*j_i);
    J' := integralClosure j';
    if f % (mm*f+ mm*integralClosure j) == 0 then return "yes";
    )

extendIdeal = method()
extendIdeal(Matrix) := Ideal => phi -> ( --This method is WRONG on integralClosure ideal"a2,b2".
    --input: f: (module I) --> M, an inclusion from an ideal 
    --to a module that is isomorphic to the inclusion of I into an ideal J containing I.
    --output: the ideal J, so that f becomes the inclusion I subset J.
    inc := transpose gens source phi;
    phi0 := transpose matrix phi;
    sz := syz transpose presentation target phi;    
    (q,r) := quotientRemainder(inc,phi0*sz);
    if r !=0 then error "phi is not isomorphic to an inclusion of ideals";
    trim ideal (sz*q)
    )

basisOfDegreeD = method()
basisOfDegreeD (List,Ring) := Matrix => (L,R) ->(
    --assumes degrees of R are non-negative
    --change to a heft value sometime.
    PL := positions(L, d-> d=!=null);    
    PV := positions(degrees R, D->any(PL,i->D#i > 0));
    PVars := (gens ambient R)_PV;
    PDegs := PVars/degree/(D->D_PL);
      kk := ultimate(coefficientRing, R);
    R1 := kk(monoid[PVars,Degrees =>PDegs]);
    back := map(R,R1,PVars);
    g := back basis(L_PL, R1);
    map(target g,,g)
    )

-- MES TODO: this function needs to be documented.  I don't know what it is really doing?
-- I have un-exported this function.
integralClosures = method (Options => options integralClosure)
integralClosures(Ideal) := opts -> I -> (
    -- input: ideal I in an affine ring S
    -- output: 
     S := ring I;
     z := local z;
     w := local w;
     t := local t;
     A := tensor(coefficientRing(S)[t],S,Join=>false);
     IA := sub(I,A);
     ReesI := (flattenRing reesAlgebra(IA,Variable =>z))_0;
     fracs := icFractions ReesI;
     phi := map(frac(A),frac(ReesI),gens(A_0*IA)|vars A);
     newfracs := delete((frac A)_0, fracs/phi);
     -- The following two lines remove powers of t, and returns a hashtable
     L := partition(f -> degree(A_0, numerator f), newfracs);
     toFracS := map(frac S, frac A, {0} | gens frac S);
     result := hashTable apply(keys L, d -> d => apply(L#d, f -> toFracS(f // (A_0)^d)));
     result
    )

----------------------------------------
-- Canonical ideal, makeS2 --------
----------------------------------------
parametersInIdeal = method()

parametersInIdeal Ideal := I -> (
     --first find a maximal system of parameters in I, that is, a set of
     --c = codim I elements generating an ideal of codimension c.
     --assumes ring I is affine. 
     --routine is probabilistic, often fails over ZZ/2, returns null when it fails.
     R := ring I;
     c := codim I;
     G := sort(gens I, DegreeOrder=>Ascending);
     s := 0; --  elements of G_{0..s-1} are already a sop (codim s)
     while s<c do(
     	  t := s-1; -- elements of G_{0..t} generate an ideal of codim <= s
     	  --make t maximal with this property, and then add one
     	  while codim ideal(G_{0..t})<=s and t<rank source G -1 do t=t+1;
     	  G1 := G_{s..t};
	  coeffs := random(source G1, R^{-last flatten degrees G1});
	  lastcoef := lift(last last entries coeffs,ultimate(coefficientRing, R));
	  --coeffs = (1/lastcoef)*coeffs;
     	  newg := G1*coeffs;
     	  if s<c-1 then G = G_{0..s-1}|newg|G_{t..rank source G-1}
	       else G = G_{0..s-1}|newg;
	  if codim ideal G <s+1 then (
	       return null;
--	       error ("random coeffs not general enough at step ",s);
	       );
	  s = s+1);
      ideal G)

canonicalIdeal1 = method()
canonicalIdeal1 Ring := R -> (
     --tries to find a canonical ideal in R. Note that if R is
     --not generically Gorenstein, then it has no canonical ideal!
     --This routine is
     --guaranteed to work if R is a domain; if R is merely reduced,
     --or just generically Gorenstein, it may fail. If it fails, 
     --it returns null
     (S,f) := flattenRing R;
     P := ideal S;
     SS := ring P;
     n := numgens SS;
     c := codim P;
     WSS := prune Ext^c(SS^1/P, SS);
     WS := prune coker (map(S,SS)) (presentation WSS);
     H := Hom(WS, S^1);
     toIdeal := homomorphism H_{0};
     if ker toIdeal != 0 then return null;
     trim ideal f^-1 (image toIdeal))

canonicalIdeal = method()
canonicalIdeal Ring := R -> (
     --try to find a canonical ideal in R by the probabilistic method.
     --If you fail, try the method of canonicalIdeal1
     if degreeLength R =!= 1 or any(degrees R, x -> first x <= 0) then
     return canonicalIdeal1 R; -- problems with parametersInIdeal prevent its use yet
     (S,f) := flattenRing R;
     P := ideal S;
     J := parametersInIdeal P;
     if J === null then return canonicalIdeal1 R;
     Jp := J:P;
     trim (f^-1) promote(Jp,S)
     )

makeS2 = method(Options=>{
	  Variable => "w",
	  Verbosity => 0})
makeS2 Ring := o -> R -> (
     --try to find the S2-ification of a domain (or more generally an
     --unmixed, generically Gorenstein ring) R.
     --    Input: R, an affine ring
     --    Output: (like "idealizer") a sequence whose 
     -- first element is a map of rings from R to its S2-ification,
     --and whose second element is a list of the fractions adjoined 
     --to obtain the S2-ification.
     --    Uses the method of Vasconcelos, "Computational Methods..." p. 161, taking the idealizer
     --of a canonical ideal.
     --Assumes that first element of canonicalIdeal R is a nonzerodivisor; else returns error.
     --CAVEAT:
          --If w_0 turns out to be a zerodivisor
	  --then we should replace it with a general element of w. But if things
	  --are multiply graded this might involve finding a degree with maximal heft 
	  --or some such. How should this be done?? There should be a "general element"
	  --routine...
     w := canonicalIdeal R;
     if w === null then return null;
     if ideal(0_R):w_0 == 0 then idealizer(w,w_0,Variable=>makeVariable o)
     else (
	  return null;
	  error"first generator of the canonical ideal was a zerodivisor"
	  )
     )
-*
S2 = method() -- from Eisenbud's CompleteIntersectionResolutions.m2
S2(ZZ,Module) := Matrix => (b,M)-> (
     --returns a map M --> M', where M' = \oplus_{d>=b} H^0(\tilde M).
     --the map is equal to the S2-ification AT LEAST in degrees >=b.
     S := ring M;
     r:= regularity M;
     if b>r+1 then return id_(truncate(b,M));
     tbasis := basis(r+1-b,S^1); --(vars S)^[r-b];
     t := map(S^1, module ideal tbasis, tbasis);
     s:=Hom(t,M)
     --could truncate source and target; but if we do it with
     --the following line then we get subquotients AND AN ERROR!
--     inducedMap(truncate(b,target s),truncate(b,source s),s)
     )
 TEST ///--of S2
S = ZZ/101[a,b,c];
M = S^1/intersect(ideal"a,b", ideal"b,c",ideal"c,a");
assert( (hf(-7..1,coker S2(-5,M))) === (0, 3, 3, 3, 3, 3, 3, 2, 0))
makeS2 (S/intersect(ideal"a,b", ideal"b,c",ideal"c,a"))
-- 'betti' no longer accepts non-free modules
--assert( (betti prune S2(-5,M)) === new BettiTally from {(0,{-6},-6) => 3, (1,{0},0) => 1} )
///
*-

--------------------------------------------------------------------
TOOSLOW = method()
TOOSLOW String := (str) -> null

beginDocumentation()
--StartWithOneMinor, "vasconcelos",RadicalCodim1,AllCodimensions,SimplifyFractions
--radical(J, Unmixed)
doc ///
  Key
    IntegralClosure
  Headline
    routines for integral closure of affine domains and ideals
  Description
    Text
      This package contains several algorithms for computing the
      integral closure (i.e. normalization) of an affine domain,
      and also of an ideal.
      
      The basic use of this package is shown in the following example.
    Example
      R = QQ[x,y,z]/(x^3-x^2*z^5-z^2*y^5)
      S = integralClosure R
      describe S
    Text
      Use @TO icFractions@ to see what fractions have been added.
    Example
      icFractions R
    Text
      Look at the ideal of S or the generators of S to see the structure of the
      integral closure.
    Example
      gens S
      trim ideal S
    Text
      The integral closure of an ideal can be computed as follows.
    Example
      use R
      I = ideal(y,z)
      integralClosure I
    Text
      Integral closures of powers of ideals can be computed in a more efficient manner than
      using e.g. {\tt integralClosure(I^d)}, by using e.g. {\tt integralClosure(I,d)}.
    Example
      integralClosure(I^2)
      integralClosure(I, 2)
      integralClosure(I, 3) == integralClosure(I^3)
    Text
      If the characteristic is positive, yet small compared to the degree, but the
      fraction ring is still separable over a subring, then use
      @TO icFracP@, which is an implementation of an algorithm due to
      Leonard-Pellikaan, and modified by Singh-Swanson (see arXiv:0901.0871).
      However, the interface to this routine is likely to change in future
      releases to more closely match the functions above.
      
      The method used by integralClosure is a modification of the basic 
      algorithm explained in Theo De Jong's paper {\em An Algorithm for 
	  Computing the Integral Closure}, J. Symbolic Computation, 
	  (1998) 26, 273-277.
///

doc ///
  Key
    integralClosure
  Headline
    integral closure of an ideal or a domain
///

doc ///
  Key
    isNormal
    (isNormal, Ring)
  Headline
    determine if a reduced ring is normal
  Usage
    isNormal R
  Inputs
    R:Ring
      a reduced equidimensional ring
  Outputs
    :Boolean
      whether {\tt R} is normal, that is, whether it satisfies
      Serre's conditions S2 and R1
  Description
   Text
     This function computes the jacobian of the ring which can be costly for 
     larger rings.  Therefore it checks the less costly S2 condition first and if 
     true, then tests the R1 condition using the jacobian of {\tt R}.
   Example
     R = QQ[x,y,z]/ideal(x^6-z^6-y^2*z^4);
     isNormal R
     isNormal(integralClosure R)
  Caveat
    The ring {\tt R} must be an equidimensional ring.  If {\tt R} is a domain,
    then sometimes computing the integral closure of {\tt R} can be faster than
    this test.
  SeeAlso
    integralClosure
    makeS2
///

doc ///
  Key
    (integralClosure, Ring)
  Headline
    compute the integral closure (normalization) of an affine domain
  Usage
    R' = integralClosure R
  Inputs
    R:Ring
      a quotient of a polynomial ring over a field
    Keep => List
      of variables of R
    Limit => ZZ
    Variable => Symbol
    Verbosity => ZZ
    Strategy => List
      of some of the symbols: AllCodimensions, Radical, RadicalCodim1, 
      Vasconcelos, StartWithOneminor, SimplifyFractions
  Outputs
    R':Ring
      the integral closure of {\tt R}
  Consequences
   Item
    The inclusion map $R \rightarrow R'$
      can be obtained with @TO icMap@.  
   Item
    The fractions corresponding to the variables
      of the ring {\tt R'} can be found with @TO icFractions@
  Description
   Text
     The integral closure of a domain is the subring of the fraction field
     consisting of all fractions integral over the domain.  For example,
   Example
     R = QQ[x,y,z]/ideal(x^6-z^6-y^2*z^4-z^3);
     R' = integralClosure R
     gens R'
     icFractions R
     icMap R
     I = trim ideal R'
   Text
     Sometimes using @TO trim@ provides a cleaner set of generators.
   Text
     If $R$ is not a domain, first decompose it, and collect all of the 
     integral closures.
   Example
     S = ZZ/101[a..d]/ideal(a*(b-c),c*(b-d),b*(c-d));
     C = decompose ideal S
     Rs = apply(C, I -> (ring I)/I);
     Rs/integralClosure
     oo/prune
   Text
     This function is roughly based on
     Theo De Jong's paper, {\em An Algorithm for 
     Computing the Integral Closure}, J. Symbolic Computation, 
     (1998) 26, 273-277. This algorithm is similar to the round two
     algorithm of Zassenhaus in algebraic number theory.
   Text
     There are several optional parameters which allows the user to control
     the way the integral closure is computed.  These options may change
     in the future.
  Caveat
    This function requires that the degree of the field extension 
    (over a pure transcendental subfield) be greater 
    than the characteristic of the base field.  If not, use @TO icFracP@.
    This function requires that the ring be finitely generated over a ring.  If not (e.g. 
    if it is f.g. over the integers), then the result is integral, but not necessarily 
    the entire integral closure. Finally, if the ring is not a domain, then
    the answers will often be incorrect, or an obscure error will occur.
  SeeAlso
    icMap
    icFractions
    conductor
    icFracP
///

doc ///
  Key
    (integralClosure, Ring, Ring)
  Headline
    compute the integral closure (normalization) of an affine reduced ring over a base ring
  Usage
    R' = integralClosure(R, A)
  Inputs
    R:Ring
      a quotient of a polynomial ring ultimately over a field
    A:Ring
      a base ring of $R$ (one of its coefficient rings)
    Keep => List
      of variables of R
    Limit => ZZ
    Variable => Symbol
    Verbosity => ZZ
    Strategy => List
      of some of the symbols: AllCodimensions, Radical, RadicalCodim1, 
      Vasconcelos, StartWithOneminor, SimplifyFractions
  Outputs
    R':Ring
      the integral closure of $R$, having coefficient ring $A$
  Consequences
   Item
    The inclusion map $R \rightarrow R'$
      can be obtained with @TO icMap@.  
   Item
    The fractions corresponding to the variables
      of the ring {\tt R'} can be found with @TO icFractions@
  Description
   Text
     This function packages the output integral closure in the desired way.
     For more details about integral closure, see @TO (integralClosure, Ring)@.
     
     In the following example, there are three possible coefficient rings for $R$: $R$, $A$ and ${\mathbb Q}$.
   Example
     A = QQ[x,y]/(x^3-y^2)
     R = reesAlgebra(ideal(x*y,y^2), Variable => z)
     coefficientRing R
     describe R
   Example
     R' = integralClosure(R, R)
     describe R'
     icMap R
     fracs1 = icFractions R
   Example
     R'' = integralClosure(R, A)
     describe R''
     icMap R
     fracs2 = icFractions R
     assert(fracs1 == fracs2)
   Example
     R''' = integralClosure(R, QQ)
     describe R'''
     icMap R
     fracs3 = icFractions R
     assert(fracs1 == fracs3)
   Text
     Note that the second and third calls to {\tt integralClosure} changes the output of {\tt icMap}
     but the fractions are the same.
  Caveat
    All the caveats of @TO (integralClosure, Ring)@ are in effect and the output of @TO icMap@
    changes upon each call to this function.
  SeeAlso
    (integralClosure, Ring)
    icMap
    icFractions
///

--StartWithOneMinor, "vasconcelos",RadicalCodim1,AllCodimensions,SimplifyFractions
doc ///
  Key
    [integralClosure, Keep]
  Headline
    list ring generators which should not be simplified away
  Usage
    integralClosure(R, Keep=>L)
  Inputs
    L:List
      a list of variables in the ring R, or {\tt null} (the default).
  Consequences
   Item
    The given list of variables (or all of the outer generators, if L is null)
    will be generators of the integral closure
  Description
   Text
     Consider the cuspidal cubic, and three different possibilities for {\tt Keep}.
   Example
     R = QQ[x,y]/ideal(x^3-y^2);
     R' = integralClosure(R, Variable => symbol t)
     trim ideal R'
   Example
     R = QQ[x,y]/ideal(x^3-y^2);
     R' = integralClosure(R, Variable => symbol t, Keep => {x})
     trim ideal R'
   Example
     R = QQ[x,y]/ideal(x^3-y^2);
     integralClosure(R, Variable => symbol t, Keep => {})
///

doc ///
  Key
    [integralClosure, Variable]
  Headline
    set the base letter for the indexed variables introduced while computing the integral closure
  Usage
    integralClosure(R, Variable=>x)
  Inputs
    x:Symbol
  Consequences
   Item
    The new variables will be subscripted using {\tt x}.
  Description
   Example
     R = QQ[x,y,z]/ideal(x^6-z^6-y^2*z^4-z^3);
     R' = integralClosure(R, Variable => symbol t)
     trim ideal R'
   Text
     The algorithm works in stages, each time adding new fractions to the ring.
     A variable {\tt t_(3,0)} represents the first (zero-th) variables added at stage 3.
     
  Caveat
    The base name should be a symbol
    The variables added may be changed to {\tt t_1, t_2, ...} in the future.
///

doc ///
  Key
    [integralClosure,Limit]
  Headline
    do a partial integral closure
  Usage
    integralClosure(R, Limit => n)
  Inputs
    n:ZZ
      how many steps to perform
  Description
   Text
     The integral closure algorithm proceeds by finding a suitable ideal $J$,
     and then computing $Hom_R(J,J)$, and repeating these steps.  This
     optional argument limits the number of such steps to perform.
     
     The result is an integral extension, but is not necessarily integrally closed.
   Example
     R = QQ[x,y,z]/ideal(x^6-z^6-y^2*z^4-z^3);
     R' = integralClosure(R, Variable => symbol t, Limit => 2)
     trim ideal R'
     icFractions R
///

doc ///
  Key
    [integralClosure,Verbosity]
  Headline
    display a certain amount of detail about the computation
  Usage
    integralClosure(R, Verbosity => n)
  Inputs
    n:ZZ
      The higher the number, the more information is displayed.  A value
      of 0 means: keep quiet.
  Description
   Text
     When the computation takes a considerable time, this function can be used to 
     decide if it will ever finish, or to get a feel for what is happening
     during the computation.
   Example
     R = QQ[x,y,z]/ideal(x^8-z^6-y^2*z^4-z^3);
     time R' = integralClosure(R, Verbosity => 2)
     trim ideal R'
     icFractions R
  Caveat
    The exact information displayed may change.
///

doc ///
  Key
    [integralClosure,Strategy]
  Headline
    control the algorithm used
  Usage
    integralClosure(R, Strategy=>L)
  Inputs
    L:List
      of a subset of the following: {\tt RadicalCodim1, Radical, AllCodimensions, Vasconcelos, SimplifyFractions, StartWithOneMinor}
  Description
   Text
     Overall, the default options are the best.  However, sometimes one of these is dramatically 
     better (or worse!).  For the examples here, one doesn't notice much difference.
     
     {\tt RadicalCodim1} chooses an alternate, often much faster, sometimes much slower,
     algorithm for computing the radical of ideals.  This will often produce a different
     presentation for the integral closure. {\tt Radical} chooses yet another such algorithm.
     
     {\tt AllCodimensions} tells the algorithm to bypass the computation of the
     S2-ification, but in each iteration of the algorithm, use the radical of
     the extended Jacobian ideal from the previous step, instead of using only the
     codimension 1 components of that.  This is useful when for some reason the
     S2-ification is hard to compute, or if the probabilistic algorithm for 
     computing it fails.  In general though, this option slows down the computation
     for many examples.
     
     {\tt StartWithOneMinor} tells the algorithm to not compute the entire Jacobian ideal, 
     just find one element in it.  This is often a bad choice, unless the ideal is large
     enough that one can't compute the Jacobian ideal.  In the future, we plan on using
     the @TO "FastMinors::FastMinors"@ package to compute part of the Jacobian ideal.
     
     {\tt SimplifyFractions} changes the fractions to hopefully be simpler.  Sometimes it
     succeeds, yet sometimes it makes the fractions worse.  This is because of the manner
     in which fraction fields work.  We are hoping that in the future, less drastic 
     change of fractions will happen by default.

     {\tt Vasconocelos} tells the routine to instead of computing Hom(J,J),
     to instead compute Hom(J^-1, J^-1).  This is usually a more time consuming
     computation, but it does potentially get to the answer in a smaller number of steps.
   Example
     S = QQ[x,y,z]
     f = ideal (x^8-z^6-y^2*z^4-z^3)
     R = S/f
     time R' = integralClosure R
     netList (ideal R')_*
     icFractions R
   Example
     S = QQ[x,y,z]
     f = ideal (x^8-z^6-y^2*z^4-z^3)
     R = S/f
     time R' = integralClosure(R, Strategy => Radical)
     netList (ideal R')_*
     icFractions R
   Example
     S = QQ[x,y,z]
     f = ideal (x^8-z^6-y^2*z^4-z^3)
     R = S/f
     time R' = integralClosure(R, Strategy => AllCodimensions)
     netList (ideal R')_*
   Example
     S = QQ[x,y,z]
     f = ideal (x^8-z^6-y^2*z^4-z^3)
     R = S/f
     time R' = integralClosure(R, Strategy => SimplifyFractions)
     netList (ideal R')_*     
   Example
     S = QQ[x,y,z]
     f = ideal (x^8-z^6-y^2*z^4-z^3)
     R = S/f
     time R' = integralClosure (R, Strategy => RadicalCodim1)
     netList (ideal R')_*
   Example
     S = QQ[x,y,z]
     f = ideal (x^8-z^6-y^2*z^4-z^3)
     R = S/f
     time R' = integralClosure (R, Strategy => Vasconcelos)
     netList (ideal R')_*
   Example
     S = QQ[a,b,c,d]
     f = monomialCurveIdeal(S,{1,3,4})
     R = S/f
     time R' = integralClosure R
     netList (ideal R')_*
   Text
    Rational Quartic
   Example
     S = QQ[a,b,c,d]
     I = monomialCurveIdeal(S,{1,3,4})
     R = S/I
     time R' = integralClosure(R, Strategy => Radical)
     icFractions R
   Example
     S = QQ[a,b,c,d]
     I = monomialCurveIdeal(S,{1,3,4})
     R = S/I
     time R' = integralClosure(R, Strategy => AllCodimensions)
     icFractions R
   Example
     S = QQ[a,b,c,d]
     I = monomialCurveIdeal(S,{1,3,4})
     R = S/I
     time R' = integralClosure (R, Strategy => RadicalCodim1)
     icFractions R
   Example
     S = QQ[a,b,c,d]
     I = monomialCurveIdeal(S,{1,3,4})
     R = S/I
     time R' = integralClosure (R, Strategy => Vasconcelos)
     icFractions R
   Text
    Projected Veronese
   Example
     S' = QQ[symbol a .. symbol f]
     M' = genericSymmetricMatrix(S',a,3)
     I' = minors(2,M')
     center = ideal(b,c,e,a-d,d-f)
     S = QQ[a,b,c,d,e]
     p = map(S'/I',S,gens center)
     I = kernel p
     betti res I
     R = S/I
     time R' = integralClosure(R, Strategy => Radical)
     icFractions R
   Example
     S' = QQ[a..f]
     M' = genericSymmetricMatrix(S',a,3)
     I' = minors(2,M')
     center = ideal(b,e,a-d,d-f)
     S = QQ[a,b,d,e]
     p = map(S'/I',S,gens center)
     I = kernel p
     betti res I
     R = S/I
     time R' = integralClosure(R, Strategy => Radical)
     icFractions R
   Example
     S = QQ[a,b,d,e]
     R = S/sub(I,S)
     time R' = integralClosure(R, Strategy => AllCodimensions)
     icFractions R
   Example
     S = QQ[a,b,d,e]
     R = S/sub(I,S)
     time R' = integralClosure (R, Strategy => RadicalCodim1, Verbosity => 1)
     icFractions R
   Example
     S = QQ[a,b,d,e]
     R = S/sub(I,S)
     time R' = integralClosure (R, Strategy => Vasconcelos, Verbosity => 1)
     icFractions R
   Text
     One can give several of these options together.  Although note that only one
     of {\tt AllCodimensions}, {\tt RadicalCodim1}, {\tt Radical} will be used.
   Example
     S = QQ[a,b,d,e]
     R = S/sub(I,S)
     time R' = integralClosure (R, Strategy => {Vasconcelos, StartWithOneMinor}, Verbosity => 1)
     icFractions R
     ideal R'
  Caveat
   The list of strategies may change in the future! 
///

--mes--  
   --StartWithOneMinor, "vasconcelos",RadicalCodim1,AllCodimensions,SimplifyFractions
   -- Example
   --   S = QQ[x,y]
   --   f = ideal (y^4-2*x^3*y^2-4*x^5*y+x^6-x^7)
   --   R = S/f
   --   --time R' = integralClosure (R, Strategy => StartWithOneMinor)
   --   icFractions R
   -- Example
   --   S = QQ[a,b,c,d]
   --   I = monomialCurveIdeal(S,{1,3,4})
   --   R = S/I
   --   time R' = integralClosure (R, Strategy => StartWithOneMinor)
   --   icFractions R
   -- Example
   --   S = QQ[a,b,c,d]
   --   I = monomialCurveIdeal(S,{1,3,4})
   --   R = S/I
   --   time R' = integralClosure(R, Strategy => SimplifyFractions)
   --   icFractions R

   -- Example
   --   S = QQ[a,b,d,e]
   --   R = S/sub(I,S)
   --   time R' = integralClosure (R, Strategy => StartWithOneMinor)
   --   icFractions R

   -- Example
   --   S = QQ[a,b,d,e]
   --   R = S/sub(I,S)
   --   time R' = integralClosure(R, Strategy => SimplifyFractions)
   --   icFractions R
-- The use of Denominator isn't working well yet.
     -- XXX     
     -- time R' = integralClosure(R, Denominator => x*(x+4)) -- crash!
     -- time R' = integralClosure(R, Denominator => x*(x+4), Verbosity => 2) -- crash!
     -- time R' = integralClosure(R, Denominator => x, Verbosity => 2)
     -- time R' = integralClosure(R, Denominator => x+4, Verbosity => 2)


doc ///
  Key
    (integralClosure, Ideal, RingElement, ZZ)  
    (integralClosure, Ideal, ZZ)  
    (integralClosure, Ideal)
    (integralClosure, Ideal, RingElement)
  Headline
    integral closure of an ideal in an affine domain
  Usage
    integralClosure J
    integralClosure(J, d)
    integralClosure(J, f)
    integralClosure(J, f, d)
  Inputs
    J:Ideal
    f:RingElement
      optional, an element of J which is a nonzerodivisor in the ring of J.
      If not give, the first generator of {\tt J} is used
    d:ZZ
      optional, default value 1
    Keep => List
      unused
    Limit => ZZ
      unused
    Variable => Symbol
      symbol used for new variables
    Verbosity => ZZ
    Strategy => List
      of some of the symbols: AllCodimensions, SimplifyFractions, Radical, RadicalCodim1, Vasconcelos.
      These are passed on to the computation of the integral closure of the Rees algebra of {\tt J}
  Outputs
    :Ideal
      the integral closure of $J^d$
  Description
   Text
     The method used is described in Vasconcelos' book, 
     {\em Computational methods in commutative algebra and algebraic
	  geometry}, Springer, section 6.6.  Basically, one first
     computes the integral closure of the Rees Algebra of the ideal,
     and then one reads off the integral closure of any of the powers
     of the ideal, using linear algebra.
   Example
     S = ZZ/32003[a,b,c];
     F = a^2*b^2*c+a^3+b^3+c^3
     J = ideal jacobian ideal F
     time integralClosure J
     time integralClosure(J, Strategy=>{RadicalCodim1})
     J2' = integralClosure(J,2)
   Text
     Sometimes it is useful to give the specific nonzerodivisor $f$ in the ideal.
   Example
     assert(integralClosure(J, J_2, 2) == J2')
  Caveat
    It is usually much faster to use {\tt integralClosure(J,d)}
    rather than {\tt integralClosure(J^d)}.
    Also, the element {\tt f} (or the first generator of {\tt J}, if {\tt f} is not given)
    must be a nonzero divisor in the ring. This is not checked.
  SeeAlso
    (integralClosure,Ring)
    reesAlgebra
    testHunekeQuestion
///

doc ///
  Key
    idealizer
    (idealizer, Ideal, RingElement)
    [idealizer, Strategy]
    [idealizer, Verbosity]
  Headline
    compute Hom(I,I) as a quotient ring
  Usage
    (F,G) = idealizer(I,f)
  Inputs
    I:Ideal
      whose endomorphism ring we'll compute
    f:RingElement
      a nonzerodivisor in $I$
    Variable:Symbol
    Index:ZZ
    Strategy => List
     possible elements include ``Vasconcelos''
    Verbosity => ZZ
     larger numbers give more information 
  Outputs
    F:RingMap
      The inclusion map from $R$ into $S = Hom_R(I,I)$
    G:RingMap
      $frac S \rightarrow frac R$, giving the fractions
      corresponding to each generator of $S$.
  Description
   Text
     The idealizer of $I$, computed as target F, 
     is the largest subring of the fraction field of ring I in which $I$ is
     still an ideal.  Note that this is NOT the common use of the term
     in commutative algebra.
     
     This is a key subroutine used in the computation of 
     integral closures.
   Example
     R = QQ[x,y]/(y^3-x^7)
     I = ideal(x^2,y^2)
     (F,G) = idealizer(I,x^2);
     target F
     first entries G.matrix
  SeeAlso
    ringFromFractions
    integralClosure
///

document {
     Key => [makeS2,Variable],
     Headline=> "Sets the name of the indexed variables introduced in computing
     the S2-ification."
     }

document {
     Key => [idealizer,Variable],
     Headline=> "Sets the name of the indexed variables introduced in computing 
     the endomorphism ring Hom(J,J)."
     }

document {
     Key => Index,
     Headline => "Optional input for idealizer",
     PARA{},
     "This option allows the user to select the starting index for the
     new variables added in computing Hom(J,J) as a ring.  The default
     value is 0 and is what most users will use.  The option is needed
     for the iteration implemented in integralClosure."
}


document {
     Key => [idealizer, Index],
     Headline => "Sets the starting index on the new variables used to build the endomorphism ring Hom(J,J)",
     "If the program idealizer is
     used independently, the user will generally want to use the
     default value of 0.  However, when used as part of the
     integralClosure computation the number needs to start higher
     depending on the level of recursion involved. "
     }

doc ///
  Key
    icMap
    (icMap,Ring)
  Headline
    natural map from an affine domain into its integral closure
  Usage
    f = icMap R
  Inputs
    R:Ring
      an affine domain
  Outputs
    f:RingMap
      from {\tt R} to its integral closure
  Description
   Text
     If the integral closure of {\tt R} has not yet been computed, 
     that computation is performed first.  No extra computation
     is involved.  If {\tt R} is integrally closed, then the identity
     map is returned.
   Example
     R = QQ[x,y]/(y^2-x^3)
     f = icMap R
     isWellDefined f
     source f === R
     describe target f
   Text     
   
     This finite ring map can be used to compute the conductor,
     that is, the ideal of elements of {\tt R} which are 
     universal denominators for the integral closure (i.e. those d \in R
	  such that d R' \subset R).
   Example
     S = QQ[a,b,c]/ideal(a^6-c^6-b^2*c^4);
     F = icMap S;
     conductor F
  Caveat
    If you want to control the computation of the integral closure via optional
    arguments, then make sure you call @TO (integralClosure,Ring)@ first, since
    {\tt icMap} does not have optional arguments.
  SeeAlso
    (integralClosure,Ring)
    conductor
///

doc ///
  Key
    icFractions
    (icFractions, Ring)
  Headline
    fractions integral over an affine domain
  Usage
    icFractions R
  Inputs
    R:Ring
      an affine domain
  Outputs
    :List
      a list of fractions over {\tt R}, generating the
      integral closure of {\tt R}, as an {\tt R}-algebra.
  Description
   Text
     If the integral closure of {\tt R} has not yet been computed, 
     that computation is performed first.  No extra computation
     is then involved to find the fractions.
   Example
     R = QQ[x,y,z]/ideal(x^6-z^6-y^2*z^4);
     icFractions R
     R' = integralClosure R
     gens R'
     netList (ideal R')_*
   Text
     Notice that the $i$-th fraction corresponds to the $i$-th generator 
     of the integral closure.  For instance, the variable $w_(3,0) = {x^2 \over z}$.
  Caveat
     (a) Currently in Macaulay2, fractions over quotients of polynomial rings
     do not have a nice normal form.  In particular, sometimes
     the fractions are `simplified' to give much nastier looking fractions.
     We hope to improve this.

     (b)
     If you want to control the computation of the integral closure via optional
     arguments, then make sure you call @TO (integralClosure,Ring)@ first, since
     {\tt icFractions} does not have optional arguments.
  SeeAlso
    integralClosure
    icMap
///

doc ///
  Key
    conductor
    (conductor,RingMap)
    (conductor,Ring)
  Headline
    the conductor of a finite ring map
  Usage
    conductor F
    conductor R
  Inputs
    F:RingMap
      $R \rightarrow S$, a finite map with $R$ an affine reduced ring
    R:Ring
      an affine domain.  In this case, $F : R \rightarrow S$ is the 
      inclusion map of $R$ into the integral closure $S$
  Outputs
    :Ideal
      of $R$ consisting of all $d \in R$ such that $dS \subset F(R)$
  Description
   Text
     Suppose that the ring map $F : R \rightarrow S$ is finite: i.e. $S$ is a finitely 
     generated $R$-module.  The conductor of $F$ is defined to be 
     $\{ g \in R \mid g S \subset F(R) \}$.
     One way to think
     about this is that the conductor is the set of universal denominators
     of {\tt S} over {\tt R}, or as the largest ideal of {\tt R}
     which is also an ideal in {\tt S}. An important case is the
     conductor of the map from a ring to its integral closure.
   Example
     R = QQ[x,y,z]/ideal(x^7-z^7-y^2*z^5);
     icFractions R
     F = icMap R
     conductor F
   Text
     If an affine domain (a ring finitely generated over a field) is given as input,
     then the conductor of $R$ in its integral closure is returned.
   Example
     conductor R
   Text
   
     If the map is not {\tt icFractions(R)}, then @TO pushForward@ is called to compute
     the conductor.
  Caveat
    Currently this function only works if {\tt F} comes from a
    integral closure computation, or is homogeneous
  SeeAlso
    integralClosure
    icFractions
    icMap
    pushForward
///

doc ///
  Key
    ringFromFractions
    (ringFromFractions,Matrix,RingElement)
    [ringFromFractions,Variable]
    [ringFromFractions,Index]
    [ringFromFractions,Verbosity]
  Headline
    find presentation for f.g. ring  
  Usage
    (F,G) = ringFromFractions(H,f)
  Inputs
    H:Matrix
      a one row matrix over a ring $R$
    f:RingElement
    Variable => Symbol
     name of symbol used for new variables
    Index => ZZ
     the starting index for new variables
    Verbosity => ZZ
     values up to 6 are implemented. Larger values show more output.
  Outputs
    F:RingMap
      $R \rightarrow S$, where $S$ is the extension ring
      of $R$ generated by the fractions $1/f H$
    G:RingMap
      $frac S \rightarrow frac R$, the fractions
  Description
   Text
     Serious restriction: It is assumed that this ring R[1/f H] is an endomorphism ring
     of an ideal in $R$.  This means that the Groebner basis, in a product order,
     will have lead terms all quadratic monomials in the new variables,
     together with other elements which are degree 0 or 1 in the new variables.
   Example
     R = QQ[x,y]/(y^2-x^3)
     H = (y * ideal(x,y)) : ideal(x,y)
     (F,G) = ringFromFractions(((gens H)_{1}), H_0);
     S = target F
     F
     G
///

doc ///
  Key
    makeS2
    (makeS2,Ring)
    [makeS2, Verbosity]
  Headline
    compute the S2ification of a reduced ring
  Usage
    (F,G) = makeS2 R
  Inputs
    R:Ring
      an equidimensional reduced (or just unmixed and genericaly Gorenstein) affine ring
    Verbosity => ZZ
     larger values give more information.
  Outputs
    F:RingMap
      $R \rightarrow S$, where $S$ is the so-called S2-ification of $R$
    G:RingMap
      $frac S \rightarrow frac R$, giving the corresponding fractions
  Description
   Text
     A ring $S$ satisfies Serre's S2 condition if every codimension 1 ideal
     contains a nonzerodivisor and every principal ideal generated by a nonzerodivisor
     is equidimensional of codimension one.  If $R$ is an affine reduced ring, 
     then there is a unique smallest extension $R\subset S\subset {\rm frac}(R)$ satisfying S2, 
     and $S$ is finite as an $R$-module.

     Uses the method of Vasconcelos, "Computational Methods..." p. 161, taking the idealizer
     of a canonical ideal.
     
     There are other methods to compute $S$, not currently implemented in this package. See
     for example the function (S2,Module) in the package "CompleteIntersectionResolutions".

     We compute the S2-ification of the rational quartic curve in $P^3$
   Example
     A = ZZ/101[a..d];
     I = monomialCurveIdeal(A,{1,3,4})
     R = A/I;
     (F,G) = makeS2 R
  Caveat
     Assumes that first element of canonicalIdeal R is a nonzerodivisor; else returns error.
     The return value of this function is likely to change in the future
  SeeAlso
    integralClosure
///

doc ///
   Key
    testHunekeQuestion
    (testHunekeQuestion, RingElement)
   Headline
    tests a conjecture on integral closures strengthening the Eisenbud-Mazur conjecture
   Usage
    B = testHunekeQuestion f
   Inputs
    f:RingElement
   Outputs
    B:Boolean
     whether f the answer to the question is yes for f
   Description
    Text
     Background:
     
     Theorem (Saito): If R is a formal power series ring over a field of char 0, 
     and f \in R is a power series with an isolated singularity,
     then f\in j(f), the Jacobian ideal iff f becomes
     quasi-homogeneous after a change of variables. 
     
     This can be tested over an affine ring by testing f % (j(f)+ideal vars S).
     If the result is 0 we call f crypto-quasi-homogeneous.

     Theorem (Lejeune-Teisser; see Swanson-Huneke Thm 7.1.5) 
     f \in integral closure(ideal apply(numgens R,i-> x_i*df/dx_i))

     Question (Huneke): Is f actually contained in the maximal ideal
     times the integral closure of
     ideal apply(numgens R,i-> df/dx_i).
     
     Note that the answer is trivially yes if f is crypto-quasi-homogeneous.
     
     Huneke has shown that if the answer is always yes, then the Eisenbud-Mazur conjecture
     on evolutions is true.
    
    Example
     R = QQ[x,y,z]
     f = random(3,R)+random(4,R)+random(5,R)
     testHunekeQuestion f
    Text
     The function y^4-2*x^3*y^2-4*x^5*y+x^6-x^7 is defines the simplest plane curve
     singularity with 2 characteristic pairs, and is thus NOT crypto- quasi-homogeneous.
    Example
     R = QQ[x,y]
     f = (y^4-2*x^3*y^2-4*x^5*y+x^6-x^7)
     testHunekeQuestion f
   SeeAlso
    (integralClosure, Ideal)
///



document {
     Key => {icFracP, (icFracP, Ring)},
     Headline => "compute the integral closure in prime characteristic",
     Usage => "icFracP R, icFracP(R, ConductorElement => D), icFracP(R, Limit => N), icFracP(R, Verbosity => ZZ)",
     Inputs => {
	"R" => {"that is reduced, equidimensional,
           finitely and separably generated over a field of characteristic p"},
	ConductorElement => {"optionally provide a non-zerodivisor conductor element ",
               TT "ConductorElement => D", ";
               the output is then the module generators of the integral closure.
               A good choice of ", TT "D", " may speed up the calculations?"},
	Limit => {"if value N is given, perform N loop steps only"},
	Verbosity => {"if value is greater than 0, report the conductor element and number of steps in the loop"},
	},
     Outputs => {{"The module generators of the integral closure of ", TT "R",
               " in its total ring of fractions.  The generators are
               given as elements in the total ring of fractions."}
          },
     "Input is an equidimensional reduced ring in characteristic p
     that is finitely and separably generated over the base field.
     The output is a finite set of fractions that generate
     the integral closure as an ", TT "R", "-module.
     An intermediate step in the code
     is the computation of a conductor element ", TT "D",
     " that is a non-zerodivisor;
     its existence is guaranteed by the separability assumption.
     The user may supply ", TT "D",
     " with the optional ", TT "ConductorElement => D", ".
     (Sometimes, but not always, supplying ", TT "D", " speeds up the computation.)
     In any case, with the non-zero divisor ", TT "D", ",
     the algorithm starts by setting the initial approximation of the integral closure
     to be the finitely generated ", TT "R", "-module
     ", TT "(1/D)R", ",
     and in the subsequent loop the code recursively constructs submodules.
     Eventually two submodules repeat;
     the repeated module is the integral closure of ", TT "R", ".
     The user may optionally provide ", TT "Limit => N", " to stop the loop
     after ", TT "N", " steps,
     and the optional ", TT "Verbosity => 1", " reports the conductor
     element and the number of steps it took for the loop to stabilize.
     The algorithm is based on the
     Leonard--Pellikaan--Singh--Swanson algorithm.",
     PARA{},
     "A simple example.",
     EXAMPLE {
          "R = ZZ/5[x,y,z]/ideal(x^6-z^6-y^2*z^4);",
          "icFracP R"
     },
     "The user may provide an optional non-zerodivisor conductor element ",
     TT "D",
     ".  The output generators need not
     be expressed in  the form with denominator ", TT "D", ".",
     EXAMPLE {
          "R = ZZ/5[x,y,u,v]/ideal(x^2*u-y^2*v);",
          "icFracP(R)",
          "icFracP(R, ConductorElement => x)",
     },
     "In case ", TT "D", " is not in the conductor, the output is ",
     TT "V_e = (1/D) {r in R | r^(p^i) in (D^(p^i-1)) ", "for ",
     TT "i = 1, ..., e}",
     " such that ", TT "V_e = V_(e+1)", " and ", TT "e",
     " is the smallest such ", TT "e", ".",
     EXAMPLE {
	  "R=ZZ/2[u,v,w,x,y,z]/ideal(u^2*x^3+u*v*y^3+v^2*z^3);",
          "icFracP(R)",
          "icFracP(R, ConductorElement => x^2)"
     },
     "The user may also supply an optional limit on the number of steps
     in the algorithm.  In this case, the output is a finitely generated ",
     TT "R", "-module contained in ", TT "(1/D)R",
     " which contains the integral closure (intersected with ", TT "(1/D)R",
     ".",
     EXAMPLE {
          "R=ZZ/2[u,v,w,x,y,z]/ideal(u^2*x^3+u*v*y^3+v^2*z^3);",
          "icFracP(R, Limit => 1)",
          "icFracP(R, Limit => 2)",
          "icFracP(R)"
     },
     "With the option above one can for example determine how many
     intermediate modules the program should compute or did compute
     in the loop to get the integral closure.  A shortcut for finding
     the number of steps performed is to supply the ",
     TT "Verbosity => 1", " option.",
     EXAMPLE {
          "R=ZZ/3[u,v,w,x,y,z]/ideal(u^2*x^4+u*v*y^4+v^2*z^4);",
          "icFracP(R, Verbosity => 1)"
     },
     "With this extra bit of information, the user can now compute
     integral closures of principal ideals in ", TT "R", " via ",
     TO icPIdeal, ".",
     SeeAlso => {"icPIdeal", "integralClosure", "isNormal"},
     Caveat => "The interface to this algorithm will likely change eventually"
--     Caveat => "NOTE: mingens is not reliable, neither is kernel of the zero map!!!"
}

document {
     Key => ConductorElement,
     Headline => "Specifies a particular non-zerodivisor in the conductor."
}

document {
     Key => [icFracP,ConductorElement],
     Headline => "Specifies a particular non-zerodivisor in the conductor.",
     "A good choice can possibly speed up the calculations.  See ",
     TO icFracP, "."
}


document {
     Key => [icFracP,Limit],
     Headline => "Limits the number of computed intermediate modules."
--     Caveat => "NOTE: How do I make M2 put icFracP on the list of all functions that use Limit?"
}

document {
     Key => [icFracP,Verbosity],
     Headline => "Prints out the conductor element and
           the number of intermediate modules it computed.",
     Usage => "icFracP(R, Verbosity => ZZ)",
     "The main use of the extra information is in computing the
     integral closure of principal ideals in ", TT "R",
     ", via ", TO icPIdeal,
     ".",
     EXAMPLE {
          "R=ZZ/3[u,v,x,y]/ideal(u*x^2-v*y^2);",
          "icFracP(R, Verbosity => 1)",
	  "S = ZZ/3[x,y,u,v];",
          "R = S/kernel map(S,S,{x-y,x+y^2,x*y,x^2});",
	  "icFracP(R, Verbosity => 1)"
     },
}

document {
     Key => {icPIdeal,(icPIdeal, RingElement, RingElement, ZZ)},
     Headline => "compute the integral closure
                  in prime characteristic of a principal ideal",
     Usage => "icPIdeal (a, D, N)",
     Inputs => {
	"a" => {"an element in ", TT "R"},
        "D" => {"a non-zerodivisor of ", TT "R",
                " that is in the conductor"},
        "N" => {"the number of steps in ", TO icFracP,
                " to compute the integral closure of ", TT "R",
                ", by using the conductor element ", TT "D"}},
     Outputs => {{"the integral closure of the ideal ", TT "(a)", "."}},
     "The main input is an element ", TT "a",
     " which generates a principal ideal whose integral closure we are
     seeking.  The other two input elements,
     a non-zerodivisor conductor element ", TT "D",
     " and the number of steps ", TT "N", 
     " are the pieces of information obtained from ",
     TT "icFracP(R, Verbosity => true)",
     ".  (See the Singh--Swanson paper, An algorithm for computing
     the integral closure, Remark 1.4.)",
     EXAMPLE {
          "R=ZZ/3[u,v,x,y]/ideal(u*x^2-v*y^2);",
          "icFracP(R, Verbosity => 1)",
          "icPIdeal(x, x^2, 3)"
     },
     SeeAlso => {"icFracP"},
     Caveat => "The interface to this algorithm will likely change eventually"     
}

doc ///
  Key
    Keep
  Headline
    an optional argument for various functions
  SeeAlso
    (integralClosure,Ring)
    (integralClosure,Ideal,ZZ)
///

doc ///
  Key
    RadicalCodim1
  Headline
    a symbol denoting a strategy element usable with integralClosure(...,Strategy=>...)
  SeeAlso
    (integralClosure,Ring)
///

doc ///
  Key
    Radical
  Headline
    a symbol denoting a strategy element usable with integralClosure(...,Strategy=>...)
  SeeAlso
    (integralClosure,Ring)
///

doc ///
  Key
    AllCodimensions
  Headline
    a symbol denoting a strategy element usable with integralClosure(...,Strategy=>...)
  SeeAlso
    (integralClosure,Ring)
///

doc ///
  Key
    StartWithOneMinor
  Headline
    a symbol denoting a strategy element usable with integralClosure(...,Strategy=>...)
  SeeAlso
    (integralClosure,Ring)
///

doc ///
  Key
    SimplifyFractions
  Headline
    a symbol denoting a strategy element usable with integralClosure(...,Strategy=>...)
  SeeAlso
    (integralClosure,Ring)
///

doc ///
  Key
    Vasconcelos
  Headline
    a symbol denoting a strategy element usable with integralClosure(...,Strategy=>...)
  SeeAlso
    (integralClosure,Ring)
///

///
  -- This (disabled) test indicates we want to use Normaliz when computing
  -- the integral closure if a binomial ideal.
  -- disabled: because the last paragraph of 4 commands takes over 10 seconds total.
  debug IntegralClosure
  setRandomSeed 0
  S' = ZZ/101[x,y]
  S = S'/ideal(x^3 -y^2)  
  J = idealInSingLocus S
  J' = idealInSingLocus (S,Strategy => {StartWithOneMinor})
  assert(J == ideal"x2,y")
  assert(numgens J' === 1)

  degs = {1,3,4,7}
  S = ZZ/101[vars(0..length degs)]
  I = monomialCurveIdeal(S,degs)
  J = reesIdeal I

  R = (ring J)/J
  R = first flattenRing reesAlgebra I
  isHomogeneous R

  elapsedTime Jsing = idealInSingLocus R;
  CJsing = decompose ideal gens gb Jsing  
  elapsedTime  R' = integralClosure R
  assert(R' === R)
///

-- MES TODO: remove this test, or at least make it a better test.
TEST ///
-*
  restart
  debug loadPackage("IntegralClosure", Reload => true)
*-
  debug IntegralClosure
  kk=ZZ/101
  S=kk[a,b,c,d]
  I=monomialCurveIdeal(S, {3,5,6})
  R=S/I
  K = ideal(b,c)
  f=b*d
  vasconcelos(K, f)
  endomorphisms(K, f)
  codim K
  R1=ringFromFractions vasconcelos(K,f)
  R2=ringFromFractions endomorphisms(K,f)
  betti res I -- NOT depth 2.
  time integralClosure(S/I, Strategy => {Vasconcelos})
  time integralClosure(S/I, Strategy => {})
  makeS2 R
///

-- MES TODO: remove this test, or at least make it a better test.
TEST ///
-*
  restart
  debug loadPackage("IntegralClosure", Reload => true)
*-
  debug IntegralClosure
  kk=ZZ/101
  S=kk[a,b,c,d]
  I=monomialCurveIdeal(S, {3,5,6})
  M=jacobian I
  D = randomMinors(2,2,M)
  R=S/I
  J = trim substitute(ideal D ,R)
  vasconcelos (J, J_0)
  codim((J*((ideal J_0):J)):ideal(J_0))
  endomorphisms (J,J_0)
  vasconcelos (radical J, J_0)
  endomorphisms (radical J,J_0)
  codim J
  syz gens J
///

TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  S = QQ [(symbol Y)_1, (symbol Y)_2, (symbol Y)_3, (symbol Y)_4, symbol x, symbol y, Degrees => {{7, 1}, {5, 1}, {6, 1}, {6, 1}, {1, 0}, {1, 0}}, MonomialOrder => ProductOrder {4, 2}]
  J =
    ideal(Y_3*y-Y_2*x^2,Y_3*x-Y_4*y,Y_1*x^3-Y_2*y^5,Y_3^2-Y_2*Y_4*x,Y_1*Y_4-Y_2^2*y^3)
  R = S/J       
  R' = integralClosure R
  KF = frac(ring ideal R')
  M1 = first entries substitute(vars R, KF)
  M2 = apply(R.icFractions, i -> matrix{{i}})

  assert(matrix{icFractions R} == substitute(matrix {{(Y_2*y^2)/x, (Y_1*x)/y,
                  Y_1, Y_2, Y_3, Y_4, x, y}}, frac R))
///

TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  assert isNormal (QQ[x]/(x^2+1))
  assert not isNormal (QQ[x,y,z]/( ideal(x*y, z) * ideal (z-1) ))
  assert not isNormal (QQ[x,y,z]/( ideal(x*y)    * ideal (x-1,y-1) ))
  assert not isNormal (QQ[x,y,z]/( ideal(x*y, z) * ideal (x-1,y-1) ))
  assert not isNormal (QQ[x,y,z]/( ideal(x*y)    * ideal (z-1) ))
  assert not isNormal (QQ[x,y,z]/( ideal(x*y)    * ideal (z-1) ))
  assert isNormal (QQ[x,y,z,t]/( ideal (x^2+y^2+z^2,t) ))

  -- here is an example of why the ring has to be equidimensional:
  -- assert isNormal (QQ[x,y,z,t]/( ideal (x^2+y^2+z^2,t) * ideal(t-1) ))
///


TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  debug IntegralClosure
  kk=ZZ/101
  S=kk[a,b,c]
  I =ideal"a3,ac2"
  M = module ideal"a2,ac"
  f=inducedMap(M,module I)
  assert(extendIdeal(f) == ideal(a^2, a*c))
///


-- MES TODO: make this into a test.  There are no assert's here.
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  debug IntegralClosure
  kk=ZZ/2
  S=kk[a,b,c,d]
  PP = monomialCurveIdeal(S,{1,3,4})
  betti res PP
  for count from 1 to 10 list parametersInIdeal PP
  for count from 1 to 10 list canonicalIdeal (S/PP)
///     

-- MES TODO: test canonicalIdeal1 here?
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  debug IntegralClosure
  setRandomSeed 0
  A = ZZ/101[a..e]
  I = ideal"ab,bc,cd,de,ea"
  R = reesAlgebra I
  describe I
  describe R
  assert(canonicalIdeal1 R == ideal(w_4, a*b))
  assert(canonicalIdeal R == ideal(w_4, a*b))
  R1 = first flattenRing R
  assert(canonicalIdeal1 R1 == ideal(w_4, a*b))
  assert(canonicalIdeal R1 == ideal(w_4, a*b))
///


TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  debug IntegralClosure
  kk=ZZ/101
  S=kk[a,b,c,d]
  canonicalIdeal S
  PP = monomialCurveIdeal(S,{1,3,4})
  betti res PP
  R = S/PP
  w=canonicalIdeal R
  w1 = canonicalIdeal1 R -- a different, somewhat less pleasing answer...
  -- check that these two different canonical ideals are isomorphic.
  F = homomorphism (Hom(w,w1))_{0}
  ker F
  prune coker F
  assert isIsomorphism F
///     

TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  kk=ZZ/101
  S = kk[a,b,d,e]
  S' = kk[a,b,c,d,e]
  I = monomialCurveIdeal(S,{1,3,4})
  R = S/I
  J = ideal integralClosure R
  J' = ideal integralClosure(target (makeS2 R)_0)
  assert(J' == substitute(J, ring J'))
  J'' = monomialCurveIdeal(S', {1,2,3,4})
  use S'
  phi = map(S',ring J,{c,a,b,d,e})
  assert(J'' == phi J)
  use R
  assert(first icFractions R == (d^2/e))
  (f,g) = makeS2 R
  assert(isWellDefined f)
  assert(source f === R)
///     

TOOSLOW ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  createD = () -> (
      C = QQ[B1,B2,B3,B4,B5,B6];
      I =  ideal(B4*B5+B1*B6,B1*B4+B2*B4-B3*B6,B1^2+B1*B2+B3*B5,B2*B5^2-B6^2,B1*B2*
           B5+B4*B6,B3*B4^2-B6^2,B3^2*B4-B1*B6-B2*B6,B2*B3*B4-B3^2*B6-B5*B6,B3^3-B1
           *B2-B2^2+B3*B5,B1*B3^2+B1*B5+B2*B5,B1*B2*B3+B3^2*B5+B5^2,B1*B2^2+B2*B3*
           B5+B4^2,B3^2*B5^2+B5^3-B3*B4*B6,B2^3*B4-B2^2*B3*B6-B3^2*B5*B6-B4^3-B5^2*
           B6);
      D = C/I
      );

  D = createD();
  assert(numgens integralClosure(D, Strategy=>{RadicalCodim1})==numgens D+2)

  D = createD();
  assert(numgens integralClosure D == numgens D + 2)

 
  D = createD();
  assert(numgens elapsedTime integralClosure(D, Strategy => {SimplifyFractions}) == numgens D + 2)

///

TOOSLOW ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  S = ZZ/32003[a,b,c,d,x,y,z,u]
  I = ideal(
     a*x-b*y,
     b*u^7+b*u^6-2*b*z*u^4+b*u^5-2*b*z*u^3-2*b*z*u^2+3*b*z^2+c*x,
     a*u^7+a*u^6-2*a*z*u^4+a*u^5-2*a*z*u^3-2*a*z*u^2+3*a*z^2+c*y,
     b*z*u^6+9142*b*z*u^5+13715*b*z^2*u^3-9143*b*z*u^4-9145*b*u^5-13716*b*z^2*u^2-13712*b*z^2*u-13713*b*z*u^2+4568*b*z^2+9145*c*x*u-9145*c*x+4572*d*x,
     a*z*u^6+9142*a*z*u^5+13715*a*z^2*u^3-9143*a*z*u^4-9145*a*u^5-13716*a*z^2*u^2-13712*a*z^2*u-13713*a*z*u^2+4568*a*z^2+9145*c*y*u-9145*c*y+4572*d*y,
     c*u^8+7111*c*z*u^6+3556*d*u^7+10667*c*z*u^5+3556*d*u^6+14224*c*z^2*u^3+14223*c*z*u^4-7112*d*z*u^4+3556*d*u^5+10668*c*z^2*u^2-7112*d*z*u^3+7112*c*z^2*u-7112*d*z*u^2+10668*d*z^2);
  R = S/I
  time R' = integralClosure(R, Strategy=>{RadicalCodim1}) -- slightly faster than without it

  use R
  netList icFractions R
  assert isWellDefined icMap R
  assert(R' === target icMap R)
  assert(R === source icMap R)
  --assert(conductor icMap R == ideal"x,y,z-u,u2-u") -- MES: get conductor working on these...
///

-- integrally closed test
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  R = QQ[u,v]/ideal(u+2)
  time J = integralClosure (R,Variable => symbol a) 
  use ring ideal J
  assert(ideal J == ideal(u+2))
  assert(set icFractions R === set{-2_(frac R), v_(frac R)})
///

-- degrees greater than 1 test
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  R = ZZ/101[symbol x..symbol z,Degrees=>{2,5,6}]/(z*y^2-x^5*z-x^8)
  time R' = integralClosure (R,Variable => symbol b) 
  use ring ideal R'
  answer = ideal(b_(1,0)*x^2-y*z, x^6-b_(1,0)*y+x^3*z, -b_(1,0)^2+x^4*z+x*z^2)
  assert(ideal R' == answer)
  use R
  assert(conductor(R.icMap) == ideal(x^2,y))
  assert((icFractions R) == first entries substitute(matrix {{y*z/x^2, x, y, z}},frac R))
  assert isWellDefined icMap R
  assert isNormal R'
///

-- multigraded test
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  R = ZZ/101[symbol x..symbol z,Degrees=>{{1,2},{1,5},{1,6}}]/(z*y^2-x^5*z-x^8)
  time R' = integralClosure (R,Variable=>symbol a) 
  use ring ideal R'
  assert(ideal R' == ideal(-x^6+a_(1,0)*y-x^3*z,-a_(1,0)*x^2+y*z,a_(1,0)^2-x^4*z-x*z^2))
  use R
  assert(0 == matrix{icFractions R} - matrix {{y*z/x^2, x, y, z}})
  assert isWellDefined icMap R'
  assert isNormal R'
///

-- multigraded homogeneous test
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  R = ZZ/101[symbol x..symbol z,Degrees=>{{4,2},{10,5},{12,6}}]/(z*y^2-x^5*z-x^8)
  time R' = integralClosure (R,Variable=>symbol a) 
  use ring ideal R'
  assert(ideal R' == ideal(a_(1,0)*x^2-y*z,a_(1,0)*y-x^6-x^3*z,a_(1,0)^2-x^4*z-x*z^2))
  use R
  assert(0 == matrix {icFractions R} - matrix {{y*z/x^2, x, y, z}})
  assert(conductor(R.icMap) == ideal(x^2,y))
///

-- Reduced not a domain test
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  S=ZZ/101[symbol a,symbol b,symbol c, symbol d]
  I=ideal(a*(b-c),c*(b-d),b*(c-d))
  R=S/I                              
  compsR = apply(decompose ideal R, J -> S/J)
  ansR = compsR/integralClosure
  compsR/icFractions
  apply(decompose ideal R, J -> integralClosure(S/J))
  assert all(compsR/icMap, f -> f == 1)
///

--Craig's example as a test
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  S = ZZ/101[symbol x,symbol y,symbol z,MonomialOrder => Lex]
  I = ideal(x^6-z^6-y^2*z^4)
  Q = S/I
  time Q' = integralClosure (Q, Variable => symbol a)
  use ring ideal Q'
  assert(ideal Q' == ideal (x^2-a_(3,0)*z, a_(3,0)*x-a_(4,0)*z, a_(3,0)^2-a_(4,0)*x, a_(4,0)^2-y^2-z^2))
  use Q
  assert(conductor(Q.icMap) == ideal(z^3,x*z^2,x^3*z,x^4))
  assert(matrix{icFractions Q} == substitute(matrix{{x^3/z^2,x^2/z,x,y,z}},frac Q)) -- MES FLAG: this looks like z^2 is in the conductor?? possible bug?
  isNormal Q'
///

--Mike's inhomogeneous test
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  R = QQ[symbol a..symbol d]
  I = ideal(a^5*b*c-d^2)
  Q = R/I
  Q' = time integralClosure(Q,Variable => symbol x, Keep=>{})
  use ring ideal Q'
  assert(ideal Q' == ideal(x_(1,0)^2-a*b*c))
  use Q
  assert(matrix{icFractions Q} == matrix{{d/a^2,a,b,c}})
///

-- rational quartic, to make sure S2 is not being forgotten!
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  S = QQ[a..d]
  I = monomialCurveIdeal(S,{1,3,4})
  R = S/I
  R' = integralClosure R
  assert(numgens R' == 5)
  assert isNormal R'
///

--Ex from Wolmer's book - tests longer example and published result.
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  R = ZZ/101[symbol a..symbol e]
  I = ideal(a^2*b*c^2+b^2*c*d^2+a^2*d^2*e+a*b^2*e^2+c^2*d*e^2,
      a*b^3*c+b*c^3*d+a^3*b*e+c*d^3*e+a*d*e^3,
      a^5+b^5+c^5+d^5-5*a*b*c*d*e+e^5,
      a^3*b^2*c*d-b*c^2*d^4+a*b^2*c^3*e-b^5*d*e-d^6*e+3*a*b*c*d^2*e^2-a^2*b*e^4-d*e^6,
      a*b*c^5-b^4*c^2*d-2*a^2*b^2*c*d*e+a*c^3*d^2*e-a^4*d*e^2+b*c*d^2*e^3+a*b*e^5,
      a*b^2*c^4-b^5*c*d-a^2*b^3*d*e+2*a*b*c^2*d^2*e+a*d^4*e^2-a^2*b*c*e^3-c*d*e^5,
      b^6*c+b*c^6+a^2*b^4*e-3*a*b^2*c^2*d*e+c^4*d^2*e-a^3*c*d*e^2-a*b*d^3*e^2+b*c*e^5,
      a^4*b^2*c-a*b*c^2*d^3-a*b^5*e-b^3*c^2*d*e-a*d^5*e+2*a^2*b*c*d*e^2+c*d^2*e^4)
  S = R/I
  elapsedTime S' = integralClosure S
  icFractions S -- MES: Seemingly poor choice for fractions?
  M = pushForward (icMap S, S'^1);
  assert(degree (M/(M_0)) == 2) -- MES: what are we testing here?
  assert(# icFractions S == 7)

   -- this is part of the above example.  But what to really place into the test?
  StoS2 = (makeS2 S)_0;
  S2 = target StoS2 -- MES: this doesn't set fractions.  Should it?
  
-*  
  time integralClosure(S2, Verbosity => 3) -- MES: example where jacobian time is long, whole thing is very long
  M = pushForward (StoS2, S2^1);
  gens M
  N = prune(M/M_0)
  assert(degree N == 2)

  time V = integralClosure (S, Variable => X) -- MES BUG: this doesn't change variable name!
  degree S
  codim singularLocus S
  use ring ideal V

  oldanswer = ideal(a^2*b*c^2+b^2*c*d^2+a^2*d^2*e+a*b^2*e^2+c^2*d*e^2,
	   a*b^3*c+b*c^3*d+a^3*b*e+c*d^3*e+a*d*e^3,
	   a^5+b^5+c^5+d^5-5*a*b*c*d*e+e^5,
	   a*b*c^4-b^4*c*d-X_0*e-a^2*b^2*d*e+a*c^2*d^2*e+b^2*c^2*e^2-b*d^2*e^3,
	   a*b^2*c^3+X_1*d+a*b*c*d^2*e-a^2*b*e^3-d*e^5,
	   a^3*b^2*c-b*c^2*d^3-X_1*e-b^5*e-d^5*e+2*a*b*c*d*e^2,
	   a^4*b*c+X_0*d-a*b^4*e-2*b^2*c^2*d*e+a^2*c*d*e^2+b*d^3*e^2,
	   X_1*c+b^5*c+a^2*b^3*e-a*b*c^2*d*e-a*d^3*e^2,
	   X_0*c-a^2*b^2*c*d-b^2*c^3*e-a^4*d*e+2*b*c*d^2*e^2+a*b*e^4,
	   X_1*b-b*c^5+2*a*b^2*c*d*e-c^3*d^2*e+a^3*d*e^2-b*e^5,
	   X_0*b+a*b*c^2*d^2-b^3*c^2*e+a*d^4*e-a^2*b*c*e^2+b^2*d^2*e^2-c*d*e^4,
	   X_1*a-b^3*c^2*d+c*d^2*e^3,X_0*a-b*c*d^4+c^4*d*e,
	   X_1^2+b^5*c^5+b^4*c^3*d^2*e+b*c^2*d^3*e^4+b^5*e^5+d^5*e^5,
	   X_0*X_1+b^3*c^4*d^3-b^2*c^7*e+b^2*c^2*d^5*e-b*c^5*d^2*e^2-
	     a*b^2*c*d^3*e^3+b^4*c*d*e^4+a^2*b^2*d*e^5-a*c^2*d^2*e^5-b^2*c^2*e^6+b*d^2*e^7,
	   X_0^2+b*c^3*d^6+2*b^5*c*d^3*e+c*d^8*e-b^4*c^4*e^2+a^3*c^3*d^2*e^2+
	     2*a^2*b^3*d^3*e^2-5*a*b*c^2*d^4*e^2+4*b^3*c^2*d^2*e^3-3*a*d^6*e^3+
	     5*a^2*b*c*d^2*e^4-b^2*d^4*e^4-2*b*c^3*d*e^5-a^3*b*e^6+3*c*d^3*e^6-a*d*e^8)

  -- We need to check the correctness of this example!
  newanswer = ideal(
    a^2*b*c^2+b^2*c*d^2+a^2*d^2*e+a*b^2*e^2+c^2*d*e^2,
    a*b^3*c+b*c^3*d+a^3*b*e+c*d^3*e+a*d*e^3,
    a^5+b^5+c^5+d^5-5*a*b*c*d*e+e^5,
    X_1*e-a^3*b^2*c+b*c^2*d^3,
    X_1*d+a*b^2*c^3-b^5*d-d^6+3*a*b*c*d^2*e-a^2*b*e^3-d*e^5,
    X_1*c-c*d^5+a^2*b^3*e+a*b*c^2*d*e-a*d^3*e^2,
    X_1*b-b^6-b*c^5-b*d^5+4*a*b^2*c*d*e-c^3*d^2*e+a^3*d*e^2-b*e^5,
    X_1*a-a*b^5-b^3*c^2*d-a*d^5+2*a^2*b*c*d*e+c*d^2*e^3,
    X_0*e-a*b*c^4+b^4*c*d,
    X_0*d+a^4*b*c-a^2*b^2*d^2+a*c^2*d^3-a*b^4*e-b^2*c^2*d*e+a^2*c*d*e^2,
    X_0*c-2*a^2*b^2*c*d+a*c^3*d^2-a^4*d*e+b*c*d^2*e^2+a*b*e^4,
    X_0*b-a^2*b^3*d+2*a*b*c^2*d^2+a*d^4*e-a^2*b*c*e^2-c*d*e^4,
    X_0*a-a^3*b^2*d+a^2*c^2*d^2-b*c*d^4+a*b^2*c^2*e+c^4*d*e-a*b*d^2*e^2,
    X_1^2-b^10-b^5*c^5+2*a*b^2*c^3*d^4-2*b^5*d^5-d^10-5*b^4*c^3*d^2*e+6*a*b*c*d^6*e-6*a^3*b^4*d*e^2-4*b^3*c*d^4*e^2+2*a^2*b*d^4*e^3-4*a*b^3*d^2*e^4+b*c^2*d^3*e^4-b^5*e^5-d^5*e^5,
    X_0*X_1-a^2*b^7*d+b^3*c^4*d^3+a^4*b*c*d^4-a^2*b^2*d^6+a*c^2*d^7+4*b^2*c^2*d^5*e+b^6*d^2*e^2+b*c^5*d^2*e^2+3*a^2*c*d^5*e^2+b*d^7*e^2+a^4*b^3*e^3+4*c^3*d^4*e^3-2*a^3*d^3*e^4+b*d^2*e^7,
    X_0^2-a^4*b^4*d^2-a^2*c^4*d^4+7*b*c^3*d^6-2*b^5*c*d^3*e-2*c^6*d^3*e+2*a^3*b*d^5*e+5*c*d^8*e+a^3*c^3*d^2*e^2-6*a^2*b^3*d^3*e^2-a*b*c^2*d^4*e^2-2*a*b^5*d*e^3-2*b^3*c^2*d^2*e^3+5*a*d^6*e^3-a^2*b*c*d^2*e^4+a^3*b*e^6+c*d^3*e^6+a*d*e^8)

  assert(ideal V == newanswer)   
*-
///

-- Test of isNormal
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  S = ZZ/101[x,y,z]/ideal(x^2-y, x*y-z^2)
  assert not isNormal S
  assert isNormal integralClosure S
///

-- Test of icMap and conductor
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  R = QQ[x,y,z]/ideal(x^6-z^6-y^2*z^4)
  R' = integralClosure R
  F = R.icMap
  use R
  assert(conductor F == ideal(z^3,x*z^2,x^3*z,x^4))
  icFractions R -- MES BUG? again, these look like z^2 is is the conductor...
///

-- Test of not keeping the original variables
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  R = QQ[x,y]/(y^2-x^3)
  R' = integralClosure(R, Keep=>{})
  assert(numgens R' == 1)
  assert(numgens ideal R' == 0)
  assert(ring x === R)
  assert(icFractions R == {y/x})
  F = icMap R
  assert(target F === R')
  assert(source F === R)
///

--huneke2
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
  kk = ZZ/32003
  S = kk[a,b,c]
  F = a^2*b^2*c+a^4+b^4+c^4
  J = ideal jacobian ideal F
  substitute(J:F, kk) -- check local quasi-homogeneity!
  I = ideal first (flattenRing reesAlgebra J)
  betti I
  R = (ring I)/I
  --time R'=integralClosure(R, Strategy => {StartWithOneMinor}, Verbosity =>3 ) -- this is bad in the first step!
  time R' = integralClosure(R, Verbosity => 3) -- this one takes perhaps too long for a test
  assert(numgens R' == 13)
  assert(numgens ideal gens gb ideal R' == 54) -- this is not an invariant...!

  -- clear R, and do another one
  R1 = (ring I)/(ideal I_*)
  time R1'=integralClosure(R1, Verbosity => 3, Strategy => {RadicalCodim1})
  assert(numgens R1' == 13)
  assert(numgens ideal gens gb ideal R1' == 54) -- this is not an invariant!
  icFractions R1 -- MES: these fractions are messier than they could be?
///

-- see https://github.com/Macaulay2/M2/issues/933
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
    S = QQ[a..f]
    I = ideal(a*b*c,a*d*f,c*e*f,b*e*d)
    assert (integralClosure I == integralClosure trim I)
///

-- added from bug-integralClosure.m2 May 2020
TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
    debug IntegralClosure -- for extendIdeal
    S = ZZ/101[a,b,c,d]
    K =ideal(a,b)
    I = c*d*K
    M = module (c*K)
    M' = module(d*K)
    phi = map(M,module I,d*id_M)
    phi' = map(M',module I,c*id_M')
    assert(isWellDefined phi)
    assert(extendIdeal phi == c*K)
    assert(extendIdeal phi'== d*K)    
    assert(integralClosure I == I)
    assert(integralClosure ideal"a2,b2" == ideal"a2,ab,b2")
///

TEST ///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
    debug IntegralClosure -- for extendIdeal
    S = ZZ/101[a,b,c]/ideal(a^3-b*(b-c)*(b+c))
    K =ideal(a,b)
    I = c*(b+c)*K
    M = module (c*K)
    M' = module((b+c)*K)
    phi = map(M,module I,(b+c)*id_M)
    phi' = map(M',module I,c*id_M')
    assert(isWellDefined phi)
    assert(isWellDefined phi')    
    assert(extendIdeal phi == c*K)
    assert(extendIdeal phi'== (b+c)*K)    
    assert(integralClosure I == I) 
///

TEST///
-*
  restart
  loadPackage("IntegralClosure", Reload => true)
*-
    debug IntegralClosure -- for extendIdeal
    S = ZZ/101[a,b,c]/ideal(a^3-b^2*c)
    K =ideal(a,b)
    I = c*(b+c)*K
    M = module (c*K)
    M' = module((b+c)*K)
    phi = map(M,module I,(b+c)*id_M)
    phi' = map(M',module I,c*id_M')
    assert(isWellDefined phi)
    assert(isWellDefined phi')    
    assert(extendIdeal(phi)== c*K)
    assert(extendIdeal(phi')== (b+c)*K)    
    assert(integralClosure(ideal(a^2,b^2))==ideal"a2,ab,b2")
    assert(integralClosure I == I)
///

load "./IntegralClosure/HarbourneExamples.m2"
-- an example of Brian Harbourne

TEST ///
-- An example construction communicated to us by Craig Huneke
-- Start with a polynomial f (but generally not quasi-homog), 
-- consider the Jacobian ideal J, then f is in the integral closure of J.
-- Actually, is this true?
-*
    restart
    loadPackage("IntegralClosure", Reload =>true)
*-
  kk = ZZ/32003
  S = kk[x,y,z,t]
  F = poly"xy-(z-t2)(z-t3)(z-t4)"
  J = ideal jacobian ideal F
  mm = ideal vars S
  F % (J+mm*F)!=0 -- shows that F is not crypto-quasihomogeneous
  J' = integralClosure J
  assert (F % (J'+mm*F) == 0)
///

-- a homogeneous example which extends the ground field
TEST ///
  kk = QQ
  R = kk[x,y, z]
  I1 = ideal(x,y-z)
  I2 = ideal(x-3*z, y-5*z)
  I3 = ideal(x,y)
  I4 = ideal(x-5*z,y-2*z)

  I = intersect(I1^3, I2^3, I3^3, I4^3)
  F = I_0 + I_1 + I_2 + I_3
  assert isHomogeneous F
  S = R/F
  V = integralClosure S
  ring presentation V
  ideal V
  trim ideal V -- MES: should we be using this? It is much simpler
  icFractions S -- nasty fraction, is it that bad?
    -- notice that this fraction is actually algebraic over the base field
  use ring ideal V
  G = eliminate(ideal V, {x,y,z})
  assert(numgens G == 1)
  assert(isPrime G_0)  -- G_0 is a cubic over kk
///

TEST ///
  -- git issue #1117
  R = QQ[a,b,c,d,e,f]
  I = ideal(a*b*d,a*c*e,b*c*f,d*e*f);
  J = I^2;
  K = integralClosure(I,2)
  F = ideal(a*b*c*d*e*f);
  assert not isSubset(F,J)
  assert isSubset(F,K)
  assert isSubset(F^2,J^2)
  assert(K != J)
  assert(K == J + F)
///

TEST ///
  -- git issue #846
  R = QQ[x,y]
  I = ideal(x^2,y^2)
  assert(integralClosure I == ideal(x^2, x*y, y^2))
///

TEST ///
  -- bug mentioned on 22 Dec 2020 in googlegroup.
  -- fixed later in Dec 2020.
  R = QQ[x,y]/(x^3-y^2)
  I = ideal(y)
  integralClosure I
  integralClosure(I,2)
  assert(integralClosure I == ideal(y, x^2))
///

TEST ///
  R = QQ[x,y]/(x^3-y^2)
  I = ideal(y)
  integralClosure I
  integralClosure(I,2)
  assert(integralClosure I == ideal(y, x^2))
///

TEST ///
  R = QQ[x,y]
  assert(integralClosure R === R)
  assert(integralClosure ideal 1_R == ideal 1_R)
  --integralClosure ideal 0_R -- so so error message, but at least there is one.

  A = QQ[x,y,z]
  assert(A === integralClosure A)
  S = A/ker map(QQ[t],A,{t^3,t^5,t^7})
  assert(integralClosure ideal(y,z) == ideal(x^2, y, z))
///

end-------------------------------------------------------------------------

-*

loadPackage("MinimalPrimes",Reload =>true)
loadPackage("IntegralClosure",Reload =>true)

restart
installPackage("MinimalPrimes")
restart
uninstallPackage("IntegralClosure")
restart
installPackage("IntegralClosure")
check IntegralClosure
viewHelp IntegralClosure
*-

restart
uninstallPackage "IntegralClosure"
uninstallPackage "MinimalPrimes"
restart
installPackage "MinimalPrimes"
installPackage "IntegralClosure"
check IntegralClosure

viewHelp IntegralClosure
viewHelp integralClosure


loadPackage("IntegralClosure", Reload=>true)
/// MIKETEST
  -- XXX
    R = ZZ/32003[x,y,z]
    f = y^4-2*x^3*y^2-4*x^5*y+x^6-x^7+z^4

    eulerIdeal = method()
    eulerIdeal RingElement := Ideal => (f) -> (
        R := ring f;
        I := ideal jacobian ideal f;
        ideal apply(numgens R, i -> R_i * I_i)
        )

    localIsQuasiHomogeneous = method()
    localIsQuasiHomogeneous RingElement := Boolean => f -> (
        mm := ideal vars ring f;
        f % (f * mm + (ideal jacobian ideal f)) == 0
        )

    assert not localIsQuasiHomogeneous f    

    -- now get the Rees ideal of the Euler ideal
    I = eulerIdeal f
    J = reesIdeal(I, I_0, Variable => w)
    J = first flattenRing J
    A = (ring J)/J
    integralClosure(A, Strategy => {SimplifyFractions}, Verbosity => 4);
    integralClosure(A, Verbosity => 4);
///


TEST ///
  -- MES TODO: put assertions in here
  S = QQ[y,x,MonomialOrder=>Lex]
  F = poly"y5-y2+x3+x4"
  factor discriminant(F,y)
  R=S/F
  R' = integralClosure R
  icFractions R
  describe R'
///

TEST ///
  -- of idealizer
  -- MES TODO: add assertions
  S = QQ[y,x,MonomialOrder=>Lex]
  F = poly"y4-y2+x3+x4"
  factor discriminant(F,y)
  R=S/F
  L = trim radical ideal(x_R)
  (f1,g1) = idealizer(L,L_0)
  U = target f1
  K = frac R
  f1
  g1
  L = trim ideal jacobian R

  R' = integralClosure R
  icFractions R
  icMap R
///



-- Tests that Mike has added:
loadPackage "IntegralClosure"
S = ZZ/101[a..d]
I = ideal(b^2-b)
R = S/I
integralClosure(R)

-- M2 crash:
kk = QQ
R = kk[x,y,z, MonomialOrder => Lex]
p1 = ideal"x,y,z"
p2 = ideal"x,y-1,z-2"
p3 = ideal"x-2,y,5,z"
p4 = ideal"x+1,y+1,z+1"
D = trim intersect(p1^3,p2^3,p3^3,p4^3)
betti D
B = basis(4,D)
F = (super(B * random(source B, R^{-4})))_(0,0)
ideal F + ideal jacobian matrix{{F}}
decompose oo

factor F
A = R/F
loadPackage "IntegralClosure"

ideal F + ideal jacobian matrix{{F}}
decompose oo
-------------------

kk = ZZ/101
R = kk[x,y,z]
p1 = ideal"x,y,z"
p2 = ideal"x,y-1,z-2"
p3 = ideal"x-2,y,5,z"
p4 = ideal"x+1,y+1"
D = trim intersect(p1^3,p2^3,p3^3,p4^2)
betti D
B = basis(5,D)
F = (super(B * random(source B, R^{-5})))_(0,0)
factor F
A = R/F
JF = trim(ideal F + ideal jacobian matrix{{F}})
codim JF
radJF = radical(JF, Strategy=>Unmixed)
NNL = radJF
NNL = substitute(NNL,A)
(phi,fracs) = idealizer(NNL,NNL_0)
phi
#fracs

----------------------

kk = ZZ/101
R = kk[x,y,z]
p1 = ideal"x,y,z"
p2 = ideal"x,y-1,z-2"
p3 = ideal"x-2,y,5,z"
p4 = ideal"x+1,y+1"
D = trim intersect(p1^3,p2^3,p3^3,p4^3)
betti D
F = random(6,D)
factor F
A = R/F
JF = trim(ideal F + ideal jacobian matrix{{F}})
codim JF
radJF = radical(JF, Strategy=>Unmixed)
decompose radJF
elapsedTime integralClosure A -- MES TODO: 19 May 2020: 4.94 seconds on my MBP

---------------------- Birational Work
-- MES TODO: what is this block of code testing?
R = ZZ/101[b_1, x,y,z, MonomialOrder => {GRevLex => {7}, GRevLex=>{2,5,6}}]
R = ZZ/101[x,y,z]
S = R[b_1, b_0]
I = ideal(b_1*x^2-42*y*z, x^6+12*b_1*y+ x^3*z, b_1^2 - 47*x^4*z - 47*x*z^2)
I = ideal(b_1*x-42*b_0, b_0*x-y*z, x^6+12*b_1*y+ x^3*z, b_1^2 -47*x^4*z - 47*x*z^2, b_0^2-x^6*z - x^4*z^2)
leadTerm gens gb I

R = ZZ/101[x,y,z]/(z*y^2-x^5*z-x^8)
J = integralClosure(R)
R.icFractions
describe J


S=ZZ/101[symbol x,symbol y,symbol z,MonomialOrder => Lex]
I=ideal(x^6-z^6-y^2*z^4)
Q=S/I
time J = integralClosure (Q, Variable => symbol a)


S = ZZ/101[a_7,a_6,x,y,z, MonomialOrder => {GRevLex => 2, GRevLex => 3}]
Inew = ideal(x^2-a_6*z,a_6*x-a_7*z,a_6^2-a_7*x,a_7^2-y^2-z^2)
leadTerm gens gb Inew
radical ideal oo


///
-*
  restart
  loadPackage"IntegralClosure"
*-
  R=ZZ/2[x,y,Weights=>{{8,9},{0,1}}]
  I=ideal(y^8+y^2*x^3+x^9) -- eliminates x and y at some point. 
  A = R/I
  elapsedTime A' = integralClosure(A, Verbosity => 1) -- MES TODO: the ideal is messy, also: is the answer correct, given ZZ/2??

  R=ZZ/2[x,y,Weights=>{{31,12},{0,1}}]
  I=ideal"y12+y11+y10x2+y8x9+x31" -- really long, should it really be this bad?
  A = R/I
  elapsedTime A' = integralClosure(A, Verbosity => 1) -- MES TODO: pretty bad timing
  transpose gens ideal A'
///

--------------
-- Examples --
--------------
-*
Theorem (Saito): If R is a formal power series ring over a field of char 0, 
and f has isolated singularity,
then f\in R is contained in j(f), the Jacobian ideal iff f is
quasi-homogeneous after a change of variables.

Theorem (Lejeune-Teisser?; see Swanson-Huneke Thm 7.1.5) 
f \in integral closure(ideal apply(numgens R,i-> x_i*df/dx_i))

Conjecture (Huneke: f is never a minimal generator of the integral closure of
ideal apply(numgens R,i-> df/dx_i).

--the method (testHunekeQuestion, Ring, RingElement) checks this
viewHelp testHunekeQuestion
*-
debug IntegralClosure -- for testHunekeQuestion
n = 3
R = QQ[x_0..x_(n-1)]
mm = ideal vars R
f = random({3},R)+random({4},R)+random(5,R)
testHunekeQuestion f

    R = ZZ/32003[x,y,z]
    f = (y^4-2*x^3*y^2-4*x^5*y+x^6-x^7+z^4)
    --    testHunekeQuestion(R,f) -- currently too slow	   


--from Eisenbud-Neumann p.11: simplest poly with 2 characteristic pairs. 
R = QQ[y,x]
f = (y^4-2*x^3*y^2-4*x^5*y+x^6-x^7)
testHunekeQuestion f
R = R/f
time R' = integralClosure R
icFractions R
icMap R

R = QQ[y,x]/(y^2-x^4-x^7)
integralClosure R
icFractions R
icMap R

R = QQ[x,y]/(y^4-2*x^3*y^2-4*x^5*y+x^6-x^7)
time R' = integralClosure R 
icFractions R
icMap R

R = ZZ/32003[x,y,z]/(z^3*y^4-2*x^3*y^2*z^2-4*x^5*y*z+x^6*z-x^7)
isHomogeneous R
time R' = integralClosure R
icFractions R
icMap R

kk = ZZ/32003
S = kk[v,u]
I=ideal(5*v^6+7*v^2*u^4+6*u^6+21*v^2*u^3+12*u^5+21*v^2*u^2+6*u^4+7*v^2*u)
R = S/I
time R' = integralClosure R
ideal R'
icFractions R
conductor icMap R -- can't do it since not homogeneous

-- Doug Leonard example ----------------------
restart
S=ZZ/2[z19,y15,y12,x9,u9,MonomialOrder=>{Weights=>{19,15,12,9,9},Weights=>{12,9,9,9,0},1,2,2}]

I = ideal(
     y15^2+y12*x9*u9,
     y15*y12+x9^2*u9+x9*u9^2+y15,
     y12^2+y15*x9+y15*u9+y12,
     z19^3+y12*x9^3*u9^2+z19*y15*x9*u9+y15*x9^3*u9+y15*x9^2*u9^2
       +z19*y12*x9*u9+z19*y15*u9+z19*y12*u9+y12*x9^2*u9)

isHomogeneous I
R = S/I;

time icFractions R -- MES TODO: correct? better basis choice?
errorDepth=0
time A = icFracP R -- MES TODO: pretty long
time A = integralClosure R;
----------------------------------------------
-- Another example from Doug Leonard
S = ZZ/2[z19,y15,y12,x9,u9,MonomialOrder=>{Weights=>{19,15,12,9,9},Weights=>{12,9,9,9,0},1,2,2}]
I = ideal(y15^3+x9*u9*y15+x9^3*u9^2+x9^2*u9^3,y15^2+y12*x9*u9,z19^3+(y12+y15)*(x9+1)*u9*z19+(y12*(x9*u9+1)+y15*(x9+u9))*x9^2*u9)
R = S/I
time A = integralClosure R;

S = ZZ/2[z19,y15,y12,x9,u9,MonomialOrder=>{Weights=>{19,15,12,9,9},Weights=>{12,9,9,9,0},1,2,2}]
I = ideal(
     y15^2+y12*x9*u9,
     y15*y12+x9^2*u9+x9*u9^2+y15,
     y12^2+y15*x9+y15*u9+y12,
     z19^3+y12*x9^3*u9^2+z19*y15*x9*u9+y15*x9^3*u9+y15*x9^2*u9^2
       +z19*y12*x9*u9+z19*y15*u9+z19*y12*u9+y12*x9^2*u9)
isHomogeneous I
R = S/I;
time A = integralClosure R;

errorDepth=0
time A = icFracP R
time A = integralClosure R;
icFractions R
----------------------------------------------
restart
load "IntegralClosure.m2"
S = ZZ/32003[x,y];
F = (y^2-3/4*y-15/17)^3-9*y*(y^2-3/4*y-15/17*x)-27*x^11
R = S/F
time R' = integralClosure R
assert(R === R')
use ring F
factor discriminant(F,y)
factor discriminant(F,x)
----------------------------------------------

restart
load "IntegralClosure.m2"

S=ZZ/2[x,y,Weights=>{{8,9},{0,1}}]
I=ideal(y^8+y^2*x^3+x^9) -- eliminates x and y at some point. 
R=S/I
time R'=integralClosure(R, Strategy => {StartWithOneMinor})--, Verbosity =>3 )
time R'=integralClosure(R)--, Verbosity =>3)
icFractions R

S=ZZ/2[x,y,Weights=>{{31,12},{0,1}}]
I=ideal"y12+y11+y10x2+y8x9+x31" 
R = S/I
time R'=integralClosure(R) -- really long?
transpose gens ideal S

S=ZZ/2[x,y]
I=ideal"y12+y11+y10x2+y8x9+x31" 
R = S/I
time R'=integralClosure(R) -- really long?
transpose gens ideal S
icFracP R -- very much faster!
----------------------------------------------

-- MES TODO: this doesn't run.
restart
-- in IntegralClosure dir:
load "IntegralClosure/runexamples.m2"
runExamples(H,10,Verbosity=>3)


restart
load "IntegralClosure.m2"
kk = QQ
S = kk[x,y,u]
R = S/(u^2-x^3*y^3)
time integralClosure R

-- another example from Doug Leonard ----------------------------
S=ZZ/2[z,y,x,MonomialOrder=>{Weights=>{32,21,14}}];
I=ideal(z^7+x^5*(x+1)^5*(x^2+x+1)^3,y^2+y*x+x*(x^2+x+1));
R=S/I;
time P=presentation(integralClosure(R));    -- used 4.73 seconds
toString(gens gb P)

-- MES TODO: FractionalIdeals is not here!
loadPackage "FractionalIdeals"
S=ZZ/2[z,y,x,MonomialOrder=>{2,1}];
I=ideal(z^7+x^5*(x+1)^5*(x^2+x+1)^3,y^2+y*x+x*(x^2+x+1));
R=S/I;
time integralClosureHypersurface(R) -- doesn't work yet
use R
time integralClosureDenominator(R,x^16+x^14+x^13+x^11+x^10+x^8+x^7+x^5)
-----------------------------------------------------------------

///
R = ZZ/101[a,b,c,Degrees=>{{1,1,0},{1,0,0},{0,0,2}}]
L = {2,2,null}
basisOfDegreeD({2,null,2}, S)

S = ZZ/101[vars(0..10), Degrees => {{2, 6}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}}]
basisOfDegreeD({2,null}, S)
///



--start of file "bug-integralClosure.m2"



--family of inhomogeneous examples suggested by craig:
--integral dependence of a power series on its derivatives.
restart
--needs "bug-integralClosure.m2"
kk = ZZ/101
S = kk[a,b]
mm = ideal vars S
T = kk[t]
f = (ker map(T,S,{t^4,t^6+t^7}))_0
R = S/f
R' = integralClosure R
vars R'
see ideal R'
netList (ideal R')_*
--the simplest plane curve singularity with 2 characteristic pairs,
--thus NOT quasi-homogeneous.
--f could be any polynomial, preferably inhomogeneous, since then it's not obvious.
I = ideal diff(vars S,f)
assert(f%(I+f*mm)!=0)--f is not even locally in I
J = integralClosure I
assert(f%J != 0)--f is not in the integral closure of I; but 
assert(f % (J+f*mm) == 0) --f IS locally in the integral closure of I

IR' = sub (I, R')
elapsedTime integralClosure (IR', Verbosity => 4)
---------------------------
--examples made with Dedekind-Mertens theorem
--Dedekind-Mertens example
--Let c(f,x) be the content of f with respect to the variable x. 
--Theorem: c(f,x)*c(g,x) is integral over c(f*g, x).
restart
loadPackage ("IntegralClosure", Reload=>true)
setRandomSeed 0
kk = QQ
S = kk[a,b,c]
f = random(2,S)
g = random(3,S)
f' = f-sub(f, {S_0=>0,S_2=>0})
g' = g-sub(g, {S_0=>0,S_2=>0})
If = content(f',S_1)
Ig = content(g',S_1)
Ifg = content(f'*g',S_1)
assert((gens(If*Ig) % Ifg)!=0)
assert(gens(If*Ig) % integralClosure Ifg == 0)


setRandomSeed 0
kk = ZZ/32003
S = kk[a,b,c,d]
phi = map(S,S,{S_0}|toList((numgens S -1):0))
f = random(4,S)
g = random(4,S)
f' = f- phi f
g' = g- phi g
If = content(f',S_0)
Ig = content(g',S_0)
--Ig = content(g'^2,S_0)

Ifg = content(f'*g',S_0)
assert((gens(If*Ig) % Ifg)!=0)
elapsedTime assert(gens(If*Ig) % integralClosure(Ifg, Verbosity => 4) == 0)
--slow in extendIdeal!
--bug when minPrimes is used.

setRandomSeed 0
kk = ZZ/32003
S = kk[a,b,c]
phi = map(S,S,{S_0}|toList((numgens S -1):0))
f = random(4,S)
g = random(4,S)
f' = f- phi f
g' = g- phi g
If = content(f'^2,S_0)
Ig = content(g'^2,S_0)
Ifg = content(f'^2*g'^2,S_0)
assert((gens(If*Ig) % Ifg)!=0)
assert(gens(If*Ig) % integralClosure Ifg == 0)


setRandomSeed 0
kk = ZZ/32003
S = kk[a,b,c,d]
phi = map(S,S,{S_0}|toList((numgens S -1):0))
f = random(3,S)
g = random(4,S)
f' = f- phi f
g' = g- phi g
If = content(f',S_0)
Ig = content(g',S_0)
--Ig = content(g'^2,S_0)

Ifg = content(f'*g',S_0)
assert((gens(If*Ig) % Ifg)!=0)
elapsedTime assert(gens(If*Ig) % integralClosure(Ifg, Verbosity => 4) == 0)
elapsedTime integralClosure Ifg


-- MES: this is me playing around trying to find better fractions, can be removed.
use ring ideal R'
contract(w_(2,0), gens ideal R')
ideal R'

use R'
use R
f = y^3 + 6*y^2 - 16*y
g = 2*x-y
(ideal g) : (ideal f)

-- eliminate: error: expected a polynomial ring over ZZ or a field
denoms = (ideal g) : (ideal f)
lift(denoms, ambient R)
eliminate(oo, S_1)
radical((ideal g) : (ideal f))
lift(oo, S)
ideal gens gb oo
eliminate(oo, S_1)

-- write it with denominator x^3*(x+4)
((x^3*(x+4) * f)) // g
----- MES: can be removed above this line --

restart
loadPackage("IntegralClosure", Reload => true)
needsPackage "Normaliz"

-- Bug in program.  Perhaps the missing variable is causing issues?
R = ZZ/101[x,y,z]
I = ideal(y^2-x^3)
normalToricRing(I, t) -- gives an error.

-- How does the following give me any info about the integral closure?
-- (It probably does, but how?)
R = ZZ/101[x,y]
I = ideal(y^2-x^3)
normalToricRing(I, t)

-- This is correct, how can I get the actual fractions added?
-- Can I?  Or the image of R in this new ring?
R = ZZ/101[a,b,c,d]
I = monomialCurveIdeal(R, {1,3,4})
normalToricRing(I, t)

----- below this line: TODO --------------------------------------------------------------

-*TODO next: 
documentation (strategies); 

StartWithOneMinor
*AllCodimensions
*RadicalCodimOne
Radical
SimplifyFractions

StartWithS2

Vasconcelos??

ConductorElement??

correctness; makeS2; 
use Normaliz where possible?; 
FastMinors?
*-

--- Should Singh/Swanson be an option to integralClosure or its own
--- program.  Right now it is well documented on its own.  I'm not
--- sure what is best long term. 

-- MES TODO. The following are either to be removed or placed above.
--     "canonicalIdeal", 
--     "parametersInIdeal",
--     "randomMinors",
     "endomorphisms",
     "vasconcelos",
     "Endomorphisms", -- compute end(I)
     "Vasconcelos", -- compute end(I^-1).  If both this and Endomorphisms are set:
                 -- compare them.
     "StartWithS2", -- compute S2-ification first
     "RecomputeJacobian",
     "S2First", 
     "S2Last", 
     "S2None", -- when to do S2-ification
     "RadicalBuiltin" -- true: use 'intersect decompose' to get radical, other wise use 'rad' in PrimaryDecomposition package


