Source

units / units.go

Full commit
package units

const (
	kilogramPart = 2
	meterPart    = 3
	secondPart   = 5
	amperePart   = 7
	kelvinPart   = 11
)

type unit struct {
	numer uint32
	denom uint32
}

var (
	Meter    = unit{meterPart, 1}
	Second   = unit{secondPart, 1}
	Kilogram = unit{kilogramPart, 1}
	Ampere   = unit{amperePart, 1}
	Kelvin   = unit{kelvinPart, 1}
	Unitless = unit{1, 1}

	Newton = Kilogram.times(Meter).per(Second).per(Second)
	Joule  = Newton.times(Meter)
	Watt   = Joule.per(Second)
	Pascal = Newton.per(Meter).per(Meter)
	Hertz  = Unitless.per(Second)

	Coulomb = Ampere.times(Second)
	Volt    = Joule.per(Coulomb)
	Ohm     = Volt.per(Ampere)
	Farad   = Coulomb.per(Volt)
	Weber   = Volt.times(Second)
	Henry   = Weber.per(Ampere)
	Tesla   = Weber.per(Meter).per(Meter)
)

func gcd(a, b uint32) uint32 {
	for b > 0 {
		a, b = b, a%b
	}
	return a
}

func (u unit) canonicalize() unit {
	if u.numer == 0 {
		u.numer = 1
	}
	if u.denom == 0 {
		u.denom = 1
	}
	d := gcd(u.numer, u.denom)
	return unit{u.numer / d, u.denom / d}
}

func (x unit) times(y unit) unit {
	x.numer *= y.numer
	x.denom *= y.denom
	return x.canonicalize()
}

func (x unit) per(y unit) unit {
	x.numer *= y.denom
	x.denom *= y.numer
	return x.canonicalize()
}

type Value struct {
	num   float64
	units unit
}

func (x Value) Mul(y Value) Value {
	return Value{x.num * y.num, x.units.times(y.units)}
}

func (x Value) Div(y Value) Value {
	return Value{x.num / y.num, x.units.per(y.units)}
}

func (x Value) Add(y Value) Value {
	if x.units != y.units {
		panic("cannot add different units")
	}
	return Value{x.num + y.num, x.units}
}

func (x Value) Sub(y Value) Value {
	if x.units != y.units {
		panic("cannot subtract different units")
	}
	return Value{x.num - y.num, x.units}
}