=begin pod :kind("Type") :subkind("class") :category("composite")

=TITLE class Range

=SUBTITLE Interval of ordered values

    class Range is Cool does Iterable does Positional {}

Ranges serve two main purposes: to generate lists of consecutive
numbers or strings, and to act as a matcher to check if a number
or string is within a certain range.

Ranges are constructed using one of the four possible range operators,
which consist of two dots, and optionally a caret which indicates that
the endpoint marked with it is excluded from the range.

    1 .. 5;  # 1 <= $x <= 5
    1^.. 5;  # 1 <  $x <= 5
    1 ..^5;  # 1 <= $x <  5
    1^..^5;  # 1 <  $x <  5

The caret is also a prefix operator for constructing numeric ranges
starting from zero:

    my $x = 10;
    say ^$x;     # same as 0 ..^ $x.Numeric

Iterating a range (or calling the C<list> method) uses the same semantics as
the C<++> prefix and postfix operators, i.e., it calls the C<succ> method on
the start point, and then the generated elements.

Ranges always go from small to larger elements; if the start point is bigger
than the end point, the range is considered empty.

    for 1..5 { .say };       # OUTPUT: «1␤2␤3␤4␤5␤»
    ('a' ^..^ 'f').list;     # OUTPUT: «'b', 'c', 'd', 'e'»
    5 ~~ ^5;                 # OUTPUT: «False»
    4.5 ~~ 0..^5;            # OUTPUT: «True»
    (1.1..5).list;           # OUTPUT: «(1.1, 2.1, 3.1, 4.1)»

Use the L<...|/language/operators#infix_...> sequence operator to produce lists
of elements that go from larger to smaller values, or to use offsets other than
increment-by-1 and other complex cases.

Use C<∞> or C<*> (Whatever) to indicate an end point to be open-ended.

=for code
for 1..* { .say };       # start from 1, continue until stopped
for 1..∞ { .say };       # the same

Beware that a L<WhateverCode|/type/WhateverCode> end point, instead of a plain
Whatever, will go through the range operator and create another WhateverCode
which returns a Range:

=for code :ok-test<WHAT>
# A Whatever produces the 1..Inf range
say (1..*).^name;        # OUTPUT: «Range␤»
say (1..*);              # OUTPUT: «1..Inf␤»
# Upper end point is now a WhateverCode
say (1..*+20).^name;     # OUTPUT: «{ ... }␤»
say (1..*+20).WHAT;      # OUTPUT: «(WhateverCode)␤»
say (1..*+20).(22);      # OUTPUT: «1..42␤»

Ranges implement L<Positional|/type/Positional> interface, so its elements can
be accessed using an index. In a case when the index given is bigger than the
Range object's size, L<Nil|/type/Nil> object will be returned. The access works
for lazy Range objects as well.

=for code
say (1..5)[1];  # OUTPUT: «2␤»
say (1..5)[10]; # OUTPUT: «Nil␤»
say (1..*)[10]; # OUTPUT: «11␤»

=head2 Ranges in subscripts

A Range can be used in a subscript to get a range of values. Please note that
assigning a Range to a scalar container turns the Range into an item. Use
binding, @-sigiled containers or a slip to get what you mean.

    my @numbers =  <4 8 15 16 23 42>;
    my $range := 0..2;
    .say for @numbers[$range]; # OUTPUT: «4␤8␤15␤»
    my @range = 0..2;
    .say for @numbers[@range]; # OUTPUT: «4␤8␤15␤»

=head2 Shifting and scaling intervals

It is possible to shift or scale the interval of a range:

    say (1..10) + 1;       # OUTPUT: «2..11␤»
    say (1..10) - 1;       # OUTPUT: «0..9␤»
    say (1..10) * 2;       # OUTPUT: «2..20␤»
    say (1..10) / 2;       # OUTPUT: «0.5..5.0␤»

=head2 Matching against Ranges

You can use L<smartmatch|/routine/~~> to match against Ranges.

    say 3 ~~ 1..12;          # OUTPUT: «True␤»
    say 2..3 ~~ 1..12;       # OUTPUT: «True␤»

X<|in-range>
I<In Rakudo only>, you can use the C<in-range> method for matching
against a range, which in fact is equivalent to smartmatch except it will throw
an exception when out of range, instead of returning C<False>:

    say ('א'..'ת').in-range('ע');  # OUTPUT: «True␤»

However, if it is not included in the range:
=for code
say ('א'..'ת').in-range('p', "Letter 'p'");
# OUTPUT: «(exit code 1) Letter 'p' out of range. Is: "p", should be in "א".."ת"␤

The second parameter to C<in-range> is the optional message that will be printed
with the exception. It will print C<Value> by default.

