Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tag: 2014.01
Fetching contributors…

Cannot retrieve contributors at this time

206 lines (186 sloc) 7.462 kb
my class X::Range::InvalidArg { ... }
my class Range is Iterable is Cool does Positional {
has $.min;
has $.max;
has $.excludes_min;
has $.excludes_max;
proto method new(|) { * }
# The order of "method new" declarations matters here, to ensure
# appropriate candidate tiebreaking when mixed type arguments
# are present (e.g., Range,Whatever or Real,Range).
multi method new(Range $min, $max, :$excludes_min, :$excludes_max) {
X::Range::InvalidArg.new(:got($min)).throw;
}
multi method new($min, Range $max, :$excludes_min, :$excludes_max) {
X::Range::InvalidArg.new(:got($max)).throw;
}
multi method new(Whatever $min, Whatever $max, :$excludes_min, :$excludes_max) {
fail "*..* is not a valid range";
}
multi method new(Whatever $min, $max, :$excludes_min, :$excludes_max) {
nqp::create(self).BUILD(-$Inf, $max, $excludes_min, $excludes_max)
}
multi method new($min, Whatever $max, :$excludes_min, :$excludes_max) {
nqp::create(self).BUILD($min, $Inf, $excludes_min, $excludes_max)
}
multi method new(Real $min, $max, :$excludes_min, :$excludes_max) {
nqp::create(self).BUILD($min, $max.Real, $excludes_min, $excludes_max)
}
multi method new($min is copy, $max, :$excludes_min, :$excludes_max) {
$min = +$min if $min ~~ any(List|Match|Parcel);
nqp::create(self).BUILD($min, $max, $excludes_min, $excludes_max)
}
submethod BUILD($min, $max, $excludes_min, $excludes_max) {
$!min = $min;
$!max = $max;
$!excludes_min = $excludes_min.Bool;
$!excludes_max = $excludes_max.Bool;
self;
}
method flat() { nqp::p6list(nqp::list(self), List, Bool::True) }
method infinite() { nqp::p6bool(nqp::istype($!max, Num)) && $!max eq 'Inf' }
method iterator() { self }
method list() { self.flat }
method bounds() { ($!min, $!max) }
multi method ACCEPTS(Range:D: Mu \topic) {
(topic cmp $!min) > -(!$!excludes_min)
and (topic cmp $!max) < +(!$!excludes_max)
}
multi method ACCEPTS(Range:D: Range \topic) {
(topic.min > $!min
|| topic.min == $!min
&& !(!topic.excludes_min && $!excludes_min))
&&
(topic.max < $!max
|| topic.max == $!max
&& !(!topic.excludes_max && $!excludes_max))
}
method reify($n = 10) {
my $value = $!excludes_min ?? $!min.succ !! $!min;
# Iterating a Str range delegates to iterating a sequence.
if Str.ACCEPTS($value) {
return $value after $!max
?? ()
!! SEQUENCE($value, $!max, :exclude_end($!excludes_max)).iterator.reify($n)
}
my $count;
if nqp::istype($n, Whatever) {
$count = self.infinite ?? 10 !! $Inf;
}
else {
$count = $n.Num;
fail "request for infinite elements from range"
if $count == $Inf && self.infinite;
}
my $cmpstop = $!excludes_max ?? 0 !! 1;
my $realmax = nqp::istype($!min, Numeric) && !nqp::istype($!max, Callable) && !nqp::istype($!max, Whatever)
?? $!max.Numeric
!! $!max;
# Pre-size the buffer, to avoid reallocations.
my Mu $rpa := nqp::list();
nqp::setelems($rpa, $count == $Inf ?? 256 !! $count.Int);
nqp::setelems($rpa, 0);
if nqp::istype($value, Int) && nqp::istype($!max, Int) && !nqp::isbig_I(nqp::decont $!max)
|| nqp::istype($value, Num) {
# optimized for int/num ranges
$value = $value.Num;
my $max = $!max.Num;
my $box_int = nqp::p6bool(nqp::istype($!min, Int));
my num $nvalue = $value;
my num $ncount = $count;
my num $nmax = $max;
my int $icmpstop = $cmpstop;
my int $ibox_int = $box_int;
nqp::while(
(nqp::isgt_n($ncount, 0e0) && nqp::islt_i(nqp::cmp_n($nvalue, $nmax), $icmpstop)),
nqp::stmts(
nqp::push($rpa, $ibox_int
?? nqp::p6box_i($nvalue)
!! nqp::p6box_n($nvalue)),
($nvalue = nqp::add_n($nvalue, 1e0)),
($ncount = nqp::sub_n($ncount, 1e0))
));
$value = nqp::p6box_i($nvalue);
}
else {
(nqp::push($rpa, $value++); $count--)
while $count > 0 && ($value cmp $realmax) < $cmpstop;
}
if ($value cmp $!max) < $cmpstop {
nqp::push($rpa,
($value.succ cmp $!max < $cmpstop)
?? nqp::create(self).BUILD($value, $!max, 0, $!excludes_max)
!! $value);
}
nqp::p6parcel($rpa, nqp::null());
}
method at_pos($pos) { self.flat.at_pos($pos) }
multi method perl(Range:D:) {
$.min.perl
~ ('^' if $.excludes_min)
~ '..'
~ ('^' if $.excludes_max)
~ $.max.perl
}
proto method roll(|) { * }
multi method roll(Range:D: Whatever) {
gather loop { take self.roll }
}
multi method roll(Range:D:) {
return self.list.roll unless nqp::istype($!min, Int) && nqp::istype($!max, Int);
my Int:D $least = $!excludes_min ?? $!min + 1 !! $!min;
my Int:D $elems = 1 + ($!excludes_max ?? $!max - 1 !! $!max) - $least;
$elems ?? ($least + nqp::rand_I(nqp::decont($elems), Int)) !! Any;
}
multi method roll(Cool $num as Int) {
return self.list.roll($num) unless nqp::istype($!min, Int) && nqp::istype($!max, Int);
return self.roll if $num == 1;
my int $n = nqp::unbox_i($num);
gather loop (my int $i = 0; $i < $n; $i = $i + 1) {
take self.roll;
}
}
proto method pick(|) { * }
multi method pick() { self.roll };
multi method pick(Whatever) { self.list.pick(*) };
multi method pick(Cool $n as Int) {
return self.list.pick($n) unless nqp::istype($!min, Int) && nqp::istype($!max, Int);
return self.roll if $n == 1;
my Int:D $least = $!excludes_min ?? $!min + 1 !! $!min;
my Int:D $elems = 1 + ($!excludes_max ?? $!max - 1 !! $!max) - $least;
return self.list.pick($n) unless $elems > 3 * $n;
my %seen;
my int $i_n = nqp::unbox_i($n);
gather while $i_n > 0 {
my Int $x = $least + nqp::rand_I(nqp::decont($elems), Int);
unless %seen{$x} {
%seen{$x} = 1;
$i_n = $i_n - 1;
take $x;
}
}
}
multi method Numeric (Range:D:) {
nextsame unless $.max ~~ Numeric and $.min ~~ Numeric;
my $diff := $.max - $.min - $.excludes_min;
# empty range
return 0 if $diff < 0;
my $floor := $diff.floor;
return $floor + 1 - ($floor == $diff ?? $.excludes_max !! 0);
}
}
sub infix:<..>($min, $max) {
Range.new($min, $max)
}
sub infix:<^..>($min, $max) {
Range.new($min, $max, :excludes_min)
}
sub infix:<..^>($min, $max) {
Range.new($min, $max, :excludes_max)
}
sub infix:<^..^>($min, $max) is pure {
Range.new($min, $max, :excludes_min, :excludes_max)
}
sub prefix:<^>($max) is pure {
Range.new(0, $max.Numeric, :excludes_max)
}
Jump to Line
Something went wrong with that request. Please try again.