Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2e3294fdbfba
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: ae5a6448858c
Choose a head ref
  • 2 commits
  • 8 files changed
  • 2 contributors

Commits on Mar 12, 2015

  1. 2
    Copy the full SHA
    d21619d View commit details

Commits on Mar 13, 2015

  1. Merge pull request #2682 from lucasallan/random-class

    [Truffle] - Initial implementation of Ruby Random class.
    chrisseaton committed Mar 13, 2015
    Copy the full SHA
    ae5a644 View commit details
5 changes: 0 additions & 5 deletions spec/truffle/tags/core/random/equal_value_tags.txt

This file was deleted.

8 changes: 0 additions & 8 deletions spec/truffle/tags/core/random/new_tags.txt

This file was deleted.

16 changes: 1 addition & 15 deletions spec/truffle/tags/core/random/rand_tags.txt
Original file line number Diff line number Diff line change
@@ -7,29 +7,15 @@ fails:Random.rand returns an Integer >= 0 if an Integer argument is passed
fails:Random.rand returns an Integer < the max argument if an Integer argument is passed
fails:Random.rand returns the same sequence for a given seed if an Integer argument is passed
fails:Random.rand coerces arguments to Integers with #to_int
fails:Random#rand with Fixnum returns an Integer
fails:Random#rand with Fixnum returns a Fixnum greater than or equal to 0
fails:Random#rand with Fixnum returns a Fixnum less than the argument
fails:Random#rand with Fixnum returns the same sequence for a given seed
fails:Random#rand with Fixnum eventually returns all possible values
fails:Random#rand with Fixnum raises an ArgumentError when the argument is 0
fails:Random#rand with Fixnum raises an ArgumentError when the argument is negative
fails:Random#rand with Bignum typically returns a Bignum
fails:Random#rand with Bignum returns a Bignum greater than or equal to 0
fails:Random#rand with Bignum returns a Bignum less than the argument
fails:Random#rand with Bignum returns the same sequence for a given seed
fails:Random#rand with Bignum raises an ArgumentError when the argument is negative
fails:Random#rand with Float returns a Float
fails:Random#rand with Float returns a Float greater than or equal to 0.0
fails:Random#rand with Float returns a Float less than the argument
fails:Random#rand with Float returns the same sequence for a given seed
fails:Random#rand with Float raises an ArgumentError when the argument is negative
fails:Random#rand with Range returns an element from the Range
fails:Random#rand with Fixnum returns the same sequence for a given seed
fails:Random#rand with Range returns an object that is a member of the Range
fails:Random#rand with Range works with inclusive ranges
fails:Random#rand with Range works with exclusive ranges
fails:Random#rand with Range returns the same sequence for a given seed
fails:Random#rand with Range eventually returns all possible values
fails:Random#rand with Range considers Integers as Floats if one end point is a float
fails:Random#rand with Range raises an ArgumentError when the startpoint lacks #+ and #- methods
fails:Random#rand with Range raises an ArgumentError when the endpoint lacks #+ and #- methods
5 changes: 0 additions & 5 deletions spec/truffle/tags/core/random/seed_tags.txt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package org.jruby.truffle.nodes.rubinius;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.Ruby;
import org.jruby.RubyFixnum;
import org.jruby.RubyNumeric;
import org.jruby.RubyRandom;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBasicObject;
import org.jruby.truffle.runtime.core.RubyBignum;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Random;

/**
* Rubinius primitives associated with the Ruby {@code Random} class.
*/

