# source:git/libpolys/polys/ext_fields/algext.cc@0afa07

spielwiese
Last change on this file since 0afa07 was 0afa07, checked in by Frank Seelisch <seelisch@…>, 12 years ago
removed cfPar, cfParDeg, n_Par, n_ParDeg, ndPar, ndParDeg from coeffs
• Property mode set to `100644`
File size: 20.4 KB
Line
1/****************************************
2*  Computer Algebra System SINGULAR     *
3****************************************/
4/* \$Id\$ */
5/*
6* ABSTRACT: numbers in an algebraic extension field K[a] / < f(a) >
7*           Assuming that we have a coeffs object cf, then these numbers
8*           are polynomials in the polynomial ring K[a] represented by
9*           cf->extRing.
10*           IMPORTANT ASSUMPTIONS:
11*           1.) So far we assume that cf->extRing is a valid polynomial
12*               ring in exactly one variable, i.e., K[a], where K is allowed
13*               to be any field (representable in SINGULAR and which may
14*               itself be some extension field, thus allowing for extension
15*               towers).
16*           2.) Moreover, this implementation assumes that
17*               cf->extRing->minideal is not NULL but an ideal with at
18*               least one non-zero generator which may be accessed by
19*               cf->extRing->minideal->m and which represents the minimal
20*               polynomial f(a) of the extension variable 'a' in K[a].
21*           3.) As soon as an std method for polynomial rings becomes
22*               availabe, all reduction steps modulo f(a) should be replaced
23*               by a call to std. Moreover, in this situation one can finally
24*               move from K[a] / < f(a) > to
25*                  K[a_1, ..., a_s] / I, with I some zero-dimensional ideal
26*                                        in K[a_1, ..., a_s] given by a lex
27*                                        GrÃ¶bner basis.
28*               The code in algext.h and algext.cc is then capable of
29*               computing in K[a_1, ..., a_s] / I.
30*/
31
32#include "config.h"
33#include <misc/auxiliary.h>
34
35#include <omalloc/omalloc.h>
36
37#include <reporter/reporter.h>
38
39#include <coeffs/coeffs.h>
40#include <coeffs/numbers.h>
41#include <coeffs/longrat.h>
42
43#include <polys/monomials/ring.h>
44#include <polys/monomials/p_polys.h>
45#include <polys/simpleideals.h>
46
47#include <polys/ext_fields/algext.h>
48
49/// our type has been defined as a macro in algext.h
50/// and is accessible by 'naID'
51
52/// forward declarations
53BOOLEAN  naGreaterZero(number a, const coeffs cf);
54BOOLEAN  naGreater(number a, number b, const coeffs cf);
55BOOLEAN  naEqual(number a, number b, const coeffs cf);
56BOOLEAN  naIsOne(number a, const coeffs cf);
57BOOLEAN  naIsMOne(number a, const coeffs cf);
58BOOLEAN  naIsZero(number a, const coeffs cf);
59number   naInit(int i, const coeffs cf);
60int      naInt(number &a, const coeffs cf);
61number   naNeg(number a, const coeffs cf);
62number   naInvers(number a, const coeffs cf);
63number   naAdd(number a, number b, const coeffs cf);
64number   naSub(number a, number b, const coeffs cf);
65number   naMult(number a, number b, const coeffs cf);
66number   naDiv(number a, number b, const coeffs cf);
67void     naPower(number a, int exp, number *b, const coeffs cf);
68number   naCopy(number a, const coeffs cf);
69void     naWrite(number &a, const coeffs cf);
70number   naRePart(number a, const coeffs cf);
71number   naImPart(number a, const coeffs cf);
72number   naGetDenom(number &a, const coeffs cf);
73number   naGetNumerator(number &a, const coeffs cf);
74number   naGcd(number a, number b, const coeffs cf);
75number   naLcm(number a, number b, const coeffs cf);
76int      naSize(number a, const coeffs cf);
77void     naDelete(number *a, const coeffs cf);
78void     naCoeffWrite(const coeffs cf);
79number   naIntDiv(number a, number b, const coeffs cf);
80const char * naRead(const char *s, number *a, const coeffs cf);
81static BOOLEAN naCoeffIsEqual(const coeffs cf, n_coeffType n, void * param);
82
83#ifdef LDEBUG
84BOOLEAN naDBTest(number a, const char *f, const int l, const coeffs cf)
85{
86  assume(getCoeffType(cf) == naID);
87  if (a == NULL) return TRUE;
88  p_Test((poly)a, naRing);
89  assume(p_Totaldegree((poly)a, naRing) <= p_Totaldegree(naMinpoly, naRing));
90  return TRUE;
91}
92#endif
93
94void heuristicReduce(poly &p, poly reducer, const coeffs cf);
95void definiteReduce(poly &p, poly reducer, const coeffs cf);
96
97/* returns the bottom field in this field extension tower; if the tower
98   is flat, i.e., if there is no extension, then r itself is returned;
99   as a side-effect, the counter 'height' is filled with the height of
100   the extension tower (in case the tower is flat, 'height' is zero) */
101static coeffs nCoeff_bottom(const coeffs r, int &height)
102{
103  assume(r != NULL);
104  coeffs cf = r;
105  height = 0;
106  while (nCoeff_is_Extension(cf))
107  {
108    assume(cf->extRing != NULL); assume(cf->extRing->cf != NULL);
109    cf = cf->extRing->cf;
110    height++;
111  }
112  return cf;
113}
114
115BOOLEAN naIsZero(number a, const coeffs cf)
116{
117  naTest(a);
118  return (a == NULL);
119}
120
121void naDelete(number * a, const coeffs cf)
122{
123  if (*a == NULL) return;
124  poly aAsPoly = (poly)(*a);
125  p_Delete(&aAsPoly, naRing);
126  *a = NULL;
127}
128
129BOOLEAN naEqual(number a, number b, const coeffs cf)
130{
131  naTest(a); naTest(b);
132
133  /// simple tests
134  if (a == b) return TRUE;
135  if ((a == NULL) && (b != NULL)) return FALSE;
136  if ((b == NULL) && (a != NULL)) return FALSE;
137
138  /// deg test
140  if (a != NULL) aDeg = p_Totaldegree((poly)a, naRing);
141  int bDeg = 0;
142  if (b != NULL) bDeg = p_Totaldegree((poly)b, naRing);
143  if (aDeg != bDeg) return FALSE;
144
145  /// subtraction test
146  number c = naSub(a, b, cf);
147  BOOLEAN result = naIsZero(c, cf);
149  return result;
150}
151
152number naCopy(number a, const coeffs cf)
153{
154  naTest(a);
155  if (a == NULL) return NULL;
156  return (number)p_Copy((poly)a, naRing);
157}
158
159number naGetNumerator(number &a, const coeffs cf)
160{
161  return naCopy(a, cf);
162}
163
164number naGetDenom(number &a, const coeffs cf)
165{
166  naTest(a);
167  return naInit(1, cf);
168}
169
170BOOLEAN naIsOne(number a, const coeffs cf)
171{
172  naTest(a);
173  poly aAsPoly = (poly)a;
174  if (!p_IsConstant(aAsPoly, naRing)) return FALSE;
175  return n_IsOne(p_GetCoeff(aAsPoly, naRing), naCoeffs);
176}
177
178BOOLEAN naIsMOne(number a, const coeffs cf)
179{
180  naTest(a);
181  poly aAsPoly = (poly)a;
182  if (!p_IsConstant(aAsPoly, naRing)) return FALSE;
183  return n_IsMOne(p_GetCoeff(aAsPoly, naRing), naCoeffs);
184}
185
186/// this is in-place, modifies a
187number naNeg(number a, const coeffs cf)
188{
189  naTest(a);
190  if (a != NULL) a = (number)p_Neg((poly)a, naRing);
191  return a;
192}
193
194number naImPart(number a, const coeffs cf)
195{
196  naTest(a);
197  return NULL;
198}
199
200number naInit(int i, const coeffs cf)
201{
202  if (i == 0) return NULL;
203  else        return (number)p_ISet(i, naRing);
204}
205
206int naInt(number &a, const coeffs cf)
207{
208  naTest(a);
209  poly aAsPoly = (poly)a;
210  if (!p_IsConstant(aAsPoly, naRing)) return 0;
211  return n_Int(p_GetCoeff(aAsPoly, naRing), naCoeffs);
212}
213
214/* TRUE iff (a != 0 and (b == 0 or deg(a) > deg(b))) */
215BOOLEAN naGreater(number a, number b, const coeffs cf)
216{
217  naTest(a); naTest(b);
218  if (naIsZero(a, cf)) return FALSE;
219  if (naIsZero(b, cf)) return TRUE;
221  if (a != NULL) aDeg = p_Totaldegree((poly)a, naRing);
222  int bDeg = 0;
223  if (b != NULL) bDeg = p_Totaldegree((poly)b, naRing);
225}
226
227/* TRUE iff a != 0 and (LC(a) > 0 or deg(a) > 0) */
228BOOLEAN naGreaterZero(number a, const coeffs cf)
229{
230  naTest(a);
231  if (a == NULL)                                            return FALSE;
232  if (n_GreaterZero(p_GetCoeff((poly)a, naRing), naCoeffs)) return TRUE;
233  if (p_Totaldegree((poly)a, naRing) > 0)                   return TRUE;
234  return FALSE;
235}
236
237void naCoeffWrite(const coeffs cf)
238{
239  char *x = rRingVar(0, naRing);
240  Print("//   Coefficients live in the extension field K[%s]/<f(%s)>\n", x, x);
241  Print("//   with the minimal polynomial f(%s) = %s\n", x,
242        p_String(naMinpoly, naRing));
243  PrintS("//   and K: "); n_CoeffWrite(cf->extRing->cf);
244}
245
246number naAdd(number a, number b, const coeffs cf)
247{
248  naTest(a); naTest(b);
249  if (a == NULL) return naCopy(b, cf);
250  if (b == NULL) return naCopy(a, cf);
251  poly aPlusB = p_Add_q(p_Copy((poly)a, naRing),
252                        p_Copy((poly)b, naRing), naRing);
253  definiteReduce(aPlusB, naMinpoly, cf);
254  return (number)aPlusB;
255}
256
257number naSub(number a, number b, const coeffs cf)
258{
259  naTest(a); naTest(b);
260  if (b == NULL) return naCopy(a, cf);
261  poly minusB = p_Neg(p_Copy((poly)b, naRing), naRing);
262  if (a == NULL) return (number)minusB;
263  poly aMinusB = p_Add_q(p_Copy((poly)a, naRing), minusB, naRing);
264  definiteReduce(aMinusB, naMinpoly, cf);
265  return (number)aMinusB;
266}
267
268number naMult(number a, number b, const coeffs cf)
269{
270  naTest(a); naTest(b);
271  if (a == NULL) return NULL;
272  if (b == NULL) return NULL;
273  poly aTimesB = p_Mult_q(p_Copy((poly)a, naRing),
274                          p_Copy((poly)b, naRing), naRing);
275  definiteReduce(aTimesB, naMinpoly, cf);
276  return (number)aTimesB;
277}
278
279number naDiv(number a, number b, const coeffs cf)
280{
281  naTest(a); naTest(b);
282  if (b == NULL) WerrorS(nDivBy0);
283  if (a == NULL) return NULL;
284  poly bInverse = (poly)naInvers(b, cf);
285  poly aDivB = p_Mult_q(p_Copy((poly)a, naRing), bInverse, naRing);
288}
289
290/* 0^0 = 0;
291   for |exp| <= 7 compute power by a simple multiplication loop;
292   for |exp| >= 8 compute power along binary presentation of |exp|, e.g.
293      p^13 = p^1 * p^4 * p^8, where we utilise that
294      p^(2^(k+1)) = p^(2^k) * p^(2^k);
295   intermediate reduction modulo the minimal polynomial is controlled by
296   the in-place method heuristicReduce(poly, poly, coeffs); see there.
297*/
298void naPower(number a, int exp, number *b, const coeffs cf)
299{
300  naTest(a);
301
302  /* special cases first */
303  if (a == NULL)
304  {
305    if (exp >= 0) *b = NULL;
306    else          WerrorS(nDivBy0);
307  }
308  else if (exp ==  0) *b = naInit(1, cf);
309  else if (exp ==  1) *b = naCopy(a, cf);
310  else if (exp == -1) *b = naInvers(a, cf);
311
312  int expAbs = exp; if (expAbs < 0) expAbs = -expAbs;
313
314  /* now compute a^expAbs */
315  poly pow; poly aAsPoly = (poly)a;
316  if (expAbs <= 7)
317  {
318    pow = p_Copy(aAsPoly, naRing);
319    for (int i = 2; i <= expAbs; i++)
320    {
321      pow = p_Mult_q(pow, p_Copy(aAsPoly, naRing), naRing);
322      heuristicReduce(pow, naMinpoly, cf);
323    }
324    definiteReduce(pow, naMinpoly, cf);
325  }
326  else
327  {
328    pow = p_ISet(1, naRing);
329    poly factor = p_Copy(aAsPoly, naRing);
330    while (expAbs != 0)
331    {
332      if (expAbs & 1)
333      {
334        pow = p_Mult_q(pow, p_Copy(factor, naRing), naRing);
335        heuristicReduce(pow, naMinpoly, cf);
336      }
337      expAbs = expAbs / 2;
338      if (expAbs != 0)
339      {
340        factor = p_Mult_q(factor, factor, naRing);
341        heuristicReduce(factor, naMinpoly, cf);
342      }
343    }
344    p_Delete(&factor, naRing);
345    definiteReduce(pow, naMinpoly, cf);
346  }
347
348  /* invert if original exponent was negative */
349  number n = (number)pow;
350  if (exp < 0)
351  {
352    number m = naInvers(n, cf);
354    n = m;
355  }
356  *b = n;
357}
358
359/* may reduce p modulo the reducer by calling definiteReduce;
360   the decision is made based on the following heuristic
361   (which should also only be changed here in this method):
362      if (deg(p) > 10*deg(reducer) then perform reduction;
363   modifies p */
364void heuristicReduce(poly &p, poly reducer, const coeffs cf)
365{
366  #ifdef LDEBUG
367  p_Test((poly)p, naRing);
368  p_Test((poly)reducer, naRing);
369  #endif
370  if (p_Totaldegree(p, naRing) > 10 * p_Totaldegree(reducer, naRing))
371    definiteReduce(p, reducer, cf);
372}
373
374void naWrite(number &a, const coeffs cf)
375{
376  naTest(a);
377  if (a == NULL)
378    StringAppendS("0");
379  else
380  {
381    poly aAsPoly = (poly)a;
382    /* basically, just write aAsPoly using p_Write,
383       but use brackets around the output, if a is not
384       a constant living in naCoeffs = cf->extRing->cf */
385    BOOLEAN useBrackets = !(p_IsConstant(aAsPoly, naRing));
386    if (useBrackets) StringAppendS("(");
387    p_String0(aAsPoly, naRing, naRing);
388    if (useBrackets) StringAppendS(")");
389  }
390}
391
392const char * naRead(const char *s, number *a, const coeffs cf)
393{
394  poly aAsPoly;
395  const char * result = p_Read(s, aAsPoly, naRing);
396  *a = (number)aAsPoly;
397  return result;
398}
399
400/* implemented by the rule lcm(a, b) = a * b / gcd(a, b) */
401number naLcm(number a, number b, const coeffs cf)
402{
403  naTest(a); naTest(b);
404  if (a == NULL) return NULL;
405  if (b == NULL) return NULL;
406  number theProduct = (number)p_Mult_q(p_Copy((poly)a, naRing),
407                                       p_Copy((poly)b, naRing), naRing);
408  /* note that theProduct needs not be reduced w.r.t. naMinpoly;
409     but the final division will take care of the necessary reduction */
410  number theGcd = naGcd(a, b, cf);
412}
413
414/* expects *param to be castable to AlgExtInfo */
415static BOOLEAN naCoeffIsEqual(const coeffs cf, n_coeffType n, void * param)
416{
417  if (naID != n) return FALSE;
418  AlgExtInfo *e = (AlgExtInfo *)param;
419  /* for extension coefficient fields we expect the underlying
420     polynomial rings to be IDENTICAL, i.e. the SAME OBJECT;
421     this expectation is based on the assumption that we have properly
422     registered cf and perform reference counting rather than creating
423     multiple copies of the same coefficient field/domain/ring */
424  return (naRing == e->r);
425  /* (Note that then also the minimal ideals will necessarily be
426     the same, as they are attached to the ring.) */
427}
428
429int naSize(number a, const coeffs cf)
430{
431  if (a == NULL) return -1;
432  /* this has been taken from the old implementation of field extensions,
433     where we computed the sum of the degree and the number of terms in
434     (poly)a; so we leave it at that, for the time being;
435     maybe, the number of terms alone is a better measure? */
436  poly aAsPoly = (poly)a;
437  int theDegree = 0; int noOfTerms = 0;
438  while (aAsPoly != NULL)
439  {
440    noOfTerms++;
441    int d = p_GetExp(aAsPoly, 1, naRing);
442    if (d > theDegree) theDegree = d;
443    pIter(aAsPoly);
444  }
445  return theDegree + noOfTerms;
446}
447
448/* performs polynomial division and overrides p by the remainder
449   of division of p by the reducer;
450   modifies p */
451void definiteReduce(poly &p, poly reducer, const coeffs cf)
452{
453  #ifdef LDEBUG
454  p_Test((poly)p, naRing);
455  p_Test((poly)reducer, naRing);
456  #endif
457  p_PolyDiv(p, reducer, FALSE, naRing);
458}
459
460/* IMPORTANT NOTE: Since an algebraic field extension is again a field,
461                   the gcd of two elements is not very interesting. (It
462                   is actually any unit in the field, i.e., any non-
463                   zero element.) Note that the below method does not operate
464                   in this strong sense but rather computes the gcd of
465                   two given elements in the underlying polynomial ring. */
466number naGcd(number a, number b, const coeffs cf)
467{
468  naTest(a); naTest(b);
469  if ((a == NULL) && (b == NULL)) WerrorS(nDivBy0);
470  return (number)p_Gcd((poly)a, (poly)b, naRing);
471}
472
473number naInvers(number a, const coeffs cf)
474{
475  naTest(a);
476  if (a == NULL) WerrorS(nDivBy0);
477  poly aFactor = NULL; poly mFactor = NULL;
478  poly theGcd = p_ExtGcd((poly)a, aFactor, naMinpoly, mFactor, naRing);
479  naTest((number)theGcd); naTest((number)aFactor); naTest((number)mFactor);
480  /* the gcd must be 1 since naMinpoly is irreducible and a != NULL: */
481  assume(naIsOne((number)theGcd, cf));
482  p_Delete(&theGcd, naRing);
483  p_Delete(&mFactor, naRing);
484  return (number)(aFactor);
485}
486
487/* assumes that src = Q, dst = Q(a) */
488number naMap00(number a, const coeffs src, const coeffs dst)
489{
490  if (n_IsZero(a, src)) return NULL;
491  assume(src == dst->extRing->cf);
492  poly result = p_One(dst->extRing);
493  p_SetCoeff(result, naCopy(a, src), dst->extRing);
494  return (number)result;
495}
496
497/* assumes that src = Z/p, dst = Q(a) */
498number naMapP0(number a, const coeffs src, const coeffs dst)
499{
500  if (n_IsZero(a, src)) return NULL;
501  /* mapping via intermediate int: */
502  int n = n_Int(a, src);
503  number q = n_Init(n, dst->extRing->cf);
504  poly result = p_One(dst->extRing);
505  p_SetCoeff(result, q, dst->extRing);
506  return (number)result;
507}
508
509/* assumes that either src = Q(a), dst = Q(a), or
510                       src = Z/p(a), dst = Z/p(a)     */
511number naCopyMap(number a, const coeffs src, const coeffs dst)
512{
513  return naCopy(a, dst);
514}
515
516/* assumes that src = Q, dst = Z/p(a) */
517number naMap0P(number a, const coeffs src, const coeffs dst)
518{
519  if (n_IsZero(a, src)) return NULL;
520  int p = rChar(dst->extRing);
521  int n = nlModP(a, p, src);
522  number q = n_Init(n, dst->extRing->cf);
523  poly result = p_One(dst->extRing);
524  p_SetCoeff(result, q, dst->extRing);
525  return (number)result;
526}
527
528/* assumes that src = Z/p, dst = Z/p(a) */
529number naMapPP(number a, const coeffs src, const coeffs dst)
530{
531  if (n_IsZero(a, src)) return NULL;
532  assume(src == dst->extRing->cf);
533  poly result = p_One(dst->extRing);
534  p_SetCoeff(result, naCopy(a, src), dst->extRing);
535  return (number)result;
536}
537
538/* assumes that src = Z/u, dst = Z/p(a), where u != p */
539number naMapUP(number a, const coeffs src, const coeffs dst)
540{
541  if (n_IsZero(a, src)) return NULL;
542  /* mapping via intermediate int: */
543  int n = n_Int(a, src);
544  number q = n_Init(n, dst->extRing->cf);
545  poly result = p_One(dst->extRing);
546  p_SetCoeff(result, q, dst->extRing);
547  return (number)result;
548}
549
550nMapFunc naSetMap(const coeffs src, const coeffs dst)
551{
552  /* dst is expected to be an algebraic field extension */
553  assume(getCoeffType(dst) == naID);
554
555  int h = 0; /* the height of the extension tower given by dst */
556  coeffs bDst = nCoeff_bottom(dst, h); /* the bottom field in the tower dst */
557
558  /* for the time being, we only provide maps if h = 1 and if b is Q or
559     some field Z/pZ: */
560  if (h != 1) return NULL;
561  if ((!nCoeff_is_Zp(bDst)) && (!nCoeff_is_Q(bDst))) return NULL;
562
563  if (nCoeff_is_Q(src) && nCoeff_is_Q(bDst))
564    return naMap00;                                      /// Q     -->  Q(a)
565
566  if (nCoeff_is_Zp(src) && nCoeff_is_Q(bDst))
567    return naMapP0;                                      /// Z/p   -->  Q(a)
568
569  if (nCoeff_is_Q(src) && nCoeff_is_Zp(bDst))
570    return naMap0P;                                      /// Q      --> Z/p(a)
571
572  if (nCoeff_is_Zp(src) && nCoeff_is_Zp(bDst))
573  {
574    if (src->ch == dst->ch) return naMapPP;              /// Z/p    --> Z/p(a)
575    else return naMapUP;                                 /// Z/u    --> Z/p(a)
576  }
577
578  coeffs bSrc = nCoeff_bottom(src, h); /* the bottom field in the tower src */
579  if (h != 1) return NULL;
580  if ((!nCoeff_is_Zp(bSrc)) && (!nCoeff_is_Q(bSrc))) return NULL;
581
582  if (nCoeff_is_Q(bSrc) && nCoeff_is_Q(bDst))
583  {
584    if (strcmp(rRingVar(0, src->extRing),
585               rRingVar(0, dst->extRing)) == 0)
586      return naCopyMap;                                  /// Q(a)   --> Q(a)
587    else
588      return NULL;                                       /// Q(b)   --> Q(a)
589  }
590
591  if (nCoeff_is_Zp(bSrc) && nCoeff_is_Zp(bDst))
592  {
593    if (strcmp(rParameter(src->extRing),
594               rParameter(dst->extRing)) == 0)
595      return naCopyMap;                                  /// Z/p(a) --> Z/p(a)
596    else
597      return NULL;                                       /// Z/p(b) --> Z/p(a)
598  }
599
600  return NULL;                                           /// default
601}
602
603BOOLEAN naInitChar(coeffs cf, void * infoStruct)
604{
605  AlgExtInfo *e = (AlgExtInfo *)infoStruct;
606  /// first check whether cf->extRing != NULL and delete old ring???
607  cf->extRing           = e->r;
608  cf->extRing->minideal = e->i;
609
610  assume(cf->extRing                     != NULL);      // extRing;
611  assume((cf->extRing->minideal          != NULL) &&    // minideal has one
612         (IDELEMS(cf->extRing->minideal) != 0)    &&    // non-zero generator
613         (cf->extRing->minideal->m    != NULL)    ); // at m;
614  assume(cf->extRing->cf                 != NULL);      // extRing->cf;
615  assume(getCoeffType(cf) == naID);                     // coeff type;
616
617  /* propagate characteristic up so that it becomes
618     directly accessible in cf: */
619  cf->ch = cf->extRing->cf->ch;
620
621  #ifdef LDEBUG
622  p_Test((poly)naMinpoly, naRing);
623  #endif
624
625  cf->cfGreaterZero  = naGreaterZero;
626  cf->cfGreater      = naGreater;
627  cf->cfEqual        = naEqual;
628  cf->cfIsZero       = naIsZero;
629  cf->cfIsOne        = naIsOne;
630  cf->cfIsMOne       = naIsMOne;
631  cf->cfInit         = naInit;
632  cf->cfInt          = naInt;
633  cf->cfNeg          = naNeg;
635  cf->cfSub          = naSub;
636  cf->cfMult         = naMult;
639  cf->cfPower        = naPower;
640  cf->cfCopy         = naCopy;
641  cf->cfWrite        = naWrite;
644  cf->cfSetMap       = naSetMap;
645  cf->cfGetDenom     = naGetDenom;
646  cf->cfGetNumerator = naGetNumerator;
647  cf->cfRePart       = naCopy;
648  cf->cfImPart       = naImPart;
649  cf->cfCoeffWrite   = naCoeffWrite;