Created
March 4, 2012 18:43
-
-
Save ben0x539/1974322 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import std.stdio; | |
import std.math; | |
import std.array; | |
import std.format; | |
struct Dimension(int m, int kg, int s) { | |
@disable this(); | |
} | |
template GetBaseType(T) { | |
static if (is(T S : Scalar!(BaseType, Dim), BaseType, Dim)) | |
alias BaseType GetBaseType; | |
else | |
alias T GetBaseType; | |
} | |
GetBaseType!(T) unwrap(T)(T v) { | |
static if (is(T S : Scalar!(BaseType, Dim), BaseType, Dim)) | |
return v.value; | |
else | |
return v; | |
} | |
template GetDimension(T) { | |
static if (is(T S : Scalar!(BaseType, Dim), BaseType, Dim)) | |
alias Dim GetDimension; | |
else | |
alias Unit GetDimension; | |
} | |
template GetResultDimension(string op, Dim1 : Dimension!(m1, kg1, s1), | |
Dim2 : Dimension!(m2, kg2, s2), | |
int m1, int kg1, int s1, | |
int m2, int kg2, int s2) { | |
static if (op == "*") { | |
alias Dimension!(m1 + m2, kg1 + kg2, s1 + s2) GetResultDimension; | |
} else static if (op == "/") { | |
alias Dimension!(m1 - m2, kg1 - kg2, s1 - s2) GetResultDimension; | |
} else static if (op == "+" || op == "-") { | |
static assert(is(Dim1 == Dim2)); | |
alias Dim1 GetResultDimension; | |
} else { | |
static assert("unknown operation"); | |
} | |
} | |
template GetResultType(string op, Lhs, Rhs) { | |
alias typeof(mixin("GetBaseType!(Lhs).init " | |
~ op | |
~ " GetBaseType!(Rhs).init")) | |
ResultBaseType; | |
alias GetResultDimension!(op, GetDimension!(Lhs), GetDimension!(Rhs)) | |
ResultDimension; | |
alias Scalar!(ResultBaseType, ResultDimension) GetResultType; | |
} | |
alias Dimension!(1, 0, 0) Meter; | |
alias Dimension!(0, 1, 0) Kilogram; | |
alias Dimension!(0, 0, 1) Second; | |
alias Dimension!(0, 0, 0) Unit; | |
struct Scalar(BaseType, Dim : Dimension!(m, kg, s), int m, int kg, int s) { | |
this(BaseType v) { value = v; } | |
private BaseType value; | |
static if (is(Dim == Unit)) { | |
@property private int getValue() { return value; } | |
alias getValue this; | |
} | |
Scalar opUnary(string op)() if (op == "+" || op == "-") { | |
return Scalar(mixin(op ~ "value")); | |
} | |
template opBinary(string op, Rhs) | |
if (op == "*" || op == "/" || op == "+" || op == "-") | |
{ | |
alias GetResultType!(op, Scalar, Rhs) ResultType; | |
ResultType opBinary(Rhs rhs) { | |
return ResultType(mixin("value " ~ op ~ "unwrap(rhs)")); | |
} | |
} | |
template opBinaryRight(string op, Lhs) | |
if (!is(Lhs S : Scalar!(BaseType, Dim), BaseType, Dim)) | |
{ | |
alias GetResultType!(op, Lhs, Scalar) ResultType; | |
ResultType opBinaryRight(Lhs lhs) { | |
return ResultType(mixin("unwrap(lhs) " ~ op ~ " value")); | |
} | |
} | |
template opOpAssign(string op, Rhs) { | |
static assert(is(GetResultDimension!(op, Dim, GetDimension!(Rhs)) == Dim)); | |
ref Scalar opOpAssign(Rhs rhs) { | |
mixin("value " ~ op ~ "= unwrap(rhs);"); | |
return this; | |
} | |
} | |
template mapBase(Dg : T delegate(BaseType), T) { | |
alias Scalar!(T, Dim) ResultType; | |
ResultType mapBase(Dg dg) { | |
return ResultType(dg(value)); | |
} | |
} | |
template castBase(T) { | |
alias Scalar!(T, Dim) ResultType; | |
@property ResultType castBase() { | |
return ResultType(cast(T) value); | |
} | |
} | |
string toString() { | |
auto w = appender!string(); | |
formattedWrite(w, "%s", value); | |
static if (m == 1) | |
formattedWrite(w, " m"); | |
else if (m) | |
formattedWrite(w, " m^%s", m); | |
static if (kg == 1) | |
formattedWrite(w, " kg"); | |
else if (kg) | |
formattedWrite(w, " kg^%s", kg); | |
static if (s == 1) | |
formattedWrite(w, " s"); | |
else if (s) | |
formattedWrite(w, " s^%s", s); | |
return w.data; | |
} | |
T opCast(T)() if (m == 0 && kg == 0 && s == 0) { | |
return cast(T) value; | |
} | |
} | |
template sqrt(T : Scalar!(BaseType, Dim), | |
BaseType, | |
Dim : Dimension!(m, kg, s), | |
int m, int kg, int s) { | |
static assert(is(typeof(std.math.sqrt(BaseType.init))), "can't use " ~ BaseType.stringof ~ " with std.math.sqrt()"); | |
static assert(m % 2 == 0 && kg % 2 == 0 && s % 2 == 0); | |
alias Dimension!(m / 2, kg / 2, s / 2) NewDim; | |
alias Scalar!(BaseType, NewDim) ResultType; | |
ResultType sqrt(T operand) { | |
return ResultType(std.math.sqrt(operand.value)); | |
} | |
} | |
template DimPow(uint n : 0, Dim) { | |
alias Unit DimPow; | |
} | |
template DimPow(uint n, Dim) { | |
alias GetResultDimension!("*", Dim, DimPow!(n - 1, Dim)) DimPow; | |
} | |
template Pow(uint n, T : Scalar!(BaseType, Dim), BaseType, Dim) { | |
alias DimPow!(n, Dim) ResultDimension; | |
alias Scalar!(BaseType, ResultDimension) ResultType; | |
ResultType Pow(T operand) { | |
return ResultType(std.math.pow(operand.value, n)); | |
} | |
} | |
mixin template mixinBlock(alias block) { | |
auto _mixin_block_detail = (block(), 1); | |
} | |
mixin template CheckType(string s) { | |
mixin mixinBlock!({ | |
auto result = mixin(s); | |
writeln(s, ": ", result, | |
" : ", typeof(result).stringof); | |
}); | |
} | |
mixin template CheckFail(string s) { | |
mixin mixinBlock!({ | |
static assert(!is(typeof(mixin(s)))); | |
writeln(s, ": ok, doesn't compile"); | |
}); | |
} | |
void main() { | |
alias Scalar!(int, Meter) IntMeter; | |
alias Scalar!(double, Second) DoubleSecond; | |
alias Scalar!(int, Unit) IntUnit;; | |
IntMeter a = 100; | |
DoubleSecond d = 500; | |
IntUnit u = 42; | |
mixin CheckType!("-a"); | |
mixin CheckType!("a + a"); | |
mixin CheckType!("a / 2"); | |
mixin CheckType!("a * d"); | |
mixin CheckType!("a / d"); | |
mixin CheckType!("1 / d"); | |
mixin CheckType!("sqrt(d * d)"); | |
mixin CheckType!("sqrt(a.castBase!double * a).castBase!int"); | |
mixin CheckType!("d.castBase!int"); | |
mixin CheckType!("d.mapBase(delegate(double v) { return v * 2; })"); | |
mixin CheckType!("{ int i = a * 20 / a; return i; }()"); | |
mixin CheckType!("cast(void*) (a / a)"); | |
mixin CheckType!("2 * IntMeter(1)"); | |
mixin CheckType!("u += 5"); | |
mixin CheckType!("a *= u"); | |
mixin CheckType!("a += 2 * IntMeter(1)"); | |
mixin CheckType!("a /= 1000"); | |
mixin CheckType!("Pow!(3)(a*a*(d/500).castBase!int)"); | |
mixin CheckFail!("++a"); | |
mixin CheckFail!("a += u"); | |
mixin CheckFail!("a += d"); | |
mixin CheckFail!("a += 2"); | |
mixin CheckFail!("cast(int) a"); | |
mixin CheckFail!("sqrt(a * a)"); // no sqrt(int) :( | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment