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}