Skip to content

Instantly share code, notes, and snippets.

@rikkimax
Last active April 12, 2017 11:34
Show Gist options
  • Save rikkimax/f5c0accd3463d52fda524f1f929b29ab to your computer and use it in GitHub Desktop.
Save rikkimax/f5c0accd3463d52fda524f1f929b29ab to your computer and use it in GitHub Desktop.
///
module engine.math.vector;
import std.traits : isBasicType, isFloatingPoint;
struct Vector(Type, size_t Dimension)
if ((isBasicType!Type || isFloatingPoint!Type) && Dimension > 0) {
nothrow:
private {
import simd = core.simd;
enum USE_SIMD_VECTOR = __traits(compiles, {simd.Vector!(Type[Dimension]) t;});
}
static if(USE_SIMD_VECTOR) {
///
simd.Vector!(Type[Dimension]) value;
} else {
union {
///
Type[Dimension] value;
struct {
static if (Dimension >= 1) {
///
Type x;
///
alias x r;
}
static if (Dimension >= 2) {
///
Type y;
///
alias y g;
}
static if (Dimension >= 3) {
///
Type z;
///
alias z b;
}
static if (Dimension >= 4) {
///
Type w;
///
alias w a;
}
}
}
}
///
this(Type[] data...)
in {
assert(data.length <= Dimension);
} body {
static if(USE_SIMD_VECTOR) {
if (data.length == 0) {
this.value.array[0 .. data.length] = data;
} else {
this.value.array = 0;
}
} else {
if (data.length == 0) {
this.value[0 .. data.length] = data[];
} else {
this.value = 0;
}
}
}
//
///
void opOpAssign(string op, size_t Dimension2)(Vector!(Type, Dimension2) other)
if ((op == "+" || op == "-" || op == "*" || op == "/") &&
Dimension2 <= Dimension && Dimension > 0) {
static if (!USE_SIMD_VECTOR) {
mixin("value[0 .. Dimension2] " ~ op ~ "= other.value[];");
} else static if (Dimension == Dimension2) {
mixin("value " ~ op ~ "= other.value;");
} else {
mixin("value.array[0 .. Dimension2] " ~ op ~ "= other.value.array[];");
}
}
///
void opOpAssign(string op)(Type other)
if (op == "+" || op == "-" || op == "*" || op == "/") {
static if (USE_SIMD_VECTOR) {
mixin("value[] " ~ op ~ "= other");
} else {
mixin("value.array[] " ~ op ~ "= other");
}
}
//
///
Vector!(Type, Dimension) opBinary(string op, size_t Dimension2)(Vector!(Type, Dimension2) other)
if ((op == "+" || op == "-" || op == "*" || op == "/") &&
Dimension2 <= Dimension && Dimension > 0) {
auto ret = this;
mixin("ret " ~ op ~ "= other;");
return ret;
}
///
Vector!(Type, Dimension) opBinaryRight(string op, size_t Dimension2)(Vector!(Type, Dimension2) other)
if ((op == "+" || op == "-" || op == "*" || op == "/") &&
Dimension2 <= Dimension && Dimension > 0) {
auto ret = other;
mixin("ret " ~ op ~ "= this;");
return ret;
}
///
Vector!(Type, Dimension) opBinary(string op)(Type other)
if (op == "+" || op == "-" || op == "*" || op == "/") {
auto ret = this;
mixin("ret " ~ op ~ "= other;");
return ret;
}
///
Vector!(Type, Dimension) opBinaryRight(string op)(Type other)
if (op == "+" || op == "-" || op == "*" || op == "/") {
auto ret = other;
mixin("ret " ~ op ~ "= this;");
return ret;
}
//
///
Vector!(Type, Dimension) opUnary(string op)() if (op == "-") {
auto ret = this;
static if (USE_SIMD_VECTOR) {
mixin("ret.value.array[] *= -1;");
} else {
mixin("ret.value[] *= -1;");
}
return ret;
}
//
static {
///
pragma(inline, true)
Vector!(Type, Dimension) zero() { return Vector!(Type, Dimension)(0); }
}
//
///
Type sum() {
Type ret = 0;
static if (USE_SIMD_VECTOR) {
foreach(v; value.array)
ret += v;
} else {
foreach(v; value)
ret += v;
}
return ret;
}
///
pragma(inline, true)
Type innerProduct(const(Vector!(Type, Dimension)) other) {
return dotProduct(other).sum();
}
///
Type norm() {
import std.math : sqrt;
// sqrt(sum(x^2 + ....))
return cast(Type)sqrt(cast(real)((this * this).sum));
}
///
pragma(inline, true)
Type distance(ref const(Vector!(Type, Dimension)) other) {
return (this - other).norm;
}
///
pragma(inline, true)
Type dotProduct(ref const(Vector!(Type, Dimension)) other) {
return (this * other).sum;
}
///
Type standardDeviation() {
import std.math : sqrt;
// cost:
// - 2 copies
// - 2 sums
// - 1 scalar divide
// - 1 vector - scalar
// - 1 vector multiplication
auto temp = this;
Type avg = temp.sum / Dimension;
temp -= avg;
return cast(Type)sqrt(cast(real)((temp * temp).sum / Dimension));
}
//
///
pragma(inline, true)
Type opIndex(size_t i) {
if (i > Dimension)
return Type.init;
static if (USE_SIMD_VECTOR) {
return value.array[i];
} else {
return value[i];
}
}
/// i++ (cos mathematics not programmers are silly)
pragma(inline, true)
Type index(size_t i) {
i += 1;
if (i > Dimension)
return Type.init;
static if (USE_SIMD_VECTOR) {
return value.array[i];
} else {
return value[i];
}
}
///
pragma(inline, true)
Vector!(Type, Dimension2) subVector(size_t Dimension2)(size_t start) {
if (Dimension >= start + Dimension2) {
static if (USE_SIMD_VECTOR) {
return Vector!(Type, Dimension2)(value.array[start .. start + Dimension2]);
} else {
return Vector!(Type, Dimension2)(value[start .. start + Dimension2]);
}
} else if (start >= Dimension) {
return Vector!(Type, Dimension2).init;
} else {
static if (USE_SIMD_VECTOR) {
return Vector!(Type, Dimension2)(value.array[start .. $]);
} else {
return Vector!(Type, Dimension2)(value[start .. $]);
}
}
}
//
///
pragma(inline, true)
bool isZeroVector() {
return this.sum() == 0;
}
///
bool isOnesVector() {
import std.math : approxEquals;
uint ret;
static if (USE_SIMD_VECTOR) {
foreach(v; value.array)
ret += cast(ubyte)approxEquals(v == 1);
} else {
foreach(v; value)
ret += cast(ubyte)approxEquals(v == 1);
}
return ret == Dimension;
}
///
bool isUnitVector() {
import std.math : approxEquals;
uint ret;
static if (USE_SIMD_VECTOR) {
foreach(v; value.array)
ret += cast(ubyte)(v == 0);
} else {
foreach(v; value)
ret += cast(ubyte)(v == 0);
}
return ret == 1 && approxEquals(sum(), 1);
}
///
bool isSparse() {
import std.math : approxEquals;
uint ret;
static if (USE_SIMD_VECTOR) {
foreach(v; value.array)
ret += cast(ubyte)(v == 0);
} else {
foreach(v; value)
ret += cast(ubyte)(v == 0);
}
return ret >= Dimension/3;
}
///
pragma(inline, true)
bool opEquals(ref const(Vector!(Type, Dimension)) other) {
static if (USE_SIMD_VECTOR) {
return other.value == value;
} else {
return other.value[] == value[];
}
}
///
pragma(inline, true)
bool opEquals(size_t Dimension2)(ref const(Vector!(Type, Dimension2)) other) {
static if (USE_SIMD_VECTOR) {
static if (Dimension2 < Dimension) {
return other.value.array[] == value[0 .. Dimension2].array[];
} else {
return other.value.array[0 .. Dimension][] == value.array[];
}
} else {
static if (Dimension2 < Dimension) {
return other.value[] == value[0 .. Dimension2][];
} else {
return other.value[0 .. Dimension][] == value[];
}
}
}
}
unittest {
Vector!(float, 2) a;
Vector!(float, 1) b;
a = Vector!(float, 2)(1, 2);
b = Vector!(float, 1)(1);
a += b;
a -= b;
a *= b;
a /= b;
a = a + b;
a = a - b;
a = a * b;
a = a / b;
a = -a;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment