import printer.formatter.(repr);
import simd.primitives as prims;


//
// index
//

[T,n,I | Integer?(I)]
inline overload index(a:Vec[T,n], i:I) = ref prims.vecElementRef(a, Int(i));



//
// type inferring constructor
//

[..A | (countValues(..A) > 0) and equalValues?(..A)]
inline overload Vec(..args:A) {
    alias n = static countValues(..A);
    alias T = firstValue(..A);
    return Vec[T, n](..args);
}



//
// basic constructor
//

[T,n,..A | (countValues(..A) == n) and equalValues?(T, ..A)]
inline overload Vec[T,n](..args:A) {
    var a = uninitializedUnsafe(Vec[T,n]);
    var i = 0;
    ..for (x in args) {
        a[i] = x;
        i += 1;
    }
    return a;
}



//
// single element replicating constructor
//

[T,n]
inline overload Vec[T,n](x:T) {
    var a = uninitializedUnsafe(Vec[T,n]);
    for (i in range(n))
        a[i] = x;
    return a;
}



//
// other constructors, moveUnsafe, resetUnsafe, assign, destroy
//

[T,n]
inline overload Vec[T,n]() = Vec[T,n](T());

[T,n]
overload Vec[T,n](src:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %src
    store ${Vec[T,n]} %1, ${Vec[T,n]} * %returned
    ret i8* null
}

[T,n]
inline overload moveUnsafe(src:Vec[T,n]) --> returned:Vec[T,n] {
    returned <-- src;
}

[T,n]
inline overload assign(ref dest:Vec[T,n], src:Vec[T,n]) {
    dest <-- src;
}



//
// unpackVec
//

[T,n]
inline unpackVec(a:Vec[T,n]) = ..unpackElements(a, static 0);

private define unpackElements;

[T,n,i]
inline overload unpackElements(a:Vec[T,n], static i) =
    a[i], ..unpackElements(a, static i+1);

[T,n,i | i == n]
inline overload unpackElements(a:Vec[T,n], static i) = ;



//
// numeric ops
//

[T,n]
inline overload add(a:Vec[T,n], b:Vec[T,n]) = prims.add(a, b);

[T,n,X | Numeric?(X)]
inline overload add(a:Vec[T,n], x:X) = a + Vec[T,n](T(x));

[T,n,X | Numeric?(X)]
inline overload add(x:X, a:Vec[T,n]) = Vec[T,n](T(x)) + a;


[T,n]
inline overload subtract(a:Vec[T,n], b:Vec[T,n]) = prims.subtract(a, b);

[T,n,X | Numeric?(X)]
inline overload subtract(a:Vec[T,n], x:X) = a - Vec[T,n](T(x));

[T,n,X | Numeric?(X)]
inline overload subtract(x:X, a:Vec[T,n]) = Vec[T,n](T(x)) - a;


[T,n]
inline overload multiply(a:Vec[T,n], b:Vec[T,n]) = prims.multiply(a, b);

[T,n,X | Numeric?(X)]
inline overload multiply(a:Vec[T,n], x:X) = a * Vec[T,n](T(x));

[T,n,X | Numeric?(X)]
inline overload multiply(x:X, a:Vec[T,n]) = Vec[T,n](T(x)) * a;


[T,n]
inline overload divide(a:Vec[T,n], b:Vec[T,n]) = prims.divide(a, b);

[T,n,X | Numeric?(X)]
inline overload divide(a:Vec[T,n], x:X) = a / Vec[T,n](T(x));

[T,n,X | Numeric?(X)]
inline overload divide(x:X, a:Vec[T,n]) = Vec[T,n](T(x)) / a;


[T,n]
inline overload remainder(a:Vec[T,n], b:Vec[T,n]) = prims.remainder(a, b);

[T,n,X | Numeric?(X)]
inline overload remainder(a:Vec[T,n], x:X) = a % Vec[T,n](T(x));

[T,n,X | Numeric?(X)]
inline overload remainder(x:X, a:Vec[T,n]) = Vec[T,n](T(x)) % a;



//
// bitwise ops
//

[T,n]
inline overload bitshl(a:Vec[T,n], b:Vec[T,n]) = prims.bitshl(a, b);

