@neutrium/quantity
Version:
The NeutriumJS unit conversion module.
145 lines (144 loc) • 5.13 kB
JavaScript
import { Decimal } from '@neutrium/math';
import { typeguards } from "@neutrium/utilities";
import { Quantity } from '../Quantity.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, b_o) {
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, b_o) {
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, b_o) {
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, b_o) {
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, yy) {
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) {
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
});
}