Skip to content

Instantly share code, notes, and snippets.

@ben0x539
Created March 4, 2012 18:43
Show Gist options
  • Save ben0x539/1974322 to your computer and use it in GitHub Desktop.
Save ben0x539/1974322 to your computer and use it in GitHub Desktop.
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