View Javadoc
1   /*
2    * (c) Copyright 2006-2020 by rapiddweller GmbH & Volker Bergmann. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, is permitted under the terms of the
6    * GNU General Public License.
7    *
8    * For redistributing this software or a derivative work under a license other
9    * than the GPL-compatible Free Software License as defined by the Free
10   * Software Foundation or approved by OSI, you must first obtain a commercial
11   * license to this software product from rapiddweller GmbH & Volker Bergmann.
12   *
13   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14   * WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
15   * REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
17   * HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24   * POSSIBILITY OF SUCH DAMAGE.
25   */
26  
27  package com.rapiddweller.benerator.distribution.sequence;
28  
29  import com.rapiddweller.benerator.Generator;
30  import com.rapiddweller.benerator.InvalidGeneratorSetupException;
31  import com.rapiddweller.benerator.NonNullGenerator;
32  import com.rapiddweller.benerator.distribution.Sequence;
33  import com.rapiddweller.benerator.wrapper.SkipGeneratorProxy;
34  import com.rapiddweller.benerator.wrapper.WrapperFactory;
35  import com.rapiddweller.common.BeanUtil;
36  import com.rapiddweller.common.ConfigurationError;
37  import com.rapiddweller.common.MathUtil;
38  import com.rapiddweller.common.NumberUtil;
39  import com.rapiddweller.common.converter.NumberToNumberConverter;
40  
41  import java.math.BigDecimal;
42  
43  import static com.rapiddweller.common.NumberUtil.toDouble;
44  import static com.rapiddweller.common.NumberUtil.toInteger;
45  import static com.rapiddweller.common.NumberUtil.toLong;
46  
47  /**
48   * Random Walk {@link Sequence} implementation that supports a variable step width.<br/>
49   * <br/>
50   * Created at 30.06.2009 07:48:40
51   *
52   * @author Volker Bergmann
53   * @since 0.6.0
54   */
55  public class RandomWalkSequence extends Sequence {
56  
57    private static final BigDecimal ONE = BigDecimal.ONE;
58    private static final BigDecimal MINUS_ONE = BigDecimal.ZERO.subtract(ONE);
59  
60    private static final boolean DEFAULT_BUFFERED = false;
61    private final boolean buffered;
62    private BigDecimal initial;
63    private BigDecimal minStep;
64    private BigDecimal maxStep;
65  
66    // constructors ----------------------------------------------------------------------------------------------------
67  
68    /**
69     * Instantiates a new Random walk sequence.
70     */
71    public RandomWalkSequence() {
72      this(MINUS_ONE, ONE);
73    }
74  
75    /**
76     * Instantiates a new Random walk sequence.
77     *
78     * @param minStep the min step
79     * @param maxStep the max step
80     */
81    public RandomWalkSequence(BigDecimal minStep, BigDecimal maxStep) {
82      this(minStep, maxStep, null);
83    }
84  
85    /**
86     * Instantiates a new Random walk sequence.
87     *
88     * @param minStep the min step
89     * @param maxStep the max step
90     * @param initial the initial
91     */
92    public RandomWalkSequence(BigDecimal minStep, BigDecimal maxStep, BigDecimal initial) {
93      this(minStep, maxStep, initial, DEFAULT_BUFFERED);
94    }
95  
96    /**
97     * Instantiates a new Random walk sequence.
98     *
99     * @param minStep  the min step
100    * @param maxStep  the max step
101    * @param initial  the initial
102    * @param buffered the buffered
103    */
104   public RandomWalkSequence(BigDecimal minStep, BigDecimal maxStep, BigDecimal initial, boolean buffered) {
105     this.minStep = minStep;
106     this.maxStep = maxStep;
107     this.initial = initial;
108     this.buffered = buffered;
109   }
110 
111   /**
112    * Sets min step.
113    *
114    * @param minStep the min step
115    */
116   public void setMinStep(BigDecimal minStep) {
117     this.minStep = minStep;
118   }
119 
120   /**
121    * Sets max step.
122    *
123    * @param maxStep the max step
124    */
125   public void setMaxStep(BigDecimal maxStep) {
126     this.maxStep = maxStep;
127   }
128 
129   /**
130    * Sets initial.
131    *
132    * @param initial the initial
133    */
134   public void setInitial(BigDecimal initial) {
135     this.initial = initial;
136   }
137 
138 
139   // Distribution interface implementation ---------------------------------------------------------------------------
140 
141   @Override
142   public <T extends Number> NonNullGenerator<T> createNumberGenerator(Class<T> numberType, T min, T max, T granularity, boolean unique) {
143     if (max == null) {
144       max = NumberUtil.maxValue(numberType);
145     }
146     NonNullGenerator<? extends Number> base;
147     if (BeanUtil.isIntegralNumberType(numberType)) {
148       base = createLongGenerator(toLong(min), toLong(max), toLong(granularity), unique);
149     } else {
150       base = createDoubleGenerator(toDouble(min), toDouble(max), toDouble(granularity), unique);
151     }
152     return WrapperFactory.asNonNullNumberGeneratorOfType(numberType, base, min, granularity);
153   }
154 
155   @Override
156   public <T> Generator<T> applyTo(Generator<T> source, boolean unique) {
157     if (buffered || MathUtil.between(0L, toLong(minStep), toLong(maxStep))) {
158       return super.applyTo(source, unique);
159     } else {
160       return applySkipGenerator(source, unique);
161     }
162   }
163 
164   private <T> Generator<T> applySkipGenerator(Generator<T> source, boolean unique) {
165     int minStepI = toInteger(minStep);
166     if (unique && minStepI <= 0) {
167       throw new ConfigurationError("Cannot generate unique values when minStep=" + minStep);
168     }
169     return new SkipGeneratorProxy<>(source, minStepI, toInteger(maxStep));
170   }
171 
172   // helper methods --------------------------------------------------------------------------------------------------
173 
174   private <T> NonNullGenerator<? extends Number> createDoubleGenerator(double min, double max, double granularity, boolean unique) {
175     if (unique && MathUtil.rangeIncludes(0., min, max)) {
176       // check if uniqueness requirements can be met
177       throw new InvalidGeneratorSetupException("Cannot guarantee uniqueness for [min=" + min + ",max=" + max + "]");
178     }
179     return new RandomWalkDoubleGenerator(
180         toDouble(min), toDouble(max), toDouble(granularity), toDouble(minStep), toDouble(maxStep));
181   }
182 
183   private <T> NonNullGenerator<? extends Number> createLongGenerator(long min, long max, long granularity, boolean unique) {
184     if (unique && MathUtil.rangeIncludes(0, min, max)) {
185       // check if uniqueness requirements can be met
186       throw new InvalidGeneratorSetupException("Cannot guarantee uniqueness for [min=" + min + ",max=" + max + "]");
187     }
188     return new RandomWalkLongGenerator(
189         min, max, toLong(granularity), toLong(initial(min, max, Long.class)), toLong(minStep), toLong(maxStep));
190   }
191 
192   private <T extends Number> T initial(T min, T max, Class<T> numberType) {
193     if (initial != null) {
194       return NumberToNumberConverter.convert(initial, numberType);
195     }
196     if (minStep.doubleValue() > 0) {
197       return min;
198     }
199     if (maxStep.doubleValue() > 0) {
200       return NumberToNumberConverter.convert((min.doubleValue() + max.doubleValue()) / 2, numberType);
201     } else {
202       return max;
203     }
204   }
205 
206 }