com.github.rinde.rinsim.core.model.rand.RandomModel.java Source code

Java tutorial

Introduction

Here is the source code for com.github.rinde.rinsim.core.model.rand.RandomModel.java

Source

/*
 * Copyright (C) 2011-2016 Rinde van Lon, iMinds-DistriNet, KU Leuven
 *
 * Licensed 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.github.rinde.rinsim.core.model.rand;

import static com.google.common.base.Preconditions.checkState;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;

import org.apache.commons.math3.random.MersenneTwister;
import org.apache.commons.math3.random.RandomGenerator;

import com.github.rinde.rinsim.core.model.DependencyProvider;
import com.github.rinde.rinsim.core.model.Model.AbstractModel;
import com.github.rinde.rinsim.core.model.ModelBuilder;
import com.github.rinde.rinsim.core.model.ModelBuilder.AbstractModelBuilder;
import com.github.rinde.rinsim.util.StochasticSupplier;
import com.github.rinde.rinsim.util.StochasticSuppliers;
import com.google.auto.value.AutoValue;

/**
 * The random model provides a centralized mechanism for distributing random
 * values throughout an application. The model can be used by implementing the
 * {@link RandomUser} interface. The model allows to set a master seed that is
 * used for all random numbers. Sometimes it is preferable to have different
 * {@link RandomGenerator} instances for different parts of the code to make
 * sure they are independent of each other. The {@link RandomProvider} that is
 * injected into a {@link RandomUser} provides several options for this use
 * case. A builder can be obtained via {@link #builder()}.
 * <p>
 * <b>Model properties</b>
 * <ul>
 * <li><i>Associated type:</i> {@link RandomUser}.</li>
 * <li><i>Provides:</i> {@link RandomProvider}.</li>
 * <li><i>Dependencies:</i> none.</li>
 * </ul>
 * See {@link ModelBuilder} for more information about model properties.
 *
 * @author Rinde van Lon
 * @see RandomUser
 * @see RandomProvider
 */
public class RandomModel extends AbstractModel<RandomUser> {
    /**
     * The default random seed: 123.
     */
    public static final long DEFAULT_SEED = 123L;

    final RandomGenerator masterRandomGenerator;
    final Map<Class<?>, RandomGenerator> classRngMap;

    RandomModel(RandomGenerator rng) {
        masterRandomGenerator = new UnmodifiableRandomGenerator(rng);
        classRngMap = new LinkedHashMap<>();
    }

    @Override
    public boolean register(RandomUser element) {
        final RngProvider provider = new RngProvider();
        element.setRandomGenerator(provider);
        provider.invalidate();
        return true;
    }

    @Override
    @Nonnull
    public <U> U get(Class<U> clazz) {
        if (clazz == RandomProvider.class) {
            return clazz.cast(new RngProvider());
        }
        throw new IllegalArgumentException();
    }

    @Override
    public boolean unregister(RandomUser element) {
        return true;
    }

    /**
     * @return A new {@link RandomModel} that uses a {@link MersenneTwister} with
     *         seed: {@link RandomModel#DEFAULT_SEED}.
     */
    @CheckReturnValue
    public static Builder builder() {
        return Builder.create(DEFAULT_SEED);
    }

    /**
     * A builder for {@link RandomModel}. Instances can be obtained via
     * {@link RandomModel#builder()}.
     * @author Rinde van Lon
     */
    @AutoValue
    public abstract static class Builder extends AbstractModelBuilder<RandomModel, RandomUser>
            implements Serializable {

        static final StochasticSupplier<MersenneTwister> DEFAULT_RNG = StochasticSuppliers.mersenneTwister();
        private static final long serialVersionUID = 7985638617806912711L;

        Builder() {
            setProvidingTypes(RandomProvider.class);
        }

        abstract long seed();

        abstract StochasticSupplier<RandomGenerator> rngSupplier();

        /**
         * Returns a copy of this builder with the specified seed.
         * @param seed The random seed.
         * @return A new builder instance.
         */
        @CheckReturnValue
        public Builder withSeed(long seed) {
            return create(seed, rngSupplier());
        }

        /**
         * Returns a copy of this builder with the specified random generator
         * supplier.
         * @param supplier The supplier of random generators.
         * @return A new builder instance.
         */
        @CheckReturnValue
        public Builder withRandomGenerator(StochasticSupplier<? extends RandomGenerator> supplier) {
            return create(seed(), supplier);
        }

        @Override
        public RandomModel build(DependencyProvider modelProvider) {
            return new RandomModel(rngSupplier().get(seed()));
        }

        static Builder create(long seed) {
            return create(seed, DEFAULT_RNG);
        }

        @SuppressWarnings("unchecked")
        static Builder create(long seed, StochasticSupplier<? extends RandomGenerator> ss) {
            return new AutoValue_RandomModel_Builder(seed, (StochasticSupplier<RandomGenerator>) ss);
        }
    }

    class RngProvider implements RandomProvider {
        boolean used;

        RngProvider() {
            used = false;
        }

        void stateCheck() {
            checkState(!used, "Can be used only once.");
            invalidate();
        }

        void invalidate() {
            used = true;
        }

        @Override
        public long getSeed() {
            stateCheck();
            return masterRandomGenerator.nextLong();
        }

        @Override
        public RandomGenerator masterInstance() {
            stateCheck();
            return masterRandomGenerator;
        }

        @Override
        public RandomGenerator newInstance() {
            stateCheck();
            return new MersenneTwister(masterRandomGenerator.nextLong());
        }

        @Override
        public RandomGenerator sharedInstance(Class<?> clazz) {
            stateCheck();
            if (!classRngMap.containsKey(clazz)) {
                final RandomGenerator rng = new UnmodifiableRandomGenerator(
                        new MersenneTwister(masterRandomGenerator.nextLong()));
                classRngMap.put(clazz, rng);
                return rng;
            }
            return classRngMap.get(clazz);
        }
    }
}