

//
// vecElementRef
//

[T,n]
vecElementRef(a:Vec[T,n], i:Int) --> returned:ByRef[T] __llvm__{
    %iVal = load $Int * %i
    %ptr = getelementptr ${Vec[T,n]} * %a, i32 0, $Int %iVal
    store $T * %ptr, $T ** %returned
    ret i8* null
}



//
// add
//

define add;

[T,n | Integer?(T)]
overload add(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = add ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}

[T,n | Float?(T)]
overload add(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = fadd ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}



//
// subtract
//

define subtract;

[T,n | Integer?(T)]
overload subtract(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = sub ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}

[T,n | Float?(T)]
overload subtract(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = fsub ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}



//
// multiply
//

define multiply;

[T,n | Integer?(T)]
overload multiply(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = mul ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}

[T,n | Float?(T)]
overload multiply(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = fmul ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}



//
// divide
//

define divide;

[T,n | SignedInteger?(T)]
overload divide(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = sdiv ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}

[T,n | UnsignedInteger?(T)]
overload divide(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = udiv ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}

[T,n | Float?(T)]
overload divide(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = fdiv ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}



//
// remainder
//

define remainder;

[T,n | SignedInteger?(T)]
overload remainder(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = srem ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}

[T,n | UnsignedInteger?(T)]
overload remainder(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = urem ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}

[T,n | Float?(T)]
overload remainder(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = frem ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}



//
// bitshl
//

define bitshl;

[T,n | Integer?(T)]
overload bitshl(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = shl ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}



//
// bitshr
//

define bitshr;

[T,n | SignedInteger?(T)]
overload bitshr(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = ashr ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}

[T,n | UnsignedInteger?(T)]
overload bitshr(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = lshr ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}



//
// bitand, bitor, bitxor
//

[T,n | Integer?(T)]
bitand(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = and ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}

[T,n | Integer?(T)]
bitor(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = or ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}

[T,n | Integer?(T)]
bitxor(a:Vec[T,n], b:Vec[T,n]) --> returned:Vec[T,n] __llvm__{
    %1 = load ${Vec[T,n]} * %a
    %2 = load ${Vec[T,n]} * %b
    %3 = xor ${Vec[T,n]} %1, %2
    store ${Vec[T,n]} %3, ${Vec[T,n]} * %returned
    ret i8* null
}
