Daniel wrote:
I also kind of hoped we'd see standard real world unit types in D.  Like sqft, 
meters, ohms, kg, L, Hz, seconds,
etc. native to D.  Being able to intrinsically convert types between these 
things would make D the most
approachable General Programming Language for real world problems.

Here it is:

// by Oskar Linde Aug 2006
// This is just a quick hack to test
// IFTI operators opMul and opDel

import std.stdio;
import std.math;
import std.string;

version = unicode;

struct SiQuantity(T,int e1, int e2, int e3, int e4, int e5, int e6, int e7) {
        T value = 0;
        alias T ValueType;
        const exp1 = e1;
        const exp2 = e2;
        const exp3 = e3;
        const exp4 = e4;
        const exp5 = e5;
        const exp6 = e6;
        const exp7 = e7;

        static assert(SiQuantity.sizeof == ValueType.sizeof);

        template AddDimensions(int mul, U) {
                static assert(is(U.ValueType == ValueType) ||
                              is(U == ValueType),
                              "incompatible value types");
                static if (is(U == ValueType))
                        alias SiQuantity AddDimensions;
                else
                        alias SiQuantity!(T,exp1+mul*U.exp1,exp2+mul*U.exp2,
                                          exp3+mul*U.exp3,exp4+mul*U.exp4,
                                          exp5+mul*U.exp5,exp6+mul*U.exp6,
                                          exp7+U.exp7) AddDimensions;
        }

        SiQuantity opAddAssign(SiQuantity rhs) {
                value += rhs.value;
                return this;
        }

        SiQuantity opSubAssign(SiQuantity rhs) {
                value -= rhs.value;
                return this;
        }

    const
    {

        SiQuantity opAdd(SiQuantity rhs) {
                SiQuantity ret;
                ret.value = value + rhs.value;
                return ret;
        }

        SiQuantity opSub(SiQuantity rhs) {
                SiQuantity ret;
                ret.value = value - rhs.value;
                return ret;
        }

        SiQuantity opNeg() {
                SiQuantity ret;
                ret.value = -value;
                return ret;
        }

        SiQuantity opPos() {
                typeof(return) ret;
                ret.value = value;
                return ret;
        }

        int opCmp(SiQuantity rhs) {
                if (value > rhs.value)
                        return 1;
                if (value < rhs.value)
                        return -1;
                return 0; // BUG: NaN
        }

        AddDimensions!(+1,Rhs) opMul(Rhs)(Rhs rhs) {
                AddDimensions!(+1,Rhs) ret;
                static if (is(Rhs : T))
                        ret.value = value * rhs;
                else
                        ret.value = value * rhs.value;
                return ret;
        }

        AddDimensions!(-1,Rhs) opDiv(Rhs)(Rhs rhs) {
                AddDimensions!(-1,Rhs) ret;
                static if (is(Rhs : T))
                        ret.value = value / rhs;
                else
                        ret.value = value / rhs.value;
                return ret;
        }

        SiQuantity opMul_r(T lhs) {
                SiQuantity ret;
                ret.value = lhs * value;
                return ret;
        }

        SiQuantity!(T,-e1,-e2,-e3,-e4,-e5,-e6,-e7) opDiv_r(T lhs) {
                SiQuantity!(T,-e1,-e2,-e3,-e4,-e5,-e6,-e7) ret;
                ret.value = lhs / value;
                return ret;
        }

        string toString() {
                string prefix = "";
                T multiplier = 1;
                T value = this.value;
                string unit;
                
                static if (is(typeof(UnitName!(SiQuantity))))
                        unit = UnitName!(SiQuantity);
                else {
                        value *= pow(cast(real)1e3,cast(uint)e2); // convert kg 
-> g
                        // Take mass (e2) first to handle kg->g prefix issue 
                        if (e2 != 0) unit ~= format("·g^%s",e2);
                        if (e1 != 0) unit ~= format("·m^%s",e1);
                        if (e3 != 0) unit ~= format("·s^%s",e3);
                        if (e4 != 0) unit ~= format("·A^%s",e4);
                        if (e5 != 0) unit ~= format("·K^%s",e5);
                        if (e6 != 0) unit ~= format("·mol^%s",e6);
                        if (e7 != 0) unit ~= format("·cd^%s",e7);
                        if (unit)
                                unit = unit[2..$].split("^1").join("");
                }

                if (value >= 1e24) { prefix = "Y"; multiplier = 1e24; }
                else if (value >= 1e21) { prefix = "Z"; multiplier = 1e21; }
                else if (value >= 1e18) { prefix = "E"; multiplier = 1e18; }
                else if (value >= 1e15) { prefix = "P"; multiplier = 1e15; }
                else if (value >= 1e12) { prefix = "T"; multiplier = 1e12; }
                else if (value >= 1e9) { prefix = "G"; multiplier = 1e9; }
                else if (value >= 1e6) { prefix = "M"; multiplier = 1e6; }
                else if (value >= 1e3) { prefix = "k"; multiplier = 1e3; }
                else if (value >= 1) { }
                else if (value >= 1e-3) { prefix = "m"; multiplier = 1e-3; }
                else if (value >= 1e-6) {
                        version(unicode)
                                prefix = "µ";
                        else
                                prefix = "u";
                        multiplier = 1e-6; }
                else if (value >= 1e-9) { prefix = "n"; multiplier = 1e-9; }
                else if (value >= 1e-12) { prefix = "p"; multiplier = 1e-12; }
                else if (value >= 1e-15) { prefix = "f"; multiplier = 1e-15; }
                else if (value >= 1e-18) { prefix = "a"; multiplier = 1e-18; }
                else if (value >= 1e-21) { prefix = "z"; multiplier = 1e-21; }
                else if (value >= 1e-24) { prefix = "y"; multiplier = 1e-24; }

                return format("%.3s %s%s",value/multiplier, prefix, unit);
        }       
    }
}

