SquirrelJME/SquirrelJME

View on GitHub
modules/cldc-compact/src/main/java/java/util/Random.java

Summary

Maintainability
A
0 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package java.util;

import cc.squirreljme.runtime.cldc.annotation.Api;
import cc.squirreljme.runtime.cldc.annotation.ImplementationNote;
import cc.squirreljme.runtime.cldc.util.FDMLMath;

/**
 * This is an insecure pseudo-random number generator.
 *
 * This class is thread safe however if multiple threads are using a single
 * instance then it is undefined if their order is the same.
 *
 * @since 2018/11/02
 */
@ImplementationNote("This code essentially is a copy and paste from the " +
    "library documentation since it must always be the same exact code. " +
    "Since there is only one way to implement this class it should be " +
    "legal to do so for compatibility purposes.")
@Api
public class Random
{
    /** The current seed value. */
    private long _seed;
    
    /** The next gaussian value. */
    private double _nextg;
    
    /** Has next next gaussian value? */
    private boolean _hasnng;
    
    /**
     * Initializes the random number generator using an unspecified and
     * potentially different key per construction.
     *
     * @since 2018/11/02
     */
    @Api
    public Random()
    {
        this(System.currentTimeMillis() +
            System.nanoTime());
    }
    
    /**
     * Initializes the random number generator with the given seed.
     *
     * @param __seed The seed to initialize with.
     * @since 2018/11/02
     */
    @Api
    public Random(long __seed)
    {
        this.setSeed(__seed);
    }
    
    /**
     * Generates the next pseudorandom number, this method is used by every
     * other method in this class. As such this is the only method that has
     * to be changed if the internal algorithm is to be changed.
     *
     * @param __bits The number of bits to return.
     * @return The produced value.
     * @since 2018/11/02
     */
    @Api
    @SuppressWarnings("MagicNumber")
    protected int next(int __bits)
    {
        // This has to be atomic
        synchronized (this)
        {
            // Update the seed
            long seed = (this._seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
            this._seed = seed;
            
            return (int)(seed >>> (48 - __bits));
        }
    }
    
    /**
     * Returns the next boolean value.
     *
     * @return The next value.
     * @since 2018/11/02
     */
    @Api
    public boolean nextBoolean()
    {
        return this.next(1) != 0;
    }
    
    /**
     * Generates random bytes and places them into the array.
     *
     * @param __a The output array.
     * @throws NullPointerException On null arguments.
     * @since 2018/11/02
     */
    @Api
    public void nextBytes(byte[] __a)
        throws NullPointerException
    {
        if (__a == null)
            throw new NullPointerException("NARG");
        
        for (int i = 0, l = __a.length; i < l;)
            for (int r = this.nextInt(), n = Math.min(l - i, 4); n-- > 0;
                r >>= 8)
                __a[i++] = (byte)r;
    }
    
    /**
     * Returns the next double value.
     *
     * @return The next value.
     * @since 2018/11/02
     */
    @Api
    @SuppressWarnings("MagicNumber")
    public double nextDouble()
    {
        return (((long)this.next(26) << 27) + this.next(27)) /
            (double)(1L << 53);
    }
    
    /**
     * Returns the next float value.
     *
     * @return The next value.
     * @since 2018/11/02
     */
    @Api
    @SuppressWarnings("MagicNumber")
    public float nextFloat()
    {
        return this.next(24) / ((float)(1 << 24));
    }
    
    /**
     * Return the next gaussian value.
     *
     * @return The next value.
     * @since 2018/11/02
     */
    @Api
    public double nextGaussian()
    {
        synchronized (this)
        {
            // If there was already a cached value, use it
            boolean hasnng = this._hasnng;
            if (hasnng)
            {
                this._hasnng = false;
                return this._nextg;
            }
            
            // Otherwise generate it
            else
            {
                double v1, v2, s;
                do
                {
                    // Between -1.0 and 1.0
                    v1 = 2 * this.nextDouble() - 1;
                    v2 = 2 * this.nextDouble() - 1;

                    s = v1 * v1 + v2 * v2;
                } while (s >= 1 || s == 0);
                
                double multiplier =
                    FDMLMath.sqrt(-2 * FDMLMath.log(s) / s);
                
                // Store for next time
                this._nextg = v2 * multiplier;
                this._hasnng = true;
                
                // Return generated value
                return v1 * multiplier;
            }
        }
    }
    
    /**
     * Returns the next int value.
     *
     * @return The next value.
     * @since 2018/11/02
     */
    @Api
    public int nextInt()
    {
        return this.next(32);
    }
    
    /**
     * Returns the next int value between inclusive 0 and exclusive
     * {@code __cap}.
     *
     * @param __cap The exclusive value to end at.
     * @return The next value.
     * @throws IllegalArgumentException If the cap is zero or negative.
     * @since 2018/11/02
     */
    @Api
    @SuppressWarnings("MagicNumber")
    public int nextInt(int __cap)
        throws IllegalArgumentException
    {
        /* {@squirreljme.error ZZ2z Cap value must be a positive integer} */
        if (__cap <= 0)
            throw new IllegalArgumentException("ZZ2z");
        
        // Cap is a power of two
        if ((__cap & -__cap) == __cap)
            return (int)((__cap * (long)this.next(31)) >> 31);
        
        // Otherwise calculate based on values in the range
        int bits, val;
        do
        {
            bits = this.next(31);
            val = bits % __cap;
        } while (bits - val + (__cap - 1) < 0);
        
        return val;
    }
    
    /**
     * Returns the next long value.
     *
     * @return The next long value.
     * @since 2018/11/02
     */
    @Api
    @SuppressWarnings("MagicNumber")
    public long nextLong()
    {
        return ((long)this.next(32) << 32) + this.next(32);
    }
    
    /**
     * Sets the seed for this random number generator.
     *
     * This is an atomic operation.
     *
     * @param __seed The seed to use.
     * @since 2018/11/02
     */
    @Api
    @SuppressWarnings("MagicNumber")
    public void setSeed(long __seed)
    {
        synchronized (this)
        {
            this._seed = (__seed ^ 0x5DEECE66DL) & ((1L << 48) - 1);
            this._hasnng = false;
        }
    }
}