[T,n,X | Numeric?(X)]
inline overload bitshl(a:Vec[T,n], x:X) = bitshl(a, Vec[T,n](T(x)));

[T,n,X | Numeric?(X)]
inline overload bitshl(x:X, a:Vec[T,n]) = bitshl(Vec[T,n](T(x)),  a);


[T,n]
inline overload bitshr(a:Vec[T,n], b:Vec[T,n]) = prims.bitshr(a, b);

[T,n,X | Numeric?(X)]
inline overload bitshr(a:Vec[T,n], x:X) = bitshr(a, Vec[T,n](T(x)));

[T,n,X | Numeric?(X)]
inline overload bitshr(x:X, a:Vec[T,n]) = bitshr(Vec[T,n](T(x)),  a);

private inline bitwiseOp(fn, a, b) {
    alias size = Int(TypeSize(Type(a)));
    return bitcast(
        fn(bitcast(a, Vec[UInt8, size]), bitcast(b, Vec[UInt8, size])),
        Type(a)
    );
}

[T,n, U,m | CompatibleVectorTypes?(Vec[T,n], Vec[U,m])]
inline overload bitand(a:Vec[T,n], b:Vec[U,m]) = bitwiseOp(prims.bitand, a, b);

[T,n,X | Numeric?(X)]
inline overload bitand(a:Vec[T,n], x:X) = bitand(a, Vec[T,n](T(x)));

[T,n,X | Numeric?(X)]
inline overload bitand(x:X, a:Vec[T,n]) = bitand(Vec[T,n](T(x)),  a);


[T,n, U,m | CompatibleVectorTypes?(Vec[T,n], Vec[U,m])]
inline overload bitor(a:Vec[T,n], b:Vec[U,m]) = bitwiseOp(prims.bitor, a, b);

[T,n,X | Numeric?(X)]
inline overload bitor(a:Vec[T,n], x:X) = bitor(a, Vec[T,n](T(x)));

[T,n,X | Numeric?(X)]
inline overload bitor(x:X, a:Vec[T,n]) = bitor(Vec[T,n](T(x)),  a);


[T,n, U,m | CompatibleVectorTypes?(Vec[T,n], Vec[U,m])]
inline overload bitxor(a:Vec[T,n], b:Vec[U,m]) = bitwiseOp(prims.bitxor, a, b);

[T,n,X | Numeric?(X)]
inline overload bitxor(a:Vec[T,n], x:X) = bitxor(a, Vec[T,n](T(x)));

[T,n,X | Numeric?(X)]
inline overload bitxor(x:X, a:Vec[T,n]) = bitxor(Vec[T,n](T(x)),  a);



//
// unary ops
//

[T,n]
inline overload plus(a:Vec[T,n]) = a;

[T,n]
inline overload minus(a:Vec[T,n]) = Vec[T,n](T(0)) - a;

[T,n]
inline overload bitnot(a:Vec[T,n]) = bitxor(Vec[T,n](componentMask(T)), a);



//
// printing
//

[T,n]
inline overload printTo(stream, a:Vec[T,n])
{
    printTo(stream, Type(a), "(", ..weaveValues(", ", ..unpackVec(a)), ")");
}

[T,n]
inline overload printReprTo(stream, a:Vec[T,n])
{
    printTo(stream, Type(a), "(", repr(..unpackVec(a)), ")");
}



//
// casting
//

CompatibleVectorTypes?(X, Y) = false;
[T,n, U,m | TypeSize(Vec[T,n]) == TypeSize(Vec[U,m])]
overload CompatibleVectorTypes?(static Vec[T,n], static Vec[U,m]) = true;

[T,n, U,m | T != U and CompatibleVectorTypes?(Vec[T,n], Vec[U,m])]
inline overload Vec[T,n](v: Vec[U,m]) = bitcast(v, Vec[T,n]);



//
// boolean masks
//

[T | Integer?(T)]
inline componentMask(static T) = wrapCast(T, -1);
inline overload componentMask(static Float32) = bitcast(-1_i, Float32);
inline overload componentMask(static Float64) = bitcast(-1_l, Float64);
