• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }