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