


//
// ValueCoordinate
//

record ValueCoordinate[T] (value : T);


[T]
inline overload dereference(c:ValueCoordinate[T]) = c.value;

[T,I | CallDefined?(add, T, I)]
inline overload add(c:ValueCoordinate[T], i:I) =
    ValueCoordinate(T(c.value + i));

[T,I | CallDefined?(subtract, T, I)]
inline overload subtract(c:ValueCoordinate[T], i:I) =
    ValueCoordinate(T(c.value - i));

[T | CallDefined?(subtract, T, T)]
inline overload subtract(c1:ValueCoordinate[T], c2:ValueCoordinate[T]) =
    c1.value - c2.value;

[T]
inline overload inc(c:ValueCoordinate[T]) {
    inc(c.value);
}

[T]
inline overload dec(c:ValueCoordinate[T]) {
    dec(c.value);
}



//
// ValueReverseCoordinate
//

record ValueReverseCoordinate[T] (value : T);

[T]
inline overload dereference(c:ValueReverseCoordinate[T]) = (c.value - 1);

[T,I | CallDefined?(subtract, T, I)]
inline overload add(c:ValueReverseCoordinate[T], i:I) =
    ValueReverseCoordinate(T(c.value - i));

[T,I | CallDefined?(add, T, I)]
inline overload subtract(c:ValueReverseCoordinate[T], i:I) =
    ValueReverseCoordinate(T(c.value + i));

[T | CallDefined?(subtract, T, T)]
inline overload subtract(c1:ValueReverseCoordinate[T], c2:ValueReverseCoordinate[T]) =
    c2.value - c1.value;

[T]
inline overload inc(c:ValueReverseCoordinate[T]) {
    dec(c.value);
}

[T]
inline overload dec(c:ValueReverseCoordinate[T]) {
    inc(c.value);
}



//
// ReverseCoordinate
//

record ReverseCoordinate[T] (coord : T);

[T]
inline overload dereference(c:ReverseCoordinate[T]) = forward ..(c.coord - 1)^;

[T,I | CallDefined?(subtract, T, I)]
inline overload add(c:ReverseCoordinate[T], i:I) =
    ReverseCoordinate(T(c.coord - i));

[T,I | CallDefined?(add, T, I)]
inline overload subtract(c:ReverseCoordinate[T], i:I) =
    ReverseCoordinate(T(c.coord + i));

[T | CallDefined?(subtract, T, T)]
inline overload subtract(c1:ReverseCoordinate[T], c2:ReverseCoordinate[T]) =
    c2.coord - c1.coord;

[T]
inline overload inc(c:ReverseCoordinate[T]) {
    dec(c.coord);
}

[T]
inline overload dec(c:ReverseCoordinate[T]) {
    inc(c.coord);
}



//
// ByValueCoordinate
//

record ByValueCoordinate[T] (coord : T);

[T]
inline overload dereference(c:ByValueCoordinate[T]) = ..c.coord^;

[T,I | CallDefined?(add, T, I)]
inline overload add(c:ByValueCoordinate[T], i:I) =
    ByValueCoordinate(T(c.coord + i));

[T,I | CallDefined?(subtract, T, I)]
inline overload subtract(c:ByValueCoordinate[T], i:I) =
    ByValueCoordinate(T(c.coord - i));

[T | CallDefined?(subtract, T, T)]
inline overload subtract(c1:ByValueCoordinate[T], c2:ByValueCoordinate[T]) =
    c1.coord - c2.coord;

[T]
inline overload inc(c:ByValueCoordinate[T]) {
    inc(c.coord);
}

[T]
inline overload dec(c:ByValueCoordinate[T]) {
    dec(c.coord);
}
