Last active
April 12, 2017 11:34
-
-
Save rikkimax/f5c0accd3463d52fda524f1f929b29ab to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// | |
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