001/* 002 * $Id: Word.java 5317 2015-11-01 17:42:27Z kredel $ 003 */ 004 005package edu.jas.poly; 006 007 008import java.util.SortedMap; 009import java.util.TreeMap; 010 011import edu.jas.structure.MonoidElem; 012import edu.jas.structure.MonoidFactory; 013import edu.jas.structure.NotInvertibleException; 014 015 016/** 017 * Word implements strings of letters for polynomials. 018 * @author Heinz Kredel 019 */ 020 021public final class Word implements MonoidElem<Word> { 022 023 024 /** 025 * Defining alphabet in WordFactory. 026 */ 027 public final WordFactory mono; 028 029 030 /** 031 * The data structure is a String of characters. 032 */ 033 /*package*/final String val; 034 035 036 /** 037 * Stored hash code. 038 */ 039 protected int hash = 0; 040 041 042 /** 043 * Constructor for Word. 044 * @param m factory for words. 045 */ 046 public Word(WordFactory m) { 047 this(m, ""); 048 } 049 050 051 /** 052 * Constructor for Word. 053 * @param m factory for words. 054 * @param s String 055 */ 056 public Word(WordFactory m, String s) { 057 this(m, s, true); 058 } 059 060 061 /** 062 * Constructor for Word. 063 * @param m factory for words. 064 * @param s String 065 * @param translate indicator if s needs translation 066 */ 067 public Word(WordFactory m, String s, boolean translate) { 068 mono = m; 069 hash = 0; 070 if (s == null) { 071 throw new IllegalArgumentException("null string not allowed"); 072 } 073 if (translate) { 074 if (mono.translation != null) { 075 //System.out.println("s = " + s); 076 String[] S = GenPolynomialTokenizer.variableList(s); 077 //System.out.println("S = " + Arrays.toString(S)); 078 val = mono.translate(S); 079 //System.out.println("val = " + val); 080 } else { 081 val = WordFactory.cleanSpace(s); //?? 082 } 083 } else { 084 val = s; 085 } 086 } 087 088 089 /** 090 * Get the corresponding element factory. 091 * @return factory for this Element. 092 * @see edu.jas.structure.Element#factory() 093 */ 094 public MonoidFactory<Word> factory() { 095 return mono; 096 } 097 098 099 /** 100 * Copy this. 101 * @return copy of this. 102 */ 103 @Override 104 public Word copy() { 105 return new Word(mono, val, false); 106 } 107 108 109 /** 110 * Get the word String. 111 * @return val. 112 */ 113 /*package*/String getVal() { 114 return val; 115 } 116 117 118 /** 119 * Get the letter at position i. 120 * @param i position. 121 * @return val[i]. 122 */ 123 public char getVal(int i) { 124 return val.charAt(i); 125 } 126 127 128 /** 129 * Get the length of this word. 130 * @return val.length. 131 */ 132 public int length() { 133 return val.length(); 134 } 135 136 137 /** 138 * Get the string representation. 139 * @see java.lang.Object#toString() 140 */ 141 @Override 142 public String toString() { 143 if (val.length() == 0) { 144 return ""; 145 } 146 StringBuffer s = new StringBuffer("\""); 147 if (mono.translation == null) { 148 for (int i = 0; i < length(); i++) { 149 if (i != 0) { 150 s.append(" "); 151 } 152 s.append(getVal(i)); 153 } 154 } else { 155 for (int i = 0; i < length(); i++) { 156 if (i != 0) { 157 s.append(" "); 158 } 159 s.append(mono.transVar(getVal(i))); 160 } 161 } 162 s.append("\""); 163 return s.toString(); 164 } 165 166 167 /** 168 * Get a scripting compatible string representation. 169 * @return script compatible representation for this Element. 170 * @see edu.jas.structure.Element#toScript() 171 */ 172 @Override 173 public String toScript() { 174 if (val.length() == 0) { 175 return ""; 176 } 177 StringBuffer s = new StringBuffer(""); 178 if (mono.translation == null) { 179 for (int i = 0; i < length(); i++) { 180 if (i != 0) { 181 s.append("*"); // checked for python vs ruby 182 } 183 s.append(getVal(i)); 184 } 185 } else { 186 for (int i = 0; i < length(); i++) { 187 if (i != 0) { 188 s.append("*"); // checked for python vs ruby 189 } 190 s.append(mono.transVar(getVal(i))); 191 } 192 } 193 s.append(""); 194 return s.toString(); 195 } 196 197 198 /** 199 * Get a scripting compatible string representation of the factory. 200 * @return script compatible representation for this ElemFactory. 201 * @see edu.jas.structure.Element#toScriptFactory() 202 */ 203 @Override 204 public String toScriptFactory() { 205 // Python case 206 return mono.toString(); 207 } 208 209 210 /** 211 * Comparison with any other object. 212 * @see java.lang.Object#equals(java.lang.Object) 213 */ 214 @Override 215 public boolean equals(Object B) { 216 if (!(B instanceof Word)) { 217 return false; 218 } 219 Word b = (Word) B; 220 // mono == b.mono ?? 221 int t = this.compareTo(b); 222 //System.out.println("equals: this = " + this + " B = " + B + " t = " + t); 223 return (0 == t); 224 } 225 226 227 /** 228 * hashCode. 229 * @see java.lang.Object#hashCode() 230 */ 231 @Override 232 public int hashCode() { 233 if (hash == 0) { 234 hash = val.hashCode(); 235 } 236 return hash; 237 } 238 239 240 /** 241 * Is Word one. 242 * @return If this is the empty word then true is returned, else false. 243 */ 244 public boolean isONE() { 245 return val.isEmpty(); 246 } 247 248 249 /** 250 * Is Word unit. 251 * @return If this is a unit then true is returned, else false. 252 */ 253 public boolean isUnit() { 254 return isONE(); 255 } 256 257 258 /** 259 * Word multiplication. 260 * @param V other word. 261 * @return this * V. 262 */ 263 public Word multiply(Word V) { 264 return new Word(mono, this.val + V.val, false); 265 } 266 267 268 /** 269 * Word divide. 270 * @param V other word. 271 * @return this / V. 272 */ 273 public Word divide(Word V) { 274 return divideLeft(V); 275 } 276 277 278 /** 279 * Word divide left. 280 * @param V other word. 281 * @return this / V = left, with left * V = this. 282 */ 283 public Word divideLeft(Word V) { 284 Word[] ret = divideWord(V,false); 285 // TODO: fail if both non zero? 286 if (!ret[1].isONE()) { 287 throw new IllegalArgumentException("not simple left dividable: left = " + ret[0] + ", right = " 288 + ret[1] + ", use divideWord"); 289 } 290 return ret[0]; 291 } 292 293 294 /** 295 * Word divide right. 296 * @param V other word. 297 * @return this / V = right, with V * right = this. 298 */ 299 public Word divideRight(Word V) { 300 Word[] ret = divideWord(V,true); 301 // TODO: fail if both non zero? 302 if (!ret[0].isONE()) { 303 throw new IllegalArgumentException("not simple right dividable: left = " + ret[0] + ", right = " 304 + ret[1] + ", use divideWord"); 305 } 306 return ret[1]; 307 } 308 309 310 /** 311 * Word divide with prefix and suffix. 312 * @param V other word. 313 * @return [left,right] with left * V * right = this. 314 */ 315 public Word[] divideWord(Word V) { 316 return divideWord(V,true); 317 } 318 319 /** 320 * Word divide with prefix and suffix. 321 * @param V other word. 322 * @param first is true for first index, false for last index. 323 * @return [left,right] with left * V * right = this. 324 */ 325 public Word[] divideWord(Word V, boolean first) { 326 int i; 327 if (first) { 328 i = this.val.indexOf(V.val); 329 } else { 330 i = this.val.lastIndexOf(V.val); 331 } 332 if (i < 0) { 333 throw new NotInvertibleException("not dividable: " + this + ", other " + V); 334 } 335 int len = V.val.length(); 336 String pre = this.val.substring(0, i); 337 String suf = this.val.substring(i + len); 338 Word[] ret = new Word[2]; 339 ret[0] = new Word(mono, pre, false); 340 ret[1] = new Word(mono, suf, false); 341 return ret; 342 } 343 344 345 /** 346 * Word remainder. 347 * @param V other word. 348 * @return this (this/V). <b>Note:</b> not useful. 349 */ 350 public Word remainder(Word V) { 351 int i = this.val.indexOf(V.val); 352 if (i < 0) { 353 throw new NotInvertibleException("not dividable: " + this + ", other " + V); 354 } 355 return V; 356 } 357 358 359 /** 360 * Quotient and remainder by division of this by S. 361 * @param S a Word 362 * @return [this/S, this - (this/S)*S]. <b>Note:</b> not useful. 363 */ 364 public Word[] quotientRemainder(Word S) { 365 return new Word[] { divide(S), remainder(S) }; 366 } 367 368 369 /** 370 * Word inverse. 371 * @return 1 / this. 372 */ 373 public Word inverse() { 374 if (val.length() == 0) { 375 return this; 376 } 377 throw new NotInvertibleException("not inversible " + this); 378 } 379 380 381 /** 382 * Word signum. 383 * @return 0 if this is one, 1 if it is non empty. 384 */ 385 public int signum() { 386 int i = val.length(); 387 if (i > 0) { 388 i = 1; 389 } 390 assert i >= 0; 391 return i; 392 } 393 394 395 /** 396 * Word degree. 397 * @return total degree of all letters. 398 */ 399 public long degree() { 400 return val.length(); 401 } 402 403 404 /** 405 * Word dependency on letters. 406 * @return sorted map of letters and the number of its occurences. 407 */ 408 public SortedMap<String, Integer> dependencyOnVariables() { 409 SortedMap<String, Integer> map = new TreeMap<String, Integer>(); 410 for (int i = 0; i < val.length(); i++) { 411 String s = String.valueOf(val.charAt(i)); 412 Integer n = map.get(s); 413 if (n == null) { 414 n = 0; 415 } 416 n = n + 1; 417 map.put(s, n); 418 } 419 return map; 420 } 421 422 423 /** 424 * Word leading exponent vector. 425 * @return an ExpVector for the first power of a letter. 426 */ 427 public ExpVector leadingExpVector() { 428 long n = 0; 429 char letter = ' '; 430 for (int i = 0; i < val.length(); i++) { 431 char s = val.charAt(i); 432 if (n == 0) { 433 letter = s; 434 n++; 435 } else if (letter == s) { 436 n++; 437 } else { 438 break; 439 } 440 } 441 int k = mono.length(); 442 if (n == 0L){ // == isONE() 443 return ExpVector.create(k); 444 } 445 int j = k - mono.indexOf(letter) - 1; 446 return ExpVector.create(k,j,n); 447 } 448 449 450 /** 451 * Word without leading exponent vector. 452 * @return an Word without the first power of a letter. 453 */ 454 public Word reductum() { 455 if (isONE()) { 456 return this; 457 } 458 int n = 0; 459 char letter = ' '; 460 for (int i = 0; i < val.length(); i++) { 461 char s = val.charAt(i); 462 if (n == 0) { 463 letter = s; 464 n++; 465 } else if (letter == s) { 466 n++; 467 } else { 468 break; 469 } 470 } 471 // n != 0 472 String r = val.substring(n); // n-1+1 473 return new Word(mono, r, false); 474 } 475 476 477 /** 478 * Word multiple test. 479 * @param V other word. 480 * @return true if this is a multiple of V, else false. 481 */ 482 public boolean multipleOf(Word V) { 483 return this.val.contains(V.val); 484 } 485 486 487 /** 488 * Word divides test. 489 * @param V other word. 490 * @return true if this divides V, else false. 491 */ 492 public boolean divides(Word V) { 493 return V.val.contains(this.val); 494 } 495 496 497 /** 498 * Word compareTo. Uses 499 * 500 * <pre> 501 * String.compareTo 502 * </pre> 503 * 504 * . 505 * @param V other word. 506 * @return 0 if U == V, -1 if U < V, 1 if U > V. 507 */ 508 @Override 509 public int compareTo(Word V) { 510 return this.val.compareTo(V.val); 511 } 512 513 514 /** 515 * Word graded comparison. Compares first be degree, then lexicographical. 516 * @param V other word. 517 * @return 0 if U == V, -1 if U < V, 1 if U > V. 518 */ 519 public int gradCompareTo(Word V) { 520 long e = this.degree(); 521 long f = V.degree(); 522 if (e < f) { 523 return 1; 524 } else if (e > f) { 525 return -1; 526 } 527 return this.compareTo(V); 528 } 529 530 531 /** 532 * Word graded comparison. Compares first be degree, then inverse 533 * lexicographical. 534 * @param V other word. 535 * @return 0 if U == V, -1 if U < V, 1 if U > V. 536 */ 537 public int gradInvlexCompareTo(Word V) { 538 long e = this.degree(); 539 long f = V.degree(); 540 if (e < f) { 541 return 1; 542 } else if (e > f) { 543 return -1; 544 } 545 return -this.compareTo(V); 546 } 547 548 549 /** 550 * Is word overlap. 551 * @param ol = [l1,r1,l2,r2] an Overlap container of four words 552 * @param V word 553 * @return true if l1 * this * r1 = l2 * V * r2, else false. 554 */ 555 public boolean isOverlap(Overlap ol, Word V) { 556 return ol.isOverlap(this, V); 557 } 558 559 560 /** 561 * Word overlap list. 562 * @param V other word. 563 * @return list of overlaps [l1,r1,l2,r2] with l1 * this * r1 = l2 * V * r2. 564 * If no such overlaps exist the empty overlap list is returned. 565 */ 566 public OverlapList overlap(Word V) { 567 OverlapList ret = new OverlapList(); 568 Word wone = mono.getONE(); 569 String a = this.val; 570 String b = V.val; 571 int ai = a.length(); 572 int bi = b.length(); 573 int j = b.indexOf(a); 574 if (j >= 0) { 575 while (j >= 0) { 576 String pre = b.substring(0, j); 577 String suf = b.substring(j + ai); 578 Word wpre = new Word(mono, pre, false); 579 Word wsuf = new Word(mono, suf, false); 580 ret.add(new Overlap(wpre, wsuf, wone, wone)); 581 j = b.indexOf(a, j + ai); // +1 also inner overlaps ? 582 } 583 return ret; 584 } 585 j = a.indexOf(b); 586 if (j >= 0) { 587 while (j >= 0) { 588 String pre = a.substring(0, j); 589 String suf = a.substring(j + bi); 590 Word wpre = new Word(mono, pre, false); 591 Word wsuf = new Word(mono, suf, false); 592 ret.add(new Overlap(wone, wone, wpre, wsuf)); 593 j = a.indexOf(b, j + bi); // +1 also inner overlaps ? 594 } 595 return ret; 596 } 597 if (ai >= bi) { 598 for (int i = 0; i < bi; i++) { 599 String as = a.substring(0, i + 1); 600 String bs = b.substring(bi - i - 1, bi); 601 //System.out.println("i = " + i + ", bs = " + bs + ", as = " + as); 602 if (as.equals(bs)) { 603 Word w1 = new Word(mono, b.substring(0, bi - i - 1), false); 604 Word w2 = new Word(mono, a.substring(i + 1), false); 605 ret.add(new Overlap(w1, wone, wone, w2)); 606 break; 607 } 608 } 609 for (int i = 0; i < bi; i++) { 610 String as = a.substring(ai - i - 1, ai); 611 String bs = b.substring(0, i + 1); 612 //System.out.println("i = " + i + ", bs = " + bs + ", as = " + as); 613 if (as.equals(bs)) { 614 Word w1 = new Word(mono, b.substring(i + 1), false); 615 Word w2 = new Word(mono, a.substring(0, ai - i - 1), false); 616 ret.add(new Overlap(wone, w1, w2, wone)); 617 break; 618 } 619 } 620 } else { // ai < bi 621 for (int i = 0; i < ai; i++) { 622 String as = a.substring(ai - i - 1, ai); 623 String bs = b.substring(0, i + 1); 624 //System.out.println("i = " + i + ", bs = " + bs + ", as = " + as); 625 if (as.equals(bs)) { 626 Word w1 = new Word(mono, b.substring(i + 1), false); 627 Word w2 = new Word(mono, a.substring(0, ai - i - 1), false); 628 ret.add(new Overlap(wone, w1, w2, wone)); 629 break; 630 } 631 } 632 for (int i = 0; i < ai; i++) { 633 String as = a.substring(0, i + 1); 634 String bs = b.substring(bi - i - 1, bi); 635 //System.out.println("i = " + i + ", bs = " + bs + ", as = " + as); 636 if (as.equals(bs)) { 637 Word w1 = new Word(mono, b.substring(0, bi - i - 1), false); 638 Word w2 = new Word(mono, a.substring(i + 1), false); 639 ret.add(new Overlap(w1, wone, wone, w2)); 640 break; 641 } 642 } 643 } 644 return ret; 645 } 646 647 648 /** 649 * Word pseudo least common multiple. 650 * @param V other word. 651 * @return w = l1*this*r1, with l1*this*r1 == l2*V*r2, if l1, r1, l2, r2 652 * exist, else null is returned. 653 */ 654 public Word lcm(Word V) { 655 OverlapList oll = overlap(V); 656 if (oll.ols.isEmpty()) { 657 return null; 658 } 659 Overlap ol = oll.ols.get(0); 660 Word w = ol.l1.multiply(this).multiply(ol.r1); 661 return w; 662 } 663 664}