package com.ldbc.driver.generator;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.ldbc.driver.Operation;
import com.ldbc.driver.util.Function0;
import com.ldbc.driver.util.Function1;
import com.ldbc.driver.util.Function2;
import com.ldbc.driver.util.Tuple;
import com.ldbc.driver.util.Tuple2;
import com.ldbc.driver.util.Tuple3;
import org.apache.commons.math3.random.RandomDataGenerator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static java.lang.String.format;
public class GeneratorFactory
{
private final RandomDataGeneratorFactory randomDataGeneratorFactory;
public GeneratorFactory( RandomDataGeneratorFactory randomDataGeneratorFactory )
{
this.randomDataGeneratorFactory = randomDataGeneratorFactory;
}
private RandomDataGenerator randomDataGenerator = null;
// Every returned generator (that takes a RandomDataGenerator as input) will use a different RandomDataGenerator
// UPDATE (2014/08/11): for performance (random data generator instantiation seems to be expensive), one
// generator shared among all generators
RandomDataGenerator getRandom()
{
if ( null == randomDataGenerator )
{ randomDataGenerator = randomDataGeneratorFactory.newRandom(); }
return randomDataGenerator;
}
/*
* ----------------------------------------------------------------------------------------------------
* ---------------------------------------- UTILS -----------------------------------------------------
* ----------------------------------------------------------------------------------------------------
*/
public enum OperationStreamComparisonResultType
{
PASS,
FAIL_ONE_STREAM_IS_EMPTY,
FAIL_STREAMS_HAVE_DIFFERENT_LENGTH,
FAIL_ONE_OPERATION_IS_NULL,
FAIL_OPERATIONS_NOT_EQUAL,
FAIL_ONE_START_TIME_IS_NULL,
FAIL_ONE_DEPENDENCY_TIME_IS_NULL,
FAIL_START_TIMES_NOT_EQUAL,
FAIL_TIME_STAMPS_NOT_EQUAL,
FAIL_DEPENDENCY_TIME_STAMPS_NOT_EQUAL
}
public class OperationStreamComparisonResult
{
private final String errorMessage;
private final OperationStreamComparisonResultType result;
public OperationStreamComparisonResult( String errorMessage, OperationStreamComparisonResultType result )
{
this.errorMessage = errorMessage;
this.result = result;
}
public String errorMessage()
{
return errorMessage;
}
public OperationStreamComparisonResultType resultType()
{
return result;
}
}
/**
* Compare operation streams by stream lengths and equality of the operations they contain
*
* @param operationStream1
* @param operationStream2
* @param compareTimes
* @return
*/
// TODO move into a separate class, test that class separately, use that class here
// TODO add check for timeStamp()
public OperationStreamComparisonResult compareOperationStreams( Iterator<Operation> operationStream1,
Iterator<Operation> operationStream2,
boolean compareTimes )
{
int operationNumber = 0;
if ( operationStream1.hasNext() != operationStream2.hasNext() )
{
return new OperationStreamComparisonResult( "",
OperationStreamComparisonResultType.FAIL_ONE_STREAM_IS_EMPTY );
}
while ( operationStream1.hasNext() )
{
operationNumber++;
if ( false == operationStream2.hasNext() )
{
return new OperationStreamComparisonResult(
format( "operation %s\nstream 2 is shorter", operationNumber ),
OperationStreamComparisonResultType.FAIL_STREAMS_HAVE_DIFFERENT_LENGTH );
}
Operation next1 = operationStream1.next();
Operation next2 = operationStream2.next();
if ( null == next1 && null == next2 )
{ continue; }
if ( null == next1 || null == next2 )
{
return new OperationStreamComparisonResult(
format( "operation %s\none operation is null\nstream 1: %s\nstream 2: %s",
operationNumber, next1, next2 ),
OperationStreamComparisonResultType.FAIL_ONE_OPERATION_IS_NULL );
}
else if ( false == next1.equals( next2 ) )
{
return new OperationStreamComparisonResult(
format( "operation %s\noperations not equal\nstream 1: %s\nstream 2: %s",
operationNumber, next1, next2 ),
OperationStreamComparisonResultType.FAIL_OPERATIONS_NOT_EQUAL );
}
if ( compareTimes )
{
long scheduledStartTimeAsMilli1 = next1.scheduledStartTimeAsMilli();
long scheduledStartTimeAsMilli2 = next2.scheduledStartTimeAsMilli();
if ( -1 == scheduledStartTimeAsMilli1 && -1 == scheduledStartTimeAsMilli2 )
{
// do nothing
}
else if ( -1 == scheduledStartTimeAsMilli1 || -1 == scheduledStartTimeAsMilli2 )
{
return new OperationStreamComparisonResult(
format( "operation %s\none start time is null\nstream 1: %s\nstream 2: %s",
scheduledStartTimeAsMilli1, scheduledStartTimeAsMilli2, operationNumber ),
OperationStreamComparisonResultType.FAIL_ONE_START_TIME_IS_NULL );
}
else if ( scheduledStartTimeAsMilli1 != scheduledStartTimeAsMilli2 )
{
return new OperationStreamComparisonResult(
format( "operation %s\nstart times not equal\nstream 1: %s\nstream 2: %s",
operationNumber, scheduledStartTimeAsMilli1, scheduledStartTimeAsMilli2 ),
OperationStreamComparisonResultType.FAIL_START_TIMES_NOT_EQUAL );
}
long timeStamp1 = next1.timeStamp();
long timeStamp2 = next2.timeStamp();
if ( -1 == timeStamp1 && -1 == timeStamp2 )
{
// do nothing
}
else if ( -1 == timeStamp1 || -1 == timeStamp2 )
{
return new OperationStreamComparisonResult(
format( "operation %s\none time stamp is null\nstream 1: %s\nstream 2: %s",
timeStamp1, timeStamp2, operationNumber ),
OperationStreamComparisonResultType.FAIL_ONE_START_TIME_IS_NULL );
}
else if ( timeStamp1 != timeStamp2 )
{
return new OperationStreamComparisonResult(
format( "operation %s\ntime stamps not equal\nstream 1: %s\nstream 2: %s",
operationNumber, timeStamp1, timeStamp2 ),
OperationStreamComparisonResultType.FAIL_TIME_STAMPS_NOT_EQUAL );
}
long dependencyTimeStamp1 = next1.dependencyTimeStamp();
long dependencyTimeStamp2 = next2.dependencyTimeStamp();
if ( -1 == dependencyTimeStamp1 && -1 == dependencyTimeStamp2 )
{
// do nothing
}
else if ( -1 == dependencyTimeStamp1 || -1 == dependencyTimeStamp2 )
{
return new OperationStreamComparisonResult(
format( "operation %s\none dependency time is null\nstream1: %s\nstream2: %s",
dependencyTimeStamp1, dependencyTimeStamp2, operationNumber ),
OperationStreamComparisonResultType.FAIL_ONE_DEPENDENCY_TIME_IS_NULL );
}
else if ( dependencyTimeStamp1 != dependencyTimeStamp2 )
{
return new OperationStreamComparisonResult(
format( "operation %s\ndependency times not equal\nstream 1: %s\nstream 2: %s",
operationNumber, dependencyTimeStamp1, dependencyTimeStamp2 ),
OperationStreamComparisonResultType.FAIL_DEPENDENCY_TIME_STAMPS_NOT_EQUAL );
}
}
}
if ( operationStream2.hasNext() )
{
return new OperationStreamComparisonResult(
format( "operation %s\nstream 1 is shorter", operationNumber ),
OperationStreamComparisonResultType.FAIL_STREAMS_HAVE_DIFFERENT_LENGTH );
}
return new OperationStreamComparisonResult( "", OperationStreamComparisonResultType.PASS );
}
public <T> void consume( Iterator<T> generator, long count )
{
for ( long consumed = 0; generator.hasNext() && consumed < count; consumed++ )
{
generator.next();
}
}
/*
* ----------------------------------------------------------------------------------------------------
* ---------------------------------------- DECORATORS ------------------------------------------------
* ----------------------------------------------------------------------------------------------------
*/
/**
* Wraps any number generator and keeps track of the minimum and maximum numbers returned by that generator.
*
* @param generator
* @param initialMin
* @param initialMax
* @param <T>
* @return
*/
public <T extends Number> MinMaxGenerator<T> minMaxGenerator( Iterator<T> generator, T initialMin, T initialMax )
{
return new MinMaxGenerator<T>( generator, initialMin, initialMax );
}
/*
* ----------------------------------------------------------------------------------------------------
* ---------------------------------------- GENERATORS ------------------------------------------------
* ----------------------------------------------------------------------------------------------------
*/
public <T1> Iterator<T1> includeOnly( Iterator<T1> generator, T1... includedItems )
{
return includeOnly( generator, new IncludeOnlyPredicate<>( includedItems ) );
}
public <T1> Iterator<T1> includeOnly( Iterator<T1> generator, Predicate<T1> isIncludedPredicate )
{
return Iterators.filter( generator, isIncludedPredicate );
}
private class IncludeOnlyPredicate<T1> implements Predicate<T1>
{
private final Set<T1> includedItems;
private IncludeOnlyPredicate( T1... includedItems )
{
this.includedItems = new HashSet<>( Arrays.asList( includedItems ) );
}
@Override
public boolean apply( T1 input )
{
return true == includedItems.contains( input );
}
}
public <T1> Iterator<T1> excludeAll( Iterator<T1> generator, T1... excludedItems )
{
return excludeAll( generator, new ExcludeAllPredicate<>( excludedItems ) );
}
public <T1> Iterator<T1> excludeAll( Iterator<T1> generator, Predicate<T1> isExcludedPredicate )
{
return Iterators.filter( generator, isExcludedPredicate );
}
private class ExcludeAllPredicate<T1> implements Predicate<T1>
{
private final Set<T1> excludedItems;
private ExcludeAllPredicate( T1... excludedItems )
{
this.excludedItems = new HashSet<>( Arrays.asList( excludedItems ) );
}
@Override
public boolean apply( T1 input )
{
return false == excludedItems.contains( input );
}
}
/**
* Maps/transforms one iterator into another, using input function to perform the transformation on each individual
* element.
* Returned iterator will have the same length input iterator.
*
* @param original
* @param fun
* @param <IN>
* @param <OUT>
* @return
*/
public <IN, OUT> Iterator<OUT> map( Iterator<IN> original, Function1<IN,OUT,RuntimeException> fun )
{
return new MappingGenerator<>( original, fun );
}
/**
* Takes two iterators as input and outputs one, using the merge function to 'merge' the head elements of each
* input iterator into one element in the output iterator.
* Returned iterator will have the same length as the shortest of the two input iterators
*
* @param in1
* @param in2
* @param mergeFun
* @param <IN_1>
* @param <IN_2>
* @param <OUT>
* @return
*/
public <IN_1, IN_2, OUT> Iterator<OUT> merge( Iterator<IN_1> in1, Iterator<IN_2> in2,
Function2<IN_1,IN_2,OUT,RuntimeException> mergeFun )
{
return new MergingGenerator<>( in1, in2, mergeFun );
}
/**
* Assigns dependency times to all operations that do not yet have one assigned,
* or to all if canOverwriteDependencyTime is true.
* The dependency time assigned is equal to the scheduled start time of the previous operation,
* starting with initialDependencyTime.
* All operations in the returned iterator will have dependency times assigned to them.
*
* @param operations
* @param initialDependencyTimeAsMilli
* @param canOverwriteDependencyTime
* @return
*/
public Iterator<Operation> assignConservativeDependencyTimes( Iterator<Operation> operations,
final long initialDependencyTimeAsMilli,
final boolean canOverwriteDependencyTime )
{
Function1<Operation,Boolean,RuntimeException> isDependency = new Function1<Operation,Boolean,RuntimeException>()
{
@Override
public Boolean apply( Operation operation )
{
return true;
}
};
return assignDependencyTimesEqualToLastEncounteredDependencyTimeStamp( operations, isDependency,
initialDependencyTimeAsMilli, canOverwriteDependencyTime );
}
/**
* Assigns dependency times to all operations that do not yet have one assigned,
* or to all if canOverwriteDependencyTime is true.
* The dependency time assigned is equal to the scheduled start time of the last operation for which the
* isDependency predicate returned true, starting with initialDependencyTime, as long as that time is lower than
* current operations start time.
* All operations in the returned iterator will have dependency times assigned to them.
*
* @param operations
* @param isDependency
* @param initialDependencyTimeAsMilli
* @param canOverwriteDependencyTime
* @return
*/
public Iterator<Operation> assignDependencyTimesEqualToLastEncounteredLowerDependencyTimeStamp(
Iterator<Operation> operations,
final Function1<Operation,Boolean,RuntimeException> isDependency,
final long initialDependencyTimeAsMilli,
final boolean canOverwriteDependencyTime )
{
Function1<Operation,Operation,RuntimeException> dependencyTimeAssigningFun =
new Function1<Operation,Operation,RuntimeException>()
{
private long secondMostRecentDependencyAsMilli = initialDependencyTimeAsMilli;
private long mostRecentDependencyAsMilli = initialDependencyTimeAsMilli;
@Override
public Operation apply( Operation operation )
{
if ( -1 == operation.dependencyTimeStamp() || canOverwriteDependencyTime )
{
if ( operation.timeStamp() > mostRecentDependencyAsMilli )
{ operation.setDependencyTimeStamp( mostRecentDependencyAsMilli ); }
else
{ operation.setDependencyTimeStamp( secondMostRecentDependencyAsMilli ); }
}
if ( isDependency.apply( operation ) )
{
if ( operation.timeStamp() > mostRecentDependencyAsMilli )
{
secondMostRecentDependencyAsMilli = mostRecentDependencyAsMilli;
mostRecentDependencyAsMilli = operation.timeStamp();
}
}
return operation;
}
};
return new MappingGenerator<>( operations, dependencyTimeAssigningFun );
}
/**
* Assigns dependency times to all operations that do not yet have one assigned,
* or to all if canOverwriteDependencyTime is true.
* The dependency time assigned is equal to the scheduled start time of the last operation for which the
* isDependency predicate returned true, starting with initialDependencyTime.
* All operations in the returned iterator will have dependency times assigned to them.
*
* @param operations
* @param isDependency
* @param initialDependencyTimeAsMilli
* @param canOverwriteDependencyTime
* @return
*/
public Iterator<Operation> assignDependencyTimesEqualToLastEncounteredDependencyTimeStamp(
Iterator<Operation> operations,
final Function1<Operation,Boolean,RuntimeException> isDependency,
final long initialDependencyTimeAsMilli,
final boolean canOverwriteDependencyTime )
{
Function1<Operation,Operation,RuntimeException> dependencyTimeAssigningFun =
new Function1<Operation,Operation,RuntimeException>()
{
private long mostRecentDependencyAsMilli = initialDependencyTimeAsMilli;
@Override
public Operation apply( Operation operation )
{
if ( -1 == operation.dependencyTimeStamp() || canOverwriteDependencyTime )
{ operation.setDependencyTimeStamp( mostRecentDependencyAsMilli ); }
if ( isDependency.apply( operation ) )
{
mostRecentDependencyAsMilli = operation.timeStamp();
}
return operation;
}
};
return new MappingGenerator<>( operations, dependencyTimeAssigningFun );
}
/**
* Returns the same operation generator, with start times assigned to each operation taken from the start time
* generator. Generator stops as soon as either of the generators, start times or operations, stops.
*
* @param startTimesAsMilli
* @param operations
* @return
*/
public Iterator<Operation> assignStartTimes( Iterator<Long> startTimesAsMilli, Iterator<Operation> operations )
{
Function2<Long,Operation,Operation,RuntimeException> startTimeAssigningFun =
new Function2<Long,Operation,Operation,RuntimeException>()
{
@Override
public Operation apply( Long timeAsMilli, Operation operation )
{
operation.setScheduledStartTimeAsMilli( timeAsMilli );
operation.setTimeStamp( timeAsMilli );
return operation;
}
};
return new MergingGenerator<>( startTimesAsMilli, operations, startTimeAssigningFun );
}
/**
* Returns the same operation generator, with dependency times assigned to each operation taken from the dependency
* time
* generator. Generator stops as soon as either of the generators, dependency times or operations, stops.
*
* @param dependencyTimesAsMilli
* @param operations
* @return
*/
public Iterator<Operation> assignDependencyTimes( Iterator<Long> dependencyTimesAsMilli,
Iterator<Operation> operations )
{
Function2<Long,Operation,Operation,RuntimeException> dependencyTimeAssigningFun =
new Function2<Long,Operation,Operation,RuntimeException>()
{
@Override
public Operation apply( Long timeAsMilli, Operation operation )
{
operation.setDependencyTimeStamp( timeAsMilli );
return operation;
}
};
return new MergingGenerator<>( dependencyTimesAsMilli, operations, dependencyTimeAssigningFun );
}
/**
* Returns numbers, starting at specified number, and thereafter incrementing by a uniformly random amount
* between the minimum and maximum amounts specified
*
* @param start
* @param minIncrement
* @param maxIncrement
* @return
*/
public <T extends Number> Iterator<T> randomIncrement( T start, T minIncrement, T maxIncrement )
{
Iterator<T> incrementAmountGenerator = uniform( minIncrement, maxIncrement );
return incrementing( start, incrementAmountGenerator );
}
/**
* Returned generator will merge all input generators into one, sorting on the scheduled start time of operations,
* ascending
*
* @param generators
* @return
*/
public Iterator<Operation> mergeSortOperationsByScheduledStartTime( Iterator<Operation>... generators )
{
return mergeSort(
new Comparator<Operation>()
{
@Override
public int compare( Operation o1, Operation o2 )
{
if ( o1.scheduledStartTimeAsMilli() > o2.scheduledStartTimeAsMilli() )
{ return 1; }
else if ( o1.scheduledStartTimeAsMilli() < o2.scheduledStartTimeAsMilli() )
{ return -1; }
else
{ return 0; }
}
},
generators
);
}
/**
* Returned generator will merge all input generators into one, sorting on the time stamp of operations, ascending
*
* @param generators
* @return
*/
public Iterator<Operation> mergeSortOperationsByTimeStamp( Iterator<Operation>... generators )
{
return mergeSort(
new Comparator<Operation>()
{
@Override
public int compare( Operation o1, Operation o2 )
{
if ( o1.timeStamp() > o2.timeStamp() )
{ return 1; }
else if ( o1.timeStamp() < o2.timeStamp() )
{ return -1; }
else
{ return 0; }
}
},
generators
);
}
/**
* Returned generator will merge all input generators into one, sorting by value, ascending
*
* @param generators
* @return
*/
public <T extends Number> Iterator<T> mergeSortNumbers( Iterator<T>... generators )
{
return mergeSort(
new Comparator<T>()
{
@Override
public int compare( T t1, T t2 )
{
if ( t1.longValue() > t2.longValue() )
{ return 1; }
else if ( t1.longValue() < t2.longValue() )
{ return -1; }
else
{ return 0; }
}
},
generators
);
}
/**
* Returned generator will merge all input generators into one, using provided comparator for sorting
*
* @param comparator
* @param generators
* @param <T>
* @return
*/
public <T> Iterator<T> mergeSort( Comparator<T> comparator, Iterator<T>... generators )
{
return Iterators.mergeSorted( Lists.newArrayList( generators ), comparator );
}
/**
* Returned generator will merge all input generators into one, using provided comparator for sorting,
* and looking ahead a bounded distance in case nearby elements of any one input generator are out of order
*
* @param comparator
* @param lookAheadDistance
* @param generators
* @param <T>
* @return
*/
public <T> Iterator<T> mergeSort( Comparator<T> comparator, int lookAheadDistance, Iterator<T>... generators )
{
return new OrderedMultiGenerator<>( comparator, lookAheadDistance, generators );
}
/**
* Returned generator will loop over input iterator indefinitely.
* <p/>
* CAUTION: the returned generator does NOT (can not) do a deep copy on the elements of the original generator.
* As such, if elements of the original generator are not primitives the repeating generator will simply return
* many references to the elements of the original generator, i.e., modifying any of them will modify the content
* of all returned elements that are referenced by that element(/reference).
*
* @param generator
* @param <T>
* @return
*/
public <T> Iterator<T> repeating( Iterator<T> generator )
{
return new RepeatingGenerator<>( generator );
}
/**
* Returned generator will iterate over all of the things, once
*
* @param things
* @param <T>
* @return
*/
public <T> Iterator<T> identity( T... things )
{
return new IdentityGenerator<>( things );
}
/**
* Interleave every element from base generator with specific amount of elements from interleave with generator.
* Returned generator will run until either base generator or interleave generator are exhausted.
*
* @param baseGenerator
* @param interleaveWithGenerator
* @param amountToInterleave
* @param <T>
* @return
*/
public <T> Iterator<T> interleave( Iterator<? extends T> baseGenerator,
Iterator<? extends T> interleaveWithGenerator, final int amountToInterleave )
{
Function0<Integer,RuntimeException> amountToInterleaveFun =
new Function0<Integer,RuntimeException>()
{
@Override
public Integer apply()
{
return amountToInterleave;
}
};
return new InterleaveGenerator<>( baseGenerator, interleaveWithGenerator, amountToInterleaveFun );
}
/**
* Interleave every element from base generator with specific amount of elements from interleave with generator.
* Returned generator will run until either base generator or interleave generator are exhausted.
*
* @param base
* @param interleaveWith
* @param amountToInterleaveFun
* @param <T>
* @return
*/
public <T> Iterator<T> interleave( Iterator<? extends T> base, Iterator<? extends T> interleaveWith,
Function0<Integer,RuntimeException> amountToInterleaveFun )
{
return new InterleaveGenerator<>( base, interleaveWith, amountToInterleaveFun );
}
/**
* Offset start times of operations in stream such that first operation is now scheduled at new start time.
*
* @param generator
* @param newStartTimeAsMilli
* @return
*/
public Iterator<Operation> timeOffset( Iterator<Operation> generator, long newStartTimeAsMilli )
{
return timeOffsetAndCompress( generator, newStartTimeAsMilli, null );
}
// TODO timeCompress (without offset)
/**
* Offset start times of operations in stream such that first operation is now scheduled at new start time.
* Compress/expand duration between start times by a fixed ratio.
* E.g. 2.0 = 2x slower, 0.5 = 2x faster
*
* @param generator
* @param newStartTimeAsMilli
* @param compressionRatio
* @return
*/
public Iterator<Operation> timeOffsetAndCompress( Iterator<Operation> generator, long newStartTimeAsMilli,
Double compressionRatio )
{
return new TimeMappingOperationGenerator( generator, newStartTimeAsMilli, compressionRatio );
}
/**
* Prefix every generated item with prefix string
*
* @param generator
* @param prefix
* @param <T>
* @return
*/
public <T> Iterator<String> prefix( Iterator<T> generator, final String prefix )
{
Function1<T,String,RuntimeException> prefixingFun =
new Function1<T,String,RuntimeException>()
{
@Override
public String apply( T item )
{
return prefix + item.toString();
}
};
return new MappingGenerator<>( generator, prefixingFun );
}
/**
* Caps the amount of things generator can return
*
* @param generator
* @param limit
* @param <T>
* @return
*/
public <T> Iterator<T> limit( Iterator<T> generator, long limit )
{
return new LimitGenerator<T>( generator, limit );
}
/**
* next() returns single item from set of items. Each item has equal probability of being chosen.
*
* @param items
* @param <T>
* @return
*/
public <T> Iterator<T> discrete( Iterable<T> items )
{
List<Tuple2<Double,Iterator<T>>> weightedIteratorItems = new ArrayList<>();
for ( T item : items )
{
weightedIteratorItems.add( Tuple.tuple2( 1d, constant( item ) ) );
}
return weightedDiscreteDereferencing( weightedIteratorItems );
}
/**
* next() retrieves single iterator from set of iterators, then returns the next() element from that iterator.
* All iterators are selected with equal probability
*
* @param itemIterators
* @param <T>
* @return
*/
public <T> Iterator<T> discreteDereferencing( Iterable<Iterator<T>> itemIterators )
{
List<Tuple2<Double,Iterator<T>>> weightedIteratorItems = new ArrayList<>();
for ( Iterator<T> itemIterator : itemIterators )
{
weightedIteratorItems.add( Tuple.tuple2( 1d, itemIterator ) );
}
return weightedDiscreteDereferencing( weightedIteratorItems );
}
/**
* next() returns single item from set of items. Probability of selecting an item depends on weight assigned to
* that
* element.
*
* @param weightedItems
* @param <T>
* @return
*/
public <T> Iterator<T> weightedDiscrete( Iterable<Tuple2<Double,T>> weightedItems )
{
List<Tuple2<Double,Iterator<T>>> weightedIteratorItems = new ArrayList<>();
for ( Tuple2<Double,T> item : weightedItems )
{
weightedIteratorItems.add( Tuple.tuple2( item._1(), constant( item._2() ) ) );
}
return weightedDiscreteDereferencing( weightedIteratorItems );
}
/**
* next() retrieves single iterator from set of iterators, then returns the next() element from that iterator.
* Probability of selecting an iterator depends on weight assigned to that iterator.
*
* @param weightedIteratorItems
* @param <T>
* @return
*/
public <T> Iterator<T> weightedDiscreteDereferencing( Iterable<Tuple2<Double,Iterator<T>>> weightedIteratorItems )
{
Iterator<Iterator<T>> discreteIteratorGenerator = new DiscreteGenerator<>( getRandom(), weightedIteratorItems );
return new IteratorDereferencingGenerator<T>( discreteIteratorGenerator );
}
// TODO discreteList
/**
* next() returns list of multiple items from a collection of items.
* Probability of selecting an item depends on weight assigned to that item.
* amountToRetrieve specifies the size of the returned list
*
* @param items
* @param amountToRetrieve
* @param <T>
* @return
*/
public <T> Iterator<List<T>> weightedDiscreteList( Iterable<Tuple2<Double,T>> items, Integer amountToRetrieve )
{
Iterator<Integer> amountToRetrieveGenerator = constant( amountToRetrieve );
return weightedDiscreteList( items, amountToRetrieveGenerator );
}
/**
* next() returns list of multiple items from a collection of items.
* Probability of selecting an item depends on weight assigned to that item.
* amountToRetrieveGenerator.next() specifies the size of the returned list, so may vary between calls
*
* @param pairs
* @param amountToRetrieveGenerator
* @param <T>
* @return
*/
public <T> Iterator<List<T>> weightedDiscreteList( Iterable<Tuple2<Double,T>> pairs,
Iterator<Integer> amountToRetrieveGenerator )
{
return new DiscreteListGenerator<T>( getRandom(), pairs, amountToRetrieveGenerator );
}
// TODO weightedDiscreteListDereferencing
/**
* next() returns a map.
* Number of keys is specified by amountToRetrieve.
* Values are generated by calling next() on the corresponding item's tuple.
* The probability of a key (and its generated value) being returned depends on the weight assigned to its item
* tuple.
*
* @param items
* @param amountToRetrieve
* @param <K>
* @param <V>
* @return
*/
public <K, V> Iterator<Map<K,V>> weightedDiscreteMap( Iterable<Tuple3<Double,K,Iterator<V>>> items,
Integer amountToRetrieve )
{
Iterator<Integer> amountToRetrieveGenerator = constant( amountToRetrieve );
return weightedDiscreteMap( items, amountToRetrieveGenerator );
}
/**
* next() returns a map.
* Number of keys is specified by amountToRetrieve.next()
* Values are generated by calling next() on the corresponding item's tuple.
* The probability of a key (and its generated value) being returned depends on the weight assigned to its item
* tuple.
*
* @param items
* @param amountToRetrieveGenerator
* @param <K>
* @param <V>
* @return
*/
public <K, V> Iterator<Map<K,V>> weightedDiscreteMap( Iterable<Tuple3<Double,K,Iterator<V>>> items,
Iterator<Integer> amountToRetrieveGenerator )
{
List<Tuple2<Double,Tuple2<K,Iterator<V>>>> probabilityItems =
new ArrayList<Tuple2<Double,Tuple2<K,Iterator<V>>>>();
for ( Tuple3<Double,K,Iterator<V>> item : items )
{
double thingProbability = item._1();
Tuple2<K,Iterator<V>> thingGeneratorPair = Tuple.tuple2( item._2(), item._3() );
probabilityItems.add( Tuple.tuple2( thingProbability, thingGeneratorPair ) );
}
Iterator<List<Tuple2<K,Iterator<V>>>> discreteListGenerator = weightedDiscreteList( probabilityItems,
amountToRetrieveGenerator );
Function1<List<Tuple2<K,Iterator<V>>>,Map<K,V>,RuntimeException> pairsToMap =
new Function1<List<Tuple2<K,Iterator<V>>>,Map<K,V>,RuntimeException>()
{
@Override
public Map<K,V> apply( List<Tuple2<K,Iterator<V>>> pairs )
{
Map<K,V> keyedValues = new HashMap<>();
for ( Tuple2<K,Iterator<V>> pair : pairs )
{
keyedValues.put( pair._1(), pair._2().next() );
}
return keyedValues;
}
};
return new MappingGenerator<>( discreteListGenerator, pairsToMap );
}
public <T extends Number> Iterator<T> naiveBoundedNumberRange( T lowerBound, T upperBound,
Iterator<T> unboundedGenerator )
{
MinMaxGenerator<T> lowerBoundGenerator = minMaxGenerator( constant( lowerBound ), lowerBound, lowerBound );
MinMaxGenerator<T> upperBoundGenerator = minMaxGenerator( constant( upperBound ), upperBound, upperBound );
return new NaiveBoundedRangeNumberGenerator<T>( unboundedGenerator, lowerBoundGenerator, upperBoundGenerator );
}
/**
* Wraps a number generator and ensures it only returns numbers within a min-max range.
* Range is defined by lowerBoundGenerator and upperBoundGenerator.
* Generator is naive because it simply calls next() on inner generator until a suitable (in range) number is
* returned.
*
* @param lowerBoundGenerator
* @param upperBoundGenerator
* @param unboundedGenerator
* @param <T>
* @return
*/
public <T extends Number> Iterator<T> naiveBoundedNumberRange( MinMaxGenerator<T> lowerBoundGenerator,
MinMaxGenerator<T> upperBoundGenerator, Iterator<T> unboundedGenerator )
{
return new NaiveBoundedRangeNumberGenerator<T>( unboundedGenerator, lowerBoundGenerator, upperBoundGenerator );
}
/**
* next() returns a uniform random number within a min-max range.
* Range is defined by lowerBound and upperBound.
*
* @param lowerBound
* @param upperBound
* @param <T>
* @return
*/
public <T extends Number> Iterator<T> uniform( T lowerBound, T upperBound )
{
MinMaxGenerator<T> lowerBoundGenerator = minMaxGenerator( constant( lowerBound ), lowerBound, lowerBound );
MinMaxGenerator<T> upperBoundGenerator = minMaxGenerator( constant( upperBound ), upperBound, upperBound );
return dynamicRangeUniform( lowerBoundGenerator, upperBoundGenerator );
}
/**
* next() returns an iterator that in turn returns uniform random bytes
*
* @return
*/
public Iterator<Iterator<Byte>> sizedUniformBytesGenerator( Iterator<Long> lengths )
{
return new SizedUniformByteGeneratorGenerator( lengths, this );
}
/**
* next() returns a uniform random byte
*
* @return
*/
public Iterator<Byte> uniformBytes()
{
return new UniformByteGenerator( getRandom() );
}
/**
* next() returns a uniform random number within a min-max range.
* Range is defined by boundingGenerator.
*
* @param boundingGenerator
* @param <T>
* @return
*/
public <T extends Number> Iterator<T> dynamicRangeUniform( MinMaxGenerator<T> boundingGenerator )
{
return dynamicRangeUniform( boundingGenerator, boundingGenerator );
}
/**
* next() returns a uniform random number within a min-max range.
* Range is defined by lowerBoundGenerator and upperBoundGenerator.
*
* @param lowerBoundGenerator
* @param upperBoundGenerator
* @param <T>
* @return
*/
public <T extends Number> Iterator<T> dynamicRangeUniform( MinMaxGenerator<T> lowerBoundGenerator,
MinMaxGenerator<T> upperBoundGenerator )
{
return new DynamicRangeUniformNumberGenerator<T>( getRandom(), lowerBoundGenerator, upperBoundGenerator );
}
/**
* next() always returns the same value, constant
* <p/>
* CAUTION: the returned generator does NOT (can not) do a deep copy on the constant parameter.
* As such, if constant is not a primitives the generator will simply return many references to that same constant,
* i.e., modifying any of them will modify all returned elements that are referenced by that element(/reference).
*
* @param constant
* @param <T>
* @return
*/
public <T> Iterator<T> constant( T constant )
{
return new ConstantGenerator<T>( constant );
}
/**
* next() returns start the first time it is called.
* Subsequent calls return the number value returned in previous call increment by incrementBy.
*
* @param start
* @param incrementBy
* @param <T>
* @return
*/
public <T extends Number> Iterator<T> incrementing( T start, T incrementBy )
{
return boundedIncrementing( start, new ConstantGenerator<T>( incrementBy ), null );
}
/**
* next() returns start the first time it is called.
* Subsequent calls return the number value returned in previous call increment by the result of calling next() on
* incrementByGenerator.
*
* @param start
* @param incrementByGenerator
* @param <T>
* @return
*/
public <T extends Number> Iterator<T> incrementing( T start, Iterator<T> incrementByGenerator )
{
return boundedIncrementing( start, incrementByGenerator, null );
}
/**
* next() returns start the first time it is called.
* Subsequent calls return the number value returned in previous call increment by incrementBy.
* When max is reached the generator will be exhausted (hasNext()==false)
*
* @param start
* @param incrementBy
* @param max
* @param <T>
* @return
*/
public <T extends Number> Iterator<T> boundedIncrementing( T start, T incrementBy, T max )
{
return boundedIncrementing( start, new ConstantGenerator<T>( incrementBy ), max );
}
/**
* next() returns start the first time it is called.
* Subsequent calls return the number value returned in previous call increment by the result of calling next() on
* incrementByGenerator.
* When max is reached the generator will be exhausted (hasNext()==false)
*
* @param start
* @param incrementByGenerator
* @param max
* @param <T>
* @return
*/
public <T extends Number> Iterator<T> boundedIncrementing( T start, Iterator<T> incrementByGenerator, T max )
{
return new IncrementingGenerator<T>( start, incrementByGenerator, max );
}
/**
* next() returns an exponential random number with given mean.
*
* @param mean
* @param <T>
* @return
*/
public <T extends Number> Iterator<T> exponential( T mean )
{
return new ExponentialNumberGenerator<T>( getRandom(), mean );
}
/**
* next() returns an exponential random number with given mean, in given min-max range.
* Range is defined by lowerBoundGenerator and upperBoundGenerator.
*
* @param lowerBoundGenerator
* @param upperBoundGenerator
* @param mean
* @param <T>
* @return
*/
public <T extends Number> Iterator<T> boundedRangeExponential( MinMaxGenerator<T> lowerBoundGenerator,
MinMaxGenerator<T> upperBoundGenerator, T mean )
{
Iterator<T> generator = new ExponentialNumberGenerator<T>( getRandom(), mean );
return naiveBoundedNumberRange( lowerBoundGenerator, upperBoundGenerator, generator );
}
}