@neutrium/quantity
Version:
The NeutriumJS unit conversion module.
208 lines (175 loc) • 4.94 kB
text/typescript
import { Decimal } from '@neutrium/math';
import { typeguards } from "@neutrium/utilities";
import { Quantity } from '../Quantity.js'
import { QuantityInitParam } from '../guards.js';
import { isCompatible } from './comparison.js';
import { addTempDegrees, subtractTemperatures, subtractTempDegrees } from './temperature.js';
import { throwIncompatibleUnits } from './errors.js';
import { cleanUnitNames } from './clean-unit-names.js'
;
const UNITY = "<1>";
//********************* */ Moved
//
// Mathematical operations on quantities
//
export function add(a: Quantity, b_o: QuantityInitParam) : Quantity
{
const b = new Quantity(b_o);
if (!isCompatible(a, b))
{
throwIncompatibleUnits();
}
if (a.isTemperature() && b.isTemperature())
{
throw new Error("Cannot add two temperatures");
}
else if (a.isTemperature())
{
return addTempDegrees(a, b);
}
else if (b.isTemperature())
{
return addTempDegrees(b, a);
}
return new Quantity({
scalar: a.scalar.add(b.to(a).scalar),
numerator: a.numerator,
denominator: a.denominator
});
}
export function sub(a: Quantity, b_o: QuantityInitParam) : Quantity
{
const b = new Quantity(b_o);
if (!isCompatible(a,b))
{
throwIncompatibleUnits();
}
if (a.isTemperature() && b.isTemperature())
{
return subtractTemperatures(a, b);
}
else if (a.isTemperature())
{
return subtractTempDegrees(a, b);
}
else if (b.isTemperature())
{
throw new Error("Cannot subtract a temperature from a differential degree unit");
}
return new Quantity({
scalar: a.scalar.sub(b.to(a).scalar),
numerator: a.numerator,
denominator: a.denominator
});
}
export function mul(a: Quantity, b_o: QuantityInitParam) : Quantity
{
if (typeguards.isNumber(b_o) || b_o instanceof Decimal)
{
return new Quantity({
scalar: a.scalar.mul(b_o),
numerator: a.numerator,
denominator: a.denominator
});
}
const b = new Quantity(b_o);
if ((a.isTemperature() || b.isTemperature()) && !(a.isUnitless() || b.isUnitless()))
{
throw new Error("Cannot multiply by temperatures");
}
// Quantities should be multiplied with same units if compatible, with base units else
let op1 = a,
op2 = b;
// so as not to confuse results, multiplication and division between temperature degrees will maintain original unit info in num/den
// multiplication and division between deg[CFRK] can never factor each other out, only themselves: "degK*degC/degC^2" == "degK/degC"
if (isCompatible(op1, op2) && op1.signature !== 400)
{
op2 = op2.to(op1);
}
let numden = cleanUnitNames(op1.numerator.concat(op2.numerator), op1.denominator.concat(op2.denominator));
return new Quantity({
scalar: op1.scalar.mul(op2.scalar),
numerator: numden[0],
denominator: numden[1]
});
}
export function div(a: Quantity, b_o: QuantityInitParam) : Quantity
{
if (typeguards.isNumber(b_o) || b_o instanceof Decimal)
{
return new Quantity({
"scalar": a.scalar.div(b_o),
"numerator": a.numerator,
"denominator": a.denominator
});
}
const b = new Quantity(b_o);
if (b.isTemperature())
{
throw new Error("Cannot divide with temperatures");
}
else if (a.isTemperature() && !b.isUnitless())
{
throw new Error("Cannot divide with temperatures");
}
// Quantities should be multiplied with same units if compatible, with base units else
let op1 = a,
op2 = b;
// so as not to confuse results, multiplication and division between temperature degrees will maintain original unit info in num/den
// multiplication and division between deg[CFRK] can never factor each other out, only themselves: "degK*degC/degC^2" == "degK/degC"
if (isCompatible(op1, op2) && op1.signature !== 400)
{
op2 = op2.to(op1);
}
let numden = cleanUnitNames(op1.numerator.concat(op2.denominator), op1.denominator.concat(op2.numerator));
return new Quantity({
scalar: op1.scalar.div(op2.scalar),
numerator: numden[0],
denominator: numden[1]
});
}
export function pow(a: Quantity, yy: number | string | Decimal) : Quantity
{
yy = new Decimal(yy);
if (!yy.isInt())
{
throw Error("Raising quantities to a fractional power not currently supported");
}
let num = a.numerator,
den = a.denominator;
for (let i = 1, len = yy.abs().toNumber(); i < len; i++)
{
num = num.concat(a.numerator);
den = den.concat(a.denominator);
}
// Invert units for negative powers
if (yy.s < 0)
{
let temp = num;
num = den;
den = temp;
}
let numden = cleanUnitNames(num, den);
return new Quantity({
scalar: a.scalar.pow(yy),
numerator: numden[0],
denominator: numden[1]
});
}
// Returns a Qty that is the inverse of Quantity a,
export function inverse(a: Quantity) : Quantity
{
if (a.isTemperature())
{
throw new Error("Cannot divide with temperatures");
}
if (a.scalar.eq(0))
{
throw new Error("Divide by zero");
}
return new Quantity({
scalar: new Decimal(1).div(a.scalar),
numerator: a.denominator,
denominator: a.numerator
});
}