=head1 Methods


=head2 method ACCEPTS

Defined as

     multi method ACCEPTS(Range:D: Mu \topic)
     multi method ACCEPTS(Range:D: Range \topic)
     multi method ACCEPTS(Range:D: Cool:D \got)
     multi method ACCEPTS(Range:D: Complex:D \got)


Indicates if the C<Range> contains (overlaps with) another C<Range>.
As an example:

    my $p = Range.new( 3, 5  );
    my $r = Range.new( 1, 10 );

    say $p.ACCEPTS( $r );    # OUTPUT: «False␤»
    say $r.ACCEPTS( $p );    # OUTPUT: «True␤»
    say $r ~~ $p;            # OUTPUT: «False␤»  (same as $p.ACCEPTS( $r )
    say $p ~~ $r;            # OUTPUT: «True␤»   (same as $r.ACCEPTS( $p )

Of course, an infinite C<Range> always contains another C<Range>, therefore:

    say 1..10 ~~ -∞..∞;    # OUTPUT: «True␤»
    say 1..10 ~~ -∞^..^∞;  # OUTPUT: «True␤»

Similarly, a C<Range> with open boundaries often includes other ranges:

    say 1..2 ~~ *..10;  # OUTPUT: «True␤»
    say 2..5 ~~ 1..*;   # OUTPUT: «True␤»


It is also possible to use non-numeric ranges, for instance string based
ones:

   say 'a'..'j' ~~ 'b'..'c';  # OUTPUT: «False␤»
   say 'b'..'c' ~~ 'a'..'j';  # OUTPUT: «True␤»
   say 'raku' ~~ -∞^..^∞;     # OUTPUT: «True␤»
   say 'raku' ~~ -∞..∞;       # OUTPUT: «True␤»
   say 'raku' ~~ 1..*;        # OUTPUT: «True␤»


When smartmatching a C<Range> of integers with a L<Cool|/type/Cool> (string)
the C<ACCEPTS> methods exploits the L<before|/routine/before>
and L<after|/routine/after> operators in order to check that
the C<Cool> value is overlapping the range:

   say 1.10 ~~ '5';   # OUTPUT: «False␤»
   say '5' before 1;  # OUTPUT: «False␤»
   say '5' after 10;  # OUTPUT: «True␤»
   say '5' ~~ *..10;  # OUTPUT: «False␤»

In the above example, since the C<'5'> string is I<after> the C<10> integer
value, the C<Range> does not overlap with the specified value.

When matching with a C<Mu> instance (i.e., a generic instance),
the L<cmp|/routine/cmp> operator is used.

=head2 method min

    method min(Range:D:)

Returns the start point of the range.

    say (1..5).min;                                   # OUTPUT: «1␤»
    say (1^..^5).min;                                 # OUTPUT: «1␤»

=head2 method excludes-min

    method excludes-min(Range:D: --> Bool:D)

Returns C<True> if the start point is excluded from the range, and C<False>
otherwise.

    say (1..5).excludes-min;                          # OUTPUT: «False␤»
    say (1^..^5).excludes-min;                        # OUTPUT: «True␤»

=head2 method max

    method max(Range:D:)

Returns the end point of the range.

    say (1..5).max;                                   # OUTPUT: «5␤»
    say (1^..^5).max;                                 # OUTPUT: «5␤»

=head2 method excludes-max

    method excludes-max(Range:D: --> Bool:D)

Returns C<True> if the end point is excluded from the range, and C<False>
otherwise.

    say (1..5).excludes-max;                          # OUTPUT: «False␤»
    say (1^..^5).excludes-max;                        # OUTPUT: «True␤»

=head2 method bounds

    method bounds()

Returns a list consisting of the start and end point.

    say (1..5).bounds;                                # OUTPUT: «(1 5)␤»
    say (1^..^5).bounds;                              # OUTPUT: «(1 5)␤»

=head2 method infinite

    method infinite(Range:D: --> Bool:D)

Returns C<True> if either end point was declared with C<∞> or C<*>.

    say (1..5).infinite;                              # OUTPUT: «False␤»
    say (1..*).infinite;                              # OUTPUT: «True␤»

=head2 method is-int

    method is-int(Range:D: --> Bool:D)

Returns C<True> if both end points are C<Int> values.

    say ('a'..'d').is-int;                            # OUTPUT: «False␤»
    say (1..^5).is-int;                               # OUTPUT: «True␤»
    say (1.1..5.5).is-int;                            # OUTPUT: «False␤»

=head2 method int-bounds

    proto method int-bounds(|)
    multi method int-bounds()
    multi method int-bounds($from is rw, $to is rw --> Bool:D)

