001/* 002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package de.cuioss.test.generator.internal.net.java.quickcheck.generator.support; 017 018import de.cuioss.test.generator.internal.net.java.quickcheck.Generator; 019import de.cuioss.test.generator.internal.net.java.quickcheck.StatefulGenerator; 020 021import java.util.Iterator; 022import java.util.Objects; 023 024import static de.cuioss.test.generator.internal.net.java.quickcheck.generator.iterable.Iterables.sizeOf; 025 026public class EnsuredValuesGenerator<T> implements StatefulGenerator<T> { 027 028 private final Iterable<T> ensured; 029 private final Generator<T> otherValues; 030 private Iterator<T> iterator; 031 private final int size; 032 private final int window; 033 private int valuesLeft; 034 private int generatesLeft; 035 036 public EnsuredValuesGenerator(Iterable<T> values) { 037 this(values, new FixedValuesGenerator<>(values)); 038 } 039 040 public EnsuredValuesGenerator(Iterable<T> ensured, Generator<T> random) { 041 this(ensured, sizeOf(ensured), random); 042 } 043 044 public EnsuredValuesGenerator(Iterable<T> ensured, int window, Generator<T> random) { 045 this.size = sizeOf(ensured); 046 if (this.size > window) { 047 throw new IllegalArgumentException("window"); 048 } 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}