public abstract class RandomPrimitiveNodes {

@RubiniusPrimitive(name = "randomizer_seed")
public static abstract class RandomizerSeedPrimitiveNode extends RubiniusPrimitiveNode {

public RandomizerSeedPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public RandomizerSeedPrimitiveNode(RandomizerSeedPrimitiveNode prev) {
super(prev);
}

@Specialization
public long randomizerSeed(RubyBasicObject random) {
notDesignedForCompilation();

return System.currentTimeMillis();
}

}

@RubiniusPrimitive(name = "randomizer_rand_float")
public static abstract class RandomizerRandFloatPrimitiveNode extends RubiniusPrimitiveNode {

public RandomizerRandFloatPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public RandomizerRandFloatPrimitiveNode(RandomizerRandFloatPrimitiveNode prev) {
super(prev);
}

@Specialization
public double randomizerRandFloat(RubyBasicObject random) {
notDesignedForCompilation();

return Math.random();
}

}

@RubiniusPrimitive(name = "randomizer_rand_int")
public static abstract class RandomizerRandIntPrimitiveNode extends RubiniusPrimitiveNode {

public RandomizerRandIntPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public RandomizerRandIntPrimitiveNode(RandomizerRandIntPrimitiveNode prev) {
super(prev);
}

@Specialization
public long randomizerRandInt(RubyBasicObject random, Integer limit) {
notDesignedForCompilation();

return RandomPrimitiveHelper.randomInt(getContext().getRuntime().getCurrentContext().getRuntime(), limit);
}

@Specialization
public long randomizerRandInt(RubyBasicObject random, Long limit) {
notDesignedForCompilation();

return RandomPrimitiveHelper.randomInt(getContext().getRuntime().getCurrentContext().getRuntime(), limit);
}
}

@RubiniusPrimitive(name = "randomizer_gen_seed")
public static abstract class RandomizerGenSeedPrimitiveNode extends RubiniusPrimitiveNode {

public RandomizerGenSeedPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public RandomizerGenSeedPrimitiveNode(RandomizerGenSeedPrimitiveNode prev) {
super(prev);
}

@Specialization
public RubyBignum randomizerGenSeed(RubyBasicObject random) {
notDesignedForCompilation();

BigInteger integer = RandomPrimitiveHelper.randomSeed(getContext().getRuntime().getCurrentContext().getRuntime());
return new RubyBignum(getContext().getCoreLibrary().getBignumClass(), integer);
}
}

static class RandomPrimitiveHelper {

@CompilerDirectives.TruffleBoundary
public static BigInteger randomSeed(Ruby context) {
return RubyRandom.randomSeed(context).getBigIntegerValue();
}

@CompilerDirectives.TruffleBoundary
public static long randomInt(Ruby context, long limit) {
RubyFixnum fixnum = context.newFixnum(limit);
return generateRandomInt(context, fixnum);
}

@CompilerDirectives.TruffleBoundary
public static long randomInt(Ruby context, int limit) {
RubyFixnum fixnum = context.newFixnum(limit);
return generateRandomInt(context, fixnum);
}

public static long generateRandomInt(Ruby context, RubyFixnum limit) {
IRubyObject params[] = new IRubyObject[1];
params[0] = limit;
RubyNumeric num = (RubyNumeric) RubyRandom.randCommon19(context.getCurrentContext(), null, params);
return num.getLongValue();
}
}
}
Original file line number Diff line number Diff line change
@@ -53,6 +53,7 @@ public static RubiniusPrimitiveManager create() {
nodeFactories.addAll(EncodingPrimitiveNodesFactory.getFactories());
nodeFactories.addAll(RegexpPrimitiveNodesFactory.getFactories());
nodeFactories.addAll(ModulePrimitiveNodesFactory.getFactories());
nodeFactories.addAll(RandomPrimitiveNodesFactory.getFactories());

final Map<String, RubiniusPrimitiveConstructor> primitives = new HashMap<>();

1 change: 1 addition & 0 deletions truffle/src/main/ruby/core.rb
Original file line number Diff line number Diff line change
@@ -72,6 +72,7 @@
require_relative 'core/rubinius/common/throw_catch'
require_relative 'core/rubinius/common/time'
require_relative 'core/rubinius/common/true'
require_relative 'core/rubinius/common/random'

require_relative 'core/rubinius/common/rational'
require_relative 'core/rubinius/common/rationalizer'
203 changes: 203 additions & 0 deletions truffle/src/main/ruby/core/rubinius/common/random.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
# Copyright (c) 2007-2014, Evan Phoenix and contributors
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Rubinius nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Only part of Rubinius' random.rb
# Extracted from https://github.com/rubinius/rubinius/blob/v2.4.1/kernel/common/random.rb
#
class Rubinius::Randomizer

def initialize
self.seed = generate_seed
end

attr_reader :seed
def seed=(new_seed)
set_seed new_seed
@seed = new_seed
end

def set_seed(new_seed)
Rubinius.primitive :randomizer_seed
raise PrimitiveFailure, "Randomizer#seed primitive failed"
end

def swap_seed(new_seed)
old_seed = self.seed
self.seed = new_seed
old_seed
end

def random(limit)
if undefined.equal?(limit)
random_float
else
if limit.kind_of?(Range)
if time_value?(limit.min)
random_time_range(limit)
else
random_range(limit)
end
elsif limit.kind_of?(Float)
raise ArgumentError, "invalid argument - #{limit}" if limit <= 0
random_float * limit
else
limit_int = Rubinius::Type.coerce_to limit, Integer, :to_int
raise ArgumentError, "invalid argument - #{limit}" if limit_int <= 0

if limit.is_a?(Integer)
random_integer(limit - 1)
elsif limit.respond_to?(:to_f)
random_float * limit
else
random_integer(limit_int - 1)
end
end
end
end

# Generate a random Float, in the range 0...1.0
def random_float
Rubinius.primitive :randomizer_rand_float
raise PrimitiveFailure, "Randomizer#rand_float primitive failed"
end

# Generate a random Integer, in the range 0...limit
def random_integer(limit)
Rubinius.primitive :randomizer_rand_int
raise PrimitiveFailure, "Randomizer#rand_int primitive failed"
end

def random_range(limit)
min, max = limit.max.coerce(limit.min)
diff = max - min
diff += 1 if max.kind_of?(Integer)
random(diff) + min
end

def generate_seed
Rubinius.primitive :randomizer_gen_seed
raise PrimitiveFailure, "Randomizer#gen_seed primitive failed"
end

##
# Returns a random value from a range made out of Time, Date or DateTime
# instances.
#
# @param [Range] range
# @return [Time|Date|DateTime]
#
def random_time_range(range)
min = time_to_float(range.min)
max = time_to_float(range.max)
time = Time.at(random(min..max))

if Object.const_defined?(:DateTime) && range.min.is_a?(DateTime)
time = time.to_datetime
elsif Object.const_defined?(:DateTime) && range.min.is_a?(Date)
time = time.to_date
end

return time
end

##
# Casts a Time/Date/DateTime instance to a Float.
#
# @param [Time|Date|DateTime] input
# @return [Float]
#
def time_to_float(input)
return input.respond_to?(:to_time) ? input.to_time.to_f : input.to_f
end

##
# Checks if a given value is a Time, Date or DateTime object.
#
# @param [Mixed] input
# @return [TrueClass|FalseClass]
#
def time_value?(input)
return input.is_a?(Time) || (Object.const_defined?(:Date) && input.is_a?(Date))
end
end

class Random
def self.new_seed
Thread.current.randomizer.generate_seed
end

def self.srand(seed=undefined)
if undefined.equal? seed
seed = Thread.current.randomizer.generate_seed
end

seed = Rubinius::Type.coerce_to seed, Integer, :to_int
Thread.current.randomizer.swap_seed seed
end

def self.rand(limit=undefined)
Thread.current.randomizer.random(limit)
end

def initialize(seed=undefined)
@randomizer = Rubinius::Randomizer.new
if !undefined.equal?(seed)
@randomizer.swap_seed seed.to_int
end
end

def rand(limit=undefined)
@randomizer.random(limit)
end

def seed
@randomizer.seed
end

def state
@randomizer.seed
end
private :state

def ==(other)
return false unless other.kind_of?(Random)
seed == other.seed
end

# Returns a random binary string.
# The argument size specified the length of the result string.
def bytes(length)
length = Rubinius::Type.coerce_to length, Integer, :to_int
s = ''
i = 0
while i < length
s << @randomizer.random_integer(255).chr
i += 1
end

s
end
end