If the C<Range> is an integer range (as indicated by L<is-int|/routine/is-int>), then this
method returns a list with the first and last value it will iterate over
(taking into account L<excludes-min|/routine/excludes-min> and L<excludes-max|/routine/excludes-max>).  Returns a
Failure if it is not an integer range.

    say (2..5).int-bounds;                            # OUTPUT: «(2 5)␤»
    say (2..^5).int-bounds;                           # OUTPUT: «(2 4)␤»

If called with (writable) arguments, these will take the values of the
higher and lower bound and returns whether integer bounds could be determined
from the C<Range>:

    if (3..5).int-bounds( my $min, my $max) {
        say "$min, $max" ; # OUTPUT: «3, 5␤»
    }
    else {
        say "Could not determine integer bounds";
    }

=head2 method minmax

Defined as:

    multi method minmax(Range:D: --> List:D)

If the C<Range> is an integer range (as indicated by L<is-int|/routine/is-int>), then this
method returns a list with the first and last value it will iterate over (taking
into account L<excludes-min|/routine/excludes-min> and L<excludes-max|/routine/excludes-max>). If the range is not an
integer range, the method will return a two element list containing the start
and end point of the range unless either of L<excludes-min|/routine/excludes-min> or
L<excludes-max|/routine/excludes-max> are C<True> in which case a L<Failure|/type/Failure> is returned.

    my $r1 = (1..5); my $r2 = (1^..5);
    say $r1.is-int, ', ', $r2.is-int;                 # OUTPUT: «True, True␤»
    say $r1.excludes-min, ', ', $r2.excludes-min;     # OUTPUT: «False, True␤»
    say $r1.minmax, ', ', $r2.minmax;                 # OUTPUT: «(1 5), (2 5)␤»

    my $r3 = (1.1..5.2); my $r4 = (1.1..^5.2);
    say $r3.is-int, ', ', $r4.is-int;                 # OUTPUT: «False, False␤»
    say $r3.excludes-max, ', ', $r4.excludes-max;     # OUTPUT: «False, True␤»
    say $r3.minmax;                                   # OUTPUT: «(1.1 5.2)␤»
    say $r4.minmax;
    CATCH { default { put .^name, ': ', .Str } };
    # OUTPUT: «X::AdHoc: Cannot return minmax on Range with excluded ends␤»

=head2 method elems

    method elems(Range:D: --> Numeric:D)

Returns the number of elements in the range, e.g. when being iterated over,
or when used as a C<List>.  Returns 0 if the start point is larger than the
end point, including when the start point was specified as C<∞>. Fails when
the Range is lazy, including when the end point was specified as C<∞> or
either end point was specified as C<*>.

    say (1..5).elems;                                 # OUTPUT: «5␤»
    say (1^..^5).elems;                               # OUTPUT: «3␤»

=head2 method list

    method list(Range:D: --> List:D)

Generates the list of elements that the range represents.

    say (1..5).list;                                  # OUTPUT: «(1 2 3 4 5)␤»
    say (1^..^5).list;                                # OUTPUT: «(2 3 4)␤»

=head2 method flat

    method flat(Range:D: --> List:D)

Generates the list of elements that the range represents.

=head2 method pick

    multi method pick(Range:D:         --> Any:D)
    multi method pick(Range:D: $number --> Seq:D)

Performs the same function as C<Range.list.pick>, but attempts to optimize
by not actually generating the list if it is not necessary.

=head2 method roll

    multi method roll(Range:D:         --> Any:D)
    multi method roll(Range:D: $number --> Seq:D)

Performs the same function as C<Range.list.roll>, but attempts to optimize
by not actually generating the list if it is not necessary.

=head2 method sum

    multi method sum(--> Numeric:D)

Returns the sum of all elements in the Range. Throws L<X::Str::Numeric|/type/X::Str::Numeric> if an
element can not be coerced into Numeric.

    (1..10).sum                                       # 55

=head2 method reverse

    method reverse(Range:D: --> Seq:D)

Returns a L<Seq|/type/Seq> where all elements that the C<Range> represents have
been reversed. Note that reversing an infinite C<Range> won't produce any
meaningful results.

=for code
say (1^..5).reverse;                            # OUTPUT: «(5 4 3 2)␤»
say ('a'..'d').reverse;                         # OUTPUT: «(d c b a)␤»
say (1..∞).reverse;                             # OUTPUT: «(Inf Inf Inf ...)␤»

=head2 method Capture

Defined as:

    method Capture(Range:D: --> Capture:D)

Returns a L<Capture|/type/Capture> with values of L«C<.min>|/type/Range#method_min»
L«C<.max>|/type/Range#method_max»,
L«C<.excludes-min>|/type/Range#method_excludes-min»,
L«C<.excludes-max>|/type/Range#method_excludes-max»,
L«C<.infinite>|/type/Range#method_infinite», and
L«C<.is-int>|/type/Range#method_is-int» as named arguments.

