

//
// Range
//

record Range[T] (
    begin:T,
    end:T,
);

[T]
overload LightweightSequence?(static Range[T]) = true;

[T]
inline overload size(a:Range[T]) = SizeT(a.end - a.begin);

[T,I | Integer?(I)]
inline overload index(a:Range[T], i:I) = T(a.begin + i);

[T]
inline overload iterator(a:Range[T]) = a;

[T]
inline overload reverseIterator(a:Range[T]) = ReverseRange(a.end, a.begin);

[T]
inline overload hasNext?(a:Range[T]) = a.begin < a.end;

[T]
inline overload next(a:Range[T]) {
    var temp = a.begin;
    inc(a.begin);
    return move(temp);
}

[T]
inline overload begin(a:Range[T]) = ValueCoordinate(a.begin);

[T]
inline overload end(a:Range[T]) = ValueCoordinate(a.end);



//
// ReverseRange
//

record ReverseRange[T] (
    end : T,
    begin : T,
);

[T]
overload LightweightSequence?(static ReverseRange[T]) = true;

[T]
inline overload size(a:ReverseRange[T]) = SizeT(a.end - a.begin);

[T,I | Integer?(I)]
inline overload index(a:ReverseRange[T], i:I) = T(a.end - i - 1);

[T]
inline overload iterator(a:ReverseRange[T]) = a;

[T]
inline overload reverseIterator(a:ReverseRange[T]) = Range(a.begin, a.end);

[T]
inline overload hasNext?(a:ReverseRange[T]) = a.end > a.begin;

[T]
inline overload next(a:ReverseRange[T]) {
    dec(a.end);
    return a.end;
}

[T]
inline overload begin(a:ReverseRange[T]) = ValueReverseCoordinate(a.end);

[T]
inline overload end(a:ReverseRange[T]) = ValueReverseCoordinate(a.begin);



//
// range, reverseRange
//

define range;

inline overload range(begin, end) = Range(begin, end);

[A,B | Integer?(A) and Integer?(B)]
inline overload range(begin:A, end:B) =
    Range(toBiggerNumericType(A, B, begin),
          toBiggerNumericType(A, B, end));

[I | Integer?(I)]
inline overload range(begin:I, end:I) = Range(begin, end);

[I | Integer?(I)]
inline overload range(end:I) = Range(I(0), end);


define reverseRange;

inline overload reverseRange(end, begin) = ReverseRange(end, begin);

[A,B | Integer?(A) and Integer?(B)]
inline overload reverseRange(end:A, begin:B) =
    ReverseRange(toBiggerNumericType(A, B, end),
                 toBiggerNumericType(A, B, begin));

[I | Integer?(I)]
inline overload reverseRange(end:I, begin:I) =
    ReverseRange(end, begin);

[I | Integer?(I)]
inline overload reverseRange(end:I) =
    ReverseRange(end, I(0));



//
// CoordinateRange
//

record CoordinateRange[T] (
    begin : T,
    end : T,
);

[T | ContiguousCoordinate?(T)]
overload ContiguousSequence?(static CoordinateRange[T]) = true;

[T]
overload LightweightSequence?(static CoordinateRange[T]) = true;

[T]
inline overload size(a:CoordinateRange[T]) = SizeT(a.end - a.begin);

[T,I | Integer?(I)]
inline overload index(a:CoordinateRange[T], i:I) {
    assert[#boundsChecks](i >= 0 and i < size(a),
        CoordinateRange[T], " index out of bounds");
    return forward ..(a.begin + i)^;
}

[T]
inline overload iterator(a:CoordinateRange[T]) = a;

[T]
inline overload reverseIterator(a:CoordinateRange[T]) =
    ReverseCoordinateRange(a.end, a.begin);

[T]
inline overload hasNext?(a:CoordinateRange[T]) = a.begin < a.end;

[T]
inline overload next(a:CoordinateRange[T]) {
    assert[#boundsChecks](hasNext?(a), "next of empty ", CoordinateRange[T]);
    var x = captureValues(..a.begin^);
    inc(a.begin);
    return forward ..forwardValues(x);
}

[T]
inline overload begin(a:CoordinateRange[T]) = a.begin;

[T]
inline overload end(a:CoordinateRange[T]) = a.end;



//
// ReverseCoordinateRange
//

record ReverseCoordinateRange[T] (
    end : T,
    begin : T,
);

[T]
overload LightweightSequence?(static ReverseCoordinateRange[T]) = true;

[T]
inline overload size(a:ReverseCoordinateRange[T]) = SizeT(a.end - a.begin);

[T,I | Integer?(I)]
inline overload index(a:ReverseCoordinateRange[T], i:I) {
    assert[#boundsChecks](i >= 0 and i < size(a),
        ReverseCoordinateRange[T], " index out of bounds");
    return forward ..(a.end - i - 1)^;
}

[T]
inline overload iterator(a:ReverseCoordinateRange[T]) = a;

[T]
inline overload reverseIterator(a:ReverseCoordinateRange[T]) =
    CoordinateRange(a.begin, a.end);

[T]
inline overload hasNext?(a:ReverseCoordinateRange[T]) = a.begin < a.end;

[T]
inline overload next(a:ReverseCoordinateRange[T]) {
    assert[#boundsChecks](hasNext?(a), "next of empty ", ReverseCoordinateRange[T]);
    dec(a.end);
    return forward ..a.end^;
}

[T]
inline overload begin(a:ReverseCoordinateRange[T]) = ReverseCoordinate(a.end);

[T]
inline overload end(a:ReverseCoordinateRange[T]) = ReverseCoordinate(a.begin);



//
// coordinateRange, reverseCoordinateRange
//

inline coordinateRange(begin, end) = CoordinateRange(begin, end);

inline reverseCoordinateRange(end, begin) = ReverseCoordinateRange(end, begin);
