001/* 002 * $Id: WordFactory.java 5833 2018-05-16 21:49:10Z kredel $ 003 */ 004 005package edu.jas.poly; 006 007 008import java.io.Reader; 009import java.io.Serializable; 010import java.math.BigInteger; 011import java.util.ArrayList; 012import java.util.Arrays; 013import java.util.Comparator; 014import java.util.List; 015import java.util.Random; 016 017import org.apache.log4j.Logger; 018 019import edu.jas.kern.StringUtil; 020import edu.jas.structure.MonoidFactory; 021 022 023/** 024 * WordFactory implements alphabet related methods. 025 * @author Heinz Kredel 026 */ 027 028public final class WordFactory implements MonoidFactory<Word> { 029 030 031 /** 032 * The data structure is a String of characters which defines the alphabet. 033 */ 034 /*package*/final String alphabet; 035 036 037 /** 038 * The empty word for this monoid. 039 */ 040 public final Word ONE; 041 042 043 /** 044 * The translation reference string. 045 */ 046 public static final String transRef = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 047 048 049 /** 050 * The translation array of Strings. 051 */ 052 public final String[] translation; 053 054 055 /** 056 * Random number generator. 057 */ 058 private final static Random random = new Random(); 059 060 061 /** 062 * Log4j logger object. 063 */ 064 private static final Logger logger = Logger.getLogger(WordFactory.class); 065 066 067 /** 068 * Comparator for Words. 069 */ 070 public static abstract class WordComparator implements Comparator<Word>, Serializable { 071 072 073 public abstract int compare(Word e1, Word e2); 074 } 075 076 077 /** 078 * Defined descending order comparator. Sorts the highest terms first. 079 */ 080 private static final WordComparator horder = new WordComparator() { 081 082 083 @Override 084 public int compare(Word e1, Word e2) { 085 //return e1.gradCompareTo(e2); 086 return e1.gradInvlexCompareTo(e2); 087 } 088 }; 089 090 091 /** 092 * Defined ascending order comparator. Sorts the lowest terms first. 093 */ 094 private static final WordComparator lorder = new WordComparator() { 095 096 097 @Override 098 public int compare(Word e1, Word e2) { 099 //return -e1.gradCompareTo(e2); 100 return -e1.gradInvlexCompareTo(e2); 101 } 102 }; 103 104 105 /** 106 * Constructor for WordFactory. 107 */ 108 public WordFactory() { 109 this(""); 110 } 111 112 113 /** 114 * Constructor for WordFactory. 115 * @param s String of single letters for alphabet 116 */ 117 public WordFactory(String s) { 118 if (s == null) { 119 throw new IllegalArgumentException("null string not allowed"); 120 } 121 alphabet = cleanSpace(s); 122 translation = null; 123 ONE = new Word(this, "", false); 124 } 125 126 127 /** 128 * Constructor for WordFactory. 129 * @param S String array for alphabet 130 */ 131 public WordFactory(String[] S) { 132 String[] V = cleanAll(S); 133 if (isSingleLetters(V)) { 134 alphabet = concat(V); 135 translation = null; 136 } else { 137 alphabet = transRef.substring(0, V.length); 138 translation = V; 139 logger.info("alphabet = " + alphabet + ", translation = " + Arrays.toString(translation)); 140 } 141 ONE = new Word(this, "", false); 142 } 143 144 145 /** 146 * Is this structure finite or infinite. 147 * @return true if this structure is finite, else false. 148 * @see edu.jas.structure.ElemFactory#isFinite() 149 */ 150 public boolean isFinite() { 151 if (alphabet.length() == 0) { 152 return true; 153 } 154 return false; 155 } 156 157 158 /** 159 * Query if this monoid is commutative. 160 * @return true if this monoid is commutative, else false. 161 */ 162 public boolean isCommutative() { 163 if (alphabet.length() == 0) { 164 return true; 165 } 166 return false; 167 } 168 169 170 /** 171 * Query if this monoid is associative. 172 * @return true if this monoid is associative, else false. 173 */ 174 public boolean isAssociative() { 175 return true; 176 } 177 178 179 /** 180 * Get the one element, the empty word. 181 * @return 1 as Word. 182 */ 183 public Word getONE() { 184 return ONE; 185 } 186 187 188 /** 189 * Copy word. 190 * @param w word to copy. 191 * @return copy of w. 192 */ 193 @Override 194 public Word copy(Word w) { 195 return new Word(this, w.getVal(), false); 196 } 197 198 199 /** 200 * Get the alphabet length. 201 * @return alphabet.length. 202 */ 203 public int length() { 204 return alphabet.length(); 205 } 206 207 208 /** 209 * Get the alphabet String. 210 * @return alphabet. 211 */ 212 /*package*/String getVal() { 213 return alphabet; 214 } 215 216 217 /** 218 * Get the translation array of Strings. 219 * @return alphabet. 220 */ 221 /*package*/String[] getTrans() { 222 return translation; 223 } 224 225 226 /** 227 * Get the alphabet letter at position i. 228 * @param i position. 229 * @return val[i]. 230 */ 231 public char getVal(int i) { 232 return alphabet.charAt(i); 233 } 234 235 236 /** 237 * Get the variable names. 238 * @return array of variable names 239 */ 240 public String[] getVars() { 241 String[] vars = new String[alphabet.length()]; 242 if (translation == null) { 243 for (int i = 0; i < alphabet.length(); i++) { 244 vars[i] = String.valueOf(getVal(i)); 245 } 246 } else { 247 for (int i = 0; i < alphabet.length(); i++) { 248 vars[i] = translation[i]; 249 } 250 } 251 return vars; 252 } 253 254 255 /** 256 * Get the string representation. 257 * @see java.lang.Object#toString() 258 */ 259 @Override 260 public String toString() { 261 StringBuffer s = new StringBuffer("\""); 262 if (translation == null) { 263 for (int i = 0; i < alphabet.length(); i++) { 264 if (i != 0) { 265 s.append(","); 266 } 267 s.append(getVal(i)); 268 } 269 } else { 270 for (int i = 0; i < alphabet.length(); i++) { 271 if (i != 0) { 272 s.append(","); 273 } 274 s.append(translation[i]); 275 } 276 } 277 s.append("\""); 278 return s.toString(); 279 } 280 281 282 /** 283 * Get a scripting compatible string representation. 284 * @return script compatible representation for this Element. 285 * @see edu.jas.structure.Element#toScript() 286 */ 287 @Override 288 public String toScript() { 289 return toString(); 290 } 291 292 293 /** 294 * Comparison with any other object. 295 * @see java.lang.Object#equals(java.lang.Object) 296 */ 297 @Override 298 public boolean equals(Object B) { 299 if (!(B instanceof WordFactory)) { 300 return false; 301 } 302 WordFactory b = (WordFactory) B; 303 return alphabet.equals(b.alphabet); 304 } 305 306 307 /** 308 * hashCode. 309 * @see java.lang.Object#hashCode() 310 */ 311 @Override 312 public int hashCode() { 313 return alphabet.hashCode(); 314 } 315 316 317 /** 318 * Get a list of the generating elements. 319 * @return list of generators for the algebraic structure. 320 */ 321 public List<Word> generators() { 322 int len = alphabet.length(); 323 List<Word> gens = new ArrayList<Word>(len); 324 // ONE is not a word generator 325 for (int i = 0; i < len; i++) { 326 Word w = new Word(this, String.valueOf(alphabet.charAt(i)), false); 327 gens.add(w); 328 } 329 return gens; 330 } 331 332 333 /** 334 * Get the Element for a. 335 * @param a long 336 * @return element corresponding to a. 337 */ 338 public Word fromInteger(long a) { 339 throw new UnsupportedOperationException("not implemented for WordFactory"); 340 } 341 342 343 /** 344 * Get the Element for a. 345 * @param a java.math.BigInteger. 346 * @return element corresponding to a. 347 */ 348 public Word fromInteger(BigInteger a) { 349 throw new UnsupportedOperationException("not implemented for WordFactory"); 350 } 351 352 353 /** 354 * Get the Element for an ExpVector. 355 * @param e ExpVector. 356 * @return element corresponding to e. 357 */ 358 public Word valueOf(ExpVector e) { 359 Word w = ONE; 360 List<Word> gens = generators(); 361 int n = alphabet.length(); 362 int m = e.length(); 363 if (m > n) { 364 throw new IllegalArgumentException("alphabet to short for exponent " + e + ", alpahbet = " 365 + alphabet); 366 } 367 for (int i = 0; i < m; i++) { 368 int x = (int) e.getVal(m - i - 1); 369 Word y = gens.get(i); 370 Word u = ONE; 371 for (int j = 0; j < x; j++) { 372 u = u.multiply(y); 373 } 374 w = w.multiply(u); 375 } 376 return w; 377 } 378 379 380 /** 381 * IndexOf for letter in alphabet. 382 * @param s letter character. 383 * @return index of s in the alphabet, or -1 if s is not contained in the alphabet. 384 */ 385 public int indexOf(char s) { 386 return alphabet.indexOf(s); 387 } 388 389 390 /** 391 * Generate a random Element with size less equal to n. 392 * @param n 393 * @return a random element. 394 */ 395 public Word random(int n) { 396 return random(n, random); 397 } 398 399 400 /** 401 * Generate a random Element with size less equal to n. 402 * @param n 403 * @param random is a source for random bits. 404 * @return a random element. 405 */ 406 public Word random(int n, Random random) { 407 StringBuffer sb = new StringBuffer(); 408 int len = alphabet.length(); 409 for (int i = 0; i < n; i++) { 410 int r = Math.abs(random.nextInt() % len); 411 sb.append(alphabet.charAt(r)); 412 } 413 return new Word(this, sb.toString(), false); 414 } 415 416 417 /** 418 * Parse from String. 419 * @param s String. 420 * @return a Element corresponding to s. 421 */ 422 public Word parse(String s) { 423 String st = clean(s); 424 String regex; 425 if (translation == null) { 426 regex = "[" + alphabet + " ]*"; 427 } else { 428 regex = "[" + concat(translation) + " ]*"; 429 } 430 if (!st.matches(regex)) { 431 throw new IllegalArgumentException("word '" + st + "' contains letters not from: " + alphabet 432 + " or from " + concat(translation)); 433 } 434 // now only alphabet or translation chars are contained in st 435 return new Word(this, st, true); 436 } 437 438 439 /** 440 * Parse from Reader. White space is delimiter for word. 441 * @param r Reader. 442 * @return the next Element found on r. 443 */ 444 public Word parse(Reader r) { 445 return parse(StringUtil.nextString(r)); 446 } 447 448 449 /** 450 * Get the descending order comparator. Sorts the highest terms first. 451 * @return horder. 452 */ 453 public WordComparator getDescendComparator() { 454 return horder; 455 } 456 457 458 /** 459 * Get the ascending order comparator. Sorts the lowest terms first. 460 * @return lorder. 461 */ 462 public WordComparator getAscendComparator() { 463 return lorder; 464 } 465 466 467 /** 468 * Prepare parse from String. 469 * @param s String. 470 * @return a Element corresponding to s. 471 */ 472 public static String cleanSpace(String s) { 473 String st = s.trim(); 474 st = st.replaceAll("\\*", ""); 475 st = st.replaceAll("\\s", ""); 476 st = st.replaceAll("\\(", ""); 477 st = st.replaceAll("\\)", ""); 478 st = st.replaceAll("\\\"", ""); 479 return st; 480 } 481 482 483 /** 484 * Prepare parse from String. 485 * @param s String. 486 * @return a Element corresponding to s. 487 */ 488 public static String clean(String s) { 489 String st = s.trim(); 490 st = st.replaceAll("\\*", " "); 491 //st = st.replaceAll("\\s", ""); 492 st = st.replaceAll("\\(", ""); 493 st = st.replaceAll("\\)", ""); 494 st = st.replaceAll("\\\"", ""); 495 return st; 496 } 497 498 499 /** 500 * Prepare parse from String array. 501 * @param v String array. 502 * @return an array of cleaned strings. 503 */ 504 public static String[] cleanAll(String[] v) { 505 String[] t = new String[v.length]; 506 for (int i = 0; i < v.length; i++) { 507 t[i] = cleanSpace(v[i]); 508 if (t[i].length() == 0) { 509 logger.error("empty v[i]: '" + v[i] + "'"); 510 } 511 //System.out.println("clean all: " + v[i] + " --> " + t[i]); 512 } 513 return t; 514 } 515 516 517 /** 518 * Concat variable names. 519 * @param v an array of strings. 520 * @return the concatination of the strings in v. 521 */ 522 public static String concat(String[] v) { 523 StringBuffer s = new StringBuffer(); 524 if (v == null) { 525 return s.toString(); 526 } 527 for (int i = 0; i < v.length; i++) { 528 //String a = v[i]; 529 //if ( a.length() != 1 ) { 530 // //logger.error("v[i] not single letter "+ a); 531 // a = a.substring(0,1); 532 //} 533 s.append(v[i]); 534 } 535 return s.toString(); 536 } 537 538 539 /** 540 * Trim all variable names. 541 * @param v an array of strings. 542 * @return an array of strings with all elements trimmed. 543 */ 544 public static String[] trimAll(String[] v) { 545 String[] t = new String[v.length]; 546 for (int i = 0; i < v.length; i++) { 547 t[i] = v[i].trim(); 548 if (t[i].length() == 0) { 549 logger.error("empty v[i]: '" + v[i] + "'"); 550 } 551 } 552 return t; 553 } 554 555 556 /** 557 * IndexOf for String array. 558 * @param v an array of strings. 559 * @param s string. 560 * @return index of s in v, or -1 if s is not contained in v. 561 */ 562 public static int indexOf(String[] v, String s) { 563 for (int i = 0; i < v.length; i++) { 564 if (s.equals(v[i])) { 565 return i; 566 } 567 } 568 return -1; 569 } 570 571 572 /** 573 * Test if all variables are single letters. 574 * @param v an array of strings. 575 * @return true, if all variables have length 1, else false. 576 */ 577 public static boolean isSingleLetters(String[] v) { 578 for (int i = 0; i < v.length; i++) { 579 if (v[i].length() != 1) { 580 return false; 581 } 582 } 583 return true; 584 } 585 586 587 /** 588 * Translate variable names. 589 * @param v an array of strings. 590 * @return the translated string of v with respect to t. 591 */ 592 public String translate(String[] v) { 593 StringBuffer s = new StringBuffer(); 594 for (int i = 0; i < v.length; i++) { 595 String a = v[i]; 596 int k = indexOf(translation, a); 597 if (k < 0) { 598 System.out.println("t = " + Arrays.toString(translation)); 599 System.out.println("v = " + Arrays.toString(v)); 600 logger.error("v[i] not found in t: " + a); 601 //continue; 602 throw new IllegalArgumentException("v[i] not found in t: " + a); 603 } 604 s.append(transRef.charAt(k)); 605 } 606 return s.toString(); 607 } 608 609 610 /** 611 * Translate variable name. 612 * @param c internal char. 613 * @return the extenal translated string. 614 */ 615 public String transVar(char c) { 616 int k = alphabet.indexOf(c); 617 return translation[k]; 618 } 619 620}