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