001/*
002 * Licensed to the Apache Software Foundation (ASF) 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 org.apache.commons.rng.simple.internal;
018
019import org.apache.commons.rng.core.util.NumberFactory;
020
021/**
022 * The native seed type. Contains values for all native seed types and methods
023 * to convert supported seed types to the native seed type.
024 *
025 * <p>Valid native seed types are:</p>
026 * <ul>
027 *  <li>{@code Integer}</li>
028 *  <li>{@code Long}</li>
029 *  <li>{@code int[]}</li>
030 *  <li>{@code long[]}</li>
031 * </ul>
032 *
033 * <p>Valid types for seed conversion are:</p>
034 * <ul>
035 *  <li>{@code Integer} (or {@code int})</li>
036 *  <li>{@code Long} (or {@code long})</li>
037 *  <li>{@code int[]}</li>
038 *  <li>{@code long[]}</li>
039 *  <li>{@code byte[]}</li>
040 * </ul>
041 *
042 * @since 1.3
043 */
044public enum NativeSeedType {
045    /** The seed type is {@code Integer}. */
046    INT(Integer.class, 4) {
047        @Override
048        public Integer createSeed(int size, int from, int to) {
049            return SeedFactory.createInt();
050        }
051        @Override
052        protected Integer convert(Integer seed, int size) {
053            return seed;
054        }
055        @Override
056        protected Integer convert(Long seed, int size) {
057            return Conversions.long2Int(seed);
058        }
059        @Override
060        protected Integer convert(int[] seed, int size) {
061            return Conversions.intArray2Int(seed);
062        }
063        @Override
064        protected Integer convert(long[] seed, int size) {
065            return Conversions.longArray2Int(seed);
066        }
067        @Override
068        protected Integer convert(byte[] seed, int size) {
069            return Conversions.byteArray2Int(seed);
070        }
071    },
072    /** The seed type is {@code Long}. */
073    LONG(Long.class, 8) {
074        @Override
075        public Long createSeed(int size, int from, int to) {
076            return SeedFactory.createLong();
077        }
078        @Override
079        protected Long convert(Integer seed, int size) {
080            return Conversions.int2Long(seed);
081        }
082        @Override
083        protected Long convert(Long seed, int size) {
084            return seed;
085        }
086        @Override
087        protected Long convert(int[] seed, int size) {
088            return Conversions.intArray2Long(seed);
089        }
090        @Override
091        protected Long convert(long[] seed, int size) {
092            return Conversions.longArray2Long(seed);
093        }
094        @Override
095        protected Long convert(byte[] seed, int size) {
096            return Conversions.byteArray2Long(seed);
097        }
098    },
099    /** The seed type is {@code int[]}. */
100    INT_ARRAY(int[].class, 4) {
101        @Override
102        public int[] createSeed(int size, int from, int to) {
103            // Limit the number of calls to the synchronized method. The generator
104            // will support self-seeding.
105            return SeedFactory.createIntArray(Math.min(size, RANDOM_SEED_ARRAY_SIZE),
106                                              from, to);
107        }
108        @Override
109        protected int[] convert(Integer seed, int size) {
110            return Conversions.int2IntArray(seed, size);
111        }
112        @Override
113        protected int[] convert(Long seed, int size) {
114            return Conversions.long2IntArray(seed, size);
115        }
116        @Override
117        protected int[] convert(int[] seed, int size) {
118            return seed;
119        }
120        @Override
121        protected int[] convert(long[] seed, int size) {
122            // Avoid zero filling seeds that are too short
123            return Conversions.longArray2IntArray(seed,
124                Math.min(size, Conversions.intSizeFromLongSize(seed.length)));
125        }
126        @Override
127        protected int[] convert(byte[] seed, int size) {
128            // Avoid zero filling seeds that are too short
129            return Conversions.byteArray2IntArray(seed,
130                Math.min(size, Conversions.intSizeFromByteSize(seed.length)));
131        }
132    },
133    /** The seed type is {@code long[]}. */
134    LONG_ARRAY(long[].class, 8) {
135        @Override
136        public long[] createSeed(int size, int from, int to) {
137            // Limit the number of calls to the synchronized method. The generator
138            // will support self-seeding.
139            return SeedFactory.createLongArray(Math.min(size, RANDOM_SEED_ARRAY_SIZE),
140                                               from, to);
141        }
142        @Override
143        protected long[] convert(Integer seed, int size) {
144            return Conversions.int2LongArray(seed, size);
145        }
146        @Override
147        protected long[] convert(Long seed, int size) {
148            return Conversions.long2LongArray(seed, size);
149        }
150        @Override
151        protected long[] convert(int[] seed, int size) {
152            // Avoid zero filling seeds that are too short
153            return Conversions.intArray2LongArray(seed,
154                Math.min(size, Conversions.longSizeFromIntSize(seed.length)));
155        }
156        @Override
157        protected long[] convert(long[] seed, int size) {
158            return seed;
159        }
160        @Override
161        protected long[] convert(byte[] seed, int size) {
162            // Avoid zero filling seeds that are too short
163            return Conversions.byteArray2LongArray(seed,
164                Math.min(size, Conversions.longSizeFromByteSize(seed.length)));
165        }
166    };
167
168    /** Error message for unrecognised seed types. */
169    private static final String UNRECOGNISED_SEED = "Unrecognized seed type: ";
170    /** Maximum length of the seed array (for creating array seeds). */
171    private static final int RANDOM_SEED_ARRAY_SIZE = 128;
172
173    /** Define the class type of the native seed. */
174    private final Class<?> type;
175
176    /**
177     * Define the number of bytes required to represent the native seed. If the type is
178     * an array then this represents the size of a single value of the type.
179     */
180    private final int bytes;
181
182    /**
183     * Instantiates a new native seed type.
184     *
185     * @param type Define the class type of the native seed.
186     * @param bytes Define the number of bytes required to represent the native seed.
187     */
188    NativeSeedType(Class<?> type, int bytes) {
189        this.type = type;
190        this.bytes = bytes;
191    }
192
193    /**
194     * Gets the class type of the native seed.
195     *
196     * @return the type
197     */
198    public Class<?> getType() {
199        return type;
200    }
201
202    /**
203     * Gets the number of bytes required to represent the native seed type. If the type is
204     * an array then this represents the size of a single value of the type.
205     *
206     * @return the number of bytes
207     */
208    public int getBytes() {
209        return bytes;
210    }
211
212    /**
213     * Creates the seed. The output seed type is determined by the native seed type. If the
214     * output is an array the required size of the array can be specified.
215     *
216     * @param size The size of the seed (array types only).
217     * @return the seed
218     */
219    public Object createSeed(int size) {
220        // Maintain behaviour since 1.3 to ensure position [0] of array seeds is non-zero.
221        return createSeed(size, 0, Math.min(size, 1));
222    }
223
224    /**
225     * Creates the seed. The output seed type is determined by the native seed type. If
226     * the output is an array the required size of the array can be specified and a
227     * sub-range that must not be all-zero.
228     *
229     * @param size The size of the seed (array types only).
230     * @param from The start of the not all-zero sub-range (inclusive; array types only).
231     * @param to The end of the not all-zero sub-range (exclusive; array types only).
232     * @return the seed
233     * @throws IndexOutOfBoundsException if the sub-range is out of bounds
234     * @since 1.5
235     */
236    public abstract Object createSeed(int size, int from, int to);
237
238    /**
239     * Converts the input seed from any of the supported seed types to the native seed type.
240     * If the output is an array the required size of the array can be specified.
241     *
242     * @param seed Input seed.
243     * @param size The size of the output seed (array types only).
244     * @return the native seed.
245     * @throws UnsupportedOperationException if the {@code seed} type is invalid.
246     */
247    public Object convertSeed(Object seed,
248                              int size) {
249        // Convert to native type.
250        // Each method must be overridden by specific implementations.
251
252        if (seed instanceof Integer) {
253            return convert((Integer) seed, size);
254        } else if (seed instanceof Long) {
255            return convert((Long) seed, size);
256        } else if (seed instanceof int[]) {
257            return convert((int[]) seed, size);
258        } else if (seed instanceof long[]) {
259            return convert((long[]) seed, size);
260        } else if (seed instanceof byte[]) {
261            return convert((byte[]) seed, size);
262        }
263
264        throw new UnsupportedOperationException(unrecognisedSeedMessage(seed));
265    }
266
267    /**
268     * Convert the input {@code Integer} seed to the native seed type.
269     *
270     * @param seed Input seed.
271     * @param size The size of the output seed (array types only).
272     * @return the native seed.
273     */
274    protected abstract Object convert(Integer seed, int size);
275
276    /**
277     * Convert the input {@code Long} seed to the native seed type.
278     *
279     * @param seed Input seed.
280     * @param size The size of the output seed (array types only).
281     * @return the native seed.
282     */
283    protected abstract Object convert(Long seed, int size);
284
285    /**
286     * Convert the input {@code int[]} seed to the native seed type.
287     *
288     * @param seed Input seed.
289     * @param size The size of the output seed (array types only).
290     * @return the native seed.
291     */
292    protected abstract Object convert(int[] seed, int size);
293
294    /**
295     * Convert the input {@code long[]} seed to the native seed type.
296     *
297     * @param seed Input seed.
298     * @param size The size of the output seed (array types only).
299     * @return the native seed.
300     */
301    protected abstract Object convert(long[] seed, int size);
302
303    /**
304     * Convert the input {@code byte[]} seed to the native seed type.
305     *
306     * @param seed Input seed.
307     * @param size The size of the output seed (array types only).
308     * @return the native seed.
309     */
310    protected abstract Object convert(byte[] seed, int size);
311
312    /**
313     * Converts the input seed from any of the supported seed types to bytes.
314     *
315     * @param seed Input seed.
316     * @return the seed bytes.
317     * @throws UnsupportedOperationException if the {@code seed} type is invalid.
318     */
319    public static byte[] convertSeedToBytes(Object seed) {
320        if (seed instanceof Integer) {
321            return NumberFactory.makeByteArray((Integer) seed);
322        } else if (seed instanceof Long) {
323            return NumberFactory.makeByteArray((Long) seed);
324        } else if (seed instanceof int[]) {
325            return NumberFactory.makeByteArray((int[]) seed);
326        } else if (seed instanceof long[]) {
327            return NumberFactory.makeByteArray((long[]) seed);
328        } else if (seed instanceof byte[]) {
329            return (byte[]) seed;
330        }
331
332        throw new UnsupportedOperationException(unrecognisedSeedMessage(seed));
333    }
334
335    /**
336     * Create an unrecognised seed message. This will add the class type of the seed.
337     *
338     * @param seed the seed
339     * @return the message
340     */
341    private static String unrecognisedSeedMessage(Object seed) {
342        return UNRECOGNISED_SEED + ((seed == null) ? "null" : seed.getClass().getName());
343    }
344}