source: git/Singular/LIB/rootsmr.lib @ 341696

spielwiese
Last change on this file since 341696 was 341696, checked in by Hans Schönemann <hannes@…>, 14 years ago
Adding Id property to all files git-svn-id: file:///usr/local/Singular/svn/trunk@12231 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100644
File size: 19.8 KB
Line 
1// $Id$
2// E. Tobis  12.Nov.2004, April 2004
3// last change 7. May 2005 (G.-M. Greuel)
4///////////////////////////////////////////////////////////////////////////////
5category="Teaching"
6info="
7LIBRARY: rootsmr.lib Counting the number of real roots of polynomial systems
8AUTHOR:               Enrique A. Tobis, etobis@dc.uba.ar
9
10OVERVIEW:  Routines for counting the number of real roots of a multivariate
11           polynomial system. Two methods are implemented: deterministic
12           computation of the number of roots, via the signature of a certain
13           bilinear form (nrRootsDeterm); and a rational univariate projection,
14           using a pseudorandom polynomial (nrRootsProbab). It also includes a
15           command to verify the correctness of the pseudorandom answer.
16           References: Basu, Pollack, Roy, \"Algorithms in Real Algebraic
17           Geometry\", Springer, 2003.
18
19PROCEDURES:
20 nrRootsProbab(I)    Number of real roots of 0-dim ideal (probabilistic)
21 nrRootsDeterm(I)    Number of real roots of 0-dim ideal (deterministic)
22 symsignature(m)     Signature of the symmetric matrix m
23 sturmquery(h,B,I)   Sturm query of h on V(I)
24 matbil(h,B,I)       Matrix of the bilinear form on R/I associated to h
25 matmult(f,B,I)      Matrix of multiplication by f (m_f) on R/I in the basis B
26 tracemult(f,B,I)    Trace of m_f (B is an ordered basis of R/I)
27 coords(f,B,I)       Coordinates of f in the ordered basis B
28 randcharpoly(B,I,n) Pseudorandom charpoly of univ. projection, n optional
29 verify(p,B,i)       Verifies the result of randcharpoly
30 randlinpoly(n)      Pseudorandom linear polynomial, n optional
31 powersums(f,B,I)    Powersums of the roots of a char polynomial
32 symmfunc(S)         Symmetric functions from the powersums S
33 univarpoly(l)       Polynomial with coefficients from l
34 qbase(i)            Like kbase, but the monomials are ordered
35
36KEYWORDS: real roots, univariate projection
37";
38///////////////////////////////////////////////////////////////////
39LIB "linalg.lib";   // We use charpoly
40LIB "rootsur.lib"; // We use varsigns
41
42proc nrRootsProbab(ideal I, list #)
43"USAGE:     nrRootsProbab(I,[n]); ideal I, int n
44RETURN:    int: the number of real roots of the ideal I by a probabilistic
45           algorithm
46ASSUME:    If I is not a Groebner basis, then a Groebner basis will be computed
47           by using std. If I is already a Groebner basis (i.e. if
48           attrib(I,"isSB"); returns 1) then this Groebner basis will be
49           used, hence it must be one w.r.t. (any) global ordering. This may
50           be useful if the ideal is known to be a Groebner basis or if it
51           can be computed faster by a different method.
52NOTE:      If n<10 is given, n is the number of digits being used for
53           constructing a random characteristic polynomial, a bigger n is
54           more safe but slower (default: n=5).
55           If printlevel>0 the number of complex solutions is displayed
56           (default: printlevel=0).
57SEE ALSO:  nrroots, nrRootsDeterm, randcharpoly, solve
58EXAMPLE:   example nrRootsProbab; shows an example"
59{
60  //Note on complexity: Let n = no of complex roots of I (= vdim(std(I)).
61  //Then the algorithm needs:
62  //1 std(I) and ~n NF computations (of randcharpoly w.r.t. I)
63
64  if (isparam(I)) {
65    ERROR("This procedure cannot operate with parametric arguments");
66  }
67  int pr = printlevel-voice+2;
68  int v;
69  int n=5;
70  if (size(#) == 1) {
71    n=#[1];
72  }
73  if (attrib(I,"isSB")!=1) {
74    I = std(I);
75  }
76
77  ideal b = qbase(I);
78  v = size(b);
79  if (v == 0) {
80    ERROR("ideal is not 0-dimensional");
81  }
82  dbprint(pr,"//ideal has " +string(v)+ " complex solutions, counted with multiplicity");
83
84  poly p = randcharpoly(b,I,n);
85
86  return (nrroots(p));
87}
88
89example
90{
91  echo = 2;
92  ring r = 0,(x,y,z),lp;
93  ideal i = (x-1)*(x-2),(y-1)^3*(x-y),(z-1)*(z-2)*(z-3)^2;
94  nrRootsProbab(i);       //no of real roots (using internally std)
95
96  i = groebner(i);        //using the hilbert driven GB computation
97  int pr = printlevel;
98  printlevel = 2;
99  nrRootsProbab(i);
100  printlevel = pr;
101}
102///////////////////////////////////////////////////////////////////////////////
103
104proc nrRootsDeterm(ideal I)
105"USAGE:     nrRootsDeterm(I); ideal I
106RETURN:    int: the number of real roots of the ideal I by a deterministic
107           algorithm
108ASSUME:    If I is not a Groebner basis, then a Groebner basis will be computed
109           by using std. If I is already a Groebner basis (i.e. if
110           attrib(I,"isSB"); returns 1) then this Groebner basis will be
111           used, hence it must be one w.r.t. (any) global ordering. This may
112           be useful if the ideal is known to be a Groebner basis or if it
113           can be computed faster by a different method.
114NOTE:      If printlevel>0 the number of complex solutions is displayed
115           (default: printlevel=0). The procedure nrRootsProbab is usually faster.
116SEE ALSO:  nrroots, nrRootsProbab, sturmquery, solve
117EXAMPLE:   example nrRootsDeterm; shows an example"
118{
119  //Note on complexity: Let n = no of complex roots of I (= vdim(std(I)).
120  //Then the algotithm needs:
121  //1 std(I) and (1/2)n*(n+1)^2 ~ 1/2n^3 NF computations (of monomials w.r.t. I)
122
123  if (isparam(I)) {
124    ERROR("This procedure cannot operate with parametric arguments");
125  }
126  int pr = printlevel-voice+2;
127  int v;
128
129  if (attrib(I,"isSB")!=1) {
130    I = std(I);
131  }
132
133  ideal b = qbase(I);
134  v = size(b);
135  if (v == 0) {
136    ERROR("ideal is not 0-dimensional");
137  }
138  dbprint(pr,"//ideal has " +string(v)+ " complex solutions, counted with multiplicity");
139
140  return (sturmquery(1,b,I));
141}
142
143example
144{
145  echo = 2;
146  ring r = 0,(x,y,z),lp;
147  ideal I = (x-1)*(x-2),(y-1),(z-1)*(z-2)*(z-3)^2;
148  nrRootsDeterm(I);       //no of real roots (using internally std)
149
150  I = groebner(I);        //using the hilbert driven GB computation
151  int pr = printlevel;
152  printlevel = 2;
153  nrRootsDeterm(I);
154  printlevel = pr;
155}
156///////////////////////////////////////////////////////////////////////////////
157
158proc symsignature(matrix m)
159"USAGE:     symsignature(m); m matrix. m must be symmetric.
160RETURN:    int: the signature of m
161SEE ALSO:  matbil,sturmquery
162EXAMPLE:   example symsignature; shows an example"
163{
164  int positive, negative, i, j;
165  list l;
166  poly variable;
167
168  if (isparam(m)) {
169    ERROR("This procedure cannot operate with parametric arguments");
170  }
171
172  if (!isSquare(m)) {
173    ERROR ("m must be a square matrix");
174  }
175
176  // We check whether m is symmetric
177  for (i = 1;i <= nrows(m);i++) {
178    for (j = i;j <= nrows(m);j++) {
179      if (m[i,j] != m[j,i]) {
180        ERROR ("m must be a symmetric matrix");
181      }
182    }
183  }
184
185  poly f = charpoly(m); // Uses the last variable of the ring
186
187  for (i = size(f);i >= 1;i--) {
188    l[i] = leadcoef(f[i]);
189  }
190  positive = varsigns(l);
191
192  variable = var(nvars(basering)); // charpoly uses the last variable
193  f = subst(f,variable,-variable);
194
195  for (i = size(f);i >= 1;i--) {
196    l[i] = leadcoef(f[i]);
197  }
198
199  negative = varsigns(l);
200  return (positive - negative);
201}
202example
203{
204  echo = 2;
205  ring r = 0,(x,y),dp;
206  ideal i = x4-y2x,y2-13;
207  i = std(i);
208  ideal b = qbase(i);
209
210  matrix m = matbil(1,b,i);
211  symsignature(m);
212}
213///////////////////////////////////////////////////////////////////////////////
214
215proc sturmquery(poly h,ideal B,ideal I)
216"USAGE:     sturmquery(h,b,i); h poly, b,i ideal
217RETURN:    int: the Sturm query of h in V(i)
218ASSUME:    i is a Groebner basis, b is an ordered monomial basis
219           of r/i, r = basering.
220SEE ALSO:  symsignature,matbil
221EXAMPLE:   example sturmquery; shows an example"
222{
223  if (isparam(h) || isparam(B) || isparam(I)) {
224    ERROR("This procedure cannot operate with parametric arguments");
225  }
226
227  return (mysymmsig(matbil(h,B,I)));
228}
229example
230{
231  echo = 2;
232  ring r = 0,(x,y),dp;
233  ideal i = x4-y2x,y2-13;
234  i = std(i);
235  ideal b = qbase(i);
236
237  sturmquery(1,b,i);
238}
239///////////////////////////////////////////////////////////////////////////////
240
241static proc mysymmsig(matrix m)
242// returns the signature of a square symmetric matrix m
243{
244  int positive, negative, i;
245  list l;
246  poly variable;
247
248  poly f = charpoly(m); // Uses the last variable of the ring
249
250  for (i = size(f);i >= 1;i--) {
251    l[i] = leadcoef(f[i]);
252  }
253  positive = varsigns(l);
254
255  variable = var(nvars(basering)); // charpoly uses the last variable
256  f = subst(f,variable,-variable);
257
258  for (i = size(f);i >= 1;i--) {
259    l[i] = leadcoef(f[i]);
260  }
261
262  negative = varsigns(l);
263  return (positive - negative);
264}
265///////////////////////////////////////////////////////////////////////////////
266
267proc matbil(poly h,ideal B,ideal I)
268"USAGE:    matbil(h,b,i); h poly, b,i ideal
269RETURN:    matrix: the matrix of the bilinear form (f,g) |-> trace(m_fhg),
270           m_fhg = multiplication with fhg on r/i
271ASSUME:    i is a Groebner basis and b is an ordered monomial basis of r/i,
272           r = basering
273SEE ALSO:  matmult,tracemult
274EXAMPLE:   example matbil; shows an example"
275{
276  matrix m[size(B)][size(B)];
277  poly f;
278  int k,l;
279  //h = reduce(h,I);
280
281  for (k = 1; k <= size(B); k++) {
282    for (l = 1; l <= k; l++) {
283      m[k,l] = tracemult(h*B[k]*B[l],B,I)[1];
284      m[l,k] = m[k,l]; // The matrix we are trying to compute is symmetric
285    }
286   }
287  return(m);
288}
289example
290{
291  echo = 2;
292  ring r = 0,(x,y),dp;
293  ideal i = x4-y2x,y2-13;
294  i = std(i);
295  ideal b = qbase(i);
296  poly f = x3-xy+y-13+x4-y2x;
297
298  matrix m = matbil(f,b,i);
299  print(m);
300
301}
302///////////////////////////////////////////////////////////////////////////////
303
304proc tracemult(poly f,ideal B,ideal I)
305"USAGE:     tracemult(f,B,I);f poly, B,I ideal
306RETURN:    number: the trace of the multiplication by f (m_f) on r/I, written in
307           the monomial basis B of r/I, r = basering (faster than matmult + trace)
308ASSUME:    I is given by a Groebner basis and B is an ordered monomial basis of r/I
309SEE ALSO:  matmult,trace
310EXAMPLE:   example tracemult; shows an example"
311{
312  int k; // Iterates over the basis monomials
313  int l; // Iterates over the rows of the matrix
314  list coordinates;
315  number m;
316  poly g;
317
318  //f = reduce(f,I);
319  for (k = 1; k <= size(B); k++) {
320    l=1;
321    g = reduce(f*B[k],I);
322    while (l <= k) {
323      if (leadmonom(g[l]) == B[k]) {
324        m = m + leadcoef(g[l]);
325        break;
326      }
327      l++;
328    }
329  }
330  return (m);
331}
332example
333{
334  echo = 2;
335  ring r = 0,(x,y),dp;
336  ideal i = x4-y2x,y2-13;
337  i = std(i);
338  ideal b = qbase(i);
339
340  poly f = x3-xy+y-13+x4-y2x;
341  matrix m = matmult(f,b,i);
342  print(m);
343
344  tracemult(f,b,i);            //the trace of m
345}
346///////////////////////////////////////////////////////////////////////////////
347
348proc matmult(poly f, ideal B, ideal I)
349"USAGE:     matmult(f,b,i); f poly, b,i ideal
350RETURN:    matrix: the matrix of the multiplication map by f (m_f) on r/i
351           w.r.t. to the monomial basis b of r/i (r = basering)
352ASSUME:    i is a Groebner basis and b is an ordered monomial basis of r/i,
353           as given by qbase(i)
354SEE ALSO:  coords,matbil
355EXAMPLE:   example matmult; shows an example"
356{
357  int k; // Iterates over the basis monomials
358  int l; // Iterates over the rows of the matrix
359  list coordinates;
360  matrix m[size(B)][size(B)];
361
362  //f = reduce(f,I);
363  for (k = 1;k <= size(B);k++) {
364    coordinates = coords(f*(B[k]),B,I); // f*x_k written on the basis B
365    for (l = 1;l <= size(B);l++) {
366      m[l,k] = coordinates[l];
367    }
368  }
369  return (m);
370}
371example
372{
373  echo = 2;
374  ring r = 0,(x,y),dp;
375  ideal i = x4-y2x,y2-13;
376  i = std(i);
377  ideal b = qbase(i);
378
379  poly f = x3-xy+y-13+x4-y2x;
380  matrix m = matmult(f,b,i);
381  print(m);
382}
383///////////////////////////////////////////////////////////////////////////////
384
385proc coords(poly f,ideal B,ideal I)
386"USAGE:     coords(f,b,i), f poly, b,i ideal
387RETURN:    list of numbers: the coordinates of the class of f (mod i)
388           in the monomial basis b
389ASSUME:    i is a Groebner basis and b is an ordered monomial basis of r/i,
390           r = basering
391SEE ALSO:  matmult,matbil
392KEYWORDS:  coordinates
393EXAMPLE:   example coords; shows an example"
394{
395  // We assume the basis is sorted according to the ring order
396  poly g;
397  int k,l=1,1;
398  list coordinates;
399  int N = size(B);
400
401  // We first compute the normal form of f w.r.t. I
402  g = reduce(f,I);
403  int n = size(g);    //allways n <= N
404
405  while (k <= N) {
406    if (leadmonom(g[l]) == B[k]) {
407      coordinates[k] = leadcoef(g[l]);
408      l++;
409    } else {
410      coordinates[k] = number(0);
411    }
412    k++;
413  }
414  return (coordinates);
415}
416example
417{
418  echo = 2;
419  ring r = 0,(x,y),dp;
420  ideal i = x4-y2x,y2-13;
421  poly f = x3-xy+y-13+x4-y2x;
422  i = std(i);
423  ideal b = qbase(i);
424  b;
425  coords(f,b,i);
426}
427///////////////////////////////////////////////////////////////////////////////
428
429static proc isSquare(matrix m)
430// returns 1 if and only if m is a square matrix
431{
432  return (nrows(m)==ncols(m));
433}
434///////////////////////////////////////////////////////////////////////////////
435
436proc randcharpoly(ideal B,ideal I,list #)
437"USAGE:     randcharpoly(b,i); randcharpoly(b,i,n); b,i ideal; n int
438RETURN:    poly: the characteristic polynomial of a pseudorandom
439           rational univariate projection having one zero per zero of i.
440           If n<10 is given, it is the number of digits being used for the
441           pseudorandom coefficients (default: n=5)
442ASSUME:    i is a Groebner basis and b is an ordered monomial basis of r/i,
443           r = basering
444NOTE:      shows a warning if printlevel>0 (default: printlevel=0)
445KEYWORDS:  rational univariate projection
446EXAMPLE:   example randcharpoly; shows an example"
447{
448  int pr = printlevel - voice + 2;
449  poly p;
450  poly generic;
451  list l;
452  matrix m;
453  poly q;
454
455  if (size(#) == 1) {
456    generic = randlinpoly(#[1]);
457  } else {
458    generic = randlinpoly();
459  }
460
461  p = reduce(generic,I);
462  m = matmult(p,B,I);
463  q = charpoly(m);
464
465  dbprint(pr,"*********************************************************************");
466  dbprint(pr,"* WARNING: This polynomial was obtained using  pseudorandom numbers.*");
467  dbprint(pr,"* If you want to verify the result, please use the command          *");
468  dbprint(pr,"*                                                                   *");
469  dbprint(pr,"* verify(p,b,i)                                                     *");
470  dbprint(pr,"*                                                                   *");
471  dbprint(pr,"* where p is the polynomial I returned, b is the monomial basis     *");
472  dbprint(pr,"* used, and i the Groebner basis of the ideal                       *");
473  dbprint(pr,"*********************************************************************");
474
475  return(q);
476}
477example
478{
479  echo = 2;
480  ring r = 0,(x,y,z),dp;
481  ideal i = (x-1)*(x-2),(y-1),(z-1)*(z-2)*(z-3)^2;
482  i = std(i);
483  ideal b = qbase(i);
484  poly p = randcharpoly(b,i);
485  p;
486  nrroots(p); // See nrroots in urrcount.lib
487
488  int pr = printlevel;
489  printlevel = pr+2;
490  p = randcharpoly(b,i,5);
491  nrroots(p);
492  printlevel = pr;
493}
494
495///////////////////////////////////////////////////////////////////////////////
496
497proc verify(poly p,ideal B,ideal I)
498"USAGE:     verify(p,B,I); p poly, B,I,ideal
499RETURN:    integer: 1 if and only if the polynomial p splits the points of V(I).
500           It's used to check the result of randcharpoly
501ASSUME:    I is given by a Groebner basis and B is an ordered monomial basis of r/I,
502           r = basering
503NOTE:      comments the result if printlevel>0 (default: printlevel=0)
504SEE ALSO:  randcharpoly
505EXAMPLE:   example verify; shows an example"
506{
507  int pr = printlevel - voice + 2;
508  poly sqr_free;
509  int correct;
510  poly variable;
511
512  if (isparam(p) || isparam(B) || isparam(I)) {
513    ERROR("This procedure cannot operate with parametric arguments");
514  }
515
516  variable = isuni(p);
517  sqr_free = p/gcd(p,diff(p,variable));
518  correct = (mat_rk(matbil(1,B,I)) == deg(sqr_free));
519
520  if (correct) {
521    dbprint(pr,"//Verification successful");
522  } else {
523    dbprint(pr,"//The choice of random numbers was not useful");
524    dbprint(pr,"//You might want to try randcharpoly with a larger number of digits");
525  }
526  return (correct);
527}
528example
529{
530  echo = 2;
531  ring r = 0,(x,y),dp;
532  poly f = x3-xy+y-13+x4-y2x;
533  ideal i = x4-y2x,y2-13;
534  i = std(i);
535  ideal b = qbase(i);
536  poly p = randcharpoly(b,i);
537  verify(p,b,i);
538}
539///////////////////////////////////////////////////////////////////////////////
540
541proc randlinpoly(list #)
542"USAGE:     randlinpoly(); randlinpoly(n); n int
543RETURN:    poly: linear combination  of the variables of the ring, with
544           pseudorandom coefficients. If n<10 is given, it is the number of
545           digits being used for the range of the coefficients (default: n=5)
546SEE ALSO:  randcharpoly;
547EXAMPLE:   example randlinpoly; shows an example"
548{
549  int n,i;
550  poly p = 0;
551  int ndigits = 5;
552
553  if (size(#) == 1) {
554    ndigits = #[1];
555  }
556
557  n = nvars(basering);
558  for (i = 1;i <= n;i++) {
559    p = p + var(i)*random(1,10^ndigits);
560  }
561  return (p);
562}
563example
564{
565  echo = 2;
566  ring r = 0,(x,y,z,w),dp;
567  poly p = randlinpoly();
568  p;
569  randlinpoly(5);
570}
571///////////////////////////////////////////////////////////////////////////////
572
573proc powersums(poly f,ideal B,ideal I)
574"USAGE:     powersums(f,b,i); f poly; b,i ideal
575RETURN:    list: the powersums of the results of evaluating f at the zeros of I
576ASSUME:    i is a Groebner basis and b is an ordered monomial basis of r/i,
577           r = basering
578SEE ALSO:  symmfunc
579EXAMPLE:   example symmfunc; shows an example"
580{
581  int N,k;
582  list sums;
583
584  N = size(B);
585  for (k = 1;k <= N;k++) {
586    sums = sums + list(leadcoef(trace(matmult(f^k,B,I))));
587  }
588  return (sums);
589}
590example
591{
592  echo = 2;
593  ring r = 0,(x,y,z),dp;
594
595  ideal i = (x-1)*(x-2),(y-1),(z+5); // V(I) = {(1,1,-5),(2,1,-5)}
596  i = std(i);
597
598  ideal b = qbase(i);
599  poly f = x+y+z;
600  list psums = list(-2-3,4+9); // f evaluated at V(I) gives {-3,-2}
601  list l = powersums(f,b,i);
602  psums;
603  l;
604}
605///////////////////////////////////////////////////////////////////////////////
606
607proc symmfunc(list S)
608"USAGE:     symmfunc(s); s list
609RETURN:    list: the symmetric functions of the roots of a polynomial, given
610                 the power sums of those roots.
611SEE ALSO:  powersums
612EXAMPLE:   example symmfunc; shows an example"
613{
614  // Takes the list of power sums and returns the symmetric functions
615  list a;
616  int j,l,N;
617  number sum;
618
619  N = size(S);
620  a[N+1] = 1; // We set the length of the list and initialize its last element.
621
622  for (l = N - 1;l >= 0;l--) {
623    sum = 0;
624    for (j = l + 1;j <= N;j++) {
625      sum = sum + ((a[j+1])*(S[j-l]));
626    }
627    sum = -sum;
628    a[l+1] = sum/(N-l);
629  }
630
631  a = reverse(a);
632  return (a);
633}
634example
635{
636  echo = 2;
637  ring r = 0,x,dp;
638  poly p = (x-1)*(x-2)*(x-3);
639  list psums = list(1+2+3,1+4+9,1+8+27);
640  list l = symmfunc(psums);
641  l;
642  p; // Compare p with the elements of l
643}
644///////////////////////////////////////////////////////////////////////////////
645
646proc univarpoly(list l)
647"USAGE:     univarpoly(l); l list
648RETURN:    poly: a polynomial p on the first variable of basering, say x,
649           with p = l[1] + l[2]*x + l[3]*x^2 + ...
650EXAMPLE:  example univarpoly; shows an example"
651{
652  poly p;
653  int i,n;
654
655  n = size(l);
656  for (i = 1;i <= n;i++) {
657    p = p + l[i]*var(1)^(n-i);
658  }
659  return (p);
660}
661example
662{
663  echo = 2;
664  ring r = 0,x,dp;
665  list l = list(1,2,3,4,5);
666  poly p = univarpoly(l);
667  p;
668}
669///////////////////////////////////////////////////////////////////////////////
670
671proc qbase(ideal i)
672"USAGE:    qbase(I); I zero-dimensional ideal
673RETURN:   ideal: A monomial basis of the quotient between the basering and the
674          ideal I, sorted according to the basering order.
675SEE ALSO: kbase
676KEYWORDS: zero-dimensional
677EXAMPLE:  example qbase; shows an example"
678{
679  ideal b;
680
681  b = kbase(i);
682  b = reverseideal(sort(b)[1]); // sort sorts in ascending order
683  return (b);
684}
685example
686{
687  echo = 2;
688  ring r = 0,(x,y,z),dp;
689
690  ideal i = 2x2,-y2,z3;
691  i = std(i);
692  ideal b = qbase(i);
693  b;
694  b = kbase(i);
695  b; // Compare this with the result of qbase
696}
697///////////////////////////////////////////////////////////////////////////////
698
699static proc reverseideal(ideal b) // Returns b reversed
700{
701  int i;
702  ideal result;
703
704  result = b[1];
705  for (i = 2;i <= size(b);i++) {
706    result = b[i], result;
707  }
708  return (result);
709}
710///////////////////////////////////////////////////////////////////////////////
711
Note: See TracBrowser for help on using the repository browser.