public class CircularRangeValueByte extends CircularRangeValue<Byte> {

    /**
     * Constructs byte circular range value with specified <code>range</code> and <code>increment</code>.
     *
     * @param range Range.
     * @param increment Increment.
     */
    public CircularRangeValueByte(Range<Byte> range, Byte increment) {
        super(range, increment);
    }

    @Override
    protected Byte zero() {
        return 0;
    }

    @Override
    protected boolean isIncrementGreaterThanRangeSize() {
        return Math.abs(range.getBeginning() - range.getEnd()) <= Math.abs(increment);
    }

    @Override
    protected Byte peekNextValue() {
        return (byte) (val + increment);
    }
} public class CircularRangeValueFloat extends CircularRangeValue<Float> {

    /**
     * Constructs float circular range value with specified <code>range</code> and <code>increment</code>.
     *
     * @param range Range.
     * @param increment Increment.
     */
    public CircularRangeValueFloat(Range<Float> range, Float increment) {
        super(range, increment);
    }

    @Override
    protected Float zero() {
        return 0f;
    }

    @Override
    protected boolean isIncrementGreaterThanRangeSize() {
        return Math.abs(range.getBeginning() - range.getEnd()) <= Math.abs(increment);
    }

    @Override
    protected Float peekNextValue() {
        return val + increment;
    }
} public class CircularRangeValueInt extends CircularRangeValue<Integer> {

    /**
     * Constructs int circular range value with specified <code>range</code> and <code>increment</code>.
     *
     * @param range Range.
     * @param increment Increment.
     */
    public CircularRangeValueInt(Range<Integer> range, Integer increment) {
        super(range, increment);
    }

    @Override
    protected Integer zero() {
        return 0;
    }

    @Override
    protected boolean isIncrementGreaterThanRangeSize() {
        return Math.abs(range.getBeginning() - range.getEnd()) <= Math.abs(increment);
    }

    @Override
    protected Integer peekNextValue() {
        return val + increment;
    }
} public class CircularRangeValueLong extends CircularRangeValue<Long> {

    /**
     * Constructs long circular range value with specified <code>range</code> and <code>increment</code>.
     *
     * @param range Range.
     * @param increment Increment.
     */
    public CircularRangeValueLong(Range<Long> range, Long increment) {
        super(range, increment);
    }

    @Override
    protected Long zero() {
        return 0L;
    }

    @Override
    protected boolean isIncrementGreaterThanRangeSize() {
        return Math.abs(range.getBeginning() - range.getEnd()) <= Math.abs(increment);
    }

    @Override
    protected Long peekNextValue() {
        return val + increment;
    }
} public class CircularRangeValueShort extends CircularRangeValue<Short> {

    /**
     * Constructs short circular range value with specified <code>range</code> and <code>increment</code>.
     *
     * @param range Range.
     * @param increment Increment.
     */
    public CircularRangeValueShort(Range<Short> range, Short increment) {
        super(range, increment);
    }

    @Override
    protected Short zero() {
        return 0;
    }

    @Override
    protected boolean isIncrementGreaterThanRangeSize() {
        return Math.abs(range.getBeginning() - range.getEnd()) <= Math.abs(increment);
    }

    @Override
    protected Short peekNextValue() {
        return (short) (val + increment);
    }
} public class CircularValue<T> extends Value<T> {

    private final List<Value<T>> values;
    private final int size;
    private int currentIndex;

    /**
     * Constructs circular value with specified <code>values</code>.
     *
     * @param values List of possible values.
     */
    public CircularValue(List<Value<T>> values) {
        if (values == null || values.isEmpty()) {
            throw new IllegalArgumentException("List of values cannot be null nor empty.");
        }
        this.values = new ArrayList<>(values);
        this.size = this.values.size();
        this.currentIndex = -1;
    }

    @Override
    public void reset() {
        super.reset();
        values.get(nextIndex()).reset();
    }

    @Override
    protected void eval() {
        currentIndex = nextIndex();
        val = values.get(currentIndex).get();
    }

    private int nextIndex() {
        if (currentIndex == size - 1) {
            return 0;
        } else {
            return currentIndex + 1;
        }
    }
} public class ConstantValue<T> extends Value<T> {

    /**
     * Constructs constant value which will always return specified <code>value</code>.
     *
     * @param value Value to be returned.
     */
    public ConstantValue(T value) {
        this.val = value;
    }

    /**
     * Helper method to construct {@link ConstantValue}.
     *
     * @param value Value to be returned by created constant value.
     * @param <T> Type this value would evaluate to.
     *
     * @return An instance of {@link ConstantValue}.
     */
    public static <T> ConstantValue<T> of(T value) {
        return new ConstantValue<T>(value);
    }
} public class DiscreteValue<T> extends Value<T> {

    private final List<Value<T>> values;
    private final Distribution distribution;

    /**
     * Constructs discrete value with specified <code>values</code>, <code>distribution</code> is set to Uniform
     * distribution.
     *
     * @param values List of possible values.
     */
    public DiscreteValue(List<Value<T>> values) {
        this(values, new UniformDistribution());
    }

    /**
     * Constructs discrete value with specified <code>values</code> and <code>distribution</code>.
     *
     * @param values List of possible values.
     * @param distribution Distribution to use for value selection.
     */
    public DiscreteValue(List<Value<T>> values, Distribution distribution) {
        if (values == null || values.isEmpty()) {
            throw new IllegalArgumentException("List of values cannot be null nor empty.");
        }
        if (distribution == null) {
            throw new IllegalArgumentException("Distribution cannot be null.");
        }
        this.values = new ArrayList<>(values);
        this.distribution = distribution;
    }

    @Override
    public void reset() {
        super.reset();
        values.forEach(v -> v.reset());
    }

    @Override
    protected void eval() {
        Value<T> chosenValue;
        int index = distribution.nextInt(values.size());
        chosenValue = values.get(index);
        val = chosenValue.get();
    }
} public class EmptyListValue<T> extends Value<List<T>> {

    @Override
    public void reset() {
    }

    @Override
    protected void eval() {
        val = Collections.emptyList();
    }
} public class EmptyMapValue<K, V> extends Value<Map<K, V>> {

    @Override
    public void reset() {
    }

    @Override
    protected void eval() {
        val = Collections.emptyMap();
    }
} public class ExactWeightedValue<T> extends Value<T> {

    private final UniformDistribution distribution;
    private final List<Long> counts;
    private final List<Long> currentNumberOfValues;
    private final List<Value<T>> values;
    private final int totalCount;
    private int currentTotalCount;

    /**
     * Constructs discrete weighted value with specified <code>values</code> and <code>weights</code>.
     *
     * @param countValuePairs List of values with their corresponding weights.
     */
    public ExactWeightedValue(List<CountValuePair<T>> countValuePairs) {
        if (countValuePairs == null || countValuePairs.isEmpty()) {
            throw new IllegalArgumentException("List of count value pairs cannot be null nor empty.");
        }
        this.distribution = new UniformDistribution();
        this.counts = new ArrayList<>();
        this.currentNumberOfValues = new ArrayList<>();
        this.values = new ArrayList<>();
        this.currentTotalCount = 0;
        for (CountValuePair<T> pair : countValuePairs) {
            currentTotalCount += pair.getCount();
            counts.add(pair.getCount());
            currentNumberOfValues.add(0L);
            values.add(pair.getValue());
        }
        this.totalCount = currentTotalCount;
    }

    @Override
    public void reset() {
        super.reset();
        values.forEach(v -> v.reset());
    }

    @Override
    protected void eval() {
        int index = sample();
        val = values.get(index).get();
        currentNumberOfValues.set(index, currentNumberOfValues.get(index) + 1L);
        if (currentNumberOfValues.get(index).equals(counts.get(index))) {
            removeValue(index);
        }
    }

    private int sample() {
        if (values.isEmpty()) {
            throw new ExactWeightedValueDepletedException("Exact weighted value depleted."
                    + " It is configured to generate " + totalCount + " elements in total.");
        }
        int randomValue = distribution.nextInt(currentTotalCount);
        int sum = 0;
        for (int i = 0; i < counts.size(); i++) {
            sum += counts.get(i);
            if (randomValue < sum) {
                return i;
            }
        }
        throw new RuntimeException("If you see this exception. There is a bug in " + getClass().getName() + " class.");
    }

    private void removeValue(int index) {
        values.remove(index);
        currentNumberOfValues.remove(index);
        currentTotalCount -= counts.get(index);
        counts.remove(index);
    }

    /**
     * Represents value with its weight.
     *
     * @param <T> Type which value will return.
     */
    public static class CountValuePair<T> {

        private final Value<T> value;
        private final long count;

        /**
         * Constructs weighted value pair with specified <code>value</code> and <code>weight</code>.
         *
         * @param value The value.
         * @param count Weight of the value.
         */
        public CountValuePair(Value<T> value, long count) {
            if (value == null) {
                throw new IllegalArgumentException("Value cannot be null.");
            }
            if (count <= 0) {
                throw new IllegalArgumentException("Weight must be greater than 0.");
            }
            this.value = value;
            this.count = count;
        }

        /**
         * Returns the value.
         *
         * @return The value.
         */
        public Value<T> getValue() {
            return value;
        }

        /**
         * Returns weight of the value.
         *
         * @return weight of the value.
         */
        public long getCount() {
            return count;
        }
    }

    /**
     * Indicates that all configured values are already generated specified number of times.
     */
    public static class ExactWeightedValueDepletedException extends RuntimeException {

        private static final long serialVersionUID = -5000331731200682045L;

        /**
         * Constructs {@link ExactWeightedValueDepletedException}.
         *
         * @param message The detail message.
         */
        public ExactWeightedValueDepletedException(String message) {
            super(message);
        }
    }
} public static class CountValuePair<T> {

    private final Value<T> value;
    private final long count;

    /**
     * Constructs weighted value pair with specified <code>value</code> and <code>weight</code>.
     *
     * @param value The value.
     * @param count Weight of the value.
     */
    public CountValuePair(Value<T> value, long count) {
        if (value == null) {
            throw new IllegalArgumentException("Value cannot be null.");
        }
        if (count <= 0) {
            throw new IllegalArgumentException("Weight must be greater than 0.");
        }
        this.value = value;
        this.count = count;
    }

    /**
     * Returns the value.
     *
     * @return The value.
     */
    public Value<T> getValue() {
        return value;
    }

    /**
     * Returns weight of the value.
     *
     * @return weight of the value.
     */
    public long getCount() {
        return count;
    }
} public class GetterTransformer<T> extends Transformer<T> {

    private final String keyName;
    private final Value<?> value;


    /**
     * Constructs getter transformer with specified <code>key</code> and <code>value</code>.
     *
     * @param keyName Name of property for which to attempt get.
     * @param keyType Type of property for which to attempt get.
     * @param value Value from which to attempt get.
     */
    public GetterTransformer(String keyName, Class<T> keyType, Value<?> value) {
        if (keyName == null || keyName.isEmpty()) {
            throw new IllegalArgumentException("keyName cannot be null nor empty.");
        }
        if (keyType == null) {
            throw new IllegalArgumentException("keyType cannot be null.");
        }
        if (value == null) {
            throw new IllegalArgumentException("Composite value cannot be null.");
        }
        this.keyName = keyName;
        this.value = value;
    }

    @Override
    public void reset() {
        super.reset();
        value.reset();
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    protected void eval() {
        Map<String, Object> v = (Map) value.get();
        val = (T) v.get(keyName);
    }
} public class JsonTransformer extends Transformer<String> {

    private final Value<?> value;
    private final ObjectMapper objectMapper;

    /**
     * Constructs JSON transformer with specified <code>value</code>.
     * New default instance of {@link ObjectMapper} will be used.
     *
     * @param value Value which will be transformed into its JSON representation.
     */
    public JsonTransformer(Value<?> value) {
        this(value, new ObjectMapper());
    }

    /**
     * Constructs JSON transformer with specified <code>value</code> and <code>objectMapper</code>.
     *
     * @param value Value which will be transformed into its JSON representation.
     * @param objectMapper Object mapper which will be used to map value to JSON.
     */
    public JsonTransformer(Value<?> value, ObjectMapper objectMapper) {
        if (value == null) {
            throw new IllegalArgumentException("Value cannot be null.");
        }
        if (objectMapper == null) {
            throw new IllegalArgumentException("Object mapper cannot be null.");
        }
        this.value = value;
        this.objectMapper = objectMapper;
    }

    @Override
    public void reset() {
        super.reset();
        value.reset();
    }

    @Override
    protected void eval() {
        try {
            val = objectMapper.writeValueAsString(value.get());
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
} public class ListValue<T> extends Value<List<T>> {

    private final List<Value<T>> values;

    /**
     * Constructs list value out of specified values.
     *
     * @param values Values which will constitute list.
     */
    public ListValue(List<Value<T>> values) {
        if (values == null || values.isEmpty()) {
            throw new IllegalArgumentException("List of values cannot be null nor empty.");
        }
        this.values = new ArrayList<>(values);
    }

    @Override
    public void reset() {
        super.reset();
        for (Value<T> value : values) {
            value.reset();
        }
    }

    @Override
    protected void eval() {
        List<T> result = new ArrayList<>();
        for (Value<T> value : values) {
            result.add(value.get());
        }
        val = Collections.unmodifiableList(result);
    }
} public class NowDateValue extends Value<Date> {

    @Override
    public void eval() {
        val = new Date();
    }
} public class NowLocalDateTimeValue extends Value<LocalDateTime> {

    @Override
    public void eval() {
        val = LocalDateTime.now();
    }
} public class NowLocalDateValue extends Value<LocalDate> {

    @Override
    public void eval() {
        val = LocalDate.now();
    }
} public class NowValue extends Value<Long> {

    @Override
    public void eval() {
        val = System.currentTimeMillis();
    }
} public class RandomContentStringValue extends Value<String> {

    private static final List<Range<Character>> DEFAULT_RANGES = Arrays.asList(new Range<Character>('a', 'z'),
            new Range<Character>('A', 'Z'), new Range<Character>('0', '9'));

    private final Value<Integer> lengthValue;
    private final List<Character> possibleCharacters;
    private final Distribution distribution;

    /**
     * Constructs random content string value with specified <code>lengthValue</code> and default character range.
     *
     * @param lengthValue Value that returns integer which represents length of generated string. It should never
     *            generate length that is less than 1.
     */
    public RandomContentStringValue(Value<Integer> lengthValue) {
        this(lengthValue, DEFAULT_RANGES);
    }

    /**
     * Constructs random content string value with specified <code>lengthValue</code> and specified
     * <code>charRanges</code>.
     *
     * @param lengthValue Value that returns integer which represents length of generated string. It should never
     *            generate length that is less than 1.
     * @param charRanges Ranges of characters from which string will be constructed.
     */
    public RandomContentStringValue(Value<Integer> lengthValue, List<Range<Character>> charRanges) {
        if (lengthValue == null) {
            throw new IllegalArgumentException("lengthValue cannot be null.");
        }
        this.lengthValue = lengthValue;
        Set<Character> chars = new HashSet<>();
        for (Range<Character> range : charRanges) {
            if (!range.isIncreasing()) {
                throw new IllegalArgumentException("All ranges must be increasing.");
            }
            for (Character c = range.getBeginning(); c <= range.getEnd(); c++) {
                chars.add(c);
            }
        }
        possibleCharacters = new ArrayList<>(chars);
        distribution = new UniformDistribution();
    }

    @Override
    public void reset() {
        super.reset();
        lengthValue.reset();
    }

    @Override
    protected void eval() {
        int length = lengthValue.get();
        if (length < 1) {
            throw new RuntimeException("Generated length cannot be less than 1, but was: " + length);
        }
        char[] chars = new char[length];
        for (int i = 0; i < length; i++) {
            chars[i] = possibleCharacters.get(distribution.nextInt(possibleCharacters.size()));
        }
        val = new String(chars);
    }
} public class RandomLengthListValue<T> extends Value<List<T>> {

    private final int minLength;
    private final int maxLength;
    private Value<T> elementGenerator;
    private final Distribution distribution;

    /**
     * Constructs random length list value out of specified values.
     *
     * @param minLength Minimum list length
     * @param maxLength Maximum list length
     * @param elementGenerator Element generator
     */
    public RandomLengthListValue(int minLength, int maxLength, Value<T> elementGenerator) {
        this(minLength, maxLength, elementGenerator, new UniformDistribution());
    }

    /**
     * Constructs random length list value out of specified values.
     *
     * @param minLength Minimum list length
     * @param maxLength Maximum list length
     * @param elementGenerator Element generator
     * @param distribution Distribution to use for number of items in list.
     */
    public RandomLengthListValue(int minLength, int maxLength, Value<T> elementGenerator, Distribution distribution) {
        if (elementGenerator == null) {
            throw new IllegalArgumentException("Element Generator cannot be null nor empty.");
        }
        this.minLength = minLength;
        this.maxLength = maxLength;
        this.elementGenerator = elementGenerator;
        this.distribution = distribution;
    }

    @Override
    public void reset() {
        super.reset();
    }

    @Override
    protected void eval() {
        int randomLength = distribution.nextInt(minLength, maxLength);
        List<T> result = new ArrayList<>();
        for (int i = 0; i < randomLength; i++) {
            result.add(elementGenerator.get());
            elementGenerator.reset();
        }
        val = Collections.unmodifiableList(result);
    }
} public abstract class RangeValue<T extends Comparable<T>> extends Value<T> {

    /**
     * Beginning value of the range.
     */
    protected final T beginning;

    /**
     * End of the range.
     */
    protected final T end;

    /**
     * Indicates whether to create edge cases as first two values or not.
     */
    protected final boolean useEdgeCases;

    /**
     * Distribution to use.
     */
    protected final Distribution distribution;

    /**
     * Constructs range value with specified <code>range</code>. <code>useEdgeCases</code> is set to
     * <code>true</code> and <code>distribution</code> is set to {@link UniformDistribution}.
     *
     * @param range Range.
     */
    public RangeValue(Range<T> range) {
        this(range, defaultUseEdgeCases());
    }

    /**
     * Constructs range value with specified <code>range</code> and <code>useEdgeCases</code>.
     * <code>distribution</code> is set to {@link UniformDistribution}.
     *
     * @param range Range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     */
    public RangeValue(Range<T> range, boolean useEdgeCases) {
        this(range, useEdgeCases, defaultDistrbution());
    }

    /**
     * Constructs range value with specified <code>range</code>, <code>useEdgeCases</code> and
     * <code>distribution</code>.
     *
     * @param range Range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     * @param distribution Distribution to use for value selection.
     */
    public RangeValue(Range<T> range, boolean useEdgeCases, Distribution distribution) {
        if (range == null) {
            throw new IllegalArgumentException("Range cannot be null.");
        }
        if (!range.isIncreasing()) {
            throw new InvalidRangeBoundsException("End of the range must be greater than the beginning of the range.");
        }
        if (distribution == null) {
            throw new IllegalArgumentException("Distribution cannot be null.");
        }
        this.beginning = range.getBeginning();
        this.end = range.getEnd();
        this.useEdgeCases = useEdgeCases;
        this.distribution = distribution;
    }

    /**
     * Default value for <code>useEdgeCases</code> property.
     *
     * @return Default value for <code>useEdgeCases</code> property.
     */
    public static boolean defaultUseEdgeCases() {
        return false;
    }

    /**
     * Default value for <code>distribution</code> property.
     *
     * @return Default value for <code>distribution</code> property.
     */
    public static Distribution defaultDistrbution() {
        return new UniformDistribution();
    }
} public class RangeValueDate extends RangeValue<Date> {

    private boolean beginningEdgeCaseUsed = false;
    private boolean endEdgeCaseUsed = false;

    /**
     * Constructs range with specified <code>range</code>.
     *
     * @param range Double range.
     */
    public RangeValueDate(Range<Date> range) {
        super(range);
    }

    /**
     * Constructs range with specified <code>range</code> and <code>useEdgeCases</code>.
     *
     * @param range Double range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     */
    public RangeValueDate(Range<Date> range, boolean useEdgeCases) {
        super(range, useEdgeCases);
    }

    /**
     * Constructs range with specified <code>range</code>, <code>useEdgeCases</code> and <code>distribution</code>.
     *
     * @param range Double range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     * @param distribution Distribution to use for value selection.
     */
    public RangeValueDate(Range<Date> range, boolean useEdgeCases, Distribution distribution) {
        super(range, useEdgeCases, distribution);
    }

    @Override
    protected void eval() {
        if (useEdgeCases && !beginningEdgeCaseUsed) {
            beginningEdgeCaseUsed = true;
            val = new Date(beginning.getTime());
            return;
        }
        if (useEdgeCases && !endEdgeCaseUsed) {
            endEdgeCaseUsed = true;
            val = new Date(end.getTime() - 1);
            return;
        }
        val = new Date(distribution.nextLong(beginning.getTime(), end.getTime()));
    }
} public class RangeValueDouble extends RangeValue<Double> {

    /**
     * Epsilon value used for edge cases.
     */
    public static final Double EPSILON = 1E-11;

    private boolean beginningEdgeCaseUsed = false;
    private boolean endEdgeCaseUsed = false;

    /**
     * Constructs range with specified <code>range</code>.
     *
     * @param range Double range.
     */
    public RangeValueDouble(Range<Double> range) {
        super(range);
    }

    /**
     * Constructs range with specified <code>range</code> and <code>useEdgeCases</code>.
     *
     * @param range Double range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     */
    public RangeValueDouble(Range<Double> range, boolean useEdgeCases) {
        super(range, useEdgeCases);
    }

    /**
     * Constructs range with specified <code>range</code>, <code>useEdgeCases</code> and <code>distribution</code>.
     *
     * @param range Double range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     * @param distribution Distribution to use for value selection.
     */
    public RangeValueDouble(Range<Double> range, boolean useEdgeCases, Distribution distribution) {
        super(range, useEdgeCases, distribution);
    }

    @Override
    protected void eval() {
        if (useEdgeCases && !beginningEdgeCaseUsed) {
            beginningEdgeCaseUsed = true;
            val = beginning;
            return;
        }
        if (useEdgeCases && !endEdgeCaseUsed) {
            endEdgeCaseUsed = true;
            val = end - EPSILON;
            return;
        }
        val = distribution.nextDouble(beginning, end);
    }
} public class RangeValueFloat extends RangeValue<Float> {

    /**
     * Epsilon value used for edge cases.
     */
    public static final Float EPSILON = 1E-5f;

    private boolean beginningEdgeCaseUsed = false;
    private boolean endEdgeCaseUsed = false;

    /**
     * Constructs range with specified <code>range</code>.
     *
     * @param range Float range.
     */
    public RangeValueFloat(Range<Float> range) {
        super(range);
    }

    /**
     * Constructs range with specified <code>range</code> and <code>useEdgeCases</code>.
     *
     * @param range Float range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     */
    public RangeValueFloat(Range<Float> range, boolean useEdgeCases) {
        super(range, useEdgeCases);
    }

    /**
     * Constructs range with specified <code>range</code>, <code>useEdgeCases</code> and <code>distribution</code>.
     *
     * @param range Float range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     * @param distribution Distribution to use for value selection.
     */
    public RangeValueFloat(Range<Float> range, boolean useEdgeCases, Distribution distribution) {
        super(range, useEdgeCases, distribution);
    }

    @Override
    protected void eval() {
        if (useEdgeCases && !beginningEdgeCaseUsed) {
            beginningEdgeCaseUsed = true;
            val = beginning;
            return;
        }
        if (useEdgeCases && !endEdgeCaseUsed) {
            endEdgeCaseUsed = true;
            val = end - EPSILON;
            return;
        }
        val = (float) distribution.nextDouble(beginning, end);
    }
} public class RangeValueInt extends RangeValue<Integer> {

    private boolean beginningEdgeCaseUsed = false;
    private boolean endEdgeCaseUsed = false;

    /**
     * Constructs range with specified <code>range</code>.
     *
     * @param range Integer range.
     */
    public RangeValueInt(Range<Integer> range) {
        super(range);
    }

    /**
     * Constructs range with specified <code>range</code> and <code>useEdgeCases</code>.
     *
     * @param range Integer range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     */
    public RangeValueInt(Range<Integer> range, boolean useEdgeCases) {
        super(range, useEdgeCases);
    }

    /**
     * Constructs range with specified <code>range</code>, <code>useEdgeCases</code> and <code>distribution</code>.
     *
     * @param range Integer range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     * @param distribution Distribution to use for value selection.
     */
    public RangeValueInt(Range<Integer> range, boolean useEdgeCases, Distribution distribution) {
        super(range, useEdgeCases, distribution);
    }

    @Override
    protected void eval() {
        if (useEdgeCases && !beginningEdgeCaseUsed) {
            beginningEdgeCaseUsed = true;
            val = beginning;
            return;
        }
        if (useEdgeCases && !endEdgeCaseUsed) {
            endEdgeCaseUsed = true;
            val = end - 1;
            return;
        }
        val = distribution.nextInt(beginning, end);
    }
} public class RangeValueLocalDateTime extends Value<LocalDateTime> {

    private final LocalDateTime beginning;
    private final LocalDateTime end;
    private final boolean useEdgeCases;
    private final Distribution distribution;

    private boolean beginningEdgeCaseUsed = false;
    private boolean endEdgeCaseUsed = false;

    /**
     * Constructs range value with specified <code>range</code>. <code>useEdgeCases</code> is set to <code>true</code>
     * and <code>distribution</code> is set to {@link UniformDistribution}.
     *
     * @param beginning The beginning of the range.
     * @param end The end of the range.
     */
    public RangeValueLocalDateTime(LocalDateTime beginning, LocalDateTime end) {
        this(beginning, end, true);
    }

    /**
     * Constructs range value with specified <code>range</code> and <code>useEdgeCases</code>. <code>distribution</code>
     * is set to {@link UniformDistribution}.
     *
     * @param beginning The beginning of the range.
     * @param end The end of the range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     */
    public RangeValueLocalDateTime(LocalDateTime beginning, LocalDateTime end, boolean useEdgeCases) {
        this(beginning, end, useEdgeCases, new UniformDistribution());
    }

    /**
     * Constructs range value with specified <code>range</code>, <code>useEdgeCases</code> and
     * <code>distribution</code>.
     *
     * @param beginning The beginning of the range.
     * @param end The end of the range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     * @param distribution Distribution to use for value selection.
     */
    public RangeValueLocalDateTime(LocalDateTime beginning, LocalDateTime end, boolean useEdgeCases,
            Distribution distribution) {
        if (beginning == null) {
            throw new IllegalArgumentException("Beginning cannot be null.");
        }
        if (end == null) {
            throw new IllegalArgumentException("End cannot be null.");
        }
        if (!isRangeIncreasing(beginning, end)) {
            throw new InvalidRangeBoundsException("End of the range must be greater than the beginning of the range.");
        }
        if (distribution == null) {
            throw new IllegalArgumentException("Distribution cannot be null.");
        }
        this.beginning = beginning;
        this.end = end;
        this.useEdgeCases = useEdgeCases;
        this.distribution = distribution;
    }

    @Override
    protected void eval() {
        // Due to simplicity, nano seconds are not handled
        if (useEdgeCases && !beginningEdgeCaseUsed) {
            beginningEdgeCaseUsed = true;
            val = beginning;
            return;
        }
        if (useEdgeCases && !endEdgeCaseUsed) {
            endEdgeCaseUsed = true;
            val = end.minusSeconds(1);
            return;
        }
        long epochSecond = distribution.nextLong(beginning.toEpochSecond(ZoneOffset.UTC),
                end.toEpochSecond(ZoneOffset.UTC));
        val = LocalDateTime.ofEpochSecond(epochSecond, 0, ZoneOffset.UTC);
    }

    private boolean isRangeIncreasing(LocalDateTime beginning, LocalDateTime end) {
        return beginning.compareTo(end) < 0;
    }
} public class RangeValueShort extends RangeValue<Short> {

    private boolean beginningEdgeCaseUsed = false;
    private boolean endEdgeCaseUsed = false;

    /**
     * Constructs range with specified <code>range</code>.
     *
     * @param range Short range.
     */
    public RangeValueShort(Range<Short> range) {
        super(range);
    }

    /**
     * Constructs range with specified <code>range</code> and <code>useEdgeCases</code>.
     *
     * @param range Short range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     */
    public RangeValueShort(Range<Short> range, boolean useEdgeCases) {
        super(range, useEdgeCases);
    }

    /**
     * Constructs range with specified <code>range</code>, <code>useEdgeCases</code> and <code>distribution</code>.
     *
     * @param range Short range.
     * @param useEdgeCases Indicates whether to create edge cases as first two values or not.
     * @param distribution Distribution to use for value selection.
     */
    public RangeValueShort(Range<Short> range, boolean useEdgeCases, Distribution distribution) {
        super(range, useEdgeCases, distribution);
    }

    @Override
    protected void eval() {
        if (useEdgeCases && !beginningEdgeCaseUsed) {
            beginningEdgeCaseUsed = true;
            val = beginning;
            return;
        }
        if (useEdgeCases && !endEdgeCaseUsed) {
            endEdgeCaseUsed = true;
            val = (short) (end - 1);
            return;
        }
        val = (short) distribution.nextInt(beginning, end);
    }
} public class StringTransformer extends Transformer<String> {

    private final String format;
    private final List<Value<?>> values;
    private final Object[] calculatedValues;

    /**
     * Constructs string transformer with specified <code>format</code> string and list of <code>values</code>.
     * Placeholder for value is defined as '{}', first placeholder uses first value, second, second value, and so on.
     *
     * @param format Format string.
     * @param values List of values.
     */
    public StringTransformer(String format, List<Value<?>> values) {
        if (format == null || format.isEmpty()) {
            throw new IllegalArgumentException("Format string cannot be null nor empty.");
        }
        if (values == null) {
            throw new IllegalArgumentException("values cannot be null nor empty.");
        }
        this.format = format;
        this.values = values;
        this.calculatedValues = new Object[this.values.size()];
    }

    @Override
    public void reset() {
        super.reset();
        values.forEach(v -> v.reset());
    }

    @Override
    protected void eval() {
        calculateValues();
        val = MessageFormatter.arrayFormat(format, calculatedValues).getMessage();
    }

    private void calculateValues() {
        for (int i = 0; i < values.size(); i++) {
            Value<?> value = values.get(i);
            calculatedValues[i] = value.get();
        }
    }
} public class TimeFormatTransformer extends Transformer<String> {

    private final Value<?> value;
    private final DateTimeFormatter dateTimeFormatter;
    private final SimpleDateFormat dateFormater;

    /**
     * Creates a formatted string with specified <code>format</code> and <code>value</code>. Format can be any date
     * format (e.g. 'YYYY-MM-dd', 'dd.MM.YYYY-hh:mm:ss').
     *
     * @param format Format string.
     * @param value Value representing time, can return {@link Long}, {@link Date}, {@link LocalDate} or
     *            {@link LocalDateTime}.
     * @param <T> Type value parameter returns.
     */
    public <T> TimeFormatTransformer(String format, Value<T> value) {
        if (format == null || format.isEmpty()) {
            throw new IllegalArgumentException("Format string cannot be null nor empty.");
        }
        if (value == null) {
            throw new IllegalArgumentException("Value cannot be null.");
        }
        this.value = value;
        this.dateTimeFormatter = DateTimeFormatter.ofPattern(format);
        this.dateFormater = new SimpleDateFormat(format);
    }

    @Override
    public void reset() {
        super.reset();
        value.reset();
    }

    @Override
    protected void eval() {
        Object generatedValue = value.get();
        if (generatedValue instanceof Long) {
            long epochMilli = (long) generatedValue;
            LocalDateTime date = LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneId.systemDefault());
            val = date.format(dateTimeFormatter);
        }
        if (generatedValue instanceof Date) {
            Date date = (Date) generatedValue;
            val = dateFormater.format(date);
        }
        if (generatedValue instanceof LocalDate) {
            LocalDate date = (LocalDate) generatedValue;
            val = date.format(dateTimeFormatter);
        }
        if (generatedValue instanceof LocalDateTime) {
            LocalDateTime date = (LocalDateTime) generatedValue;
            val = date.format(dateTimeFormatter);
        }
    }
} public class UUIDValue extends Value<String> {

    @Override
    public void eval() {
        val = UUID.randomUUID().toString();
    }
} public class WeightedValue<T> extends Value<T> {

    private final EnumeratedDistribution<Value<T>> enumeratedDistribution;
    private final List<Value<T>> values;

    /**
     * Constructs discrete weighted value with specified <code>values</code> and <code>weights</code>.
     *
     * @param weightedValuePairs List of values with their corresponding weights.
     */
    public WeightedValue(List<WeightedValuePair<T>> weightedValuePairs) {
        if (weightedValuePairs == null || weightedValuePairs.isEmpty()) {
            throw new IllegalArgumentException("List of weighted values cannot be null nor empty.");
        }
        this.enumeratedDistribution = new EnumeratedDistribution<>(mapToPairList(weightedValuePairs));
        this.values = weightedValuePairs.stream().map(x -> x.getValue()).collect(Collectors.toList());
    }

    @Override
    public void reset() {
        super.reset();
        values.forEach(v -> v.reset());
    }

    @Override
    protected void eval() {
        val = enumeratedDistribution.sample().get();
    }

    private List<Pair<Value<T>, Double>> mapToPairList(List<WeightedValuePair<T>> weightedValuePairs) {
        return weightedValuePairs.stream().map(x -> new Pair<Value<T>, Double>(x.getValue(), x.getWeight()))
                .collect(Collectors.toList());
    }

    /**
     * Represents value with its weight.
     *
     * @param <T> Type which value will return.
     */
    public static class WeightedValuePair<T> {
        private final Value<T> value;
        private final double weight;

        /**
         * Constructs weighted value pair with specified <code>value</code> and <code>weight</code>.
         *
         * @param value The value.
         * @param weight Weight of the value.
         */
        public WeightedValuePair(Value<T> value, double weight) {
            if (value == null) {
                throw new IllegalArgumentException("Value cannot be null.");
            }
            if (weight <= 0) {
                throw new IllegalArgumentException("Weight must be greater than 0.");
            }
            this.value = value;
            this.weight = weight;
        }

        /**
         * Returns the value.
         *
         * @return The value.
         */
        public Value<T> getValue() {
            return value;
        }

        /**
         * Returns weight of the value.
         *
         * @return weight of the value.
         */
        public double getWeight() {
            return weight;
        }
    }
} public static class WeightedValuePair<T> {
    private final Value<T> value;
    private final double weight;

    /**
     * Constructs weighted value pair with specified <code>value</code> and <code>weight</code>.
     *
     * @param value The value.
     * @param weight Weight of the value.
     */
    public WeightedValuePair(Value<T> value, double weight) {
        if (value == null) {
            throw new IllegalArgumentException("Value cannot be null.");
        }
        if (weight <= 0) {
            throw new IllegalArgumentException("Weight must be greater than 0.");
        }
        this.value = value;
        this.weight = weight;
    }

    /**
     * Returns the value.
     *
     * @return The value.
     */
    public Value<T> getValue() {
        return value;
    }

    /**
     * Returns weight of the value.
     *
     * @return weight of the value.
     */
    public double getWeight() {
        return weight;
    }
} public class AdditionValueByte extends Value<Byte> {

    @SuppressWarnings("rawtypes")
    private final Value summand1;

    @SuppressWarnings("rawtypes")
    private final Value summand2;

    /**
     * Creates Addition value with specified <code>summand1</code> and <code>summand2</code>.
     *
     * @param summand1 Value which will be used as summand1 for this addition.
     * @param summand2 Value which will be used as summand2 for this addition.
     */
    @SuppressWarnings("rawtypes")
    public AdditionValueByte(Value summand1, Value summand2) {
        this.summand1 = summand1;
        this.summand2 = summand2;
    }

    @Override
    public void reset() {
        super.reset();
        summand1.reset();
        summand2.reset();
    }

    @Override
    protected void eval() {
        val = (byte) (((Number) summand1.get()).byteValue() + ((Number) summand2.get()).byteValue());
    }
} public class AdditionValueDouble extends Value<Double> {

    @SuppressWarnings("rawtypes")
    private final Value summand1;

    @SuppressWarnings("rawtypes")
    private final Value summand2;

    /**
     * Creates Addition value with specified <code>summand1</code> and <code>summand2</code>.
     *
     * @param summand1 Value which will be used as summand1 for this addition.
     * @param summand2 Value which will be used as summand2 for this addition.
     */
    @SuppressWarnings("rawtypes")
    public AdditionValueDouble(Value summand1, Value summand2) {
        this.summand1 = summand1;
        this.summand2 = summand2;
    }

    @Override
    public void reset() {
        super.reset();
        summand1.reset();
        summand2.reset();
    }

    @Override
    protected void eval() {
        val = ((Number) summand1.get()).doubleValue() + ((Number) summand2.get()).doubleValue();
    }
} public class AdditionValueFloat extends Value<Float> {

    @SuppressWarnings("rawtypes")
    private final Value summand1;

    @SuppressWarnings("rawtypes")
    private final Value summand2;

    /**
     * Creates Addition value with specified <code>summand1</code> and <code>summand2</code>.
     *
     * @param summand1 Value which will be used as summand1 for this addition.
     * @param summand2 Value which will be used as summand2 for this addition.
     */
    @SuppressWarnings("rawtypes")
    public AdditionValueFloat(Value summand1, Value summand2) {
        this.summand1 = summand1;
        this.summand2 = summand2;
    }

    @Override
    public void reset() {
        super.reset();
        summand1.reset();
        summand2.reset();
    }

    @Override
    protected void eval() {
        val = ((Number) summand1.get()).floatValue() + ((Number) summand2.get()).floatValue();
    }
} public class AdditionValueInteger extends Value<Integer> {

    @SuppressWarnings("rawtypes")
    private final Value summand1;

    @SuppressWarnings("rawtypes")
    private final Value summand2;

    /**
     * Creates Addition value with specified <code>summand1</code> and <code>summand2</code>.
     *
     * @param summand1 Value which will be used as summand1 for this addition.
     * @param summand2 Value which will be used as summand2 for this addition.
     */
    @SuppressWarnings("rawtypes")
    public AdditionValueInteger(Value summand1, Value summand2) {
        this.summand1 = summand1;
        this.summand2 = summand2;
    }

    @Override
    public void reset() {
        super.reset();
        summand1.reset();
        summand2.reset();
    }

    @Override
    protected void eval() {
        val = ((Number) summand1.get()).intValue() + ((Number) summand2.get()).intValue();
    }
} public class AdditionValueLong extends Value<Long> {

    @SuppressWarnings("rawtypes")
    private final Value summand1;

    @SuppressWarnings("rawtypes")
    private final Value summand2;

    /**
     * Creates Addition value with specified <code>summand1</code> and <code>summand2</code>.
     *
     * @param summand1 Value which will be used as summand1 for this addition.
     * @param summand2 Value which will be used as summand2 for this addition.
     */
    @SuppressWarnings("rawtypes")
    public AdditionValueLong(Value summand1, Value summand2) {
        this.summand1 = summand1;
        this.summand2 = summand2;
    }

    @Override
    public void reset() {
        super.reset();
        summand1.reset();
        summand2.reset();
    }

    @Override
    protected void eval() {
        val = ((Number) summand1.get()).longValue() + ((Number) summand2.get()).longValue();
    }
} public class AdditionValueShort extends Value<Short> {

    @SuppressWarnings("rawtypes")
    private final Value summand1;

    @SuppressWarnings("rawtypes")
    private final Value summand2;

    /**
     * Creates Addition value with specified <code>summand1</code> and <code>summand2</code>.
     *
     * @param summand1 Value which will be used as summand1 for this addition.
     * @param summand2 Value which will be used as summand2 for this addition.
     */
    @SuppressWarnings("rawtypes")
    public AdditionValueShort(Value summand1, Value summand2) {
        this.summand1 = summand1;
        this.summand2 = summand2;
    }

    @Override
    public void reset() {
        super.reset();
        summand1.reset();
        summand2.reset();
    }

    @Override
    protected void eval() {
        val = (short) (((Number) summand1.get()).shortValue() + ((Number) summand2.get()).shortValue());
    }
} public class DivisionValueByte extends Value<Byte> {

    @SuppressWarnings("rawtypes")
    private final Value dividend;

    @SuppressWarnings("rawtypes")
    private final Value divisor;

    /**
     * Creates Division value with specified <code>dividend</code> and <code>divisor</code>.
     *
     * @param dividend Value which will be used as dividend for this division.
     * @param divisor Value which will be used as divisor for this division.
     */
    @SuppressWarnings("rawtypes")
    public DivisionValueByte(Value dividend, Value divisor) {
        this.dividend = dividend;
        this.divisor = divisor;
    }

    @Override
    public void reset() {
        super.reset();
        dividend.reset();
        divisor.reset();
    }

    @Override
    protected void eval() {
        val = (byte) (((Number) dividend.get()).byteValue() / ((Number) divisor.get()).byteValue());
    }
} public class DivisionValueDouble extends Value<Double> {

    @SuppressWarnings("rawtypes")
    private final Value dividend;

    @SuppressWarnings("rawtypes")
    private final Value divisor;

    /**
     * Creates Division value with specified <code>dividend</code> and <code>divisor</code>.
     *
     * @param dividend Value which will be used as dividend for this division.
     * @param divisor Value which will be used as divisor for this division.
     */
    @SuppressWarnings("rawtypes")
    public DivisionValueDouble(Value dividend, Value divisor) {
        this.dividend = dividend;
        this.divisor = divisor;
    }

    @Override
    public void reset() {
        super.reset();
        dividend.reset();
        divisor.reset();
    }

    @Override
    protected void eval() {
        val = ((Number) dividend.get()).doubleValue() / ((Number) divisor.get()).doubleValue();
    }
} public class DivisionValueFloat extends Value<Float> {

    @SuppressWarnings("rawtypes")
    private final Value dividend;

    @SuppressWarnings("rawtypes")
    private final Value divisor;

    /**
     * Creates Division value with specified <code>dividend</code> and <code>divisor</code>.
     *
     * @param dividend Value which will be used as dividend for this division.
     * @param divisor Value which will be used as divisor for this division.
     */
    @SuppressWarnings("rawtypes")
    public DivisionValueFloat(Value dividend, Value divisor) {
        this.dividend = dividend;
        this.divisor = divisor;
    }

    @Override
    public void reset() {
        super.reset();
        dividend.reset();
        divisor.reset();
    }

    @Override
    protected void eval() {
        val = ((Number) dividend.get()).floatValue() / ((Number) divisor.get()).floatValue();
    }
} public class DivisionValueLong extends Value<Long> {

    @SuppressWarnings("rawtypes")
    private final Value dividend;

    @SuppressWarnings("rawtypes")
    private final Value divisor;

    /**
     * Creates Division value with specified <code>dividend</code> and <code>divisor</code>.
     *
     * @param dividend Value which will be used as dividend for this division.
     * @param divisor Value which will be used as divisor for this division.
     */
    @SuppressWarnings("rawtypes")
    public DivisionValueLong(Value dividend, Value divisor) {
        this.dividend = dividend;
        this.divisor = divisor;
    }

    @Override
    public void reset() {
        super.reset();
        dividend.reset();
        divisor.reset();
    }

    @Override
    protected void eval() {
        val = ((Number) dividend.get()).longValue() / ((Number) divisor.get()).longValue();
    }
} public class DivisionValueShort extends Value<Short> {

    @SuppressWarnings("rawtypes")
    private final Value dividend;

    @SuppressWarnings("rawtypes")
    private final Value divisor;

    /**
     * Creates Division value with specified <code>dividend</code> and <code>divisor</code>.
     *
     * @param dividend Value which will be used as dividend for this division.
     * @param divisor Value which will be used as divisor for this division.
     */
    @SuppressWarnings("rawtypes")
    public DivisionValueShort(Value dividend, Value divisor) {
        this.dividend = dividend;
        this.divisor = divisor;
    }

    @Override
    public void reset() {
        super.reset();
        dividend.reset();
        divisor.reset();
    }

    @Override
    protected void eval() {
        val = (short) (((Number) dividend.get()).shortValue() / ((Number) divisor.get()).shortValue());
    }
} public class MultiplicationValueByte extends Value<Byte> {

    @SuppressWarnings("rawtypes")
    private final Value factor1;

    @SuppressWarnings("rawtypes")
    private final Value factor2;

    /**
     * Creates Multiplication value with specified <code>factor1</code> and <code>factor2</code>.
     *
     * @param factor1 Value which will be used as factor1 for this multiplication.
     * @param factor2 Value which will be used as factor2 for this multiplication.
     */
    @SuppressWarnings("rawtypes")
    public MultiplicationValueByte(Value factor1, Value factor2) {
        this.factor1 = factor1;
        this.factor2 = factor2;
    }

    @Override
    public void reset() {
        super.reset();
        factor1.reset();
        factor2.reset();
    }

    @Override
    protected void eval() {
        val = (byte) (((Number) factor1.get()).byteValue() * ((Number) factor2.get()).byteValue());
    }
} public class MultiplicationValueInteger extends Value<Integer> {

    @SuppressWarnings("rawtypes")
    private final Value factor1;

    @SuppressWarnings("rawtypes")
    private final Value factor2;

    /**
     * Creates Multiplication value with specified <code>factor1</code> and <code>factor2</code>.
     *
     * @param factor1 Value which will be used as factor1 for this multiplication.
     * @param factor2 Value which will be used as factor2 for this multiplication.
     */
    @SuppressWarnings("rawtypes")
    public MultiplicationValueInteger(Value factor1, Value factor2) {
        this.factor1 = factor1;
        this.factor2 = factor2;
    }

    @Override
    public void reset() {
        super.reset();
        factor1.reset();
        factor2.reset();
    }

    @Override
    protected void eval() {
        val = ((Number) factor1.get()).intValue() * ((Number) factor2.get()).intValue();
    }
} public class MultiplicationValueShort extends Value<Short> {

    @SuppressWarnings("rawtypes")
    private final Value factor1;

    @SuppressWarnings("rawtypes")
    private final Value factor2;

    /**
     * Creates Multiplication value with specified <code>factor1</code> and <code>factor2</code>.
     *
     * @param factor1 Value which will be used as factor1 for this multiplication.
     * @param factor2 Value which will be used as factor2 for this multiplication.
     */
    @SuppressWarnings("rawtypes")
    public MultiplicationValueShort(Value factor1, Value factor2) {
        this.factor1 = factor1;
        this.factor2 = factor2;
    }

    @Override
    public void reset() {
        super.reset();
        factor1.reset();
        factor2.reset();
    }

    @Override
    protected void eval() {
        val = (short) (((Number) factor1.get()).shortValue() * ((Number) factor2.get()).shortValue());
    }
} public class SubtractionValueByte extends Value<Byte> {

    @SuppressWarnings("rawtypes")
    private final Value minuend;

    @SuppressWarnings("rawtypes")
    private final Value subtrahend;

    /**
     * Creates Subtraction value with specified <code>minuend</code> and <code>subtrahend</code>.
     *
     * @param minuend Value which will be used as minuend for this subtraction.
     * @param subtrahend Value which will be used as subtrahend for this subtraction.
     */
    @SuppressWarnings("rawtypes")
    public SubtractionValueByte(Value minuend, Value subtrahend) {
        this.minuend = minuend;
        this.subtrahend = subtrahend;
    }

    @Override
    public void reset() {
        super.reset();
        minuend.reset();
        subtrahend.reset();
    }

    @Override
    protected void eval() {
        val = (byte) (((Number) minuend.get()).byteValue() - ((Number) subtrahend.get()).byteValue());
    }
} public class SubtractionValueDouble extends Value<Double> {

    @SuppressWarnings("rawtypes")
    private final Value minuend;

    @SuppressWarnings("rawtypes")
    private final Value subtrahend;

    /**
     * Creates Subtraction value with specified <code>minuend</code> and <code>subtrahend</code>.
     *
     * @param minuend Value which will be used as minuend for this subtraction.
     * @param subtrahend Value which will be used as subtrahend for this subtraction.
     */
    @SuppressWarnings("rawtypes")
    public SubtractionValueDouble(Value minuend, Value subtrahend) {
        this.minuend = minuend;
        this.subtrahend = subtrahend;
    }

    @Override
    public void reset() {
        super.reset();
        minuend.reset();
        subtrahend.reset();
    }

    @Override
    protected void eval() {
        val = ((Number) minuend.get()).doubleValue() - ((Number) subtrahend.get()).doubleValue();
    }
} public class SubtractionValueFloat extends Value<Float> {

    @SuppressWarnings("rawtypes")
    private final Value minuend;

    @SuppressWarnings("rawtypes")
    private final Value subtrahend;

    /**
     * Creates Subtraction value with specified <code>minuend</code> and <code>subtrahend</code>.
     *
     * @param minuend Value which will be used as minuend for this subtraction.
     * @param subtrahend Value which will be used as subtrahend for this subtraction.
     */
    @SuppressWarnings("rawtypes")
    public SubtractionValueFloat(Value minuend, Value subtrahend) {
        this.minuend = minuend;
        this.subtrahend = subtrahend;
    }

    @Override
    public void reset() {
        super.reset();
        minuend.reset();
        subtrahend.reset();
    }

    @Override
    protected void eval() {
        val = ((Number) minuend.get()).floatValue() - ((Number) subtrahend.get()).floatValue();
    }
} public class SubtractionValueLong extends Value<Long> {

    @SuppressWarnings("rawtypes")
    private final Value minuend;

    @SuppressWarnings("rawtypes")
    private final Value subtrahend;

    /**
     * Creates Subtraction value with specified <code>minuend</code> and <code>subtrahend</code>.
     *
     * @param minuend Value which will be used as minuend for this subtraction.
     * @param subtrahend Value which will be used as subtrahend for this subtraction.
     */
    @SuppressWarnings("rawtypes")
    public SubtractionValueLong(Value minuend, Value subtrahend) {
        this.minuend = minuend;
        this.subtrahend = subtrahend;
    }

    @Override
    public void reset() {
        super.reset();
        minuend.reset();
        subtrahend.reset();
    }

    @Override
    protected void eval() {
        val = ((Number) minuend.get()).longValue() - ((Number) subtrahend.get()).longValue();
    }
} public class CsvReaderValue extends Value<Map<String, String>> {

    private final Map<String, String> evaluatedValues;
    private final CSVParser csvParser;
    private Iterator<CSVRecord> iterator;

    /**
     * Constructs composite value with specified initial child values.
     *
     * @param parserSettings Settings for the CSV parser.
     */
    public CsvReaderValue(CSVParserSettings parserSettings) {
        if (parserSettings == null) {
            throw new IllegalArgumentException("parserSettings cannot be null.");
        }
        this.evaluatedValues = new HashMap<>();
        this.csvParser = createCSVParser(parserSettings);
        this.iterator = csvParser.iterator();
    }

    @Override
    public void reset() {
        super.reset();
    }

    @Override
    protected void eval() {
        evaluatedValues.clear();
        CSVRecord record = iterator.next();
        for (int i = 0; i < record.size(); i++) {
            String value = record.get(i);
            evaluatedValues.put("c" + i, value);
        }
        val = evaluatedValues;
    }

    private CSVParser createCSVParser(CSVParserSettings parserSettings) {
        CSVFormat csvFormat = CSVFormat.DEFAULT.withDelimiter(parserSettings.getDelimiter())
                .withRecordSeparator(parserSettings.getRecordSeparator())
                .withTrim(parserSettings.isTrim())
                .withQuote(parserSettings.getQuote())
                .withCommentMarker(parserSettings.getCommentMarker())
                .withIgnoreEmptyLines(parserSettings.isIgnoreEmptyLines())
                .withNullString(parserSettings.getNullString());
        try {
            File file = new File(parserSettings.getPath());
            return new CSVParser(new FileReader(file), csvFormat);
        } catch (Exception e) {
            throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
        }
    }
}
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.smartcat.ranger.core.CircularRangeValueByte;
import io.smartcat.ranger.core.CircularRangeValueDouble;
import io.smartcat.ranger.core.CircularRangeValueFloat;
import io.smartcat.ranger.core.CircularRangeValueInt;
import io.smartcat.ranger.core.CircularRangeValueLong;
import io.smartcat.ranger.core.CircularRangeValueShort;
import io.smartcat.ranger.core.CircularValue;
import io.smartcat.ranger.core.ConstantValue;
import io.smartcat.ranger.core.DiscreteValue;
import io.smartcat.ranger.core.EmptyListValue;
import io.smartcat.ranger.core.EmptyMapValue;
import io.smartcat.ranger.core.ExactWeightedValue;
import io.smartcat.ranger.core.ExactWeightedValue.CountValuePair;
import io.smartcat.ranger.core.GetterTransformer;
import io.smartcat.ranger.core.JsonTransformer;
import io.smartcat.ranger.core.ListValue;
import io.smartcat.ranger.core.NowDateValue;
import io.smartcat.ranger.core.NowLocalDateTimeValue;
import io.smartcat.ranger.core.NowLocalDateValue;
import io.smartcat.ranger.core.NowValue;
import io.smartcat.ranger.core.RandomContentStringValue;
import io.smartcat.ranger.core.RandomLengthListValue;
import io.smartcat.ranger.core.RangeValue;
import io.smartcat.ranger.core.RangeValueByte;
import io.smartcat.ranger.core.RangeValueDate;
import io.smartcat.ranger.core.RangeValueDouble;
import io.smartcat.ranger.core.RangeValueFloat;
import io.smartcat.ranger.core.RangeValueInt;
import io.smartcat.ranger.core.RangeValueLocalDate;
import io.smartcat.ranger.core.RangeValueLocalDateTime;
import io.smartcat.ranger.core.RangeValueLong;
import io.smartcat.ranger.core.RangeValueShort;
import io.smartcat.ranger.core.StringTransformer;
import io.smartcat.ranger.core.TimeFormatTransformer;
import io.smartcat.ranger.core.UUIDValue;
import io.smartcat.ranger.core.Value;
import io.smartcat.ranger.core.WeightedValue;
import io.smartcat.ranger.core.WeightedValue.WeightedValuePair;
import io.smartcat.ranger.core.arithmetic.AdditionValueByte;
import io.smartcat.ranger.core.arithmetic.AdditionValueDouble;
import io.smartcat.ranger.core.arithmetic.AdditionValueFloat;
import io.smartcat.ranger.core.arithmetic.AdditionValueInteger;
import io.smartcat.ranger.core.arithmetic.AdditionValueLong;
import io.smartcat.ranger.core.arithmetic.AdditionValueShort;
import io.smartcat.ranger.core.arithmetic.DivisionValueByte;
import io.smartcat.ranger.core.arithmetic.DivisionValueDouble;
import io.smartcat.ranger.core.arithmetic.DivisionValueFloat;
import io.smartcat.ranger.core.arithmetic.DivisionValueInteger;
import io.smartcat.ranger.core.arithmetic.DivisionValueLong;
import io.smartcat.ranger.core.arithmetic.DivisionValueShort;
import io.smartcat.ranger.core.arithmetic.MultiplicationValueByte;
import io.smartcat.ranger.core.arithmetic.MultiplicationValueDouble;
import io.smartcat.ranger.core.arithmetic.MultiplicationValueFloat;
import io.smartcat.ranger.core.arithmetic.MultiplicationValueInteger;
import io.smartcat.ranger.core.arithmetic.MultiplicationValueLong;
import io.smartcat.ranger.core.arithmetic.MultiplicationValueShort;
import io.smartcat.ranger.core.arithmetic.SubtractionValueByte;
import io.smartcat.ranger.core.arithmetic.SubtractionValueDouble;
import io.smartcat.ranger.core.arithmetic.SubtractionValueFloat;
import io.smartcat.ranger.core.arithmetic.SubtractionValueInteger;
import io.smartcat.ranger.core.arithmetic.SubtractionValueLong;
import io.smartcat.ranger.core.arithmetic.SubtractionValueShort;
import io.smartcat.ranger.core.csv.CsvReaderValue;
import io.smartcat.ranger.core.csv.CSVParserSettings;
import io.smartcat.ranger.distribution.Distribution;
     *
     * @param lengthValue Value that returns integer which represents length of generated string. It should never
     *            generate length that is less than 1.
     * @param ranges List of ranges from which characters are taken with uniform distribution.
     * @return An instance of {@link ObjectGenerator} which generates random string values of specified length.
     */
    public static ObjectGenerator<String> randomContentString(ObjectGenerator<Integer> lengthValue,
            List<Range<Character>> ranges) {
        List<io.smartcat.ranger.core.Range<Character>> convertedRanges = new ArrayList<>();
        for (Range<Character> range : ranges) {
            convertedRanges.add(toRange(range));
        }
        return wrap(new RandomContentStringValue(lengthValue.value, convertedRanges));
    }

    /**
     * Creates an instance of {@link Range}. This is a helper method that is useful for following methods:<br>
     * {@link #random(Range)}<br>
     * {@link #random(Range, boolean)}<br>
     * {@link #random(Range, boolean, Distribution)}<br>
     * {@link #circular(Range, Object)}<br>
     * {@link #randomContentString(ObjectGenerator, Range...)}<br>
     * {@link #randomContentString(ObjectGenerator, List)}<br>
     *
     * @param beginning Beginning of the range.
     * @param end End of the range.
     * @param <T> Type of the range.
     * @return An instance of {@link Range}.
     */
    public static <T> Range<T> range(T beginning, T end) {
        return new Range<>(beginning, end);
    }

    /**
     * Creates an instance of {@link ObjectGenerator} which generates current time in milliseconds.
     *
     * @return An instance of {@link ObjectGenerator} which generates current time in milliseconds.
     */
    public static ObjectGenerator<Long> now() {
        return wrap(new NowValue());
    }

    /**
     * Creates an instance of {@link ObjectGenerator} which generates current date-time as {@link Date} object.
     *
     * @return An instance of {@link ObjectGenerator} which generates current date-time as {@link Date} object.
     */
    public static ObjectGenerator<Date> nowDate() {
        return wrap(new NowDateValue());
    }

    /**
     * Creates an instance of {@link ObjectGenerator} which generates current date as {@link LocalDate} object.
     *
     * @return An instance of {@link ObjectGenerator} which generates current date as {@link LocalDate} object.
     */
    public static ObjectGenerator<LocalDate> nowLocalDate() {
        return wrap(new NowLocalDateValue());
    }

    /**
     * Creates an instance of {@link ObjectGenerator} which generates current date-time as {@link LocalDateTime} object.
     *
     * @return An instance of {@link ObjectGenerator} which generates current date-time as {@link LocalDateTime} object.
     */
    public static ObjectGenerator<LocalDateTime> nowLocalDateTime() {
        return wrap(new NowLocalDateTimeValue());
    }

    /**
     * Creates an instance of {@link ObjectGenerator} which adds up values of given object generators.
     *
     * @param type Type object generator will return.
     * @param summand1 First object generator for addition.
     * @param summand2 Second object generator for addition.
     * @param <T> Type object generator will return.
     * @return An instance of {@link ObjectGenerator} which adds up values of given object generators.
     */
    @SuppressWarnings("unchecked")
    public static <T> ObjectGenerator<T> add(Class<T> type, ObjectGenerator<T> summand1, ObjectGenerator<T> summand2) {
        if (type.equals(Byte.class)) {
            return (ObjectGenerator<T>) wrap(new AdditionValueByte(summand1.value, summand2.value));
        } else if (type.equals(Short.class)) {
            return (ObjectGenerator<T>) wrap(new AdditionValueShort(summand1.value, summand2.value));
        } else if (type.equals(Integer.class)) {
            return (ObjectGenerator<T>) wrap(new AdditionValueInteger(summand1.value, summand2.value));
        } else if (type.equals(Long.class)) {
            return (ObjectGenerator<T>) wrap(new AdditionValueLong(summand1.value, summand2.value));
        } else if (type.equals(Float.class)) {
            return (ObjectGenerator<T>) wrap(new AdditionValueFloat(summand1.value, summand2.value));
        } else if (type.equals(Double.class)) {
            return (ObjectGenerator<T>) wrap(new AdditionValueDouble(summand1.value, summand2.value));
        } else {
            throw new RuntimeException("Type: " + type.getName() + " not supported.");
        }
    }

    /**
     * Creates an instance of {@link ObjectGenerator} which subtracts values of given object generators.
     *
     * @param type Type object generator will return.
     * @param minuend Object generator which will be used as minuend.
     * @param subtrahend Object generator which will be used as subtrahend
     * @param <T> Type object generator will return.
     * @return An instance of {@link ObjectGenerator} which subtracts values of given object generators.
     */
    @SuppressWarnings("unchecked")
    public static <T> ObjectGenerator<T> subtract(Class<T> type, ObjectGenerator<T> minuend,
            ObjectGenerator<T> subtrahend) {
        if (type.equals(Byte.class)) {
            return (ObjectGenerator<T>) wrap(new SubtractionValueByte(minuend.value, subtrahend.value));
        } else if (type.equals(Short.class)) {
            return (ObjectGenerator<T>) wrap(new SubtractionValueShort(minuend.value, subtrahend.value));
        } else if (type.equals(Integer.class)) {
            return (ObjectGenerator<T>) wrap(new SubtractionValueInteger(minuend.value, subtrahend.value));
        } else if (type.equals(Long.class)) {
            return (ObjectGenerator<T>) wrap(new SubtractionValueLong(minuend.value, subtrahend.value));
        } else if (type.equals(Float.class)) {
            return (ObjectGenerator<T>) wrap(new SubtractionValueFloat(minuend.value, subtrahend.value));
        } else if (type.equals(Double.class)) {
            return (ObjectGenerator<T>) wrap(new SubtractionValueDouble(minuend.value, subtrahend.value));