source: git/libpolys/polys/ext_fields/transext.cc @ 7ecf26

spielwiese
Last change on this file since 7ecf26 was 7ecf26, checked in by Hans Schoenemann <hannes@…>, 6 years ago
fix: typo in transext.cc
  • Property mode set to 100644
File size: 67.6 KB
Line 
1/****************************************
2*  Computer Algebra System SINGULAR     *
3****************************************/
4/*
5* ABSTRACT: numbers in a rational function field K(t_1, .., t_s) with
6*           transcendental variables t_1, ..., t_s, where s >= 1.
7*           Denoting the implemented coeffs object by cf, then these numbers
8*           are represented as quotients of polynomials living in the
9*           polynomial ring K[t_1, .., t_s] represented by cf->extring.
10*
11*           An element of K(t_1, .., t_s) may have numerous representations,
12*           due to the possibility of common polynomial factors in the
13*           numerator and denominator. This problem is handled by a
14*           cancellation heuristic: Each number "knows" its complexity
15*           which is 0 if and only if common factors have definitely been
16*           cancelled, and some positive integer otherwise.
17*           Each arithmetic operation of two numbers with complexities c1
18*           and c2 will result in a number of complexity c1 + c2 + some
19*           penalty (specific for each arithmetic operation; see constants
20*           in the *.h file). Whenever the resulting complexity exceeds a
21*           certain threshold (see constant in the *.h file), then the
22*           cancellation heuristic will call 'factory' to compute the gcd
23*           and cancel it out in the given number.
24*           For the special case of K = Q (i.e., when computing over the
25*           rationals), this definite cancellation procedure will also take
26*           care of nested fractions: If there are fractional coefficients
27*           in the numerator or denominator of a number, then this number
28*           is being replaced by a quotient of two polynomials over Z, or
29*           - if the denominator is a constant - by a polynomial over Q.
30*
31*           TODO: the description above needs a major update!!!
32*/
33#define TRANSEXT_PRIVATES
34
35#include "misc/auxiliary.h"
36
37#include "omalloc/omalloc.h"
38#include "factory/factory.h"
39
40#include "reporter/reporter.h"
41
42#include "coeffs/coeffs.h"
43#include "coeffs/numbers.h"
44
45#include "coeffs/longrat.h"
46
47#include "polys/monomials/ring.h"
48#include "polys/monomials/p_polys.h"
49#include "polys/simpleideals.h"
50
51#include "polys/clapsing.h"
52#include "polys/clapconv.h"
53
54#include "polys/prCopy.h"
55#include "transext.h"
56#include "algext.h"
57
58#include "polys/PolyEnumerator.h"
59
60
61/* constants for controlling the complexity of numbers */
62#define ADD_COMPLEXITY 1   /**< complexity increase due to + and - */
63#define MULT_COMPLEXITY 2   /**< complexity increase due to * and / */
64#define DIFF_COMPLEXITY 2   /**< complexity increase due to * and / */
65#define BOUND_COMPLEXITY 10   /**< maximum complexity of a number */
66
67/// TRUE iff num. represents 1
68#define NUMIS1(f) (p_IsOne(NUM(f), cf->extRing))
69
70#define COM(f) (f)->complexity
71
72
73#ifdef LDEBUG
74static BOOLEAN  ntDBTest(number a, const char *f, const int l, const coeffs r);
75#endif
76
77#define ntTest(a) n_Test(a, cf)
78
79/* polynomial ring in which the numerators and denominators of our
80   numbers live */
81#define ntRing cf->extRing
82
83/* coeffs object in which the coefficients of our numbers live;
84 * methods attached to ntCoeffs may be used to compute with the
85 * coefficients of our numbers, e.g., use ntCoeffs->nAdd to add
86 * coefficients of our numbers */
87#define ntCoeffs cf->extRing->cf
88
89
90omBin fractionObjectBin = omGetSpecBin(sizeof(fractionObject));
91
92/// forward declarations
93static void heuristicGcdCancellation(number a, const coeffs cf);
94static void definiteGcdCancellation(number a, const coeffs cf,
95                             BOOLEAN simpleTestsHaveAlreadyBeenPerformed);
96
97/* test routine, usualy disabled *
98 * if want to activate it, activate also the calls to check_N *
99 *
100void check_normalized(number t,const coeffs cf, const char *f, int l)
101{
102  if (IS0(t)) return;
103  if(rField_is_Q(ntRing))
104  {
105    poly pp=NUM(t);
106    while(pp!=NULL)
107    {
108      if (((SR_HDL(pGetCoeff(pp)) & SR_INT)==0)&&(SR_HDL(pGetCoeff(pp))!=NULL))
109      {
110        if (pGetCoeff(pp)->s==0)
111        {
112          Print("NUM not normalized in %s:%d\n",f,l);
113          p_Normalize(pp,ntRing);
114        }
115        else if (pGetCoeff(pp)->s==1)
116          Print("NUM is rational in %s:%d\n",f,l);
117      }
118      pIter(pp);
119    }
120    pp=DEN(t);
121    while(pp!=NULL)
122    {
123      if (((SR_HDL(pGetCoeff(pp)) & SR_INT)==0)&&(SR_HDL(pGetCoeff(pp))!=NULL))
124      {
125        if (pGetCoeff(pp)->s==0)
126        {
127          Print("NUM not normalized in %s:%d\n",f,l);
128          p_Normalize(pp,ntRing);
129        }
130        else if (pGetCoeff(pp)->s==1)
131          Print("DEN is rational in %s:%d\n",f,l);
132      }
133      pIter(pp);
134    }
135  }
136}
137#define check_N(A,B) check_normalized(A,B,__FILE__,__LINE__)
138*/
139
140#ifdef LDEBUG
141static BOOLEAN ntDBTest(number a, const char *f, const int l, const coeffs cf)
142{
143  assume(getCoeffType(cf) == n_transExt);
144
145  if (IS0(a)) return TRUE;
146
147  const fraction t = (fraction)a;
148
149  //check_N(a,cf);
150  const poly num = NUM(t);
151  assume(num != NULL);   ///< t != 0 ==> numerator(t) != 0
152
153  p_Test(num, ntRing);
154
155  if (getCoeffType(ntCoeffs)==n_Q)
156    for( poly p = num; p != NULL; pIter(p) )
157      if (! nlIsInteger( p_GetCoeff(p, ntRing), ntCoeffs) )
158      {
159        Print("ERROR in %s:%d: non-integer Q coeff in num. poly\n",f,l);
160        Print("TERM: ");  p_wrp(p, ntRing); PrintLn();
161        return FALSE;
162      }
163
164  const poly den = DEN(t);
165
166  if (den != NULL) // !DENIS1(f)
167  {
168    p_Test(den, ntRing);
169
170    if (getCoeffType(ntCoeffs)==n_Q)
171      for( poly p = den; p != NULL; pIter(p) )
172        if (! nlIsInteger( p_GetCoeff(p, ntRing), ntCoeffs) )
173        {
174          Print("ERROR in %s:%d: non-integer Q coeff in den. poly\n",f,l);
175          Print("TERM: "); p_wrp(p, ntRing);  PrintLn();
176          return FALSE;
177        }
178
179    if (getCoeffType(ntCoeffs)==n_Zp)
180    {
181      if( p_IsConstant(den, ntRing) )
182      {
183        Print("ERROR in %s:%d: constant den. poly / Zp\n",f,l);
184        PrintS("NUM: ");  p_Write(num, ntRing);
185        PrintS("DEN: ");  p_Write(den, ntRing);
186        return FALSE;
187      }
188
189      if( !n_IsOne(pGetCoeff(den), ntCoeffs) )
190      {
191        Print("ERROR in %s:%d: non-monic den. poly / Zp\n",f,l);
192        PrintS("NUM: ");  p_Write(num, ntRing);
193        PrintS("DEN: ");  p_Write(den, ntRing);
194        return FALSE;
195      }
196    }
197
198    if (COM(t)==0)
199    {
200      poly gcd = singclap_gcd_r( num, den, ntRing );
201      if(gcd!=NULL)
202      {
203        if((gcd!=NULL) && !p_IsOne(gcd, ntRing) )
204        {
205          Print("ERROR in %s:%d: 1 != GCD between num. & den. poly\n",f,l);
206          PrintS("GCD: ");  p_Write(gcd, ntRing);
207          PrintS("NUM: ");  p_Write(num, ntRing);
208          PrintS("DEN: ");  p_Write(den, ntRing);
209          return FALSE;
210        }
211        p_Delete( &gcd, ntRing );
212      }
213    }
214    return TRUE;
215
216    if(p_IsConstant(den, ntRing) && (n_IsOne(pGetCoeff(den), ntCoeffs)))
217    {
218      Print("?/1 in %s:%d\n",f,l);
219      return FALSE;
220    }
221    if( !n_GreaterZero(pGetCoeff(den), ntCoeffs) )
222    {
223      Print("negative sign of DEN. of a fraction in %s:%d\n",f,l);
224      return FALSE;
225    }
226    // test that den is over integers!?
227  }
228  else
229  {
230    return TRUE;
231
232    // num != NULL // den == NULL
233//    if( COM(t) != 0 )
234//    {
235//      Print("?//NULL with non-zero complexity: %d in %s:%d\n", COM(t), f, l);
236//      return FALSE;
237//    }
238    // test that nume is over integers!?
239  }
240  if (getCoeffType(ntCoeffs)==n_Q)
241  {
242    poly p=num; // !=NULL
243    do
244    {
245      number n=pGetCoeff(p);
246      n_Test(n,ntCoeffs);
247      if ((!(SR_HDL(n) & SR_INT))&&(n->s==0))
248      /* not normalized, just do for the following test*/
249      {
250        n_Normalize(pGetCoeff(p),ntCoeffs);
251        n=pGetCoeff(p);
252      }
253      if (!(SR_HDL(n) & SR_INT))
254      {
255        if (n->s<2)
256          Print("rational coeff in num: %s:%d\n",f,l);
257      }
258      pIter(p);
259    } while(p!=NULL);
260    p=den;
261    while(p!=NULL)
262    {
263      number n=pGetCoeff(p);
264      if (!(SR_HDL(n) & SR_INT))
265      {
266        if (n->s!=3)
267          Print("rational coeff in den.:%s:%d\n",f,l);
268      }
269      pIter(p);
270    }
271  }
272  return TRUE;
273}
274#endif
275
276poly gcd_over_Q ( poly f, poly g, const ring r)
277{
278  poly res;
279  f=p_Copy(f,r);
280  p_Cleardenom(f, r);
281  g=p_Copy(g,r);
282  p_Cleardenom(g, r);
283  res=singclap_gcd_r(f,g,r);
284  p_Delete(&f, r);
285  p_Delete(&g, r);
286  return res;
287}
288
289/* returns the bottom field in this field extension tower; if the tower
290   is flat, i.e., if there is no extension, then r itself is returned;
291   as a side-effect, the counter 'height' is filled with the height of
292   the extension tower (in case the tower is flat, 'height' is zero) */
293static coeffs nCoeff_bottom(const coeffs r, int &height)
294{
295  assume(r != NULL);
296  coeffs cf = r;
297  height = 0;
298  while (nCoeff_is_Extension(cf))
299  {
300    assume(cf->extRing != NULL); assume(cf->extRing->cf != NULL);
301    cf = cf->extRing->cf;
302    height++;
303  }
304  return cf;
305}
306
307static BOOLEAN ntIsZero(number a, const coeffs cf)
308{
309  //check_N(a,cf);
310  ntTest(a); // !!!
311  return (IS0(a));
312}
313
314static void ntDelete(number * a, const coeffs cf)
315{
316  //check_N(*a,cf);
317  ntTest(*a); // !!!
318
319  fraction f = (fraction)(*a);
320  if (IS0(f)) return;
321  p_Delete(&NUM(f), ntRing);
322  if (!DENIS1(f)) p_Delete(&DEN(f), ntRing);
323  omFreeBin((ADDRESS)f, fractionObjectBin);
324  *a = NULL;
325}
326
327static BOOLEAN ntEqual(number a, number b, const coeffs cf)
328{
329  //check_N(a,cf);
330  //check_N(b,cf);
331  ntTest(a);
332  ntTest(b);
333
334  /// simple tests
335  if (a == b) return TRUE;
336  if ((IS0(a)) && (!IS0(b))) return FALSE;
337  if ((IS0(b)) && (!IS0(a))) return FALSE;
338
339  /// cheap test if gcd's have been cancelled in both numbers
340  fraction fa = (fraction)a;
341  fraction fb = (fraction)b;
342  if ((COM(fa) == 1) && (COM(fb) == 1))
343  {
344    poly f = p_Add_q(p_Copy(NUM(fa), ntRing),
345                     p_Neg(p_Copy(NUM(fb), ntRing), ntRing),
346                     ntRing);
347    if (f != NULL) { p_Delete(&f, ntRing); return FALSE; }
348    if (DENIS1(fa) && DENIS1(fb))  return TRUE;
349    if (DENIS1(fa) && !DENIS1(fb)) return FALSE;
350    if (!DENIS1(fa) && DENIS1(fb)) return FALSE;
351    f = p_Add_q(p_Copy(DEN(fa), ntRing),
352                p_Neg(p_Copy(DEN(fb), ntRing), ntRing),
353                ntRing);
354    if (f != NULL) { p_Delete(&f, ntRing); return FALSE; }
355    return TRUE;
356  }
357
358  /* default: the more expensive multiplication test
359              a/b = c/d  <==>  a*d = b*c */
360  poly f = p_Copy(NUM(fa), ntRing);
361  if (!DENIS1(fb)) f = p_Mult_q(f, p_Copy(DEN(fb), ntRing), ntRing);
362  poly g = p_Copy(NUM(fb), ntRing);
363  if (!DENIS1(fa)) g = p_Mult_q(g, p_Copy(DEN(fa), ntRing), ntRing);
364  poly h = p_Add_q(f, p_Neg(g, ntRing), ntRing);
365  if (h == NULL) return TRUE;
366  else
367  {
368    p_Delete(&h, ntRing);
369    return FALSE;
370  }
371}
372
373static number ntCopy(number a, const coeffs cf)
374{
375  //check_N(a,cf);
376  ntTest(a); // !!!
377  if (IS0(a)) return NULL;
378  fraction f = (fraction)a;
379  poly g = NUM(f);
380  poly h = NULL;
381  h =DEN(f);
382  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
383  NUM(result) = p_Copy(g,cf->extRing);
384  DEN(result) = p_Copy(h,cf->extRing);
385  COM(result) = COM(f);
386  ntTest((number)result);
387  return (number)result;
388}
389
390/* assumes that cf represents the rationals, i.e. Q, and will only
391   be called in that case;
392   assumes furthermore that f != NULL and that the denominator of f != 1;
393   generally speaking, this method removes denominators in the rational
394   coefficients of the numerator and denominator of 'a';
395   more concretely, the following normalizations will be performed,
396   where t^alpha denotes a monomial in the transcendental variables t_k
397   (1) if 'a' is of the form
398          (sum_alpha a_alpha/b_alpha * t^alpha)
399          -------------------------------------
400            (sum_beta c_beta/d_beta * t^beta)
401       with integers a_alpha, b_alpha, c_beta, d_beta, then both the
402       numerator and the denominator will be multiplied by the LCM of
403       the b_alpha's and the d_beta's (if this LCM is != 1),
404   (2) if 'a' is - e.g. after having performed step (1) - of the form
405          (sum_alpha a_alpha * t^alpha)
406          -----------------------------
407            (sum_beta c_beta * t^beta)
408       with integers a_alpha, c_beta, and with a non-constant denominator,
409       then both the numerator and the denominator will be divided by the
410       GCD of the a_alpha's and the c_beta's (if this GCD is != 1),
411   this procedure does not alter COM(f) (this has to be done by the
412   calling procedure);
413   modifies f */
414static void handleNestedFractionsOverQ(fraction f, const coeffs cf)
415{
416  assume(nCoeff_is_Q(ntCoeffs));
417  assume(!IS0(f));
418  assume(!DENIS1(f));
419
420  { /* step (1); see documentation of this procedure above */
421    number lcmOfDenominators = n_Init(1, ntCoeffs);
422    number c; number tmp;
423    poly p = NUM(f);
424    /* careful when using n_NormalizeHelper!!! It computes the lcm of the numerator
425       of the 1st argument and the denominator of the 2nd!!! */
426    while (p != NULL)
427    {
428      c = p_GetCoeff(p, ntRing);
429      tmp = n_NormalizeHelper(lcmOfDenominators, c, ntCoeffs);
430      n_Delete(&lcmOfDenominators, ntCoeffs);
431      lcmOfDenominators = tmp;
432      pIter(p);
433    }
434    p = DEN(f);
435    while (p != NULL)
436    {
437      c = p_GetCoeff(p, ntRing);
438      tmp = n_NormalizeHelper(lcmOfDenominators, c, ntCoeffs);
439      n_Delete(&lcmOfDenominators, ntCoeffs);
440      lcmOfDenominators = tmp;
441      pIter(p);
442    }
443    if (!n_IsOne(lcmOfDenominators, ntCoeffs))
444    { /* multiply NUM(f) and DEN(f) with lcmOfDenominators */
445      NUM(f) = __p_Mult_nn(NUM(f), lcmOfDenominators, ntRing);
446      p_Normalize(NUM(f), ntRing);
447      DEN(f) = __p_Mult_nn(DEN(f), lcmOfDenominators, ntRing);
448      p_Normalize(DEN(f), ntRing);
449    }
450    n_Delete(&lcmOfDenominators, ntCoeffs);
451    if (DEN(f)!=NULL)
452    { /* step (2); see documentation of this procedure above */
453      p = NUM(f);
454      number gcdOfCoefficients = n_Copy(p_GetCoeff(p, ntRing), ntCoeffs);
455      pIter(p);
456      while ((p != NULL) && (!n_IsOne(gcdOfCoefficients, ntCoeffs)))
457      {
458        c = p_GetCoeff(p, ntRing);
459        tmp = n_Gcd(c, gcdOfCoefficients, ntCoeffs);
460        n_Delete(&gcdOfCoefficients, ntCoeffs);
461        gcdOfCoefficients = tmp;
462        pIter(p);
463      }
464      p = DEN(f);
465      while ((p != NULL) && (!n_IsOne(gcdOfCoefficients, ntCoeffs)))
466      {
467        c = p_GetCoeff(p, ntRing);
468        tmp = n_Gcd(c, gcdOfCoefficients, ntCoeffs);
469        n_Delete(&gcdOfCoefficients, ntCoeffs);
470        gcdOfCoefficients = tmp;
471        pIter(p);
472      }
473      if (!n_IsOne(gcdOfCoefficients, ntCoeffs))
474      { /* divide NUM(f) and DEN(f) by gcdOfCoefficients */
475        number inverseOfGcdOfCoefficients = n_Invers(gcdOfCoefficients,
476                                                     ntCoeffs);
477        NUM(f) = __p_Mult_nn(NUM(f), inverseOfGcdOfCoefficients, ntRing);
478        p_Normalize(NUM(f), ntRing);
479        DEN(f) = __p_Mult_nn(DEN(f), inverseOfGcdOfCoefficients, ntRing);
480        p_Normalize(DEN(f), ntRing);
481        n_Delete(&inverseOfGcdOfCoefficients, ntCoeffs);
482      }
483      n_Delete(&gcdOfCoefficients, ntCoeffs);
484    }
485  }
486
487  /* Now, due to the above computations, DEN(f) may have become the
488     1-polynomial which needs to be represented by NULL: */
489  if ((DEN(f) != NULL) &&
490      p_IsConstant(DEN(f), ntRing) &&
491      n_IsOne(p_GetCoeff(DEN(f), ntRing), ntCoeffs))
492  {
493    p_Delete(&DEN(f), ntRing); DEN(f) = NULL;
494  }
495
496  if( DEN(f) != NULL )
497    if( !n_GreaterZero(pGetCoeff(DEN(f)), ntCoeffs) )
498    {
499      NUM(f) = p_Neg(NUM(f), ntRing);
500      DEN(f) = p_Neg(DEN(f), ntRing);
501    }
502  COM(f)=BOUND_COMPLEXITY+1;
503  ntTest((number)f); // TODO!
504}
505
506/// TODO: normalization of a!?
507static number ntGetNumerator(number &a, const coeffs cf)
508{
509  //check_N(a,cf);
510  ntTest(a);
511  if (IS0(a)) return NULL;
512
513  definiteGcdCancellation(a, cf, FALSE);
514
515  fraction f = (fraction)a;
516  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
517
518  const BOOLEAN denis1= DENIS1 (f);
519
520  if (getCoeffType (ntCoeffs) == n_Q && !denis1)
521    handleNestedFractionsOverQ (f, cf);
522
523  if (getCoeffType (ntCoeffs) == n_Q && denis1)
524  {
525    assume( DEN (f) == NULL );
526
527    number g;
528    // TODO/NOTE: the following should not be necessary (due to
529    // Hannes!) as NUM (f) should be over Z!!!
530    CPolyCoeffsEnumerator itr(NUM(f));
531
532
533    n_ClearDenominators(itr, g, ntCoeffs);
534
535    if( !n_GreaterZero(g, ntCoeffs) )
536    {
537      NUM (f) = p_Neg(NUM (f), ntRing);
538      g = n_InpNeg(g, ntCoeffs);
539    }
540
541    // g should be a positive integer now!
542    assume( n_GreaterZero(g, ntCoeffs) );
543
544    if( !n_IsOne(g, ntCoeffs) )
545    {
546      DEN (f) = p_NSet(g, ntRing);
547      COM (f) ++;
548      assume( DEN (f) != NULL );
549    }
550    else
551      n_Delete(&g, ntCoeffs);
552
553    ntTest(a);
554  }
555
556  // Call ntNormalize instead of above?!?
557
558  NUM (result) = p_Copy (NUM (f), ntRing); // ???
559  //DEN (result) = NULL; // done by ..Alloc0..
560  //COM (result) = 0; // done by ..Alloc0..
561
562  ntTest((number)result);
563  //check_N((number)result,cf);
564  return (number)result;
565}
566
567/// TODO: normalization of a!?
568static number ntGetDenom(number &a, const coeffs cf)
569{
570  //check_N(a,cf);
571  ntTest(a);
572
573  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
574  //DEN (result)= NULL; // done by ..Alloc0..
575  //COM (result)= 0; // done by ..Alloc0..
576
577  if (IS0(a))
578  {
579    NUM (result) = p_One(ntRing);
580    return (number)result;
581  }
582
583  definiteGcdCancellation(a, cf, FALSE);
584
585  fraction f = (fraction)a;
586
587  assume( !IS0(f) );
588
589  const BOOLEAN denis1 = DENIS1 (f);
590
591  if( denis1 && (getCoeffType (ntCoeffs) != n_Q) ) // */1 or 0
592  {
593    NUM (result)= p_One(ntRing);
594    ntTest((number)result);
595    return (number)result;
596  }
597
598  if (!denis1) // */* / Q
599  {
600    assume( DEN (f) != NULL );
601
602    if (getCoeffType (ntCoeffs) == n_Q)
603      handleNestedFractionsOverQ (f, cf);
604
605    ntTest(a);
606
607    if( DEN (f) != NULL ) // is it ?? // 1 now???
608    {
609      assume( !p_IsOne(DEN (f), ntRing) );
610
611      NUM (result) = p_Copy (DEN (f), ntRing);
612      ntTest((number)result);
613      return (number)result;
614    }
615//    NUM (result) = p_One(ntRing); // NOTE: just in order to be sure...
616  }
617
618  // */1 / Q
619  assume( getCoeffType (ntCoeffs) == n_Q );
620  assume( DEN (f) == NULL );
621
622  number g;
623//    poly num= p_Copy (NUM (f), ntRing); // ???
624
625
626  // TODO/NOTE: the following should not be necessary (due to
627  // Hannes!) as NUM (f) should be over Z!!!
628  CPolyCoeffsEnumerator itr(NUM(f));
629
630  n_ClearDenominators(itr, g, ntCoeffs); // may return -1 :(((
631
632  if( !n_GreaterZero(g, ntCoeffs) )
633  {
634//     NUM (f) = p_Neg(NUM (f), ntRing); // Ugly :(((
635//     g = n_InpNeg(g, ntCoeffs);
636    NUM (f) = p_Neg(NUM (f), ntRing); // Ugly :(((
637    g = n_InpNeg(g, ntCoeffs);
638  }
639
640  // g should be a positive integer now!
641  assume( n_GreaterZero(g, ntCoeffs) );
642
643  if( !n_IsOne(g, ntCoeffs) )
644  {
645    assume( n_GreaterZero(g, ntCoeffs) );
646    assume( !n_IsOne(g, ntCoeffs) );
647
648    DEN (f) = p_NSet(g, ntRing); // update COM(f)???
649    assume( DEN (f) != NULL );
650    COM (f) ++;
651
652    NUM (result)= p_Copy (DEN (f), ntRing);
653  }
654  else
655  { // common denom == 1?
656    NUM (result)= p_NSet(g, ntRing); // p_Copy (DEN (f), ntRing);
657//  n_Delete(&g, ntCoeffs);
658  }
659
660//    if (!p_IsConstant (num, ntRing) && pNext(num) != NULL)
661//    else
662//      g= p_GetAllDenom (num, ntRing);
663//    result= (fraction) ntSetMap (ntCoeffs, cf) (g, ntCoeffs, cf);
664
665  ntTest((number)result);
666  //check_N((number)result,cf);
667  return (number)result;
668}
669
670static BOOLEAN ntIsOne(number a, const coeffs cf)
671{
672  //check_N(a,cf);
673  ntTest(a); // !!!
674  definiteGcdCancellation(a, cf, FALSE);
675  fraction f = (fraction)a;
676  return (f!=NULL) && DENIS1(f) && NUMIS1(f);
677}
678
679static BOOLEAN ntIsMOne(number a, const coeffs cf)
680{
681  //check_N(a,cf);
682  ntTest(a);
683  definiteGcdCancellation(a, cf, FALSE);
684  fraction f = (fraction)a;
685  if ((f==NULL) || (!DENIS1(f))) return FALSE;
686  poly g = NUM(f);
687  if (!p_IsConstant(g, ntRing)) return FALSE;
688  return n_IsMOne(p_GetCoeff(g, ntRing), ntCoeffs);
689}
690
691/// this is in-place, modifies a
692static number ntNeg(number a, const coeffs cf)
693{
694  //check_N(a,cf);
695  ntTest(a);
696  if (!IS0(a))
697  {
698    fraction f = (fraction)a;
699    NUM(f) = p_Neg(NUM(f), ntRing);
700  }
701  ntTest(a);
702  return a;
703}
704
705number ntInit(long i, const coeffs cf)
706{
707  if (i != 0)
708  {
709    poly p=p_ISet(i, ntRing);
710    if (p!=NULL)
711    {
712      fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
713      NUM(result) = p;
714      //DEN(result) = NULL; // done by omAlloc0Bin
715      //COM(result) = 0; // done by omAlloc0Bin
716      ntTest((number)result);
717      //check_N((number)result,cf);
718      return (number)result;
719    }
720  }
721  return NULL;
722}
723
724
725// takes over p!
726number ntInit(poly p, const coeffs cf)
727{
728  if (p == NULL) return NULL;
729
730  p_Test( p, ntRing);
731  fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
732
733  if (nCoeff_is_Q(ntCoeffs))
734  {
735    number g;
736    // the following is necessary because
737    // NUM (f) should be over Z,
738    // while p may be over Q
739    CPolyCoeffsEnumerator itr(p);
740
741    n_ClearDenominators(itr, g, ntCoeffs);
742
743    if( !n_GreaterZero(g, ntCoeffs) )
744    {
745      p = p_Neg(p, ntRing);
746      g = n_InpNeg(g, ntCoeffs);
747    }
748
749    // g should be a positive integer now!
750    assume( n_GreaterZero(g, ntCoeffs) );
751
752    if( !n_IsOne(g, ntCoeffs) )
753    {
754      DEN (f) = p_NSet(g, ntRing);
755      p_Normalize(DEN(f), ntRing);
756      assume( DEN (f) != NULL );
757    }
758    else
759    {
760      //DEN(f) = NULL; // done by omAlloc0
761      n_Delete(&g, ntCoeffs);
762    }
763  }
764
765  p_Normalize(p, ntRing);
766  NUM(f) = p;
767  //COM(f) = 0; // done by omAlloc0
768
769  //check_N((number)f,cf);
770  ntTest((number)f);
771  return (number)f;
772}
773
774static long ntInt(number &a, const coeffs cf)
775{
776  //check_N(a,cf);
777  ntTest(a);
778  if (IS0(a)) return 0;
779  definiteGcdCancellation(a, cf, FALSE);
780  fraction f = (fraction)a;
781  if (!DENIS1(f)) return 0;
782
783  const poly aAsPoly = NUM(f);
784
785  if(aAsPoly == NULL)
786    return 0;
787
788  if (!p_IsConstant(aAsPoly, ntRing))
789    return 0;
790
791  assume( aAsPoly != NULL );
792
793  return n_Int(p_GetCoeff(aAsPoly, ntRing), ntCoeffs);
794}
795
796/* return FALSE, if a is a constant <=0 */
797static BOOLEAN ntGreaterZero(number a, const coeffs cf)
798{
799  //check_N(a,cf);
800  ntTest(a);
801  if (IS0(a)) return FALSE;
802  fraction f = (fraction)a;
803  poly g = NUM(f);
804  return (!p_LmIsConstant(g,ntRing)|| n_GreaterZero(pGetCoeff(g), ntCoeffs));
805}
806
807static BOOLEAN ntGreater(number a, number b, const coeffs cf)
808{
809  //check_N(a,cf);
810  //check_N(b,cf);
811  ntTest(a);
812  ntTest(b);
813  if (IS0(a))
814  {
815    if (IS0(b)) return FALSE;
816    fraction fb = (fraction)b;
817    return (!n_GreaterZero(pGetCoeff(NUM(fb)), ntCoeffs));
818  }
819  if (IS0(b))
820  {
821    fraction fa = (fraction)a;
822    return (n_GreaterZero(pGetCoeff(NUM(fa)), ntCoeffs));
823  }
824  // now: a!=0, b!=0
825  fraction fa = (fraction)a;
826  number aNumCoeff = p_GetCoeff(NUM(fa), ntRing);
827  int aNumDeg = p_Totaldegree(NUM(fa), ntRing);
828  number aDenCoeff = NULL; int aDenDeg = 0;
829  if (DEN(fa)!=NULL)
830  {
831    aDenCoeff=p_GetCoeff(DEN(fa),ntRing);
832    aDenDeg = p_Totaldegree(DEN(fa), ntRing);
833  }
834  fraction fb = (fraction)b;
835  number bNumCoeff = p_GetCoeff(NUM(fb), ntRing);
836  int bNumDeg = p_Totaldegree(NUM(fb), ntRing);
837  number bDenCoeff = NULL; int bDenDeg = 0;
838  if (DEN(fb)!=NULL)
839  {
840    bDenCoeff=p_GetCoeff(DEN(fb),ntRing);
841    bDenDeg = p_Totaldegree(DEN(fb), ntRing);
842  }
843  if (aNumDeg-aDenDeg > bNumDeg-bDenDeg) return TRUE;
844  if (aNumDeg-aDenDeg < bNumDeg-bDenDeg) return FALSE;
845  number aa;
846  number bb;
847  if (bDenCoeff==NULL) aa=n_Copy(aNumCoeff,ntCoeffs);
848  else                 aa=n_Mult(aNumCoeff,bDenCoeff,ntCoeffs);
849  if (aDenCoeff==NULL) bb=n_Copy(bNumCoeff,ntCoeffs);
850  else                 bb=n_Mult(bNumCoeff,aDenCoeff,ntCoeffs);
851  BOOLEAN rr= n_Greater(aa, bb, ntCoeffs);
852  n_Delete(&aa,ntCoeffs);
853  n_Delete(&bb,ntCoeffs);
854  return rr;
855}
856
857static void ntCoeffWrite(const coeffs cf, BOOLEAN details)
858{
859  assume( cf != NULL );
860
861  const ring A = cf->extRing;
862
863  assume( A != NULL );
864  assume( A->cf != NULL );
865
866  n_CoeffWrite(A->cf, details);
867
868//  rWrite(A);
869
870  const int P = rVar(A);
871  assume( P > 0 );
872
873  PrintS("(");
874
875  for (int nop=0; nop < P; nop ++)
876  {
877    Print("%s", rRingVar(nop, A));
878    if (nop!=P-1) PrintS(", ");
879  }
880
881  PrintS(")");
882
883  assume( A->qideal == NULL );
884
885/*
886  PrintS("//   Coefficients live in the rational function field\n");
887  Print("//   K(");
888  for (int i = 0; i < rVar(ntRing); i++)
889  {
890    if (i > 0) PrintS(" ");
891    Print("%s", rRingVar(i, ntRing));
892  }
893  PrintS(") with\n");
894  PrintS("//   K: "); n_CoeffWrite(cf->extRing->cf);
895*/
896}
897
898number ntDiff(number a, number d, const coeffs cf)
899{
900  //check_N(a,cf);
901  //check_N(d,cf);
902  ntTest(a);
903  ntTest(d);
904
905  if (IS0(d))
906  {
907    WerrorS("ringvar expected");
908    return NULL;
909  }
910  fraction t = (fraction) d;
911  if (!DENIS1(t))
912  {
913    WerrorS("expected differentiation by a variable");
914    return NULL;
915  }
916  int k=p_Var(NUM(t),ntRing);
917  if (k==0)
918  {
919    WerrorS("expected differentiation by a variable");
920    return NULL;
921  }
922
923  if (IS0(a)) return ntCopy(a, cf);
924
925  fraction fa = (fraction)a;
926  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
927  if (DENIS1(fa))
928  {
929     NUM(result) = p_Diff(NUM(fa),k,ntRing);
930     //DEN(result) = NULL; // done by ..Alloc0..
931     if (NUM(result)==NULL)
932     {
933       omFreeBin((ADDRESS)result, fractionObjectBin);
934       return(NULL);
935     }
936     COM(result) = COM(fa)+DIFF_COMPLEXITY;
937     //check_N((number)result,cf);
938     ntTest((number)result);
939     return (number)result;
940  }
941
942  poly fg = p_Mult_q(p_Copy(DEN(fa),ntRing),p_Diff(NUM(fa),k,ntRing),ntRing);
943  poly gf = p_Mult_q(p_Copy(NUM(fa),ntRing),p_Diff(DEN(fa),k,ntRing),ntRing);
944  NUM(result) = p_Sub(fg,gf,ntRing);
945  if (NUM(result)==NULL) return(NULL);
946  DEN(result) = pp_Mult_qq(DEN(fa), DEN(fa), ntRing);
947  COM(result) = COM(fa) + COM(fa) + DIFF_COMPLEXITY;
948  heuristicGcdCancellation((number)result, cf);
949
950  //check_N((number)result,cf);
951  ntTest((number)result);
952  return (number)result;
953}
954
955static number ntAdd(number a, number b, const coeffs cf)
956{
957  //check_N(a,cf);
958  //check_N(b,cf);
959  ntTest(a);
960  ntTest(b);
961  if (IS0(a)) return ntCopy(b, cf);
962  if (IS0(b)) return ntCopy(a, cf);
963
964  fraction fa = (fraction)a;
965  fraction fb = (fraction)b;
966
967  poly g = p_Copy(NUM(fa), ntRing);
968  if (!DENIS1(fb)) g = p_Mult_q(g, p_Copy(DEN(fb), ntRing), ntRing);
969  poly h = p_Copy(NUM(fb), ntRing);
970  if (!DENIS1(fa)) h = p_Mult_q(h, p_Copy(DEN(fa), ntRing), ntRing);
971  g = p_Add_q(g, h, ntRing);
972
973  if (g == NULL) return NULL;
974
975  poly f;
976  if      (DENIS1(fa) && DENIS1(fb))  f = NULL;
977  else if (!DENIS1(fa) && DENIS1(fb)) f = p_Copy(DEN(fa), ntRing);
978  else if (DENIS1(fa) && !DENIS1(fb)) f = p_Copy(DEN(fb), ntRing);
979  else /* both denom's are != 1 */    f = p_Mult_q(p_Copy(DEN(fa), ntRing),
980                                                   p_Copy(DEN(fb), ntRing),
981                                                   ntRing);
982
983  fraction result = (fraction)omAllocBin(fractionObjectBin);
984  NUM(result) = g;
985  DEN(result) = f;
986  COM(result) = COM(fa) + COM(fb) + ADD_COMPLEXITY;
987  heuristicGcdCancellation((number)result, cf);
988
989//  ntTest((number)result);
990
991  //check_N((number)result,cf);
992  ntTest((number)result);
993  return (number)result;
994}
995
996static number ntSub(number a, number b, const coeffs cf)
997{
998  //check_N(a,cf);
999  //check_N(b,cf);
1000  ntTest(a);
1001  ntTest(b);
1002  if (IS0(a)) return ntNeg(ntCopy(b, cf), cf);
1003  if (IS0(b)) return ntCopy(a, cf);
1004
1005  fraction fa = (fraction)a;
1006  fraction fb = (fraction)b;
1007
1008  poly g = p_Copy(NUM(fa), ntRing);
1009  if (!DENIS1(fb)) g = p_Mult_q(g, p_Copy(DEN(fb), ntRing), ntRing);
1010  poly h = p_Copy(NUM(fb), ntRing);
1011  if (!DENIS1(fa)) h = p_Mult_q(h, p_Copy(DEN(fa), ntRing), ntRing);
1012  g = p_Add_q(g, p_Neg(h, ntRing), ntRing);
1013
1014  if (g == NULL) return NULL;
1015
1016  poly f;
1017  if      (DENIS1(fa) && DENIS1(fb))  f = NULL;
1018  else if (!DENIS1(fa) && DENIS1(fb)) f = p_Copy(DEN(fa), ntRing);
1019  else if (DENIS1(fa) && !DENIS1(fb)) f = p_Copy(DEN(fb), ntRing);
1020  else /* both den's are != 1 */      f = p_Mult_q(p_Copy(DEN(fa), ntRing),
1021                                                   p_Copy(DEN(fb), ntRing),
1022                                                   ntRing);
1023
1024  fraction result = (fraction)omAllocBin(fractionObjectBin);
1025  NUM(result) = g;
1026  DEN(result) = f;
1027  COM(result) = COM(fa) + COM(fb) + ADD_COMPLEXITY;
1028  heuristicGcdCancellation((number)result, cf);
1029//  ntTest((number)result);
1030  //check_N((number)result,cf);
1031  ntTest((number)result);
1032  return (number)result;
1033}
1034
1035static number ntMult(number a, number b, const coeffs cf)
1036{
1037  //check_N(a,cf);
1038  //check_N(b,cf);
1039  ntTest(a); // !!!?
1040  ntTest(b); // !!!?
1041
1042  if (IS0(a) || IS0(b)) return NULL;
1043
1044  fraction fa = (fraction)a;
1045  fraction fb = (fraction)b;
1046
1047  const poly g = pp_Mult_qq(NUM(fa), NUM(fb), ntRing);
1048
1049  if (g == NULL) return NULL; // may happen due to zero divisors???
1050
1051  fraction result = (fraction)omAllocBin(fractionObjectBin);
1052
1053  NUM(result) = g;
1054
1055  const poly da = DEN(fa);
1056  const poly db = DEN(fb);
1057
1058
1059  //check_N((number)result,cf);
1060  if (db == NULL)
1061  {
1062    // b = ? // NULL
1063
1064    if(da == NULL)
1065    { // both fa && fb are ?? // NULL!
1066      assume (da == NULL && db == NULL);
1067      DEN(result) = NULL;
1068      COM(result) = 0;
1069    }
1070    else
1071    {
1072      assume (da != NULL && db == NULL);
1073      DEN(result) = p_Copy(da, ntRing);
1074      COM(result) = COM(fa) + MULT_COMPLEXITY;
1075      heuristicGcdCancellation((number)result, cf);
1076      //check_N((number)result,cf);
1077    }
1078  }
1079  else
1080  { // b = ?? / ??
1081    if (da == NULL)
1082    { // a == ? // NULL
1083      assume( db != NULL && da == NULL);
1084      DEN(result) = p_Copy(db, ntRing);
1085      COM(result) = COM(fb) + MULT_COMPLEXITY;
1086      heuristicGcdCancellation((number)result, cf);
1087      //check_N((number)result,cf);
1088    }
1089    else /* both den's are != 1 */
1090    {
1091      assume (da != NULL && db != NULL);
1092      DEN(result) = pp_Mult_qq(da, db, ntRing);
1093      COM(result) = COM(fa) + COM(fb) + MULT_COMPLEXITY;
1094      heuristicGcdCancellation((number)result, cf);
1095      //check_N((number)result,cf);
1096    }
1097  }
1098
1099//  ntTest((number)result);
1100
1101  //check_N((number)result,cf);
1102  ntTest((number)result);
1103  return (number)result;
1104}
1105
1106static void ntNormalizeDen(fraction result, const ring R)
1107{
1108  if ((nCoeff_has_simple_inverse(R->cf))
1109  && (result!=NULL)
1110  && (DEN(result)!=NULL))
1111  {
1112    poly n=DEN(result);
1113    if (!n_IsOne(pGetCoeff(n),R->cf))
1114    {
1115      number inv=n_Invers(pGetCoeff(n),R->cf);
1116      DEN(result)=__p_Mult_nn(n,inv,R);
1117      NUM(result)=__p_Mult_nn(NUM(result),inv,R);
1118      n_Delete(&inv,R->cf);
1119      if (p_IsOne(DEN(result), R))
1120      {
1121        n=DEN(result);
1122        DEN(result)=NULL;
1123        COM(result) = 0;
1124        p_Delete(&n,R);
1125      }
1126    }
1127  }
1128}
1129
1130static number ntDiv(number a, number b, const coeffs cf)
1131{
1132  //check_N(a,cf);
1133  //check_N(b,cf);
1134  ntTest(a);
1135  ntTest(b);
1136  if (IS0(a)) return NULL;
1137  if (IS0(b)) WerrorS(nDivBy0);
1138
1139  fraction fa = (fraction)a;
1140  fraction fb = (fraction)b;
1141
1142  poly g = p_Copy(NUM(fa), ntRing);
1143  if (!DENIS1(fb)) g = p_Mult_q(g, p_Copy(DEN(fb), ntRing), ntRing);
1144
1145  if (g == NULL) return NULL;   /* may happen due to zero divisors */
1146
1147  poly f = p_Copy(NUM(fb), ntRing);
1148  if (!DENIS1(fa)) f = p_Mult_q(f, p_Copy(DEN(fa), ntRing), ntRing);
1149
1150  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
1151  NUM(result) = g;
1152  if (!n_GreaterZero(pGetCoeff(f),ntCoeffs))
1153  {
1154    g=p_Neg(g,ntRing);
1155    f=p_Neg(f,ntRing);
1156    NUM(result) = g;
1157  }
1158  if (!p_IsConstant(f,ntRing) || !n_IsOne(pGetCoeff(f),ntCoeffs))
1159  {
1160    DEN(result) = f;
1161  }
1162  else
1163  {
1164    p_Delete(&f, ntRing);
1165  }
1166  COM(result) = COM(fa) + COM(fb) + MULT_COMPLEXITY;
1167//  definiteGcdCancellation((number)result, cf,FALSE);
1168  heuristicGcdCancellation((number)result, cf);
1169//  ntTest((number)result);
1170  //check_N((number)result,cf);
1171  ntNormalizeDen(result,ntRing);
1172  ntTest((number)result);
1173  return (number)result;
1174}
1175
1176static number ntInvers(number a, const coeffs cf)
1177{
1178  //check_N(a,cf);
1179  ntTest(a);
1180  if (IS0(a))
1181  {
1182    WerrorS(nDivBy0);
1183    return NULL;
1184  }
1185  fraction f = (fraction)a;
1186  assume( f != NULL );
1187
1188  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
1189
1190  assume( NUM(f) != NULL );
1191  const poly den = DEN(f);
1192
1193  if (den == NULL)
1194    NUM(result) = p_One(ntRing);
1195  else
1196    NUM(result) = p_Copy(den, ntRing);
1197
1198  if( !NUMIS1(f) )
1199  {
1200    poly num_f=NUM(f);
1201    BOOLEAN neg= !n_GreaterZero(pGetCoeff(num_f),ntCoeffs);
1202    if (neg)
1203    {
1204      num_f=p_Neg(p_Copy(num_f, ntRing), ntRing);
1205      NUM(result)=p_Neg(NUM(result), ntRing);
1206    }
1207    else
1208    {
1209      num_f=p_Copy(num_f, ntRing);
1210    }
1211    DEN(result) = num_f;
1212    COM(result) = COM(f);
1213    if (neg)
1214    {
1215      if (p_IsOne(num_f, ntRing))
1216      {
1217        DEN(result)=NULL;
1218        //COM(result) = 0;
1219        p_Delete(&num_f,ntRing);
1220      }
1221    }
1222  }
1223  //else// Alloc0
1224  //{
1225  //  DEN(result) = NULL;
1226  //  COM(result) = 0;
1227  //}
1228  ntNormalizeDen(result,ntRing);
1229  ntTest((number)result); // !!!!
1230  //check_N((number)result,cf);
1231  return (number)result;
1232}
1233
1234/* 0^0 = 0;
1235   for |exp| <= 7 compute power by a simple multiplication loop;
1236   for |exp| >= 8 compute power along binary presentation of |exp|, e.g.
1237      p^13 = p^1 * p^4 * p^8, where we utilise that
1238      p^(2^(k+1)) = p^(2^k) * p^(2^k);
1239   intermediate cancellation is controlled by the in-place method
1240   heuristicGcdCancellation; see there.
1241*/
1242static void ntPower(number a, int exp, number *b, const coeffs cf)
1243{
1244  ntTest(a);
1245
1246  /* special cases first */
1247  if (IS0(a))
1248  {
1249    if (exp >= 0) *b = NULL;
1250    else          WerrorS(nDivBy0);
1251  }
1252  else if (exp ==  0) { *b = ntInit(1, cf); return;}
1253  else if (exp ==  1) { *b = ntCopy(a, cf); return;}
1254  else if (exp == -1) { *b = ntInvers(a, cf); return;}
1255
1256  int expAbs = exp; if (expAbs < 0) expAbs = -expAbs;
1257
1258  /* now compute a^expAbs */
1259  number pow; number t;
1260  if (expAbs <= 7)
1261  {
1262    pow = ntCopy(a, cf);
1263    for (int i = 2; i <= expAbs; i++)
1264    {
1265      t = ntMult(pow, a, cf);
1266      ntDelete(&pow, cf);
1267      pow = t;
1268      heuristicGcdCancellation(pow, cf);
1269    }
1270  }
1271  else
1272  {
1273    pow = ntInit(1, cf);
1274    number factor = ntCopy(a, cf);
1275    while (expAbs != 0)
1276    {
1277      if (expAbs & 1)
1278      {
1279        t = ntMult(pow, factor, cf);
1280        ntDelete(&pow, cf);
1281        pow = t;
1282        heuristicGcdCancellation(pow, cf);
1283      }
1284      expAbs = expAbs / 2;
1285      if (expAbs != 0)
1286      {
1287        t = ntMult(factor, factor, cf);
1288        ntDelete(&factor, cf);
1289        factor = t;
1290        heuristicGcdCancellation(factor, cf);
1291      }
1292    }
1293    ntDelete(&factor, cf);
1294  }
1295
1296  /* invert if original exponent was negative */
1297  if (exp < 0)
1298  {
1299    t = ntInvers(pow, cf);
1300    ntDelete(&pow, cf);
1301    pow = t;
1302  }
1303  *b = pow;
1304  ntTest(*b);
1305  //check_N(*b,cf);
1306}
1307
1308/* modifies a */
1309/* this is an intermediate simplification routine - not a comple "normalize" */
1310static void heuristicGcdCancellation(number a, const coeffs cf)
1311{
1312  if (IS0(a)) return;
1313
1314  fraction f = (fraction)a;
1315  p_Normalize(NUM(f),ntRing);
1316  if (DENIS1(f) || NUMIS1(f)) { COM(f) = 0; return; }
1317
1318  assume( DEN(f) != NULL );
1319  p_Normalize(DEN(f),ntRing);
1320
1321  /* check whether NUM(f) = DEN(f), and - if so - replace 'a' by 1 */
1322  if (p_EqualPolys(NUM(f), DEN(f), ntRing))
1323  { /* numerator and denominator are both != 1 */
1324    p_Delete(&NUM(f), ntRing); NUM(f) = p_ISet(1, ntRing);
1325    p_Delete(&DEN(f), ntRing); DEN(f) = NULL;
1326    COM(f) = 0;
1327  }
1328  else
1329  {
1330    if (COM(f) > BOUND_COMPLEXITY)
1331      definiteGcdCancellation(a, cf, TRUE);
1332
1333    // TODO: check if it is enough to put the following into definiteGcdCancellation?!
1334    if( DEN(f) != NULL )
1335    {
1336      if( !n_GreaterZero(pGetCoeff(DEN(f)), ntCoeffs) )
1337      {
1338        NUM(f) = p_Neg(NUM(f), ntRing);
1339        DEN(f) = p_Neg(DEN(f), ntRing);
1340      }
1341      if (ntCoeffs->has_simple_Inverse)
1342      {
1343        if (!n_IsOne(pGetCoeff(DEN(f)),ntCoeffs))
1344        {
1345          number inv=n_Invers(pGetCoeff(DEN(f)),ntCoeffs);
1346          DEN(f)=__p_Mult_nn(DEN(f),inv,ntRing);
1347          NUM(f)=__p_Mult_nn(NUM(f),inv,ntRing);
1348        }
1349        if(p_LmIsConstant(DEN(f),ntRing))
1350        {
1351          p_Delete(&DEN(f),ntRing);
1352          COM(f)=0;
1353        }
1354      }
1355      if ((DEN(f)!=NULL)
1356      && (pNext(DEN(f))==NULL))
1357      {
1358        poly den_f=DEN(f);
1359        poly h=NUM(f);
1360        loop
1361        {
1362          if (h==NULL)
1363          {
1364            h=NUM(f);
1365            do
1366            {
1367              p_ExpVectorDiff(h,h,den_f,ntRing);
1368              pIter(h);
1369            } while(h!=NULL);
1370            p_ExpVectorDiff(den_f,den_f,den_f,ntRing);
1371            break;
1372          }
1373          int i=0;
1374          do
1375          {
1376            i++;
1377            if (p_GetExp(den_f,i,ntRing) > p_GetExp(h,i,ntRing)) return;
1378          } while(i<ntRing->N);
1379          pIter(h);
1380        }
1381      }
1382    }
1383  }
1384  if ((DEN(f)!=NULL)
1385  && (pNext(DEN(f))==NULL)
1386  && (p_LmIsConstantComp(DEN(f),ntRing))
1387  && (n_IsOne(pGetCoeff(DEN(f)),ntCoeffs)))
1388  {
1389     p_Delete(&DEN(f),ntRing);
1390     COM(f)=0;
1391  }
1392}
1393
1394/// modifies a
1395static void definiteGcdCancellation(number a, const coeffs cf,
1396                             BOOLEAN simpleTestsHaveAlreadyBeenPerformed)
1397{
1398//  ntTest(a); // !!!!
1399
1400  fraction f = (fraction)a;
1401
1402  if (IS0(a)) return;
1403  if (COM(f)==0) return;
1404  if (DENIS1(f) || NUMIS1(f)) { COM(f) = 0; ntTest(a); return; }
1405  if (!simpleTestsHaveAlreadyBeenPerformed)
1406  {
1407
1408    /* check whether NUM(f) = DEN(f), and - if so - replace 'a' by 1 */
1409    if (p_EqualPolys(NUM(f), DEN(f), ntRing))
1410    { /* numerator and denominator are both != 1 */
1411      p_Delete(&NUM(f), ntRing); NUM(f) = p_ISet(1, ntRing);
1412      p_Delete(&DEN(f), ntRing); DEN(f) = NULL;
1413      COM(f) = 0;
1414      ntTest(a);
1415      return;
1416    }
1417  }
1418  /*if (rField_is_Q(ntRing))
1419  {
1420    number c=n_Copy(pGetCoeff(NUM(f)),ntCoeffs);
1421    poly p=pNext(NUM(f));
1422    while((p!=NULL)&&(!n_IsOne(c,ntCoeffs)))
1423    {
1424      number cc=n_Gcd(c,pGetCoeff(p),ntCoeffs);
1425      n_Delete(&c,ntCoeffs);
1426      c=cc;
1427      pIter(p);
1428    };
1429    p=DEN(f);
1430    while((p!=NULL)&&(!n_IsOne(c,ntCoeffs)))
1431    {
1432      number cc=n_Gcd(c,pGetCoeff(p),ntCoeffs);
1433      n_Delete(&c,ntCoeffs);
1434      c=cc;
1435      pIter(p);
1436    };
1437    if(!n_IsOne(c,ntCoeffs))
1438    {
1439      p=NUM(f);
1440      do
1441      {
1442        number cc=n_Div(pGetCoeff(p),c,ntCoeffs);
1443        n_Normalize(cc,ntCoeffs);
1444        p_SetCoeff(p,cc,ntRing);
1445        pIter(p);
1446      } while(p!=NULL);
1447      p=DEN(f);
1448      do
1449      {
1450        number cc=n_Div(pGetCoeff(p),c,ntCoeffs);
1451        n_Normalize(cc,ntCoeffs);
1452        p_SetCoeff(p,cc,ntRing);
1453        pIter(p);
1454      } while(p!=NULL);
1455      n_Delete(&c,ntCoeffs);
1456      if(pNext(DEN(f))==NULL)
1457      {
1458        if (p_IsOne(DEN(f),ntRing))
1459        {
1460          p_LmDelete(&DEN(f),ntRing);
1461          COM(f)=0;
1462          return;
1463        }
1464        else
1465        {
1466          return;
1467        }
1468      }
1469    }
1470  }*/
1471
1472  /* here we assume: NUM(f), DEN(f) !=NULL, in Z_a reqp. Z/p_a */
1473  //StringSetS("");ntWriteLong(a,cf);
1474  poly pGcd = singclap_gcd_and_divide(NUM(f), DEN(f), ntRing);
1475  //PrintS("gcd= ");p_wrp(pGcd,ntRing);PrintLn();
1476  if (p_IsConstant(pGcd, ntRing)
1477  && n_IsOne(p_GetCoeff(pGcd, ntRing), ntCoeffs)
1478  )
1479  { /* gcd = 1; nothing to cancel;
1480       Suppose the given rational function field is over Q. Although the
1481       gcd is 1, we may have produced fractional coefficients in NUM(f),
1482       DEN(f), or both, due to previous arithmetics. The next call will
1483       remove those nested fractions, in case there are any. */
1484    if (nCoeff_is_Zp(ntCoeffs))
1485    {
1486      number d=p_GetCoeff (DEN(f),ntRing);
1487      BOOLEAN d_not_1=FALSE;
1488      if (!n_IsOne(d,ntCoeffs))
1489      {
1490        NUM (f) = p_Div_nn (NUM (f), d, ntRing);
1491        d_not_1=TRUE;
1492      }
1493      if (p_IsConstant (DEN (f), ntRing))
1494      {
1495        p_Delete(&DEN (f), ntRing);
1496        DEN (f) = NULL;
1497      }
1498      else if (d_not_1)
1499      {
1500        DEN (f) = p_Div_nn (DEN (f), d, ntRing);
1501      }
1502    } else if (nCoeff_is_Q(ntCoeffs)) handleNestedFractionsOverQ(f, cf);
1503  }
1504  else
1505  { /* We divide both NUM(f) and DEN(f) by the gcd which is known
1506       to be != 1. */
1507    if (p_IsConstant(DEN(f), ntRing) &&
1508      n_IsOne(p_GetCoeff(DEN(f), ntRing), ntCoeffs))
1509    {
1510      /* DEN(f) = 1 needs to be represented by NULL! */
1511      p_Delete(&DEN(f), ntRing);
1512      DEN(f) = NULL;
1513    }
1514    else
1515    {
1516      if (nCoeff_is_Zp(ntCoeffs))
1517      {
1518        NUM (f) = p_Div_nn (NUM (f), p_GetCoeff (DEN(f),ntRing), ntRing);
1519        if (p_IsConstant (DEN (f), ntRing))
1520        {
1521          p_Delete(&DEN (f), ntRing);
1522          DEN (f) = NULL;
1523        }
1524        else
1525        {
1526          p_Norm (DEN (f),ntRing);
1527        }
1528      }
1529    }
1530  }
1531  p_Delete(&pGcd, ntRing);
1532//  StringAppendS(" -> ");ntWriteLong(a,cf);StringAppendS("\n");{ char* s = StringEndS(); Print("%s", s); omFree(s); }
1533  COM(f) = 0;
1534
1535  if( DEN(f) != NULL )
1536  {
1537    if( !n_GreaterZero(pGetCoeff(DEN(f)), ntCoeffs) )
1538    {
1539      NUM(f) = p_Neg(NUM(f), ntRing);
1540      DEN(f) = p_Neg(DEN(f), ntRing);
1541      if (p_IsConstant(DEN(f), ntRing) &&
1542        n_IsOne(p_GetCoeff(DEN(f), ntRing), ntCoeffs))
1543      {
1544        /* DEN(f) = 1 needs to be represented by NULL! */
1545        p_Delete(&DEN(f), ntRing);
1546        DEN (f) = NULL;
1547      }
1548    }
1549  }
1550  ntTest(a); // !!!!
1551}
1552
1553static void ntWriteLong(number a, const coeffs cf)
1554{
1555  ntTest(a);
1556  if (IS0(a))
1557    StringAppendS("0");
1558  else
1559  {
1560    fraction f = (fraction)a;
1561    // stole logic from napWrite from kernel/longtrans.cc of legacy singular
1562    BOOLEAN omitBrackets = p_IsConstant(NUM(f), ntRing);
1563    if (!omitBrackets) StringAppendS("(");
1564    p_String0Long(NUM(f), ntRing, ntRing);
1565    if (!omitBrackets) StringAppendS(")");
1566    if (!DENIS1(f))
1567    {
1568      StringAppendS("/");
1569      omitBrackets = p_IsConstant(DEN(f), ntRing);
1570      if (!omitBrackets) StringAppendS("(");
1571      p_String0Long(DEN(f), ntRing, ntRing);
1572      if (!omitBrackets) StringAppendS(")");
1573    }
1574  }
1575  ntTest(a); // !!!!
1576}
1577
1578static void ntWriteShort(number a, const coeffs cf)
1579{
1580  ntTest(a);
1581  if (IS0(a))
1582    StringAppendS("0");
1583  else
1584  {
1585    fraction f = (fraction)a;
1586    // stole logic from napWrite from kernel/longtrans.cc of legacy singular
1587    BOOLEAN omitBrackets = p_IsConstant(NUM(f), ntRing);
1588    if (!omitBrackets) StringAppendS("(");
1589    p_String0Short(NUM(f), ntRing, ntRing);
1590    if (!omitBrackets) StringAppendS(")");
1591    if (!DENIS1(f))
1592    {
1593      StringAppendS("/");
1594      omitBrackets = p_IsConstant(DEN(f), ntRing);
1595      if (!omitBrackets) StringAppendS("(");
1596      p_String0Short(DEN(f), ntRing, ntRing);
1597      if (!omitBrackets) StringAppendS(")");
1598    }
1599  }
1600  ntTest(a);
1601}
1602
1603static const char * ntRead(const char *s, number *a, const coeffs cf)
1604{
1605  poly p;
1606  const char * result = p_Read(s, p, ntRing);
1607  if (p == NULL) *a = NULL;
1608  else *a = ntInit(p, cf);
1609  ntTest(*a);
1610  return result;
1611}
1612
1613static void ntNormalize (number &a, const coeffs cf)
1614{
1615  if ( /*(*/ a!=NULL /*)*/ )
1616  {
1617    //PrintS("num=");p_wrp(NUM(a),ntRing);
1618    //PrintS(" den=");p_wrp(DEN(a),ntRing);PrintLn();
1619    if (COM((fraction)a)>0) definiteGcdCancellation(a, cf, FALSE);
1620    if ((DEN((fraction)a)!=NULL)
1621    &&(!n_GreaterZero(pGetCoeff(DEN((fraction)a)),ntCoeffs)))
1622    {
1623      NUM((fraction)a)=p_Neg(NUM((fraction)a),ntRing);
1624      DEN((fraction)a)=p_Neg(DEN((fraction)a),ntRing);
1625    }
1626  }
1627  ntNormalizeDen((fraction)a,ntRing);
1628  ntTest(a); // !!!!
1629}
1630
1631/* expects *param to be castable to TransExtInfo */
1632static BOOLEAN ntCoeffIsEqual(const coeffs cf, n_coeffType n, void * param)
1633{
1634  if (n_transExt != n) return FALSE;
1635  TransExtInfo *e = (TransExtInfo *)param;
1636  /* for rational function fields we expect the underlying
1637     polynomial rings to be IDENTICAL, i.e. the SAME OBJECT;
1638     this expectation is based on the assumption that we have properly
1639     registered cf and perform reference counting rather than creating
1640     multiple copies of the same coefficient field/domain/ring */
1641  if (ntRing == e->r)
1642    return TRUE;
1643
1644  // NOTE: Q(a)[x] && Q(a)[y] should better share the _same_ Q(a)...
1645  if( rEqual(ntRing, e->r, TRUE) )
1646  {
1647    rDelete(e->r);
1648    return TRUE;
1649  }
1650
1651  return FALSE;
1652}
1653
1654static number ntNormalizeHelper(number a, number b, const coeffs cf)
1655{
1656  ntTest(a);
1657  ntTest(b);
1658  fraction fb = (fraction)b;
1659  if ((b==NULL)||(DEN(fb)==NULL)) return ntCopy(a,cf);
1660  fraction fa = (fraction)a;
1661
1662  poly pGcd;
1663  if (nCoeff_is_Q(ntCoeffs))
1664  {
1665    poly pa = NUM(fa);
1666    poly pb = DEN(fb);
1667    if (p_IsConstant(pa,ntRing) && p_IsConstant(pb,ntRing))
1668    {
1669      pGcd = p_Copy(pa,ntRing);
1670      p_SetCoeff (pGcd, n_Gcd (pGetCoeff(pGcd), pGetCoeff(pb), ntCoeffs), ntRing);
1671    }
1672    else
1673    {
1674      number contentpa, contentpb, tmp;
1675
1676      contentpb= n_Copy(p_GetCoeff(pb, ntRing),ntCoeffs);
1677      pIter(pb);
1678      while (pb != NULL)
1679      {
1680        tmp = n_SubringGcd(contentpb, p_GetCoeff(pb, ntRing) , ntCoeffs);
1681        n_Delete(&contentpb, ntCoeffs);
1682        contentpb = tmp;
1683        pIter(pb);
1684      }
1685
1686      contentpa= n_Copy(p_GetCoeff(pa, ntRing),ntCoeffs);
1687      pIter(pa);
1688      while (pa != NULL)
1689      {
1690        tmp = n_SubringGcd(contentpa, p_GetCoeff(pa, ntRing), ntCoeffs);
1691        n_Delete(&contentpa, ntCoeffs);
1692        contentpa = tmp;
1693        pIter(pa);
1694      }
1695
1696      tmp= n_SubringGcd (contentpb, contentpa, ntCoeffs);
1697      n_Delete(&contentpa, ntCoeffs);
1698      n_Delete(&contentpb, ntCoeffs);
1699      contentpa= tmp;
1700
1701      pGcd = gcd_over_Q(NUM(fa), DEN(fb), ntRing);
1702      pGcd= __p_Mult_nn (pGcd, contentpa, ntRing);
1703      n_Delete(&contentpa, ntCoeffs);
1704    }
1705  }
1706  else
1707    pGcd = singclap_gcd_r(NUM(fa), DEN(fb), ntRing);
1708
1709  /* Note that, over Q, singclap_gcd will remove the denominators in all
1710     rational coefficients of pa and pb, before starting to compute
1711     the gcd. Thus, we do not need to ensure that the coefficients of
1712     pa and pb live in Z; they may well be elements of Q\Z. */
1713
1714  if (p_IsConstant(pGcd, ntRing) &&
1715      n_IsOne(p_GetCoeff(pGcd, ntRing), ntCoeffs))
1716  { /* gcd = 1; return pa*pb*/
1717    p_Delete(&pGcd,ntRing);
1718    fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
1719    NUM(result) = pp_Mult_qq(NUM(fa),DEN(fb),ntRing);
1720
1721    ntTest((number)result); // !!!!
1722
1723    return (number)result;
1724  }
1725
1726  /* return pa*pb/gcd */
1727    poly newNum = singclap_pdivide(NUM(fa), pGcd, ntRing);
1728    p_Delete(&pGcd,ntRing);
1729    fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
1730    NUM(result) = p_Mult_q(p_Copy(DEN(fb),ntRing),newNum,ntRing);
1731    ntTest((number)result); // !!!!
1732    return (number)result;
1733}
1734
1735static number ntGcd(number a, number b, const coeffs cf)
1736{
1737  ntTest(a);
1738  ntTest(b);
1739  if (a==NULL) return ntCopy(b,cf);
1740  if (b==NULL) return ntCopy(a,cf);
1741  fraction fa = (fraction)a;
1742  fraction fb = (fraction)b;
1743
1744
1745  poly pGcd;
1746  if (nCoeff_is_Q(ntCoeffs))
1747  {
1748    poly pa = NUM(fa);
1749    poly pb = NUM(fb);
1750    if (p_IsConstant(pa,ntRing) && p_IsConstant(pb,ntRing))
1751    {
1752      pGcd = p_Copy(pa,ntRing);
1753      p_SetCoeff (pGcd, n_SubringGcd (pGetCoeff(pGcd), pGetCoeff(pb), ntCoeffs), ntRing);
1754    }
1755    else
1756    {
1757      number contentpa, contentpb, tmp;
1758
1759      contentpb= n_Copy(p_GetCoeff(pb, ntRing),ntCoeffs);
1760      pIter(pb);
1761      while (pb != NULL)
1762      {
1763        tmp = n_SubringGcd(contentpb, p_GetCoeff(pb, ntRing) , ntCoeffs);
1764        n_Delete(&contentpb, ntCoeffs);
1765        contentpb = tmp;
1766        pIter(pb);
1767      }
1768
1769      contentpa= n_Copy(p_GetCoeff(pa, ntRing),ntCoeffs);
1770      pIter(pa);
1771      while (pa != NULL)
1772      {
1773        tmp = n_SubringGcd(contentpa, p_GetCoeff(pa, ntRing), ntCoeffs);
1774        n_Delete(&contentpa, ntCoeffs);
1775        contentpa = tmp;
1776        pIter(pa);
1777      }
1778
1779      tmp= n_SubringGcd (contentpb, contentpa, ntCoeffs);
1780      n_Delete(&contentpa, ntCoeffs);
1781      n_Delete(&contentpb, ntCoeffs);
1782      contentpa= tmp;
1783
1784      pGcd = gcd_over_Q(NUM(fa), NUM(fb), ntRing);
1785      pGcd= __p_Mult_nn (pGcd, contentpa, ntRing);
1786      n_Delete(&contentpa, ntCoeffs);
1787    }
1788  }
1789  else
1790    pGcd = singclap_gcd_r(NUM(fa), NUM(fb), ntRing);
1791  /* Note that, over Q, singclap_gcd will remove the denominators in all
1792     rational coefficients of pa and pb, before starting to compute
1793     the gcd. Thus, we do not need to ensure that the coefficients of
1794     pa and pb live in Z; they may well be elements of Q\Z. */
1795
1796  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
1797  NUM(result) = pGcd;
1798  ntTest((number)result); // !!!!
1799  return (number)result;
1800}
1801//number ntGcd_dummy(number a, number b, const coeffs cf)
1802//{
1803//  extern char my_yylinebuf[80];
1804//  Print("ntGcd in >>%s<<\n",my_yylinebuf);
1805//  return ntGcd(a,b,cf);
1806//}
1807
1808static int ntSize(number a, const coeffs cf)
1809{
1810  ntTest(a);
1811  if (IS0(a)) return 0;
1812  fraction f = (fraction)a;
1813  poly p = NUM(f);
1814  unsigned long noOfTerms = 0;
1815  unsigned long numDegree = 0;
1816  if (p!=NULL)
1817  {
1818    numDegree = p_Totaldegree(p,ntRing);
1819    noOfTerms = pLength(p);
1820  }
1821  unsigned long denDegree = 0;
1822  if (!DENIS1(f))
1823  {
1824    denDegree =  p_Totaldegree(DEN(f),ntRing);
1825    noOfTerms += pLength(DEN(f));
1826  }
1827  ntTest(a); // !!!!
1828  // avoid int overflow:
1829  unsigned long t= ((numDegree + denDegree)*(numDegree + denDegree) + 1) * noOfTerms; // must be >0
1830  if (t>INT_MAX) return INT_MAX;
1831  else return (int)t;
1832}
1833
1834/* assumes that src = Q or Z, dst = Q(t_1, ..., t_s) */
1835static number ntMap00(number a, const coeffs src, const coeffs dst)
1836{
1837  n_Test(a, src);
1838
1839  if (n_IsZero(a, src)) return NULL;
1840  assume(src->rep == dst->extRing->cf->rep);
1841  if ((SR_HDL(a) & SR_INT) || (a->s==3))
1842  {
1843    number res=ntInit(p_NSet(n_Copy(a, src), dst->extRing), dst);
1844    n_Test(res, dst);
1845    return res;
1846  }
1847  number nn=n_GetDenom(a,src);
1848  number zz=n_GetNumerator(a,src);
1849  number res=ntInit(p_NSet(zz,dst->extRing), dst);
1850  fraction ff=(fraction)res;
1851  if (n_IsOne(nn,src)) DEN(ff)=NULL;
1852  else                 DEN(ff)=p_NSet(nn,dst->extRing);
1853
1854  n_Test((number)ff,dst);
1855  //check_N((number)ff,dst);
1856  return (number)ff;
1857}
1858
1859static number ntMapZ0(number a, const coeffs src, const coeffs dst)
1860{
1861  n_Test(a, src);
1862  if (n_IsZero(a, src)) return NULL;
1863  nMapFunc nMap=n_SetMap(src,dst->extRing->cf);
1864  poly p=p_NSet(nMap(a, src,dst->extRing->cf), dst->extRing);
1865  if (n_IsZero(pGetCoeff(p),dst->extRing->cf))
1866    p_Delete(&p,dst->extRing);
1867  number res=ntInit(p, dst);
1868  n_Test(res,dst);
1869  return res;
1870}
1871
1872/* assumes that src = Z/p, dst = Q(t_1, ..., t_s) */
1873static number ntMapP0(number a, const coeffs src, const coeffs dst)
1874{
1875  n_Test(a, src);
1876  if (n_IsZero(a, src)) return NULL;
1877  /* mapping via intermediate int: */
1878  int n = n_Int(a, src);
1879  number q = n_Init(n, dst->extRing->cf);
1880  if (n_IsZero(q, dst->extRing->cf))
1881  {
1882    n_Delete(&q, dst->extRing->cf);
1883    return NULL;
1884  }
1885  return ntInit(p_NSet(q, dst->extRing), dst);
1886}
1887
1888 /* assumes that either src = K(t_1, ..., t_s), dst = K(t_1, ..., t_s) */
1889static number ntCopyMap(number a, const coeffs cf, const coeffs dst)
1890{
1891  ntTest(a);
1892  if (IS0(a)) return NULL;
1893
1894  const ring rSrc = cf->extRing;
1895  const ring rDst = dst->extRing;
1896
1897  if( rSrc == rDst )
1898    return ntCopy(a, dst); // USUALLY WRONG!
1899
1900  fraction f = (fraction)a;
1901  poly g = prCopyR(NUM(f), rSrc, rDst);
1902
1903  poly h = NULL;
1904
1905  if (!DENIS1(f))
1906     h = prCopyR(DEN(f), rSrc, rDst);
1907
1908  fraction result = (fraction)omAllocBin(fractionObjectBin);
1909
1910  NUM(result) = g;
1911  DEN(result) = h;
1912  COM(result) = COM(f);
1913  //check_N((number)result,dst);
1914  n_Test((number)result, dst);
1915  return (number)result;
1916}
1917
1918static number ntGenMap(number a, const coeffs cf, const coeffs dst)
1919{
1920  ntTest(a);
1921  if (IS0(a)) return NULL;
1922
1923  const ring rSrc = cf->extRing;
1924  const ring rDst = dst->extRing;
1925
1926  const nMapFunc nMap=n_SetMap(rSrc->cf,rDst->cf);
1927  fraction f = (fraction)a;
1928  poly g = prMapR(NUM(f), nMap, rSrc, rDst);
1929  /* g may contain summands with coeff 0 */
1930  poly hh=g;
1931  poly prev=NULL;
1932  while(hh!=NULL)
1933  {
1934    if (n_IsZero(pGetCoeff(hh),rDst->cf))
1935    {
1936      if (prev==NULL)
1937      {
1938        g=p_LmFreeAndNext(g,rDst);
1939        hh=g;
1940      }
1941      else
1942      {
1943        prev->next=p_LmFreeAndNext(prev->next,rDst);
1944        hh=prev->next;
1945      }
1946    }
1947    else
1948    {
1949      prev=hh;
1950      pIter(hh);
1951    }
1952  }
1953  if (g==NULL) return NULL;
1954
1955  poly h = NULL;
1956
1957  if (!DENIS1(f))
1958  {
1959     h = prMapR(DEN(f), nMap, rSrc, rDst);
1960     /* h may contain summands with coeff 0 */
1961    hh=h;
1962    prev=NULL;
1963    while(hh!=NULL)
1964    {
1965      if (n_IsZero(pGetCoeff(hh),rDst->cf))
1966      {
1967        if (prev==NULL)
1968        {
1969          h=p_LmFreeAndNext(h,rDst);
1970          hh=h;
1971        }
1972        else
1973        {
1974          prev->next=p_LmFreeAndNext(prev->next,rDst);
1975          hh=prev->next;
1976        }
1977      }
1978      else
1979      {
1980        prev=hh;
1981        pIter(hh);
1982      }
1983    }
1984    if (h==NULL) WerrorS("mapping to */0");
1985  }
1986
1987  fraction result = (fraction)omAllocBin(fractionObjectBin);
1988
1989  NUM(result) = g;
1990  DEN(result) = h;
1991  COM(result) = COM(f);
1992  //check_N((number)result,dst);
1993  n_Test((number)result, dst);
1994  return (number)result;
1995}
1996
1997static number ntCopyAlg(number a, const coeffs cf, const coeffs dst)
1998{
1999  n_Test(a, cf) ;
2000  if (n_IsZero(a, cf)) return NULL;
2001  return ntInit(prCopyR((poly)a, cf->extRing, dst->extRing),dst);
2002}
2003
2004static number ntGenAlg(number a, const coeffs cf, const coeffs dst)
2005{
2006  n_Test(a, cf) ;
2007  if (n_IsZero(a, cf)) return NULL;
2008
2009  const nMapFunc nMap=n_SetMap(cf->extRing->cf,dst->extRing->cf);
2010  return ntInit(prMapR((poly)a, nMap, cf->extRing, dst->extRing),dst);
2011}
2012
2013/* assumes that src = Q, dst = Z/p(t_1, ..., t_s) */
2014static number ntMap0P(number a, const coeffs src, const coeffs dst)
2015{
2016  n_Test(a, src) ;
2017  if (n_IsZero(a, src)) return NULL;
2018  // int p = rChar(dst->extRing);
2019
2020  number q = nlModP(a, src, dst->extRing->cf); // FIXME? TODO? // extern number nlModP(number q, const coeffs Q, const coeffs Zp); // Map q \in QQ \to Zp
2021
2022  if (n_IsZero(q, dst->extRing->cf))
2023  {
2024    n_Delete(&q, dst->extRing->cf);
2025    return NULL;
2026  }
2027
2028  poly g = p_NSet(q, dst->extRing);
2029
2030  fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
2031  NUM(f) = g; // DEN(f) = NULL; COM(f) = 0;
2032  n_Test((number)f, dst);
2033  //check_N((number)f,dst);
2034  return (number)f;
2035}
2036
2037/* assumes that src = Z/p, dst = Z/p(t_1, ..., t_s) */
2038static number ntMapPP(number a, const coeffs src, const coeffs dst)
2039{
2040  n_Test(a, src) ;
2041  if (n_IsZero(a, src)) return NULL;
2042  assume(src == dst->extRing->cf);
2043  poly p = p_One(dst->extRing);
2044  p_SetCoeff(p, n_Copy(a, src), dst->extRing);
2045  fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
2046  NUM(f) = p; // DEN(f) = NULL; COM(f) = 0;
2047  n_Test((number)f, dst);
2048  //check_N((number)f,dst);
2049  return (number)f;
2050}
2051
2052/* assumes that src = Z/u, dst = Z/p(t_1, ..., t_s), where u != p */
2053static number ntMapUP(number a, const coeffs src, const coeffs dst)
2054{
2055  n_Test(a, src) ;
2056  if (n_IsZero(a, src)) return NULL;
2057  /* mapping via intermediate int: */
2058  int n = n_Int(a, src);
2059  number q = n_Init(n, dst->extRing->cf);
2060  poly p;
2061  if (n_IsZero(q, dst->extRing->cf))
2062  {
2063    n_Delete(&q, dst->extRing->cf);
2064    return NULL;
2065  }
2066  p = p_One(dst->extRing);
2067  p_SetCoeff(p, q, dst->extRing);
2068  fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
2069  NUM(f) = p; // DEN(f) = NULL; COM(f) = 0;
2070  n_Test((number)f, dst);
2071  //check_N((number)f,dst);
2072  return (number)f;
2073}
2074
2075nMapFunc ntSetMap(const coeffs src, const coeffs dst)
2076{
2077  /* dst is expected to be a rational function field */
2078  assume(getCoeffType(dst) == n_transExt);
2079
2080  if( src == dst ) return ndCopyMap;
2081
2082  int h = 0; /* the height of the extension tower given by dst */
2083  coeffs bDst = nCoeff_bottom(dst, h); /* the bottom field in the tower dst */
2084  coeffs bSrc = nCoeff_bottom(src, h); /* the bottom field in the tower src */
2085
2086  /* for the time being, we only provide maps if h = 1 and if b is Q or
2087     some field Z/pZ: */
2088  if (h==0)
2089  {
2090    if ((src->rep==n_rep_gap_rat) && nCoeff_is_Q(bDst))
2091      return ntMap00;                                 /// Q or Z   -->  Q(T)
2092    if (src->rep==n_rep_gap_gmp)
2093      return ntMapZ0;                                 /// Z   -->  K(T)
2094    if (nCoeff_is_Zp(src) && nCoeff_is_Q(bDst))
2095      return ntMapP0;                                 /// Z/p     -->  Q(T)
2096    if (nCoeff_is_Q_or_BI(src) && nCoeff_is_Zp(bDst))
2097      return ntMap0P;                                 /// Q       --> Z/p(T)
2098    if (nCoeff_is_Zp(src) && nCoeff_is_Zp(bDst))
2099    {
2100      if (src->ch == dst->ch) return ntMapPP;         /// Z/p     --> Z/p(T)
2101      else return ntMapUP;                            /// Z/u     --> Z/p(T)
2102    }
2103  }
2104  if (h != 1) return NULL;
2105  //if ((!nCoeff_is_Zp(bDst)) && (!nCoeff_is_Q(bDst))) return NULL;
2106
2107  /* Let T denote the sequence of transcendental extension variables, i.e.,
2108     K[t_1, ..., t_s] =: K[T];
2109     Let moreover, for any such sequence T, T' denote any subsequence of T
2110     of the form t_1, ..., t_w with w <= s. */
2111
2112  if (rVar(src->extRing) > rVar(dst->extRing))
2113     return NULL;
2114
2115  for (int i = 0; i < rVar(src->extRing); i++)
2116    if (strcmp(rRingVar(i, src->extRing), rRingVar(i, dst->extRing)) != 0)
2117       return NULL;
2118
2119  if (src->type==n_transExt)
2120  {
2121     if (src->extRing->cf==dst->extRing->cf)
2122       return ntCopyMap;          /// K(T')   --> K(T)
2123     else
2124       return ntGenMap;          /// K(T')   --> K'(T)
2125  }
2126  else
2127  {
2128     if (src->extRing->cf==dst->extRing->cf)
2129       return ntCopyAlg;          /// K(T')   --> K(T)
2130     else
2131       return ntGenAlg;          /// K(T')   --> K'(T)
2132  }
2133
2134  return NULL;                                 /// default
2135}
2136#if 0
2137nMapFunc ntSetMap_T(const coeffs src, const coeffs dst)
2138{
2139  nMapFunc n=ntSetMap(src,dst);
2140  if (n==ntCopyAlg) printf("n=ntCopyAlg\n");
2141  else if (n==ntCopyMap) printf("n=ntCopyMap\n");
2142  else if (n==ntMapUP) printf("n=ntMapUP\n");
2143  else if (n==ntMap0P) printf("n=ntMap0P\n");
2144  else if (n==ntMapP0) printf("n=ntMapP0\n");
2145  else if (n==ntMap00) printf("n=ntMap00\n");
2146  else if (n==NULL) printf("n=NULL\n");
2147  else printf("n=?\n");
2148  return n;
2149}
2150#endif
2151
2152static void ntKillChar(coeffs cf)
2153{
2154  if ((--cf->extRing->ref) == 0)
2155    rDelete(cf->extRing);
2156}
2157static number ntConvFactoryNSingN( const CanonicalForm n, const coeffs cf)
2158{
2159  if (n.isZero()) return NULL;
2160  poly p=convFactoryPSingP(n,ntRing);
2161  p_Normalize(p,ntRing);
2162  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
2163  NUM(result) = p;
2164  //DEN(result) = NULL; // done by omAlloc0Bin
2165  //COM(result) = 0; // done by omAlloc0Bin
2166  ntTest((number)result);
2167  return (number)result;
2168}
2169static CanonicalForm ntConvSingNFactoryN( number n, BOOLEAN /*setChar*/, const coeffs cf )
2170{
2171  ntTest(n);
2172  if (IS0(n)) return CanonicalForm(0);
2173
2174  fraction f = (fraction)n;
2175  return convSingPFactoryP(NUM(f),ntRing);
2176}
2177
2178static int ntParDeg(number a, const coeffs cf)
2179{
2180  ntTest(a);
2181  if (IS0(a)) return -1;
2182  fraction fa = (fraction)a;
2183  return cf->extRing->pFDeg(NUM(fa),cf->extRing);
2184}
2185
2186/// return the specified parameter as a number in the given trans.ext.
2187static number ntParameter(const int iParameter, const coeffs cf)
2188{
2189  assume(getCoeffType(cf) == n_transExt);
2190
2191  const ring R = cf->extRing;
2192  assume( R != NULL );
2193  assume( 0 < iParameter && iParameter <= rVar(R) );
2194
2195  poly p = p_One(R); p_SetExp(p, iParameter, 1, R); p_Setm(p, R);
2196  p_Test(p,R);
2197
2198  fraction f = (fraction)omAlloc0Bin(fractionObjectBin);
2199  NUM(f) = p;
2200  //DEN(f) = NULL;
2201  //COM(f) = 0;
2202
2203  ntTest((number)f);
2204
2205  return (number)f;
2206}
2207
2208/// if m == var(i)/1 => return i,
2209int ntIsParam(number m, const coeffs cf)
2210{
2211  ntTest(m);
2212  assume(getCoeffType(cf) == n_transExt);
2213
2214  const ring R = cf->extRing;
2215  assume( R != NULL );
2216
2217  fraction f = (fraction)m;
2218
2219  if( DEN(f) != NULL )
2220    return 0;
2221
2222  return p_Var( NUM(f), R );
2223}
2224
2225struct NTNumConverter
2226{
2227  static inline poly convert(const number& n)
2228  {
2229    // suitable for trans. ext. numbers that are fractions of polys
2230    return NUM((fraction)n); // return the numerator
2231  }
2232};
2233
2234
2235static void ntClearContent(ICoeffsEnumerator& numberCollectionEnumerator, number& c, const coeffs cf)
2236{
2237  assume(cf != NULL);
2238  assume(getCoeffType(cf) == n_transExt);
2239  // all coeffs are given by fractions of polynomails over integers!!!
2240  // without denominators!!!
2241
2242  const ring   R = cf->extRing;
2243  assume(R != NULL);
2244  const coeffs Q = R->cf;
2245  assume(Q != NULL);
2246  assume(nCoeff_is_Q(Q));
2247
2248
2249  numberCollectionEnumerator.Reset();
2250
2251  if( !numberCollectionEnumerator.MoveNext() ) // empty zero polynomial?
2252  {
2253    c = ntInit(1, cf);
2254    return;
2255  }
2256
2257  // all coeffs are given by integers after returning from this routine
2258
2259  // part 1, collect product of all denominators /gcds
2260  poly cand = NULL;
2261
2262  do
2263  {
2264    number &n = numberCollectionEnumerator.Current();
2265
2266    ntNormalize(n, cf);
2267
2268    fraction f = (fraction)n;
2269
2270    assume( f != NULL );
2271
2272    const poly den = DEN(f);
2273
2274    assume( den == NULL ); // ?? / 1 ?
2275
2276    const poly num = NUM(f);
2277
2278    if( cand == NULL )
2279      cand = p_Copy(num, R);
2280    else
2281    {
2282      poly tmp = singclap_gcd_r(cand, num, R); // gcd(cand, num)
2283      p_Delete(&cand,R);
2284      cand=tmp;
2285    }
2286
2287    if( p_IsConstant(cand, R) )
2288      break;
2289  }
2290  while( numberCollectionEnumerator.MoveNext() ) ;
2291
2292
2293  // part2: all coeffs = all coeffs * cand
2294  if( cand != NULL )
2295  {
2296  if( !p_IsConstant(cand, R) )
2297  {
2298    c = ntInit(cand, cf);
2299    numberCollectionEnumerator.Reset();
2300    while (numberCollectionEnumerator.MoveNext() )
2301    {
2302      number &n = numberCollectionEnumerator.Current();
2303      const number t = ntDiv(n, c, cf); // TODO: rewrite!?
2304      ntDelete(&n, cf);
2305      n = t;
2306    }
2307  } // else NUM (result) = p_One(R);
2308  else { p_Delete(&cand, R); cand = NULL; }
2309  }
2310
2311  // Quick and dirty fix for constant content clearing: consider numerators???
2312  CRecursivePolyCoeffsEnumerator<NTNumConverter> itr(numberCollectionEnumerator); // recursively treat the NUM(numbers) as polys!
2313  number cc;
2314
2315  n_ClearContent(itr, cc, Q);
2316  number g = ntInit(p_NSet(cc, R), cf);
2317
2318  if( cand != NULL )
2319  {
2320    number gg = ntMult(g, c, cf);
2321    ntDelete(&g, cf);
2322    ntDelete(&c, cf); c = gg;
2323  } else
2324    c = g;
2325  ntTest(c);
2326}
2327
2328static void ntClearDenominators(ICoeffsEnumerator& numberCollectionEnumerator, number& c, const coeffs cf)
2329{
2330  assume(cf != NULL);
2331  assume(getCoeffType(cf) == n_transExt); // both over Q(a) and Zp(a)!
2332  // all coeffs are given by fractions of polynomails over integers!!!
2333
2334  numberCollectionEnumerator.Reset();
2335
2336  if( !numberCollectionEnumerator.MoveNext() ) // empty zero polynomial?
2337  {
2338    c = ntInit(1, cf);
2339    return;
2340  }
2341
2342  // all coeffs are given by integers after returning from this routine
2343
2344  // part 1, collect product of all denominators /gcds
2345  poly cand = NULL;
2346
2347  const ring R = cf->extRing;
2348  assume(R != NULL);
2349
2350  const coeffs Q = R->cf;
2351  assume(Q != NULL);
2352//  assume(nCoeff_is_Q(Q));
2353
2354  do
2355  {
2356    number &n = numberCollectionEnumerator.Current();
2357
2358    ntNormalize(n, cf);
2359
2360    fraction f = (fraction)ntGetDenom (n, cf);
2361
2362    assume( f != NULL );
2363
2364    const poly den = NUM(f);
2365
2366    if( den == NULL ) // ?? / 1 ?
2367      continue;
2368
2369    if( cand == NULL )
2370      cand = p_Copy(den, R);
2371    else
2372    {
2373      // cand === LCM( cand, den )!!!!
2374      // NOTE: maybe it's better to make the product and clearcontent afterwards!?
2375      // TODO: move the following to factory?
2376      poly gcd = singclap_gcd_r(cand, den, R); // gcd(cand, den) is monic no mater leading coeffs! :((((
2377      if (nCoeff_is_Q (Q))
2378      {
2379        number LcGcd= n_SubringGcd (p_GetCoeff (cand, R), p_GetCoeff(den, R), Q);
2380        gcd = __p_Mult_nn(gcd, LcGcd, R);
2381        n_Delete(&LcGcd,Q);
2382      }
2383//      assume( n_IsOne(pGetCoeff(gcd), Q) ); // TODO: this may be wrong...
2384      cand = p_Mult_q(cand, p_Copy(den, R), R); // cand *= den
2385      const poly t = singclap_pdivide( cand, gcd, R ); // cand' * den / gcd(cand', den)
2386      p_Delete(&cand, R);
2387      p_Delete(&gcd, R);
2388      cand = t;
2389    }
2390  }
2391  while( numberCollectionEnumerator.MoveNext() );
2392
2393  if( cand == NULL )
2394  {
2395    c = ntInit(1, cf);
2396    return;
2397  }
2398
2399  c = ntInit(cand, cf);
2400
2401  numberCollectionEnumerator.Reset();
2402
2403  number d = NULL;
2404
2405  while (numberCollectionEnumerator.MoveNext() )
2406  {
2407    number &n = numberCollectionEnumerator.Current();
2408    number t = ntMult(n, c, cf); // TODO: rewrite!?
2409    ntDelete(&n, cf);
2410
2411    ntNormalize(t, cf); // TODO: needed?
2412    n = t;
2413
2414    fraction f = (fraction)t;
2415    assume( f != NULL );
2416
2417    const poly den = DEN(f);
2418
2419    if( den != NULL ) // ?? / ?? ?
2420    {
2421      assume( p_IsConstant(den, R) );
2422      assume( pNext(den) == NULL );
2423
2424      if( d == NULL )
2425        d = n_Copy(pGetCoeff(den), Q);
2426      else
2427      {
2428        number g = n_NormalizeHelper(d, pGetCoeff(den), Q);
2429        n_Delete(&d, Q); d = g;
2430      }
2431    }
2432  }
2433
2434  if( d != NULL )
2435  {
2436    numberCollectionEnumerator.Reset();
2437    while (numberCollectionEnumerator.MoveNext() )
2438    {
2439      number &n = numberCollectionEnumerator.Current();
2440      fraction f = (fraction)n;
2441
2442      assume( f != NULL );
2443
2444      const poly den = DEN(f);
2445
2446      if( den == NULL ) // ?? / 1 ?
2447        NUM(f) = __p_Mult_nn(NUM(f), d, R);
2448      else
2449      {
2450        assume( p_IsConstant(den, R) );
2451        assume( pNext(den) == NULL );
2452
2453        number ddd = n_Div(d, pGetCoeff(den), Q); // but be an integer now!!!
2454        NUM(f) = __p_Mult_nn(NUM(f), ddd, R);
2455        n_Delete(&ddd, Q);
2456
2457        p_Delete(&DEN(f), R);
2458        DEN(f) = NULL; // TODO: check if this is needed!?
2459      }
2460
2461      assume( DEN(f) == NULL );
2462    }
2463
2464    NUM((fraction)c) = __p_Mult_nn(NUM((fraction)c), d, R);
2465    n_Delete(&d, Q);
2466  }
2467
2468
2469  ntTest(c);
2470}
2471
2472static number ntChineseRemainder(number *x, number *q,int rl, BOOLEAN /*sym*/,CFArray &inv_cache,const coeffs cf)
2473{
2474  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
2475
2476  poly *P=(poly*)omAlloc(rl*sizeof(poly*));
2477  number *X=(number *)omAlloc(rl*sizeof(number));
2478
2479  int i;
2480
2481  for(i=0;i<rl;i++) P[i]=p_Copy(NUM((fraction)(x[i])),cf->extRing);
2482  NUM(result)=p_ChineseRemainder(P,X,q,rl,inv_cache,cf->extRing);
2483
2484  for(i=0;i<rl;i++)
2485  {
2486    P[i]=p_Copy(DEN((fraction)(x[i])),cf->extRing);
2487    if (P[i]==NULL) P[i]=p_One(cf->extRing);
2488  }
2489  DEN(result)=p_ChineseRemainder(P,X,q,rl,inv_cache,cf->extRing);
2490
2491  omFreeSize(X,rl*sizeof(number));
2492  omFreeSize(P,rl*sizeof(poly*));
2493  if (p_IsConstant(DEN(result), ntRing)
2494  && n_IsOne(pGetCoeff(DEN(result)), ntCoeffs))
2495  {
2496    p_Delete(&DEN(result),ntRing);
2497  }
2498  ntTest((number)result);
2499  return ((number)result);
2500}
2501
2502static number ntFarey(number p, number n, const coeffs cf)
2503{
2504  // n is really a bigint
2505  fraction result = (fraction)omAlloc0Bin(fractionObjectBin);
2506  NUM(result)=p_Farey(p_Copy(NUM((fraction)p),cf->extRing),n,cf->extRing);
2507  DEN(result)=p_Farey(p_Copy(DEN((fraction)p),cf->extRing),n,cf->extRing);
2508  ntTest((number)result);
2509  return ((number)result);
2510}
2511
2512BOOLEAN ntInitChar(coeffs cf, void * infoStruct)
2513{
2514
2515  assume( infoStruct != NULL );
2516
2517  TransExtInfo *e = (TransExtInfo *)infoStruct;
2518
2519  assume( e->r                != NULL);      // extRing;
2520  assume( e->r->cf            != NULL);      // extRing->cf;
2521  assume( e->r->qideal == NULL );
2522
2523  assume( cf != NULL );
2524  assume(getCoeffType(cf) == n_transExt);                // coeff type;
2525
2526  ring R = e->r;
2527  assume(R != NULL);
2528
2529  R->ref ++; // increase the ref.counter for the ground poly. ring!
2530
2531  cf->extRing           = R;
2532  /* propagate characteristic up so that it becomes
2533     directly accessible in cf: */
2534  cf->ch = R->cf->ch;
2535
2536  cf->is_field=TRUE;
2537  cf->is_domain=TRUE;
2538  cf->rep=n_rep_rat_fct;
2539
2540  cf->factoryVarOffset = R->cf->factoryVarOffset + rVar(R);
2541
2542  cf->cfCoeffString = naCoeffString; // FIXME? TODO? // extern char* naCoeffString(const coeffs r);
2543  cf->cfCoeffName =  naCoeffName; // FIXME? TODO? // extern char* naCoeffString(const coeffs r);
2544
2545  cf->cfGreaterZero  = ntGreaterZero;
2546  cf->cfGreater      = ntGreater;
2547  cf->cfEqual        = ntEqual;
2548  cf->cfIsZero       = ntIsZero;
2549  cf->cfIsOne        = ntIsOne;
2550  cf->cfIsMOne       = ntIsMOne;
2551  cf->cfInit         = ntInit;
2552  cf->cfFarey        = ntFarey;
2553  cf->cfChineseRemainder = ntChineseRemainder;
2554  cf->cfInt          = ntInt;
2555  cf->cfAdd          = ntAdd;
2556  cf->cfInpNeg       = ntNeg;
2557  cf->cfSub          = ntSub;
2558  cf->cfMult         = ntMult;
2559  cf->cfDiv          = ntDiv;
2560  cf->cfExactDiv     = ntDiv;
2561  cf->cfPower        = ntPower;
2562  cf->cfCopy         = ntCopy;
2563  cf->cfWriteLong    = ntWriteLong;
2564  cf->cfRead         = ntRead;
2565  cf->cfNormalize    = ntNormalize;
2566  cf->cfDelete       = ntDelete;
2567  cf->cfSetMap       = ntSetMap;
2568  cf->cfGetDenom     = ntGetDenom;
2569  cf->cfGetNumerator = ntGetNumerator;
2570  //cf->cfRePart       = ntCopy;
2571  //cf->cfImPart       = ntImPart;
2572  cf->cfCoeffWrite   = ntCoeffWrite;
2573#ifdef LDEBUG
2574  cf->cfDBTest       = ntDBTest;
2575#endif
2576  //cf->cfGcd          = ntGcd_dummy;
2577  cf->cfSubringGcd   = ntGcd;
2578  cf->cfNormalizeHelper = ntNormalizeHelper;
2579  cf->cfSize         = ntSize;
2580  cf->nCoeffIsEqual  = ntCoeffIsEqual;
2581  cf->cfInvers       = ntInvers;
2582  cf->cfKillChar     = ntKillChar;
2583
2584  if( rCanShortOut(ntRing) )
2585    cf->cfWriteShort = ntWriteShort;
2586  else
2587    cf->cfWriteShort = ntWriteLong;
2588
2589  cf->convFactoryNSingN =ntConvFactoryNSingN;
2590  cf->convSingNFactoryN =ntConvSingNFactoryN;
2591  cf->cfParDeg = ntParDeg;
2592
2593  cf->iNumberOfParameters = rVar(R);
2594  cf->pParameterNames = (const char**)R->names;
2595  cf->cfParameter = ntParameter;
2596  cf->has_simple_Inverse= FALSE;
2597  /* cf->has_simple_Alloc= FALSE; */
2598
2599
2600  if( nCoeff_is_Q(R->cf) )
2601    cf->cfClearContent = ntClearContent;
2602
2603  cf->cfClearDenominators = ntClearDenominators;
2604
2605  return FALSE;
2606}
2607
2608template class CRecursivePolyCoeffsEnumerator<NTNumConverter>;
2609template class IEnumerator<snumber*>;
Note: See TracBrowser for help on using the repository browser.