source: git/Singular/LIB/hnoether.lib @ 76cee4e

spielwiese
Last change on this file since 76cee4e was 76cee4e, checked in by Hans Schönemann <hannes@…>, 16 years ago
*hannes: added nvars test for squarefree git-svn-id: file:///usr/local/Singular/svn/trunk@10453 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100644
File size: 152.8 KB
Line 
1version="$Id: hnoether.lib,v 1.56 2007-12-04 18:03:43 Singular Exp $";
2category="Singularities";
3info="
4LIBRARY:  hnoether.lib   Hamburger-Noether (Puiseux) Expansion
5AUTHORS:   Martin Lamm,      lamm@mathematik.uni-kl.de
6           Christoph Lossen, lossen@mathematik.uni-kl.de
7
8OVERVIEW:
9 A library for computing the Hamburger-Noether expansion (analogue of
10 Puiseux expansion over fields of arbitrary characteristic) of a reduced
11 plane curve singularity following [Campillo, A.: Algebroid curves in
12 positive characteristic, Springer LNM 813 (1980)]. @*
13 The library contains also procedures for computing the (topological)
14 numerical invariants of plane curve singularities.
15
16MAIN PROCEDURES:
17 hnexpansion(f [,\"ess\"]); Hamburger-Noether (HN) expansion of f
18 develop(f [,n]);           HN expansion of irreducible plane curve germs
19 extdevelop(hne,n);         extension of the H-N expansion hne of f
20 param(hne [,s]);           parametrization of branches described by HN data
21 displayHNE(hne);           display HN expansion as an ideal
22 invariants(hne);           invariants of f, e.g. the characteristic exponents
23 displayInvariants(hne);    display invariants of f
24 multsequence(hne);         sequence of multiplicities
25 displayMultsequence(hne);  display sequence of multiplicities
26 intersection(hne1,hne2);   intersection multiplicity of two local branches
27 is_irred(f);               test whether f is irreducible as power series
28 delta(f);                  delta invariant of f
29 newtonpoly(f);             (local) Newton polygon of f
30 is_NND(f);                 test whether f is Newton non-degenerate
31
32
33AUXILIARY PROCEDURES:
34 stripHNE(hne);             reduce amount of memory consumed by hne
35 puiseux2generators(m,n);   convert Puiseux pairs to generators of semigroup
36 separateHNE(hne1,hne2);    number of quadratic transf. needed for separation
37 squarefree(f);             a squarefree divisor of the poly f
38 allsquarefree(f,l);        the maximal squarefree divisor of the poly f
39 further_hn_proc();         show further procedures useful for interactive use
40
41KEYWORDS: Hamburger-Noether expansion; Puiseux expansion; curve singularities
42";
43
44// essdevelop(f);             HN expansion of essential branches
45// multiplicities(hne);       multiplicities of blowed up curves
46
47///////////////////////////////////////////////////////////////////////////////
48LIB "primitiv.lib";
49LIB "inout.lib";
50LIB "sing.lib";
51
52///////////////////////////////////////////////////////////////////////////////
53
54proc further_hn_proc()
55"USAGE: further_hn_proc();
56NOTE:  The library @code{hnoether.lib} contains some more procedures which
57       are not shown when typing @code{help hnoether.lib;}. They may be useful
58       for interactive use (e.g. if you want to do the calculation of an HN
59       development \"by hand\" to see the intermediate results), and they
60       can be enumerated by calling @code{further_hn_proc()}. @*
61       Use @code{help <procedure>;} for detailed information about each of
62       them.
63"
64{
65 "
66 The following procedures are also part of `hnoether.lib':
67
68 getnm(f);           intersection pts. of Newton polygon with axes
69 T_Transform(f,Q,N); returns f(y,xy^Q)/y^NQ (f: poly, Q,N: int)
70 T1_Transform(f,d,M); returns f(x,y+d*x^M)  (f: poly,d:number,M:int)
71 T2_Transform(f,d,M,N,ref);   a composition of T1 & T
72 koeff(f,I,J);       gets coefficient of indicated monomial of poly f
73 redleit(f,S,E);     restriction of monomials of f to line (S-E)
74 leit(f,n,m);        special case of redleit (for irred. polynomials)
75 testreducible(f,n,m); tests whether f is reducible
76 charPoly(f,M,N);    characteristic polynomial of f
77 find_in_list(L,p);  find int p in list L
78 get_last_divisor(M,N); last divisor in Euclid's algorithm
79 factorfirst(f,M,N); try to factor f without `factorize'
80 factorlist(L);      factorize a list L of polynomials
81 referencepoly(D);   a polynomial f s.t. D is the Newton diagram of f";
82
83//       static procedures not useful for interactive use:
84// polytest(f);        tests coefficients and exponents of poly f
85// extractHNEs(H,t);   extracts output H of HN to output of hnexpansion
86// HN(f,grenze);       recursive subroutine for hnexpansion
87// constructHNEs(...); subroutine for HN
88}
89example
90{ echo=2;
91  further_hn_proc();
92}
93///////////////////////////////////////////////////////////////////////////////
94
95proc getnm (poly f)
96"USAGE:   getnm(f); f bivariate polynomial
97RETURN:  intvec(n,m) : (0,n) is the intersection point of the Newton
98         polygon of f with the y-axis, n=-1 if it doesn't exist
99         (m,0) is its intersection point with the x-axis,
100         m=-1 if this point doesn't exist
101ASSUME:  ring has ordering `ls' or `ds'
102EXAMPLE: example getnm; shows an example
103"
104{
105 // assume being called by develop ==> ring ordering is ls (ds would also work)
106 return(ord(subst(f,var(1),0)),ord(subst(f,var(2),0)));
107}
108example
109{ "EXAMPLE:"; echo = 2;
110   ring r = 0,(x,y),ds;
111   poly f = x5+x4y3-y2+y4;
112   getnm(f);
113}
114///////////////////////////////////////////////////////////////////////////////
115
116proc leit (poly f, int n, int m)
117"USAGE:   leit(f,n,m);  poly f, int n,m
118RETURN:  all monomials on the line from (0,n) to (m,0) in the Newton diagram
119EXAMPLE: example leit;  shows an example
120"
121{
122 return(jet(f,m*n,intvec(n,m))-jet(f,m*n-1,intvec(n,m)))
123}
124example
125{ "EXAMPLE:"; echo = 2;
126   ring r = 0,(x,y),ds;
127   poly f = x5+x4y3-y2+y4;
128   leit(f,2,5);
129}
130///////////////////////////////////////////////////////////////////////////////
131proc testreducible (poly f, int n, int m)
132"USAGE:   testreducible(f,n,m);  f poly, n,m int
133RETURN:  1 if there are points in the Newton diagram below the line (0,n)-(m,0)
134         0 else
135EXAMPLE: example testreducible;  shows an example
136"
137{
138 return(size(jet(f,m*n-1,intvec(n,m))) != 0)
139}
140example
141{ "EXAMPLE:"; echo = 2;
142  ring rg=0,(x,y),ls;
143  testreducible(x2+y3-xy4,3,2);
144}
145///////////////////////////////////////////////////////////////////////////////
146proc T_Transform (poly f, int Q, int N)
147"USAGE:   T_Transform(f,Q,N);  f poly, Q,N int
148RETURN:  f(y,xy^Q)/y^NQ   if x,y are the ring variables
149NOTE:    this is intended for irreducible power series f
150EXAMPLE: example T_Transform;  shows an example
151"
152{
153 map T = basering,var(2),var(1)*var(2)^Q;
154 return(T(f)/var(2)^(N*Q));
155}
156example
157{ "EXAMPLE:"; echo = 2;
158  ring exrg=0,(x,y),ls;
159  export exrg;
160  T_Transform(x3+y2-xy3,1,2);
161  kill exrg;
162}
163///////////////////////////////////////////////////////////////////////////////
164proc T1_Transform (poly f, number d, int Q)
165"USAGE:   T1_Transform(f,d,Q);  f poly, d number, Q int
166RETURN:  f(x,y+d*x^Q)   if x,y are the ring variables
167EXAMPLE: example T1_Transform;  shows an example
168"
169{
170 map T1 = basering,var(1),var(2)+d*var(1)^Q;
171 return(T1(f));
172}
173example
174{ "EXAMPLE:"; echo = 2;
175  ring exrg=0,(x,y),ls;
176  export exrg;
177  T1_Transform(y2-2xy+x2+x2y,1,1);
178  kill exrg;
179}
180///////////////////////////////////////////////////////////////////////////////
181
182proc T2_Transform (poly f_neu, number d, int M, int N, poly refpoly)
183"USAGE:   T2_Transform(f,d,M,N,ref); f poly, d number; M,N int; ref poly
184RETURN:  list: poly T2(f,d',M,N), number d' in \{ d, 1/d \}
185ASSUME:  ref has the same Newton polygon as f (but can be simpler)
186         for this you can e.g. use the proc `referencepoly' or simply f again
187COMMENT: T2 is a composition of T_Transform and T1_Transform; the exact
188         definition can be found in  Rybowicz: `Sur le calcul des places ...'
189         or in  Lamm: `Hamburger-Noether-Entwicklung von Kurvensingularitaeten'
190SEE ALSO: T_Transform, T1_Transform, referencepoly
191EXAMPLE: example T2_Transform;  shows an example
192"
193{
194 //---------------------- compute gcd and extgcd of N,M -----------------------
195  int ggt=gcd(M,N);
196  M=M/ggt; N=N/ggt;
197  list ts=extgcd(M,N);
198  int tau,sigma=ts[2],-ts[3];
199  int s,t;
200  poly xp=var(1);
201  poly yp=var(2);
202  poly hilf;
203  if (sigma<0) { tau=-tau; sigma=-sigma;}
204 // es gilt: 0<=tau<=N, 0<=sigma<=M, |N*sigma-M*tau| = 1 = ggT(M,N)
205  if (N*sigma < M*tau) { d = 1/d; }
206 //--------------------------- euklid. Algorithmus ----------------------------
207  int R;
208  int M1,N1=M,N;
209  for ( R=M1%N1; R!=0; ) { M1=N1; N1=R; R=M1%N1;}
210  int Q=M1 / N1;
211  map T1 = basering,xp,yp+d*xp^Q;
212  map Tstar=basering,xp^(N-Q*tau)*yp^tau,xp^(M-sigma*Q)*yp^sigma;
213  if (defined(HNDebugOn)) {
214   "Trafo. T2: x->x^"+string(N-Q*tau)+"*y^"+string(tau)+", y->x^"
215    +string(M-sigma*Q)+"*y^"+string(sigma);
216   "delt =",d,"Q =",Q,"tau,sigma =",tau,sigma;
217  }
218 //------------------- Durchfuehrung der Transformation T2 --------------------
219  f_neu=Tstar(f_neu);
220  refpoly=Tstar(refpoly);
221  //--- dividiere f_neu so lange durch x & y, wie die Division aufgeht,
222  //    benutze ein Referenzpolynom mit gleichem Newtonpolynom wie f_neu zur
223  //    Beschleunigung: ---
224  for (hilf=refpoly/xp; hilf*xp==refpoly; hilf=refpoly/xp) {refpoly=hilf; s++;}
225  for (hilf=refpoly/yp; hilf*yp==refpoly; hilf=refpoly/yp) {refpoly=hilf; t++;}
226  f_neu=f_neu/(xp^s*yp^t);
227  return(list(T1(f_neu),d));
228}
229example
230{ "EXAMPLE:"; echo = 2;
231  ring exrg=0,(x,y),ds;
232  export exrg;
233  poly f=y2-2x2y+x6-x5y+x4y2;
234  T2_Transform(f,1/2,4,1,f);
235  T2_Transform(f,1/2,4,1,referencepoly(newtonpoly(f,1)));
236  // if  size(referencepoly) << size(f)  the 2nd example would be faster
237  referencepoly(newtonpoly(f,1));
238  kill exrg;
239}
240///////////////////////////////////////////////////////////////////////////////
241
242proc koeff (poly f, int I, int J)
243"USAGE:   koeff(f,I,J); f bivariate polynomial, I,J integers
244RETURN:  if f = sum(a(i,j)*x^i*y^j), then koeff(f,I,J)= a(I,J) (of type number)
245NOTE:    J must be in the range of the exponents of the 2nd ring variable
246EXAMPLE: example koeff;  shows an example
247"
248{
249  matrix mat = coeffs(coeffs(f,var(2))[J+1,1],var(1));
250  if (size(mat) <= I) { return(0);}
251  else { return(leadcoef(mat[I+1,1]));}
252}
253example
254{ "EXAMPLE:"; echo = 2;
255  ring r=0,(x,y),dp;
256  koeff(x2+2xy+3xy2-x2y-2y3,1,2);
257}
258///////////////////////////////////////////////////////////////////////////////
259
260proc squarefree (poly f)
261"USAGE:  squarefree(f);  f poly
262ASSUME:  f is a bivariate polynomial (in the first 2 ring variables).
263RETURN:  poly, a squarefree divisor of f.
264NOTE:    Usually, the return value is the greatest squarefree divisor, but
265         there is one exception: factors with a p-th root, p the
266         characteristic of the basering, are lost.
267SEE ALSO: allsquarefree
268EXAMPLE: example squarefree; shows some examples.
269"
270{
271 //----------------- Wechsel in geeigneten Ring & Variablendefinition ---------
272  def altring = basering;
273  int e;
274  int gcd_ok=1;
275  string mipl="0";
276  if (nvars(basering)!=2)
277  { ERROR("basering must have exactly 2 variables for Hnoether::squarefree"); }
278  if (size(parstr(altring))==1) { mipl=string(minpoly); }
279 //---- test: char = (p^k,a) (-> gcd not implemented) or (p,a) (gcd works) ----
280  if ((char(basering)!=0) and (charstr(basering)!=string(char(basering)))) {
281    string tststr=charstr(basering);
282    tststr=tststr[1..find(tststr,",")-1];           //-> "p^k" bzw. "p"
283    gcd_ok=(tststr==string(char(basering)));
284  }
285  execute("ring rsqrf = ("+charstr(altring)+"),(x,y),dp;");
286  if ((gcd_ok!=0) && (mipl!="0")) { execute("minpoly="+mipl+";"); }
287  poly f=fetch(altring,f);
288  poly dif,g,l;
289  if ((char(basering)==0) and (charstr(basering)!=string(char(basering)))
290      and (mipl!="0")) {
291    gcd_ok=0;                   // since Singular 1.2 gcd no longer implemented
292  }
293  if (gcd_ok!=0) {
294 //--------------------- Berechne f/ggT(f,df/dx,df/dy) ------------------------
295    dif=diff(f,x);
296    if (dif==0) { g=f; }        // zur Beschleunigung
297    else { g=gcd(f,dif); }
298    if (g!=1) {                 // sonst schon sicher, dass f quadratfrei
299     dif=diff(f,y);
300     if (dif!=0) { g=gcd(g,dif); }
301    }
302    if (g!=1) {
303     e=0;
304     if (g==f) { l=1; }         // zur Beschleunigung
305     else {
306       module m=syz(ideal(g,f));
307       if (deg(m[2,1])>0) {
308         "!! The Singular command 'syz' has returned a wrong result !!";
309         l=1;                   // Division f/g muss aufgehen
310       }
311       else { l=m[1,1]; }
312     }
313    }
314    else { e=1; }
315  }
316  else {
317 //------------------- Berechne syz(f,df/dx) oder syz(f,df/dy) ----------------
318 //-- Achtung: Ist f reduzibel, koennen Faktoren mit Ableitung Null verloren --
319 //-- gehen! Ist aber nicht weiter schlimm, weil char (p^k,a) nur im irred.  --
320 //-- Fall vorkommen kann. Wenn f nicht g^p ist, wird auf jeden Fall         --
321 //------------------------ ein Faktor gefunden. ------------------------------
322    dif=diff(f,x);
323    if (dif == 0) {
324     dif=diff(f,y);
325     if (dif==0) { e=2; l=1; } // f is of power divisible by char of basefield
326     else { l=syz(ideal(dif,f))[1,1];  // x^p+y^(p-1) abgedeckt
327            if (subst(f,x,0)==0) { l=l*x; }
328            if (deg(l)==deg(f))  { e=1;}
329            else {e=0;}
330     }
331    }
332    else { l=syz(ideal(dif,f))[1,1];
333           if (subst(f,y,0)==0) { l=l*y; }
334           if (deg(l)==deg(f))  { e=1;}
335           else {e=0;}
336    }
337  }
338 //--------------- Wechsel in alten Ring und Rueckgabe des Ergebnisses --------
339  setring altring;
340  if (e==1) { return(f); }    // zur Beschleunigung
341  else {
342   poly l=fetch(rsqrf,l);
343   return(l);
344  }
345}
346example
347{ "EXAMPLE:"; echo = 2;
348 ring exring=3,(x,y),dp;
349 squarefree((x3+y)^2);
350 squarefree((x+y)^3*(x-y)^2); // Warning: (x+y)^3 is lost
351 squarefree((x+y)^4*(x-y)^2); // result is (x+y)*(x-y)
352}
353///////////////////////////////////////////////////////////////////////////////
354
355proc allsquarefree (poly f, poly l)
356"USAGE : allsquarefree(f,g);  f,g poly
357ASSUME: g is the output of @code{squarefree(f)}.
358RETURN: the greatest squarefree divisor of f.
359NOTE  : This proc uses factorize to get the missing factors of f not in g and,
360        therefore, may be slow.
361SEE ALSO: squarefree
362EXAMPLE: example allsquarefree;  shows an example
363"
364{
365 //------------------------ Wechsel in geeigneten Ring ------------------------
366 def altring = basering;
367 string mipl="0";
368 if (size(parstr(altring))==1) { mipl=string(minpoly); }
369 if ((char(basering)!=0) and (charstr(basering)!=string(char(basering)))) {
370   string tststr=charstr(basering);
371   tststr=tststr[1..find(tststr,",")-1];           //-> "p^k" bzw. "p"
372   if (tststr!=string(char(basering))) {
373     " Sorry -- not implemented for this ring (gcd doesn't work)";
374     return(l);
375   }
376 }
377 execute("ring rsqrf = ("+charstr(altring)+"),(x,y),dp;");
378 if (mipl!="0") { execute("minpoly="+mipl+";"); }
379 poly f=fetch(altring,f);
380 poly l=fetch(altring,l);
381 //---------- eliminiere bereits mit squarefree gefundene Faktoren ------------
382 poly g=l;
383 while (deg(g)!=0) {
384   f=syz(ideal(g,f))[1,1];                         // f=f/g;
385   g=gcd(f,l);
386 }                                                 // jetzt f=h^p
387 //--------------- Berechne uebrige Faktoren mit factorize --------------------
388 if (deg(f)>0) {
389  g=1;
390//*CL old:  ideal factf=factorize(f,1);
391//*         for (int i=1; i<=size(factf); i++) { g=g*factf[i]; }
392  ideal factf=factorize(f)[1];
393  for (int i=2; i<=size(factf); i++) { g=g*factf[i]; }
394  poly testp=squarefree(g);
395  if (deg(testp)<deg(g)) {
396    "!! factorize has not worked correctly !!";
397    if (testp==1) {" We cannot proceed ..."; g=1;}
398    else {" But we could recover some factors..."; g=testp;}
399  }
400  l=l*g;
401 }
402 //--------------- Wechsel in alten Ring und Rueckgabe des Ergebnisses --------
403 setring altring;
404 l=fetch(rsqrf,l);
405 return(l);
406}
407example
408{ "EXAMPLE:"; echo = 2;
409  ring exring=7,(x,y),dp;
410  poly f=(x+y)^7*(x-y)^8;
411  poly g=squarefree(f);
412  g;                      // factor x+y lost, since characteristic=7
413  allsquarefree(f,g);     // all factors (x+y)*(x-y) found
414}
415///////////////////////////////////////////////////////////////////////////////
416
417proc is_irred (poly f)
418"USAGE:   is_irred(f); f poly
419ASSUME:  f is a squarefree bivariate polynomial (in the first 2 ring
420         variables).
421RETURN:  int (0 or 1): @*
422         - @code{is_irred(f)=1} if f is irreducible as a formal power
423         series over the algebraic closure of its coefficient field (f
424         defines an analytically irreducible curve at zero), @*
425         - @code{is_irred(f)=0} otherwise.
426NOTE:    0 and units in the ring of formal power series are considered to be
427         not irreducible.
428KEYWORDS: irreducible power series
429EXAMPLE: example is_irred;  shows an example
430"
431{
432  int pl=printlevel;
433  printlevel=-1;
434  list hnl=develop(f,-1);
435  printlevel=pl;
436  return(hnl[5]);
437}
438example
439{ "EXAMPLE:"; echo = 2;
440  ring exring=0,(x,y),ls;
441  is_irred(x2+y3);
442  is_irred(x2+y2);
443  is_irred(x2+y3+1);
444}
445///////////////////////////////////////////////////////////////////////////////
446
447static proc polytest(poly f)
448"USAGE : polytest(f); f poly in x and y
449RETURN: a monomial of f with |coefficient| > 16001
450          or exponent divisible by 32003, if there is one
451        0 else (in this case computing a squarefree divisor
452                in characteristic 32003 could make sense)
453NOTE:   this procedure is only useful in characteristic zero, because otherwise
454        there is no appropriate ordering of the leading coefficients
455"
456{
457 poly verbrecher=0;
458 intvec leitexp;
459 for (; (f<>0) and (verbrecher==0); f=f-lead(f)) {
460  if ((leadcoef(f)<-16001) or (leadcoef(f)>16001)) {verbrecher=lead(f);}
461  leitexp=leadexp(f);
462  if (( ((leitexp[1] % 32003) == 0)   and (leitexp[1]<>0))
463     or ( ((leitexp[2] % 32003) == 0) and (leitexp[2]<>0)) )
464       {verbrecher=lead(f);}
465 }
466 return(verbrecher);
467}
468
469//////////////////////////////////////////////////////////////////////////////
470
471
472proc develop
473"USAGE:   develop(f [,n]); f poly, n int
474ASSUME:  f is a bivariate polynomial (in the first 2 ring variables) and
475         irreducible as power series (for reducible f use @code{hnexpansion}).
476RETURN:  list @code{L} with:
477@texinfo
478@table @asis
479@item @code{L[1]}; matrix:
480         Each row contains the coefficients of the corresponding line of the
481         Hamburger-Noether expansion (HNE). The end of the line is marked in
482         the matrix by the first ring variable (usually x).
483@item @code{L[2]}; intvec:
484         indicating the length of lines of the HNE
485@item @code{L[3]}; int:
486         0  if the 1st ring variable was transversal (with respect to f), @*
487         1  if the variables were changed at the beginning of the
488            computation, @*
489        -1  if an error has occurred.
490@item @code{L[4]}; poly:
491         the transformed polynomial of f to make it possible to extend the
492         Hamburger-Noether development a posteriori without having to do
493         all the previous calculation once again (0 if not needed)
494@item @code{L[5]}; int:
495         1  if the curve has exactly one branch (i.e., is irreducible), @*
496         0  else (i.e., the curve has more than one HNE, or f is not valid).
497@end table
498@end texinfo
499DISPLAY: The (non zero) elements of the HNE (if not called by another proc).
500NOTE:    The optional parameter @code{n} affects only the computation of
501         the LAST line of the HNE. If it is given, the HN-matrix @code{L[1]}
502         will have at least @code{n} columns. @*
503         Otherwise, the number of columns will be chosen minimal such that the
504         matrix contains all necessary information (i.e., all lines of the HNE
505         but the last (which is in general infinite) have place). @*
506         If @code{n} is negative, the algorithm is stopped as soon as the
507         computed information is sufficient for @code{invariants(L)}, but the
508         HN-matrix @code{L[1]} may still contain undetermined elements, which
509         are marked with the 2nd variable (of the basering). @*
510         For time critical computations it is recommended to use
511         @code{ring ...,(x,y),ls} as basering - it increases the algorithm's
512         speed. @*
513         If @code{printlevel>=0} comments are displayed (default is
514         @code{printlevel=0}).
515SEE ALSO: hnexpansion, extdevelop, displayHNE
516EXAMPLES: example develop;         shows an example
517          example parametrize;     shows an example for using the 2nd parameter
518"
519{
520 //--------- Abfangen unzulaessiger Ringe: 1) nur eine Unbestimmte ------------
521 poly f=#[1];
522 if (size(#) > 1) {int maxspalte=#[2];}
523 else             {int maxspalte= 1 ; }
524 if (nvars(basering) < 2) {
525   " Sorry. I need two variables in the ring.";
526   return(list(matrix(maxideal(1)[1]),intvec(0),-1,poly(0),0));}
527 if (nvars(basering) > 2) {
528   dbprint(printlevel-voice+2,
529   " Warning! You have defined too many variables!
530 All variables except the first two will be ignored!"
531           );
532 }
533
534 string namex=varstr(1); string namey=varstr(2);
535 list return_error=matrix(maxideal(1)[2]),intvec(0),int(-1),poly(0),int(0);
536
537 //------------- 2) mehrere Unbestimmte, weitere unzulaessige Ringe -----------
538 // Wir koennen einheitlichen Rueckgabewert benutzen, aus dem ersichtlich ist,
539 // dass ein Fehler aufgetreten ist: return_error.
540 //----------------------------------------------------------------------------
541
542 if (charstr(basering)=="real") {
543  " The algorithm doesn't work with 'real' as coefficient field.";
544                     // denn : map from characteristic -1 to -1 not implemented
545  return(return_error);
546 }
547 if ((char(basering)!=0) and (charstr(basering)!=string(char(basering)))) {
548 //-- teste, ob char = (p^k,a) (-> a primitiv; erlaubt) oder (p,a[,b,...]) ----
549    string tststr=charstr(basering);
550    tststr=tststr[1..find(tststr,",")-1];           //-> "p^k" bzw. "p"
551    int primit=(tststr==string(char(basering)));
552    if (primit!=0) {
553      " Such extensions of Z/p are not implemented.";
554      " Please try (p^k,a) as ground field or use `hnexpansion'.";
555      return(return_error);
556    }
557 }
558 //---- Ende der unzulaessigen Ringe; Ringwechsel in einen guenstigen Ring: ---
559
560 int ringwechsel=(varstr(basering)!="x,y") or (ordstr(basering)!="ls(2),C");
561
562 def altring = basering;
563 if (ringwechsel) {
564   string mipl=string(minpoly);
565   execute("ring guenstig = ("+charstr(altring)+"),(x,y),ls;");
566   if ((char(basering)==0) && (mipl!="0")) {
567     execute("minpoly="+mipl+";");
568   }}
569 else { def guenstig=basering; }
570 export guenstig;
571
572 //-------------------------- Initialisierungen -------------------------------
573 map m=altring,x,y;
574 if (ringwechsel) { poly f=m(f); }
575 if (defined(HNDebugOn))
576 {"received polynomial: ",f,", where x =",namex,", y =",namey;}
577 kill m;
578 int M,N,Q,R,l,e,hilf,eps,getauscht,Abbruch,zeile,exponent,Ausgabe;
579
580 // Werte von Ausgabe: 0 : normale HNE-Matrix,
581 // 1 : Fehler aufgetreten - Matrix (namey) zurueck
582 // 2 : Die HNE ist eine Nullzeile - Matrix (0) zurueck
583 // int maxspalte=1; geaendert: wird jetzt am Anfang gesetzt
584
585 int minimalHNE=0;          // Flag fuer minimale HNE-Berechnung
586 int einzweig=1;            // Flag fuer Irreduzibilit"at
587 intvec hqs;                // erhaelt die Werte von h(zeile)=Q;
588
589 if (maxspalte<0) {
590   minimalHNE=1;
591   maxspalte=1;
592 }
593
594 number c,delt;
595 int p = char(basering);
596 string ringchar=charstr(basering);
597 map xytausch = basering,y,x;
598 if ((p!=0) and (ringchar != string(p))) {
599                            // coefficient field is extension of Z/pZ
600   execute("int n_elements="+
601           ringchar[1,size(ringchar)-size(parstr(basering))-1]+";");
602                            // number of elements of actual ring
603   number generat=par(1);   // generator of the coefficient field of the ring
604 }
605
606
607 //========= Abfangen von unzulaessigen oder trivialen Eingaben ===============
608 //------------ Nullpolynom oder Einheit im Potenzreihenring: -----------------
609 if (f == 0) {
610   dbprint(printlevel+1,"The given polynomial is the zero-polynomial !");
611   Abbruch=1; Ausgabe=1;
612 }
613 else {
614   intvec nm = getnm(f);
615   N = nm[1]; M = nm[2]; // Berechne Schnittpunkte Newtonpolygon mit Achsen
616   if (N == 0) {
617     dbprint(printlevel+1,"The given polynomial is a unit as power series !");
618     Abbruch=1; Ausgabe=1;
619   }
620   else {
621    if (N == -1) {
622      if ((voice==2) && (printlevel > -1)) { "The HNE is x = 0"; }
623      Abbruch=1; Ausgabe=2; getauscht=1;
624      if (M <> 1) { einzweig=0; }
625    }
626    else {
627     if (M == -1) {
628       if ((voice==2) && (printlevel > -1)) { "The HNE is y = 0"; }
629       Abbruch=1; Ausgabe=2;
630       if (N <> 1) { einzweig=0; }
631   }}}
632 }
633 //--------------------- Test auf Quadratfreiheit -----------------------------
634 if (Abbruch==0) {
635
636 //-------- Fall basering==0,... : Wechsel in Ring mit char >0 ----------------
637 // weil squarefree eine Standardbasis berechnen muss (verwendet Syzygien)
638 // -- wenn f in diesem Ring quadratfrei ist, dann erst recht im Ring guenstig
639 //----------------------------------------------------------------------------
640
641  if ((p==0) and (size(charstr(basering))==1)) {
642   int testerg=(polytest(f)==0);
643   ring zweitring = 32003,(x,y),dp;
644   map polyhinueber=guenstig,x,y;     // fetch geht nicht
645   poly f=polyhinueber(f);
646   poly test_sqr=squarefree(f);
647   if (test_sqr != f) {
648    if (printlevel>0) {
649      "Most probably the given polynomial is not squarefree. But the test was";
650      "made in characteristic 32003 and not 0 to improve speed. You can";
651      "(r) redo the test in char 0 (but this may take some time)";
652      "(c) continue the development, if you're sure that the polynomial",
653      "IS squarefree";
654      if (testerg==1) {
655        "(s) continue the development with a squarefree factor (*)";}
656      "(q) or just quit the algorithm (default action)";
657      "";"Please enter the letter of your choice:";
658      string str=read("")[1];
659    }
660    else { string str="r"; }      // printlevel <= 0: non-interactive behaviour
661    setring guenstig;
662    map polyhinueber=zweitring,x,y;
663    if (str=="r") {
664      poly test_sqr=squarefree(f);
665      if (test_sqr != f) {
666       if (printlevel>0) { "The given polynomial is in fact not squarefree."; }
667       else              { "The given polynomial is not squarefree!"; }
668       "I'll continue with the radical.";
669       if (printlevel>0) { pause("Hit RETURN to continue:"); }
670       f=test_sqr;
671      }
672      else {
673       dbprint(printlevel,
674        "everything is ok -- the polynomial is squarefree in char(k)=0");
675      }
676    }
677    else {
678      if ((str=="s") and (testerg==1)) {
679       "(*) attention: it could be that the factor is only one in char 32003!";
680        f=polyhinueber(test_sqr);
681      }
682      else {
683        if (str<>"c") {
684          setring altring;kill guenstig;kill zweitring;
685          return(return_error);}
686        else { "if the algorithm doesn't terminate, you were wrong...";}
687    }}
688    kill zweitring;
689    nm = getnm(f);             // N,M haben sich evtl. veraendert
690    N = nm[1]; M = nm[2];      // Berechne Schnittpunkte Newtonpoly mit Achsen
691    if (defined(HNDebugOn)) {"I continue with the polynomial",f; }
692   }
693   else {
694     setring guenstig;
695     kill zweitring;
696   }
697  }
698 // ------------------- Fall Charakteristik > 0 -------------------------------
699  else {
700   poly test_sqr=squarefree(f);
701   if (test_sqr == 1) {
702    "The given polynomial is of the form g^"+string(p)+", therefore",
703    "reducible.";"Please try again.";
704    setring altring;
705    kill guenstig;
706    return(return_error);}
707   if (test_sqr != f) {
708    "The given polynomial is not squarefree. I'll continue with the radical.";
709    if (p != 0)
710     {"But if the polynomial contains a factor of the form g^"+string(p)+",";
711      "this factor will be lost.";}
712    if (printlevel>0) { pause("Hit RETURN to continue:"); }
713    f=test_sqr;
714    nm = getnm(f);              // N,M haben sich veraendert
715    N = nm[1]; M = nm[2];       // Berechne Schnittpunkte Newtonpoly mit Achsen
716    if (defined(HNDebugOn)) {"I continue with the polynomial",f; }
717   }
718
719  }                             // endelse(p==0)
720
721  if (N==0) {
722    " Sorry. The remaining polynomial is a unit in the power series ring...";
723    setring altring;kill guenstig;return(return_error);
724  }
725 //---------------------- gewaehrleiste, dass x transvers ist -----------------
726  if (M < N)
727  { f = xytausch(f);            // Variablentausch : x jetzt transvers
728    getauscht = 1;              // den Tausch merken
729    M = M+N; N = M-N; M = M-N;  // M, N auch vertauschen
730  }
731  if (defined(HNDebugOn)) {
732   if (getauscht) {"x<->y were exchanged; poly is now ",f;}
733   else           {"x , y were not exchanged";}
734   "M resp. N are now",M,N;
735  }
736 }                              // end(if Abbruch==0)
737
738 ideal a(0);
739 while (Abbruch==0) {
740
741 //================= Beginn der Schleife (eigentliche Entwicklung) ============
742
743 //------------------- ist das Newtonpolygon eine gerade Linie? ---------------
744  if (testreducible(f,N,M)) {
745    dbprint(printlevel+1," The given polynomial is not irreducible");
746    kill guenstig;
747    setring altring;
748    return(return_error);       // Abbruch der Prozedur!
749  }
750  R = M%N;
751  Q = M / N;
752
753 //-------------------- Fall Rest der Division R = 0 : ------------------------
754  if (R == 0) {
755    c = koeff(f,0,N);
756    if (c == 0) {"Something has gone wrong! I didn't get N correctly!"; exit;}
757    e = gcd(M,N);
758 //----------------- Test, ob leitf = c*(y^N - delta*x^(m/e))^e ist -----------
759    if (p==0) {
760      delt = koeff(f,M/ e,N - N/ e) / (-1*e*c);
761      if (defined(HNDebugOn)) {"quasihomogeneous leading form:",
762         leit(f,N,M)," = ",c,"* (y -",delt,"* x^"+string(M/ e)+")^",e," ?";}
763      if (leit(f,N,M) != c*(y^(N/ e) - delt*x^(M/ e))^e) {
764        dbprint(printlevel+1," The given polynomial is reducible !");
765        Abbruch=1; Ausgabe=1; }
766    }
767    else {                     // p!=0
768      if (e%p != 0) {
769        delt = koeff(f,M/ e,N - N/ e) / (-1*e*c);
770        if (defined(HNDebugOn)) {"quasihomogeneous leading form:",
771           leit(f,N,M)," = ",c,"* (y -",delt,"* x^"+string(M/ e)+")^",e," ?";}
772        if (leit(f,N,M) != c*(y^(N/ e) - delt*x^(M/ e))^e) {
773           dbprint(printlevel+1," The given polynomial is reducible !");
774           Abbruch=1; Ausgabe=1; }
775      }
776
777      else {                   // e%p == 0
778        eps = e;
779        for (l = 0; eps%p == 0; l=l+1) { eps=eps/ p;}
780        if (defined(HNDebugOn)) {e," -> ",eps,"*",p,"^",l;}
781        delt = koeff(f,(M/ e)*p^l,(N/ e)*p^l*(eps-1)) / (-1*eps*c);
782
783        if ((ringchar != string(p)) and (delt != 0)) {
784 //- coeff. field is not Z/pZ => we`ve to correct delta by taking (p^l)th root-
785          if (delt == generat) {exponent=1;}
786          else {
787           if (delt == 1) {exponent=0;}
788           else {
789            exponent=pardeg(delt);
790
791 //-- an dieser Stelle kann ein Fehler auftreten, wenn wir eine transzendente -
792 //-- Erweiterung von Z/pZ haben: dann ist das hinzuadjungierte Element kein  -
793 //-- Erzeuger der mult. Gruppe, d.h. in Z/pZ (a) gibt es i.allg. keinen      -
794 //-- Exponenten mit z.B. a2+a = a^exp                                        -
795 //----------------------------------------------------------------------------
796          }}
797          delt = generat^(extgcd(n_elements-1,p^l)[3]*exponent);
798        }
799
800        if (defined(HNDebugOn)) {"quasihomogeneous leading form:",
801          leit(f,N,M)," = ",c,"* (y^"+string(N/ e),"-",delt,"* x^"
802          +string(M/ e)+")^",e,"  ?";}
803        if (leit(f,N,M) != c*(y^(N/ e) - delt*x^(M/ e))^e) {
804          dbprint(printlevel+1," The given polynomial is reducible !");
805          Abbruch=1; Ausgabe=1; }
806      }
807    }
808    if (Abbruch == 0) {
809      f = T1_Transform(f,delt,M/ e);
810      dbprint(printlevel-voice+2,"a("+string(zeile)+","+string(Q)+") = "
811              +string(delt));
812      a(zeile)[Q]=delt;
813      if (defined(HNDebugOn)) {"transformed polynomial: ",f;}}
814
815      nm=getnm(f); N=nm[1]; M=nm[2];        // Neuberechnung des Newtonpolygons
816  }
817 //--------------------------- Fall R > 0 : -----------------------------------
818  else {
819    dbprint(printlevel-voice+2, "h("+string(zeile)+ ") ="+string(Q));
820    hqs[zeile+1]=Q;                  // denn zeile beginnt mit dem Wert 0
821    a(zeile)[Q+1]=x;                 // Markierung des Zeilenendes der HNE
822    maxspalte=maxspalte*((Q+1) < maxspalte) + (Q+1)*((Q+1) >= maxspalte);
823                                     // Anpassung der Sp.zahl der HNE-Matrix
824    f = T_Transform(f,Q,N);
825    if (defined(HNDebugOn)) {"transformed polynomial: ",f;}
826    zeile=zeile+1;
827 //------------ Bereitstellung von Speicherplatz fuer eine neue Zeile: --------
828    ideal a(zeile);
829    M=N;N=R;
830  }
831
832 //--------------- schneidet das Newtonpolygon beide Achsen? ------------------
833  if (M==-1) {
834     dbprint(printlevel-voice+2,"The HNE is finite!");
835     a(zeile)[Q+1]=x;   // Markiere das Ende der Zeile
836     hqs[zeile+1]=Q;
837     maxspalte=maxspalte*((Q+1) < maxspalte) + (Q+1)*((Q+1) >= maxspalte);
838     if (N <> 1) { einzweig=0; }
839     f=0;               // transformiertes Polynom wird nicht mehr gebraucht
840     Abbruch=1;
841  }
842  else {if (M<N) {"Something has gone wrong: M<N";}}
843  if(defined(HNDebugOn)) {"new M,N:",M,N;}
844
845 //----------------- Abbruchbedingungen fuer die Schleife: --------------------
846  if ((N==1) and (Abbruch!=1) and ((M > maxspalte) or (minimalHNE==1))
847      and (size(a(zeile))>0))
848 //----------------------------------------------------------------------------
849 // Abbruch, wenn die Matrix so voll ist, dass eine neue Spalte angefangen
850 // werden muesste und die letzte Zeile nicht nur Nullen enthaelt
851 // oder wenn die Matrix nicht voll gemacht werden soll (minimale Information)
852 //----------------------------------------------------------------------------
853   { Abbruch=1; hqs[zeile+1]=-1;
854     if (maxspalte < ncols(a(zeile))) { maxspalte=ncols(a(zeile));}
855     if ((minimalHNE==1) and (M <= maxspalte)) {
856 // teile param mit, dass Eintraege der letzten Zeile nur teilw. richtig sind:-
857       hqs[zeile+1]=-M;
858 //------------- markiere den Rest der Zeile als unbekannt: -------------------
859       for (R=M; R <= maxspalte; R++) { a(zeile)[R]=y;}
860     }                  // R wird nicht mehr gebraucht
861   }
862 //========================= Ende der Schleife ================================
863
864 }
865 setring altring;
866 if (Ausgabe == 0) {
867 //-------------------- Ergebnis in den alten Ring transferieren: -------------
868   map zurueck=guenstig,maxideal(1)[1],maxideal(1)[2];
869   matrix amat[zeile+1][maxspalte];
870   ideal uebergabe;
871   for (e=0; e<=zeile; e=e+1) {
872     uebergabe=zurueck(a(e));
873     if (ncols(uebergabe) > 1) {
874      amat[e+1,1..ncols(uebergabe)]=uebergabe;}
875     else {amat[e+1,1]=uebergabe[1];}
876   }
877   if (ringwechsel) {
878     if (nvars(altring)==2) { f=fetch(guenstig,f); }
879     else                   { f=zurueck(f); }
880   }
881 }
882
883 kill guenstig;
884 if ((einzweig==0) && (voice==2) && (printlevel > -1)) {
885    "// Note: The curve is reducible, but we were able to compute a HNE.";
886    "// This means the result is only one of several existing HNE's.";
887 }
888 if (Ausgabe == 0) { return(list(amat,hqs,getauscht,f,einzweig));}
889 if (Ausgabe == 1) { return(return_error);}             // error has occurred
890 if (Ausgabe == 2) { return(list(matrix(ideal(0,x)),intvec(1),getauscht,
891                                 poly(0),einzweig));}   // HNE is x=0 or y=0
892}
893example
894{ "EXAMPLE:"; echo = 2;
895  ring exring = 7,(x,y),ds;
896  list Hne=develop(4x98+2x49y7+x11y14+2y14);
897  print(Hne[1]);
898  // therefore the HNE is:
899  // z(-1)= 3*z(0)^7 + z(0)^7*z(1),
900  // z(0) = z(1)*z(2),       (there is 1 zero in the 2nd row before x)
901  // z(1) = z(2)^3*z(3),     (there are 3 zeroes in the 3rd row)
902  // z(2) = z(3)*z(4),
903  // z(3) = -z(4)^2 + 0*z(4)^3 +...+ 0*z(4)^8 + ?*z(4)^9 + ...
904  // (the missing x in the last line indicates that it is not complete.)
905  Hne[2];
906  param(Hne);
907  // parametrization:   x(t)= -t^14+O(t^21),  y(t)= -3t^98+O(t^105)
908  // (the term -t^109 in y may have a wrong coefficient)
909  displayHNE(Hne);
910}
911
912///////////////////////////////////////////////////////////////////////////////
913//               procedures to extract information out of HNE                //
914///////////////////////////////////////////////////////////////////////////////
915
916proc param (list L, list #)
917"USAGE:  param(L [,s]); L list, s any type (optional)
918ASSUME:  L is the output of @code{develop(f)}, or of
919        @code{extdevelop(develop(f),n)}, or (one entry in) the list of HN
920        data created by @code{hnexpansion(f[,\"ess\"])}.
921RETURN: If L are the HN data of an irreducible plane curve singularity f: a
922        parametrization for f in the following format: @*
923        - if only the list L is given, the result is an ideal of two
924        polynomials p[1],p[2]: if the HNE was finite then f(p[1],p[2])=0};
925        if not, the true parametrization will be given by two power series,
926        and p[1],p[2] are truncations of these series.@*
927        - if the optional parameter s is given, the result is a list l:
928        l[1]=param(L) (ideal) and l[2]=intvec with two entries indicating
929        the highest degree up to which the coefficients of the monomials in
930        l[1] are exact (entry -1 means that the corresponding parametrization
931        is exact).
932        If L collects the HN data of a reducible plane curve singularity f,
933        the return value is a list of parametrizations in the respective
934        format.
935NOTE:   If the basering has only 2 variables, the first variable is chosen
936        as indefinite. Otherwise, the 3rd variable is chosen.
937SEE ALSO: develop, extdevelop
938KEYWORDS: parametrization
939EXAMPLE: example param;     shows an example
940         example develop;   shows another example
941"
942{
943 //-------------------------- Initialisierungen -------------------------------
944 int return_list;
945 if (size(#)>0) { return_list=1; }
946
947 if (typeof(L[1])=="list") { // output of hnexpansion (> 1 branch)
948   list Ergebnis;
949   for (int i=1; i<=size(L); i++) {
950     dbprint(printlevel-voice+4,"// Parametrization of branch number "
951       +string(i)+" computed.");
952     printlevel=printlevel+1;
953     if (return_list==1) { Ergebnis[i]=param(L[i],1); }
954     else                { Ergebnis[i]=param(L[i]); }
955     printlevel=printlevel-1;
956   }
957   return(Ergebnis);
958 }
959 else {
960   matrix m=L[1];
961   intvec v=L[2];
962   int switch=L[3];
963 }
964 if (switch==-1) {
965   "An error has occurred in develop, so there is no HNE.";
966   return(ideal(0,0));
967 }
968 int fehler,fehlervor,untergrad,untervor,beginn,i,zeile,hilf;
969
970 if (nvars(basering) > 2) { poly z(size(v)+1)=var(3); }
971 else                     { poly z(size(v)+1)=var(1); }
972 poly z(size(v));
973 zeile=size(v);
974 //------------- Parametrisierung der untersten Zeile der HNE -----------------
975 if (v[zeile] > 0) {
976   fehler=0;           // die Parametrisierung wird exakt werden
977   for (i=1; i<=v[zeile]; i++) {
978     z(zeile)=z(zeile)+m[zeile,i]*z(zeile+1)^i;
979   }
980 }
981 else {
982   untervor=1;         // = Untergrad der vorhergehenden Zeile
983   if (v[zeile]==-1) {
984     fehler=ncols(m)+1;
985     for (i=1; i<=ncols(m); i++) {
986       z(zeile)=z(zeile)+m[zeile,i]*z(zeile+1)^i;
987       if ((untergrad==0) and (m[zeile,i]!=0)) {untergrad=i;}
988                       // = Untergrad der aktuellen Zeile
989     }
990   }
991   else {
992     fehler= -v[zeile];
993     for (i=1; i<-v[zeile]; i++) {
994       z(zeile)=z(zeile)+m[zeile,i]*z(zeile+1)^i;
995       if ((untergrad==0) and (m[zeile,i]!=0)) {untergrad=i;}
996     }
997   }
998 }
999 //------------- Parametrisierung der restlichen Zeilen der HNE ---------------
1000 for (zeile=size(v)-1; zeile>0; zeile--) {
1001   poly z(zeile);
1002   beginn=0;             // Beginn der aktuellen Zeile
1003   for (i=1; i<=v[zeile]; i++) {
1004     z(zeile)=z(zeile)+m[zeile,i]*z(zeile+1)^i;
1005     if ((beginn==0) and (m[zeile,i]!=0)) { beginn=i;}
1006   }
1007   z(zeile)=z(zeile) + z(zeile+1)^v[zeile] * z(zeile+2);
1008   if (beginn==0) {
1009     if (fehler>0) {     // damit fehler=0 bleibt bei exakter Param.
1010     fehlervor=fehler;   // Fehler der letzten Zeile
1011     fehler=fehler+untergrad*(v[zeile]-1)+untervor;   // Fehler dieser Zeile
1012     hilf=untergrad;
1013     untergrad=untergrad*v[zeile]+untervor;
1014     untervor=hilf;}     // untervor = altes untergrad
1015   }
1016   else {
1017     fehlervor=fehler;
1018     fehler=fehler+untergrad*(beginn-1);
1019     untervor=untergrad;
1020     untergrad=untergrad*beginn;
1021   }
1022 }
1023 //--------------------- Ausgabe der Fehlerabschaetzung -----------------------
1024 if (switch==0) {
1025   if (fehler>0) {
1026     if (fehlervor>0) {
1027       dbprint(printlevel-voice+4,""+
1028         "// ** Warning: result is exact up to order "+string(fehlervor-1)+
1029         " in "+ string(var(1))+" and "+string(fehler-1)+" in " +
1030         string(var(2))+" !");
1031     }
1032     else {
1033       dbprint(printlevel-voice+4,""+
1034         "// ** Warning: result is exact up to order "+ string(fehler-1)+
1035         " in "+string(var(2))+" !");
1036     }
1037   }
1038   if (return_list==0) { return(ideal(z(2),z(1))); }
1039   else   { return(list(ideal(z(2),z(1)),intvec(fehlervor-1,fehler-1))); }
1040 }
1041 else {
1042   if (fehler>0) {
1043     if (fehlervor>0) {
1044       dbprint(printlevel-voice+4,""+
1045         "// ** Warning: result is exact up to order "+string(fehler-1)+
1046         " in "+ string(var(1))+" and "+string(fehlervor-1)+" in " +
1047         string(var(2))+" !");
1048     }
1049     else {
1050       dbprint(printlevel-voice+4,""+
1051        "// ** Warning: result is exact up to order "+ string(fehler-1)+
1052         " in "+string(var(1))+" !");
1053     }
1054   }
1055   if (return_list==0) { return(ideal(z(1),z(2))); }
1056   else   { return(list(ideal(z(1),z(2)),intvec(fehler-1,fehlervor-1))); }
1057 }
1058}
1059example
1060{ "EXAMPLE:"; echo = 2;
1061 ring exring=0,(x,y,t),ds;
1062 poly f=x3+2xy2+y2;
1063 list Hne=develop(f);
1064 list hne_extended=extdevelop(Hne,10);
1065            //   compare the HNE matrices ...
1066 print(Hne[1]);
1067 print(hne_extended[1]);
1068            // ... and the resulting parametrizations:
1069 param(Hne);
1070 param(hne_extended);
1071 param(hne_extended,0);
1072
1073 // An example with more than one branch:
1074 list L=hnexpansion(f*(x2+y4));
1075 def HNring = L[1]; setring HNring;
1076 param(hne);
1077}
1078
1079///////////////////////////////////////////////////////////////////////////////
1080
1081proc invariants
1082"USAGE:   invariants(INPUT); INPUT list or poly
1083ASSUME:  @code{INPUT} is the output of @code{develop(f)}, or of
1084         @code{extdevelop(develop(f),n)}, or one entry of the list of HN data
1085         computed by @code{hnexpansion(f[,\"ess\"])}.
1086RETURN:  list @code{INV} of the following format:
1087@format
1088    INV[1]:  intvec    (characteristic exponents)
1089    INV[2]:  intvec    (generators of the semigroup)
1090    INV[3]:  intvec    (Puiseux pairs, 1st components)
1091    INV[4]:  intvec    (Puiseux pairs, 2nd components)
1092    INV[5]:  int       (degree of the conductor)
1093    INV[6]:  intvec    (sequence of multiplicities)
1094@end format
1095         If @code{INPUT} contains no valid HN expansion, the empty list is
1096         returned.
1097ASSUME:  @code{INPUT} is a bivariate polynomial f, or the output of
1098         @code{hnexpansion(f)}, or the list of HN data computed by
1099         @code{hnexpansion(f [,\"ess\"])}.
1100RETURN:  list @code{INV}, such that @code{INV[i]} coincides with the output of
1101         @code{invariants(develop(f[i]))}, where f[i] is the i-th branch of
1102         f, and the last entry of @code{INV} contains further invariants of
1103         f in the format:
1104@format
1105    INV[last][1] : intmat    (contact matrix of the branches)
1106    INV[last][2] : intmat    (intersection multiplicities of the branches)
1107    INV[last][3] : int       (delta invariant of f)
1108@end format
1109NOTE:    In case the Hamburger-Noether expansion of the curve f is needed
1110         for other purposes as well it is better to calculate this first
1111         with the aid of @code{hnexpansion} and use it as input instead of
1112         the polynomial itself.
1113SEE ALSO: hnexpansion, develop, displayInvariants, multsequence, intersection
1114KEYWORDS: characteristic exponents; semigroup of values; Puiseux pairs;
1115          conductor, degree; multiplicities, sequence of
1116EXAMPLE:  example invariants; shows an example
1117"
1118{
1119 //---- INPUT = poly, or HNEring, or hne of reducible curve  -----------------
1120 if (typeof(#[1])!="matrix") {
1121   if (typeof(#[1])=="poly") {
1122      list L=hnexpansion(#[1]);
1123      if (typeof(L[1])=="ring") {
1124        def altring = basering;
1125        def HNring = L[1]; setring HNring;
1126        list Ergebnis = invariants(hne);
1127        setring altring;
1128        kill HNring;
1129        return(Ergebnis);
1130      }
1131      else {
1132        return(invariants(L));
1133      }
1134   }
1135   if (typeof(#[1])=="ring") {
1136     def altring = basering;
1137     def HNring = #[1]; setring HNring;
1138     list Ergebnis = invariants(hne);
1139     setring altring;
1140     kill HNring;
1141     return(Ergebnis);
1142   }
1143   if (typeof(#[1])=="list") {
1144     list hne=#;
1145     list Ergebnis;
1146     for (int lauf=1;lauf<=size(hne);lauf++) {
1147       Ergebnis[lauf]=invariants(hne[lauf]);
1148     }
1149     // Calculate the intersection matrix and the intersection multiplicities.
1150     intmat contact[size(hne)][size(hne)];
1151     intmat intersectionmatrix[size(hne)][size(hne)];
1152     int Lauf;
1153     for (lauf=1;lauf<=size(hne);lauf++) {
1154       for (Lauf=lauf+1;Lauf<=size(hne);Lauf++) {
1155         contact[lauf,Lauf]=separateHNE(hne[lauf],hne[Lauf]);
1156         contact[Lauf,lauf]=contact[lauf,Lauf];
1157         intersectionmatrix[lauf,Lauf]=intersection(hne[lauf],hne[Lauf]);
1158         intersectionmatrix[Lauf,lauf]=intersectionmatrix[lauf,Lauf];
1159       }
1160     }
1161     // Calculate the delta invariant.
1162     int inters;
1163     int del=Ergebnis[size(hne)][5]/2;
1164     for(lauf=1;lauf<=size(hne)-1;lauf++) {
1165       del=del+Ergebnis[lauf][5]/2;
1166       for(Lauf=lauf+1;Lauf<=size(hne);Lauf++) {
1167         inters=inters+intersectionmatrix[lauf,Lauf];
1168       }
1169     }
1170     del=del+inters;
1171     list LAST=contact,intersectionmatrix,del;
1172     Ergebnis[size(hne)+1]=LAST;
1173     return(Ergebnis);
1174   }
1175 }
1176 //-------------------------- Initialisierungen -------------------------------
1177 matrix m=#[1];
1178 intvec v=#[2];
1179 int switch=#[3];
1180 list ergebnis;
1181 if (switch==-1) {
1182   "An error has occurred in develop, so there is no HNE.";
1183   return(ergebnis);
1184 }
1185 intvec beta,s,svorl,ordnung,multseq,mpuiseux,npuiseux,halbgr;
1186 int genus,zeile,i,j,k,summe,conductor,ggT;
1187 string Ausgabe;
1188 int nc=ncols(m); int nr=nrows(m);
1189 ordnung[nr]=1;
1190         // alle Indizes muessen (gegenueber [Ca]) um 1 erhoeht werden,
1191         // weil 0..r nicht als Wertebereich erlaubt ist (aber nrows(m)==r+1)
1192
1193 //---------------- Bestimme den Untergrad der einzelnen Zeilen ---------------
1194 for (zeile=nr; zeile>1; zeile--) {
1195   if ((size(ideal(m[zeile,1..nc])) > 1) or (zeile==nr)) { // keine Nullzeile
1196      k=1;
1197      while (m[zeile,k]==0) {k++;}
1198      ordnung[zeile-1]=k*ordnung[zeile]; // vgl. auch Def. von untergrad in
1199      genus++;                           // proc param
1200      svorl[genus]=zeile;} // werden gerade in umgekehrter Reihenfolge abgelegt
1201   else {
1202      ordnung[zeile-1]=v[zeile]*ordnung[zeile]+ordnung[zeile+1];
1203 }}
1204 //----------------- charakteristische Exponenten (beta) ----------------------
1205 s[1]=1;
1206 for (k=1; k <= genus; k++) { s[k+1]=svorl[genus-k+1];} // s[2]==s(1), u.s.w.
1207 beta[1]=ordnung[1]; //charakt. Exponenten: Index wieder verschoben
1208 for (k=1; k <= genus; k++) {
1209   summe=0;
1210   for (i=1; i <= s[k]; i++) {summe=summe+v[i]*ordnung[i];}
1211   beta[k+1]=summe+ordnung[s[k]]+ordnung[s[k]+1]-ordnung[1];
1212 }
1213 //--------------------------- Puiseuxpaare -----------------------------------
1214 int produkt=1;
1215 for (i=1; i<=genus; i++) {
1216   ggT=gcd(beta[1],beta[i+1]*produkt);
1217   mpuiseux[i]=beta[i+1]*produkt / ggT;
1218   npuiseux[i]=beta[1] / ggT;
1219   produkt=produkt*npuiseux[i];
1220 }
1221 //---------------------- Grad des Konduktors ---------------------------------
1222 summe=1-ordnung[1];
1223 if (genus > 0) {
1224   for (i=2; i <= genus+1; i++) {
1225     summe=summe + beta[i] * (ordnung[s[i-1]] - ordnung[s[i]]);
1226   }                              // n.b.: Indizierung wieder um 1 verschoben
1227 }
1228 conductor=summe;
1229 //------------------- Erzeuger der Halbgruppe: -------------------------------
1230 halbgr=puiseux2generators(mpuiseux,npuiseux);
1231
1232 //------------------- Multiplizitaetensequenz: -------------------------------
1233 k=1;
1234 for (i=1; i<size(v); i++) {
1235   for (j=1; j<=v[i]; j++) {
1236     multseq[k]=ordnung[i];
1237     k++;
1238 }}
1239 multseq[k]=1;
1240 //--- fuelle die Multipl.seq. mit den notwendigen Einsen auf -- T.Keilen ----
1241 int tester=k;
1242 while((multseq[tester]==1) and (tester>1))
1243 {
1244   tester=tester-1;
1245 }
1246 if ((multseq[tester]!=1) and (multseq[tester]!=k-tester))
1247 {
1248   for (i=k+1; i<=tester+multseq[tester]; i++)
1249   {
1250     multseq[i]=1;
1251   }
1252 }
1253 //--- Ende T.Keilen --- 06.05.02
1254 //------------------------- Rueckgabe ----------------------------------------
1255 ergebnis=beta,halbgr,mpuiseux,npuiseux,conductor,multseq;
1256 return(ergebnis);
1257}
1258example
1259{ "EXAMPLE:"; echo = 2;
1260 ring exring=0,(x,y),dp;
1261 list Hne=develop(y4+2x3y2+x6+x5y);
1262 list INV=invariants(Hne);
1263 INV[1];                   // the characteristic exponents
1264 INV[2];                   // the generators of the semigroup of values
1265 INV[3],INV[4];            // the Puiseux pairs in packed form
1266 INV[5] / 2;               // the delta-invariant
1267 INV[6];                   // the sequence of multiplicities
1268                           // To display the invariants more 'nicely':
1269 displayInvariants(Hne);
1270 /////////////////////////////
1271 INV=invariants((x2-y3)*(x3-y5));
1272 INV[1][1];                // the characteristic exponents of the first branch
1273 INV[2][6];                // the sequence of multiplicities of the second branch
1274 print(INV[size(INV)][1]);         // the contact matrix of the branches
1275 print(INV[size(INV)][2]);         // the intersection numbers of the branches
1276 INV[size(INV)][3];                // the delta invariant of the curve
1277}
1278
1279///////////////////////////////////////////////////////////////////////////////
1280
1281proc displayInvariants
1282"USAGE:  displayInvariants(INPUT); INPUT list or poly
1283ASSUME:  @code{INPUT} is a bivariate polynomial, or the output of
1284         @code{develop(f)}, resp. of @code{extdevelop(develop(f),n)}, or (one
1285         entry of) the list of HN data computed by
1286         @code{hnexpansion(f[,\"ess\"])}.
1287RETURN:  none
1288DISPLAY: invariants of the corresponding branch, resp. of all branches,
1289         in a better readable form.
1290NOTE:    If the Hamburger-Noether expansion of the curve f is needed
1291         for other purposes as well it is better to calculate this first
1292         with the aid of @code{hnexpansion} and use it as input instead of
1293         the polynomial itself.
1294SEE ALSO: invariants, intersection, develop, hnexpansion
1295EXAMPLE: example displayInvariants;  shows an example
1296"
1297{
1298 // INPUT = poly or ring
1299 if (typeof(#[1])=="poly") {
1300   list L=hnexpansion(#[1]);
1301   if (typeof(L[1])=="ring") {
1302     def HNring = L[1]; setring HNring;
1303     displayInvariants(hne);
1304     return();
1305   }
1306   else {
1307     displayInvariants(L);
1308     return();
1309   }
1310 }
1311 if (typeof(#[1])=="ring")
1312 {
1313   def HNring = #[1]; setring HNring;
1314   displayInvariants(hne);
1315   return();
1316 }
1317 // INPUT = hne of a plane curve
1318 int i,j,k,mul;
1319 string Ausgabe;
1320 list ergebnis;
1321 //-- entferne ueberfluessige Daten zur Erhoehung der Rechengeschwindigkeit: --
1322 #=stripHNE(#);
1323 //-------------------- Ausgabe eines Zweiges ---------------------------------
1324 if (typeof(#[1])=="matrix") {
1325   ergebnis=invariants(#);
1326   if (size(ergebnis)!=0) {
1327    " characteristic exponents  :",ergebnis[1];
1328    " generators of semigroup   :",ergebnis[2];
1329    if (size(ergebnis[1])>1) {
1330     for (i=1; i<=size(ergebnis[3]); i++) {
1331       Ausgabe=Ausgabe+"("+string(ergebnis[3][i])+","
1332       +string(ergebnis[4][i])+")";
1333    }}
1334    " Puiseux pairs             :",Ausgabe;
1335    " degree of the conductor   :",ergebnis[5];
1336    " delta invariant           :",ergebnis[5]/2;
1337    " sequence of multiplicities:",ergebnis[6];
1338 }}
1339 //-------------------- Ausgabe aller Zweige ----------------------------------
1340 else {
1341  ergebnis=invariants(#);
1342  intmat contact=ergebnis[size(#)+1][1];
1343  intmat intersectionmatrix=ergebnis[size(#)+1][2];
1344  for (j=1; j<=size(#); j++) {
1345    " --- invariants of branch number",j,": ---";
1346    " characteristic exponents  :",ergebnis[j][1];
1347    " generators of semigroup   :",ergebnis[j][2];
1348    Ausgabe="";
1349    if (size(ergebnis[j][1])>1) {
1350     for (i=1; i<=size(ergebnis[j][3]); i++) {
1351       Ausgabe=Ausgabe+"("+string(ergebnis[j][3][i])+","
1352       +string(ergebnis[j][4][i])+")";
1353    }}
1354    " Puiseux pairs             :",Ausgabe;
1355    " degree of the conductor   :",ergebnis[j][5];
1356    " delta invariant           :",ergebnis[j][5]/2;
1357    " sequence of multiplicities:",ergebnis[j][6];
1358    "";
1359  }
1360  if (size(#)>1)
1361  {
1362    " -------------- contact numbers : -------------- ";"";
1363    Ausgabe="branch |   ";
1364    for (j=size(#); j>1; j--)
1365    {
1366      if (size(string(j))==1) { Ausgabe=Ausgabe+" "+string(j)+"    "; }
1367      else                    { Ausgabe=Ausgabe+string(j)+"    "; }
1368    }
1369    Ausgabe;
1370    Ausgabe="-------+";
1371    for (j=2; j<size(#); j++) { Ausgabe=Ausgabe+"------"; }
1372    Ausgabe=Ausgabe+"-----";
1373    Ausgabe;
1374  }
1375  for (j=1; j<size(#); j++)
1376  {
1377    if (size(string(j))==1) { Ausgabe="    "+string(j)+"  |"; }
1378    else                    { Ausgabe="   " +string(j)+"  |"; }
1379    for (k=size(#); k>j; k--)
1380    {
1381      mul=contact[j,k];//separateHNE(#[j],#[k]);
1382      for (i=1; i<=5-size(string(mul)); i++) { Ausgabe=Ausgabe+" "; }
1383      Ausgabe=Ausgabe+string(mul);
1384      if (k>j+1) { Ausgabe=Ausgabe+","; }
1385    }
1386    Ausgabe;
1387  }
1388  "";
1389  if (size(#)>1)
1390  {
1391    " -------------- intersection multiplicities : -------------- ";"";
1392    Ausgabe="branch |   ";
1393    for (j=size(#); j>1; j--)
1394    {
1395      if (size(string(j))==1) { Ausgabe=Ausgabe+" "+string(j)+"    "; }
1396      else                    { Ausgabe=Ausgabe+string(j)+"    "; }
1397    }
1398    Ausgabe;
1399    Ausgabe="-------+";
1400    for (j=2; j<size(#); j++) { Ausgabe=Ausgabe+"------"; }
1401    Ausgabe=Ausgabe+"-----";
1402    Ausgabe;
1403  }
1404  for (j=1; j<size(#); j++)
1405  {
1406    if (size(string(j))==1) { Ausgabe="    "+string(j)+"  |"; }
1407    else                    { Ausgabe="   " +string(j)+"  |"; }
1408    for (k=size(#); k>j; k--)
1409    {
1410      mul=intersectionmatrix[j,k];//intersection(#[j],#[k]);
1411      for (i=1; i<=5-size(string(mul)); i++) { Ausgabe=Ausgabe+" "; }
1412      Ausgabe=Ausgabe+string(mul);
1413      if (k>j+1) { Ausgabe=Ausgabe+","; }
1414    }
1415    Ausgabe;
1416  }
1417  "";
1418  " -------------- delta invariant of the curve : ",ergebnis[size(#)+1][3];
1419
1420 }
1421 return();
1422}
1423example
1424{ "EXAMPLE:"; echo = 2;
1425 ring exring=0,(x,y),dp;
1426 list Hne=develop(y4+2x3y2+x6+x5y);
1427 displayInvariants(Hne);
1428}
1429///////////////////////////////////////////////////////////////////////////////
1430
1431proc multiplicities
1432"USAGE:   multiplicities(L); L list
1433ASSUME:  L is the output of @code{develop(f)}, or of
1434         @code{extdevelop(develop(f),n)}, or one entry in the list @code{hne}
1435         in the ring created by @code{hnexpansion(f[,\"ess\"])}.
1436RETURN:  intvec of the different multiplicities that occur when successively
1437         blowing-up the curve singularity corresponding to f.
1438SEE ALSO: multsequence, develop
1439EXAMPLE: example multiplicities;  shows an example
1440"
1441{
1442 matrix m=#[1];
1443 intvec v=#[2];
1444 int switch=#[3];
1445 list ergebnis;
1446 if (switch==-1) {
1447   "An error has occurred in develop, so there is no HNE.";
1448   return(intvec(0));
1449 }
1450 intvec ordnung;
1451 int zeile,k;
1452 int nc=ncols(m); int nr=nrows(m);
1453 ordnung[nr]=1;
1454 //---------------- Bestimme den Untergrad der einzelnen Zeilen ---------------
1455 for (zeile=nr; zeile>1; zeile--) {
1456   if ((size(ideal(m[zeile,1..nc])) > 1) or (zeile==nr)) { // keine Nullzeile
1457      k=1;
1458      while (m[zeile,k]==0) {k++;}
1459      ordnung[zeile-1]=k*ordnung[zeile];
1460   }
1461   else {
1462      ordnung[zeile-1]=v[zeile]*ordnung[zeile]+ordnung[zeile+1];
1463 }}
1464 return(ordnung);
1465}
1466example
1467{ "EXAMPLE:"; echo = 2;
1468  int p=printlevel; printlevel=-1;
1469  ring r=0,(x,y),dp;
1470  multiplicities(develop(x5+y7));
1471  // The first value is the multiplicity of the curve itself, here it's 5
1472  printlevel=p;
1473}
1474///////////////////////////////////////////////////////////////////////////////
1475
1476proc puiseux2generators (intvec m, intvec n)
1477"USAGE:   puiseux2generators(m,n); m,n intvec
1478ASSUME:  m, resp. n, represent the 1st, resp. 2nd, components of Puiseux pairs
1479         (e.g., @code{m=invariants(L)[3]}, @code{n=invariants(L)[4]}).
1480RETURN:  intvec of the generators of the semigroup of values.
1481SEE ALSO: invariants
1482EXAMPLE: example puiseux2generators;  shows an example
1483"
1484{
1485 intvec beta;
1486 int q=1;
1487 //------------ glatte Kurve (eigentl. waeren m,n leer): ----------------------
1488 if (m==0) { return(intvec(1)); }
1489 //------------------- singulaere Kurve: --------------------------------------
1490 for (int i=1; i<=size(n); i++) { q=q*n[i]; }
1491 beta[1]=q; // == q_0
1492 m=1,m; n=1,n; // m[1] ist damit m_0 usw., genau wie beta[1]==beta_0
1493 for (i=2; i<=size(n); i++) {
1494  beta[i]=m[i]*q/n[i] - m[i-1]*q + n[i-1]*beta[i-1];
1495  q=q/n[i]; // == q_i
1496 }
1497 return(beta);
1498}
1499example
1500{ "EXAMPLE:"; echo = 2;
1501  // take (3,2),(7,2),(15,2),(31,2),(63,2),(127,2) as Puiseux pairs:
1502  puiseux2generators(intvec(3,7,15,31,63,127),intvec(2,2,2,2,2,2));
1503}
1504///////////////////////////////////////////////////////////////////////////////
1505
1506proc intersection (list hn1, list hn2)
1507"USAGE:   intersection(hne1,hne2); hne1, hne2 lists
1508ASSUME: @code{hne1, hne2} represent an HN expansion of an irreducible plane
1509        curve singularity (that is, are the output of @code{develop(f)}, or of
1510        @code{extdevelop(develop(f),n)}, or one entry of the list of HN data
1511        computed by @code{hnexpansion(f[,\"ess\"])}).
1512RETURN:  int, the intersection multiplicity of the irreducible plane curve
1513         singularities corresponding to @code{hne1} and @code{hne2}.
1514SEE ALSO: hnexpansion, displayInvariants
1515KEYWORDS: intersection multiplicity
1516EXAMPLE: example intersection;  shows an example
1517"
1518{
1519 //------------------ `intersect' ist schon reserviert ... --------------------
1520 int i,j,s,sum,schnitt,unterschied;
1521 matrix a1=hn1[1];
1522 matrix a2=hn2[1];
1523 intvec h1=hn1[2];
1524 intvec h2=hn2[2];
1525 intvec n1=multiplicities(hn1);
1526 intvec n2=multiplicities(hn2);
1527 if (hn1[3]!=hn2[3]) {
1528 //-- die jeweils erste Zeile von hn1,hn2 gehoert zu verschiedenen Parametern -
1529 //---------------- d.h. beide Kurven schneiden sich transversal --------------
1530   schnitt=n1[1]*n2[1];        // = mult(hn1)*mult(hn2)
1531 }
1532 else {
1533 //--------- die jeweils erste Zeile gehoert zum gleichen Parameter -----------
1534   unterschied=0;
1535   for (s=1; (h1[s]==h2[s]) && (s<size(h1)) && (s<size(h2))
1536              && (unterschied==0); s++) {
1537     for (i=1; (a1[s,i]==a2[s,i]) && (i<=h1[s]); i++) {;}
1538     if (i<=h1[s]) {
1539       unterschied=1;
1540       s--;                    // um s++ am Schleifenende wieder auszugleichen
1541     }
1542   }
1543   if (unterschied==0) {
1544     if ((s<size(h1)) && (s<size(h2))) {
1545       for (i=1; (a1[s,i]==a2[s,i]) && (i<=h1[s]) && (i<=h2[s]); i++) {;}
1546     }
1547     else {
1548 //-------------- Sonderfall: Unterschied in letzter Zeile suchen -------------
1549 // Beachte: Es koennen undefinierte Stellen auftreten, bei abbrechender HNE
1550 // muss die Ende-Markierung weg, h_[r] ist unendlich, die Matrix muss mit
1551 // Nullen fortgesetzt gedacht werden
1552 //----------------------------------------------------------------------------
1553       if (ncols(a1)>ncols(a2)) { j=ncols(a1); }
1554       else                     { j=ncols(a2); }
1555       unterschied=0;
1556       if ((h1[s]>0) && (s==size(h1))) {
1557         a1[s,h1[s]+1]=0;
1558         if (ncols(a1)<=ncols(a2)) { unterschied=1; }
1559       }
1560       if ((h2[s]>0) && (s==size(h2))) {
1561         a2[s,h2[s]+1]=0;
1562         if (ncols(a2)<=ncols(a1)) { unterschied=1; }
1563       }
1564       if (unterschied==1) {                   // mind. eine HNE war endlich
1565         matrix ma1[1][j]=a1[s,1..ncols(a1)];  // und bedarf der Fortsetzung
1566         matrix ma2[1][j]=a2[s,1..ncols(a2)];  // mit Nullen
1567       }
1568       else {
1569         if (ncols(a1)>ncols(a2)) { j=ncols(a2); }
1570         else                     { j=ncols(a1); }
1571         matrix ma1[1][j]=a1[s,1..j];          // Beschr. auf vergleichbaren
1572         matrix ma2[1][j]=a2[s,1..j];          // Teil (der evtl. y's enth.)
1573       }
1574       for (i=1; (ma1[1,i]==ma2[1,i]) && (i<j) && (ma1[1,i]!=var(2)); i++) {;}
1575       if (ma1[1,i]==ma2[1,i]) {
1576         "//** The two HNE's are identical!";
1577         "//** You have either tried to intersect a branch with itself,";
1578         "//** or the two branches have been developed separately.";
1579         "//   In the latter case use `extdevelop' to extend the HNE's until",
1580         "they differ.";
1581         return(-1);
1582       }
1583       if ((ma1[1,i]==var(2)) || (ma2[1,i]==var(2))) {
1584         "//** The two HNE's are (so far) identical. This is because they",
1585         "have been";
1586         "//** computed separately. I need more data; use `extdevelop' to",
1587         "extend them,";
1588         if (ma1[1,i]==var(2)) {"//** at least the first one.";}
1589         else                  {"//** at least the second one.";}
1590         return(-1);
1591       }
1592     }
1593   }
1594   sum=0;
1595   h1[size(h1)]=ncols(a1)+42;        // Ersatz fuer h1[r]=infinity
1596   h2[size(h2)]=ncols(a2)+42;
1597   for (j=1; j<s; j++) {sum=sum+h1[j]*n1[j]*n2[j];}
1598   if ((i<=h1[s]) && (i<=h2[s]))    { schnitt=sum+i*n1[s]*n2[s]; }
1599   if (i==h2[s]+1) { schnitt=sum+h2[s]*n1[s]*n2[s]+n2[s+1]*n1[s]; }
1600   if (i==h1[s]+1) { schnitt=sum+h1[s]*n2[s]*n1[s]+n1[s+1]*n2[s]; }
1601 // "s:",s-1,"i:",i,"S:",sum;
1602 }
1603 return(schnitt);
1604}
1605example
1606{
1607  "EXAMPLE:"; echo = 2;
1608  ring r=0,(x,y),dp;
1609  list Hne=hnexpansion((x2-y3)*(x2+y3));
1610  intersection(Hne[1],Hne[2]);
1611}
1612///////////////////////////////////////////////////////////////////////////////
1613
1614proc multsequence
1615"USAGE:   multsequence(INPUT); INPUT list or poly
1616ASSUME:  @code{INPUT} is the output of @code{develop(f)}, or of
1617         @code{extdevelop(develop(f),n)}, or one entry of the list of HN data
1618         computed by @code{hnexpansion(f[,\"ess\"])}.
1619RETURN:  intvec corresponding to the multiplicity sequence of the irreducible
1620         plane curve singularity described by the HN data (return value
1621         coincides with @code{invariants(INPUT)[6]}).
1622
1623ASSUME:  @code{INPUT} is a bivariate polynomial f, or the output of
1624         @code{hnexpansion(f)}, or the list of HN data computed by
1625         @code{hnexpansion(f [,\"ess\"])}.
1626RETURN:  list of two integer matrices:
1627@texinfo
1628@table @asis
1629@item  @code{multsequence(INPUT)[1][i,*]}
1630   contains the multiplicities of the branches at their infinitely near point
1631   of 0 in its (i-1) order neighbourhood (i.e., i=1: multiplicity of the
1632   branches themselves, i=2: multiplicity of their 1st quadratic transform,
1633   etc., @*
1634   Hence, @code{multsequence(INPUT)[1][*,j]} is the multiplicity sequence
1635   of branch j.
1636@item  @code{multsequence(INPUT)[2][i,*]}:
1637   contains the information which of these infinitely near points coincide.
1638@end table
1639@end texinfo
1640NOTE:  The order of the elements of the list of HN data obtained from
1641       @code{hnexpansion(f [,\"ess\"])} must not be changed (because otherwise
1642       the coincident infinitely near points couldn't be grouped together,
1643       see the meaning of the 2nd intmat in the example).
1644       Hence, it is not wise to compute the HN expansion of polynomial factors
1645       separately, put them into a list INPUT and call
1646       @code{multsequence(INPUT)}. @*
1647       Use @code{displayMultsequence} to produce a better readable output for
1648       reducible curves on the screen. @*
1649       In case the Hamburger-Noether expansion of the curve f is needed
1650       for other purposes as well it is better to calculate this first
1651       with the aid of @code{hnexpansion} and use it as input instead of
1652       the polynomial itself.
1653SEE ALSO: displayMultsequence, develop, hnexpansion, separateHNE
1654KEYWORDS: multiplicity sequence
1655EXAMPLE: example multsequence;  shows an example
1656"
1657{
1658 //---- INPUT = poly, or HNEring --------------------
1659 if (typeof(#[1])=="poly") {
1660   list L=hnexpansion(#[1]);
1661   if (typeof(L[1])=="ring") {
1662     def altring = basering;
1663     def HNring = L[1]; setring HNring;
1664     list Ergebnis = multsequence(hne);
1665     setring altring;
1666     kill HNring;
1667     return(Ergebnis);
1668   }
1669   else {
1670     return(multsequence(L));
1671   }
1672 }
1673 if (typeof(#[1])=="ring") {
1674   def altring = basering;
1675   def HNring = #[1]; setring HNring;
1676   list Ergebnis = multsequence(hne);
1677   setring altring;
1678   kill HNring;
1679   return(Ergebnis);
1680 }
1681 //-- entferne ueberfluessige Daten zur Erhoehung der Rechengeschwindigkeit: --
1682 #=stripHNE(#);
1683 int k,i,j;
1684 //----------------- Multiplizitaetensequenz eines Zweiges --------------------
1685 if (typeof(#[1])=="matrix") {
1686  intvec v=#[2];
1687  list ergebnis;
1688  if (#[3]==-1) {
1689    "An error has occurred in develop, so there is no HNE.";
1690   return(intvec(0));
1691  }
1692  intvec multips,multseq;
1693  multips=multiplicities(#);
1694  k=1;
1695  for (i=1; i<size(v); i++) {
1696    for (j=1; j<=v[i]; j++) {
1697      multseq[k]=multips[i];
1698      k++;
1699  }}
1700  multseq[k]=1;
1701  //--- fuelle die Multipl.seq. mit den notwendigen Einsen auf -- T.Keilen ----
1702  int tester=k;
1703  while((multseq[tester]==1) and (tester>1))
1704  {
1705    tester=tester-1;
1706  }
1707  if((multseq[tester]!=1) and (multseq[tester]!=k-tester))
1708  {
1709    for (i=k+1; i<=tester+multseq[tester]; i++)
1710    {
1711      multseq[i]=1;
1712    }
1713  }
1714  //--- Ende T.Keilen --- 06.05.02
1715  return(multseq);
1716 }
1717 //---------------------------- mehrere Zweige --------------------------------
1718 else {
1719   list HNEs=#;
1720   int anzahl=size(HNEs);
1721   int maxlength=0;
1722   int bisher;
1723   intvec schnitt,ones;
1724   ones[anzahl]=0;
1725   ones=ones+1;                  // = 1,1,...,1
1726   for (i=1; i<anzahl; i++) {
1727     schnitt[i]=separateHNE(HNEs[i],HNEs[i+1]);
1728     j=size(multsequence(HNEs[i]));
1729     maxlength=maxlength*(j < maxlength) + j*(j >= maxlength);
1730     maxlength=maxlength*(schnitt[i]+1 < maxlength)
1731               + (schnitt[i]+1)*(schnitt[i]+1 >= maxlength);
1732   }
1733   j=size(multsequence(HNEs[anzahl]));
1734   maxlength=maxlength*(j < maxlength) + j*(j >= maxlength);
1735
1736//-------------- Konstruktion der ersten zu berechnenden Matrix ---------------
1737   intmat allmults[maxlength][anzahl];
1738   for (i=1; i<=maxlength; i++)  { allmults[i,1..anzahl]=ones[1..anzahl]; }
1739   for (i=1; i<=anzahl; i++) {
1740     ones=multsequence(HNEs[i]);
1741     allmults[1..size(ones),i]=ones[1..size(ones)];
1742   }
1743//---------------------- Konstruktion der zweiten Matrix ----------------------
1744   intmat separate[maxlength][anzahl];
1745   for (i=1; i<=maxlength; i++) {
1746     k=1;
1747     bisher=0;
1748     if (anzahl==1) { separate[i,1]=1; }
1749     for (j=1; j<anzahl; j++)   {
1750       if (schnitt[j]<i) {
1751         separate[i,k]=j-bisher;
1752         bisher=j;
1753         k++;
1754       }
1755       separate[i,k]=anzahl-bisher;
1756     }
1757   }
1758  return(list(allmults,separate));
1759 }
1760}
1761example
1762{
1763  "EXAMPLE:"; echo = 2;
1764  ring r=0,(x,y),dp;
1765  list Hne=hnexpansion((x6-y10)*(x+y2-y3)*(x+y2+y3));
1766  multsequence(Hne[1]),"  |  ",multsequence(Hne[2]),"  |  ",
1767  multsequence(Hne[3]),"  |  ",multsequence(Hne[4]);
1768  multsequence(Hne);
1769  // The meaning of the entries of the 2nd matrix is as follows:
1770  displayMultsequence(Hne);
1771}
1772///////////////////////////////////////////////////////////////////////////////
1773
1774proc displayMultsequence
1775"USAGE:   displayMultsequence(INPUT); INPUT list or poly
1776ASSUME:  @code{INPUT} is a bivariate polynomial, or the output of
1777         @code{develop(f)}, resp. of @code{extdevelop(develop(f),n)}, or (one
1778         entry of) the list of HN data computed by @code{hnexpansion(f[,\"ess\"])},
1779         or the output of @code{hnexpansion(f)}.
1780RETURN:  nothing
1781DISPLAY: the sequence of multiplicities:
1782@format
1783 - if @code{INPUT=develop(f)} or @code{INPUT=extdevelop(develop(f),n)} or @code{INPUT=hne[i]}:
1784                      @code{a , b , c , ....... , 1}
1785 - if @code{INPUT=f} or @code{INPUT=hnexpansion(f)} or @code{INPUT=hne}:
1786                      @code{[(a_1, .... , b_1 , .... , c_1)],}
1787                      @code{[(a_2, ... ), ... , (... , c_2)],}
1788                      @code{ ........................................ ,}
1789                      @code{[(a_n),(b_n), ....., (c_n)]}
1790     with:
1791       @code{a_1 , ... , a_n} the sequence of multiplicities of the 1st branch,
1792       @code{[...]} the multiplicities of the j-th transform of all branches,
1793       @code{(...)} indicating branches meeting in an infinitely near point.
1794@end format
1795NOTE:  The Same restrictions as in @code{multsequence} apply for the input.@*
1796       In case the Hamburger-Noether expansion of the curve f is needed
1797       for other purposes as well it is better to calculate this first
1798       with the aid of @code{hnexpansion} and use it as input instead of
1799       the polynomial itself.
1800SEE ALSO: multsequence, develop, hnexpansion, separateHNE
1801EXAMPLE: example displayMultsequence;  shows an example
1802"
1803{
1804 //---- INPUT = poly, or HNEring --------------------
1805 if (typeof(#[1])=="poly") {
1806   list L=hnexpansion(#[1]);
1807   if (typeof(L[1])=="ring") {
1808     def HNring = L[1]; setring HNring;
1809     displayMultsequence(hne);
1810     return();
1811   }
1812   else {
1813     displayMultsequence(L);
1814     return();
1815   }
1816 }
1817 if (typeof(#[1])=="ring") {
1818   def HNring = #[1]; setring HNring;
1819   displayMultsequence(hne);
1820   return();
1821 }
1822
1823 //-- entferne ueberfluessige Daten zur Erhoehung der Rechengeschwindigkeit: --
1824 #=stripHNE(#);
1825 //----------------- Multiplizitaetensequenz eines Zweiges --------------------
1826 if (typeof(#[1])=="matrix") {
1827   if (#[3]==-1) {
1828     "An error has occurred in develop, so there is no HNE.";
1829   }
1830   else {
1831     "The sequence of multiplicities is  ",multsequence(#);
1832 }}
1833 //---------------------------- mehrere Zweige --------------------------------
1834 else {
1835   list multips=multsequence(#);
1836   int i,j,k,l;
1837   string output;
1838   for (i=1; i<=nrows(multips[1]); i++) {
1839     output="[";
1840     k=1;
1841     for (l=1; k<=ncols(multips[1]); l++) {
1842       output=output+"(";
1843       for (j=1; j<=multips[2][i,l]; j++) {
1844         output=output+string(multips[1][i,k]);
1845         k++;
1846         if (j<multips[2][i,l]) { output=output+","; }
1847       }
1848       output=output+")";
1849       if ((k-1) < ncols(multips[1])) { output=output+","; }
1850      }
1851     output=output+"]";
1852     if (i<nrows(multips[1])) { output=output+","; }
1853     output;
1854   }
1855 }
1856}
1857example
1858{
1859  "EXAMPLE:"; echo = 2;
1860  ring r=0,(x,y),dp;
1861  // Example 1: Input = output of develop
1862  displayMultsequence(develop(x3-y5));
1863
1864  // Example 2: Input = bivariate polynomial
1865  displayMultsequence((x6-y10)*(x+y2-y3)*(x+y2+y3));
1866}
1867
1868///////////////////////////////////////////////////////////////////////////////
1869
1870proc separateHNE (list hn1,list hn2)
1871"USAGE:    separateHNE(hne1,hne2);  hne1, hne2 lists
1872ASSUME:   hne1, hne2 are HNEs (=output of
1873          @code{develop(f)}, @code{extdevelop(develop(f),n)}, or
1874          one entry in the list @code{hne} in the ring created by
1875          @code{hnexpansion(f[,\"ess\"])}.
1876RETURN:   number of quadratic transformations needed to separate both curves
1877          (branches).
1878SEE ALSO: develop, hnexpansion, multsequence, displayMultsequence
1879EXAMPLE:  example separateHNE;  shows an example
1880"
1881{
1882 int i,j,s,unterschied,separated;
1883 matrix a1=hn1[1];
1884 matrix a2=hn2[1];
1885 intvec h1=hn1[2];
1886 intvec h2=hn2[2];
1887 if (hn1[3]!=hn2[3]) {
1888 //-- die jeweils erste Zeile von hn1,hn2 gehoert zu verschiedenen Parametern -
1889 //---------------- d.h. beide Kurven schneiden sich transversal --------------
1890   separated=1;
1891 }
1892 else {
1893 //--------- die jeweils erste Zeile gehoert zum gleichen Parameter -----------
1894   unterschied=0;
1895   for (s=1; (h1[s]==h2[s]) && (s<size(h1)) && (s<size(h2))
1896              && (unterschied==0); s++) {
1897     for (i=1; (a1[s,i]==a2[s,i]) && (i<=h1[s]); i++) {;}
1898     if (i<=h1[s]) {
1899       unterschied=1;
1900       s--;                    // um s++ am Schleifenende wieder auszugleichen
1901     }
1902   }
1903   if (unterschied==0) {
1904     if ((s<size(h1)) && (s<size(h2))) {
1905       for (i=1; (a1[s,i]==a2[s,i]) && (i<=h1[s]) && (i<=h2[s]); i++) {;}
1906     }
1907     else {
1908 //-------------- Sonderfall: Unterschied in letzter Zeile suchen -------------
1909 // Beachte: Es koennen undefinierte Stellen auftreten, bei abbrechender HNE
1910 // muss die Ende-Markierung weg, h_[r] ist unendlich, die Matrix muss mit
1911 // Nullen fortgesetzt gedacht werden
1912 //----------------------------------------------------------------------------
1913       if (ncols(a1)>ncols(a2)) { j=ncols(a1); }
1914       else                     { j=ncols(a2); }
1915       unterschied=0;
1916       if ((h1[s]>0) && (s==size(h1))) {
1917         a1[s,h1[s]+1]=0;
1918         if (ncols(a1)<=ncols(a2)) { unterschied=1; }
1919       }
1920       if ((h2[s]>0) && (s==size(h2))) {
1921         a2[s,h2[s]+1]=0;
1922         if (ncols(a2)<=ncols(a1)) { unterschied=1; }
1923       }
1924       if (unterschied==1) {                   // mind. eine HNE war endlich
1925         matrix ma1[1][j]=a1[s,1..ncols(a1)];  // und bedarf der Fortsetzung
1926         matrix ma2[1][j]=a2[s,1..ncols(a2)];  // mit Nullen
1927       }
1928       else {
1929         if (ncols(a1)>ncols(a2)) { j=ncols(a2); }
1930         else                     { j=ncols(a1); }
1931         matrix ma1[1][j]=a1[s,1..j];          // Beschr. auf vergleichbaren
1932         matrix ma2[1][j]=a2[s,1..j];          // Teil (der evtl. y's enth.)
1933       }
1934       for (i=1; (ma1[1,i]==ma2[1,i]) && (i<j) && (ma1[1,i]!=var(2)); i++) {;}
1935       if (ma1[1,i]==ma2[1,i]) {
1936         "//** The two HNE's are identical!";
1937         "//** You have either tried to compare a branch with itself,";
1938         "//** or the two branches have been developed separately.";
1939         "//   In the latter case use `extdevelop' to extend the HNE's until",
1940         "they differ.";
1941         return(-1);
1942       }
1943       if ((ma1[1,i]==var(2)) || (ma2[1,i]==var(2))) {
1944         "//** The two HNE's are (so far) identical. This is because they",
1945         "have been";
1946         "//** computed separately. I need more data; use `extdevelop' to",
1947         "extend them,";
1948         if (ma1[1,i]==var(2)) {"//** at least the first one.";}
1949         else                  {"//** at least the second one.";}
1950         return(-1);
1951       }
1952     }
1953   }
1954   separated=i;
1955   for (j=1; j<s; j++) { separated=separated+h1[j]; }
1956 }
1957 return(separated);
1958}
1959example
1960{ "EXAMPLE:"; echo = 2;
1961  int p=printlevel; printlevel=-1;
1962  ring r=0,(x,y),dp;
1963  list hne1=develop(x);
1964  list hne2=develop(x+y);
1965  list hne3=develop(x+y2);
1966  separateHNE(hne1,hne2);  // two transversal lines
1967  separateHNE(hne1,hne3);  // one quadratic transform. gives 1st example
1968  printlevel=p;
1969}
1970///////////////////////////////////////////////////////////////////////////////
1971
1972proc displayHNE(list ldev,list #)
1973"USAGE:   displayHNE(L[,n]); L list, n int
1974ASSUME:  L is the output of @code{develop(f)}, or of @code{exdevelop(f,n)},
1975         or of @code{hnexpansion(f[,\"ess\"])}, or (one entry in) the list
1976         @code{hne} in the ring created by @code{hnexpansion(f[,\"ess\"])}.
1977RETURN:  - if only one argument is given and if the input are the HN data
1978         of an irreducible plane curve singularity, no return value, but
1979         display an ideal HNE of the following form:
1980     @example
1981       y = []*x^1+[]*x^2   +...+x^<>*z(1)
1982       x =        []*z(1)^2+...+z(1)^<>*z(2)
1983       z(1) =     []*z(2)^2+...+z(2)^<>*z(3)
1984       .......             ..........................
1985       z(r-1) =   []*z(r)^2+[]*z(r)^3+......
1986     @end example
1987        where @code{x},@code{y} are the first 2 variables of the basering.
1988        The values of @code{[]} are the coefficients of the Hamburger-Noether
1989        matrix, the values of @code{<>} are represented by @code{x} in the
1990        HN matrix.@*
1991        - if a second argument is given and if the input are the HN data
1992        of an irreducible plane curve singularity, return a ring containing
1993        an ideal @code{HNE} as described above.@*
1994        - if L corresponds to the output of @code{hnexpansion(f)}
1995        or to the list of HN data computed by @code{hnexpansion(f[,\"ess\"])},
1996        @code{displayHNE(L[,n])} shows the HNE's of all branches of f in the
1997        format described above. The optional parameter is then ignored.
1998NOTE:  The 1st line of the above ideal (i.e., @code{HNE[1]}) means that
1999     @code{y=[]*z(0)^1+...}, the 2nd line (@code{HNE[2]}) means that
2000     @code{x=[]*z(1)^2+...}, so you can see which indeterminate
2001     corresponds to which line (it's also possible that @code{x} corresponds
2002     to the 1st line and @code{y} to the 2nd).
2003
2004SEE ALSO: develop, hnexpansion
2005EXAMPLE: example displayHNE; shows an example
2006"
2007{
2008 if ((typeof(ldev[1])=="list") || (typeof(ldev[1])=="none")) {
2009   for (int i=1; i<=size(ldev); i++) {
2010     "// Hamburger-Noether development of branch nr."+string(i)+":";
2011     displayHNE(ldev[i]);"";
2012   }
2013   return();
2014 }
2015 //--------------------- Initialisierungen und Ringwechsel --------------------
2016 matrix m=ldev[1];
2017 intvec v=ldev[2];
2018 int switch=ldev[3];
2019 if (switch==-1) {
2020   "An error has occurred throughout the expansion, so there is no HNE.";
2021   return(ideal(0));
2022 }
2023 def altring=basering;
2024 /////////////////////////////////////////////////////////
2025 //  Change by T. Keilen 08.06.2002
2026 //  ring + ring does not work if one ring is an algebraic extension
2027/*
2028 if (parstr(basering)!="") {
2029   if (charstr(basering)!=string(char(basering))+","+parstr(basering)) {
2030     execute
2031      ("ring dazu=("+charstr(basering)+"),z(0.."+string(size(v)-1)+"),ls;");
2032   }
2033   else { ring dazu=char(altring),z(0..size(v)-1),ls; }
2034 }
2035 else   { ring dazu=char(altring),z(0..size(v)-1),ls; }
2036 def displayring=dazu+altring;
2037*/
2038 execute("ring displayring=("+charstr(basering)+"),(z(0.."+string(size(v)-1)+"),"+varstr(basering)+"),(ls("+string(size(v))+"),"+ordstr(basering)+");");
2039 // End change by T. Keilen
2040 //////////////////////////////////////////////////////////////
2041 setring displayring;
2042 map holematrix=altring,0;        // mappt nur die Monome vom Grad Null
2043 matrix m=holematrix(m);
2044 int i,j;
2045
2046 // lossen: check the last row for finiteness (06/2004)
2047 int rowM=nrows(m);
2048 int colM=ncols(m);
2049 int undef_bd=v[size(v)];
2050 if ( undef_bd<-1 ){
2051   for (j=-undef_bd; j<=colM; j++) { m[rowM,j]=0; }
2052 }
2053
2054 //--------------------- Erzeuge Matrix n mit n[i,j]=z(j-1)^i -----------------
2055 matrix n[colM][rowM];
2056 for (j=1; j<=rowM; j++) {
2057    for (i=1; i<=colM; i++) { n[i,j]=z(j-1)^i; }
2058 }
2059 matrix displaymat=m*n;
2060 ideal HNE;
2061 for (i=1; i<rowM; i++) { HNE[i]=displaymat[i,i]+z(i)*z(i-1)^v[i]; }
2062 HNE[rowM]=displaymat[rowM,rowM];
2063
2064 // lossen: output modified (06/2004)
2065 if (size(#) == 0)
2066 {
2067   if (switch==0) {
2068     HNE=subst(HNE,z(0),var(size(v)+1));
2069   }
2070   else  {
2071     HNE=subst(HNE,z(0),var(size(v)+2));
2072   }
2073
2074   for (j=1; j<=ncols(HNE); j++){
2075     string stHNE(j)=string(HNE[j]);
2076   }
2077   if (undef_bd<-1)
2078   {
2079     stHNE(size(v))=stHNE(size(v))+" + ..... (terms of degree >="
2080                                 +string(-undef_bd)+")";
2081   }
2082   if (undef_bd==-1)
2083   {
2084     stHNE(size(v))=stHNE(size(v))+" + ..... (terms of degree >="
2085                                 +string(colM+1)+")";
2086   }
2087
2088   if (switch==0) {
2089     stHNE(1) = "  "+string(var(size(v)+2))+" = "+stHNE(1);
2090   }
2091   else {
2092     stHNE(1) = "  "+string(var(size(v)+1))+" = "+stHNE(1);
2093   }
2094   stHNE(1);
2095   if (ncols(HNE)==1) {return();}
2096
2097   if (switch==0) {
2098     stHNE(2) = "  "+string(var(size(v)+1))+" = "+stHNE(2);
2099   }
2100   else {
2101     stHNE(2) = "  "+string(var(size(v)+2))+" = "+stHNE(2);
2102   }
2103   stHNE(2);
2104
2105   for (j=3; j<=ncols(HNE); j++){
2106     stHNE(j)= "  "+"z(" +string(j-2)+ ") = "+stHNE(j);
2107     stHNE(j);
2108   }
2109   return();
2110 }
2111
2112 if (rowM<2) { HNE[2]=z(0); }
2113
2114 if (switch==0) {
2115    HNE[1] = HNE[1]-var(size(v)+2);
2116    HNE[2] = HNE[2]-var(size(v)+1);
2117 }
2118 else {
2119    HNE[1] = HNE[1]-var(size(v)+1);
2120    HNE[2] = HNE[2]-var(size(v)+2);
2121 }
2122if (size(#) == 0) {
2123   HNE;
2124   return();
2125 }
2126if (size(#) != 0) {
2127   HNE;
2128   export(HNE);
2129   return(displayring);
2130 }
2131}
2132example
2133{ "EXAMPLE:"; echo = 2;
2134  ring r=0,(x,y),dp;
2135  poly f=x3+2xy2+y2;
2136  list hn=develop(f);
2137  displayHNE(hn);
2138}
2139///////////////////////////////////////////////////////////////////////////////
2140//                      procedures for reducible curves                      //
2141///////////////////////////////////////////////////////////////////////////////
2142
2143// proc newtonhoehne (poly f)
2144// USAGE:   newtonhoehne(f);   f poly
2145// ASSUME:  basering = ...,(x,y),ds  or ls
2146// RETURN:  list of intvec(x,y) of coordinates of the newtonpolygon of f
2147// NOTE:    This proc is only available in versions of Singular that know the
2148//         command  system("newton",f);  f poly
2149// {
2150// intvec nm = getnm(f);
2151//  if ((nm[1]>0) && (nm[2]>0)) { f=jet(f,nm[1]*nm[2],nm); }
2152//  list erg=system("newton",f);
2153//  int i; list Ausgabe;
2154//  for (i=1; i<=size(erg); i++) { Ausgabe[i]=leadexp(erg[i]); }
2155// return(Ausgabe);
2156// }
2157///////////////////////////////////////////////////////////////////////////////
2158
2159proc newtonpoly (poly f, int #)
2160"USAGE:   newtonpoly(f);   f poly
2161ASSUME:  basering has exactly two variables; @*
2162         f is convenient, that is, f(x,0) != 0 != f(0,y).
2163RETURN:  list of intvecs (= coordinates x,y of the Newton polygon of f).
2164NOTE:    Procedure uses @code{execute}; this can be avoided by calling
2165         @code{newtonpoly(f,1)} if the ordering of the basering is @code{ls}.
2166KEYWORDS: Newton polygon
2167EXAMPLE: example newtonpoly;  shows an example
2168"
2169{
2170  if (size(#)>=1)
2171  {
2172    if (typeof(#[1])=="int")
2173    {
2174      // this is done to avoid the "execute" command for procedures in
2175      //  hnoether.lib
2176      def is_ls=#[1];
2177    }
2178  }
2179  if (defined(is_ls)<=0)
2180  {
2181    def @Rold=basering;
2182    execute("ring @RR=("+charstr(basering)+"),("+varstr(basering)+"),ls;");
2183    poly f=imap(@Rold,f);
2184  }
2185  intvec A=(0,ord(subst(f,var(1),0)));
2186  intvec B=(ord(subst(f,var(2),0)),0);
2187  intvec C,H; list L;
2188  int abbruch,i;
2189  poly hilf;
2190  L[1]=A;
2191  f=jet(f,A[2]*B[1]-1,intvec(A[2],B[1]));
2192  if (defined(is_ls))
2193  {
2194    map xytausch=basering,var(2),var(1);
2195  }
2196  else
2197  {
2198    map xytausch=@RR,var(2),var(1);
2199  }
2200  for (i=2; f!=0; i++)
2201  {
2202     abbruch=0;
2203     while (abbruch==0)
2204     {
2205        C=leadexp(f);
2206        if(jet(f,A[2]*C[1]-A[1]*C[2]-1,intvec(A[2]-C[2],C[1]-A[1]))==0)
2207        {
2208           abbruch=1;
2209        }
2210        else
2211        {
2212           f=jet(f,-C[1]-1,intvec(-1,0));
2213        }
2214    }
2215    hilf=jet(f,A[2]*C[1]-A[1]*C[2],intvec(A[2]-C[2],C[1]-A[1]));
2216    H=leadexp(xytausch(hilf));
2217    A=H[2],H[1];
2218    L[i]=A;
2219    f=jet(f,A[2]*B[1]-1,intvec(A[2],B[1]-A[1]));
2220  }
2221  L[i]=B;
2222  if (defined(is_ls))
2223  {
2224    return(L);
2225  }
2226  else
2227  {
2228    setring @Rold;
2229    return(L);
2230  }
2231}
2232example
2233{
2234 "EXAMPLE:"; echo = 2;
2235  ring r=0,(x,y),ls;
2236  poly f=x5+2x3y-x2y2+3xy5+y6-y7;
2237  newtonpoly(f);
2238}
2239///////////////////////////////////////////////////////////////////////////////
2240
2241proc is_NND (poly f, list #)
2242"USAGE:   is_NND(f[,mu,NP]);   f poly, mu int, NP list of intvecs
2243ASSUME:  f is convenient, that is, f(x,0) != 0 != f(0,y);@*
2244         mu (optional) is Milnor number of f.@*
2245         NP (optional) is output of @code{newtonpoly(f)}.
2246RETURN:  int: 1 if f is Newton non-degenerate, 0 otherwise.
2247SEE ALSO: newtonpoly
2248KEYWORDS: Newton non-degenerate; Newton polygon
2249EXAMPLE: example is_NND;  shows examples
2250"
2251{
2252  int i;
2253  int i_print=printlevel-voice+2;
2254
2255  if (size(#)==0)
2256  {
2257    int mu=milnor(f);
2258    list NP=newtonpoly(f);
2259  }
2260  else
2261  {
2262    if (typeof(#[1])=="int")
2263    {
2264      def mu=#[1];
2265      def NP=#[2];
2266      for (i=1;i<=size(NP);i++)
2267      {
2268        if (typeof(NP[i])!="intvec")
2269        {
2270          print("third input cannot be Newton polygon ==> ignored ")
2271          NP=newtonpoly(f);
2272          i=size(NP)+1;
2273        }
2274      }
2275    }
2276    else
2277    {
2278      print("second input cannot be Milnor number ==> ignored ")
2279      int mu=milnor(f);
2280      NP=newtonpoly(f);
2281    }
2282  }
2283
2284  // computation of the Newton number:
2285  int s=size(NP);
2286  int nN=-NP[1][2]-NP[s][1]+1;
2287  intmat m[2][2];
2288  for(i=1;i<=s-1;i++)
2289  {
2290    m=NP[i+1],NP[i];
2291    nN=nN+det(m);
2292  }
2293
2294  if(mu==nN)
2295  { // the Newton-polygon is non-degenerate
2296    return(1);
2297  }
2298  else
2299  {
2300    return(0);
2301  }
2302}
2303example
2304{
2305 "EXAMPLE:"; echo = 2;
2306  ring r=0,(x,y),ls;
2307  poly f=x5+y3;
2308  is_NND(f);
2309  poly g=(x-y)^5+3xy5+y6-y7;
2310  is_NND(g);
2311
2312  // if already computed, one should give the Minor number and Newton polygon
2313  // as second and third input:
2314  int mu=milnor(g);
2315  list NP=newtonpoly(g);
2316  is_NND(g,mu,NP);
2317}
2318
2319
2320///////////////////////////////////////////////////////////////////////////////
2321
2322proc charPoly(poly f, int M, int N)
2323"USAGE:  charPoly(f,M,N);  f bivariate poly,  M,N int: length and height
2324                          of Newton polygon of f, which has to be only one line
2325RETURN:  the characteristic polynomial of f
2326EXAMPLE: example charPoly;  shows an example
2327"
2328{
2329 poly charp;
2330 int Np=N/ gcd(M,N);
2331 f=subst(f,var(1),1);
2332 for(charp=0; f<>0; f=f-lead(f))
2333  { charp=charp+leadcoef(f)*var(2)^(leadexp(f)[2]/ Np);}
2334 return(charp);
2335}
2336example
2337{ "EXAMPLE:"; echo = 2;
2338  ring exring=0,(x,y),dp;
2339  charPoly(y4+2y3x2-yx6+x8,8,4);
2340  charPoly(y6+3y3x2-x4,4,6);
2341}
2342///////////////////////////////////////////////////////////////////////////////
2343
2344proc find_in_list(list L,int p)
2345"USAGE:   find_in_list(L,p); L: list of intvec(x,y)
2346         (sorted in y: L[1][2]>=L[2][2]), int p >= 0
2347RETURN:  int i: L[i][2]=p if existent; otherwise i with L[i][2]<p if existent;
2348         otherwise i = size(L)+1;
2349EXAMPLE: example find_in_list;  shows an example
2350"
2351{
2352 int i;
2353 L[size(L)+1]=intvec(0,-1);          // falls p nicht in L[.][2] vorkommt
2354 for (i=1; L[i][2]>p; i++) {;}
2355 return(i);
2356}
2357example
2358{ "EXAMPLE:"; echo = 2;
2359  list L = intvec(0,4), intvec(1,2), intvec(2,1), intvec(4,0);
2360  find_in_list(L,1);
2361  L[find_in_list(L,2)];
2362}
2363///////////////////////////////////////////////////////////////////////////////
2364
2365proc get_last_divisor(int M, int N)
2366"USAGE:   get_last_divisor(M,N); int M,N
2367RETURN:  int Q: M=q1*N+r1, N=q2*r1+r2, ..., ri=Q*r(i+1) (Euclidean alg.)
2368EXAMPLE: example get_last_divisor; shows an example
2369"
2370{
2371 int R=M%N; int Q=M / N;
2372 while (R!=0) {M=N; N=R; R=M%N; Q=M / N;}
2373 return(Q)
2374}
2375example
2376{ "EXAMPLE"; echo = 2;
2377  ring r=0,(x,y),dp;
2378  get_last_divisor(12,10);
2379}
2380///////////////////////////////////////////////////////////////////////////////
2381proc redleit (poly f,intvec S, intvec E)
2382"USAGE:   redleit(f,S,E);  f poly, S,E intvec(x,y)
2383         S,E are two different points on a line in the Newton diagram of f
2384RETURN:  poly g: all monomials of f which lie on or below that line
2385NOTE:    The main purpose is that if the line defined by S and E is part of the
2386         Newton polygon, the result is the quasihomogeneous leading form of f
2387         wrt. that line.
2388SEE ALSO: newtonpoly
2389EXAMPLE: example redleit;  shows an example
2390"
2391{
2392 if (E[1]<S[1]) { intvec H=E; E=S; S=H; } // S,E verkehrt herum eingegeben
2393 return(jet(f,E[1]*S[2]-E[2]*S[1],intvec(S[2]-E[2],E[1]-S[1])));
2394}
2395example
2396{ "EXAMPLE"; echo = 2;
2397  ring exring=0,(x,y),dp;
2398  redleit(y6+xy4-2x3y2+x4y+x6,intvec(3,2),intvec(4,1));
2399}
2400///////////////////////////////////////////////////////////////////////////////
2401
2402
2403proc extdevelop (list l, int Exaktheit)
2404"USAGE:   extdevelop(L,N); list L, int N
2405ASSUME:  L is the output of @code{develop(f)}, or of @code{extdevelop(l,n)},
2406         or one entry in the list @code{hne} in the ring created by
2407          @code{hnexpansion(f[,\"ess\"])}.
2408RETURN:  an extension of the Hamburger-Noether development of f as a list
2409         in the same format as L has (up to the last entry in the output
2410         of @code{develop(f)}).@*
2411         Type @code{help develop;}, resp. @code{help hnexpansion;} for more
2412         details.
2413NOTE:    The new HN-matrix will have at least N columns (if the HNE is not
2414         finite). In particular, if f is irreducible then (in most cases)
2415         @code{extdevelop(develop(f),N)} will produce the same result as
2416         @code{develop(f,N)}.@*
2417         If the matrix M of L has n columns then, compared with
2418         @code{parametrization(L)}, @code{paramametrize(extdevelop(L,N))} will increase the
2419         exactness by at least (N-n) more significant monomials.
2420SEE ALSO: develop, hnexpansion, param
2421EXAMPLE: example extdevelop;  shows an example
2422"
2423{
2424 //------------ Initialisierungen und Abfangen unzulaessiger Aufrufe ----------
2425 matrix m=l[1];
2426 intvec v=l[2];
2427 int switch=l[3];
2428 if (nvars(basering) < 2) {
2429   " Sorry. I need two variables in the ring.";
2430   return(list(matrix(maxideal(1)[1]),intvec(0),-1,poly(0)));}
2431 if (switch==-1) {
2432   "An error has occurred in develop, so there is no HNE and no extension.";
2433   return(l);
2434 }
2435 poly f=l[4];
2436 if (f==0) {
2437   " No extension is possible";
2438   return(l);
2439 }
2440 int Q=v[size(v)];
2441 if (Q>0) {
2442   " The HNE was already exact";
2443   return(l);
2444 }
2445 else {
2446   if (Q==-1) { Q=ncols(m); }
2447   else { Q=-Q-1; }
2448 }
2449 int zeile=nrows(m);
2450 int spalten,i,M;
2451 ideal lastrow=m[zeile,1..Q];
2452 int ringwechsel=(varstr(basering)!="x,y") or (ordstr(basering)!="ls(2),C");
2453
2454 //------------------------- Ringwechsel, falls noetig ------------------------
2455 if (ringwechsel) {
2456  def altring = basering;
2457  int p = char(basering);
2458  if (charstr(basering)!=string(p)) {
2459     string tststr=charstr(basering);
2460     tststr=tststr[1..find(tststr,",")-1];     //-> "p^k" bzw. "p"
2461     if (tststr==string(p)) {
2462       if (size(parstr(basering))>1) {         // ring (p,a,..),...
2463        execute("ring extdguenstig=("+charstr(basering)+"),(x,y),ls;");
2464       }
2465       else {                                  // ring (p,a),...
2466        string mipl=string(minpoly);
2467        ring extdguenstig=(p,`parstr(basering)`),(x,y),ls;
2468        if (mipl!="0") { execute("minpoly="+mipl+";"); }
2469       }
2470     }
2471     else {
2472       execute("ring extdguenstig=("+charstr(basering)+"),(x,y),ls;");
2473     }
2474  }
2475  else {                               // charstr(basering)== p : no parameter
2476     ring extdguenstig=p,(x,y),ls;
2477  }
2478  export extdguenstig;
2479  map hole=altring,x,y;
2480 //----- map kann sehr zeitaufwendig sein, daher Vermeidung, wo moeglich: -----
2481  if (nvars(altring)==2) { poly f=fetch(altring,f); }
2482  else                   { poly f=hole(f);          }
2483  ideal a=hole(lastrow);
2484 }
2485 else { ideal a=lastrow; }
2486 list Newton=newtonpoly(f,1);
2487 int M1=Newton[size(Newton)-1][1];     // konstant
2488 number delt;
2489 if (Newton[size(Newton)-1][2]!=1) {
2490    " *** The transformed polynomial was not valid!!";}
2491 else {
2492 //--------------------- Fortsetzung der HNE ----------------------------------
2493  while (Q<Exaktheit) {
2494    M=ord(subst(f,y,0));
2495    Q=M-M1;
2496 //------ quasihomogene Leitform ist c*x^M1*y+d*x^(M1+Q) => delta=-d/c: -------
2497    delt=-koeff(f,M,0)/koeff(f,M1,1);
2498    a[Q]=delt;
2499    dbprint(printlevel-voice+2,"a("+string(zeile-1)+","+string(Q)+") = "+string(delt));
2500    if (Q<Exaktheit) {
2501     f=T1_Transform(f,delt,Q);
2502     if (defined(HNDebugOn)) { "transformed polynomial:",f; }
2503     if (subst(f,y,0)==0) {
2504       dbprint(printlevel-voice+2,"The HNE is finite!");
2505       a[Q+1]=x; Exaktheit=Q;
2506       f=0;                        // Speicherersparnis: f nicht mehr gebraucht
2507     }
2508    }
2509  }
2510 }
2511 //------- Wechsel in alten Ring, Zusammensetzung alte HNE + Erweiterung ------
2512 if (ringwechsel) {
2513  setring altring;
2514  map zurueck=extdguenstig,var(1),var(2);
2515  if (nvars(altring)==2) { f=fetch(extdguenstig,f); }
2516  else                   { f=zurueck(f);            }
2517  lastrow=zurueck(a);
2518 }
2519 else { lastrow=a; }
2520 if (ncols(lastrow)>ncols(m)) { spalten=ncols(lastrow); }
2521 else { spalten=ncols(m); }
2522 matrix mneu[zeile][spalten];
2523 for (i=1; i<nrows(m); i++) {
2524  mneu[i,1..ncols(m)]=m[i,1..ncols(m)];
2525 }
2526 mneu[zeile,1..ncols(lastrow)]=lastrow;
2527 if (lastrow[ncols(lastrow)]!=var(1)) {
2528  if (ncols(lastrow)==spalten) { v[zeile]=-1; }  // keine undefinierten Stellen
2529  else {
2530   v[zeile]=-Q-1;
2531   for (i=ncols(lastrow)+1; i<=spalten; i++) {
2532    mneu[zeile,i]=var(2);           // fuelle nicht def. Stellen der Matrix auf
2533 }}}
2534 else { v[zeile]=Q; }               // HNE war exakt
2535 if (ringwechsel)
2536 {
2537   kill extdguenstig;
2538 }
2539
2540 return(list(mneu,v,switch,f));
2541}
2542example
2543{
2544  "EXAMPLE:"; echo = 2;
2545  ring exring=0,(x,y),dp;
2546  list Hne=hnexpansion(x14-3y2x11-y3x10-y2x9+3y4x8+y5x7+3y4x6+x5*(-y6+y5)
2547                      -3y6x3-y7x2+y8);
2548  displayHNE(Hne);    // HNE of 1st,3rd branch is finite
2549  print(extdevelop(Hne[1],5)[1]);
2550  list ehne=extdevelop(Hne[2],5);
2551  displayHNE(ehne);
2552  param(Hne[2]);
2553  param(ehne);
2554
2555}
2556///////////////////////////////////////////////////////////////////////////////
2557
2558proc stripHNE (list l)
2559"USAGE:   stripHNE(L);  L list
2560ASSUME:  L is the output of @code{develop(f)}, or of
2561         @code{extdevelop(develop(f),n)}, or (one entry of) the list
2562         @code{hne} in the ring created by @code{hnexpansion(f[,\"ess\"])}.
2563RETURN:  list in the same format as L, but all polynomials L[4], resp.
2564         L[i][4], are set to zero.
2565NOTE:    The purpose of this procedure is to remove huge amounts of data
2566         no longer needed. It is useful, if one or more of the polynomials
2567         in L consume much memory. It is still possible to compute invariants,
2568         parametrizations etc. with the stripped HNE(s), but it is not possible
2569         to use @code{extdevelop} with them.
2570SEE ALSO: develop, hnexpansion, extdevelop
2571EXAMPLE: example stripHNE;  shows an example
2572"
2573{
2574 list h;
2575 if (typeof(l[1])=="matrix") { l[4]=poly(0); }
2576 else {
2577  for (int i=1; i<=size(l); i++) {
2578    h=l[i];
2579    h[4]=poly(0);
2580    l[i]=h;
2581  }
2582 }
2583 return(l);
2584}
2585example
2586{
2587  "EXAMPLE:"; echo = 2;
2588  ring r=0,(x,y),dp;
2589  list Hne=develop(x2+y3+y4);
2590  Hne;
2591  stripHNE(Hne);
2592}
2593///////////////////////////////////////////////////////////////////////////////
2594static proc extractHNEs(list HNEs, int transvers)
2595"USAGE:  extractHNEs(HNEs,transvers);  list HNEs (output from HN),
2596        int transvers: 1 if x,y were exchanged, 0 else
2597RETURN: list of Hamburger-Noether-Extensions in the form of hne in hnexpansion
2598NOTE:   This procedure is only for internal purpose; examples don't make sense
2599"
2600{
2601 int i,maxspalte,hspalte,hnezaehler;
2602 list HNEaktu,Ergebnis;
2603 for (hnezaehler=1; hnezaehler<=size(HNEs); hnezaehler++) {
2604  maxspalte=0;
2605  HNEaktu=HNEs[hnezaehler];
2606  if (defined(HNDebugOn)) {"To process:";HNEaktu;}
2607  if (size(HNEaktu)!=size(HNEaktu[1])+2) {
2608     "The ideals and the hqs in HNEs[",hnezaehler,"] don't match!!";
2609     HNEs[hnezaehler];
2610  }
2611 //------------ ermittle  maximale Anzahl benoetigter Spalten: ----------------
2612  for (i=2; i<size(HNEaktu); i++) {
2613    hspalte=ncols(HNEaktu[i]);
2614    maxspalte=maxspalte*(hspalte < maxspalte)+hspalte*(hspalte >= maxspalte);
2615  }
2616 //------------- schreibe Ausgabe fuer hnezaehler-ten Zweig: ------------------
2617  matrix ma[size(HNEaktu)-2][maxspalte];
2618  for (i=1; i<=(size(HNEaktu)-2); i++) {
2619    if (ncols(HNEaktu[i+1]) > 1) {
2620      ma[i,1..ncols(HNEaktu[i+1])]=HNEaktu[i+1]; }
2621    else { ma[i,1]=HNEaktu[i+1][1];}
2622  }
2623  Ergebnis[hnezaehler]=list(ma,HNEaktu[1],transvers,HNEaktu[size(HNEaktu)]);
2624  kill ma;
2625 }
2626 return(Ergebnis);
2627}
2628///////////////////////////////////////////////////////////////////////////////
2629
2630proc factorfirst(poly f, int M, int N)
2631"USAGE : factorfirst(f,M,N); f poly, M,N int
2632RETURN: number d such that f=const*(y^(N/e) - d*x^(M/e))^e, where e=gcd(M,N),
2633        0 if such a d does not exist
2634EXAMPLE: example factorfirst;  shows an example
2635"
2636{
2637 number c = koeff(f,0,N);
2638 number delt;
2639 int eps,l;
2640 int p=char(basering);
2641 string ringchar=charstr(basering);
2642
2643 if (c == 0) {"Something has gone wrong! I didn't get N correctly!"; exit;}
2644 int e = gcd(M,N);
2645
2646 if (p==0) { delt = koeff(f,M/ e,N - N/ e) / (-1*e*c); }
2647 else {
2648   if (e%p != 0) { delt = koeff(f,M/ e,N - N/ e) / (-1*e*c); }
2649   else {
2650     eps = e;
2651     for (l = 0; eps%p == 0; l=l+1) { eps=eps/ p;}
2652     if (defined(HNDebugOn)) {e," -> ",eps,"*",p,"^",l;}
2653     delt = koeff(f,(M/ e)*p^l,(N/ e)*p^l*(eps-1)) / (-1*eps*c);
2654
2655     if ((charstr(basering) != string(p)) and (delt != 0)) {
2656 //------ coefficient field is not Z/pZ => (p^l)th root is not identity -------
2657       delt=0;
2658       if (defined(HNDebugOn)) {
2659         "trivial factorization not implemented for",
2660         "parameters---I've to use 'factorize'";
2661       }
2662     }
2663   }
2664 }
2665 if (defined(HNDebugOn)) {"quasihomogeneous leading form:",f," = ",c,
2666        "* (y^"+string(N/ e),"-",delt,"* x^"+string(M/ e)+")^",e," ?";}
2667 if (f != c*(var(2)^(N/ e) - delt*var(1)^(M/ e))^e) {return(0);}
2668 else {return(delt);}
2669}
2670example
2671{ "EXAMPLE:"; echo = 2;
2672  ring exring=7,(x,y),dp;
2673  factorfirst(2*(y3-3x4)^5,20,15);
2674  factorfirst(x14+y7,14,7);
2675  factorfirst(x14+x8y3+y7,14,7);
2676}
2677
2678///////////////////////////////////////////////////////////////////////////
2679
2680proc hnexpansion(poly f,list #)
2681"USAGE:   hnexpansion(f[,\"ess\"]);   f poly
2682ASSUME:  f is a bivariate polynomial (in the first 2 ring variables)
2683RETURN:  list @code{L}, containing Hamburger-Noether data of @code{f}:
2684         If the computation of the HNE required no field extension, @code{L}
2685         is a list of lists @code{L[i]} (corresponding to the output of
2686         @code{develop}, applied to a branch of @code{f}, but the last entry
2687         being omitted):
2688@texinfo
2689@table @asis
2690@item @code{L[i][1]}; matrix:
2691         Each row contains the coefficients of the corresponding line of the
2692         Hamburger-Noether expansion (HNE) for the i-th branch. The end of
2693         the line is marked in the matrix by the first ring variable
2694         (usually x).
2695@item @code{L[i][2]}; intvec:
2696         indicating the length of lines of the HNE
2697@item @code{L[i][3]}; int:
2698         0  if the 1st ring variable was transversal (with respect to the
2699            i-th branch), @*
2700         1  if the variables were changed at the beginning of the
2701            computation, @*
2702        -1  if an error has occurred.
2703@item @code{L[i][4]}; poly:
2704         the transformed equation of the i-th branch to make it possible
2705         to extend the Hamburger-Noether data a posteriori without having
2706         to do all the previous calculation once again (0 if not needed).
2707@end table
2708@end texinfo
2709         If the computation of the HNE required a field extension, the first
2710         entry @code{L[1]} of the list is a ring, in which a list @code{hne}
2711         of lists (the HN data, as above) and a poly @code{f} (image of
2712         @code{f} over the new field) are stored.
2713         @*
2714         If called with an additional input parameter, @code{hnexpansion}
2715         computes only one representative for each class of conjugate
2716         branches (over the ground field active when calling the procedure).
2717         In this case, the returned list @code{L} always has only two
2718         entries: @code{L[1]} is either a list of lists (the HN data) or a
2719         ring (as above), and @code{L[2]} is an integer vector (the number
2720         of branches in the respective conjugacy classes).
2721
2722NOTE:    If f is known to be irreducible as a power series, @code{develop(f)}
2723         could be chosen instead to avoid a change of basering during the
2724         computations. @*
2725         Increasing  @code{printlevel} leads to more and more comments. @*
2726         Having defined a variable @code{HNDebugOn} leads to a maximum
2727         number of comments.
2728
2729SEE ALSO: develop, extdevelop, param, displayHNE
2730EXAMPLE: example hnexpansion;  shows an example
2731"
2732{
2733 int essential;
2734 if (size(#)==1) { essential=1; }
2735 int field_ext;
2736 def altring=basering;
2737
2738 //--------- Falls Ring (p^k,a),...: Wechsel in (p,a),... + minpoly -----------
2739 if ( (find(charstr(basering),string(char(basering)))!=1) &&
2740      (charstr(basering)<>"real")&&
2741      (charstr(basering)<>"complex") ) {
2742   string strmip=string(minpoly);
2743   string strf=string(f);
2744   execute("ring tempr=("+string(char(basering))+","+parstr(basering)+"),("
2745           +varstr(basering)+"),dp;");
2746   execute("minpoly="+strmip+";");
2747   execute("poly f="+strf+";");
2748   field_ext=1;
2749   def L=pre_HN(f,essential);
2750   if (size(L)==0) { return(list()); }
2751   def HNEring=L[1];
2752   setring HNEring;
2753   if ((typeof(hne[1])=="ideal")) { return(list()); }
2754   if ((voice==2) && (printlevel > -1)) {
2755     "// Attention: The parameter",par(1),"may have changed its meaning!";
2756     "// It needs no longer be a generator of the cyclic group of unities!";
2757   }
2758   dbprint(printlevel-voice+2,
2759     "// result: "+string(size(hne))+" branch(es) successfully computed.");
2760 }
2761 else {
2762   def L=pre_HN(f,essential);
2763   if (size(L)==0) { return(list()); }
2764   if (L[2]==1) { field_ext=1; }
2765   intvec hne_conj=L[3];
2766   def HNEring=L[1];
2767   setring HNEring;
2768   if ((typeof(hne[1])=="ideal")) { return(list()); }
2769   dbprint(printlevel-voice+2,
2770      "// result: "+string(size(hne))+" branch(es) successfully computed.");
2771 }
2772
2773 // ----- Lossen 10/02 : the branches have to be resorted to be able to
2774 // -----                display the multsequence in a nice way
2775 if (size(hne)>2)
2776 {
2777   int i,j,k,m;
2778   list dummy;
2779   int nbsave;
2780   int no_br = size(hne);
2781   intmat nbhd[no_br][no_br];
2782   for (i=1;i<no_br;i++)
2783   {
2784     for (j=i+1;j<=no_br;j++)
2785     {
2786       nbhd[i,j]=separateHNE(hne[i],hne[j]);
2787       k=i+1;
2788       while ( (nbhd[i,k] >= nbhd[i,j]) and (k<j) )
2789       {
2790         k++;
2791       }
2792       if (k<j)  // branches have to be resorted
2793       {
2794         dummy=hne[j];
2795         nbsave=nbhd[i,j];
2796         for (m=k; m<j; m++)
2797         {
2798           hne[m+1]=hne[m];
2799           nbhd[i,m+1]=nbhd[i,m];
2800         }
2801         hne[k]=dummy;
2802         nbhd[i,k]=nbsave;
2803       }
2804     }
2805   }
2806 }
2807 // -----
2808
2809 if (field_ext==1) {
2810   dbprint(printlevel-voice+3,"
2811// 'hnexpansion' created a list of one ring.
2812// To see the ring and the data stored in the ring, type (if you assigned
2813// the name L to the list):
2814     show(L);
2815// To display the computed HN expansion, type
2816     def HNring = L[1]; setring HNring;  displayHNE(hne); ");
2817   if (essential==1) {
2818     dbprint(printlevel-voice+3,""+
2819"// As second entry of the returned list L, you obtain an integer vector,
2820// indicating the number of conjugates for each of the computed branches.");
2821     return(list(HNEring,hne_conj));
2822   }
2823   return(list(HNEring));
2824 }
2825 else { // no change of basering necessary --> map data to original ring
2826   setring altring;
2827   if ((npars(altring)==1) and (minpoly!=0)) {
2828     ring HNhelpring=char(altring),(a,x,y),ls;
2829     list hne=imap(HNEring,hne);
2830     setring altring;
2831     map mmm=HNhelpring,par(1),var(1),var(2);
2832     list hne=mmm(hne);
2833     kill mmm,HNhelpring;
2834   }
2835   else {
2836     list hne=fetch(HNEring,hne);
2837   }
2838   kill HNEring;
2839   if (essential==1) {
2840     dbprint(printlevel-voice+3,""+
2841"// No change of ring necessary, return value is a list:
2842//   first entry  =  list :  HN expansion of essential branches.
2843//   second entry =  intvec: numbers of conjugated branches ");
2844     return(list(hne,hne_conj));
2845   }
2846   else {
2847     dbprint(printlevel-voice+3,""+
2848"// No change of ring necessary, return value is HN expansion.");
2849     return(hne);
2850   }
2851 }
2852}
2853example
2854{
2855  "EXAMPLE:"; echo = 2;
2856  ring r=0,(x,y),dp;
2857  // First, an example which requires no field extension:
2858  list Hne=hnexpansion(x4-y6);
2859  size(Hne);           // number of branches
2860  displayHNE(Hne);     // HN expansion of branches
2861  param(Hne[1]);       // parametrization of 1st branch
2862  param(Hne[2]);       // parametrization of 2nd branch
2863
2864  // An example which requires a field extension:
2865  list L=hnexpansion((x4-y6)*(y2+x4));
2866  def R=L[1]; setring R; displayHNE(hne);
2867  basering;
2868  setring r; kill R;
2869
2870  // Computing only one representative per conjugacy class:
2871  L=hnexpansion((x4-y6)*(y2+x4),"ess");
2872  def R=L[1]; setring R; displayHNE(hne);
2873  L[2];     // number of branches in respective conjugacy classes
2874}
2875
2876///////////////////////////////////////////////////////////////////////////////
2877
2878static proc pre_HN (poly f, int essential)
2879"NOTE: This procedure is only for internal use, it is called via
2880       hnexpansion
2881RETURN: list:  first entry = HNEring  (containing list hne, poly f)
2882               second entry = 0  if no change of base ring necessary
2883                              1  if change of base ring necessary
2884               third entry = numbers of conjugates ( if essential = 1 )
2885        if some error has occured, the empty list is returned
2886"
2887{
2888 def altring = basering;
2889 int p = char(basering);
2890 int field_ext;
2891 intvec hne_conj;
2892
2893 //-------------------- Tests auf Zulaessigkeit von basering ------------------
2894 if (charstr(basering)=="real") {
2895   " Singular cannot factorize over 'real' as ground field";
2896   return(list());
2897 }
2898 if (size(maxideal(1))<2) {
2899   " A univariate polynomial ring makes no sense !";
2900   return(list());
2901 }
2902 if (size(maxideal(1))>2) {
2903   dbprint(printlevel-voice+2,
2904   " Warning: all variables except the first two will be ignored!");
2905 }
2906 if (find(charstr(basering),string(char(basering)))!=1) {
2907   " ring of type (p^k,a) not implemented";
2908 //----------------------------------------------------------------------------
2909 // weder primitives Element noch factorize noch map "char p^k" -> "char -p"
2910 // [(p^k,a)->(p,a) ground field] noch fetch
2911 //----------------------------------------------------------------------------
2912   return(list());
2913 }
2914 //----------------- Definition eines neuen Ringes: HNEring -------------------
2915 string namex=varstr(1); string namey=varstr(2);
2916 if (string(char(altring))==charstr(altring)) { // kein Parameter, nicht 'real'
2917   ring HNEring = char(altring),(x,y),ls;
2918   map m=altring,x,y;
2919   poly f=m(f);
2920   export f;
2921   kill m;
2922 }
2923 else {
2924   string mipl=string(minpoly);
2925   if (mipl=="0") {
2926     "// ** WARNING: Algebraic extension of given ground field not possible!";
2927     "// ** We try to develop this polynomial, but if the need for a field";
2928     "// ** extension occurs during the calculation, we cannot proceed with";
2929     "// ** the corresponding branches.";
2930     execute("ring HNEring=("+charstr(basering)+"),(x,y),ls;");
2931   }
2932   else {
2933    string pa=parstr(altring);
2934    ring HNhelpring=p,`pa`,dp;
2935    execute("poly mipo="+mipl+";");  // Minimalpolynom in Polynom umgewandelt
2936    ring HNEring=(p,a),(x,y),ls;
2937    map getminpol=HNhelpring,a;
2938    mipl=string(getminpol(mipo));    // String umgewandelt mit 'a' als Param.
2939    execute("minpoly="+mipl+";");    // "minpoly=poly is not supported"
2940    kill HNhelpring, getminpol;
2941   }
2942   if (nvars(altring)==2) {
2943     poly f=fetch(altring,f);
2944     export f;
2945   }
2946   else {
2947     if (defined(pa)) { // Parameter hatte vorher anderen Namen als 'a'
2948       ring HNhelpring=p,(`pa`,x,y),ls;
2949       poly f=imap(altring,f);
2950       setring HNEring;
2951       map m=HNhelpring,a,x,y;
2952       poly f=m(f);
2953       kill HNhelpring;
2954     }
2955     else {
2956       map m=altring,x,y;
2957       poly f=m(f);
2958     }
2959     export f;
2960     kill m;
2961   }
2962 }
2963
2964 if (defined(HNDebugOn))
2965 {"received polynomial: ",f,", with x =",namex,", y =",namey;}
2966
2967 //----------------------- Variablendefinitionen ------------------------------
2968 int Abbruch,i,NullHNEx,NullHNEy;
2969 string str;
2970 list Newton,hne;
2971
2972 // --- changed for SINGULAR 3: ---
2973 hne=ideal(0);
2974 export hne;
2975
2976 //====================== Tests auf Zulaessigkeit des Polynoms ================
2977
2978 //-------------------------- Test, ob Einheit oder Null ----------------------
2979 if (subst(subst(f,x,0),y,0)!=0) {
2980   dbprint(printlevel+1,
2981           "The given polynomial is a unit in the power series ring!");
2982   setring altring; kill HNEring;
2983   return(list());                   // there are no HNEs
2984 }
2985 if (f==0) {
2986   dbprint(printlevel+1,"The given polynomial is zero!");
2987   setring altring; kill HNEring;
2988   return(list());                   // there are no HNEs
2989 }
2990
2991 //-----------------------  Test auf Quadratfreiheit --------------------------
2992
2993 if ((p==0) and (size(charstr(basering))==1)) {
2994
2995 //-------- Fall basering==0,... : Wechsel in Ring mit char >0 ----------------
2996 // weil squarefree eine Standardbasis berechnen muss (verwendet Syzygien)
2997 // -- wenn f in diesem Ring quadratfrei ist, dann erst recht im Ring HNEring
2998 //----------------------------------------------------------------------------
2999  int testerg=(polytest(f)==0);
3000  ring zweitring = 32003,(x,y),dp;
3001
3002  map polyhinueber=HNEring,x,y;         // fetch geht nicht
3003  poly f=polyhinueber(f);
3004  poly test_sqr=squarefree(f);
3005  if (test_sqr != f) {
3006   if (printlevel>0) {
3007     "Most probably the given polynomial is not squarefree. But the test was";
3008     "made in characteristic 32003 and not 0 to improve speed. You can";
3009     "(r) redo the test in char 0 (but this may take some time)";
3010     "(c) continue the development, if you're sure that the polynomial IS",
3011     "squarefree";
3012     if (testerg==1) {
3013       "(s) continue the development with a squarefree factor (*)";}
3014     "(q) or just quit the algorithm (default action)";
3015     "";"Please enter the letter of your choice:";
3016     str=read("")[1];             // reads one character
3017   }
3018   else { str="r"; }              // printlevel <= 0: non-interactive behaviour
3019   setring HNEring;
3020   map polyhinueber=zweitring,x,y;
3021   if (str=="r") {
3022     poly test_sqr=squarefree(f);
3023     if (test_sqr != f) {
3024      if (printlevel>0) { "The given polynomial is in fact not squarefree."; }
3025      else              { "The given polynomial is not squarefree!"; }
3026      "I'll continue with the radical.";
3027      f=test_sqr;
3028     }
3029     else {
3030      dbprint(printlevel,
3031       "everything is ok -- the polynomial is squarefree in characteristic 0");
3032     }
3033   }
3034   else {
3035     if ((str=="s") and (testerg==1)) {
3036       "(*)attention: it could be that the factor is only one in char 32003!";
3037       f=polyhinueber(test_sqr);
3038     }
3039     else {
3040       if (str<>"c") {
3041         setring altring;
3042         kill HNEring;kill zweitring;
3043         return(list());}
3044       else { "if the algorithm doesn't terminate, you were wrong...";}
3045   }}
3046   kill zweitring;
3047   if (defined(HNDebugOn)) {"I continue with the polynomial",f; }
3048  }
3049  else {
3050    setring HNEring;
3051    kill zweitring;
3052  }
3053 }
3054 //------------------ Fall Char > 0 oder Ring hat Parameter -------------------
3055 else {
3056  poly test_sqr=squarefree(f);
3057  if (test_sqr != f) {
3058   if (printlevel>0) {
3059    if (test_sqr == 1) {
3060     "The given polynomial is of the form g^"+string(p)+",";
3061     "therefore not squarefree.  You can:";
3062     " (q) quit the algorithm (recommended) or";
3063     " (f) continue with the full radical (using a factorization of the";
3064     "     pure power part; this could take much time)";
3065     "";"Please enter the letter of your choice:";
3066     str=read("")[1];
3067     if (str<>"f") { str="q"; }
3068    }
3069    else {
3070     "The given polynomial is not squarefree.";
3071     if (p != 0)
3072      {
3073       " You can:";
3074       " (c) continue with a squarefree divisor (but factors of the form g^"
3075       +string(p);
3076       "     are lost; this is recommended, takes no extra time)";
3077       " (f) continue with the full radical (using a factorization of the";
3078       "     pure power part; this could take some time)";
3079       " (q) quit the algorithm";
3080       "";"Please enter the letter of your choice:";
3081       str=read("")[1];
3082       if ((str<>"f") && (str<>"q")) { str="c"; }
3083      }
3084     else { "I'll continue with the radical."; str="c"; }
3085    }                                // endelse (test_sqr!=1)
3086   }
3087   else {
3088     "//** Error: The given polynomial is not squarefree!";
3089     "//** Since the global variable `printlevel' has the value",printlevel,
3090       "we stop here.";
3091     "//   Either call me again with a squarefree polynomial f or assign";
3092     "            printlevel=1;";
3093     "//   before calling me with a non-squarefree f.";
3094     "//   If printlevel > 0, I present some possibilities how to proceed.";
3095     str="q";
3096   }
3097   if (str=="q") {
3098    setring altring;kill HNEring;
3099    return(list());
3100   }
3101   if (str=="c") { f=test_sqr; }
3102   if (str=="f") { f=allsquarefree(f,test_sqr); }
3103  }
3104  if (defined(HNDebugOn)) {"I continue with the polynomial",f; }
3105
3106 }
3107 //====================== Ende Test auf Quadratfreiheit =======================
3108 if (subst(subst(f,x,0),y,0)!=0) {
3109   "The polynomial is a unit in the power series ring. No HNE computed.";
3110   setring altring;kill HNEring;
3111   return(list());
3112 }
3113 //---------------------- Test, ob f teilbar durch x oder y -------------------
3114 if (subst(f,y,0)==0) {
3115   f=f/y; NullHNEy=1; }             // y=0 is a solution
3116 if (subst(f,x,0)==0) {
3117   f=f/x; NullHNEx=1; }             // x=0 is a solution
3118
3119 Newton=newtonpoly(f,1);
3120 i=1; Abbruch=0;
3121 //----------------------------------------------------------------------------
3122 // finde Eckpkt. des Newtonpolys, der den Teil abgrenzt, fuer den x transvers:
3123 // Annahme: Newton ist sortiert, s.d. Newton[1]=Punkt auf der y-Achse,
3124 // Newton[letzt]=Punkt auf der x-Achse
3125 //----------------------------------------------------------------------------
3126 while ((i<size(Newton)) and (Abbruch==0)) {
3127  if ((Newton[i+1][1]-Newton[i][1])>=(Newton[i][2]-Newton[i+1][2]))
3128   {Abbruch=1;}
3129  else {i=i+1;}
3130 }
3131 int grenze1=Newton[i][2];
3132 int grenze2=Newton[i][1];
3133 //----------------------------------------------------------------------------
3134 // Stelle Ring bereit zur Uebertragung der Daten im Fall einer Koerperer-
3135 // weiterung. Definiere Objekte, die spaeter uebertragen werden.
3136 // Binde die Listen (azeilen,...) an den Ring (um sie nicht zu ueberschreiben
3137 // bei Def. in einem anderen Ring).
3138 //----------------------------------------------------------------------------
3139 ring HNE_noparam = char(altring),(a,x,y),ls;
3140 poly f;
3141 list azeilen=ideal(0);
3142 list HNEs=ideal(0);
3143 list aneu=ideal(0);
3144 list faktoren=ideal(0);
3145
3146 ideal deltais;
3147 poly delt;
3148
3149 //----- hier steht die Anzahl bisher benoetigter Ringerweiterungen drin: -----
3150 int EXTHNEnumber=0;
3151
3152 list EXTHNEring;
3153 list HNE_RingDATA;
3154 int number_of_letztring;
3155 setring HNEring;
3156 number_of_letztring=0;
3157
3158 // ================= Die eigentliche Berechnung der HNE: =====================
3159
3160 // ------- Berechne HNE von allen Zweigen, fuer die x transversal ist: -------
3161 if (defined(HNDebugOn))
3162   {"1st step: Treat Newton polygon until height",grenze1;}
3163 if (grenze1>0) {
3164  if (EXTHNEnumber>0){ EXTHNEring = EXTHNEring(1..EXTHNEnumber); }
3165  HNE_RingDATA = list(HNEring, HNE_noparam, EXTHNEnumber, EXTHNEring,
3166                      number_of_letztring);
3167
3168  list hilflist=HN(HNE_RingDATA,f,grenze1,1,essential,0,hne_conj,1);
3169  kill HNEring, HNE_noparam;
3170  if (EXTHNEnumber>0) { kill EXTHNEring(1..EXTHNEnumber);}
3171  def HNEring = hilflist[1][1];
3172  def HNE_noparam = hilflist[1][2];
3173  EXTHNEnumber = hilflist[1][3];
3174  for (i=1; i<=EXTHNEnumber; i++) { def EXTHNEring(i)=hilflist[1][4][i]; }
3175  if (hilflist[2]==0) { setring HNEring; number_of_letztring=0; }
3176  else                { setring EXTHNEring(hilflist[2]);}
3177  if (hilflist[3]==1){field_ext=1;}
3178  hne_conj=hilflist[5];
3179
3180  if (number_of_letztring != hilflist[2])
3181  {  // Ringwechsel in Prozedur HN
3182     map hole=HNE_noparam,transfproc,x,y;
3183     setring HNE_noparam;
3184     if (not(defined(f))) {poly f;}
3185     f=imap(HNEring,f);
3186     setring EXTHNEring(EXTHNEnumber);
3187     if (not(defined(f))) {poly f; f=hole(f); export f;}
3188     else                 {f=hole(f);}
3189  }
3190  number_of_letztring = hilflist[2];
3191  kill hilflist;
3192 }
3193
3194 if (NullHNEy==1) {
3195  if ((typeof(hne[1])=="ideal")) { hne=list(); }
3196  hne=hne+list(list(matrix(ideal(0,x)),intvec(1),int(0),poly(0)));
3197  if (hne_conj==0) { hne_conj=1; }
3198  else { hne_conj = hne_conj, 1; }
3199 }
3200 // --------------- Berechne HNE von allen verbliebenen Zweigen: --------------
3201 if (defined(HNDebugOn))
3202    {"2nd step: Treat Newton polygon until height",grenze2;}
3203 if (grenze2>0) {
3204
3205  if (EXTHNEnumber>0){ EXTHNEring = EXTHNEring(1..EXTHNEnumber); }
3206
3207  if (essential==1) { number_of_letztring=0; }
3208  if (number_of_letztring==0) { setring HNEring; }
3209  else                        { setring EXTHNEring(number_of_letztring); }
3210  map xytausch=basering,y,x;
3211
3212  HNE_RingDATA = list(HNEring, HNE_noparam, EXTHNEnumber, EXTHNEring,
3213                      number_of_letztring);
3214  list hilflist=HN(HNE_RingDATA,xytausch(f),grenze2,1,essential,1,hne_conj,1);
3215  kill HNEring, HNE_noparam;
3216  if (EXTHNEnumber>0){ kill EXTHNEring(1..EXTHNEnumber); }
3217  def HNEring = hilflist[1][1];
3218  def HNE_noparam = hilflist[1][2];
3219  EXTHNEnumber = hilflist[1][3];
3220  for (i=1; i<=EXTHNEnumber; i++) { def EXTHNEring(i)=hilflist[1][4][i]; }
3221  if (hilflist[2]==0) { setring HNEring; number_of_letztring=0; }
3222  else                { setring EXTHNEring(hilflist[2]);
3223                        number_of_letztring=hilflist[2]; }
3224  if (hilflist[3]==1){field_ext=1;}
3225  hne_conj=hilflist[5];
3226  kill hilflist;
3227 }
3228 if (NullHNEx==1) {
3229  if ((typeof(hne[1])=="ideal")) { hne=list(); }
3230  hne=hne+list(list(matrix(ideal(0,x)),intvec(1),int(1),poly(0)));
3231  if (hne_conj==0) { hne_conj=1; }
3232  else { hne_conj = hne_conj, 1; }
3233 }
3234
3235
3236 // --- aufraeumen ---
3237 if (defined(HNEakut)){
3238   kill HNEakut,faktoren,deltais,transformiert,teiler,leitf;
3239 }
3240 if (defined(hilflist)) {kill hilflist;}
3241 if (defined(erg)) {kill erg;}
3242 if (defined(delt)) {kill delt;}
3243 if (defined(azeilen)) { kill azeilen;}
3244 if (defined(aneu)) { kill aneu;}
3245 if (defined(transfproc)) { kill transfproc;}
3246 if (defined(transf)) { kill transf;}
3247 if (not(defined(f))) { poly f = imap(HNEring,f); export f; }
3248
3249 return(list(basering,field_ext,hne_conj));
3250}
3251
3252//////////////////////////////////////////////////////////////////////////////
3253proc essdevelop (poly f)
3254"USAGE:   essdevelop(f); f poly
3255NOTE:     command is obsolete, use hnexpansion(f,\"ess\") instead.
3256SEE ALSO: hnexpansion, develop, extdevelop, param
3257"
3258{
3259 printlevel=printlevel+1;
3260 list Ergebnis=hnexpansion(f,1);
3261 printlevel=printlevel-1;
3262 return(Ergebnis);
3263}
3264
3265///////////////////////////////////////////////////////////////////////////////
3266static proc HN (list HNE_RingDATA,poly fneu,int grenze,Aufruf_Ebene,
3267                     essential,getauscht,intvec hne_conj,int conj_factor)
3268"NOTE: This procedure is only for internal use, it is called via pre_HN
3269RETURN: list: first entry = list of HNErings,
3270              second entry = number of new base ring (0 for HNEring,
3271                                                      -1 for HNE_noparam,
3272                                                      i for EXTHNEring(i))
3273              third entry = 0 if no field extension necessary
3274                            1 if field extension necessary
3275              forth entry = HNEs (only if no change of basering)
3276"
3277{
3278 //---------- Variablendefinitionen fuer den unverzweigten Teil: --------------
3279 if (defined(HNDebugOn)) {"procedure HN",Aufruf_Ebene;}
3280 int Abbruch,ende,i,j,k,e,M,N,Q,R,zeiger,zeile,zeilevorher,dd;
3281 intvec hqs;
3282 int field_ext;
3283 int ring_changed, hneshift;
3284 intvec conjugates,conj2,conj1;
3285
3286 list EXTHNEring;
3287 def HNEring = HNE_RingDATA[1];
3288 def HNE_noparam = HNE_RingDATA[2];
3289 int EXTHNEnumber = HNE_RingDATA[3];
3290 for (i=1; i<=EXTHNEnumber; i++) { def EXTHNEring(i)=HNE_RingDATA[4][i]; }
3291 int number_of_letztring = HNE_RingDATA[5];
3292 if (defined(basering))
3293 {
3294   if (number_of_letztring==0) { kill HNEring; def HNEring=basering; }
3295   else                 { kill EXTHNEring(number_of_letztring);
3296                          def EXTHNEring(number_of_letztring)=basering; }
3297 }
3298 else
3299 {
3300   if ( number_of_letztring==0) { setring HNEring; }
3301   else                         { setring EXTHNEring(number_of_letztring); }
3302 }
3303 if (not(defined(hne))) {list hne;}
3304 poly fvorher;
3305 list erg=ideal(0); list HNEs=ideal(0); // um die Listen an den Ring zu binden
3306
3307 //-------------------- Bedeutung von Abbruch: --------------------------------
3308 //------- 0:keine Verzweigung | 1:Verzweigung,nicht fertig | 2:fertig --------
3309 //
3310 // Struktur von HNEs : Liste von Listen L (fuer jeden Zweig) der Form
3311 // L[1]=intvec (hqs), L[2],L[3],... ideal (die Zeilen (0,1,...) der HNE)
3312 // L[letztes]=poly (transformiertes f)
3313 //----------------------------------------------------------------------------
3314 list Newton;
3315 number delt;
3316 int p = char(basering);                // Ringcharakteristik
3317 list azeilen=ideal(0);
3318
3319 ideal hilfid; intvec hilfvec;
3320
3321 // ======================= der unverzweigte Teil: ============================
3322 while (Abbruch==0) {
3323  Newton=newtonpoly(fneu,1);
3324  zeiger=find_in_list(Newton,grenze);
3325  if (Newton[zeiger][2] != grenze)
3326    {"Didn't find an edge in the Newton polygon!";}
3327  if (zeiger==size(Newton)-1) {
3328    if (defined(HNDebugOn)) {"only one relevant side in Newton polygon";}
3329    M=Newton[zeiger+1][1]-Newton[zeiger][1];
3330    N=Newton[zeiger][2]-Newton[zeiger+1][2];
3331    R = M%N;
3332    Q = M / N;
3333
3334    //-------- 1. Versuch: ist der quasihomogene Leitterm reine Potenz ? ------
3335    //              (dann geht alles wie im irreduziblen Fall)
3336    //-------------------------------------------------------------------------
3337    e = gcd(M,N);
3338    delt=factorfirst(redleit(fneu,Newton[zeiger],Newton[zeiger+1])
3339                      /x^Newton[zeiger][1],M,N);
3340    if (delt==0) {
3341      if (defined(HNDebugOn)) {" The given polynomial is reducible !";}
3342      Abbruch=1;
3343    }
3344    if (Abbruch==0) {
3345      //----------- fneu,zeile retten fuer den Spezialfall (###): -------------
3346      fvorher=fneu;zeilevorher=zeile;
3347      if (R==0) {
3348        //-------- transformiere fneu mit T1, wenn kein Abbruch nachher: ------
3349        if (N>1) { fneu = T1_Transform(fneu,delt,M/ e); }
3350        else     { ende=1; }
3351        if (defined(HNDebugOn)) {"a("+string(zeile)+","+string(Q)+") =",delt;}
3352        azeilen[zeile+1][Q]=delt;
3353      }
3354      else {
3355        //------------- R > 0 : transformiere fneu mit T2 ---------------------
3356        erg=T2_Transform(fneu,delt,M,N,referencepoly(Newton));
3357        fneu=erg[1];delt=erg[2];
3358        //----- vollziehe Euklid.Alg. nach, um die HN-Matrix zu berechnen: ----
3359        while (R!=0) {
3360         if (defined(HNDebugOn)) { "h("+string(zeile)+") =",Q; }
3361         hqs[zeile+1]=Q;         // denn zeile beginnt mit dem Wert 0
3362         //--------------- markiere das Zeilenende der HNE: -------------------
3363         azeilen[zeile+1][Q+1]=x;
3364         zeile=zeile+1;
3365         //-------- Bereitstellung von Speicherplatz fuer eine neue Zeile: ----
3366         azeilen[zeile+1]=ideal(0);
3367         M=N; N=R; R=M%N; Q=M / N;
3368        }
3369        if (defined(HNDebugOn)) {"a("+string(zeile)+","+string(Q)+") =",delt;}
3370        azeilen[zeile+1][Q]=delt;
3371      }
3372      if (defined(HNDebugOn)) {"transformed polynomial: ",fneu;}
3373      grenze=e;
3374      //----------------------- teste Abbruchbedingungen: ---------------------
3375      if (subst(fneu,y,0)==0) {              // <==> y|fneu
3376        dbprint(printlevel-voice+3,"finite HNE of one branch found");
3377           // voice abzufragen macht bei rekursiven procs keinen Sinn
3378        azeilen[zeile+1][Q+1]=x;
3379        //----- Q wird nur in hqs eingetragen, wenn der Spezialfall nicht
3380        //      eintritt (siehe unten) -----
3381        Abbruch=2;
3382        if (grenze>1) {
3383         if (jet(fneu,1,intvec(0,1))==0) {
3384           //- jet(...)=alle Monome von fneu, die nicht durch y2 teilbar sind -
3385           "THE TEST FOR SQUAREFREENESS WAS BAD!!";
3386           " The polynomial was NOT squarefree!!!";}
3387         else {
3388           //----------------------- Spezialfall (###): -----------------------
3389           // Wir haben das Problem, dass die HNE eines Zweiges hier abbricht,
3390           // aber ein anderer Zweig bis hierher genau die gleiche HNE hat, die
3391           // noch weiter geht
3392           // Loesung: mache Transform. rueckgaengig und behandle fneu im
3393           // Verzweigungsteil
3394           //------------------------------------------------------------------
3395          Abbruch=1;
3396          fneu=fvorher;zeile=zeilevorher;grenze=Newton[zeiger][2];
3397        }}
3398        else {fneu=0;}     // fneu nicht mehr gebraucht - spare Speicher
3399        if (Abbruch==2) { hqs[zeile+1]=Q; }
3400      }                 // Spezialfall nicht eingetreten
3401      else {
3402        if (ende==1) {
3403          dbprint(printlevel-voice+2,"HNE of one branch found");
3404          Abbruch=2; hqs[zeile+1]=-Q-1;}
3405      }
3406    }                   // end(if Abbruch==0)
3407  }                     // end(if zeiger...)
3408  else { Abbruch=1;}
3409 }                      // end(while Abbruch==0)
3410
3411 // ===================== der Teil bei Verzweigung: ===========================
3412 if (Abbruch==1) {
3413  //---------- Variablendefinitionen fuer den verzweigten Teil: ---------------
3414  poly leitf,teiler,transformiert;
3415  list aneu=ideal(0);
3416  list faktoren;
3417  ideal deltais;
3418  list HNEakut=ideal(0);
3419  intvec eis;
3420  int zaehler,hnezaehler,zl,zl1,M1,N1,R1,Q1,needext;
3421  int numberofRingchanges,lastRingnumber,ringischanged,flag;
3422  string letztringname;
3423
3424  zeiger=find_in_list(Newton,grenze);
3425  if (defined(HNDebugOn)) {
3426    "Branching part reached---Newton polygon :",Newton;
3427    "relevant part until height",grenze,", from",Newton[zeiger],"on";
3428  }
3429  azeilen=list(hqs)+azeilen; // hat jetzt Struktur von HNEs: hqs in der 1.Zeile
3430
3431  //======= Schleife fuer jede zu betrachtende Seite des Newtonpolygons: ======
3432  for(i=zeiger; i<size(Newton); i++) {
3433   if ((essential==1) and (EXTHNEnumber>number_of_letztring)) {
3434     // ----- setze ring zurueck fuer neue Kante  -----
3435     // ---- (damit konjugierte Zweige erkennbar) -----
3436     hneshift=hneshift+hnezaehler;
3437     hnezaehler=0;
3438     ring_changed=0;
3439     def SaveRing = EXTHNEring(EXTHNEnumber);
3440     setring SaveRing;
3441     if (not(defined(HNEs))) { // HN wurde zum 2.Mal von pre_HN aufgerufen
3442       list HNEs=ideal(0);
3443     }
3444     for (k=number_of_letztring+1; k<=EXTHNEnumber; k++) { kill EXTHNEring(k);}
3445     EXTHNEnumber=number_of_letztring;
3446     if (EXTHNEnumber==0) { setring HNEring; }
3447     else                 { setring EXTHNEring(EXTHNEnumber); }
3448     if (not(defined(HNEs))) { list HNEs; }
3449     HNEs=ideal(0);
3450     deltais=0;
3451     delt=0;
3452     if (defined(zerlege)) { kill zerlege; }
3453   }
3454
3455   if (defined(HNDebugOn)) { "we consider side",Newton[i],Newton[i+1]; }
3456   M=Newton[i+1][1]-Newton[i][1];
3457   N=Newton[i][2]-Newton[i+1][2];
3458   R = M%N;
3459   Q = M / N;
3460   e=gcd(M,N);
3461   needext=1;
3462   letztringname=nameof(basering);
3463   lastRingnumber=EXTHNEnumber;
3464   faktoren=list(ideal(charPoly(redleit(fneu,Newton[i],Newton[i+1])
3465                       /(x^Newton[i][1]*y^Newton[i+1][2]),M,N)  ),
3466                 intvec(1));                  // = (zu faktoriserendes Poly, 1)
3467   conjugates=conj_factor;
3468
3469   //-- wechsle so lange in Ringerweiterungen, bis Leitform vollstaendig
3470   //   in Linearfaktoren zerfaellt -----
3471   for (numberofRingchanges=1; needext==1; numberofRingchanges++) {
3472    leitf=redleit(fneu,Newton[i],Newton[i+1])/
3473                     (x^Newton[i][1]*y^Newton[i+1][2]);
3474    delt=factorfirst(leitf,M,N);
3475    needext=0;
3476    if (delt==0) {
3477     //---------- Sonderbehandlung: faktorisiere einige Polynome ueber Q(a): --
3478     if ((charstr(basering)=="0,a") and (essential==0)) {
3479        // ====================================================
3480        // neu CL:  06.10.05
3481        poly CHPOLY=charPoly(leitf,M,N);
3482        poly tstpoly;
3483        if (defined(faktoren)!=0) {
3484          // Test, damit kein Fehler eingebaut (vermutlich nicht notwendig)
3485          tstpoly = faktoren[1][1]^faktoren[2][1];
3486          for (k=2; k<=size(faktoren[1]); k++) {
3487            tstpoly = tstpoly * faktoren[1][k]^faktoren[2][k];
3488          }
3489          tstpoly = CHPOLY-tstpoly*(CHPOLY/tstpoly);
3490          kill CHPOLY;
3491        }
3492        if ((numberofRingchanges>1) and (defined(faktoren)!=0) and (tstpoly==0)) {
3493          def L_help=factorlist(faktoren,conjugates);
3494          faktoren=L_help[1];
3495          conjugates=L_help[2];
3496          kill L_help;
3497        }
3498        else {
3499          faktoren=factorize(charPoly(leitf,M,N));
3500          conjugates=conj_factor;
3501          for (k=2;k<=size(faktoren[2]);k++) {conjugates=conjugates,conj_factor;}
3502        }
3503        kill tstpoly;
3504        // Ende neu (vorher nur else Fall)
3505        // ====================================================
3506     }
3507     else {
3508       //------------------ faktorisiere das charakt. Polynom: ----------------
3509       if ((numberofRingchanges==1) or (essential==0)) {
3510         def L_help=factorlist(faktoren,conjugates);
3511         faktoren=L_help[1];
3512         conjugates=L_help[2];
3513         kill L_help;
3514       }
3515       else {     // eliminiere alle konjugierten Nullstellen bis auf eine:
3516         ideal hilf_id;
3517         for (zaehler=1; zaehler<=size(faktoren[1]); zaehler++) {
3518           hilf_id=factorize(faktoren[1][zaehler],1);
3519           if (size(hilf_id)>1) {
3520             poly fac=hilf_id[1];
3521             dd=deg(fac);
3522             // Zur Sicherheit:
3523             if (deg(fac)==0) { fac=hilf_id[2]; }
3524             for (k=2;k<=size(hilf_id);k++) {
3525               dd=dd+deg(hilf_id[k]);
3526               if (deg(hilf_id[k])<deg(fac)) { fac=hilf_id[k]; }
3527             }
3528             faktoren[1][zaehler]=fac;
3529             kill fac;
3530             if (conjugates[zaehler]==conj_factor) {
3531               // number of conjugates not yet determined for this factor
3532               conjugates[zaehler]=conjugates[zaehler]*dd;
3533             }
3534           }
3535           else {
3536             faktoren[1][zaehler]=hilf_id[1];
3537           }
3538         }
3539       }
3540     }
3541
3542     zaehler=1; eis=0;
3543     for (j=1; j<=size(faktoren[2]); j++) {
3544      teiler=faktoren[1][j];
3545      if (teiler/y != 0) {         // sonst war's eine Konstante --> wegwerfen!
3546        if (defined(HNDebugOn)) {"factor of leading form found:",teiler;}
3547        if (teiler/y2 == 0) {      // --> Faktor hat die Form cy+d
3548          deltais[zaehler]=-subst(teiler,y,0)/koeff(teiler,0,1); //=-d/c
3549          eis[zaehler]=faktoren[2][j];
3550          conj2[zaehler]=conjugates[j];
3551          zaehler++;
3552        }
3553        else {
3554          dbprint(printlevel-voice+2,
3555             " Change of basering (field extension) necessary!");
3556          if (defined(HNDebugOn)) { teiler,"is not yet properly factorized!"; }
3557          if (needext==0) { poly zerlege=teiler; }
3558          needext=1;
3559          field_ext=1;
3560        }
3561      }
3562     }  // end(for j)
3563    }
3564    else { deltais=ideal(delt); eis=e; conj2=conj_factor; }
3565    if (defined(HNDebugOn)) {"roots of char. poly:";deltais;
3566                             "with multiplicities:",eis;}
3567    if (needext==1) {
3568      //--------------------- fuehre den Ringwechsel aus: ---------------------
3569      ringischanged=1;
3570      if ((size(parstr(basering))>0) && string(minpoly)=="0") {
3571        " ** We've had bad luck! The HNE cannot be calculated completely!";
3572        // HNE in transzendenter Erweiterung fehlgeschlagen
3573        kill zerlege;
3574        ringischanged=0; break;    // weiter mit gefundenen Faktoren
3575      }
3576      if (parstr(basering)=="") {
3577        EXTHNEnumber++;
3578        def EXTHNEring(EXTHNEnumber) = splitring(zerlege);
3579        setring EXTHNEring(EXTHNEnumber);
3580
3581        poly transf=0;
3582        poly transfproc=0;
3583        ring_changed=1;
3584        export transfproc;
3585      }
3586      else {
3587        if (numberofRingchanges>1) {  // ein Ringwechsel hat nicht gereicht
3588         def helpring = splitring(zerlege,list(transf,transfproc,faktoren));
3589         kill EXTHNEring(EXTHNEnumber);
3590         def EXTHNEring(EXTHNEnumber)=helpring;
3591         setring EXTHNEring(EXTHNEnumber);
3592         kill helpring;
3593
3594         poly transf=erg[1];
3595         poly transfproc=erg[2];
3596         ring_changed=1;
3597         list faktoren=erg[3];
3598         export transfproc;
3599         kill erg;
3600        }
3601        else {
3602         if (ring_changed==1) { // in dieser proc geschah schon Ringwechsel
3603          EXTHNEnumber++;
3604          def EXTHNEring(EXTHNEnumber) = splitring(zerlege,list(a,transfproc));
3605          setring EXTHNEring(EXTHNEnumber);
3606          poly transf=erg[1];
3607          poly transfproc=erg[2];
3608          export transfproc;
3609          kill erg;
3610         }
3611         else { // parameter war vorher da
3612          EXTHNEnumber++;
3613          def EXTHNEring(EXTHNEnumber) = splitring(zerlege,a);
3614          setring EXTHNEring(EXTHNEnumber);
3615          poly transf=erg[1];
3616          poly transfproc=transf;
3617          ring_changed=1;
3618          export transfproc;
3619          kill erg;
3620        }}
3621      }
3622      //-----------------------------------------------------------------------
3623      // transf enthaelt jetzt den alten Parameter des Ringes, der aktiv war
3624      // vor Beginn der Schleife (evtl. also ueber mehrere Ringwechsel
3625      // weitergereicht),
3626      // transfproc enthaelt den alten Parameter des Ringes, der aktiv war zu
3627      // Beginn der Prozedur, und der an die aufrufende Prozedur zurueckgegeben
3628      // werden muss
3629      // transf ist Null, falls der alte Ring keinen Parameter hatte,
3630      // das gleiche gilt fuer transfproc
3631      //-----------------------------------------------------------------------
3632
3633      //---- Neudef. von Variablen, Uebertragung bisher errechneter Daten: ----
3634      poly leitf,teiler,transformiert;
3635      list aneu=ideal(0);
3636      ideal deltais;
3637      number delt;
3638      setring HNE_noparam;
3639      if (defined(letztring)) { kill letztring; }
3640      if (EXTHNEnumber>1) { def letztring=EXTHNEring(EXTHNEnumber-1); }
3641      else                { def letztring=HNEring; }
3642      if (not defined(fneu)) {poly fneu;}
3643      fneu=imap(letztring,fneu);
3644      if (not defined(f)) {poly f;}
3645      f=imap(letztring,f);
3646      if (not defined(hne)) {list hne;}
3647      hne=imap(letztring,hne);
3648
3649      if (not defined(faktoren)) {list faktoren; }
3650      faktoren=imap(letztring,faktoren);
3651
3652      if (not(defined(azeilen))){list azeilen,HNEs;}
3653      azeilen=imap(letztring,azeilen);
3654      HNEs=imap(letztring,HNEs);
3655
3656      setring EXTHNEring(EXTHNEnumber);
3657      if (not(defined(hole))) { map hole; }
3658      hole=HNE_noparam,transf,x,y;
3659      poly fneu=hole(fneu);
3660      if (not defined(faktoren)) {
3661        list faktoren;
3662        faktoren=hole(faktoren);
3663      }
3664      if (not(defined(f)))
3665      {
3666        poly f=hole(f);
3667        list hne=hole(hne);
3668        export f,hne;
3669      }
3670    }
3671   }    // end (while needext==1) bzw. for (numberofRingchanges)
3672
3673   if (eis==0) { i++; continue; }
3674   if (ringischanged==1) {
3675    list erg,HNEakut;            // dienen nur zum Sp. von Zwi.erg.
3676
3677    ideal hilfid;
3678    erg=ideal(0); HNEakut=erg;
3679
3680    hole=HNE_noparam,transf,x,y;
3681    setring HNE_noparam;
3682    if (not(defined(azeilen))){list azeilen,HNEs;}
3683    azeilen=imap(letztring,azeilen);
3684    HNEs=imap(letztring,HNEs);
3685
3686    setring EXTHNEring(EXTHNEnumber);
3687    list azeilen=hole(azeilen);
3688    list HNEs=hole(HNEs);
3689    kill letztring;
3690    ringischanged=0;
3691   }
3692
3693   //============ Schleife fuer jeden gefundenen Faktor der Leitform: =========
3694   for (j=1; j<=size(eis); j++) {
3695     //---- Mache Transformation T1 oder T2, trage Daten in HNEs ein,
3696     //     falls HNE abbricht: ----
3697
3698    //------------------------ Fall R==0: -------------------------------------
3699    if (R==0) {
3700      transformiert = T1_Transform(fneu,number(deltais[j]),M/ e);
3701      if (defined(HNDebugOn)) {
3702        "a("+string(zeile)+","+string(Q)+") =",deltais[j];
3703        "transformed polynomial: ",transformiert;
3704      }
3705      if (subst(transformiert,y,0)==0) {
3706       dbprint(printlevel-voice+3,"finite HNE found");
3707       hnezaehler++;
3708       //-------- trage deltais[j],x ein in letzte Zeile, fertig: -------------
3709       HNEakut=azeilen+list(poly(0));        // =HNEs[hnezaehler];
3710       hilfid=HNEakut[zeile+2]; hilfid[Q]=deltais[j]; hilfid[Q+1]=x;
3711       HNEakut[zeile+2]=hilfid;
3712       HNEakut[1][zeile+1]=Q;                // aktualisiere Vektor mit den hqs
3713       HNEs[hnezaehler]=HNEakut;
3714       conj1[hneshift+hnezaehler]=conj2[j];
3715       if (eis[j]>1) {
3716        transformiert=transformiert/y;
3717        if (subst(transformiert,y,0)==0){"THE TEST FOR SQUAREFREENESS WAS BAD!"
3718                                  +"! The polynomial was NOT squarefree!!!";}
3719        else {
3720          //--- Spezialfall (###) eingetreten: Noch weitere Zweige vorhanden --
3721          eis[j]=eis[j]-1;
3722        }
3723       }
3724      }
3725    }
3726    else {
3727      //------------------------ Fall R <> 0: ---------------------------------
3728      erg=T2_Transform(fneu,number(deltais[j]),M,N,referencepoly(Newton));
3729      transformiert=erg[1];delt=erg[2];
3730      if (defined(HNDebugOn)) {"transformed polynomial: ",transformiert;}
3731      if (subst(transformiert,y,0)==0) {
3732       dbprint(printlevel-voice+3,"finite HNE found");
3733       hnezaehler++;
3734       //---------------- trage endliche HNE in HNEs ein: ---------------------
3735       HNEakut=azeilen;           // dupliziere den gemeins. Anfang der HNE's
3736       zl=2;                      // (kommt schliesslich nach HNEs[hnezaehler])
3737       //----------------------------------------------------------------------
3738       // Werte von:  zeile: aktuelle Zeilennummer der HNE (gemeinsamer Teil)
3739       //             zl : die HNE spaltet auf; zeile+zl ist der Index fuer die
3740       //                  Zeile des aktuellen Zweigs; (zeile+zl-2) ist die
3741       //                  tatsaechl. Zeilennr. (bei 0 angefangen) der HNE
3742       //                  ([1] <- intvec(hqs), [2] <- 0. Zeile usw.)
3743       //----------------------------------------------------------------------
3744
3745       //----- vollziehe Euklid.Alg. nach, um die HN-Matrix zu berechnen: -----
3746       M1=M;N1=N;R1=R;Q1=M1/ N1;
3747       while (R1!=0) {
3748        if (defined(HNDebugOn)) { "h("+string(zeile+zl-2)+") =",Q1; }
3749        HNEakut[1][zeile+zl-1]=Q1;
3750        HNEakut[zeile+zl][Q1+1]=x;
3751                                  // markiere das Zeilenende der HNE
3752        zl=zl+1;
3753        //----- Bereitstellung von Speicherplatz fuer eine neue Zeile: --------
3754        HNEakut[zeile+zl]=ideal(0);
3755
3756        M1=N1; N1=R1; R1=M1%N1; Q1=M1 / N1;
3757       }
3758       if (defined(HNDebugOn)) {
3759         "a("+string(zeile+zl-2)+","+string(Q1)+") =",delt;
3760       }
3761       HNEakut[zeile+zl][Q1]  =delt;
3762       HNEakut[zeile+zl][Q1+1]=x;
3763       HNEakut[1][zeile+zl-1] =Q1;     // aktualisiere Vektor mit hqs
3764       HNEakut[zeile+zl+1]=poly(0);
3765       HNEs[hnezaehler]=HNEakut;
3766       conj1[hneshift+hnezaehler]=conj2[j];
3767
3768       //-------------------- Ende der Eintragungen in HNEs -------------------
3769
3770       if (eis[j]>1) {
3771        transformiert=transformiert/y;
3772        if (subst(transformiert,y,0)==0){"THE TEST FOR SQUAREFREENESS WAS BAD!"
3773                               +" The polynomial was NOT squarefree!!!";}
3774         else {
3775          //--- Spezialfall (###) eingetreten: Noch weitere Zweige vorhanden --
3776          eis[j]=eis[j]-1;
3777       }}
3778      }                           // endif (subst()==0)
3779    }                             // endelse (R<>0)
3780
3781    //========== Falls HNE nicht abbricht: Rekursiver Aufruf von HN: ==========
3782    //------------------- Berechne HNE von transformiert ----------------------
3783    if (subst(transformiert,y,0)!=0) {
3784     lastRingnumber=EXTHNEnumber;
3785
3786     if (EXTHNEnumber>0){ EXTHNEring = EXTHNEring(1..EXTHNEnumber); }
3787     HNE_RingDATA = list( HNEring, HNE_noparam, EXTHNEnumber, EXTHNEring,
3788                          lastRingnumber);
3789     if (defined(HNerg)) {kill HNerg;}
3790     list HNerg=HN(HNE_RingDATA,transformiert,eis[j],Aufruf_Ebene+1,
3791                                essential,getauscht,hne_conj,conj2[j]);
3792     HNE_RingDATA = HNerg[1];
3793     if (conj1==0) { conj1=HNerg[5]; }
3794     else  { conj1 = conj1,HNerg[5]; }
3795
3796     if (HNerg[3]==1) { field_ext=1; }
3797     if (HNerg[2]==lastRingnumber) { // kein Ringwechsel in HN aufgetreten
3798       if (not(defined(aneu))) { list aneu; }
3799       aneu = HNerg[4];
3800     }
3801     else { // Ringwechsel aufgetreten
3802       if (defined(HNDebugOn))
3803          {" ring change in HN(",Aufruf_Ebene+1,") detected";}
3804       EXTHNEnumber = HNerg[1][3];
3805       for (i=lastRingnumber+1; i<=EXTHNEnumber; i++) {
3806         def EXTHNEring(i)=HNerg[1][4][i];
3807       }
3808       if (HNerg[2]==0) { setring HNEring; }
3809       else             { setring EXTHNEring(HNerg[2]); }
3810       def tempRing=HNerg[4];
3811       def aneu=imap(tempRing,HNEs);
3812       kill tempRing;
3813
3814       //--- stelle lokale Variablen im neuen Ring wieder her, und rette
3815       //    gegebenenfalls ihren Inhalt: ----
3816       list erg,faktoren,HNEakut;
3817       ideal hilfid;
3818       erg=ideal(0); faktoren=erg; HNEakut=erg;
3819       poly leitf,teiler,transformiert;
3820       map hole=HNE_noparam,transfproc,x,y;
3821       setring HNE_noparam;
3822       if (lastRingnumber>0) { def letztring=EXTHNEring(lastRingnumber); }
3823       else                  { def letztring=HNEring; }
3824       if (not defined(HNEs)) {list HNEs;}
3825       HNEs=imap(letztring,HNEs);
3826       if (not defined(azeilen)) {list azeilen;}
3827       azeilen=imap(letztring,azeilen);
3828       if (not defined(deltais)) {ideal deltais;}
3829       deltais=imap(letztring,deltais);
3830       if (not defined(delt)) {poly delt;}
3831       delt=imap(letztring,delt);
3832       if (not defined(fneu)) {poly fneu;}
3833       fneu=imap(letztring,fneu);
3834       if (not defined(f)) {poly f;}
3835       f=imap(letztring,f);
3836       if (not defined(hne)) {list hne;}
3837       hne=imap(letztring,hne);
3838
3839       setring EXTHNEring(EXTHNEnumber);
3840       list HNEs=hole(HNEs);
3841       list azeilen=hole(azeilen);
3842       ideal deltais=hole(deltais);
3843       number delt=number(hole(delt));
3844       poly fneu=hole(fneu);
3845       if (not(defined(f)))
3846       {
3847         poly f=hole(f);
3848         list hne=hole(hne);
3849         export f,hne;
3850       }
3851     }
3852
3853     //========== Verknuepfe bisherige HNE mit von HN gelieferten HNEs: ======
3854     if (R==0) {
3855       HNEs,hnezaehler=constructHNEs(HNEs,hnezaehler,aneu,azeilen,zeile,
3856                       deltais,Q,j);
3857       kill aneu;
3858     }
3859     else {
3860      for (zaehler=1; zaehler<=size(aneu); zaehler++) {
3861       hnezaehler++;
3862       HNEakut=azeilen;          // dupliziere den gemeinsamen Anfang der HNE's
3863       zl=2;                     // (kommt schliesslich nach HNEs[hnezaehler])
3864       //------------ Trage Beitrag dieser Transformation T2 ein: -------------
3865       //------ Zur Bedeutung von zeile, zl: siehe Kommentar weiter oben ------
3866
3867       //----- vollziehe Euklid.Alg. nach, um die HN-Matrix zu berechnen: -----
3868       M1=M;N1=N;R1=R;Q1=M1/ N1;
3869       while (R1!=0) {
3870        if (defined(HNDebugOn)) { "h("+string(zeile+zl-2)+") =",Q1; }
3871        HNEakut[1][zeile+zl-1]=Q1;
3872        HNEakut[zeile+zl][Q1+1]=x;    // Markierung des Zeilenendes der HNE
3873        zl=zl+1;
3874        //----- Bereitstellung von Speicherplatz fuer eine neue Zeile: --------
3875        HNEakut[zeile+zl]=ideal(0);
3876        M1=N1; N1=R1; R1=M1%N1; Q1=M1 / N1;
3877       }
3878       if (defined(HNDebugOn)) {
3879         "a("+string(zeile+zl-2)+","+string(Q1)+") =",delt;
3880       }
3881       HNEakut[zeile+zl][Q1]=delt;
3882
3883       //-- Daten aus T2_Transform sind eingetragen; haenge Daten von HN an: --
3884       hilfid=HNEakut[zeile+zl];
3885       for (zl1=Q1+1; zl1<=ncols(aneu[zaehler][2]); zl1++) {
3886        hilfid[zl1]=aneu[zaehler][2][zl1];
3887       }
3888       HNEakut[zeile+zl]=hilfid;
3889       // ------ vorher HNEs[.][zeile+zl]<-aneu[.][2],
3890       //        jetzt [zeile+zl+1] <- [3] usw.: --------
3891       for (zl1=3; zl1<=size(aneu[zaehler]); zl1++) {
3892         HNEakut[zeile+zl+zl1-2]=aneu[zaehler][zl1];
3893       }
3894       //--- setze hqs zusammen: HNEs[hnezaehler][1]=HNEs[..][1],aneu[..][1] --
3895       hilfvec=HNEakut[1],aneu[zaehler][1];
3896       HNEakut[1]=hilfvec;
3897       //-------- weil nicht geht: liste[1]=liste[1],aneu[zaehler][1] ---------
3898       HNEs[hnezaehler]=HNEakut;
3899      }                     // end(for zaehler)
3900     kill aneu;
3901     }                      // endelse (R<>0)
3902    }                       // endif (subst()!=0)  (weiteres Aufblasen mit HN)
3903
3904   }                        // end(for j) (Behandlung der einzelnen delta_i)
3905
3906
3907   // ------------------------- new for essdevelop ----------------------------
3908   if ((essential==1) and (defined(SaveRing))) {
3909     // ----- uebertrage Daten in gemeinsame Koerpererweiterung ---------------
3910     if (EXTHNEnumber>number_of_letztring) {
3911       // ----- fuer aktuelle Kante war Koerpererweiterung erforderlich -------
3912       EXTHNEnumber++;
3913       if (not(defined(minPol))) { poly miniPol; }
3914       miniPol=minpoly;
3915       setring SaveRing;
3916       if (not(defined(miniPol))) { poly miniPol; }
3917       miniPol=minpoly;
3918       setring HNE_noparam;
3919       if (not(defined(a_x))){ map a_x,a_y; poly mp_save, mp_new; }
3920       mp_save=imap(SaveRing,miniPol);
3921       mp_new=imap(EXTHNEring(EXTHNEnumber-1),miniPol);
3922       if (mp_save==mp_new) { // Sonderfall: wieder gleicher Ring
3923         def EXTHNEring(EXTHNEnumber)=SaveRing;
3924         setring EXTHNEring(EXTHNEnumber);
3925         if (not(defined(f))) {poly f; f=hole(f); export f;}
3926         list dummyL=imap(EXTHNEring(EXTHNEnumber-1),HNEs);
3927         if (not(defined(HNEs))) { def HNEs=list(); }
3928         if ((size(HNEs)==1) and (typeof(HNEs[1])=="ideal")) {HNEs=list();}
3929         HNEs[size(HNEs)+1..size(HNEs)+size(dummyL)]=dummyL[1..size(dummyL)];
3930         kill dummyL,SaveRing;
3931       }
3932       else { // verschiedene Ringe
3933         a_x=HNE_noparam,x,0,0;
3934         a_y=HNE_noparam,y,0,0;
3935         mp_save=a_x(mp_save); // minpoly aus SaveRing mit a --> x
3936         mp_new=a_y(mp_new);   // minpoly aus SaveRing mit a --> y
3937         setring SaveRing;
3938         poly mp_new=imap(HNE_noparam,mp_new);
3939         list Lfac=factorize(mp_new,1);
3940         poly fac=Lfac[1][1];
3941         for (k=2;k<=size(Lfac[1]);k++) {
3942           if (deg(Lfac[1][k])<deg(fac)) { fac=Lfac[1][k]; }
3943         }
3944
3945         if (deg(fac)==1) { // keine Erweiterung noetig
3946           def EXTHNEring(EXTHNEnumber)=SaveRing;
3947           setring HNE_noparam;
3948           HNEs=imap(EXTHNEring(EXTHNEnumber-1),HNEs);
3949           setring EXTHNEring(EXTHNEnumber);
3950           if (not(defined(f))) {poly f; f=hole(f); export f;}
3951           map phi=HNE_noparam,-subst(fac,y,0)/koeff(fac,0,1),x,y;
3952           list dummyL=phi(HNEs);
3953           if (not(defined(HNEs))) { def HNEs=list(); }
3954           if ((size(HNEs)==1) and (typeof(HNEs[1])=="ideal")) {HNEs=list();}
3955           HNEs[size(HNEs)+1..size(HNEs)+size(dummyL)]=dummyL[1..size(dummyL)];
3956           kill dummyL,phi,SaveRing;
3957         }
3958         else { // Koerpererweiterung noetig
3959           def EXTHNEring(EXTHNEnumber) = splitring(fac,list(a,transfproc));
3960           setring EXTHNEring(EXTHNEnumber);
3961           poly transf=erg[1];  // image of parameter from SaveRing
3962           poly transfproc=erg[2];
3963           poly transb=erg[3];  // image of parameter from EXTHNEring(..)
3964           export transfproc;
3965           kill erg;
3966           setring HNE_noparam;
3967           if (not(defined(HNEs1))) { list HNEs1=ideal(0); }
3968           HNEs1=imap(EXTHNEring(EXTHNEnumber-1),HNEs);
3969           if (not(defined(hne))) { list hne=ideal(0); }
3970           hne=imap(SaveRing,hne);
3971           HNEs=imap(SaveRing,HNEs);
3972           setring EXTHNEring(EXTHNEnumber);
3973           map hole=HNE_noparam,transf,x,y;
3974           poly fneu=hole(fneu);
3975           poly f=hole(f);
3976           map phi=HNE_noparam,transb,x,y;
3977           list HNEs=hole(HNEs);
3978           list hne=hole(hne);
3979           export f,hne;
3980           if ((size(HNEs)==1) and (typeof(HNEs[1])=="ideal")) {HNEs=list();}
3981           list dummyL=phi(HNEs1);
3982           HNEs[size(HNEs)+1..size(HNEs)+size(dummyL)]=dummyL[1..size(dummyL)];
3983           kill dummyL,phi,SaveRing;
3984         }
3985       }
3986     }
3987     else { // nur bei letzter Kante muss was getan werden
3988       if (i==size(Newton)-1) {
3989         EXTHNEnumber++;
3990         if (number_of_letztring==0) { def letztring=HNEring; }
3991         else       { def letztring=EXTHNEring(EXTHNEnumber); }
3992         if (minpoly==0) {
3993           def EXTHNEring(EXTHNEnumber)=SaveRing;
3994           setring EXTHNEring(EXTHNEnumber);
3995           if (not(defined(f))) {poly f; f=hole(f); export f;}
3996           if ((size(HNEs)==1) and (typeof(HNEs[1])=="ideal")) {HNEs=list();}
3997           list dummyL=imap(letztring,HNEs);
3998           HNEs[size(HNEs)+1..size(HNEs)+size(dummyL)]=dummyL[1..size(dummyL)];
3999           kill dummyL,letztring,SaveRing;
4000         }
4001         else { // muessen Daten nach SaveRing uebertragen;
4002           setring HNE_noparam;
4003           if (not(defined(HNEs))) { list HNEs; }
4004           HNEs=imap(letztring,HNEs);
4005           def EXTHNEring(EXTHNEnumber)=SaveRing;
4006           setring EXTHNEring(EXTHNEnumber);
4007           if (not(defined(hole))) { map hole; }
4008           hole=HNE_noparam,transfproc,x,y;
4009           list dummyL=hole(HNEs);
4010           if (not(defined(HNEs))) { def HNEs=list(); }
4011           if ((size(HNEs)==1) and (typeof(HNEs[1])=="ideal")) {HNEs=list();}
4012           HNEs[size(HNEs)+1..size(HNEs)+size(dummyL)]=dummyL[1..size(dummyL)];
4013           kill dummyL, letztring,SaveRing;
4014         }
4015       }
4016     }
4017   }
4018   // -----------------end of new part (loop for essential=1) ----------------
4019  } // end (Loop uber Kanten)
4020  if (defined(SaveRing)) { kill SaveRing; }
4021 }
4022 else {
4023  HNEs[1]=list(hqs)+azeilen+list(fneu); // fneu ist transform. Poly oder Null
4024  conj1[1]=conj_factor;
4025 }
4026
4027 if (Aufruf_Ebene == 1)
4028 {
4029   if ((number_of_letztring!=EXTHNEnumber) and (not(defined(hne))))
4030   {
4031     //----- falls Zweige in transz. Erw. berechnet werden konnten ---------
4032     if (defined(transfproc))
4033     { // --- Ringwechsel hat stattgefunden ---
4034       if (defined(HNDebugOn)) {" ring change in HN(",1,") detected";}
4035       if (not(defined(hole))) { map hole; }
4036       hole=HNE_noparam,transfproc,x,y;
4037       setring HNE_noparam;
4038       f=imap(HNEring,f);
4039       if (number_of_letztring==0) { def letztring=HNEring; }
4040       else                        { def letztring=EXTHNEring(EXTHNEnumber); }
4041       if (not(defined(hne))) { list hne; }
4042       hne=imap(letztring,hne);
4043       setring EXTHNEring(EXTHNEnumber);
4044       if (not(defined(f))) { poly f=hole(f); export f; }
4045       list hne=hole(hne);
4046       export hne;
4047     }
4048   }
4049   if (size(HNEs)>0) {
4050     if ((size(HNEs)>1) or (typeof(HNEs[1])!="ideal") or (size(HNEs[1])>0)) {
4051       if ((typeof(hne[1])=="ideal")) { hne=list(); }
4052       hne=hne+extractHNEs(HNEs,getauscht);
4053       if (hne_conj==0) { hne_conj=conj1; }
4054       else { hne_conj = hne_conj, conj1; }
4055     }
4056   }
4057 }
4058 else
4059 { // HN wurde rekursiv aufgerufen
4060   if (number_of_letztring!=EXTHNEnumber)
4061   { // Ringwechsel hatte stattgefunden
4062     string mipl_alt = string(minpoly);
4063     execute("ring tempRing = ("+charstr(basering)+"),("+varstr(basering)+
4064                              "),("+ordstr(basering)+");");
4065     execute("minpoly="+ mipl_alt +";");
4066     list HNEs=imap(EXTHNEring(EXTHNEnumber),HNEs);
4067     export HNEs;
4068     if (defined(HNDebugOn)) {" ! tempRing defined ! ";}
4069   }
4070   if (conj1!=0) { hne_conj=conj1; }
4071   else          { hne_conj=conj_factor; }
4072 }
4073 if (EXTHNEnumber>0){ EXTHNEring = EXTHNEring(1..EXTHNEnumber); }
4074 HNE_RingDATA = list(HNEring, HNE_noparam, EXTHNEnumber, EXTHNEring);
4075 if (number_of_letztring==EXTHNEnumber) {
4076   return(list(HNE_RingDATA,EXTHNEnumber,field_ext,HNEs,hne_conj));
4077 }
4078 else {
4079   if (defined(tempRing)) {
4080     return(list(HNE_RingDATA,EXTHNEnumber,field_ext,tempRing,hne_conj));
4081   }
4082   return(list(HNE_RingDATA,EXTHNEnumber,field_ext,0,hne_conj));
4083 }
4084}
4085
4086///////////////////////////////////////////////////////////////////////////////
4087
4088static proc constructHNEs (list HNEs,int hnezaehler,list aneu,list azeilen,
4089                    int zeile,ideal deltais,int Q,int j)
4090"NOTE: This procedure is only for internal use, it is called via HN"
4091{
4092  int zaehler,zl;
4093  ideal hilfid;
4094  list hilflist;
4095  intvec hilfvec;
4096  for (zaehler=1; zaehler<=size(aneu); zaehler++) {
4097     hnezaehler++;
4098     // HNEs[hnezaehler]=azeilen;            // dupliziere gemeins. Anfang
4099 //----------------------- trage neu berechnete Daten ein ---------------------
4100     hilfid=azeilen[zeile+2];
4101     hilfid[Q]=deltais[j];
4102     for (zl=Q+1; zl<=ncols(aneu[zaehler][2]); zl++) {
4103      hilfid[zl]=aneu[zaehler][2][zl];
4104     }
4105     hilflist=azeilen; hilflist[zeile+2]=hilfid;
4106 //------------------ haenge uebrige Zeilen von aneu[] an: --------------------
4107     for (zl=3; zl<=size(aneu[zaehler]); zl++) {
4108      hilflist[zeile+zl]=aneu[zaehler][zl];
4109     }
4110 //--- setze die hqs zusammen: HNEs[hnezaehler][1]=HNEs[..][1],aneu[..][1] ----
4111     if (hilflist[1]==0) { hilflist[1]=aneu[zaehler][1]; }
4112     else { hilfvec=hilflist[1],aneu[zaehler][1]; hilflist[1]=hilfvec; }
4113     HNEs[hnezaehler]=hilflist;
4114  }
4115  return(HNEs,hnezaehler);
4116}
4117///////////////////////////////////////////////////////////////////////////////
4118
4119proc referencepoly (list newton)
4120"USAGE:   referencepoly(newton);
4121         newton is list of intvec(x,y) which represents points in the Newton
4122         diagram (e.g. output of the proc newtonpoly)
4123RETURN:  a polynomial which has newton as Newton diagram
4124SEE ALSO: newtonpoly
4125EXAMPLE: example referencepoly;  shows an example
4126"
4127{
4128 poly f;
4129 for (int i=1; i<=size(newton); i++) {
4130   f=f+var(1)^newton[i][1]*var(2)^newton[i][2];
4131 }
4132 return(f);
4133}
4134example
4135{ "EXAMPLE:"; echo = 2;
4136 ring exring=0,(x,y),ds;
4137 referencepoly(list(intvec(0,4),intvec(2,3),intvec(5,1),intvec(7,0)));
4138}
4139///////////////////////////////////////////////////////////////////////////////
4140
4141proc factorlist (list L, list #)
4142"USAGE:   factorlist(L);   L a list in the format of `factorize'
4143RETURN:  the nonconstant irreducible factors of
4144         L[1][1]^L[2][1] * L[1][2]^L[2][2] *...* L[1][size(L[1])]^...
4145         with multiplicities (same format as factorize)
4146SEE ALSO: factorize
4147EXAMPLE: example factorlist;  shows an example
4148"
4149{
4150 int k;
4151 if ((size(#)>=1) and ((typeof(#[1])=="intvec") or (typeof(#[1])=="int"))) {
4152   int with_conj = 1; intvec C = #[1];
4153 }
4154 else {
4155   int with_conj = 0; intvec C = L[2];
4156 }
4157 // eine Sortierung der Faktoren eruebrigt sich, weil keine zwei versch.
4158 // red.Fakt. einen gleichen irred. Fakt. haben koennen (I.3.27 Diplarb.)
4159 int i,gross;
4160 list faktoren,hilf;
4161 intvec conjugates;
4162 ideal hil1,hil2;
4163 intvec v,w,hilf_conj;
4164 for (i=1; (L[1][i] == jet(L[1][i],0)) && (i<size(L[1])); i++) {;}
4165 if (L[1][i] != jet(L[1][i],0)) {
4166   hilf=factorize(L[1][i]);
4167 // Achtung!!! factorize(..,2) wirft entgegen der Beschreibung nicht nur
4168 // konstante Faktoren raus, sondern alle Einheiten in der LOKALISIERUNG nach
4169 // der Monomordnung!!! Im Beispiel unten verschwindet der Faktor x+y+1, wenn
4170 // man ds statt dp als Ordnung nimmt!
4171   hilf_conj=C[i];
4172   for (k=2;k<=size(hilf[2]);k++){ hilf_conj=hilf_conj,C[i]; }
4173   hilf[2]=hilf[2]*L[2][i];
4174   hil1=hilf[1];
4175   gross=size(hil1);
4176   if (gross>1) {
4177     v=hilf[2];
4178     faktoren=list(ideal(hil1[2..gross]),intvec(v[2..gross]));
4179     conjugates=intvec(hilf_conj[2..gross]);
4180   }
4181   else         { faktoren=hilf; conjugates=hilf_conj; }
4182 }
4183 else {
4184   faktoren=L;
4185   conjugates=C;
4186 }
4187
4188 for (i++; i<=size(L[2]); i++) {
4189 //------------------------- linearer Term -- irreduzibel ---------------------
4190   if (L[1][i] == jet(L[1][i],1)) {
4191     if (L[1][i] != jet(L[1][i],0)) {           // konst. Faktoren eliminieren
4192       hil1=faktoren[1];
4193       hil1[size(hil1)+1]=L[1][i];
4194       faktoren[1]=hil1;
4195       v=faktoren[2],L[2][i];
4196       conjugates=conjugates,C[i];
4197       faktoren[2]=v;
4198     }
4199   }
4200 //----------------------- nichtlinearer Term -- faktorisiere -----------------
4201   else {
4202     hilf=factorize(L[1][i]);
4203     hilf_conj=C[i];
4204     for (k=2;k<=size(hilf[2]);k++){ hilf_conj=hilf_conj,C[i]; }
4205     hilf[2]=hilf[2]*L[2][i];
4206     hil1=faktoren[1];
4207     hil2=hilf[1];
4208     gross=size(hil2);
4209       // hil2[1] ist konstant, wird weggelassen:
4210     hil1[(size(hil1)+1)..(size(hil1)+gross-1)]=hil2[2..gross];
4211       // ideal+ideal does not work due to simplification;
4212       // ideal,ideal not allowed
4213     faktoren[1]=hil1;
4214     w=hilf[2];
4215     v=faktoren[2],w[2..gross];
4216     conjugates=conjugates,hilf_conj[2..gross];
4217     faktoren[2]=v;
4218   }
4219 }
4220 if (with_conj==0) { return(faktoren); }
4221 else { return(list(faktoren,conjugates)); }  // for essential development
4222}
4223example
4224{ "EXAMPLE:"; echo = 2;
4225 ring exring=0,(x,y),ds;
4226 list L=list(ideal(x,(x-y)^2*(x+y+1),x+y),intvec(2,2,1));
4227 L;
4228 factorlist(L);
4229}
4230
4231///////////////////////////////////////////////////////////////////////////////
4232
4233proc delta
4234"USAGE:  delta(INPUT);  INPUT a polynomial defining an isolated plane curve
4235         singularity at 0, or the Hamburger-Noether expansion thereof, i.e.
4236         the output of @code{develop(f)}, or the output of @code{hnexpansion(f)},
4237         or the list of HN data computed by @code{hnexpansion(f)}.
4238RETURN:  int, the delta invariant of the singularity at 0, that is, the vector
4239         space dimension of R~/R, (R~ the normalization of the local ring of
4240         the singularity).
4241NOTE:    In case the Hamburger-Noether expansion of the curve f is needed
4242         for other purposes as well it is better to calculate this first
4243         with the aid of @code{hnexpansion} and use it as input instead of
4244         the polynomial itself.
4245SEE ALSO: deltaLoc, invariants
4246KEYWORDS: delta invariant
4247EXAMPLE: example delta;  shows an example
4248"
4249{
4250  if (typeof(#[1])=="poly") { // INPUT = polynomial defining the singularity
4251    list L=hnexpansion(#[1]);
4252    if (typeof(L[1])=="ring") {
4253      def altring = basering;
4254      def HNring = L[1]; setring HNring;
4255      return(delta(hne));
4256    }
4257    else {
4258      return(delta(L));
4259    }
4260  }
4261  if (typeof(#[1])=="ring") { // INPUT = HNEring of curve
4262    def HNring = #[1]; setring HNring;
4263    return(delta(hne));
4264  }
4265  if (typeof(#[1])=="matrix")
4266  { // INPUT = hne of an irreducible curve
4267    return(invariants(#)[5]/2);
4268  }
4269  else
4270  { // INPUT = hne of a reducible curve
4271    list INV=invariants(#);
4272    return(INV[size(INV)][3]);
4273  }
4274}
4275example
4276{ "EXAMPLE:"; echo = 2;
4277  ring r = 32003,(x,y),ds;
4278  poly f = x25+x24-4x23-1x22y+4x22+8x21y-2x21-12x20y-4x19y2+4x20+10x19y
4279           +12x18y2-24x18y-20x17y2-4x16y3+x18+60x16y2+20x15y3-9x16y
4280           -80x14y3-10x13y4+36x14y2+60x12y4+2x11y5-84x12y3-24x10y5
4281           +126x10y4+4x8y6-126x8y5+84x6y6-36x4y7+9x2y8-1y9;
4282  delta(f);
4283}
4284
4285///////////////////////////////////////////////////////////////////////////////
Note: See TracBrowser for help on using the repository browser.