This is an automated email from the ASF dual-hosted git repository. aherbert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-rng.git
commit e637b4f300955942ebb875cc53735e73f4b2a952 Author: Alex Herbert <aherb...@apache.org> AuthorDate: Sat Oct 5 00:41:35 2019 +0100 Created Hex class for encoding/decoding hex bytes. Added hex seed option to the output command. --- .../apache/commons/rng/examples/stress/Hex.java | 106 +++++++++++++++++++++ .../commons/rng/examples/stress/OutputCommand.java | 58 +++++++++-- .../commons/rng/examples/stress/RNGUtils.java | 32 ------- .../rng/examples/stress/StressTestCommand.java | 2 +- 4 files changed, 157 insertions(+), 41 deletions(-) diff --git a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/Hex.java b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/Hex.java new file mode 100644 index 0000000..c71532c --- /dev/null +++ b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/Hex.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.rng.examples.stress; + +/** + * Encodes and decodes bytes as hexadecimal characters. + * + * <p>Adapted from commons-codec.</p> + */ +final class Hex { + /** + * Used to build 4-bit numbers as Hex. + */ + private static final char[] HEX_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + /** No public construction. */ + private Hex() {} + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal + * values of each byte in order. The returned array will be double the length of the + * passed array, as it takes two characters to represent any given byte. + * + * <p>This can be used to encode byte array seeds into a text representation.</p> + * + * @param data A byte[] to convert to Hex characters + * @return A char[] containing the lower-case Hex representation + */ + static char[] encodeHex(final byte[] data) { + final int l = data.length; + final char[] out = new char[l << 1]; + // Two characters form the hex value + for (int i = 0; i < l; i++) { + // Upper 4-bits + out[2 * i] = HEX_DIGITS[(0xf0 & data[i]) >>> 4]; + // Lower 4-bits + out[2 * i + 1] = HEX_DIGITS[ 0x0f & data[i]]; + } + return out; + } + + /** + * Converts an array of characters representing hexadecimal values into an array + * of bytes of those same values. The returned array will be half the length of + * the passed array, as it takes two characters to represent any given byte. An + * exception is thrown if the passed char array has an odd number of elements. + * + * @param data An array of characters containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied char array. + * @throws IllegalArgumentException Thrown if an odd number or illegal of + * characters is supplied + */ + public static byte[] decodeHex(final CharSequence data) { + + final int len = data.length(); + + if ((len & 0x01) != 0) { + throw new IllegalArgumentException("Odd number of characters."); + } + + final byte[] out = new byte[len >> 1]; + + // Two characters form the hex value + for (int j = 0; j < len; j += 2) { + final int f = (toDigit(data, j) << 4) | + toDigit(data, j + 1); + out[j / 2] = (byte) f; + } + + return out; + } + + /** + * Converts a hexadecimal character to an integer. + * + * @param data An array of characters containing hexadecimal digits + * @param index The index of the character in the source + * @return An integer + * @throws IllegalArgumentException Thrown if ch is an illegal hex character + */ + protected static int toDigit(final CharSequence data, final int index) { + final char ch = data.charAt(index); + final int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new IllegalArgumentException("Illegal hexadecimal character " + ch + + " at index " + index); + } + return digit; + } +} diff --git a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/OutputCommand.java b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/OutputCommand.java index da5646b..b27b197 100644 --- a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/OutputCommand.java +++ b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/OutputCommand.java @@ -91,9 +91,16 @@ class OutputCommand implements Callable<Void> { /** The random seed. */ @Option(names = {"-s", "--seed"}, - description = {"The random seed (default: auto)."}) + description = {"The 64-bit number random seed (default: auto)."}) private Long seed; + /** The random seed as a byte[]. */ + @Option(names = {"-x", "--hex-seed"}, + description = {"The hex-encoded random seed.", + "Bytes for other primitives use little-endian format.", + "Over-rides the --seed parameter."}) + private String byteSeed; + /** The count of numbers to output. */ @Option(names = {"-n", "--count"}, description = {"The count of numbers to output.", @@ -156,7 +163,8 @@ class OutputCommand implements Callable<Void> { @Override public Void call() { LogUtils.setLogLevel(reusableOptions.logLevel); - UniformRandomProvider rng = createRNG(); + final Object objectSeed = createSeed(); + UniformRandomProvider rng = createRNG(objectSeed); // Upper or lower bits from 64-bit generators must be created first. // This will throw if not a 64-bit generator. @@ -183,7 +191,7 @@ class OutputCommand implements Callable<Void> { writeBinaryData(rng, out); break; case DIEHARDER: - writeDieharder(rng, seed == null ? "auto" : seed, out); + writeDieharder(rng, out); break; case BITS: writeBitData(rng, out); @@ -198,12 +206,48 @@ class OutputCommand implements Callable<Void> { } /** + * Creates the seed. + * + * @return the seed + */ + private Object createSeed() { + if (byteSeed != null) { + try { + return Hex.decodeHex(byteSeed); + } catch (IllegalArgumentException ex) { + throw new ApplicationException("Invalid hex seed: " + ex.getMessage(), ex); + } + } + if (seed != null) { + return seed; + } + // Let the factory constructor create the native seed. + return null; + } + + /** + * Creates the seed. + * + * @return the seed + */ + private String createSeedString() { + if (byteSeed != null) { + return byteSeed; + } + if (seed != null) { + return seed.toString(); + } + return "auto"; + } + + /** * Creates the RNG. * + * @param objectSeed Seed. * @return the uniform random provider * @throws ApplicationException If the RNG cannot be created */ - private UniformRandomProvider createRNG() { + private UniformRandomProvider createRNG(Object objectSeed) { if (randomSource == null) { throw new ApplicationException("Random source is null"); } @@ -216,7 +260,7 @@ class OutputCommand implements Callable<Void> { data.add(RNGUtils.parseArgument(argument)); } try { - return RandomSource.create(randomSource, seed, data.toArray()); + return RandomSource.create(randomSource, objectSeed, data.toArray()); } catch (IllegalStateException | IllegalArgumentException ex) { throw new ApplicationException("Failed to create RNG: " + randomSource + ". " + ex.getMessage(), ex); } @@ -310,13 +354,11 @@ class OutputCommand implements Callable<Void> { * Write int data to the specified output using the dieharder text format. * * @param rng The random generator. - * @param seedObject The seed using to create the random source. * @param out The output. * @throws IOException Signals that an I/O exception has occurred. * @throws ApplicationException If the count is not positive. */ private void writeDieharder(final UniformRandomProvider rng, - final Object seedObject, final OutputStream out) throws IOException { checkCount(count, OutputFormat.DIEHARDER); @@ -333,7 +375,7 @@ class OutputCommand implements Callable<Void> { output.write("# generator "); output.write(rng.toString()); output.write(" seed = "); - output.write(String.valueOf(seedObject)); + output.write(createSeedString()); output.write(NEW_LINE); writeHeaderLine(output); output.write("type: d"); diff --git a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/RNGUtils.java b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/RNGUtils.java index 4c3056c..29c09a5 100644 --- a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/RNGUtils.java +++ b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/RNGUtils.java @@ -46,13 +46,6 @@ final class RNGUtils { /** Message when not a RandomLongSource. */ private static final String NOT_LONG_SOURCE = "Not a 64-bit long generator: "; - /** - * Used to build 4-bit numbers as Hex. - */ - private static final char[] HEX_DIGITS = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - /** No public construction. */ private RNGUtils() {} @@ -419,29 +412,4 @@ final class RNGUtils { throw new ApplicationException("Failed to parse RandomSource argument: " + argument, ex); } } - - /** - * Converts an array of bytes into an array of characters representing the hexadecimal - * values of each byte in order. The returned array will be double the length of the - * passed array, as it takes two characters to represent any given byte. - * - * <p>This can be used to encode byte array seeds into a text representation.</p> - * - * <p>Adapted from commons-codec.</p> - * - * @param data a byte[] to convert to Hex characters - * @return A char[] containing the lower-case Hex representation - */ - static char[] encodeHex(final byte[] data) { - final int l = data.length; - final char[] out = new char[l << 1]; - // Two characters form the hex value - for (int i = 0; i < l; i++) { - // Upper 4-bits - out[2 * i] = HEX_DIGITS[(0xf0 & data[i]) >>> 4]; - // Lower 4-bits - out[2 * i + 1] = HEX_DIGITS[ 0x0f & data[i]]; - } - return out; - } } diff --git a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java index b8b2c5d..286f825 100644 --- a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java +++ b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java @@ -690,7 +690,7 @@ class StressTestCommand implements Callable<Void> { sb.append(C).append(N) .append(C).append("RandomSource: ").append(randomSource.name()).append(N) .append(C).append("RNG: ").append(rng.toString()).append(N) - .append(C).append("Seed: ").append(RNGUtils.encodeHex(seed)).append(N) + .append(C).append("Seed: ").append(Hex.encodeHex(seed)).append(N) .append(C).append(N) // Match the output of 'java -version', e.g.