

//
// core operations
//

define destroy;
define moveUnsafe;
define resetUnsafe;
define assign;
define updateAssign;

define equals?;
define lesser?;
define lesserEquals?;
define greater?;
define greaterEquals?;

define plus;
define minus;

define add;
define subtract;
define multiply;
define divide;
define remainder;

define inc;
define dec;

define bitshl;
define bitshr;
define bitand;
define bitor;
define bitxor;
define bitnot;

define dereference;
define call;
define fieldRef;
define staticIndex;

define fieldRefAssign;
define fieldRefUpdateAssign;
define staticIndexAssign;
define staticIndexUpdateAssign;
define indexAssign;
define indexUpdateAssign;



//
// uninitialized stack allocation
//

[T] uninitializedUnsafe(static T) --> x:T {}


//
// coordinate type predicates
//

Coordinate?(T) = CallDefined?(dereference, T);
CoordinateTargetType(T) = ..Type(..dereference(typeToLValue(T)));

RandomAccessCoordinate?(T) =
    Coordinate?(T) and
    CallDefined?(add, T, Int) and
    CallDefined?(subtract, T, Int) and
    CallDefined?(subtract, T, T);

LValueCoordinate?(T) =
    Coordinate?(T) and
    LValue?(dereference(typeToLValue(T)));

// true if adjacent coordinates point to adjacent locations in memory
ContiguousCoordinate?(T) = false;



//
// iterator type predicates
//

Iterator?(T) = CallDefined?(hasNext?, T) and CallDefined?(next, T);
IteratorTargetType(T) = ..Type(..next(typeToLValue(T)));



//
// iteration protocol
//

define iterator; // create iterator

define hasNext?; // iterator operation
define next;     // iterator operation



//
// reverse iteration
//

define reverseIterator;



//
// sequence protocol
//

define size;
define index;

define begin;
define end;

define front;
define back;

define push;
define pop;
define clear;
define insert;
define remove;
define reserve;
define resize;
define resizeUnsafe;

define pushFront;
define popFront;

// defined for statically sized sequences like Array[T,n]
define StaticSize;



//
// sequence type predicates
//

Sequence?(T) = CallDefined?(iterator, T);
SequenceElementType(T) = ..Type(..next(iterator(typeToLValue(T))));
SizedSequence?(T) = Sequence?(T) and CallDefined?(size, T);
StaticSizedSequence?(T) = Sequence?(T) and CallDefined?(StaticSize, Static[T]);
RandomAccessSequence?(T) = Sequence?(T) and CallDefined?(index, T, Int);
LValueSequence?(T) = Sequence?(T) and LValue?(next(iterator(typeToLValue(T))));

CoordinateSequence?(T) = Sequence?(T) and CallDefined?(begin, T) and CallDefined?(end, T);
SequenceCoordinateType(T) = Type(begin(typeToLValue(T)));

SequenceIteratorType(T) = Type(iterator(typeToLValue(T)));

// true if sequence elements are stored contiguously in memory
ContiguousSequence?(T) = false;

// true if copying the sequence is efficient
LightweightSequence?(T) = false;

// true for sequences which have value semantics.
SequenceContainer?(T) = false;

// true if sequence is not single-valued (0 valued sequences are included).
MultiValuedSequence?(T) =
    Sequence?(T) and multiValued?(..Type(..next(iterator(typeToLValue(T)))));

private multiValued?(..x) = true;
overload multiValued?(x) = false;



//
// hashing
//

define hash;



//
// mapping protocol
//

// 'index' and 'size' are available for mappings
// 'remove' is available

define lookup; // get pointer to element, null if not available
define put;
define items; // iterate over all items

inline contains?(map, key) = not null?(lookup(map, key));



//
// LValue?, RValue?
//

define LValue?;
alias overload LValue?(ref a) = true;
alias overload LValue?(rvalue a) = false;

alias RValue?(x) = not LValue?(x);



//
// defaults
//

inline initialize(a) {
    a <-- Type(a)();
}

[T]
inline copy(a:T) = T(a);

[T]
inline move(src:T) --> returned:T {
    returned <-- moveUnsafe(src);
    resetUnsafe(src);
}

[T]
inline overload assign(ref dest:T, rvalue src:T) {
    swap(dest, src);
}

[F]
inline overload updateAssign(static F, ref dest, forward src) {
    dest = F(dest, src);
}

inline notEquals?(a, b) = not equals?(a, b);

inline ordered?(a, b) = (a <= b) or (b <= a);

inline overload lesserEquals?(a, b) = not lesser?(b, a);

inline overload greater?(a, b) = lesser?(b, a);

inline overload greaterEquals?(a, b) = not lesser?(a, b);

inline overload bitand(a, b, c, ..rest) =
    bitand(bitand(a,b), c, ..rest);

inline overload bitor(a, b, c, ..rest) =
    bitor(bitor(a,b), c, ..rest);

inline overload bitxor(a, b, c, ..rest) =
    bitxor(bitxor(a,b), c, ..rest);

[FIELD]
inline overload fieldRefAssign(dest, static FIELD, forward src) {
    assign(fieldRef(dest, static FIELD), src);
}

