org.jruby.RubyRandom Maven / Gradle / Ivy
/***** BEGIN LICENSE BLOCK *****
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Common Public
* License Version 1.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.eclipse.org/legal/cpl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the CPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the CPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby;
import java.util.Random;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.javasupport.util.RuntimeHelpers;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import static org.jruby.runtime.Visibility.PRIVATE;
import static org.jruby.CompatVersion.*;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import static org.jruby.javasupport.util.RuntimeHelpers.invokedynamic;
import static org.jruby.runtime.MethodIndex.OP_EQUAL;
/**
* Implementation of the Random class.
*/
@JRubyClass(name = "Random")
public class RubyRandom extends RubyObject {
private Random random = new Random();
private IRubyObject seed;
public static RubyClass createRandomClass(Ruby runtime) {
RubyClass randomClass = runtime.defineClass("Random", runtime.getObject(), RANDOM_ALLOCATOR);
runtime.setRandomClass(randomClass);
randomClass.defineAnnotatedMethods(RubyRandom.class);
return randomClass;
}
private static ObjectAllocator RANDOM_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new RubyRandom(runtime, klass);
}
};
private RubyRandom(Ruby runtime, RubyClass rubyClass) {
super(runtime, rubyClass);
}
@JRubyMethod(visibility = PRIVATE, compat = RUBY1_9)
public IRubyObject initialize(ThreadContext context) {
Ruby runtime = context.getRuntime();
long seedLong = random.nextLong();
seed = RubyBignum.newBignum(runtime, seedLong);
random.setSeed(seedLong);
return this;
}
@JRubyMethod(visibility = PRIVATE, compat = RUBY1_9)
public IRubyObject initialize(ThreadContext context, IRubyObject arg) {
long seedLong;
if (arg instanceof RubyFloat) {
seed = RubyBignum.num2fix(((RubyFloat) arg).truncate());
seedLong = RubyNumeric.num2long(seed);
} else if (arg instanceof RubyBignum) {
seed = arg;
seedLong = (long)RubyBignum.big2dbl((RubyBignum)arg);
} else {
seed = arg.convertToInteger();
seedLong = RubyNumeric.num2long(seed);
}
random.setSeed(seedLong);
return this;
}
@JRubyMethod(name = "seed", compat = RUBY1_9)
public IRubyObject seed(ThreadContext context) {
return seed;
}
@JRubyMethod(name = "rand", meta = true, compat = RUBY1_9)
public static IRubyObject rand(ThreadContext context, IRubyObject recv) {
return randCommon(context, context.nil, context.runtime.getRandom(), false);
}
@JRubyMethod(name = "rand", meta = true, compat = RUBY1_9)
public static IRubyObject rand(ThreadContext context, IRubyObject recv, IRubyObject arg0) {
return randCommon(context, arg0, context.runtime.getRandom(), false);
}
@JRubyMethod(name = "rand", compat = RUBY1_9)
public IRubyObject randObj(ThreadContext context) {
return randCommon(context, context.nil, random, true);
}
@JRubyMethod(name = "rand", compat = RUBY1_9)
public IRubyObject randObj(ThreadContext context, IRubyObject arg0) {
return randCommon(context, arg0, random, true);
}
private static IRubyObject randCommon(ThreadContext context, IRubyObject arg, Random random, boolean raiseArgError) {
Ruby runtime = context.getRuntime();
if (arg.isNil()) { // NO ARGUMENTS
return runtime.newFloat(random.nextFloat());
} else if (arg instanceof RubyRange) { // RANGE ARGUMENT
RubyRange range = (RubyRange) arg;
IRubyObject first = range.first();
IRubyObject last = range.last();
boolean returnFloat = first instanceof RubyFloat || last instanceof RubyFloat;
if (returnFloat) {
first = first.convertToFloat();
last = last.convertToFloat();
}
if (range.include_p19(context, last).isTrue() && (!returnFloat)) {
last = last.callMethod(context, "+", runtime.newFixnum(1));
}
if (!first.respondsTo("-") || !first.respondsTo("+") ||
!last.respondsTo("-") || !last.respondsTo("+")) {
throw runtime.newArgumentError("invalid argument - " + arg.toString());
}
IRubyObject difference = last.callMethod(context, "-", first);
if (returnFloat) {
double max = (double) RubyNumeric.num2dbl(difference);
double rand = random.nextDouble() * ((RubyFloat) difference).getDoubleValue();
return RubyFloat.newFloat(runtime, ((RubyFloat) first).getDoubleValue() + rand);
} else {
int max = (int) RubyNumeric.num2long(difference);
int rand = random.nextInt(max);
return RubyNumeric.num2fix(first.callMethod(context, "+", runtime.newFixnum(rand)));
}
} else if (arg instanceof RubyFloat) { // FLOAT ARGUMENT
double max = RubyNumeric.num2dbl(arg);
if (max <= 0 && raiseArgError) {
throw runtime.newArgumentError("invalid argument - " + arg.toString());
}
return runtime.newFloat(random.nextFloat() * max);
} else { // OTHERWISE
int max = 0;
if (arg instanceof RubyBignum) {
max = (int)RubyBignum.big2dbl((RubyBignum)arg);
} else {
if (arg.respondsTo("to_i")) {
arg = arg.callMethod(context, "to_i");
}
max = (int)RubyNumeric.num2long(arg);
}
if (max <= 0 && raiseArgError) {
throw runtime.newArgumentError("invalid argument - " + arg.toString());
}
int rand = random.nextInt(max);
if (arg instanceof RubyBignum) {
return RubyBignum.newBignum(runtime, rand);
}
return runtime.newFixnum(rand);
}
}
@JRubyMethod(meta = true, compat = RUBY1_9)
public static IRubyObject srand(ThreadContext context, IRubyObject recv) {
return srand(context, recv, context.getRuntime().getNil());
}
@JRubyMethod(meta = true, compat = RUBY1_9)
public static IRubyObject srand(ThreadContext context, IRubyObject recv, IRubyObject arg) {
return srandCommon(context, recv, arg, false);
}
public static IRubyObject srandCommon(ThreadContext context, IRubyObject recv, IRubyObject arg, boolean acceptZero) {
Ruby runtime = context.getRuntime();
IRubyObject newSeed = arg;
IRubyObject oldRandomSeed = runtime.getRandomSeed();
long seedArg = 0;
if (arg instanceof RubyBignum) {
seedArg = ((RubyBignum)arg).getValue().longValue();
} else if (!arg.isNil()) {
seedArg = RubyNumeric.num2long(arg);
}
if (arg.isNil() || (!acceptZero && seedArg == 0)) {
seedArg = runtime.newRandomSeed();
newSeed = RubyBignum.newBignum(runtime, seedArg);
}
runtime.setRandomSeed((RubyInteger) newSeed);
runtime.getRandom().setSeed(seedArg);
return oldRandomSeed;
}
@Override
@JRubyMethod(name = "==", required = 1, compat = RUBY1_9)
public IRubyObject op_equal_19(ThreadContext context, IRubyObject obj) {
Ruby runtime = context.getRuntime();
// TODO: mri also compares the "state"
if (!(obj instanceof RubyRandom)) {
return runtime.getFalse();
} else {
RubyRandom r2 = (RubyRandom) obj;
return invokedynamic(context, this.seed(context), OP_EQUAL, r2.seed(context));
}
}
@JRubyMethod(name = "marshal_dump", backtrace = true, compat = RUBY1_9)
public IRubyObject marshal_dump(ThreadContext context) {
RubyArray dump = context.getRuntime().newArray(this, seed);
if (hasVariables()) dump.syncVariables(this);
return dump;
}
@JRubyMethod(compat = RUBY1_9)
public IRubyObject marshal_load(ThreadContext context, IRubyObject arg) {
RubyArray load = arg.convertToArray();
if (load.size() > 0) {
RubyRandom rand = (RubyRandom) load.eltInternal(0);
seed = load.eltInternal(1);
random.setSeed(seed.convertToInteger().getLongValue());
}
if (load.hasVariables()) syncVariables((IRubyObject)load);
return this;
}
@JRubyMethod(compat = RUBY1_9)
public IRubyObject bytes(ThreadContext context, IRubyObject arg) {
int size = RubyNumeric.num2int(arg);
byte[] bytes = new byte[size];
random.nextBytes(bytes);
return context.getRuntime().newString(new ByteList(bytes));
}
public static double randomReal(ThreadContext context, IRubyObject obj) {
Random random = null;
if (obj.equals(context.runtime.getRandomClass())) {
random = context.runtime.getRandom();
}
if (obj instanceof RubyRandom) {
random = ((RubyRandom) obj).random;
}
if (random != null) {
return random.nextDouble();
}
double d = RubyNumeric.num2dbl(RuntimeHelpers.invoke(context, obj, "rand"));
if (d < 0.0 || d >= 1.0) {
throw context.runtime.newRangeError("random number too big: " + d);
}
return d;
}
@JRubyMethod(name = "new_seed", meta = true, compat = RUBY1_9)
public static IRubyObject newSeed(ThreadContext context, IRubyObject recv) {
Ruby runtime = context.getRuntime();
long rand = runtime.getRandom().nextLong();
RubyBignum newSeed = RubyBignum.newBignum(runtime, rand);
runtime.setRandomSeed(newSeed);
runtime.getRandom().setSeed(rand);
return newSeed;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy