001/*
002 * $Id: GenPolynomialTokenizer.java 4266 2012-10-21 20:24:01Z kredel $
003 */
004
005package edu.jas.poly;
006
007
008import java.io.BufferedReader;
009import java.io.IOException;
010import java.io.InputStreamReader;
011import java.io.Reader;
012import java.io.StreamTokenizer;
013import java.nio.charset.Charset;
014import java.util.ArrayList;
015import java.util.Arrays;
016import java.util.Iterator;
017import java.util.List;
018import java.util.Scanner;
019import java.util.Set;
020import java.util.TreeSet;
021
022import org.apache.log4j.Logger;
023
024import edu.jas.arith.BigComplex;
025import edu.jas.arith.BigDecimal;
026import edu.jas.arith.BigInteger;
027import edu.jas.arith.BigQuaternion;
028import edu.jas.arith.BigRational;
029import edu.jas.arith.ModInteger;
030import edu.jas.arith.ModIntegerRing;
031import edu.jas.arith.ModLongRing;
032import edu.jas.structure.Power;
033import edu.jas.structure.RingElem;
034import edu.jas.structure.RingFactory;
035
036
037/**
038 * GenPolynomial Tokenizer. Used to read rational polynomials and lists of
039 * polynomials from input streams. Arbitrary polynomial rings and coefficient
040 * rings can be read with RingFactoryTokenizer. <b>Note:</b> Can no more read
041 * QuotientRing since end of 2010, revision 3441. Quotient coefficients and
042 * others can still be read if the respective factory is provided via the
043 * constructor.
044 * @see edu.jas.application.RingFactoryTokenizer
045 * @author Heinz Kredel
046 */
047public class GenPolynomialTokenizer {
048
049
050    private static final Logger logger = Logger.getLogger(GenPolynomialTokenizer.class);
051
052
053    private final boolean debug = logger.isDebugEnabled();
054
055
056    private String[] vars;
057
058
059    private int nvars = 1;
060
061
062    private TermOrder tord;
063
064
065    private RelationTable table;
066
067
068    //private Reader in;
069    private final StreamTokenizer tok;
070
071
072    private final Reader reader;
073
074
075    private RingFactory fac;
076
077
078    private static enum coeffType {
079        BigRat, BigInt, ModInt, BigC, BigQ, BigD, ANrat, ANmod, IntFunc
080    };
081
082
083    private coeffType parsedCoeff = coeffType.BigRat;
084
085
086    private GenPolynomialRing pfac;
087
088
089    private static enum polyType {
090        PolBigRat, PolBigInt, PolModInt, PolBigC, PolBigD, PolBigQ, PolANrat, PolANmod, PolIntFunc
091    };
092
093
094    private polyType parsedPoly = polyType.PolBigRat;
095
096
097    private GenSolvablePolynomialRing spfac;
098
099
100    /**
101     * noargs constructor reads from System.in.
102     */
103    public GenPolynomialTokenizer() {
104        this(new BufferedReader(new InputStreamReader(System.in,Charset.forName("UTF8"))));
105    }
106
107
108    /**
109     * constructor with Ring and Reader.
110     * @param rf ring factory.
111     * @param r reader stream.
112     */
113    public GenPolynomialTokenizer(GenPolynomialRing rf, Reader r) {
114        this(r);
115        if (rf == null) {
116            return;
117        }
118        if (rf instanceof GenSolvablePolynomialRing) {
119            pfac = rf;
120            spfac = (GenSolvablePolynomialRing) rf;
121        } else {
122            pfac = rf;
123            spfac = null;
124        }
125        fac = rf.coFac;
126        vars = rf.vars;
127        if (vars != null) {
128            nvars = vars.length;
129        }
130        tord = rf.tord;
131        // relation table
132        if (spfac != null) {
133            table = spfac.table;
134        } else {
135            table = null;
136        }
137    }
138
139
140    /**
141     * constructor with Reader.
142     * @param r reader stream.
143     */
144    @SuppressWarnings("unchecked")
145    public GenPolynomialTokenizer(Reader r) {
146        //BasicConfigurator.configure();
147        vars = null;
148        tord = new TermOrder();
149        nvars = 1;
150        fac = new BigRational(1);
151
152        pfac = new GenPolynomialRing<BigRational>(fac, nvars, tord, vars);
153        spfac = new GenSolvablePolynomialRing<BigRational>(fac, nvars, tord, vars);
154
155        reader = r;
156        tok = new StreamTokenizer(reader);
157        tok.resetSyntax();
158        // tok.eolIsSignificant(true); no more
159        tok.eolIsSignificant(false);
160        tok.wordChars('0', '9');
161        tok.wordChars('a', 'z');
162        tok.wordChars('A', 'Z');
163        tok.wordChars('_', '_'); // for subscripts x_i
164        tok.wordChars('/', '/'); // wg. rational numbers
165        tok.wordChars(128 + 32, 255);
166        tok.whitespaceChars(0, ' ');
167        tok.commentChar('#');
168        tok.quoteChar('"');
169        tok.quoteChar('\'');
170        //tok.slashStarComments(true); does not work
171
172    }
173
174
175    /**
176     * Initialize coefficient and polynomial factories.
177     * @param rf ring factory.
178     * @param ct coefficient type.
179     */
180    @SuppressWarnings("unchecked")
181    public void initFactory(RingFactory rf, coeffType ct) {
182        fac = rf;
183        parsedCoeff = ct;
184
185        switch (ct) {
186        case BigRat:
187            pfac = new GenPolynomialRing<BigRational>(fac, nvars, tord, vars);
188            parsedPoly = polyType.PolBigRat;
189            break;
190        case BigInt:
191            pfac = new GenPolynomialRing<BigInteger>(fac, nvars, tord, vars);
192            parsedPoly = polyType.PolBigInt;
193            break;
194        case ModInt:
195            pfac = new GenPolynomialRing<ModInteger>(fac, nvars, tord, vars);
196            parsedPoly = polyType.PolModInt;
197            break;
198        case BigC:
199            pfac = new GenPolynomialRing<BigComplex>(fac, nvars, tord, vars);
200            parsedPoly = polyType.PolBigC;
201            break;
202        case BigQ:
203            pfac = new GenPolynomialRing<BigQuaternion>(fac, nvars, tord, vars);
204            parsedPoly = polyType.PolBigQ;
205            break;
206        case BigD:
207            pfac = new GenPolynomialRing<BigDecimal>(fac, nvars, tord, vars);
208            parsedPoly = polyType.PolBigD;
209            break;
210        case IntFunc:
211            pfac = new GenPolynomialRing<GenPolynomial<BigRational>>(fac, nvars, tord, vars);
212            parsedPoly = polyType.PolIntFunc;
213            break;
214        default:
215            pfac = new GenPolynomialRing<BigRational>(fac, nvars, tord, vars);
216            parsedPoly = polyType.PolBigRat;
217        }
218    }
219
220
221    /**
222     * Initialize polynomial and solvable polynomial factories.
223     * @param rf ring factory.
224     * @param ct coefficient type.
225     */
226    @SuppressWarnings("unchecked")
227    public void initSolvableFactory(RingFactory rf, coeffType ct) {
228        fac = rf;
229        parsedCoeff = ct;
230
231        switch (ct) {
232        case BigRat:
233            spfac = new GenSolvablePolynomialRing<BigRational>(fac, nvars, tord, vars);
234            parsedPoly = polyType.PolBigRat;
235            break;
236        case BigInt:
237            spfac = new GenSolvablePolynomialRing<BigInteger>(fac, nvars, tord, vars);
238            parsedPoly = polyType.PolBigInt;
239            break;
240        case ModInt:
241            spfac = new GenSolvablePolynomialRing<ModInteger>(fac, nvars, tord, vars);
242            parsedPoly = polyType.PolModInt;
243            break;
244        case BigC:
245            spfac = new GenSolvablePolynomialRing<BigComplex>(fac, nvars, tord, vars);
246            parsedPoly = polyType.PolBigC;
247            break;
248        case BigQ:
249            spfac = new GenSolvablePolynomialRing<BigQuaternion>(fac, nvars, tord, vars);
250            parsedPoly = polyType.PolBigQ;
251            break;
252        case BigD:
253            spfac = new GenSolvablePolynomialRing<BigDecimal>(fac, nvars, tord, vars);
254            parsedPoly = polyType.PolBigD;
255            break;
256        case IntFunc:
257            spfac = new GenSolvablePolynomialRing<GenPolynomial<BigRational>>(fac, nvars, tord, vars);
258            parsedPoly = polyType.PolIntFunc;
259            break;
260        default:
261            spfac = new GenSolvablePolynomialRing<BigRational>(fac, nvars, tord, vars);
262            parsedPoly = polyType.PolBigRat;
263        }
264    }
265
266
267    /**
268     * Parsing method for GenPolynomial. syntax ? (simple)
269     * @return the next polynomial.
270     * @throws IOException
271     */
272    @SuppressWarnings("unchecked")
273    public GenPolynomial nextPolynomial() throws IOException {
274        if (debug) {
275            logger.debug("torder = " + tord);
276        }
277        GenPolynomial a = pfac.getZERO();
278        GenPolynomial a1 = pfac.getONE();
279        ExpVector leer = pfac.evzero;
280
281        if (debug) {
282            logger.debug("a = " + a);
283            logger.debug("a1 = " + a1);
284        }
285        GenPolynomial b = a1;
286        GenPolynomial c;
287        int tt; //, oldtt;
288        //String rat = "";
289        char first;
290        RingElem r;
291        ExpVector e;
292        int ix;
293        long ie;
294        boolean done = false;
295        while (!done) {
296            // next input. determine next action
297            tt = tok.nextToken();
298            //System.out.println("while tt = " + tok);
299            logger.debug("while tt = " + tok);
300            if (tt == StreamTokenizer.TT_EOF)
301                break;
302            switch (tt) {
303            case ')':
304            case ',':
305                return a; // do not change or remove
306            case '-':
307                b = b.negate();
308            case '+':
309            case '*':
310                tt = tok.nextToken();
311                break;
312            default: // skip
313            }
314            // read coefficient, monic monomial and polynomial
315            if (tt == StreamTokenizer.TT_EOF)
316                break;
317            switch (tt) {
318            // case '_': removed 
319            case '}':
320                throw new InvalidExpressionException("mismatch of braces after " + a + ", error at " + b);
321            case '{': // recursion
322                StringBuffer rf = new StringBuffer();
323                int level = 0;
324                do {
325                    tt = tok.nextToken();
326                    //System.out.println("token { = " + ((char)tt) + ", " + tt + ", level = " + level);
327                    if (tt == StreamTokenizer.TT_EOF) {
328                        throw new InvalidExpressionException("mismatch of braces after " + a + ", error at "
329                                        + b);
330                    }
331                    if (tt == '{') {
332                        level++;
333                    }
334                    if (tt == '}') {
335                        level--;
336                        if (level < 0) {
337                            continue; // skip last closing brace 
338                        }
339                    }
340                    if (tok.sval != null) {
341                        if (rf.length() > 0 && rf.charAt(rf.length() - 1) != '.') {
342                            rf.append(" ");
343                        }
344                        rf.append(tok.sval); // " " + 
345                    } else {
346                        rf.append((char) tt);
347                    }
348                } while (level >= 0);
349                //System.out.println("coeff{} = " + rf.toString() );
350                try {
351                    r = (RingElem) fac.parse(rf.toString());
352                } catch (NumberFormatException re) {
353                    throw new InvalidExpressionException("not a number " + rf, re);
354                }
355                if (debug)
356                    logger.debug("coeff " + r);
357                ie = nextExponent();
358                if (debug)
359                    logger.debug("ie " + ie);
360                r = Power.<RingElem> positivePower(r, ie);
361                if (debug)
362                    logger.debug("coeff^ie " + r);
363                b = b.multiply(r, leer);
364                tt = tok.nextToken();
365                if (debug)
366                    logger.debug("tt,digit = " + tok);
367                //no break;
368                break;
369
370            case StreamTokenizer.TT_WORD:
371                //System.out.println("TT_WORD: " + tok.sval);
372                if (tok.sval == null || tok.sval.length() == 0)
373                    break;
374                // read coefficient
375                first = tok.sval.charAt(0);
376                if (digit(first)) {
377                    //System.out.println("coeff 0 = " + tok.sval );
378                    StringBuffer df = new StringBuffer();
379                    df.append(tok.sval);
380                    if (tok.sval.charAt(tok.sval.length() - 1) == 'i') { // complex number
381                        tt = tok.nextToken();
382                        if (debug)
383                            logger.debug("tt,im = " + tok);
384                        if (tok.sval != null || tt == '-') {
385                            if (tok.sval != null) {
386                                df.append(tok.sval);
387                            } else {
388                                df.append("-");
389                            }
390                            if (tt == '-') {
391                                tt = tok.nextToken(); // todo: decimal number
392                                if (tok.sval != null && digit(tok.sval.charAt(0))) {
393                                    df.append(tok.sval);
394
395                                } else {
396                                    tok.pushBack();
397                                }
398                            }
399                        } else {
400                            tok.pushBack();
401                        }
402                    }
403                    tt = tok.nextToken();
404                    if (tt == '.') { // decimal number
405                        tt = tok.nextToken();
406                        if (debug)
407                            logger.debug("tt,dot = " + tok);
408                        if (tok.sval != null) {
409                            df.append(".");
410                            df.append(tok.sval);
411                        } else {
412                            tok.pushBack();
413                            tok.pushBack();
414                        }
415                    } else {
416                        tok.pushBack();
417                    }
418                    try {
419                        r = (RingElem) fac.parse(df.toString());
420                    } catch (NumberFormatException re) {
421                        throw new InvalidExpressionException("not a number " + df, re);
422                    }
423                    if (debug)
424                        logger.debug("coeff " + r);
425                    //System.out.println("r = " + r.toScriptFactory());
426                    ie = nextExponent();
427                    if (debug)
428                        logger.debug("ie " + ie);
429                    // r = r^ie;
430                    r = Power.<RingElem> positivePower(r, ie);
431                    if (debug)
432                        logger.debug("coeff^ie " + r);
433                    b = b.multiply(r, leer);
434                    tt = tok.nextToken();
435                    if (debug)
436                        logger.debug("tt,digit = " + tok);
437                }
438                if (tt == StreamTokenizer.TT_EOF)
439                    break;
440                if (tok.sval == null)
441                    break;
442                // read monomial or recursion 
443                first = tok.sval.charAt(0);
444                if (letter(first)) {
445                    ix = leer.indexVar(tok.sval, vars); //indexVar( tok.sval );
446                    if (ix < 0) { // not found
447                        try {
448                            r = (RingElem) fac.parse(tok.sval);
449                        } catch (NumberFormatException re) {
450                            throw new InvalidExpressionException("recursively unknown variable " + tok.sval);
451                        }
452                        if (debug)
453                            logger.info("coeff " + r);
454                        //if (r.isONE() || r.isZERO()) {
455                        //logger.error("Unknown varibable " + tok.sval);
456                        //done = true;
457                        //break;
458                        //throw new InvalidExpressionException("recursively unknown variable " + tok.sval);
459                        //}
460                        ie = nextExponent();
461                        //  System.out.println("ie: " + ie);
462                        r = Power.<RingElem> positivePower(r, ie);
463                        b = b.multiply(r);
464                    } else { // found
465                        //  System.out.println("ix: " + ix);
466                        ie = nextExponent();
467                        //  System.out.println("ie: " + ie);
468                        e = ExpVector.create(vars.length, ix, ie);
469                        b = b.multiply(e);
470                    }
471                    tt = tok.nextToken();
472                    if (debug)
473                        logger.debug("tt,letter = " + tok);
474                }
475                break;
476
477            case '(':
478                c = nextPolynomial();
479                if (debug)
480                    logger.debug("factor " + c);
481                ie = nextExponent();
482                if (debug)
483                    logger.debug("ie " + ie);
484                c = Power.<GenPolynomial> positivePower(c, ie);
485                if (debug)
486                    logger.debug("factor^ie " + c);
487                b = b.multiply(c);
488                tt = tok.nextToken();
489                if (debug)
490                    logger.debug("tt,digit = " + tok);
491                //no break;
492                break;
493
494            default: //skip 
495            }
496            if (done)
497                break; // unknown variable
498            if (tt == StreamTokenizer.TT_EOF)
499                break;
500            // complete polynomial
501            tok.pushBack();
502            switch (tt) {
503            case '-':
504            case '+':
505            case ')':
506            case ',':
507                logger.debug("b, = " + b);
508                a = a.sum(b);
509                b = a1;
510                break;
511            case '*':
512                logger.debug("b, = " + b);
513                //a = a.sum(b); 
514                //b = a1;
515                break;
516            case '\n':
517                tt = tok.nextToken();
518                if (debug)
519                    logger.debug("tt,nl = " + tt);
520                break;
521            default: // skip or finish ?
522                if (debug)
523                    logger.debug("default: " + tok);
524            }
525        }
526        if (debug)
527            logger.debug("b = " + b);
528        a = a.sum(b);
529        logger.debug("a = " + a);
530        // b = a1;
531        return a;
532    }
533
534
535    /**
536     * Parsing method for exponent (of variable). syntax: ^long | **long.
537     * @return the next exponent or 1.
538     * @throws IOException
539     */
540    public long nextExponent() throws IOException {
541        long e = 1;
542        char first;
543        int tt;
544        tt = tok.nextToken();
545        if (tt == '^') {
546            if (debug)
547                logger.debug("exponent ^");
548            tt = tok.nextToken();
549            if (tok.sval != null) {
550                first = tok.sval.charAt(0);
551                if (digit(first)) {
552                    e = Long.parseLong(tok.sval);
553                    return e;
554                }
555            }
556        }
557        if (tt == '*') {
558            tt = tok.nextToken();
559            if (tt == '*') {
560                if (debug)
561                    logger.debug("exponent **");
562                tt = tok.nextToken();
563                if (tok.sval != null) {
564                    first = tok.sval.charAt(0);
565                    if (digit(first)) {
566                        e = Long.parseLong(tok.sval);
567                        return e;
568                    }
569                }
570            }
571            tok.pushBack();
572        }
573        tok.pushBack();
574        return e;
575    }
576
577
578    /**
579     * Parsing method for comments. syntax: (* comment *) | /_* comment *_/
580     * without _ Does not work with this pushBack(), unused.
581     */
582    public String nextComment() throws IOException {
583        // syntax: (* comment *) | /* comment */ 
584        StringBuffer c = new StringBuffer();
585        int tt;
586        if (debug)
587            logger.debug("comment: " + tok);
588        tt = tok.nextToken();
589        if (debug)
590            logger.debug("comment: " + tok);
591        if (tt == '(') {
592            tt = tok.nextToken();
593            if (debug)
594                logger.debug("comment: " + tok);
595            if (tt == '*') {
596                if (debug)
597                    logger.debug("comment: ");
598                while (true) {
599                    tt = tok.nextToken();
600                    if (tt == '*') {
601                        tt = tok.nextToken();
602                        if (tt == ')') {
603                            return c.toString();
604                        }
605                        tok.pushBack();
606                    }
607                    c.append(tok.sval);
608                }
609            }
610            tok.pushBack();
611            if (debug)
612                logger.debug("comment: " + tok);
613        }
614        tok.pushBack();
615        if (debug)
616            logger.debug("comment: " + tok);
617        return c.toString();
618    }
619
620
621    /**
622     * Parsing method for variable list. syntax: (a, b c, de) gives [ "a", "b",
623     * "c", "de" ]
624     * @return the next variable list.
625     * @throws IOException
626     */
627    public String[] nextVariableList() throws IOException {
628        List<String> l = new ArrayList<String>();
629        int tt;
630        tt = tok.nextToken();
631        //System.out.println("vList tok = " + tok);
632        if (tt == '(' || tt == '{') {
633            logger.debug("variable list");
634            tt = tok.nextToken();
635            while (true) {
636                if (tt == StreamTokenizer.TT_EOF)
637                    break;
638                if (tt == ')' || tt == '}')
639                    break;
640                if (tt == StreamTokenizer.TT_WORD) {
641                    //System.out.println("TT_WORD: " + tok.sval);
642                    l.add(tok.sval);
643                }
644                tt = tok.nextToken();
645            }
646        }
647        Object[] ol = l.toArray();
648        String[] v = new String[ol.length];
649        for (int i = 0; i < v.length; i++) {
650            v[i] = (String) ol[i];
651        }
652        return v;
653    }
654
655
656    /**
657     * Parsing method for coefficient ring. syntax: Rat | Q | Int | Z | Mod
658     * modul | Complex | C | D | Quat | AN[ (var) ( poly ) ] | AN[ modul (var) (
659     * poly ) ] | IntFunc (var_list)
660     * @return the next coefficient factory.
661     * @throws IOException
662     */
663    @SuppressWarnings("unchecked")
664    public RingFactory nextCoefficientRing() throws IOException {
665        RingFactory coeff = null;
666        coeffType ct = null;
667        int tt;
668        tt = tok.nextToken();
669        if (tok.sval != null) {
670            if (tok.sval.equalsIgnoreCase("Q")) {
671                coeff = new BigRational(0);
672                ct = coeffType.BigRat;
673            } else if (tok.sval.equalsIgnoreCase("Rat")) {
674                coeff = new BigRational(0);
675                ct = coeffType.BigRat;
676            } else if (tok.sval.equalsIgnoreCase("D")) {
677                coeff = new BigDecimal(0);
678                ct = coeffType.BigD;
679            } else if (tok.sval.equalsIgnoreCase("Z")) {
680                coeff = new BigInteger(0);
681                ct = coeffType.BigInt;
682            } else if (tok.sval.equalsIgnoreCase("Int")) {
683                coeff = new BigInteger(0);
684                ct = coeffType.BigInt;
685            } else if (tok.sval.equalsIgnoreCase("C")) {
686                coeff = new BigComplex(0);
687                ct = coeffType.BigC;
688            } else if (tok.sval.equalsIgnoreCase("Complex")) {
689                coeff = new BigComplex(0);
690                ct = coeffType.BigC;
691            } else if (tok.sval.equalsIgnoreCase("Quat")) {
692                coeff = new BigQuaternion(0);
693                ct = coeffType.BigQ;
694            } else if (tok.sval.equalsIgnoreCase("Mod")) {
695                tt = tok.nextToken();
696                boolean openb = false;
697                if (tt == '[') { // optional
698                    openb = true;
699                    tt = tok.nextToken();
700                }
701                if (tok.sval != null && tok.sval.length() > 0) {
702                    if (digit(tok.sval.charAt(0))) {
703                        BigInteger mo = new BigInteger(tok.sval);
704                        BigInteger lm = new BigInteger(ModLongRing.MAX_LONG); //wrong: Long.MAX_VALUE);
705                        if (mo.compareTo(lm) < 0) {
706                            coeff = new ModLongRing(mo.getVal());
707                        } else {
708                            coeff = new ModIntegerRing(mo.getVal());
709                        }
710                        //System.out.println("coeff = " + coeff + " :: " + coeff.getClass());
711                        ct = coeffType.ModInt;
712                    } else {
713                        tok.pushBack();
714                    }
715                } else {
716                    tok.pushBack();
717                }
718                if (tt == ']' && openb) { // optional
719                    tt = tok.nextToken();
720                }
721            } else if (tok.sval.equalsIgnoreCase("RatFunc") || tok.sval.equalsIgnoreCase("ModFunc")) {
722                //logger.error("RatFunc and ModFunc can no more be read, see edu.jas.application.RingFactoryTokenizer.");
723                throw new InvalidExpressionException(
724                                "RatFunc and ModFunc can no more be read, see edu.jas.application.RingFactoryTokenizer.");
725            } else if (tok.sval.equalsIgnoreCase("IntFunc")) {
726                String[] rfv = nextVariableList();
727                //System.out.println("rfv = " + rfv.length + " " + rfv[0]);
728                int vr = rfv.length;
729                BigRational bi = new BigRational();
730                TermOrder to = new TermOrder(TermOrder.INVLEX);
731                GenPolynomialRing<BigRational> pcf = new GenPolynomialRing<BigRational>(bi, vr, to, rfv);
732                coeff = pcf;
733                ct = coeffType.IntFunc;
734            } else if (tok.sval.equalsIgnoreCase("AN")) {
735                tt = tok.nextToken();
736                if (tt == '[') {
737                    tt = tok.nextToken();
738                    RingFactory tcfac = new ModIntegerRing("19");
739                    if (tok.sval != null && tok.sval.length() > 0) {
740                        if (digit(tok.sval.charAt(0))) {
741                            tcfac = new ModIntegerRing(tok.sval);
742                        } else {
743                            tcfac = new BigRational();
744                            tok.pushBack();
745                        }
746                    } else {
747                        tcfac = new BigRational();
748                        tok.pushBack();
749                    }
750                    String[] anv = nextVariableList();
751                    //System.out.println("anv = " + anv.length + " " + anv[0]);
752                    int vs = anv.length;
753                    if (vs != 1) {
754                        throw new InvalidExpressionException(
755                                        "AlgebraicNumber only for univariate polynomials "
756                                                        + Arrays.toString(anv));
757                    }
758                    String[] ovars = vars;
759                    vars = anv;
760                    GenPolynomialRing tpfac = pfac;
761                    RingFactory tfac = fac;
762                    fac = tcfac;
763                    // pfac and fac used in nextPolynomial()
764                    if (tcfac instanceof ModIntegerRing) {
765                        pfac = new GenPolynomialRing<ModInteger>(tcfac, vs, new TermOrder(), anv);
766                    } else {
767                        pfac = new GenPolynomialRing<BigRational>(tcfac, vs, new TermOrder(), anv);
768                    }
769                    if (debug) {
770                        logger.debug("pfac = " + pfac);
771                    }
772                    tt = tok.nextToken();
773                    GenPolynomial mod;
774                    if (tt == '(') {
775                        mod = nextPolynomial();
776                        tt = tok.nextToken();
777                        if (tok.ttype != ')')
778                            tok.pushBack();
779                    } else {
780                        tok.pushBack();
781                        mod = nextPolynomial();
782                    }
783                    if (debug) {
784                        logger.debug("mod = " + mod);
785                    }
786                    pfac = tpfac;
787                    fac = tfac;
788                    vars = ovars;
789                    if (tcfac instanceof ModIntegerRing) {
790                        GenPolynomial<ModInteger> gfmod;
791                        gfmod = (GenPolynomial<ModInteger>) mod;
792                        coeff = new AlgebraicNumberRing<ModInteger>(gfmod);
793                        ct = coeffType.ANmod;
794                    } else {
795                        GenPolynomial<BigRational> anmod;
796                        anmod = (GenPolynomial<BigRational>) mod;
797                        coeff = new AlgebraicNumberRing<BigRational>(anmod);
798                        ct = coeffType.ANrat;
799                    }
800                    if (debug) {
801                        logger.debug("coeff = " + coeff);
802                    }
803                    tt = tok.nextToken();
804                    if (tt == ']') {
805                        //ok, no nextToken();
806                    } else {
807                        tok.pushBack();
808                    }
809                } else {
810                    tok.pushBack();
811                }
812            }
813        }
814        if (coeff == null) {
815            tok.pushBack();
816            coeff = new BigRational();
817            ct = coeffType.BigRat;
818        }
819        parsedCoeff = ct;
820        return coeff;
821    }
822
823
824    /**
825     * Parsing method for weight list. syntax: (w1, w2, w3, ..., wn)
826     * @return the next weight list.
827     * @throws IOException
828     */
829    public long[] nextWeightList() throws IOException {
830        List<Long> l = new ArrayList<Long>();
831        long[] w = null;
832        long e;
833        char first;
834        int tt;
835        tt = tok.nextToken();
836        if (tt == '(') {
837            logger.debug("weight list");
838            tt = tok.nextToken();
839            while (true) {
840                if (tt == StreamTokenizer.TT_EOF)
841                    break;
842                if (tt == ')')
843                    break;
844                if (tok.sval != null) {
845                    first = tok.sval.charAt(0);
846                    if (digit(first)) {
847                        e = Long.parseLong(tok.sval);
848                        l.add(Long.valueOf(e));
849                        //System.out.println("w: " + e);
850                    }
851                }
852                tt = tok.nextToken(); // also comma
853            }
854        }
855        Object[] ol = l.toArray();
856        w = new long[ol.length];
857        for (int i = 0; i < w.length; i++) {
858            w[i] = ((Long) ol[ol.length - i - 1]).longValue();
859        }
860        return w;
861    }
862
863
864    /**
865     * Parsing method for weight array. syntax: ( (w11, ...,w1n), ..., (wm1,
866     * ..., wmn) )
867     * @return the next weight array.
868     * @throws IOException
869     */
870    public long[][] nextWeightArray() throws IOException {
871        List<long[]> l = new ArrayList<long[]>();
872        long[][] w = null;
873        long[] e;
874        char first;
875        int tt;
876        tt = tok.nextToken();
877        if (tt == '(') {
878            logger.debug("weight array");
879            tt = tok.nextToken();
880            while (true) {
881                if (tt == StreamTokenizer.TT_EOF)
882                    break;
883                if (tt == ')')
884                    break;
885                if (tt == '(') {
886                    tok.pushBack();
887                    e = nextWeightList();
888                    l.add(e);
889                    //System.out.println("wa: " + e);
890                } else if (tok.sval != null) {
891                    first = tok.sval.charAt(0);
892                    if (digit(first)) {
893                        tok.pushBack();
894                        tok.pushBack();
895                        e = nextWeightList();
896                        l.add(e);
897                        break;
898                        //System.out.println("w: " + e);
899                    }
900                }
901                tt = tok.nextToken(); // also comma
902            }
903        }
904        Object[] ol = l.toArray();
905        w = new long[ol.length][];
906        for (int i = 0; i < w.length; i++) {
907            w[i] = (long[]) ol[i];
908        }
909        return w;
910    }
911
912
913    /**
914     * Parsing method for split index. syntax: |i|
915     * @return the next split index.
916     * @throws IOException
917     */
918    public int nextSplitIndex() throws IOException {
919        int e = -1; // =unknown
920        int e0 = -1; // =unknown
921        char first;
922        int tt;
923        tt = tok.nextToken();
924        if (tt == '|') {
925            logger.debug("split index");
926            tt = tok.nextToken();
927            if (tt == StreamTokenizer.TT_EOF) {
928                return e;
929            }
930            if (tok.sval != null) {
931                first = tok.sval.charAt(0);
932                if (digit(first)) {
933                    e = Integer.parseInt(tok.sval);
934                    //System.out.println("w: " + i);
935                }
936                tt = tok.nextToken();
937                if (tt != '|') {
938                    tok.pushBack();
939                }
940            }
941        } else if (tt == '[') {
942            logger.debug("split index");
943            tt = tok.nextToken();
944            if (tt == StreamTokenizer.TT_EOF) {
945                return e;
946            }
947            if (tok.sval != null) {
948                first = tok.sval.charAt(0);
949                if (digit(first)) {
950                    e0 = Integer.parseInt(tok.sval);
951                    //System.out.println("w: " + i);
952                }
953                tt = tok.nextToken();
954                if (tt == ',') {
955                    tt = tok.nextToken();
956                    if (tt == StreamTokenizer.TT_EOF) {
957                        return e0;
958                    }
959                    if (tok.sval != null) {
960                        first = tok.sval.charAt(0);
961                        if (digit(first)) {
962                            e = Integer.parseInt(tok.sval);
963                            //System.out.println("w: " + i);
964                        }
965                    }
966                    if (tt != ']') {
967                        tok.pushBack();
968                    }
969                }
970            }
971        } else {
972            tok.pushBack();
973        }
974        return e;
975    }
976
977
978    /**
979     * Parsing method for term order name. syntax: termOrderName = L, IL, LEX,
980     * G, IG, GRLEX, W(weights) |split index|
981     * @return the next term order.
982     * @throws IOException
983     */
984    public TermOrder nextTermOrder() throws IOException {
985        int evord = TermOrder.DEFAULT_EVORD;
986        int tt;
987        tt = tok.nextToken();
988        if (tt == StreamTokenizer.TT_EOF) { /* nop */
989        } else if (tt == StreamTokenizer.TT_WORD) {
990            // System.out.println("TT_WORD: " + tok.sval);
991            if (tok.sval != null) {
992                if (tok.sval.equalsIgnoreCase("L")) {
993                    evord = TermOrder.INVLEX;
994                } else if (tok.sval.equalsIgnoreCase("IL")) {
995                    evord = TermOrder.INVLEX;
996                } else if (tok.sval.equalsIgnoreCase("INVLEX")) {
997                    evord = TermOrder.INVLEX;
998                } else if (tok.sval.equalsIgnoreCase("LEX")) {
999                    evord = TermOrder.LEX;
1000                } else if (tok.sval.equalsIgnoreCase("G")) {
1001                    evord = TermOrder.IGRLEX;
1002                } else if (tok.sval.equalsIgnoreCase("IG")) {
1003                    evord = TermOrder.IGRLEX;
1004                } else if (tok.sval.equalsIgnoreCase("IGRLEX")) {
1005                    evord = TermOrder.IGRLEX;
1006                } else if (tok.sval.equalsIgnoreCase("GRLEX")) {
1007                    evord = TermOrder.GRLEX;
1008                } else if (tok.sval.equalsIgnoreCase("W")) {
1009                    long[][] w = nextWeightArray();
1010                    //int s = nextSplitIndex(); // no more
1011                    return new TermOrder(w);
1012                }
1013            }
1014        }
1015        int s = nextSplitIndex();
1016        if (s <= 0) {
1017            return new TermOrder(evord);
1018        }
1019        return new TermOrder(evord, evord, vars.length, s);
1020    }
1021
1022
1023    /**
1024     * Parsing method for polynomial list. syntax: ( p1, p2, p3, ..., pn )
1025     * @return the next polynomial list.
1026     * @throws IOException
1027     */
1028    public List<GenPolynomial> nextPolynomialList() throws IOException {
1029        GenPolynomial a;
1030        List<GenPolynomial> L = new ArrayList<GenPolynomial>();
1031        int tt;
1032        tt = tok.nextToken();
1033        if (tt == StreamTokenizer.TT_EOF)
1034            return L;
1035        if (tt != '(')
1036            return L;
1037        logger.debug("polynomial list");
1038        while (true) {
1039            tt = tok.nextToken();
1040            if (tok.ttype == ',')
1041                continue;
1042            if (tt == '(') {
1043                a = nextPolynomial();
1044                tt = tok.nextToken();
1045                if (tok.ttype != ')')
1046                    tok.pushBack();
1047            } else {
1048                tok.pushBack();
1049                a = nextPolynomial();
1050            }
1051            logger.info("next pol = " + a);
1052            L.add(a);
1053            if (tok.ttype == StreamTokenizer.TT_EOF)
1054                break;
1055            if (tok.ttype == ')')
1056                break;
1057        }
1058        return L;
1059    }
1060
1061
1062    /**
1063     * Parsing method for submodule list. syntax: ( ( p11, p12, p13, ..., p1n ),
1064     * ..., ( pm1, pm2, pm3, ..., pmn ) )
1065     * @return the next list of polynomial lists.
1066     * @throws IOException
1067     */
1068    public List<List<GenPolynomial>> nextSubModuleList() throws IOException {
1069        List<List<GenPolynomial>> L = new ArrayList<List<GenPolynomial>>();
1070        int tt;
1071        tt = tok.nextToken();
1072        if (tt == StreamTokenizer.TT_EOF)
1073            return L;
1074        if (tt != '(')
1075            return L;
1076        logger.debug("module list");
1077        List<GenPolynomial> v = null;
1078        while (true) {
1079            tt = tok.nextToken();
1080            if (tok.ttype == ',')
1081                continue;
1082            if (tok.ttype == ')')
1083                break;
1084            if (tok.ttype == StreamTokenizer.TT_EOF)
1085                break;
1086            if (tt == '(') {
1087                tok.pushBack();
1088                v = nextPolynomialList();
1089                logger.info("next vect = " + v);
1090                L.add(v);
1091            }
1092        }
1093        return L;
1094    }
1095
1096
1097    /**
1098     * Parsing method for solvable polynomial relation table. syntax: ( p_1,
1099     * p_2, p_3, ..., p_{n+3} ) semantics: p_{n+1} * p_{n+2} = p_{n+3} The next
1100     * relation table is stored into the solvable polynomial factory.
1101     * @throws IOException
1102     */
1103    @SuppressWarnings("unchecked")
1104    public void nextRelationTable() throws IOException {
1105        if (spfac == null) {
1106            return;
1107        }
1108        RelationTable table = spfac.table;
1109        List<GenPolynomial> rels = null;
1110        GenPolynomial p;
1111        GenSolvablePolynomial sp;
1112        int tt;
1113        tt = tok.nextToken();
1114        if (debug) {
1115            logger.debug("relation table: " + tt);
1116        }
1117        if (tok.sval != null) {
1118            if (tok.sval.equalsIgnoreCase("RelationTable")) {
1119                rels = nextPolynomialList();
1120            }
1121        }
1122        if (rels == null) {
1123            tok.pushBack();
1124            return;
1125        }
1126        for (Iterator<GenPolynomial> it = rels.iterator(); it.hasNext();) {
1127            p = it.next();
1128            ExpVector e = p.leadingExpVector();
1129            if (it.hasNext()) {
1130                p = it.next();
1131                ExpVector f = p.leadingExpVector();
1132                if (it.hasNext()) {
1133                    p = it.next();
1134                    sp = new GenSolvablePolynomial(spfac, p.val);
1135                    table.update(e, f, sp);
1136                }
1137            }
1138        }
1139        if (debug) {
1140            logger.info("table = " + table);
1141        }
1142        return;
1143    }
1144
1145
1146    /**
1147     * Parsing method for polynomial set. syntax: coeffRing varList
1148     * termOrderName polyList.
1149     * @return the next polynomial set.
1150     * @throws IOException
1151     */
1152    @SuppressWarnings("unchecked")
1153    public PolynomialList nextPolynomialSet() throws IOException {
1154        //String comments = "";
1155        //comments += nextComment();
1156        //if (debug) logger.debug("comment = " + comments);
1157
1158        RingFactory coeff = nextCoefficientRing();
1159        logger.info("coeff = " + coeff);
1160
1161        vars = nextVariableList();
1162        logger.info("vars = " + Arrays.toString(vars));
1163        if (vars != null) {
1164            nvars = vars.length;
1165        }
1166
1167        tord = nextTermOrder();
1168        logger.info("tord = " + tord);
1169        // check more TOs
1170
1171        initFactory(coeff, parsedCoeff); // global: nvars, tord, vars
1172        List<GenPolynomial> s = null;
1173        s = nextPolynomialList();
1174        logger.info("s = " + s);
1175        // comments += nextComment();
1176        return new PolynomialList(pfac, s);
1177    }
1178
1179
1180    /**
1181     * Parsing method for module set. syntax: coeffRing varList termOrderName
1182     * moduleList.
1183     * @return the next module set.
1184     * @throws IOException
1185     */
1186    @SuppressWarnings("unchecked")
1187    public ModuleList nextSubModuleSet() throws IOException {
1188        //String comments = "";
1189        //comments += nextComment();
1190        //if (debug) logger.debug("comment = " + comments);
1191
1192        RingFactory coeff = nextCoefficientRing();
1193        logger.info("coeff = " + coeff);
1194
1195        vars = nextVariableList();
1196        logger.info("vars = " + Arrays.toString(vars));
1197        if (vars != null) {
1198            nvars = vars.length;
1199        }
1200
1201        tord = nextTermOrder();
1202        logger.info("tord = " + tord);
1203        // check more TOs
1204
1205        initFactory(coeff, parsedCoeff); // global: nvars, tord, vars
1206        List<List<GenPolynomial>> m = null;
1207        m = nextSubModuleList();
1208        logger.info("m = " + m);
1209        // comments += nextComment();
1210
1211        return new ModuleList(pfac, m);
1212    }
1213
1214
1215    /**
1216     * Parsing method for solvable polynomial list. syntax: ( p1, p2, p3, ...,
1217     * pn )
1218     * @return the next solvable polynomial list.
1219     * @throws IOException
1220     */
1221    @SuppressWarnings("unchecked")
1222    public List<GenSolvablePolynomial> nextSolvablePolynomialList() throws IOException {
1223        List<GenPolynomial> s = nextPolynomialList();
1224        logger.info("s = " + s);
1225        // comments += nextComment();
1226
1227        GenPolynomial p;
1228        GenSolvablePolynomial ps;
1229        List<GenSolvablePolynomial> sp = new ArrayList<GenSolvablePolynomial>(s.size());
1230        for (Iterator<GenPolynomial> it = s.iterator(); it.hasNext();) {
1231            p = it.next();
1232            ps = new GenSolvablePolynomial(spfac, p.val);
1233            //System.out.println("ps = " + ps);
1234            sp.add(ps);
1235        }
1236        return sp;
1237    }
1238
1239
1240    /**
1241     * Parsing method for solvable polynomial. syntax: p.
1242     * @return the next polynomial.
1243     * @throws IOException
1244     */
1245    @SuppressWarnings("unchecked")
1246    public GenSolvablePolynomial nextSolvablePolynomial() throws IOException {
1247        GenPolynomial p = nextPolynomial();
1248        logger.info("p = " + p);
1249        // comments += nextComment();
1250
1251        GenSolvablePolynomial ps = new GenSolvablePolynomial(spfac, p.val);
1252        //System.out.println("ps = " + ps);
1253        return ps;
1254    }
1255
1256
1257    /**
1258     * Parsing method for solvable polynomial set. syntax: varList termOrderName
1259     * relationTable polyList.
1260     * @return the next solvable polynomial set.
1261     * @throws IOException
1262     */
1263
1264    @SuppressWarnings("unchecked")
1265    public PolynomialList nextSolvablePolynomialSet() throws IOException {
1266        //String comments = "";
1267        //comments += nextComment();
1268        //if (debug) logger.debug("comment = " + comments);
1269
1270        RingFactory coeff = nextCoefficientRing();
1271        logger.info("coeff = " + coeff);
1272
1273        vars = nextVariableList();
1274        logger.info("vars = " + Arrays.toString(vars));
1275        if (vars != null) {
1276            nvars = vars.length;
1277        }
1278
1279        tord = nextTermOrder();
1280        logger.info("tord = " + tord);
1281        // check more TOs
1282
1283        initFactory(coeff, parsedCoeff); // must be because of symmetric read
1284        initSolvableFactory(coeff, parsedCoeff); // global: nvars, tord, vars
1285        //System.out.println("pfac = " + pfac);
1286        //System.out.println("spfac = " + spfac);
1287
1288        nextRelationTable();
1289        if (logger.isInfoEnabled()) {
1290            logger.info("table = " + table);
1291        }
1292
1293        List<GenSolvablePolynomial> s = null;
1294        s = nextSolvablePolynomialList();
1295        logger.info("s = " + s);
1296        // comments += nextComment();
1297        return new PolynomialList(spfac, s); // Ordered ?
1298    }
1299
1300
1301    /**
1302     * Parsing method for solvable submodule list. syntax: ( ( p11, p12, p13,
1303     * ..., p1n ), ..., ( pm1, pm2, pm3, ..., pmn ) )
1304     * @return the next list of solvable polynomial lists.
1305     * @throws IOException
1306     */
1307    public List<List<GenSolvablePolynomial>> nextSolvableSubModuleList() throws IOException {
1308        List<List<GenSolvablePolynomial>> L = new ArrayList<List<GenSolvablePolynomial>>();
1309        int tt;
1310        tt = tok.nextToken();
1311        if (tt == StreamTokenizer.TT_EOF)
1312            return L;
1313        if (tt != '(')
1314            return L;
1315        logger.debug("module list");
1316        List<GenSolvablePolynomial> v = null;
1317        while (true) {
1318            tt = tok.nextToken();
1319            if (tok.ttype == ',')
1320                continue;
1321            if (tok.ttype == ')')
1322                break;
1323            if (tok.ttype == StreamTokenizer.TT_EOF)
1324                break;
1325            if (tt == '(') {
1326                tok.pushBack();
1327                v = nextSolvablePolynomialList();
1328                logger.info("next vect = " + v);
1329                L.add(v);
1330            }
1331        }
1332        return L;
1333    }
1334
1335
1336    /**
1337     * Parsing method for solvable module set. syntax: varList termOrderName
1338     * relationTable moduleList.
1339     * @return the next solvable module set.
1340     * @throws IOException
1341     */
1342    @SuppressWarnings("unchecked")
1343    public ModuleList nextSolvableSubModuleSet() throws IOException {
1344        //String comments = "";
1345        //comments += nextComment();
1346        //if (debug) logger.debug("comment = " + comments);
1347
1348        RingFactory coeff = nextCoefficientRing();
1349        logger.info("coeff = " + coeff);
1350
1351        vars = nextVariableList();
1352        logger.info("vars = " + Arrays.toString(vars));
1353        if (vars != null) {
1354            nvars = vars.length;
1355        }
1356
1357        tord = nextTermOrder();
1358        logger.info("tord = " + tord);
1359        // check more TOs
1360
1361        initFactory(coeff, parsedCoeff); // must be because of symmetric read
1362        initSolvableFactory(coeff, parsedCoeff); // global: nvars, tord, vars
1363
1364        //System.out.println("spfac = " + spfac);
1365
1366        nextRelationTable();
1367        if (logger.isInfoEnabled()) {
1368            logger.info("table = " + table);
1369        }
1370
1371        List<List<GenSolvablePolynomial>> s = null;
1372        s = nextSolvableSubModuleList();
1373        logger.info("s = " + s);
1374        // comments += nextComment();
1375
1376        return new OrderedModuleList(spfac, s); // Ordered
1377    }
1378
1379
1380    // must also allow +/- // does not work with tokenizer
1381    //private static boolean number(char x) {
1382    //    return digit(x) || x == '-' || x == '+';
1383    //}
1384
1385
1386    private static boolean digit(char x) {
1387        return '0' <= x && x <= '9';
1388    }
1389
1390
1391    private static boolean letter(char x) {
1392        return ('a' <= x && x <= 'z') || ('A' <= x && x <= 'Z');
1393    }
1394
1395
1396    // unused
1397    public void nextComma() throws IOException {
1398        int tt;
1399        if (tok.ttype == ',') {
1400            tt = tok.nextToken();
1401            if (debug) {
1402                logger.debug("after comma: " + tt);
1403            }
1404        }
1405    }
1406
1407
1408    /**
1409     * Parse variable list from String.
1410     * @param s String. Syntax: (n1,...,nk) or (n1 ... nk), parenthesis are also
1411     *            optional.
1412     * @return array of variable names found in s.
1413     */
1414    public static String[] variableList(String s) {
1415        String[] vl = null;
1416        if (s == null) {
1417            return vl;
1418        }
1419        String st = s.trim();
1420        if (st.length() == 0) {
1421            return new String[0];
1422        }
1423        if (st.charAt(0) == '(') {
1424            st = st.substring(1);
1425        }
1426        if (st.charAt(st.length() - 1) == ')') {
1427            st = st.substring(0, st.length() - 1);
1428        }
1429        st = st.replaceAll(",", " ");
1430        List<String> sl = new ArrayList<String>();
1431        Scanner sc = new Scanner(st);
1432        while (sc.hasNext()) {
1433            String sn = sc.next();
1434            sl.add(sn);
1435        }
1436        vl = new String[sl.size()];
1437        int i = 0;
1438        for (String si : sl) {
1439            vl[i] = si;
1440            i++;
1441        }
1442        return vl;
1443    }
1444
1445
1446    /**
1447     * Extract variable list from expression.
1448     * @param s String. Syntax: any polynomial expression.
1449     * @return array of variable names found in s.
1450     */
1451    public static String[] expressionVariables(String s) {
1452        String[] vl = null;
1453        if (s == null) {
1454            return vl;
1455        }
1456        String st = s.trim();
1457        if (st.length() == 0) {
1458            return new String[0];
1459        }
1460        st = st.replaceAll(",", " ");
1461        st = st.replaceAll("\\+", " ");
1462        st = st.replaceAll("-", " ");
1463        st = st.replaceAll("\\*", " ");
1464        st = st.replaceAll("/", " ");
1465        st = st.replaceAll("\\(", " ");
1466        st = st.replaceAll("\\)", " ");
1467        st = st.replaceAll("\\{", " ");
1468        st = st.replaceAll("\\}", " ");
1469        st = st.replaceAll("\\[", " ");
1470        st = st.replaceAll("\\]", " ");
1471        st = st.replaceAll("\\^", " ");
1472        //System.out.println("st = " + st);
1473
1474        Set<String> sl = new TreeSet<String>();
1475        Scanner sc = new Scanner(st);
1476        while (sc.hasNext()) {
1477            String sn = sc.next();
1478            if (sn == null || sn.length() == 0) {
1479                continue;
1480            }
1481            //System.out.println("sn = " + sn);
1482            int i = 0;
1483            while (digit(sn.charAt(i)) && i < sn.length() - 1) {
1484                i++;
1485            }
1486            //System.out.println("sn = " + sn + ", i = " + i);
1487            if (i > 0) {
1488                sn = sn.substring(i, sn.length());
1489            }
1490            //System.out.println("sn = " + sn);
1491            if (sn.length() == 0) {
1492                continue;
1493            }
1494            if (!letter(sn.charAt(0))) {
1495                continue;
1496            }
1497            //System.out.println("sn = " + sn);
1498            sl.add(sn);
1499        }
1500        vl = new String[sl.size()];
1501        int i = 0;
1502        for (String si : sl) {
1503            vl[i] = si;
1504            i++;
1505        }
1506        return vl;
1507    }
1508
1509}