/*
* Licensed to the Ted Dunning under one or more contributor license
* agreements. See the NOTICE file that may be
* distributed with this work for additional information
* regarding copyright ownership. Ted Dunning 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 com.mapr.synth;
import com.google.common.base.Charsets;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import java.util.Iterator;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Defines a tree of random number generators such that complex randomized structures can be
* built in parallel in a deterministic way. The based idea is that a NestedRandom can produce
* off-shoot NestedRandom generators given either an index or a String. You can also get a
* conventional Random from a NestedRandom at any time. The idea is that a data structure
* can have components which need stable randomness sources and those components will also have
* components. At the leaves of such a JSON-style nested data structure, you have conventional
* random number generators seeded consistently given the path down to that generator.
*
* Note that NestedRandom instances are immutable and do not share any storage. They are
* fully thread safe.
*/
public class NestedRandom implements Iterable<NestedRandom> {
private NestedRandom parent;
private final String content;
private int seed;
/**
* Returns a NestedRandom with a default seed.
*/
public NestedRandom() {
this(0);
}
/**
* Returns a NestedRandom with a specified seed.
* @param seed The seed to use.
*/
public NestedRandom(int seed) {
this(null, seed);
}
/**
* Get a NestedRandom corresponding to the component with the specified name.
* @param component Which component to return.
* @return A component stream.
*/
public NestedRandom get(String component) {
return new NestedRandom(this, component);
}
/**
* Get a NestedRandom corresponding to a specified position in the notional array
* of random generators with index i.
* @param i Which component to get.
* @return The requested component.
*/
public NestedRandom get(int i) {
return new NestedRandom(this, i);
}
/**
* Returns an iterator through the same generators indexed using @get(int).
* @return An iterator that returns all of the integer addressable components.
*/
public Iterator<NestedRandom> iterator() {
return new Iterator<NestedRandom>() {
AtomicInteger i = new AtomicInteger(-1);
public boolean hasNext() {
return true;
}
public NestedRandom next() {
return NestedRandom.this.get(i.incrementAndGet());
}
@Override
public void remove() {
throw new UnsupportedOperationException("Default operation");
}
};
}
/**
* Return the random number generator associated with the current NestedRandom component.
*/
public Random random() {
return new Random(hash(0));
}
private NestedRandom(NestedRandom parent, int value) {
this.parent = parent;
content = null;
seed = value;
}
private NestedRandom(NestedRandom parent, String component) {
this.parent = parent;
content = component;
}
private void hash(Hasher hasher) {
if (content != null) {
hasher.putString(content, Charsets.UTF_8);
} else {
hasher.putInt(seed);
}
if (parent != null) {
parent.hash(hasher);
}
}
private long hash(long seed) {
Hasher h = Hashing.murmur3_128().newHasher();
h.putLong(seed);
hash(h);
return h.hash().asLong();
}
}