=head2 method rand

Defined as:

    method rand(Range:D --> Num:D)

Returns a pseudo-random value belonging to the range.

    say (1^..5).rand;                              # OUTPUT: «1.02405550417031␤»
    say (0.1..0.3).rand;                           # OUTPUT: «0.2130353370062␤»

=head2 method EXISTS-POS

Defined as

    multi method EXISTS-POS(Range:D: int \pos)
    multi method EXISTS-POS(Range:D: Int \pos)

Returns C<True> if C<pos> is greater or equal than zero and lower than
C<self.elems>. Returns C<False> otherwise.

    say (6..10).EXISTS-POS(2); # OUTPUT: «True␤»
    say (6..10).EXISTS-POS(7); # OUTPUT: «False␤»

=head2 method AT-POS

Defined as

    multi method AT-POS(Range:D: int \pos)
    multi method AT-POS(Range:D: int:D \pos)

Checks if the L<Int|/type/Int> position exists and in that case returns the
element in that position.

    say (1..4).AT-POS(2) # OUTPUT: «3␤»

=head2 method raku

Defined as

    multi method raku(Range:D:)

Returns an implementation-specific string that produces an
L<equivalent|/routine/eqv> object when given to L<EVAL|/routine/EVAL>.

=for code :ok-test<raku>
say (1..2).raku # OUTPUT: «1..2␤»

=head2 method fmt

Defined as

    method fmt(|c)

Returns a string where C<min> and C<max> in the L<Range|/type/Range> have been
formatted according to C<|c>.

For more information about formats strings, see L<sprintf|/routine/sprintf>.

    say (1..2).fmt("Element: %d", ",") # OUTPUT: «Element: 1,Element: 2␤»

=head2 method WHICH

Defined as

    multi method WHICH (Range:D:)

This returns a string that identifies the object. The string is composed by the
type of the instance (C<Range>) and the C<min> and C<max> attributes:

    say (1..2).WHICH # OUTPUT: «Range|1..2␤»

=head2 sub infix:<+>

    multi sub infix:<+>(Range:D \r, Real:D \v)
    multi sub infix:<+>(Real:D \v, Range:D \r)

Takes an L<C<Real>|/type/Real> and adds that number to both
boundaries of the L<Range|/type/Range> object. Be careful with
the use of parenthesis.

    say (1..2) + 2; # OUTPUT: «3..4␤»
    say 1..2 + 2;   # OUTPUT: «1..4␤»

=head2 sub infix:<->

    multi sub infix:<->(Range:D \r, Real:D \v)

Takes an L<C<Real>|/type/Real> and subtract that number to both
boundaries of the L<Range|/type/Range> object. Be careful with
the use of parenthesis.

    say (1..2) - 1; # OUTPUT: «0..1␤»
    say 1..2 - 1;   # OUTPUT: «1..1␤»

=head2 sub infix:<*>

    multi sub infix:<*>(Range:D \r, Real:D \v)
    multi sub infix:<*>(Real:D \v, Range:D \r)

Takes an L<C<Real>|/type/Real> and multiply both boundaries
of the L<Range|/type/Range> object by that number.

    say (1..2) * 2; # OUTPUT: «2..4␤»

=head2 sub infix:</>

    multi sub infix:</>(Range:D \r, Real:D \v)

Takes an L<Real|/type/Real> and divide both boundaries
of the L<Range|/type/Range> object by that number.

    say (2..4) / 2; # OUTPUT: «1..2␤»

=head2 sub infix:<cmp>

    multi sub infix:<cmp>(Range:D \a, Range:D \b --> Order:D)
    multi sub infix:<cmp>(Num(Real) \a, Range:D \b --> Order:D)
    multi sub infix:<cmp>(Range:D \a, Num(Real) \b --> Order:D)
    multi sub infix:<cmp>(Positional \a, Range:D \b --> Order:D)
    multi sub infix:<cmp>(Range:D \a, Positional \b --> Order:D)

Compares two L<Range|/type/Range> objects. If you use a L<C<Real>|/type/Real>,
it will be compared to the L<Range|/type/Range> C<b..b>. If you use a
L<Positional|/type/Positional>.

    say (1..2) cmp (1..2); # OUTPUT: «Same␤»
    say (1..2) cmp (1..3); # OUTPUT: «Less␤»
    say (1..4) cmp (1..3); # OUTPUT: «More␤»
    say (1..2) cmp 3;      # OUTPUT: «Less␤»
    say (1..2) cmp [1,2];  # OUTPUT: «Same␤»

=end pod

# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6
