
import vectors.generic.*;
import numbers.floats.(floatBits);



//
// booleans, integers, floats, pointers, characters
//

[| TypeSize(SizeT) == 4]
inline overload hash(x:Bool) = if (x) SizeT(1235232835u) else SizeT(3786054923u);
[| TypeSize(SizeT) == 8]
inline overload hash(x:Bool) =
    if (x) SizeT(2068536678500253190ul)
    else SizeT(12193288086786919936ul);

[T | Integer?(T)]
inline overload hash(x:T) = wrapCast(SizeT, x);

inline overload hash(x:Float32) =
    if (x == 0.0f) SizeT(0)
    else wrapCast(SizeT, floatBits(x));
[| TypeSize(SizeT) == 8]
inline overload hash(x:Float64) =
    if (x == 0.0f) SizeT(0)
    else wrapCast(SizeT, floatBits(x));
[| TypeSize(SizeT) == 4]
inline overload hash(x:Float64) = wrapCast(SizeT, bitshr(floatBits(x), 32));

inline overload hash(x:Float80) =
    wrapCast(SizeT, wrapAdd(floatBits(x).mantissa, floatBits(x).exponent));

[T | Imaginary?(T)]
inline overload hash(z:T) = hash(imagValue(z));

[T | Complex?(T)]
inline overload hash(z:T) = wrapAdd(
    hash(real(z)), wrapMultiply(hash(imagValue(z)), SizeT(31321)));

[T]
inline overload hash(x:Pointer[T]) = bitcast(x, SizeT);

inline overload hash(x:Char) = wrapCast(SizeT, x);



//
// arrays
//

[T,n]
inline overload hash(a:Array[T,n]) = hashSequence(a);



//
// tuples
//

inline hashValues(..values) {
    var h = SizeT(0);
    ..for (x in values)
        h = wrapAdd(wrapMultiply(SizeT(7), h), wrapMultiply(SizeT(13), hash(x)));
    return h;
}
inline overload hashValues(value) = hash(value);

[..T]
inline overload hash(x:Tuple[..T]) = hashValues(..tupleElements(x));


//
// records
//

[T | Record?(T)]
inline overload hash(x:T) = hashValues(..recordFields(x));



//
// enumerations
//

[T | Enum?(T)]
inline overload hash(x:T) = SizeT(x);



//
// shared pointers
//

[T]
inline overload hash(x:SharedPointer[T]) = hash(x.ptr);



//
// strings
//

inline overload hash(a:String) = hashSequence(a);
inline overload hash(a:StringConstant) = hashSequence(a);



//
// vectors
//

[T]
inline overload hash(a:Vector[T]) = hashSequence(a);



//
// hashSequence
//

hashSequence(a) {
    var h = SizeT(0);
    var f = SizeT(11);
    for (x in a) {
        h = wrapAdd(h, wrapMultiply(f, hashValues(x)));
        f = wrapSubtract(wrapMultiply(SizeT(2), f), SizeT(1));
    }
    return h;
}



//
// variants
//

[T | Variant?(T)]
overload hash(x:T) = wrapAdd(SizeT(wrapMultiply(23, variantTag(x))), hash(*x));



//
// reference types
//

[T | ReferenceType?(T)]
overload hash(x:T) = hash(referencePtr(x));


//
// statics
//

[x]
overload hash(s:Static[x]) = SizeT(0);
