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