Merge branch 'feature-derived-units' of https://github.com/ericman314/mathjs into ericman314-feature-derived-units

Conflicts:
	examples/units.js
This commit is contained in:
jos 2015-08-05 21:04:53 +02:00
commit a94f0c6c5e
15 changed files with 1276 additions and 193 deletions

View File

@ -26,6 +26,7 @@ Example usage:
var a = math.unit(45, 'cm'); // Unit 450 mm
var b = math.unit('0.1 kilogram'); // Unit 100 gram
var c = math.unit('2 inch'); // Unit 2 inch
var d = math.unit('90 km/h'); // Unit 90 km/h
```
A `Unit` contains the following functions:
@ -72,7 +73,7 @@ d.toString(); // String "5.08 cm"
## Calculations
Basic operations `add`, `subtract`, `multiply`, and `divide` can be performed
Basic operations `add`, `subtract`, `multiply`, `divide`, and `pow` can be performed
on units. Trigonometric functions like `sin` support units with an angle as
argument.
@ -84,6 +85,12 @@ math.multiply(b, 2); // Unit 200 mm
var c = math.unit(45, 'deg'); // Unit 45 deg
math.cos(c); // Number 0.7071067811865476
// Kinetic energy of average sedan on highway
var d = math.unit('80 mi/h') // Unit 80 mi/h
var e = math.unit('2 tonne') // Unit 2 tonne
var f = math.multiply(0.5, math.multipy(math.pow(d, 2), e));
// 1.2790064742399996 MJ
```
The expression parser supports units too. This is described in the section about

View File

