1 /* 2 ******************************************************************************* 3 * Copyright (C) 2002-2012, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 package org.unicode.cldr.util; 8 9 import com.google.common.collect.LinkedHashMultiset; 10 import com.google.common.collect.Multiset; 11 import com.ibm.icu.text.UTF16; 12 import com.ibm.icu.text.UnicodeSet; 13 import java.util.ArrayList; 14 import java.util.Arrays; 15 import java.util.HashSet; 16 import java.util.Random; 17 import java.util.Set; 18 19 public abstract class Pick { 20 private static boolean DEBUG = false; 21 22 // for using to get strings 23 24 static class Target { 25 26 static int MAX_COUNT = 5; 27 28 private Pick pick; 29 private Random random; 30 private Quoter quoter; 31 private Multiset<Pick> stack = LinkedHashMultiset.create(); 32 make(Pick pick, Random random, Quoter quoter)33 public static Target make(Pick pick, Random random, Quoter quoter) { 34 Target result = new Target(); 35 result.pick = pick; 36 result.random = random; 37 result.quoter = quoter; 38 return result; 39 } 40 next()41 public String next() { 42 quoter.clear(); 43 stack.clear(); 44 while (true) { 45 try { 46 pick.addTo(this); 47 return get(); 48 } catch (DepthExceededException e) { 49 for (Pick pick : e.target.stack.elementSet()) { 50 System.out.println(pick.name + ": " + e.target.stack.count(pick)); 51 } 52 int debug = 0; 53 } 54 } 55 } 56 get()57 public String get() { 58 return quoter.toString(); 59 } 60 copyState(Target other)61 private void copyState(Target other) { 62 random = other.random; 63 } 64 clear()65 private void clear() { 66 quoter.clear(); 67 } 68 69 /*private int length() { 70 return quoter.length(); 71 }*/ append(int codepoint)72 private Target append(int codepoint) { 73 if (codepoint == '-') { 74 int debug = 0; 75 } 76 quoter.append(codepoint); 77 return this; 78 } 79 append(String s)80 private Target append(String s) { 81 if (s.contains("-")) { 82 int debug = 0; 83 } 84 quoter.append(s); 85 return this; 86 } 87 88 // must return value between 0 (inc) and 1 (exc) nextDouble()89 private double nextDouble() { 90 return random.nextDouble(); 91 } 92 exitStack(Pick pick)93 public void exitStack(Pick pick) { 94 stack.remove(pick); 95 } 96 enterStack(Pick pick)97 public void enterStack(Pick pick) { 98 int count = stack.count(pick); 99 if (count > MAX_COUNT) { 100 throw new DepthExceededException(this, pick); 101 } 102 stack.add(pick); 103 } 104 } 105 106 // for Building 107 replace(String toReplace, Pick replacement)108 public Pick replace(String toReplace, Pick replacement) { 109 Replacer visitor = new Replacer(toReplace, replacement); 110 return visit(visitor); 111 } 112 name(String nameStr)113 public Pick name(String nameStr) { 114 name = nameStr; 115 return this; 116 } 117 makeSequence()118 public static Pick.Sequence makeSequence() { 119 return new Sequence(); 120 } 121 makeAlternation()122 public static Pick.Alternation makeAlternation() { 123 return new Alternation(); 124 } 125 /* 126 static public Pick.Sequence and(Object item) { 127 return new Sequence().and2(item); 128 } 129 static public Pick.Sequence and(Object[] items) { 130 return new Sequence().and2(items); 131 } 132 static public Pick.Alternation or(int itemWeight, Object item) { 133 return new Alternation().or2(itemWeight, item); 134 } 135 static public Pick.Alternation or(Object[] items) { 136 return new Alternation().or2(1, items); 137 } 138 static public Pick.Alternation or(int itemWeight, Object[] items) { 139 return new Alternation().or2(itemWeight, items); 140 } 141 static public Pick.Alternation or(int[] itemWeights, Object[] items) { 142 return new Alternation().or2(itemWeights, items); 143 } 144 145 static public Pick maybe(int percent, Object item) { 146 return new Repeat(0, 1, new int[]{100-percent, percent}, item); 147 //return Pick.or(1.0-percent, NOTHING).or2(percent, item); 148 } 149 static public Pick repeat(int minCount, int maxCount, int itemWeights, Object item) { 150 return new Repeat(minCount, maxCount, itemWeights, item); 151 } 152 153 static public Pick codePoint(String source) { 154 return new CodePoint(new UnicodeSet(source)); 155 } 156 */ 157 repeat(int minCount, int maxCount, int[] itemWeights, Pick item)158 public static Pick repeat(int minCount, int maxCount, int[] itemWeights, Pick item) { 159 return new Repeat(minCount, maxCount, itemWeights, item); 160 } 161 codePoint(UnicodeSet source)162 public static Pick codePoint(UnicodeSet source) { 163 return new CodePoint(source); 164 } 165 string(String source)166 public static Pick string(String source) { 167 return new Literal(source); 168 } 169 /* 170 static public Pick unquoted(String source) { 171 return new Literal(source); 172 } 173 static public Pick string(int minLength, int maxLength, Pick item) { 174 return new Morph(item, minLength, maxLength); 175 } 176 */ 177 getInternal(int depth, Set alreadySeen)178 public abstract String getInternal(int depth, Set alreadySeen); 179 // Internals 180 181 protected String name; 182 addTo(Target target)183 protected abstract void addTo(Target target); 184 match(String input, Position p)185 public abstract boolean match(String input, Position p); 186 187 static class DepthExceededException extends RuntimeException { 188 private static final long serialVersionUID = -2478735802169169979L; 189 private final Target target; 190 private final Pick pick; 191 DepthExceededException(Target target, Pick pick)192 public DepthExceededException(Target target, Pick pick) { 193 this.target = target; 194 this.pick = pick; 195 } 196 } 197 198 public static class Sequence extends ListPick { and2(Pick item)199 public Sequence and2(Pick item) { 200 addInternal(new Pick[] {item}); // we don't care about perf 201 return this; // for chaining 202 } 203 and2(Pick[] itemArray)204 public Sequence and2(Pick[] itemArray) { 205 addInternal(itemArray); 206 return this; // for chaining 207 } 208 209 @Override addTo(Target target)210 protected void addTo(Target target) { 211 for (int i = 0; i < items.length; ++i) { 212 items[i].addTo(target); 213 } 214 } 215 216 @Override getInternal(int depth, Set alreadySeen)217 public String getInternal(int depth, Set alreadySeen) { 218 String result = checkName(name, alreadySeen); 219 if (result.startsWith("$")) return result; 220 result = indent(depth) + result + "SEQ("; 221 for (int i = 0; i < items.length; ++i) { 222 if (i != 0) result += ", "; 223 result += items[i].getInternal(depth + 1, alreadySeen); 224 } 225 result += ")"; 226 return result; 227 } 228 229 // keep private Sequence()230 private Sequence() {} 231 232 @Override match(String input, Position p)233 public boolean match(String input, Position p) { 234 int originalIndex = p.index; 235 for (int i = 0; i < items.length; ++i) { 236 if (!items[i].match(input, p)) { 237 p.index = originalIndex; 238 return false; 239 } 240 } 241 return true; 242 } 243 } 244 checkName(String nameStr, Set alreadySeen)245 String checkName(String nameStr, Set alreadySeen) { 246 if (nameStr == null) return ""; 247 if (alreadySeen.contains(nameStr)) return nameStr; 248 alreadySeen.add(nameStr); 249 return "{" + nameStr + "=}"; 250 } 251 252 public static class Alternation extends ListPick { 253 private WeightedIndex weightedIndex = new WeightedIndex(0); 254 or2(Pick[] newItems)255 public Alternation or2(Pick[] newItems) { 256 return or2(1, newItems); 257 } 258 or2(int itemWeight, Pick item)259 public Alternation or2(int itemWeight, Pick item) { 260 return or2(itemWeight, new Pick[] {item}); // we don't care about perf 261 } 262 or2(int itemWeight, Pick[] newItems)263 public Alternation or2(int itemWeight, Pick[] newItems) { 264 int[] itemWeights = new int[newItems.length]; 265 Arrays.fill(itemWeights, itemWeight); 266 return or2(itemWeights, newItems); // we don't care about perf 267 } 268 or2(int[] itemWeights, Pick[] newItems)269 public Alternation or2(int[] itemWeights, Pick[] newItems) { 270 if (newItems.length != itemWeights.length) { 271 throw new ArrayIndexOutOfBoundsException( 272 "or lengths must be equal: " 273 + newItems.length 274 + " != " 275 + itemWeights.length); 276 } 277 // int lastLen = this.items.length; 278 addInternal(newItems); 279 weightedIndex.add(itemWeights); 280 return this; // for chaining 281 } 282 283 @Override addTo(Target target)284 protected void addTo(Target target) { 285 final int index = weightedIndex.toIndex(target.nextDouble()); 286 int last = index - 1; 287 if (last < weightedIndex.minCount) { 288 last -= weightedIndex.minCount; 289 last += weightedIndex.weights.length; 290 } 291 for (int i = index; ; ) { 292 try { 293 target.enterStack(this); 294 items[index].addTo(target); // may cause exception if stack overflows 295 // no exception, continue normally 296 target.exitStack(this); 297 return; 298 } catch (DepthExceededException e) { 299 target.exitStack(this); 300 if (i == last) { 301 throw e; // we tried all the options, and none of them work. 302 } 303 i++; 304 if (i >= weightedIndex.weights.length) { 305 i -= weightedIndex.weights.length - weightedIndex.minCount; 306 } 307 } 308 } 309 } 310 311 @Override getInternal(int depth, Set alreadySeen)312 public String getInternal(int depth, Set alreadySeen) { 313 String result = checkName(name, alreadySeen); 314 if (result.startsWith("$")) return result; 315 result = indent(depth) + result + "OR("; 316 for (int i = 0; i < items.length; ++i) { 317 if (i != 0) result += ", "; 318 result += 319 items[i].getInternal(depth + 1, alreadySeen) 320 + "/" 321 + weightedIndex.weights[i]; 322 } 323 return result + ")"; 324 } 325 326 // keep private Alternation()327 private Alternation() {} 328 329 // take first matching option 330 @Override match(String input, Position p)331 public boolean match(String input, Position p) { 332 for (int i = 0; i < weightedIndex.weights.length; ++i) { 333 if (p.isFailure(this, i)) continue; 334 if (items[i].match(input, p)) return true; 335 p.setFailure(this, i); 336 } 337 return false; 338 } 339 } 340 indent(int depth)341 private static String indent(int depth) { 342 String result = "\r\n"; 343 for (int i = 0; i < depth; ++i) { 344 result += " "; 345 } 346 return result; 347 } 348 349 private static class Repeat extends ItemPick { 350 WeightedIndex weightedIndex; 351 int minCount = 0; 352 Repeat(int minCount, int maxCount, int[] itemWeights, Pick item)353 private Repeat(int minCount, int maxCount, int[] itemWeights, Pick item) { 354 super(item); 355 weightedIndex = new WeightedIndex(minCount).add(maxCount - minCount + 1, itemWeights); 356 } 357 358 /*private Repeat(int minCount, int maxCount, int itemWeight, Pick item) { 359 super(item); 360 weightedIndex = new WeightedIndex(minCount).add(maxCount-minCount+1, itemWeight); 361 }*/ 362 /* 363 private Repeat(int minCount, int maxCount, Object item) { 364 this.item = convert(item); 365 weightedIndex = new WeightedIndex(minCount).add(maxCount-minCount+1, 1); 366 } 367 */ 368 @Override addTo(Target target)369 protected void addTo(Target target) { 370 // int count ; 371 final int count = weightedIndex.toIndex(target.nextDouble()); 372 for (int i = count; i > 0; --i) { 373 item.addTo(target); 374 } 375 } 376 377 @Override getInternal(int depth, Set alreadySeen)378 public String getInternal(int depth, Set alreadySeen) { 379 String result = checkName(name, alreadySeen); 380 if (result.startsWith("$")) return result; 381 result = 382 indent(depth) 383 + result 384 + "REPEAT(" 385 + weightedIndex 386 + "; " 387 + item.getInternal(depth + 1, alreadySeen) 388 + ")"; 389 return result; 390 } 391 392 // match longest, e.g. up to just before a failure 393 @Override match(String input, Position p)394 public boolean match(String input, Position p) { 395 // int bestMatch = p.index; 396 int count = 0; 397 for (int i = 0; i < weightedIndex.weights.length; ++i) { 398 if (p.isFailure(this, i)) break; 399 if (!item.match(input, p)) { 400 p.setFailure(this, i); 401 break; 402 } 403 // bestMatch = p.index; 404 count++; 405 } 406 if (count >= minCount) { 407 return true; 408 } 409 // TODO fix failure 410 return false; 411 } 412 } 413 414 private static class CodePoint extends FinalPick { 415 private UnicodeSet source; 416 CodePoint(UnicodeSet source)417 private CodePoint(UnicodeSet source) { 418 this.source = source; 419 } 420 421 @Override addTo(Target target)422 protected void addTo(Target target) { 423 target.append(source.charAt(pick(target.random, 0, source.size() - 1))); 424 } 425 426 @Override match(String s, Position p)427 public boolean match(String s, Position p) { 428 int cp = UTF16.charAt(s, p.index); 429 if (source.contains(cp)) { 430 p.index += UTF16.getCharCount(cp); 431 return true; 432 } 433 p.setMax("codePoint"); 434 return false; 435 } 436 437 @Override getInternal(int depth, Set alreadySeen)438 public String getInternal(int depth, Set alreadySeen) { 439 String result = checkName(name, alreadySeen); 440 if (result.startsWith("$")) return result; 441 return source.toString(); 442 } 443 } 444 445 static class Morph extends ItemPick { Morph(Pick item)446 Morph(Pick item) { 447 super(item); 448 } 449 450 private String lastValue = null; 451 private Target addBuffer = Target.make(this, null, new Quoter.RuleQuoter()); 452 private StringBuffer mergeBuffer = new StringBuffer(); 453 454 private static final int COPY_NEW = 0, 455 COPY_BOTH = 1, 456 COPY_LAST = 3, 457 SKIP = 4, 458 LEAST_SKIP = 4; 459 // give weights to the above. make sure we delete about the same as we insert 460 private static final WeightedIndex choice = 461 new WeightedIndex(0).add(new int[] {10, 10, 100, 10}); 462 463 @Override addTo(Target target)464 protected void addTo(Target target) { 465 // get contents into separate buffer 466 addBuffer.copyState(target); 467 addBuffer.clear(); 468 item.addTo(addBuffer); 469 String newValue = addBuffer.get(); 470 if (DEBUG) System.out.println("Old: " + lastValue + ", New:" + newValue); 471 472 // if not first one, merge with old 473 if (lastValue != null) { 474 mergeBuffer.setLength(0); 475 int lastIndex = 0; 476 int newIndex = 0; 477 // the new length is a random value between old and new. 478 int newLenLimit = pick(target.random, lastValue.length(), newValue.length()); 479 480 while (mergeBuffer.length() < newLenLimit 481 && newIndex < newValue.length() 482 && lastIndex < lastValue.length()) { 483 int c = choice.toIndex(target.nextDouble()); 484 if (c == COPY_NEW || c == COPY_BOTH || c == SKIP) { 485 newIndex = getChar(newValue, newIndex, mergeBuffer, c < LEAST_SKIP); 486 if (mergeBuffer.length() >= newLenLimit) break; 487 } 488 if (c == COPY_LAST || c == COPY_BOTH || c == SKIP) { 489 lastIndex = getChar(lastValue, lastIndex, mergeBuffer, c < LEAST_SKIP); 490 } 491 } 492 newValue = mergeBuffer.toString(); 493 } 494 lastValue = newValue; 495 target.append(newValue); 496 if (DEBUG) System.out.println("Result: " + newValue); 497 } 498 499 @Override getInternal(int depth, Set alreadySeen)500 public String getInternal(int depth, Set alreadySeen) { 501 String result = checkName(name, alreadySeen); 502 if (result.startsWith("$")) return result; 503 return indent(depth) 504 + result 505 + "MORPH(" 506 + item.getInternal(depth + 1, alreadySeen) 507 + ")"; 508 } 509 510 /* (non-Javadoc) 511 * @see Pick#match(java.lang.String, Pick.Position) 512 */ 513 @Override match(String input, Position p)514 public boolean match(String input, Position p) { 515 // TODO Auto-generated method stub 516 return false; 517 } 518 } 519 520 /* Add character if we can 521 */ getChar(String newValue, int newIndex, StringBuffer mergeBuffer, boolean copy)522 static int getChar(String newValue, int newIndex, StringBuffer mergeBuffer, boolean copy) { 523 if (newIndex >= newValue.length()) return newIndex; 524 int cp = UTF16.charAt(newValue, newIndex); 525 if (copy) UTF16.append(mergeBuffer, cp); 526 return newIndex + UTF16.getCharCount(cp); 527 } 528 529 /* 530 // quoted add 531 appendQuoted(target, addBuffer.toString(), quoteBuffer); 532 // fix buffers 533 StringBuffer swapTemp = addBuffer; 534 addBuffer = source; 535 source = swapTemp; 536 } 537 } 538 */ 539 540 static class Quote extends ItemPick { Quote(Pick item)541 Quote(Pick item) { 542 super(item); 543 } 544 545 @Override addTo(Target target)546 protected void addTo(Target target) { 547 target.quoter.setQuoting(true); 548 item.addTo(target); 549 target.quoter.setQuoting(false); 550 } 551 552 @Override match(String s, Position p)553 public boolean match(String s, Position p) { 554 return false; 555 } 556 557 @Override getInternal(int depth, Set alreadySeen)558 public String getInternal(int depth, Set alreadySeen) { 559 String result = checkName(name, alreadySeen); 560 if (result.startsWith("$")) return result; 561 return indent(depth) 562 + result 563 + "QUOTE(" 564 + item.getInternal(depth + 1, alreadySeen) 565 + ")"; 566 } 567 } 568 569 private static class Literal extends FinalPick { 570 @Override toString()571 public String toString() { 572 return name; 573 } 574 Literal(String source)575 private Literal(String source) { 576 this.name = source; 577 } 578 579 @Override addTo(Target target)580 protected void addTo(Target target) { 581 target.append(name); 582 } 583 584 @Override match(String input, Position p)585 public boolean match(String input, Position p) { 586 int len = name.length(); 587 if (input.regionMatches(p.index, name, 0, len)) { 588 p.index += len; 589 return true; 590 } 591 p.setMax("literal"); 592 return false; 593 } 594 595 @Override getInternal(int depth, Set alreadySeen)596 public String getInternal(int depth, Set alreadySeen) { 597 return "'" + name + "'"; 598 } 599 } 600 601 public static class Position { 602 public ArrayList failures = new ArrayList(); 603 public int index; 604 public int maxInt; 605 public String maxType; 606 setMax(String type)607 public void setMax(String type) { 608 if (index >= maxInt) { 609 maxType = type; 610 } 611 } 612 613 @Override toString()614 public String toString() { 615 return "index; " + index + ", maxInt:" + maxInt + ", maxType: " + maxType; 616 } 617 /*private static final Object BAD = new Object(); 618 private static final Object GOOD = new Object();*/ 619 isFailure(Pick pick, int item)620 public boolean isFailure(Pick pick, int item) { 621 ArrayList val = (ArrayList) failures.get(index); 622 if (val == null) return false; 623 Set set = (Set) val.get(item); 624 if (set == null) return false; 625 return !set.contains(pick); 626 } 627 setFailure(Pick pick, int item)628 public void setFailure(Pick pick, int item) { 629 ArrayList val = (ArrayList) failures.get(index); 630 if (val == null) { 631 val = new ArrayList(); 632 failures.set(index, val); 633 } 634 Set set = (Set) val.get(item); 635 if (set == null) { 636 set = new HashSet(); 637 val.set(item, set); 638 } 639 set.add(pick); 640 } 641 } 642 643 /* 644 public static final Pick NOTHING = new Nothing(); 645 646 647 private static class Nothing extends FinalPick { 648 protected void addTo(Target target) {} 649 protected boolean match(String input, Position p) { 650 return true; 651 } 652 public String getInternal(int depth, Set alreadySeen) { 653 return indent(depth) + "\u00F8"; 654 } 655 } 656 */ 657 658 // intermediates 659 660 abstract static class Visitor { 661 Set already = new HashSet(); 662 663 // Note: each visitor should return the Pick that will replace a (or a itself) handle(Pick a)664 abstract Pick handle(Pick a); 665 alreadyEntered(Pick item)666 boolean alreadyEntered(Pick item) { 667 boolean result = already.contains(item); 668 already.add(item); 669 return result; 670 } 671 reset()672 void reset() { 673 already.clear(); 674 } 675 } 676 visit(Visitor visitor)677 protected abstract Pick visit(Visitor visitor); 678 679 static class Replacer extends Visitor { 680 String toReplace; 681 Pick replacement; 682 Replacer(String toReplace, Pick replacement)683 Replacer(String toReplace, Pick replacement) { 684 this.toReplace = toReplace; 685 this.replacement = replacement; 686 } 687 688 @Override handle(Pick a)689 public Pick handle(Pick a) { 690 if (toReplace.equals(a.name)) { 691 a = replacement; 692 } 693 return a; 694 } 695 } 696 697 private abstract static class FinalPick extends Pick { 698 @Override visit(Visitor visitor)699 public Pick visit(Visitor visitor) { 700 return visitor.handle(this); 701 } 702 } 703 704 private abstract static class ItemPick extends Pick { 705 protected Pick item; 706 ItemPick(Pick item)707 ItemPick(Pick item) { 708 this.item = item; 709 } 710 711 @Override visit(Visitor visitor)712 public Pick visit(Visitor visitor) { 713 Pick result = visitor.handle(this); 714 if (visitor.alreadyEntered(this)) return result; 715 if (item != null) item = item.visit(visitor); 716 return result; 717 } 718 } 719 720 private abstract static class ListPick extends Pick { 721 protected Pick[] items = new Pick[0]; 722 simplify()723 Pick simplify() { 724 if (items.length > 1) return this; 725 if (items.length == 1) return items[0]; 726 return null; 727 } 728 size()729 int size() { 730 return items.length; 731 } 732 getLast()733 Pick getLast() { 734 return items[items.length - 1]; 735 } 736 setLast(Pick newOne)737 void setLast(Pick newOne) { 738 items[items.length - 1] = newOne; 739 } 740 addInternal(Pick[] objs)741 protected void addInternal(Pick[] objs) { 742 int lastLen = items.length; 743 items = realloc(items, items.length + objs.length); 744 for (int i = 0; i < objs.length; ++i) { 745 items[lastLen + i] = objs[i]; 746 } 747 } 748 749 @Override visit(Visitor visitor)750 public Pick visit(Visitor visitor) { 751 Pick result = visitor.handle(this); 752 if (visitor.alreadyEntered(this)) return result; 753 for (int i = 0; i < items.length; ++i) { 754 items[i] = items[i].visit(visitor); 755 } 756 return result; 757 } 758 } 759 760 /** 761 * Simple class to distribute a number between 0 (inclusive) and 1 (exclusive) among a number of 762 * indices, where each index is weighted. Item weights may be zero, but cannot be negative. 763 * 764 * @author Davis 765 */ 766 // As in other case, we use an array for runtime speed; don't care about buildspeed. 767 public static class WeightedIndex { 768 private int[] weights = new int[0]; 769 private int minCount = 0; 770 private double total; 771 WeightedIndex(int minCount)772 public WeightedIndex(int minCount) { 773 this.minCount = minCount; 774 } 775 add(int count, int itemWeights)776 public WeightedIndex add(int count, int itemWeights) { 777 if (count > 0) { 778 int[] newWeights = new int[count]; 779 if (itemWeights < 1) itemWeights = 1; 780 Arrays.fill(newWeights, 0, count, itemWeights); 781 add(1, newWeights); 782 } 783 return this; // for chaining 784 } 785 add(int[] newWeights)786 public WeightedIndex add(int[] newWeights) { 787 return add(newWeights.length, newWeights); 788 } 789 add(int maxCount, int[] newWeights)790 public WeightedIndex add(int maxCount, int[] newWeights) { 791 if (newWeights == null) newWeights = new int[] {1}; 792 int oldLen = weights.length; 793 if (maxCount < newWeights.length) maxCount = newWeights.length; 794 weights = realloc(weights, weights.length + maxCount); 795 System.arraycopy(newWeights, 0, weights, oldLen, newWeights.length); 796 int lastWeight = weights[oldLen + newWeights.length - 1]; 797 for (int i = oldLen + newWeights.length; i < maxCount; ++i) { 798 weights[i] = lastWeight; 799 } 800 total = 0; 801 for (int i = 0; i < weights.length; ++i) { 802 if (weights[i] < 0) { 803 throw new RuntimeException("only positive weights: " + i); 804 } 805 total += weights[i]; 806 } 807 return this; // for chaining 808 } 809 810 // TODO, make this more efficient toIndex(double zeroToOne)811 public int toIndex(double zeroToOne) { 812 double weight = zeroToOne * total; 813 int i; 814 for (i = 0; i < weights.length; ++i) { 815 weight -= weights[i]; 816 if (weight <= 0) break; 817 } 818 return i + minCount; 819 } 820 821 @Override toString()822 public String toString() { 823 String result = ""; 824 for (int i = 0; i < minCount; ++i) { 825 if (result.length() != 0) result += ","; 826 result += "0"; 827 } 828 for (int i = 0; i < weights.length; ++i) { 829 if (result.length() != 0) result += ","; 830 result += weights[i]; 831 } 832 return result; 833 } 834 } 835 /* 836 private static Pick convert(Object obj) { 837 if (obj instanceof Pick) return (Pick)obj; 838 return new Literal(obj.toString(), false); 839 } 840 */ 841 // Useful statics 842 pick(Random random, int start, int end)843 public static int pick(Random random, int start, int end) { 844 return start + (int) (random.nextDouble() * (end + 1 - start)); 845 } 846 pick(Random random, double start, double end)847 public static double pick(Random random, double start, double end) { 848 return start + (random.nextDouble() * (end + 1 - start)); 849 } 850 pick(Random random, double percent)851 public static boolean pick(Random random, double percent) { 852 return random.nextDouble() <= percent; 853 } 854 pick(Random random, UnicodeSet s)855 public static int pick(Random random, UnicodeSet s) { 856 return s.charAt(pick(random, 0, s.size() - 1)); 857 } 858 pick(Random random, String[] source)859 public static String pick(Random random, String[] source) { 860 return source[pick(random, 0, source.length - 1)]; 861 } 862 863 // these utilities really ought to be in Java 864 realloc(double[] source, int newSize)865 public static double[] realloc(double[] source, int newSize) { 866 double[] temp = new double[newSize]; 867 if (newSize > source.length) newSize = source.length; 868 if (newSize != 0) System.arraycopy(source, 0, temp, 0, newSize); 869 return temp; 870 } 871 realloc(int[] source, int newSize)872 public static int[] realloc(int[] source, int newSize) { 873 int[] temp = new int[newSize]; 874 if (newSize > source.length) newSize = source.length; 875 if (newSize != 0) System.arraycopy(source, 0, temp, 0, newSize); 876 return temp; 877 } 878 realloc(Pick[] source, int newSize)879 public static Pick[] realloc(Pick[] source, int newSize) { 880 Pick[] temp = new Pick[newSize]; 881 if (newSize > source.length) newSize = source.length; 882 if (newSize != 0) System.arraycopy(source, 0, temp, 0, newSize); 883 return temp; 884 } 885 886 // test utilities 887 /*private static void append(StringBuffer target, String toAdd, StringBuffer quoteBuffer) { 888 Utility.appendToRule(target, (int)-1, true, false, quoteBuffer); // close previous quote 889 if (DEBUG) System.out.println("\"" + toAdd + "\""); 890 target.append(toAdd); 891 } 892 893 private static void appendQuoted(StringBuffer target, String toAdd, StringBuffer quoteBuffer) { 894 if (DEBUG) System.out.println("\"" + toAdd + "\""); 895 Utility.appendToRule(target, toAdd, false, false, quoteBuffer); 896 }*/ 897 898 /* 899 public static abstract class MatchHandler { 900 public abstract void handleString(String source, int start, int limit); 901 public abstract void handleSequence(String source, int start, int limit); 902 public abstract void handleAlternation(String source, int start, int limit); 903 904 } 905 */ 906 /* 907 // redistributes random value 908 // values are still between 0 and 1, but with a different distribution 909 public interface Spread { 910 public double spread(double value); 911 } 912 913 // give the weight for the high end. 914 // values are linearly scaled according to the weight. 915 static public class SimpleSpread implements Spread { 916 static final Spread FLAT = new SimpleSpread(1.0); 917 boolean flat = false; 918 double aa, bb, cc; 919 public SimpleSpread(double maxWeight) { 920 if (maxWeight > 0.999 && maxWeight < 1.001) { 921 flat = true; 922 } else { 923 double q = (maxWeight - 1.0); 924 aa = -1/q; 925 bb = 1/(q*q); 926 cc = (2.0+q)/q; 927 } 928 } 929 public double spread(double value) { 930 if (flat) return value; 931 value = aa + Math.sqrt(bb + cc*value); 932 if (value < 0.0) return 0.0; // catch math gorp 933 if (value >= 1.0) return 1.0; 934 return value; 935 } 936 } 937 static public int pick(Spread spread, Random random, int start, int end) { 938 return start + (int)(spread.spread(random.nextDouble()) * (end + 1 - start)); 939 } 940 941 */ 942 943 } 944