Merge: modelize: ask that attributes in refinements are either noautoninit or have...
[nit.git] / lib / standard / collection / range.nit
index 39c1aab..29fa17e 100644 (file)
@@ -4,7 +4,7 @@
 #
 # This file is free software, which comes along with NIT.  This software is
 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A 
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
 # PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
 # is kept unaltered, and a notification of the changes is added.
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
@@ -19,7 +19,7 @@ import abstract_collection
 class Range[E: Discrete]
        super Collection[E]
 
-       redef var first: E
+       redef var first
 
        # Get the last element.
        var last: E
@@ -27,10 +27,17 @@ class Range[E: Discrete]
        # Get the element after the last one.
        var after: E
 
-       redef fun has(item) do return item >= first and item <= last
+       #     assert [1..10].has(5)
+       #     assert [1..10].has(10)
+       #     assert not [1..10[.has(10)
+       redef fun has(item) do return item isa Comparable and item >= first and item <= last
 
+       #     assert [1..1].has_only(1)
+       #     assert not [1..10].has_only(1)
        redef fun has_only(item) do return first == item and item == last or is_empty
 
+       #     assert [1..10].count(1)   == 1
+       #     assert [1..10].count(0)   == 0
        redef fun count(item)
        do
                if has(item) then
@@ -42,8 +49,22 @@ class Range[E: Discrete]
 
        redef fun iterator do return new IteratorRange[E](self)
 
+       # Gets an iterator starting at the end and going backwards
+       #
+       #     var reviter = [1..4].reverse_iterator
+       #     assert reviter.to_a == [4,3,2,1]
+       #
+       #     reviter = [1..4[.reverse_iterator
+       #     assert reviter.to_a == [3,2,1]
+       fun reverse_iterator: Iterator[E] do return new ReverseIteratorRange[E](self)
+
+       #     assert [1..10].length             == 10
+       #     assert [1..10[.length             == 9
+       #     assert [1..1].length              == 1
+       #     assert [1..-10].length    == 0
        redef fun length
        do
+               if is_empty then return 0
                var nb = first.distance(after)
                if nb > 0 then
                        return nb
@@ -52,10 +73,18 @@ class Range[E: Discrete]
                end
        end
 
+       #     assert not [1..10[.is_empty
+       #     assert not [1..1].is_empty
+       #     assert [1..-10].is_empty
        redef fun is_empty do return first >= after
 
        # Create a range [`from`, `to`].
-       # The syntax `[from..to[` is equivalent.
+       # The syntax `[from..to]` is equivalent.
+       #
+       #     var a = [10..15]
+       #     var b = new Range[Int] (10,15)
+       #     assert a == b
+       #     assert a.to_a == [10, 11, 12, 13, 14, 15]
        init(from: E, to: E) is old_style_init do
                first = from
                last = to
@@ -64,24 +93,144 @@ class Range[E: Discrete]
 
        # Create a range [`from`, `to`[.
        # The syntax `[from..to[` is equivalent.
+       #
+       #     var a = [10..15[
+       #     var b = new Range[Int].without_last(10,15)
+       #     assert a == b
+       #     assert a.to_a == [10, 11, 12, 13, 14]
        init without_last(from: E, to: E)
        do
                first = from