[F, FIELD]
inline overload fieldRefUpdateAssign(static F, dest, static FIELD, forward src) {
    updateAssign(static F, fieldRef(dest, static FIELD), src);
}

[F, FIELD, T | RValue?(fieldRef(typeToLValue(T), static FIELD))]
inline overload fieldRefUpdateAssign(static F, dest:T, static FIELD, forward src) {
    fieldRefAssign(dest, static FIELD, F(fieldRef(dest, static FIELD), src));
}

[i]
inline overload staticIndexAssign(dest, static i, forward src) {
    assign(staticIndex(dest, static i), src);
}

[F, i]
inline overload staticIndexUpdateAssign(static F, dest, static i, forward src) {
    updateAssign(static F, staticIndex(dest, static i), src);
}

[F, i, T | RValue?(staticIndex(typeToLValue(T), static i))]
inline overload staticIndexUpdateAssign(static F, dest:T, static i, forward src) {
    staticIndexAssign(dest, static i, F(staticIndex(dest, static i), src));
}

[..T | countValues(..T) >= 2]
inline overload indexAssign(dest, forward ..indicesAndSrc:T) {
    assign(
        index(dest, ..withoutNthValue(
            static countValues(..T)-1,
            ..indicesAndSrc
        )),
        lastValue(..indicesAndSrc),
    );
}

[F, ..A | countValues(..A) >= 2]
inline overload indexUpdateAssign(static F, dest, forward ..indicesAndSrc:A) {
    updateAssign(
        static F,
        index(dest, ..withoutNthValue(
            static countValues(..A)-1,
            ..indicesAndSrc
        )),
        lastValue(..indicesAndSrc),
    );
}

[F, T, ..A |
    (countValues(..A) >= 2) and
    RValue?(
        index(
            typeToLValue(T),
            ..mapValues(
                typeToLValue,
                ..withoutNthValue(static countValues(..A)-1, ..A)
            )
        )
    )
]
inline overload indexUpdateAssign(static F, dest:T, forward ..indicesAndSrc:A) {
    indexAssign(
        dest,
        ..withoutNthValue(static countValues(..A)-1, ..indicesAndSrc),
        F(
            index(
                dest,
                ..withoutNthValue(static countValues(..A)-1, ..indicesAndSrc)
            ),
            lastValue(..indicesAndSrc)
        )
    );
}




//
// swap
//

inline swap(a, b) {
    var temp = moveUnsafe(a);
    a <-- moveUnsafe(b);
    b <-- move(temp);
}

[T | BitwiseMovedType?(T)]
inline overload swap(a:T, b:T) {
    alias TBytes = Array[UInt8, Int(TypeSize(T))];
    var temp = bitcast(a, TBytes);
    bitcast(a, TBytes) <-- bitcast(b, TBytes);
    bitcast(b, TBytes) <-- temp;
}


//
// min, max
//

inline min(a, b) {
    if (a <= b)
        return a;
    else
        return b;
}

inline overload min(ref a, ref b) {
    if (a <= b)
        return ref a;
    else
        return ref b;
}

inline max(a, b) {
    if (a > b)
        return a;
    else
        return b;
}

inline overload max(ref a, ref b) {
    if (a > b)
        return ref a;
    else
        return ref b;
}



//
// empty?
//

[T | SizedSequence?(T)]
inline empty?(a:T) = (size(a) == 0);



//
// ifExpression
//

define ifExpression;

[T]
alias overload ifExpression(condition, consequent:T, alternate:T) : T {
    if (condition)
        return consequent;
    else
        return alternate;
}

[T]
alias overload ifExpression(condition, ref consequent:T, ref alternate:T) : ByRef[T] {
    if (condition)
        return ref consequent;
    else
        return ref alternate;
}



//
// bitcast, bitandc, bitblend
//

[T, U | TypeSize(T) >= TypeSize(U)]
inline bitcast(x: T, static U) = ref Pointer[U](&x)^;

inline bitandc(a, b) = bitand(a, bitnot(b));
inline bitblend(mask, ones, zeros)
    = bitor(bitand(ones, mask), bitandc(zeros, mask));



//
// bitrol, bitror
//

[I, J | Integer?(I) and Integer?(J)]
bitrol(x:I, n:J) = bitor(wrapBitshl(x, n), bitshr(x, TypeSize(I)*8 - n));

[I, J | Integer?(I) and Integer?(J)]
bitror(x:I, n:J) = bitor(bitshr(x, n), wrapBitshl(x, TypeSize(I)*8 - n));



//
// PrimitiveType?
//

