001/*
002 * Licensed to the author under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package de.cuioss.test.generator.internal.net.java.quickcheck.generator.support;
018
019import static de.cuioss.test.generator.internal.net.java.quickcheck.generator.iterable.Iterables.sizeOf;
020import static de.cuioss.tools.base.Preconditions.checkArgument;
021
022import java.util.Iterator;
023import java.util.Objects;
024
025import de.cuioss.test.generator.internal.net.java.quickcheck.Generator;
026import de.cuioss.test.generator.internal.net.java.quickcheck.StatefulGenerator;
027
028public class EnsuredValuesGenerator<T> implements StatefulGenerator<T> {
029
030    private final Iterable<T> ensured;
031    private final Generator<T> otherValues;
032    private Iterator<T> iterator;
033    private final int size;
034    private final int window;
035    private int valuesLeft;
036    private int generatesLeft;
037
038    public EnsuredValuesGenerator(Iterable<T> values) {
039        this(values, new FixedValuesGenerator<>(values));
040    }
041
042    public EnsuredValuesGenerator(Iterable<T> ensured, Generator<T> random) {
043        this(ensured, sizeOf(ensured), random);
044    }
045
046    public EnsuredValuesGenerator(Iterable<T> ensured, int window, Generator<T> random) {
047        this.size = sizeOf(ensured);
048        checkArgument(this.size <= window, "window");
049        this.window = window;
050        Objects.requireNonNull(random, "random");
051        this.ensured = ensured;
052        this.otherValues = random;
053
054        reset();
055    }
056
057    @Override
058    public T next() {
059        return takeEnsured() ? iterator.next() : otherValues.next();
060    }
061
062    private boolean takeEnsured() {
063        if (valuesLeft > 0 && spreadOverWindow()) {
064            valuesLeft--;
065            return true;
066        }
067        generatesLeft--;
068        return false;
069    }
070
071    /**
072     * @return true if an ensured values should be taken. The results should be such
073     *         that the ensured values are uniformly distributed over the window.
074     */
075    private boolean spreadOverWindow() {
076        assert valuesLeft > 0 && generatesLeft >= 0;
077        return new IntegerGenerator(0, valuesLeft + generatesLeft).next() <= valuesLeft;
078    }
079
080    @Override
081    public void reset() {
082        iterator = ensured.iterator();
083        valuesLeft = size;
084        generatesLeft = window - size;
085        assert generatesLeft >= 0 : generatesLeft;
086    }
087}