-               last = to.predecessor(1)
-               after = to
+               if from <= to then
+                       last = to.predecessor(1)
+                       after = to
+               else
+                       last = to.successor(1)
+                       after = to
+               end
+       end
+
+       # Two ranges are equals if they have the same first and last elements.
+       #
+       #     var a = new Range[Int](10, 15)
+       #     var b = new Range[Int].without_last(10, 15)
+       #     assert a == [10..15]
+       #     assert a == [10..16[
+       #     assert not a == [10..15[
+       #     assert b == [10..15[
+       #     assert b == [10..14]
+       #     assert not b == [10..15]
+       redef fun ==(o) do
+               return o isa Range[E] and self.first == o.first and self.last == o.last
+       end
+
+       #     var a = new Range[Int](10, 15)
+       #     assert a.hash == 455
+       #     var b = new Range[Int].without_last(10, 15)
+       #     assert b.hash == 432
+       redef fun hash do
+               # 11 and 23 are magic numbers empirically determined to be not so bad.
+               return first.hash * 11 + last.hash * 23
+       end
+
+       # Gets an iterator that progress with a given step.
+       #
+       # The main usage is in `for` construction.
+       #
+       # ~~~
+       # for i in [10..25].step(10) do assert i == 10 or i == 20
+       # ~~~
+       #
+       # But `step` is usable as any kind of iterator.
+       #
+       # ~~~
+       # assert [10..27].step(5).to_a == [10,15,20,25]
+       # ~~~
+       #
+       # If `step == 1`, then it is equivalent to the default `iterator`.
+       #
+       # ~~~
+       # assert [1..5].step(1).to_a == [1..5].to_a
+       # ~~~
+       #
+       # If `step` is negative, then the iterator will iterate on ranges whose `first` > `last`.
+       #
+       # ~~~
+       # assert [25..12].step(-5).to_a == [25,20,15]
+       # ~~~
+       #
+       # On such ranges, the default `iterator` will be empty
+       #
+       # ~~~
+       # assert [5..1].step(1).to_a.is_empty
+       # assert [5..1].iterator.to_a.is_empty
+       # assert [5..1].to_a.is_empty
+       # assert [5..1].is_empty
+       # ~~~
+       #
+       # Note that on non-empty range, iterating with a negative step will be empty
+       #
+       # ~~~
+       # assert [1..5].step(-1).to_a.is_empty
+       # ~~~
+       fun step(step: Int): Iterator[E]
+       do
+               var i
+               if step >= 0 then
+                       i = iterator
+               else
+                       i = new DowntoIteratorRange[E](self)
+                       step = -step
+               end
+
+               if step == 1 then return i
+               return i.to_step(step)
        end
 end
 
+# Iterator on ranges.
 private class IteratorRange[E: Discrete]
-       # Iterator on ranges.
        super Iterator[E]
        var range: Range[E]
        redef var item is noinit
 
        redef fun is_ok do return _item < _range.after
-       
+
        redef fun next do _item = _item.successor(1)
-       
+
+       init
+       do
+               _item = _range.first
+       end
+end
+
+# Reverse iterator on ranges.
+private class ReverseIteratorRange[E: Discrete]
+       super Iterator[E]
+       var range: Range[E]
+       redef var item is noinit
+
+       redef fun is_ok do return _item >= _range.first
+
+       redef fun next do _item = _item.predecessor(1)
+
+       init
+       do
+               _item = _range.last
+       end
+end
+
+# Iterator on ranges.
+private class DowntoIteratorRange[E: Discrete]
+       super IndexedIterator[E]
+       var range: Range[E]
+       redef var item is noinit
+       redef fun index do return _item.distance(_range.first)
+
+       redef fun is_ok do return _item >= _range.last
+
+       redef fun next do _item = _item.predecessor(1)
+
        init
        do
                _item = _range.first
@@ -89,14 +238,16 @@ private class IteratorRange[E: Discrete]
 end
 
 redef class Int
-       # Returns the range from 0 to `self-1`, is used to do:
+       # Returns the range from 0 to `self-1`.
+       #
+       #     assert 3.times == [0..3[
+       #     assert 10.times == [0..10[
+       #     assert ((-1).times).is_empty
+       #
+       # This can be usefull for loops:
        #
        #    var s = new Array[String]
        #    for i in 3.times do s.add "cool"
        #    assert s.join(" ") == "cool cool cool"
-       #
-       #    s.clear
-       #    for i in 10.times do s.add(i.to_s)
-       #    assert s.to_s == "0123456789"
        fun times: Range[Int] do return [0 .. self[
 end