PrimitiveType?(X) = false;
overload PrimitiveType?(static Bool) = true;
overload PrimitiveType?(static Int8) = true;
overload PrimitiveType?(static Int16) = true;
overload PrimitiveType?(static Int32) = true;
overload PrimitiveType?(static Int64) = true;
overload PrimitiveType?(static Int128) = true;
overload PrimitiveType?(static UInt8) = true;
overload PrimitiveType?(static UInt16) = true;
overload PrimitiveType?(static UInt32) = true;
overload PrimitiveType?(static UInt64) = true;
overload PrimitiveType?(static UInt128) = true;
overload PrimitiveType?(static Float32) = true;
overload PrimitiveType?(static Float64) = true;
overload PrimitiveType?(static Float80) = true;
overload PrimitiveType?(static Imag32) = true;
overload PrimitiveType?(static Imag64) = true;
overload PrimitiveType?(static Imag80) = true;
overload PrimitiveType?(static Complex32) = true;
overload PrimitiveType?(static Complex64) = true;
overload PrimitiveType?(static Complex80) = true;
[T] overload PrimitiveType?(static Pointer[T]) = true;
[I, O] overload PrimitiveType?(static CodePointer[I, O]) = true;
[I, O] overload PrimitiveType?(static CCodePointer[I, O]) = true;
[I, O] overload PrimitiveType?(static VarArgsCCodePointer[I, O]) = true;
[I, O] overload PrimitiveType?(static StdCallCodePointer[I, O]) = true;
[I, O] overload PrimitiveType?(static FastCallCodePointer[I, O]) = true;
[T] overload PrimitiveType?(static Static[T]) = true;
[T,n] overload PrimitiveType?(static Vec[T,n]) = true;
[E | Enum?(E)] overload PrimitiveType?(static E) = true;
[..T] overload PrimitiveType?(static Union[..T]) = true;
[] overload PrimitiveType?(static Union[]) = true;
overload PrimitiveType?(static Tuple[]) = true;



//
// PrimitiveCompoundType?, PrimitiveCompoundMemberTypes
//

PrimitiveCompoundType?(X) = false;

[T,n] overload PrimitiveCompoundType?(static Array[T,n]) = true;
[..T] overload PrimitiveCompoundType?(static Tuple[..T]) = true;
[R | RegularRecord?(R)] overload PrimitiveCompoundType?(static R) = true;
[V | Variant?(V)] overload PrimitiveCompoundType?(static V) = true;

define PrimitiveCompoundMemberTypes;
[T,n] overload PrimitiveCompoundMemberTypes(static Array[T,n]) = T;
[..T] overload PrimitiveCompoundMemberTypes(static Tuple[..T]) = ..T;
[R | RegularRecord?(R)]
overload PrimitiveCompoundMemberTypes(static R) = ..RecordFieldTypes(R);
[V | Variant?(V)]
overload PrimitiveCompoundMemberTypes(static V) = ..VariantMembers(V);


//
// PODType?
//

PODType?(X) = false;

[T | PrimitiveType?(T)]
overload PODType?(static T) = true;

[T | PrimitiveCompoundType?(T)]
overload PODType?(static T) = allValues?(PODType?, ..PrimitiveCompoundMemberTypes(T));



//
// Finer-grained POD-related properties
//

BitwiseCopiedType?(T) = PODType?(T);
BitwiseMovedType?(T) = PODType?(T);
NotDestroyedType?(T) = PODType?(T);
NotResetType?(T) = PODType?(T);
CopyDoesNotThrowType?(T) = PODType?(T);
AssignDoesNotThrowType?(T) = PODType?(T);
BitwiseAssignedType?(T) = BitwiseCopiedType?(T) and NotDestroyedType?(T);
BitwiseMoveAssignedType?(T) = BitwiseMovedType?(T) and NotDestroyedType?(T);

[T | PrimitiveCompoundType?(T)]
overload BitwiseCopiedType?(static T)
    = allValues?(BitwiseCopiedType?, ..PrimitiveCompoundMemberTypes(T));
[T | PrimitiveCompoundType?(T)]
overload BitwiseMovedType?(static T)
    = allValues?(BitwiseMovedType?, ..PrimitiveCompoundMemberTypes(T));
[T | PrimitiveCompoundType?(T)]
overload NotDestroyedType?(static T)
    = allValues?(NotDestroyedType?, ..PrimitiveCompoundMemberTypes(T));
[T | PrimitiveCompoundType?(T)]
overload NotResetType?(static T)
    = allValues?(NotResetType?, ..PrimitiveCompoundMemberTypes(T));
[T | PrimitiveCompoundType?(T)]
overload CopyDoesNotThrowType?(static T)
    = allValues?(CopyDoesNotThrowType?, ..PrimitiveCompoundMemberTypes(T));
[T | PrimitiveCompoundType?(T)]
overload AssignDoesNotThrowType?(static T)
    = allValues?(AssignDoesNotThrowType?, ..PrimitiveCompoundMemberTypes(T));
[T | PrimitiveCompoundType?(T)]
overload BitwiseAssignedType?(static T)
    = allValues?(BitwiseAssignedType?, ..PrimitiveCompoundMemberTypes(T));
[T | PrimitiveCompoundType?(T)]
overload BitwiseMoveAssignedType?(static T)
    = allValues?(BitwiseMoveAssignedType?, ..PrimitiveCompoundMemberTypes(T));



//
// identity test
//

inline is?(x, y) = &x == &y;
inline isNot?(x, y) = not is?(x, y);


//
// case?
//

inline case?(x, forward ..ys) : Bool {
    ..for (y in ..ys)
        if (equals?(x, y))
            return true;
    return false;
}
