View Javadoc
1   package eu.simuline.testhelpers;
2   
3   import java.util.List;
4   import java.util.ArrayList;
5   
6   /**
7    * Creates double values for tests. 
8    *
9    *
10   * Created: Tue Mar 20 01:05:54 2012
11   *
12   * @author <a href="mailto:ernst@">Ernst Reissner</a>
13   * @version 1.0
14   */
15  public abstract class DTestHelper {
16  
17  	/**
18  	 * Returns a random number with absolute value in <code>[0,1]</code>.
19  	 *
20  	 * @param signed
21  	 *               whether the number returned may be negative.
22  	 * @return
23  	 *         a random number
24  	 *         <ul>
25  	 *         <li>in <code>[-1,1]</code> if <code> signed</code>.
26  	 *         <li>in <code>[ 0,1]</code> if <code>!signed</code>.
27  	 *         </ul>
28  	 */
29  	static double random(boolean signed) {
30  		return signed ? 2. * (Math.random() - 0.5) : Math.random();
31  	}
32  
33  	public static double createArgD(boolean isSigned) {
34  		double cand;
35  		cand = Math.random();
36  		cand *= createPower2();
37  		cand *= isSigned ? Math.signum(Math.random() - 0.5) : 1;
38  		return cand;
39  	}
40  
41  	/**
42  	 * Returns a random number which is signed
43  	 * if demanded by <code>isSigned</code>
44  	 * and which ranges from the given exponents.
45  	 */
46  	public static double createArgD(boolean isSigned, int exp0, int exp1) {
47  		double cand;
48  		cand = Math.random();
49  		cand *= Math.pow(2.0, (exp1 - exp0) * Math.random() + exp0);
50  		cand *= isSigned ? Math.signum(Math.random() - 0.5) : 1;
51  		return cand;
52  	}
53  
54  	/**
55  	 * Returns a list of <code>numArgs</code> arguments as double values.
56  	 * The sum of all numbers are restricted as specified by the parameters.
57  	 *
58  	 * @param numArgs
59  	 *                the length of the argument list to be returned.
60  	 * @param signed
61  	 *                whether the arguments may be negative.
62  	 * @param inRange
63  	 *                whether the sum of the entries in the result List
64  	 *                is in <code>[0,1)</code> or,
65  	 *                if <code>signed</code> in <code>(-1,1)</code>.
66  	 * @return
67  	 *         a list of <code>numArgs</code> arguments as double values.
68  	 *         The sum of all numbers is in <code>[0,1)</code>
69  	 *         and for <code>!signed</code> even in <code>[0.5,1)</code>.
70  	 *         The entries are in <code>[0,1)</code> for <code>!signed</code>
71  	 *         and in <code>(-1,1)</code> for <code>signed</code>.
72  	 */
73  	public static List<Double> createMultArgsSumD(int numArgs,
74  			boolean signed,
75  			boolean inRange) {
76  
77  		// create a list of random numbers in Double-format.
78  		List<Double> resultD = new ArrayList<Double>(numArgs);
79  
80  		if (numArgs == 0) {
81  			return resultD;
82  		}
83  
84  		double sum = 0;
85  		double max = 0;
86  		double cand;
87  		for (int i = 0; i < numArgs; i++) {
88  			cand = random(signed);
89  			// cand in [-1,1] or in [ 0,1] depending on signed
90  			max = Math.max(max, Math.abs(cand));
91  			resultD.add(cand);
92  			sum += cand;
93  		} // for i
94  		// Here, sum contains the sum of the entries of resultD
95  		// whereas max contains the maximum of the absolute values.
96  
97  		if (inRange) {
98  			int sgn = (int) Math.signum(sum);
99  
100 			// make sure that the sum of all arguments does not reach 1.
101 			max = Math.max(max, Math.abs(sum));
102 			// TBC: why is this necessary?
103 			double shift = Math.pow(2,
104 					// Math.ceil(MathExt.ld(max)));
105 					Math.ceil(Math.log(max) / Math.log(2)));
106 			// Note that shift is in fact an integer.
107 			// leaving out Math.ceil, we obtain
108 			// shift <= Math.pow(2,MathExt.ld(max))=max
109 
110 			assert -1 < sum / shift && sum / shift < 1;
111 			assert -1 < max / shift && max / shift < 1;
112 
113 			for (int i = 0; i < numArgs; i++) {
114 				// "/shift" that all entries and the sum are in (-1,1)
115 				// "*sgn" that the sum is even in [0,1)
116 				resultD.set(i, (double) resultD.get(i) / shift * sgn);
117 			}
118 		} // inRange
119 
120 		return resultD;
121 	}
122 
123 	private static final int MAX_NUM_ARGS = 10;
124 
125 	// at least 1, at most MAX_NUM_ARGS
126 	private static int createNumArgs() {
127 		return 1 + (int) Math.round(MAX_NUM_ARGS * Math.random());
128 	}
129 
130 	public static List<Double> createMultArgsSumD(boolean signed,
131 			boolean inRange) {
132 		return createMultArgsSumD(createNumArgs(), signed, inRange);
133 	}
134 
135 	private static final double FRAC_NON_NAN = 0.95;
136 
137 	public static List<Double> createMultArgsD(int numArgs,
138 			boolean allowsSigned,
139 			boolean inRange,
140 			boolean allowsNaN) {
141 		// create a list of random numbers in Double-format.
142 		List<Double> resultD = new ArrayList<Double>(numArgs);
143 		double num;
144 		for (int i = 0; i < numArgs; i++) {
145 			if (allowsNaN && Math.random() > FRAC_NON_NAN) {
146 				num = Double.NaN;
147 			} else {
148 				num = Math.random();
149 				num *= inRange ? 1 : createPower2();
150 				num *= allowsSigned ? Math.signum(Math.random() - 0.5) : 1;
151 			}
152 
153 			resultD.add(num);
154 		}
155 
156 		return resultD;
157 	}
158 
159 	private static final int RANGE_POW2 = 50;
160 
161 	private static double createPower2() {
162 		return Math.pow(2.0, 2 * RANGE_POW2 * Math.random() - RANGE_POW2);
163 		// TBD: try also with cast to int. 
164 		// This unveils overflow of tolerance 
165 	}
166 
167 	public static List<Double> createMultArgsD(boolean signed,
168 			boolean inRange,
169 			boolean allowsNaN) {
170 		return createMultArgsD(createNumArgs(), signed, inRange, allowsNaN);
171 	}
172 
173 }