@ -325,8 +325,9 @@ parser.eval('number(a)'); // Error: 2 + i is no valid number
### Units
math.js supports units. Units can be used in basic arithmetic operations like
add and subtract, and units can be converted from one to another.
math.js supports units. Units can be used in the arithmetic operations
add, subtract, multiply, divide, and exponentiation.
Units can also be converted from one to another.
An overview of all available units can be found on the page
[Units](../datatypes/units.md).
@ -339,6 +340,7 @@ math.eval('5.4 kg'); // Unit, 5.4 kg
// convert a unit
math.eval('2 inch to cm'); // Unit, 5.08 cm
math.eval('20 celsius in fahrenheit'); // Unit, ~68 fahrenheit
math.eval('90 km/h to m/s'); // Unit, 25 m / s
// convert a unit to a number
// A second parameter with the unit for the exported number must be provided
@ -350,6 +352,7 @@ math.eval('3 inch + 2 cm'); // Unit, 3.7874 inch
math.eval('3 inch + 2 cm'); // Unit, 3.7874 inch
math.eval('12 seconds * 2'); // Unit, 24 seconds
math.eval('sin(45 deg)'); // Number, 0.7071067811865475
math.eval('9.81 m/s^2 * 5 s to mi/h') // Unit, 109.72172512527 mi / h
```

View File

@ -19,11 +19,16 @@ Electric current | ampere (A)
Temperature | kelvin (K), celsius (degC), fahrenheit (degF), rankine (degR)
Amount of substance | mole (mol)
Luminous intensity | candela (cd)
Force | newton (N), poundforce (lbf)
Force | newton (N), dyne (dyn), poundforce (lbf)
Energy | J, erg, Wh, BTU
Power | W, hp
Pressure | Pa, psi, atm
Binary | bit (b), byte (B)
Note that all relevant units can also be written in plural form, for example `5 meters` instead of `5 meter` or `10 seconds` instead of `10 second`.
Surface and volume units can alternatively be expressed in terms of length units raised to a power, for example `100 in^2` instead of `100 sqin`.
## Prefixes
The following decimal prefixes are available.

View File

@ -21,10 +21,14 @@ print(a); // 450 mm
print(b); // 100 mm
console.log();
// units can be added, subtracted, and multiplied or divided by numbers
// units can be added, subtracted, and multiplied or divided by numbers and by other units
console.log('perform operations');
print(math.add(a, b)); // 0.55 m
print(math.multiply(b, 2)); // 200 mm
print(math.divide(math.unit('1 m'), math.unit('1 s')));
// 1 m / s
print(math.pow(math.unit('12 in'), 3));
// 1728 in^3
console.log();
// units can be converted to a specific type, or to a number
@ -32,14 +36,50 @@ console.log('convert to another type or to a number');
print(b.to('cm')); // 10 cm Alternatively: math.to(b, 'cm')
print(math.to(b, 'inch')); // 3.937 inch
print(b.toNumber('cm')); // 10
print(math.number(b, 'cm')); // 10
console.log();
// the expression parser supports units too
console.log('parse expressions');
print(math.eval('2 inch to cm')); // 5.08 cm
print(math.eval('cos(45 deg)')); // 0.70711
print(math.eval('90 km/h to m/s')); // 25 m / s
console.log();
// convert a unit to a number
// A second parameter with the unit for the exported number must be provided
print(math.eval('number(5 cm, mm)')); // number, 50
console.log();
// simplify units
console.log('simplify units');
print(math.eval('100000 N / m^2')); // 100 kPa
print(math.eval('9.81 m/s^2 * 100 kg * 40 m')); // 39.24 kJ
console.log();
// example engineering calculations
console.log('compute molar volume of ideal gas at 65 C, 14.7 psi in L/mol');
var Rg = math.unit('8.314 N m / mol K');
var P = math.unit('14.7 psi');
var T = math.unit('65 degF');
var v = math.divide(math.multiply(Rg, T), P);
console.log('gas constant (Rg) = ');
print(Rg);
console.log('P = ');
print(P);
console.log('T = ');
print(T);
console.log('v = Rg * T / P =');
print(math.to(v, 'L/mol'));
console.log();
console.log('compute speed of fluid flowing out of hole in a container');
var g = math.unit('9.81 m / s^2');
var h = math.unit('1 m');
console.log('g = ');
var v = math.pow(math.multiply(2, math.multiply(g, h)), 0.5);
print(g);
console.log('h = ');
print(h);
console.log('v = (2 g h) ^ 0.5 =');
print(v);

View File

@ -33,7 +33,17 @@ function factory(type, config, load, typed) {
var res = x.clone();
res.value = ((res.value === null) ? res._normalize(1) : res.value) / y;
return res;
},
'number, Unit': function (x, y) {
var xUnit = new type.Unit(x);
return xUnit.divide(y);
},
'Unit, Unit': function (x, y) {
return x.divide(y);
}
});
/**

View File

@ -46,7 +46,12 @@ function factory(type, config, load, typed) {
var res = x.clone();
res.value = (res.value === null) ? res._normalize(y) : (res.value * y);
return res;
},
'Unit, Unit': function (x, y) {
return x.multiply(y);
}
});
return multiplyScalar;

View File

@ -76,7 +76,12 @@ function factory (type, config, load, typed) {
'Matrix, BigNumber': function (x, y) {
return _powMatrix(x, y.toNumber());
},
'Unit, number': function (x, y) {
return x.pow(y);
}
});
/**
@ -156,6 +161,8 @@ function factory (type, config, load, typed) {
return matrix(_powArray(x.valueOf(), y));
}
pow.toTex = '\\left(${args[0]}\\right)' + latex.operators['pow'] + '{${args[1]}}';
return pow;

File diff suppressed because it is too large Load Diff

View File

@ -123,6 +123,23 @@ describe('divide', function() {
assert.equal(divide(math.unit('m'), 2).toString(), '500 mm');
});
it('should divide a number by a unit', function() {
assert.equal(divide(20, math.unit('4 N s')).toString(), '5 N^-1 s^-1');
});
it('should divide two units', function() {
assert.equal(divide(math.unit('75 mi/h'), math.unit('40 mi/gal')).to('gal/minute').toString(), '0.03125 gal / minute');
});
it('should divide one valued unit by a valueless unit and vice-versa', function() {
assert.equal(divide(math.unit('4 gal'), math.unit('L')).format(5), '15.142');
assert.equal(divide(math.unit('gal'), math.unit('4 L')).format(3), '0.946');
});
it('should divide (but not simplify) two valueless units', function() {
assert.equal(divide(math.unit('gal'), math.unit('L')).toString(), 'gal / L');
});
// TODO: divide units by a bignumber
it('should divide units by a big number', function() {
//assert.equal(divide(math.unit('5 m'), bignumber(10)).toString(), '500 mm'); // TODO
@ -160,6 +177,8 @@ describe('divide', function() {
assert.throws(function () {divide(a, [[1]])});
});
/*
// These are supported now --ericman314
it('should throw an error if dividing a number by a unit', function() {
assert.throws(function () {divide(10, math.unit('5 m')).toString()});
});
@ -167,6 +186,7 @@ describe('divide', function() {
it('should throw an error if dividing a unit by a non-number', function() {
assert.throws(function () {divide(math.unit('5 m'), math.unit('5cm')).toString()});
});
*/
it('should throw an error if there\'s wrong number of arguments', function() {
assert.throws(function () {divide(2,3,4); });

View File

@ -52,9 +52,16 @@ describe('dotDivide', function() {
assert.equal(dotDivide(math.unit('5 m'), 10).toString(), '500 mm');
});
it('should divide a number by a unit', function() {
assert.equal(dotDivide(10, math.unit('5 m')).toString(), '2 m^-1');
});
/*
// This is supported not --ericman314
it('should throw an error if dividing a number by a unit', function() {
assert.throws(function () {dotDivide(10, math.unit('5 m')).toString();});
});
*/
describe('Array', function () {

View File

@ -154,6 +154,22 @@ describe('multiply', function() {
assert.equal(multiply(unit('inch'), 2).toString(), '2 inch');
});
it('should multiply two units correctly', function() {
assert.equal(multiply(unit('2 m'), unit('4 m')).toString(), '8 m^2');
assert.equal(multiply(unit('2 ft'), unit('4 ft')).toString(), '8 ft^2');
assert.equal(multiply(unit('65 mi/h'), unit('2 h')).to('mi').toString(), '130 mi');
assert.equal(multiply(unit('2 L'), unit('1 s^-1')).toString(), '2 L / s');
assert.equal(multiply(unit('2 m/s'), unit('0.5 s/m')).toString(), '1');
});
it('should multiply valueless units correctly', function() {
assert.equal(multiply(unit('m'), unit('4 m')).toString(), '4 m^2');
assert.equal(multiply(unit('ft'), unit('4 ft')).format(5), '4 ft^2');
assert.equal(multiply(unit('65 mi/h'), unit('h')).to('mi').toString(), '65 mi');
assert.equal(multiply(unit('2 L'), unit('s^-1')).toString(), '2 L / s');
assert.equal(multiply(unit('m/s'), unit('h/m')).toString(), '(m h) / (s m)');
});
// TODO: cleanup once decided to not downgrade BigNumber to number
it.skip('should multiply a bignumber and a unit correctly', function() {
assert.equal(multiply(bignumber(2), unit('5 mm')).toString(), '10 mm');
@ -172,12 +188,15 @@ describe('multiply', function() {
assert.equal(multiply(unit('inch'), bignumber(2)).toString(), '2 inch');
});
it('should throw an error in case of unit non-numeric argument', function() {
assert.throws(function () {multiply(math.unit('5cm'), math.unit('4cm'));}, /TypeError: Unexpected type/);
// Multiplying two units is supported now --ericman314
//assert.throws(function () {multiply(math.unit('5cm'), math.unit('4cm'));}, /TypeError: Unexpected type/);
assert.throws(function () {multiply(math.unit('5cm'), math.complex('2+3i'));}, /TypeError: Unexpected type/);
assert.throws(function () {multiply(math.complex('2+3i'), math.unit('5cm'));}, /TypeError: Unexpected type/);
});
it('should throw an error if used with strings', function() {
assert.throws(function () {multiply("hello", "world");});
assert.throws(function () {multiply("hello", 2);});

View File

@ -152,8 +152,27 @@ describe('pow', function() {
approx.deepEqual(pow(complex(0, 2), math.bignumber(2)), complex(-4, 0));
});
it('should throw an error if used with a unit', function() {
assert.throws(function () {pow(unit('5cm'), 2)});
it('should correctly calculate unit ^ number', function() {
assert.equal(pow(unit('4 N'), 2).toString(), "16 N^2");
assert.equal(pow(unit('0.25 m/s'), -0.5).toString(), "2 s^0.5 / m^0.5");
assert.equal(pow(unit('123 hogshead'), 0).toString(), "1");
});
it('should return a cloned value and not affect the argument', function() {
var unit1 = unit('2 m');
var unit2 = pow(unit1, 2);
assert.equal(unit1.toString(), '2 m');
assert.equal(unit2.toString(), '4 m^2');
});
it('should return a valuelessUnit when calculating valuelessUnit ^ number', function() {
assert.equal(pow(unit('kg^0.5 m^0.5 s^-1'), 2).toString(), "(kg m) / s^2");
});
it('should throw an error when doing number ^ unit', function() {
// This is supported now --ericman314
//assert.throws(function () {pow(unit('5cm'), 2)});
assert.throws(function () {pow(2, unit('5cm'))});
});

View File

@ -60,7 +60,7 @@ describe('to', function() {
it('should throw an error if converting between incompatible units', function() {
assert.throws(function () {math.to(unit('20 kg'), unit('cm'));});
assert.throws(function () {math.to(unit('20 celsius'), unit('litre'));});
assert.throws(function () {math.to(unit('5 cm'), unit('2 m'));});
assert.throws(function () {math.to(unit('5 cm'), unit('2 m^2'));});
});
it('should throw an error if called with a wrong number of arguments', function() {
@ -74,7 +74,7 @@ describe('to', function() {
it('should throw an error if called with a number', function() {
assert.throws(function () {math.to(5, unit('m'));}, TypeError);
assert.throws(function () {math.to(unit('5cm'), 2);}, /SyntaxError: Unknown unit "2"/);
assert.throws(function () {math.to(unit('5cm'), 2);}, /SyntaxError: "2" contains no units/);
});
it('should throw an error if called with a string', function() {

View File

@ -10,33 +10,39 @@ describe('unit', function() {
it('should create unit correctly', function() {
var unit1 = new Unit(5000, 'cm');
assert.equal(unit1.value, 50);
assert.equal(unit1.unit.name, 'm');
assert.equal(unit1.units[0].unit.name, 'm');
unit1 = new Unit(5, 'kg');
assert.equal(unit1.value, 5);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.units[0].unit.name, 'g');
unit1 = new Unit(null, 'kg');
assert.equal(unit1.value, null);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.units[0].unit.name, 'g');
unit1 = new Unit(9.81, "kg m/s^2");
assert.equal(unit1.value, 9.81);
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[1].unit.name, 'm');
assert.equal(unit1.units[2].unit.name, 's');
});
it('should create square meter correctly', function() {
var unit1 = new Unit(0.000001, 'km2');
assert.equal(unit1.value, 1);
assert.equal(unit1.unit.name, 'm2');
assert.equal(unit1.units[0].unit.name, 'm2');
});
it('should create cubic meter correctly', function() {
var unit1 = new Unit(0.000000001, 'km3');
assert.equal(unit1.value, 1);
assert.equal(unit1.unit.name, 'm3');
assert.equal(unit1.units[0].unit.name, 'm3');
});
it('should ignore properties on Object.prototype', function() {
Object.prototype.foo = Unit.UNITS['meter'];
assert.throws(function () {new Unit(1, 'foo')}, /Unknown unit/);
assert.throws(function () {new Unit(1, 'foo')}, /Unit "foo" not found/);
delete Object.prototype.foo;
});
@ -54,6 +60,16 @@ describe('unit', function() {
assert.throws(function () { new Unit(0, 3); });
});
it('should flag unit as already simplified', function() {
unit1 = new Unit(9.81, "kg m/s^2");
assert.equal(unit1.isUnitListSimplified, true);
assert.equal(unit1.toString(), "9.81 (kg m) / s^2");
unit1 = new Unit(null, "kg m/s^2");
assert.equal(unit1.isUnitListSimplified, true);
assert.equal(unit1.toString(), "(kg m) / s^2");
});
});
describe('isValuelessUnit', function() {
@ -90,6 +106,7 @@ describe('unit', function() {
it('should test whether a unit has a certain base unit', function() {
assert.equal(new Unit(5, 'cm').hasBase(Unit.BASE_UNITS.ANGLE), false);
assert.equal(new Unit(5, 'cm').hasBase(Unit.BASE_UNITS.LENGTH), true);
assert.equal(new Unit(5, 'kg m / s ^ 2').hasBase(Unit.BASE_UNITS.FORCE), true);
});
});
@ -99,6 +116,8 @@ describe('unit', function() {
it('should test whether two units have the same base unit', function() {
assert.equal(new Unit(5, 'cm').equalBase(new Unit(10, 'm')), true);
assert.equal(new Unit(5, 'cm').equalBase(new Unit(10, 'kg')), false);
assert.equal(new Unit(5, 'N').equalBase(new Unit(10, 'kg m / s ^ 2')), true);
assert.equal(new Unit(8.314, 'J / mol K').equalBase(new Unit(0.02366, 'ft^3 psi / mol degF')), true);
});
});
@ -109,6 +128,9 @@ describe('unit', function() {
assert.equal(new Unit(100, 'cm').equals(new Unit(1, 'm')), true);
assert.equal(new Unit(100, 'cm').equals(new Unit(2, 'm')), false);
assert.equal(new Unit(100, 'cm').equals(new Unit(1, 'kg')), false);
assert.equal(new Unit(100, 'ft lbf').equals(new Unit(1200, 'in lbf')), true);
assert.equal(new Unit(100, 'N').equals(new Unit(100, 'kg m / s ^ 2')), true);
assert.equal(new Unit(100, 'N').equals(new Unit(100, 'kg m / s')), false);
});
});
@ -131,6 +153,11 @@ describe('unit', function() {
assert(u5 !== u6);
assert.deepEqual(u5, u6);
var u7 = new Unit(8.314, 'kg m^2 / s^2 K mol');
var u8 = u7.clone();
assert(u7 !== u8);
assert.deepEqual(u7, u8);
});
});
@ -141,12 +168,18 @@ describe('unit', function() {
approx.equal(u.toNumber('mm'), 50000);
approx.equal(new Unit(5.08, 'cm').toNumber('inch'), 2);
approx.equal(new Unit(101325, 'N/m^2').toNumber('lbf/in^2'), 14.6959487763741);
});
it ('should convert a unit with fixed prefix to a number', function () {
var u1 = new Unit(5000, 'cm');
var u2 = u1.to('km');
approx.equal(u2.toNumber('mm'), 50000);
var u1 = new Unit(981, 'cm/s^2');
var u2 = u1.to('km/ms^2');
approx.equal(u2.toNumber('m/s^2'), 9.81);
});
});
@ -155,81 +188,143 @@ describe('unit', function() {
it ('should convert a unit to a fixed unitName', function () {
var u1 = new Unit(5000, 'cm');
assert.equal(u1.value, 50);
assert.equal(u1.unit.name, 'm');
assert.equal(u1.prefix.name, 'c');
assert.equal(u1.units[0].unit.name, 'm');
assert.equal(u1.units[0].prefix.name, 'c');
assert.equal(u1.fixPrefix, false);
var u2 = u1.to('inch');
assert.notStrictEqual(u1, u2); // u2 must be a clone
assert.equal(u2.value, 50);
assert.equal(u2.unit.name, 'inch');
assert.equal(u2.prefix.name, '');
assert.equal(u2.units[0].unit.name, 'inch');
assert.equal(u2.units[0].prefix.name, '');
assert.equal(u2.fixPrefix, true);
var u3 = new Unit(299792.458, 'km/s');
assert.equal(u3.value, 299792458);
assert.equal(u3.units[0].unit.name, 'm');
assert.equal(u3.units[1].unit.name, 's');
assert.equal(u3.units[0].prefix.name, 'k');
assert.equal(u3.fixPrefix, false);
var u4 = u3.to('mi/h');
assert.notStrictEqual(u3, u4); // u4 must be a clone
assert.equal(u4.value, 299792458);
assert.equal(u4.units[0].unit.name, 'mi');
assert.equal(u4.units[1].unit.name, 'h');
assert.equal(u4.units[0].prefix.name, '');
assert.equal(u4.fixPrefix, true);
});
it ('should convert a unit to a fixed unit', function () {
var u1 = new Unit(5000, 'cm');
assert.equal(u1.value, 50);
assert.equal(u1.unit.name, 'm');
assert.equal(u1.prefix.name, 'c');
assert.equal(u1.units[0].unit.name, 'm');
assert.equal(u1.units[0].prefix.name, 'c');
assert.equal(u1.fixPrefix, false);
var u2 = u1.to(new Unit(null, 'km'));
assert.notStrictEqual(u1, u2); // u2 must be a clone
assert.equal(u2.value, 50);
assert.equal(u2.unit.name, 'm');
assert.equal(u2.prefix.name, 'k');
assert.equal(u2.units[0].unit.name, 'm');
assert.equal(u2.units[0].prefix.name, 'k');
assert.equal(u2.fixPrefix, true);
var u1 = new Unit(5000, 'cm/s');
assert.equal(u1.value, 50);
assert.equal(u1.units[0].unit.name, 'm');
assert.equal(u1.units[1].unit.name, 's');
assert.equal(u1.units[0].prefix.name, 'c');
assert.equal(u1.fixPrefix, false);
var u2 = u1.to(new Unit(null, 'km/h'));
assert.notStrictEqual(u1, u2); // u2 must be a clone
assert.equal(u2.value, 50);
assert.equal(u2.units[0].unit.name, 'm');
assert.equal(u2.units[1].unit.name, 'h');
assert.equal(u2.units[0].prefix.name, 'k');
assert.equal(u2.fixPrefix, true);
});
it ('should convert a valueless unit', function () {
var u1 = new Unit(null, 'm');
assert.equal(u1.value, null);
assert.equal(u1.unit.name, 'm');
assert.equal(u1.prefix.name, '');
assert.equal(u1.units[0].unit.name, 'm');
assert.equal(u1.units[0].prefix.name, '');
assert.equal(u1.fixPrefix, false);
var u2 = u1.to(new Unit(null, 'cm'));
assert.notStrictEqual(u1, u2); // u2 must be a clone
assert.equal(u2.value, 1); // u2 must have a value
assert.equal(u2.unit.name, 'm');
assert.equal(u2.prefix.name, 'c');
assert.equal(u2.units[0].unit.name, 'm');
assert.equal(u2.units[0].prefix.name, 'c');
assert.equal(u2.fixPrefix, true);
var u1 = new Unit(null, 'm/s');
assert.equal(u1.value, null);
assert.equal(u1.units[0].unit.name, 'm');
assert.equal(u1.units[1].unit.name, 's');
assert.equal(u1.units[0].prefix.name, '');
assert.equal(u1.fixPrefix, false);
var u2 = u1.to(new Unit(null, 'cm/s'));
assert.notStrictEqual(u1, u2); // u2 must be a clone
assert.equal(u2.value, 1); // u2 must have a value
assert.equal(u2.units[0].unit.name, 'm');
assert.equal(u2.units[1].unit.name, 's');
assert.equal(u2.units[0].prefix.name, 'c');
assert.equal(u2.fixPrefix, true);
});
it ('should convert a binary prefixes (1)', function () {
var u1 = new Unit(1, 'Kib');
assert.equal(u1.value, 1024);
assert.equal(u1.unit.name, 'b');
assert.equal(u1.prefix.name, 'Ki');
assert.equal(u1.units[0].unit.name, 'b');
assert.equal(u1.units[0].prefix.name, 'Ki');
assert.equal(u1.fixPrefix, false);
var u2 = u1.to(new Unit(null, 'b'));
assert.notStrictEqual(u1, u2); // u2 must be a clone
assert.equal(u2.value, 1024); // u2 must have a value
assert.equal(u2.unit.name, 'b');
assert.equal(u2.prefix.name, '');
assert.equal(u2.units[0].unit.name, 'b');
assert.equal(u2.units[0].prefix.name, '');
assert.equal(u2.fixPrefix, true);
var u1 = new Unit(1, 'Kib/s');
assert.equal(u1.value, 1024);
assert.equal(u1.units[0].unit.name, 'b');
assert.equal(u1.units[1].unit.name, 's');
assert.equal(u1.units[0].prefix.name, 'Ki');
assert.equal(u1.fixPrefix, false);
var u2 = u1.to(new Unit(null, 'b/s'));
assert.notStrictEqual(u1, u2); // u2 must be a clone
assert.equal(u2.value, 1024); // u2 must have a value
assert.equal(u2.units[0].unit.name, 'b');
assert.equal(u2.units[1].unit.name, 's');
assert.equal(u2.units[0].prefix.name, '');
assert.equal(u2.fixPrefix, true);
});
it ('should convert a binary prefixes (2)', function () {
var u1 = new Unit(1, 'kb');
assert.equal(u1.value, 1000);
assert.equal(u1.unit.name, 'b');
assert.equal(u1.prefix.name, 'k');
assert.equal(u1.units[0].unit.name, 'b');
assert.equal(u1.units[0].prefix.name, 'k');
assert.equal(u1.fixPrefix, false);
var u2 = u1.to(new Unit(null, 'b'));
assert.notStrictEqual(u1, u2); // u2 must be a clone
assert.equal(u2.value, 1000); // u2 must have a value
assert.equal(u2.unit.name, 'b');
assert.equal(u2.prefix.name, '');
assert.equal(u2.units[0].unit.name, 'b');
assert.equal(u2.units[0].prefix.name, '');
assert.equal(u2.fixPrefix, true);
});
it ('should throw an error when converting to an incompatible unit', function () {
var u1 = new Unit(5000, 'cm');
assert.throws(function () {u1.to('kg')}, /Units do not match/);
var u1 = new Unit(5000, 'N s');
assert.throws(function () {u1.to('kg^5 / s')}, /Units do not match/);
});
it ('should throw an error when converting to a unit having a value', function () {
@ -249,6 +344,11 @@ describe('unit', function() {
assert.equal(new Unit(5000, 'cm').toString(), '50 m');
assert.equal(new Unit(5, 'kg').toString(), '5 kg');
assert.equal(new Unit(2/3, 'm').toString(), '0.6666666666666666 m');
assert.equal(new Unit(5, 'N').toString(), '5 N');
assert.equal(new Unit(5, 'kg^1.0e0 m^1.0e0 s^-2.0e0').toString(), '5 (kg m) / s^2');
assert.equal(new Unit(5, 's^-2').toString(), '5 s^-2');
assert.equal(new Unit(5, 'm / s ^ 2').toString(), '5 m / s^2');
assert.equal(new Unit(null, 'kg m^2 / s^2 mol').toString(), '(kg m^2) / (s^2 mol)');
});
it('should render with the best prefix', function() {
@ -266,6 +366,60 @@ describe('unit', function() {
assert.equal(new Unit(1000 ,'m').toString(), '1 km');
});
});
describe('simplifyUnitListLazy', function() {
it('should not simplify units created with new Unit()', function() {
var unit1 = new Unit(10, "kg m/s^2");
assert.equal(unit1.units[0].unit.name, "g");
assert.equal(unit1.units[1].unit.name, "m");
assert.equal(unit1.units[2].unit.name, "s");
assert.equal(unit1.toString(), "10 (kg m) / s^2");
});
it('should only simplify units with values', function() {
var unit1 = new Unit(null, "kg m mol / s^2 mol");
unit1.isUnitListSimplified = false;
unit1.simplifyUnitListLazy();
assert.equal(unit1.toString(), "(kg m mol) / (s^2 mol)");
unit1 = math.multiply(unit1, 1);
assert.equal(unit1.toString(), "1 N");
});
it('should simplify units resulting from multiply/divide/power functions only when formatting for output', function() {
var unit1 = new Unit(2, "kg");
var unit2 = new Unit(5, "m/s^2");
var unit3 = math.multiply(unit1, unit2);
assert.equal(unit3.units[0].unit.name, "g");
assert.equal(unit3.units[1].unit.name, "m");
assert.equal(unit3.units[2].unit.name, "s");
assert.equal(unit3.toString(), "10 N"); // Triggers simplification
assert.equal(unit3.units[0].unit.name, "N");
});
it('should simplify units according to chosen unit system', function() {
var unit1 = new Unit(10, "N");
Unit.setUnitSystem('us');
unit1.isUnitListSimplified = false;
assert.equal(unit1.toString(), "2.248089430997105 lbf");
assert.equal(unit1.units[0].unit.name, "lbf");
Unit.setUnitSystem('cgs');
unit1.isUnitListSimplified = false;
assert.equal(unit1.format(2), "1 Mdyn");
assert.equal(unit1.units[0].unit.name, "dyn");
});
it('should correctly simplify units when unit system is "auto"', function() {
Unit.setUnitSystem('auto');
var unit1 = new Unit(5, "lbf min / s");
unit1.isUnitListSimplified = false;
assert.equal(unit1.toString(), "300 lbf");
});
});
describe('valueOf', function() {
@ -274,6 +428,9 @@ describe('unit', function() {
assert.strictEqual(new Unit(5000, 'cm').valueOf(), '50 m');
assert.strictEqual(new Unit(5, 'kg').valueOf(), '5 kg');
assert.strictEqual(new Unit(2/3, 'm').valueOf(), '0.6666666666666666 m');
assert.strictEqual(new Unit(5, 'N').valueOf(), '5 N');
assert.strictEqual(new Unit(5, 'kg^1.0e0 m^1.0e0 s^-2.0e0').valueOf(), '5 (kg m) / s^2');
assert.strictEqual(new Unit(5, 's^-2').valueOf(), '5 s^-2');
});
});
@ -285,6 +442,8 @@ describe('unit', function() {
{'mathjs': 'Unit', value: 5, unit: 'cm', fixPrefix: false});
assert.deepEqual(new Unit(5, 'cm').to('mm').toJSON(),
{'mathjs': 'Unit', value: 50, unit: 'mm', fixPrefix: true});
assert.deepEqual(new Unit(5, 'kN').to('kg m s ^ -2').toJSON(),
{'mathjs': 'Unit', value: 5000, unit: '(kg m) / s^2', fixPrefix: true});
});
it('fromJSON', function () {
@ -297,6 +456,11 @@ describe('unit', function() {
var u4 = Unit.fromJSON({'mathjs': 'Unit', value: 50, unit: 'mm', fixPrefix: true});
assert.ok(u4 instanceof Unit);
assert.deepEqual(u4, u3);
var u5 = new Unit(5, 'kN').to('kg m/s^2');
var u6 = Unit.fromJSON({'mathjs': 'Unit', value: 5000, unit: 'kg m s^-2', fixPrefix: true});
assert.ok(u6 instanceof Unit);
assert.deepEqual(u5, u6);
});
});
@ -312,11 +476,13 @@ describe('unit', function() {
it('should format a unit without value', function() {
assert.equal(new Unit(null, 'cm').format(), 'cm');
assert.equal(new Unit(null, 'm').format(), 'm');
assert.equal(new Unit(null, 'kg m/s').format(), '(kg m) / s');
});
it('should format a unit with fixed prefix and without value', function() {
assert.equal(new Unit(null, 'km').to('cm').format(), '1e+5 cm');
assert.equal(new Unit(null, 'inch').to('cm').format(), '2.54 cm');
assert.equal(new Unit(null, 'N/m^2').to('lbf/inch^2').format(5), '1.4504e-4 lbf / inch^2');
});
it('should ignore properties in Object.prototype when finding the best prefix', function() {
@ -336,91 +502,189 @@ describe('unit', function() {
unit1 = Unit.parse('5kg');
assert.equal(unit1.value, 5);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.prefix.name, 'k');
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[0].prefix.name, 'k');
unit1 = Unit.parse('5 kg');
assert.equal(unit1.value, 5);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.prefix.name, 'k');
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[0].prefix.name, 'k');
unit1 = Unit.parse(' 5 kg ');
assert.equal(unit1.value, 5);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.prefix.name, 'k');
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[0].prefix.name, 'k');
unit1 = Unit.parse('5e-3kg');
assert.equal(unit1.value, 0.005);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.prefix.name, 'k');
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[0].prefix.name, 'k');
unit1 = Unit.parse('5e+3kg');
assert.equal(unit1.value, 5000);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.prefix.name, 'k');
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[0].prefix.name, 'k');
unit1 = Unit.parse('5e3kg');
assert.equal(unit1.value, 5000);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.prefix.name, 'k');
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[0].prefix.name, 'k');
unit1 = Unit.parse('-5kg');
assert.equal(unit1.value, -5);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.prefix.name, 'k');
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[0].prefix.name, 'k');
unit1 = Unit.parse('+5kg');
assert.equal(unit1.value, 5);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.prefix.name, 'k');
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[0].prefix.name, 'k');
unit1 = Unit.parse('.5kg');
assert.equal(unit1.value, .5);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.prefix.name, 'k');
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[0].prefix.name, 'k');
unit1 = Unit.parse('-5mg');
assert.equal(unit1.value, -0.000005);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.prefix.name, 'm');
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[0].prefix.name, 'm');
unit1 = Unit.parse('5.2mg');
approx.equal(unit1.value, 0.0000052);
assert.equal(unit1.unit.name, 'g');
assert.equal(unit1.prefix.name, 'm');
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[0].prefix.name, 'm');
unit1 = Unit.parse('300 kg/minute');
approx.equal(unit1.value, 5);
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[1].unit.name, 'minute');
assert.equal(unit1.units[0].prefix.name, 'k');
unit1 = Unit.parse('981 cm/s^2');
approx.equal(unit1.value, 9.81);
assert.equal(unit1.units[0].unit.name, 'm');
assert.equal(unit1.units[1].unit.name, 's');
assert.equal(unit1.units[1].power, -2);
assert.equal(unit1.units[0].prefix.name, 'c');
unit1 = Unit.parse('8.314 kg m^2 / s^2 / K / mol');
approx.equal(unit1.value, 8.314);
assert.equal(unit1.units[0].unit.name, 'g');
assert.equal(unit1.units[1].unit.name, 'm');
assert.equal(unit1.units[2].unit.name, 's');
assert.equal(unit1.units[3].unit.name, 'K');
assert.equal(unit1.units[4].unit.name, 'mol');
assert.equal(unit1.units[0].power, 1);
assert.equal(unit1.units[1].power, 2);
assert.equal(unit1.units[2].power, -2);
assert.equal(unit1.units[3].power, -1);
assert.equal(unit1.units[4].power, -1);
assert.equal(unit1.units[0].prefix.name, 'k');
unit1 = Unit.parse('5exabytes');
approx.equal(unit1.value, 4e19);
assert.equal(unit1.units[0].unit.name, 'bytes');
});
it('should return null when parsing an invalid unit', function() {
assert.equal(Unit.parse('.meter'), null);
assert.equal(Unit.parse('5e'), null);
assert.equal(Unit.parse('5e. meter'), null);
assert.equal(Unit.parse('5e1.3 meter'), null);
assert.equal(Unit.parse('5'), null);
assert.equal(Unit.parse(''), null);
it('should return null (update: throw exception --ericman314) when parsing an invalid unit', function() {
// I'm worried something else will break if Unit.parse throws an exception instead of returning null???? --ericman314
assert.throws(function () {Unit.parse('.meter')}, /Could not parse/);
assert.throws(function () {Unit.parse('5e')}, /Unit "e" not found/);
assert.throws(function () {Unit.parse('5e.')}, /Unit "e" not found/);
assert.throws(function () {Unit.parse('5e1.3')}, /Could not parse/);
assert.throws(function () {Unit.parse('5')}, /contains no units/);
assert.throws(function () {Unit.parse('')}, /contains no units/);
assert.throws(function () {Unit.parse('meter.')}, /Could not parse/);
assert.throws(function () {Unit.parse('meter/')}, /Trailing characters/);
assert.throws(function () {Unit.parse('/meter')}, /Could not parse/);
assert.throws(function () {Unit.parse('45 kg 34 m')}, /Could not parse/);
// assert.equal(Unit.parse('.meter'), null);
// assert.equal(Unit.parse('5e'), null);
// assert.equal(Unit.parse('5e. meter'), null);
// assert.equal(Unit.parse('5e1.3 meter'), null);
// assert.equal(Unit.parse('5'), null);
// assert.equal(Unit.parse(''), null);
// assert.equal(Unit.parse('meter.'), null);
// assert.equal(Unit.parse('meter/'), null);
// assert.equal(Unit.parse('/meter'), null);
});
it('should return null when parsing an invalid type of argument', function() {
assert.equal(Unit.parse(123), null);
assert.throws(function () {Unit.parse(123)}, /Invalid argument in Unit.parse. Valid types are/);
// assert.equal(Unit.parse(123), null);
});
});
describe('_isDerived', function() {
it('should return the correct value', function () {
assert.equal(Unit.parse('34 kg')._isDerived(), false);
assert.equal(Unit.parse('34 kg/s')._isDerived(), true);
assert.equal(Unit.parse('34 kg^2')._isDerived(), true);
assert.equal(Unit.parse('34 N')._isDerived(), false);
assert.equal(Unit.parse('34 kg m / s^2')._isDerived(), true);
var unit1 = Unit.parse('34 kg m / s^2');
assert.equal(unit1._isDerived(), true);
unit1.isUnitListSimplified = false;
unit1.simplifyUnitListLazy();
assert.equal(unit1._isDerived(), false);
});
});
describe('multiply, divide, and pow', function() {
it('should flag the unit as requiring simplification', function() {
var unit1 = new Unit(10, 'kg');
var unit2 = new Unit(9.81, 'm/s^2');
assert.equal(unit1.multiply(unit2).isUnitListSimplified, false);
assert.equal(unit1.divide(unit2).isUnitListSimplified, false);
assert.equal(unit1.pow(2).isUnitListSimplified, false);
});
it('should retain the units of their operands without simplifying', function() {
var unit1 = new Unit(10, "N/s");
var unit2 = new Unit(10, "h");
var unitM = unit1.multiply(unit2);
assert.equal(unitM.units[0].unit.name, 'N');
assert.equal(unitM.units[1].unit.name, 's');
assert.equal(unitM.units[2].unit.name, 'h');
var unit3 = new Unit(14.7, "lbf");
var unit4 = new Unit(1, "in in");
var unitD = unit3.divide(unit4);
assert.equal(unitD.units[0].unit.name, 'lbf');
assert.equal(unitD.units[1].unit.name, 'in');
assert.equal(unitD.units[2].unit.name, 'in');
var unit5 = new Unit(1, "N h/s");
var unitP = unit5.pow(-3.5);
assert.equal(unitP.units[0].unit.name, 'N');
assert.equal(unitP.units[1].unit.name, 'h');
assert.equal(unitP.units[2].unit.name, 's');
});
});
describe('plurals', function() {
it('should support plurals', function () {
var unit1 = new Unit(5, 'meters');
assert.equal(unit1.value, 5);
assert.equal(unit1.unit.name, 'meters');
assert.equal(unit1.prefix.name, '');
assert.equal(unit1.units[0].unit.name, 'meters');
assert.equal(unit1.units[0].prefix.name, '');
var unit2 = new Unit(5, 'kilometers');
assert.equal(unit2.value, 5000);
assert.equal(unit2.unit.name, 'meters');
assert.equal(unit2.prefix.name, 'kilo');
assert.equal(unit2.units[0].unit.name, 'meters');
assert.equal(unit2.units[0].prefix.name, 'kilo');
var unit3 = new Unit(5, 'inches');
approx.equal(unit3.value, 0.127);
assert.equal(unit3.unit.name, 'inches');
assert.equal(unit3.prefix.name, '');
assert.equal(unit3.units[0].unit.name, 'inches');
assert.equal(unit3.units[0].prefix.name, '');
var unit3 = new Unit(9.81, 'meters/second^2');
approx.equal(unit3.value, 9.81);
assert.equal(unit3.units[0].unit.name, 'meters');
assert.equal(unit3.units[0].prefix.name, '');
});
});
@ -430,15 +694,15 @@ describe('unit', function() {
var unit1 = new Unit(5, 'lt');
assert.equal(unit1.value, 5e-3);
assert.equal(unit1.unit.name, 'l');
assert.equal(unit1.prefix.name, '');
assert.equal(unit1.units[0].unit.name, 'l');
assert.equal(unit1.units[0].prefix.name, '');
var unit2 = new Unit(1, 'lb');
assert.equal(unit2.value, 453.59237e-3);
assert.equal(unit2.unit.name, 'lbm');
assert.equal(unit2.prefix.name, '');
assert.equal(unit2.units[0].unit.name, 'lbm');
assert.equal(unit2.units[0].prefix.name, '');
});
});
// TODO: test the value of each of the available units...
});
});

View File

@ -35,7 +35,7 @@ describe('unit', function() {
});
it('should throw an error if called with a number', function() {
assert.throws(function () {unit(2)}, /SyntaxError: String "2" is no valid unit/);
assert.throws(function () {unit(2)}, /SyntaxError: "2" contains no units/);
});
it('should throw an error if called with a complex', function() {