//length                        meter           m
//mass                          kilogram        kg
//time                          second          s
//electric current              ampere          A
//thermodynamic temperature     kelvin          K
//amount of substance           mole            mol
//luminous intensity            candela         cd

// Si base quantities
alias SiQuantity!(real,1,0,0,0,0,0,0) Length;
alias SiQuantity!(real,0,1,0,0,0,0,0) Mass;
alias SiQuantity!(real,0,0,1,0,0,0,0) Time;
alias SiQuantity!(real,0,0,0,1,0,0,0) Current;
alias SiQuantity!(real,0,0,0,0,1,0,0) Temperature;
alias SiQuantity!(real,0,0,0,0,0,1,0) AmountOfSubstance;
alias SiQuantity!(real,0,0,0,0,0,0,1) Intensity;

alias SiQuantity!(real,0,0,0,0,0,0,0) UnitLess;

// Derived quantities
alias typeof(Length*Length)             Area;
alias typeof(Length*Area)               Volume;
alias typeof(Mass/Volume)               Density;
alias typeof(Length*Mass/Time/Time)     Force;
alias typeof(1/Time)                    Frequency;
alias typeof(Force/Area)                Pressure;
alias typeof(Force*Length)              Energy;
alias typeof(Energy/Time)               Power;
alias typeof(Time*Current)              Charge;
alias typeof(Power/Current)             Voltage;
alias typeof(Charge/Voltage)            Capacitance;
alias typeof(Voltage/Current)           Resistance;
alias typeof(1/Resistance)              Conductance;
alias typeof(Voltage*Time)              MagneticFlux;
alias typeof(MagneticFlux/Area)         MagneticFluxDensity;
alias typeof(MagneticFlux/Current)      Inductance;
alias typeof(Intensity*UnitLess)        LuminousFlux;
alias typeof(LuminousFlux/Area)         Illuminance;

// SI fundamental units
const Length            meter           = {1};
const Mass              kilogram        = {1};
const Time              second          = {1};
const Current           ampere          = {1};
const Temperature       kelvin          = {1};
const AmountOfSubstance mole            = {1};
const Intensity         candela         = {1};

// Derived units
const Frequency         hertz           = {1};
const Force             newton          = {1};
const Pressure          pascal          = {1};
const Energy            joule           = {1};
const Power             watt            = {1};
const Charge            coulomb         = {1};
const Voltage           volt            = {1};
const Capacitance       farad           = {1};
const Resistance        ohm             = {1};
const Conductance       siemens         = {1};
const MagneticFlux      weber           = {1};
const MagneticFluxDensity tesla         = {1};
const Inductance        henry           = {1};
const LuminousFlux      lumen           = {1};
const Illuminance       lux             = {1};

template UnitName(U:Frequency)  { const UnitName = "Hz"; }
template UnitName(U:Force)      { const UnitName = "N"; }
template UnitName(U:Pressure)   { const UnitName = "Pa"; }
template UnitName(U:Energy)     { const UnitName = "J"; }
template UnitName(U:Power)      { const UnitName = "W"; }
template UnitName(U:Charge)     { const UnitName = "C"; }
template UnitName(U:Voltage)    { const UnitName = "V"; }
template UnitName(U:Capacitance){ const UnitName = "F"; }
version(unicode) {
        template UnitName(U:Resistance) { const UnitName = "Ω"; }
} else {
        template UnitName(U:Resistance) { const UnitNAme = "ohm"; }
}       
template UnitName(U:Conductance){ const UnitName = "S"; }
template UnitName(U:MagneticFlux){ const UnitName = "Wb"; }
template UnitName(U:MagneticFluxDensity) { const UnitName = "T"; }
template UnitName(U:Inductance) { const UnitName = "H"; }

void main() {
        Area a = 25 * meter * meter;
        Length l = 10 * 1e3 * meter;
        Volume vol = a * l;
        Mass m = 100 * kilogram;
        assert(!is(typeof(vol / m) == Density));
        //Density density = vol / m; // dimension error -> syntax error
        Density density = m / vol;
        writefln("The volume is %s",vol.toString);
        writefln("The mass is %s",m.toString);
        writefln("The density is %s",density.toString);

        writef("\nElectrical example:\n\n");
        Voltage v = 5 * volt;
        Resistance r =  1 * 1e3 * ohm;
        Current i = v/r;
        Time ti = 1 * second;
        Power w = v*v/r;
        Energy e = w * ti;

        // One wishes the .toString was unnecessary...
        writefln("A current of ",i.toString);
        writefln("through a voltage of ",v.toString);
        writefln("requires a resistance of ",r.toString);
        writefln("and produces ",w.toString," of heat.");
        writefln("Total energy used in ",ti.toString," is ",e.toString);

        writef("\nCapacitor time curve:\n\n");
        
        Capacitance C = 0.47 * 1e-6 * farad;    // Capacitance
        Voltage V0 = 5 * volt;                  // Starting voltage
        Resistance R = 4.7 * 1e3 * ohm;         // Resistance

        for (Time t; t < 51 * 1e-3 * second; t += 1e-3 * second) {
                Voltage Vt = V0 * exp((-t / (R*C)).value);

                writefln("at %5s the voltage is %s",t.toString,Vt.toString);
        }
}

Reply via email to