View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.rng.core.util;
18  
19  import java.nio.ByteBuffer;
20  import java.nio.ByteOrder;
21  import java.util.Arrays;
22  import java.util.concurrent.ThreadLocalRandom;
23  import java.util.stream.IntStream;
24  import java.util.stream.LongStream;
25  import org.apache.commons.math3.util.Precision;
26  import org.junit.jupiter.api.Assertions;
27  import org.junit.jupiter.api.RepeatedTest;
28  import org.junit.jupiter.api.Test;
29  import org.junit.jupiter.params.ParameterizedTest;
30  import org.junit.jupiter.params.provider.MethodSource;
31  
32  /**
33   * Tests for the {@link NumberFactory}.
34   */
35  class NumberFactoryTest {
36      /** sizeof(int) in bytes. */
37      private static final int INT_SIZE = Integer.BYTES;
38      /** sizeof(long) in bytes. */
39      private static final int LONG_SIZE = Long.BYTES;
40  
41      /** Test values. */
42      private static final long[] LONG_TEST_VALUES = new long[] {0L, 1L, -1L, 19337L, 1234567891011213L,
43          -11109876543211L, Integer.MAX_VALUE, Integer.MIN_VALUE, Long.MAX_VALUE,
44          Long.MIN_VALUE, 0x9e3779b97f4a7c13L};
45      /** Test values. */
46      private static final int[] INT_TEST_VALUES = new int[] {0, 1, -1, 19337, 1234567891, -1110987656,
47          Integer.MAX_VALUE, Integer.MIN_VALUE, 0x9e3779b9};
48  
49      /**
50       * Provide a stream of the test values for long conversion.
51       *
52       * @return the stream
53       */
54      static LongStream longTestValues() {
55          return Arrays.stream(LONG_TEST_VALUES);
56      }
57  
58      /**
59       * Provide a stream of the test values for long conversion.
60       *
61       * @return the stream
62       */
63      static IntStream intTestValues() {
64          return Arrays.stream(INT_TEST_VALUES);
65      }
66  
67      @ParameterizedTest
68      @MethodSource(value = {"intTestValues"})
69      void testMakeBooleanFromInt(int v) {
70          // Test if the bit is set differently then the booleans are opposite
71          final boolean b1 = NumberFactory.makeBoolean(v);
72          final boolean b2 = NumberFactory.makeBoolean(~v);
73          Assertions.assertNotEquals(b1, b2);
74      }
75  
76      @ParameterizedTest
77      @MethodSource(value = {"longTestValues"})
78      void testMakeBooleanFromLong(long v) {
79          // Test if the bit is set differently then the booleans are opposite
80          final boolean b1 = NumberFactory.makeBoolean(v);
81          final boolean b2 = NumberFactory.makeBoolean(~v);
82          Assertions.assertNotEquals(b1, b2);
83      }
84  
85      @Test
86      void testMakeIntFromLong() {
87          // Test the high order bits and low order bits are xor'd together
88          Assertions.assertEquals(0xffffffff, NumberFactory.makeInt(0xffffffff00000000L));
89          Assertions.assertEquals(0x00000000, NumberFactory.makeInt(0xffffffffffffffffL));
90          Assertions.assertEquals(0xffffffff, NumberFactory.makeInt(0x00000000ffffffffL));
91          Assertions.assertEquals(0x00000000, NumberFactory.makeInt(0x0000000000000000L));
92          Assertions.assertEquals(0x0f0f0f0f, NumberFactory.makeInt(0x0f0f0f0f00000000L));
93          Assertions.assertEquals(0xf0f0f0f0, NumberFactory.makeInt(0x00000000f0f0f0f0L));
94          Assertions.assertEquals(0x00000000, NumberFactory.makeInt(0x0f0f0f0f0f0f0f0fL));
95          Assertions.assertEquals(0xffffffff, NumberFactory.makeInt(0x0f0f0f0ff0f0f0f0L));
96      }
97  
98      @ParameterizedTest
99      @MethodSource(value = {"longTestValues"})
100     void testExtractLoExtractHi(long v) {
101         final int vL = NumberFactory.extractLo(v);
102         final int vH = NumberFactory.extractHi(v);
103 
104         final long actual = (((long) vH) << 32) | (vL & 0xffffffffL);
105         Assertions.assertEquals(v, actual);
106     }
107 
108     @ParameterizedTest
109     @MethodSource(value = {"longTestValues"})
110     void testLong2Long(long v) {
111         final int vL = NumberFactory.extractLo(v);
112         final int vH = NumberFactory.extractHi(v);
113 
114         Assertions.assertEquals(v, NumberFactory.makeLong(vH, vL));
115     }
116 
117     @Test
118     void testLongToByteArraySignificanceOrder() {
119         // Start at the least significant bit
120         long value = 1;
121         for (int i = 0; i < LONG_SIZE; i++) {
122             final byte[] b = NumberFactory.makeByteArray(value);
123             for (int j = 0; j < LONG_SIZE; j++) {
124                 // Only one byte should be non zero
125                 Assertions.assertEquals(b[j] != 0, j == i);
126             }
127             // Shift to the next byte
128             value <<= 8;
129         }
130     }
131 
132     @ParameterizedTest
133     @MethodSource(value = {"longTestValues"})
134     void testLongToBytesIsLittleEndian(long v) {
135         final ByteBuffer bb = ByteBuffer.allocate(LONG_SIZE).order(ByteOrder.LITTLE_ENDIAN);
136         bb.putLong(v);
137         Assertions.assertArrayEquals(bb.array(), NumberFactory.makeByteArray(v));
138     }
139 
140     @RepeatedTest(value = 5)
141     void testByteArrayToLongArrayIsLittleEndian() {
142         final int n = 5;
143         byte[] bytes = new byte[n * LONG_SIZE];
144         ThreadLocalRandom.current().nextBytes(bytes);
145         final ByteBuffer bb = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
146         final long[] data = NumberFactory.makeLongArray(bytes);
147         for (int i = 0; i < n; i++) {
148             Assertions.assertEquals(bb.getLong(), data[i]);
149         }
150         Assertions.assertArrayEquals(bytes, NumberFactory.makeByteArray(data));
151     }
152 
153     @ParameterizedTest
154     @MethodSource(value = {"longTestValues"})
155     void testLongFromByteArray2Long(long expected) {
156         final byte[] b = NumberFactory.makeByteArray(expected);
157         Assertions.assertEquals(expected, NumberFactory.makeLong(b));
158     }
159 
160     @Test
161     void testLongArrayFromByteArray2LongArray() {
162         final byte[] b = NumberFactory.makeByteArray(LONG_TEST_VALUES);
163         Assertions.assertArrayEquals(LONG_TEST_VALUES, NumberFactory.makeLongArray(b));
164     }
165 
166     @ParameterizedTest
167     @MethodSource(value = {"longTestValues"})
168     void testLongArrayToByteArrayMatchesLongToByteArray(long v) {
169         // Test individually the bytes are the same as the array conversion
170         final byte[] b1 = NumberFactory.makeByteArray(v);
171         final byte[] b2 = NumberFactory.makeByteArray(new long[] {v});
172         Assertions.assertArrayEquals(b1, b2);
173     }
174 
175     @Test
176     void testIntToByteArraySignificanceOrder() {
177         // Start at the least significant bit
178         int value = 1;
179         for (int i = 0; i < INT_SIZE; i++) {
180             final byte[] b = NumberFactory.makeByteArray(value);
181             for (int j = 0; j < INT_SIZE; j++) {
182                 // Only one byte should be non zero
183                 Assertions.assertEquals(b[j] != 0, j == i);
184             }
185             // Shift to the next byte
186             value <<= 8;
187         }
188     }
189 
190     @ParameterizedTest
191     @MethodSource(value = {"intTestValues"})
192     void testIntToBytesIsLittleEndian(int v) {
193         final ByteBuffer bb = ByteBuffer.allocate(INT_SIZE).order(ByteOrder.LITTLE_ENDIAN);
194         bb.putInt(v);
195         Assertions.assertArrayEquals(bb.array(), NumberFactory.makeByteArray(v));
196     }
197 
198     @RepeatedTest(value = 5)
199     void testByteArrayToIntArrayIsLittleEndian() {
200         final int n = 5;
201         byte[] bytes = new byte[n * INT_SIZE];
202         ThreadLocalRandom.current().nextBytes(bytes);
203         final ByteBuffer bb = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
204         final int[] data = NumberFactory.makeIntArray(bytes);
205         for (int i = 0; i < n; i++) {
206             Assertions.assertEquals(bb.getInt(), data[i]);
207         }
208         Assertions.assertArrayEquals(bytes, NumberFactory.makeByteArray(data));
209     }
210 
211     @ParameterizedTest
212     @MethodSource(value = {"intTestValues"})
213     void testIntFromByteArray2Int(int expected) {
214         final byte[] b = NumberFactory.makeByteArray(expected);
215         Assertions.assertEquals(expected, NumberFactory.makeInt(b));
216     }
217 
218     @Test
219     void testIntArrayFromByteArray2IntArray() {
220         final byte[] b = NumberFactory.makeByteArray(INT_TEST_VALUES);
221         Assertions.assertArrayEquals(INT_TEST_VALUES, NumberFactory.makeIntArray(b));
222     }
223 
224     @ParameterizedTest
225     @MethodSource(value = {"intTestValues"})
226     void testIntArrayToByteArrayMatchesIntToByteArray(int v) {
227         // Test individually the bytes are the same as the array conversion
228         final byte[] b1 = NumberFactory.makeByteArray(v);
229         final byte[] b2 = NumberFactory.makeByteArray(new int[] {v});
230         Assertions.assertArrayEquals(b1, b2);
231     }
232 
233     @Test
234     void testMakeIntPrecondition1() {
235         for (int i = 0; i <= 10; i++) {
236             final byte[] bytes = new byte[i];
237             if (i != INT_SIZE) {
238                 Assertions.assertThrows(IllegalArgumentException.class,
239                     () -> NumberFactory.makeInt(bytes));
240             } else {
241                 Assertions.assertEquals(0, NumberFactory.makeInt(bytes));
242             }
243         }
244     }
245 
246     @Test
247     void testMakeIntArrayPrecondition1() {
248         for (int i = 0; i <= 20; i++) {
249             final byte[] bytes = new byte[i];
250             if (i != 0 && i % INT_SIZE != 0) {
251                 Assertions.assertThrows(IllegalArgumentException.class,
252                     () -> NumberFactory.makeIntArray(bytes));
253             } else {
254                 Assertions.assertArrayEquals(new int[i / INT_SIZE], NumberFactory.makeIntArray(bytes));
255             }
256         }
257     }
258 
259     @Test
260     void testMakeLongPrecondition1() {
261         for (int i = 0; i <= 10; i++) {
262             final byte[] bytes = new byte[i];
263             if (i != LONG_SIZE) {
264                 Assertions.assertThrows(IllegalArgumentException.class,
265                     () -> NumberFactory.makeLong(bytes));
266             } else {
267                 Assertions.assertEquals(0L, NumberFactory.makeLong(bytes));
268             }
269         }
270     }
271 
272     @Test
273     void testMakeLongArrayPrecondition1() {
274         for (int i = 0; i <= 20; i++) {
275             final byte[] bytes = new byte[i];
276             if (i != 0 && i % LONG_SIZE != 0) {
277                 Assertions.assertThrows(IllegalArgumentException.class,
278                     () -> NumberFactory.makeLongArray(bytes));
279             } else {
280                 Assertions.assertArrayEquals(new long[i / LONG_SIZE], NumberFactory.makeLongArray(bytes));
281             }
282         }
283     }
284 
285     /**
286      * Test different methods for generation of a {@code float} from a {@code int}. The output
287      * value should be in the range between 0 and 1.
288      */
289     @Test
290     void testFloatGenerationMethods() {
291         final int allBits = 0xffffffff;
292 
293         // Not capable of generating 1. Set the delta with 1 or 2 ULP of 1.
294         assertCloseToNotAbove1((allBits >>> 9) * 0x1.0p-23f, 2);
295         assertCloseToNotAbove1((allBits >>> 8) * 0x1.0p-24f, 1);
296         assertCloseToNotAbove1(Float.intBitsToFloat(0x7f << 23 | allBits >>> 9) - 1.0f, 2);
297 
298         final int noBits = 0;
299         Assertions.assertEquals(0.0f, (noBits >>> 9) * 0x1.0p-23f);
300         Assertions.assertEquals(0.0f, (noBits >>> 8) * 0x1.0p-24f);
301         Assertions.assertEquals(0.0f, Float.intBitsToFloat(0x7f << 23 | noBits >>> 9) - 1.0f);
302     }
303 
304     /**
305      * Test different methods for generation of a {@code double} from a {@code long}. The output
306      * value should be in the range between 0 and 1.
307      */
308     @Test
309     void testDoubleGenerationMethods() {
310         final long allBits = 0xffffffffffffffffL;
311 
312         // Not capable of generating 1. Set the delta with 1 or 2 ULP of 1.
313         assertCloseToNotAbove1((allBits >>> 12) * 0x1.0p-52d, 2);
314         assertCloseToNotAbove1((allBits >>> 11) * 0x1.0p-53d, 1);
315         assertCloseToNotAbove1(Double.longBitsToDouble(0x3ffL << 52 | allBits >>> 12) - 1.0, 2);
316 
317         final long noBits = 0;
318         Assertions.assertEquals(0.0, (noBits >>> 12) * 0x1.0p-52d);
319         Assertions.assertEquals(0.0, (noBits >>> 11) * 0x1.0p-53d);
320         Assertions.assertEquals(0.0, Double.longBitsToDouble(0x3ffL << 52 | noBits >>> 12) - 1.0);
321     }
322 
323     @Test
324     void testMakeDoubleFromLong() {
325         final long allBits = 0xffffffffffffffffL;
326         final long noBits = 0;
327         // Within 1 ULP of 1.0
328         assertCloseToNotAbove1(NumberFactory.makeDouble(allBits), 1);
329         Assertions.assertEquals(0.0, NumberFactory.makeDouble(noBits));
330     }
331 
332     @Test
333     void testMakeDoubleFromIntInt() {
334         final int allBits = 0xffffffff;
335         final int noBits = 0;
336         // Within 1 ULP of 1.0
337         assertCloseToNotAbove1(NumberFactory.makeDouble(allBits, allBits), 1);
338         Assertions.assertEquals(0.0, NumberFactory.makeDouble(noBits, noBits));
339     }
340 
341     @Test
342     void testMakeFloatFromInt() {
343         final int allBits = 0xffffffff;
344         final int noBits = 0;
345         // Within 1 ULP of 1.0f
346         assertCloseToNotAbove1(NumberFactory.makeFloat(allBits), 1);
347         Assertions.assertEquals(0.0f, NumberFactory.makeFloat(noBits));
348     }
349 
350     /**
351      * Assert that the value is close to but <strong>not above</strong> 1. This is used to test
352      * the output from methods that produce a {@code float} value that must be in the range
353      * between 0 and 1.
354      *
355      * @param value the value
356      * @param maxUlps {@code (maxUlps - 1)} is the number of floating point values between x and y.
357      * @see Precision#equals(float, float, int)
358      */
359     private static void assertCloseToNotAbove1(float value, int maxUlps) {
360         Assertions.assertTrue(value <= 1.0f, "Not <= 1.0f");
361         Assertions.assertTrue(Precision.equals(1.0f, value, maxUlps),
362             () -> "Not equal to 1.0f within units of least precision: " + maxUlps);
363     }
364 
365     /**
366      * Assert that the value is close to but <strong>not above</strong> 1. This is used to test
367      * the output from methods that produce a {@code double} value that must be in the range
368      * between 0 and 1.
369      *
370      * @param value the value
371      * @param maxUlps {@code (maxUlps - 1)} is the number of floating point values between x and y.
372      * @see Precision#equals(double, double, int)
373      */
374     private static void assertCloseToNotAbove1(double value, int maxUlps) {
375         Assertions.assertTrue(value <= 1.0, "Not <= 1.0");
376         Assertions.assertTrue(Precision.equals(1.0, value, maxUlps),
377             () -> "Not equal to 1.0 within units of least precision: " + maxUlps);
378     }
379 }