UNPKG

93.6 kBJavaScriptView Raw
1'use strict'
2
3const endsWith = require('../../utils/string').endsWith
4const clone = require('../../utils/object').clone
5const constants = require('../../utils/bignumber/constants')
6
7function factory (type, config, load, typed, math) {
8 const add = load(require('../../function/arithmetic/addScalar'))
9 const subtract = load(require('../../function/arithmetic/subtract'))
10 const multiply = load(require('../../function/arithmetic/multiplyScalar'))
11 const divide = load(require('../../function/arithmetic/divideScalar'))
12 const pow = load(require('../../function/arithmetic/pow'))
13 const abs = load(require('../../function/arithmetic/abs'))
14 const fix = load(require('../../function/arithmetic/fix'))
15 const round = load(require('../../function/arithmetic/round'))
16 const equal = load(require('../../function/relational/equal'))
17 const isNumeric = load(require('../../function/utils/isNumeric'))
18 const format = load(require('../../function/string/format'))
19 const getTypeOf = load(require('../../function/utils/typeof'))
20 const toNumber = load(require('../../type/number'))
21 const Complex = load(require('../../type/complex/Complex'))
22
23 /**
24 * A unit can be constructed in the following ways:
25 *
26 * const a = new Unit(value, name)
27 * const b = new Unit(null, name)
28 * const c = Unit.parse(str)
29 *
30 * Example usage:
31 *
32 * const a = new Unit(5, 'cm') // 50 mm
33 * const b = Unit.parse('23 kg') // 23 kg
34 * const c = math.in(a, new Unit(null, 'm') // 0.05 m
35 * const d = new Unit(9.81, "m/s^2") // 9.81 m/s^2
36 *
37 * @class Unit
38 * @constructor Unit
39 * @param {number | BigNumber | Fraction | Complex | boolean} [value] A value like 5.2
40 * @param {string} [name] A unit name like "cm" or "inch", or a derived unit of the form: "u1[^ex1] [u2[^ex2] ...] [/ u3[^ex3] [u4[^ex4]]]", such as "kg m^2/s^2", where each unit appearing after the forward slash is taken to be in the denominator. "kg m^2 s^-2" is a synonym and is also acceptable. Any of the units can include a prefix.
41 */
42 function Unit (value, name) {
43 if (!(this instanceof Unit)) {
44 throw new Error('Constructor must be called with the new operator')
45 }
46
47 if (!(value === null || value === undefined || isNumeric(value) || type.isComplex(value))) {
48 throw new TypeError('First parameter in Unit constructor must be number, BigNumber, Fraction, Complex, or undefined')
49 }
50 if (name !== undefined && (typeof name !== 'string' || name === '')) {
51 throw new TypeError('Second parameter in Unit constructor must be a string')
52 }
53
54 if (name !== undefined) {
55 const u = Unit.parse(name)
56 this.units = u.units
57 this.dimensions = u.dimensions
58 } else {
59 this.units = [
60 {
61 unit: UNIT_NONE,
62 prefix: PREFIXES.NONE, // link to a list with supported prefixes
63 power: 0
64 }
65 ]
66 this.dimensions = []
67 for (let i = 0; i < BASE_DIMENSIONS.length; i++) {
68 this.dimensions[i] = 0
69 }
70 }
71
72 this.value = (value !== undefined && value !== null) ? this._normalize(value) : null
73
74 this.fixPrefix = false // if true, function format will not search for the
75 // best prefix but leave it as initially provided.
76 // fixPrefix is set true by the method Unit.to
77
78 // The justification behind this is that if the constructor is explicitly called,
79 // the caller wishes the units to be returned exactly as he supplied.
80 this.isUnitListSimplified = true
81 }
82
83 /**
84 * Attach type information
85 */
86 Unit.prototype.type = 'Unit'
87 Unit.prototype.isUnit = true
88
89 // private variables and functions for the Unit parser
90 let text, index, c
91
92 function skipWhitespace () {
93 while (c === ' ' || c === '\t') {
94 next()
95 }
96 }
97
98 function isDigitDot (c) {
99 return ((c >= '0' && c <= '9') || c === '.')
100 }
101
102 function isDigit (c) {
103 return ((c >= '0' && c <= '9'))
104 }
105
106 function next () {
107 index++
108 c = text.charAt(index)
109 }
110
111 function revert (oldIndex) {
112 index = oldIndex
113 c = text.charAt(index)
114 }
115
116 function parseNumber () {
117 let number = ''
118 let oldIndex
119 oldIndex = index
120
121 if (c === '+') {
122 next()
123 } else if (c === '-') {
124 number += c
125 next()
126 }
127
128 if (!isDigitDot(c)) {
129 // a + or - must be followed by a digit
130 revert(oldIndex)
131 return null
132 }
133
134 // get number, can have a single dot
135 if (c === '.') {
136 number += c
137 next()
138 if (!isDigit(c)) {
139 // this is no legal number, it is just a dot
140 revert(oldIndex)
141 return null
142 }
143 } else {
144 while (isDigit(c)) {
145 number += c
146 next()
147 }
148 if (c === '.') {
149 number += c
150 next()
151 }
152 }
153 while (isDigit(c)) {
154 number += c
155 next()
156 }
157
158 // check for exponential notation like "2.3e-4" or "1.23e50"
159 if (c === 'E' || c === 'e') {
160 // The grammar branches here. This could either be part of an exponent or the start of a unit that begins with the letter e, such as "4exabytes"
161
162 let tentativeNumber = ''
163 const tentativeIndex = index
164
165 tentativeNumber += c
166 next()
167
168 if (c === '+' || c === '-') {
169 tentativeNumber += c
170 next()
171 }
172
173 // Scientific notation MUST be followed by an exponent (otherwise we assume it is not scientific notation)
174 if (!isDigit(c)) {
175 // The e or E must belong to something else, so return the number without the e or E.
176 revert(tentativeIndex)
177 return number
178 }
179
180 // We can now safely say that this is scientific notation.
181 number = number + tentativeNumber
182 while (isDigit(c)) {
183 number += c
184 next()
185 }
186 }
187
188 return number
189 }
190
191 function parseUnit () {
192 let unitName = ''
193
194 // Alphanumeric characters only; matches [a-zA-Z0-9]
195 let code = text.charCodeAt(index)
196 while ((code >= 48 && code <= 57) ||
197 (code >= 65 && code <= 90) ||
198 (code >= 97 && code <= 122)) {
199 unitName += c
200 next()
201 code = text.charCodeAt(index)
202 }
203
204 // Must begin with [a-zA-Z]
205 code = unitName.charCodeAt(0)
206 if ((code >= 65 && code <= 90) ||
207 (code >= 97 && code <= 122)) {
208 return unitName || null
209 } else {
210 return null
211 }
212 }
213
214 function parseCharacter (toFind) {
215 if (c === toFind) {
216 next()
217 return toFind
218 } else {
219 return null
220 }
221 }
222
223 /**
224 * Parse a string into a unit. The value of the unit is parsed as number,
225 * BigNumber, or Fraction depending on the math.js config setting `number`.
226 *
227 * Throws an exception if the provided string does not contain a valid unit or
228 * cannot be parsed.
229 * @memberof Unit
230 * @param {string} str A string like "5.2 inch", "4e2 cm/s^2"
231 * @return {Unit} unit
232 */
233 Unit.parse = function (str, options) {
234 options = options || {}
235 text = str
236 index = -1
237 c = ''
238
239 if (typeof text !== 'string') {
240 throw new TypeError('Invalid argument in Unit.parse, string expected')
241 }
242
243 const unit = new Unit()
244 unit.units = []
245
246 let powerMultiplierCurrent = 1
247 let expectingUnit = false
248
249 // A unit should follow this pattern:
250 // [number] ...[ [*/] unit[^number] ]
251 // unit[^number] ... [ [*/] unit[^number] ]
252
253 // Rules:
254 // number is any floating point number.
255 // unit is any alphanumeric string beginning with an alpha. Units with names like e3 should be avoided because they look like the exponent of a floating point number!
256 // The string may optionally begin with a number.
257 // Each unit may optionally be followed by ^number.
258 // Whitespace or a forward slash is recommended between consecutive units, although the following technically is parseable:
259 // 2m^2kg/s^2
260 // it is not good form. If a unit starts with e, then it could be confused as a floating point number:
261 // 4erg
262
263 next()
264 skipWhitespace()
265
266 // Optional number at the start of the string
267 const valueStr = parseNumber()
268 let value = null
269 if (valueStr) {
270 if (config.number === 'BigNumber') {
271 value = new type.BigNumber(valueStr)
272 } else if (config.number === 'Fraction') {
273 value = new type.Fraction(valueStr)
274 } else { // number
275 value = parseFloat(valueStr)
276 }
277
278 skipWhitespace() // Whitespace is not required here
279
280 // handle multiplication or division right after the value, like '1/s'
281 if (parseCharacter('*')) {
282 powerMultiplierCurrent = 1
283 expectingUnit = true
284 } else if (parseCharacter('/')) {
285 powerMultiplierCurrent = -1
286 expectingUnit = true
287 }
288 }
289
290 // Stack to keep track of powerMultipliers applied to each parentheses group
291 const powerMultiplierStack = []
292
293 // Running product of all elements in powerMultiplierStack
294 let powerMultiplierStackProduct = 1
295
296 while (true) {
297 skipWhitespace()
298
299 // Check for and consume opening parentheses, pushing powerMultiplierCurrent to the stack
300 // A '(' will always appear directly before a unit.
301 while (c === '(') {
302 powerMultiplierStack.push(powerMultiplierCurrent)
303 powerMultiplierStackProduct *= powerMultiplierCurrent
304 powerMultiplierCurrent = 1
305 next()
306 skipWhitespace()
307 }
308
309 // Is there something here?
310 let uStr
311 if (c) {
312 const oldC = c
313 uStr = parseUnit()
314 if (uStr === null) {
315 throw new SyntaxError('Unexpected "' + oldC + '" in "' + text + '" at index ' + index.toString())
316 }
317 } else {
318 // End of input.
319 break
320 }
321
322 // Verify the unit exists and get the prefix (if any)
323 const res = _findUnit(uStr)
324 if (res === null) {
325 // Unit not found.
326 throw new SyntaxError('Unit "' + uStr + '" not found.')
327 }
328
329 let power = powerMultiplierCurrent * powerMultiplierStackProduct
330 // Is there a "^ number"?
331 skipWhitespace()
332 if (parseCharacter('^')) {
333 skipWhitespace()
334 const p = parseNumber()
335 if (p === null) {
336 // No valid number found for the power!
337 throw new SyntaxError('In "' + str + '", "^" must be followed by a floating-point number')
338 }
339 power *= p
340 }
341
342 // Add the unit to the list
343 unit.units.push({
344 unit: res.unit,
345 prefix: res.prefix,
346 power: power
347 })
348 for (let i = 0; i < BASE_DIMENSIONS.length; i++) {
349 unit.dimensions[i] += (res.unit.dimensions[i] || 0) * power
350 }
351
352 // Check for and consume closing parentheses, popping from the stack.
353 // A ')' will always follow a unit.
354 skipWhitespace()
355 while (c === ')') {
356 if (powerMultiplierStack.length === 0) {
357 throw new SyntaxError('Unmatched ")" in "' + text + '" at index ' + index.toString())
358 }
359 powerMultiplierStackProduct /= powerMultiplierStack.pop()
360 next()
361 skipWhitespace()
362 }
363
364 // "*" and "/" should mean we are expecting something to come next.
365 // Is there a forward slash? If so, negate powerMultiplierCurrent. The next unit or paren group is in the denominator.
366 expectingUnit = false
367
368 if (parseCharacter('*')) {
369 // explicit multiplication
370 powerMultiplierCurrent = 1
371 expectingUnit = true
372 } else if (parseCharacter('/')) {
373 // division
374 powerMultiplierCurrent = -1
375 expectingUnit = true
376 } else {
377 // implicit multiplication
378 powerMultiplierCurrent = 1
379 }
380
381 // Replace the unit into the auto unit system
382 if (res.unit.base) {
383 const baseDim = res.unit.base.key
384 UNIT_SYSTEMS.auto[baseDim] = {
385 unit: res.unit,
386 prefix: res.prefix
387 }
388 }
389 }
390
391 // Has the string been entirely consumed?
392 skipWhitespace()
393 if (c) {
394 throw new SyntaxError('Could not parse: "' + str + '"')
395 }
396
397 // Is there a trailing slash?
398 if (expectingUnit) {
399 throw new SyntaxError('Trailing characters: "' + str + '"')
400 }
401
402 // Is the parentheses stack empty?
403 if (powerMultiplierStack.length !== 0) {
404 throw new SyntaxError('Unmatched "(" in "' + text + '"')
405 }
406
407 // Are there any units at all?
408 if (unit.units.length === 0 && !options.allowNoUnits) {
409 throw new SyntaxError('"' + str + '" contains no units')
410 }
411
412 unit.value = (value !== undefined) ? unit._normalize(value) : null
413 return unit
414 }
415
416 /**
417 * create a copy of this unit
418 * @memberof Unit
419 * @return {Unit} Returns a cloned version of the unit
420 */
421 Unit.prototype.clone = function () {
422 const unit = new Unit()
423
424 unit.fixPrefix = this.fixPrefix
425 unit.isUnitListSimplified = this.isUnitListSimplified
426
427 unit.value = clone(this.value)
428 unit.dimensions = this.dimensions.slice(0)
429 unit.units = []
430 for (let i = 0; i < this.units.length; i++) {
431 unit.units[i] = { }
432 for (const p in this.units[i]) {
433 if (this.units[i].hasOwnProperty(p)) {
434 unit.units[i][p] = this.units[i][p]
435 }
436 }
437 }
438
439 return unit
440 }
441
442 /**
443 * Return whether the unit is derived (such as m/s, or cm^2, but not N)
444 * @memberof Unit
445 * @return {boolean} True if the unit is derived
446 */
447 Unit.prototype._isDerived = function () {
448 if (this.units.length === 0) {
449 return false
450 }
451 return this.units.length > 1 || Math.abs(this.units[0].power - 1.0) > 1e-15
452 }
453
454 /**
455 * Normalize a value, based on its currently set unit(s)
456 * @memberof Unit
457 * @param {number | BigNumber | Fraction | boolean} value
458 * @return {number | BigNumber | Fraction | boolean} normalized value
459 * @private
460 */
461 Unit.prototype._normalize = function (value) {
462 let unitValue, unitOffset, unitPower, unitPrefixValue
463 let convert
464
465 if (value === null || value === undefined || this.units.length === 0) {
466 return value
467 } else if (this._isDerived()) {
468 // This is a derived unit, so do not apply offsets.
469 // For example, with J kg^-1 degC^-1 you would NOT want to apply the offset.
470 let res = value
471 convert = Unit._getNumberConverter(getTypeOf(value)) // convert to Fraction or BigNumber if needed
472
473 for (let i = 0; i < this.units.length; i++) {
474 unitValue = convert(this.units[i].unit.value)
475 unitPrefixValue = convert(this.units[i].prefix.value)
476 unitPower = convert(this.units[i].power)
477 res = multiply(res, pow(multiply(unitValue, unitPrefixValue), unitPower))
478 }
479
480 return res
481 } else {
482 // This is a single unit of power 1, like kg or degC
483 convert = Unit._getNumberConverter(getTypeOf(value)) // convert to Fraction or BigNumber if needed
484
485 unitValue = convert(this.units[0].unit.value)
486 unitOffset = convert(this.units[0].unit.offset)
487 unitPrefixValue = convert(this.units[0].prefix.value)
488
489 return multiply(add(value, unitOffset), multiply(unitValue, unitPrefixValue))
490 }
491 }
492
493 /**
494 * Denormalize a value, based on its currently set unit(s)
495 * @memberof Unit
496 * @param {number} value
497 * @param {number} [prefixValue] Optional prefix value to be used (ignored if this is a derived unit)
498 * @return {number} denormalized value
499 * @private
500 */
501 Unit.prototype._denormalize = function (value, prefixValue) {
502 let unitValue, unitOffset, unitPower, unitPrefixValue
503 let convert
504
505 if (value === null || value === undefined || this.units.length === 0) {
506 return value
507 } else if (this._isDerived()) {
508 // This is a derived unit, so do not apply offsets.
509 // For example, with J kg^-1 degC^-1 you would NOT want to apply the offset.
510 // Also, prefixValue is ignored--but we will still use the prefix value stored in each unit, since kg is usually preferable to g unless the user decides otherwise.
511 let res = value
512 convert = Unit._getNumberConverter(getTypeOf(value)) // convert to Fraction or BigNumber if needed
513
514 for (let i = 0; i < this.units.length; i++) {
515 unitValue = convert(this.units[i].unit.value)
516 unitPrefixValue = convert(this.units[i].prefix.value)
517 unitPower = convert(this.units[i].power)
518 res = divide(res, pow(multiply(unitValue, unitPrefixValue), unitPower))
519 }
520
521 return res
522 } else {
523 // This is a single unit of power 1, like kg or degC
524 convert = Unit._getNumberConverter(getTypeOf(value)) // convert to Fraction or BigNumber if needed
525
526 unitValue = convert(this.units[0].unit.value)
527 unitPrefixValue = convert(this.units[0].prefix.value)
528 unitOffset = convert(this.units[0].unit.offset)
529
530 if (prefixValue === undefined || prefixValue === null) {
531 return subtract(divide(divide(value, unitValue), unitPrefixValue), unitOffset)
532 } else {
533 return subtract(divide(divide(value, unitValue), prefixValue), unitOffset)
534 }
535 }
536 }
537
538 /**
539 * Find a unit from a string
540 * @memberof Unit
541 * @param {string} str A string like 'cm' or 'inch'
542 * @returns {Object | null} result When found, an object with fields unit and
543 * prefix is returned. Else, null is returned.
544 * @private
545 */
546 function _findUnit (str) {
547 // First, match units names exactly. For example, a user could define 'mm' as 10^-4 m, which is silly, but then we would want 'mm' to match the user-defined unit.
548 if (UNITS.hasOwnProperty(str)) {
549 const unit = UNITS[str]
550 const prefix = unit.prefixes['']
551 return {
552 unit,
553 prefix
554 }
555 }
556
557 for (const name in UNITS) {
558 if (UNITS.hasOwnProperty(name)) {
559 if (endsWith(str, name)) {
560 const unit = UNITS[name]
561 const prefixLen = (str.length - name.length)
562 const prefixName = str.substring(0, prefixLen)
563 const prefix = unit.prefixes.hasOwnProperty(prefixName)
564 ? unit.prefixes[prefixName]
565 : undefined
566 if (prefix !== undefined) {
567 // store unit, prefix, and value
568 return {
569 unit,
570 prefix
571 }
572 }
573 }
574 }
575 }
576
577 return null
578 }
579
580 /**
581 * Test if the given expression is a unit.
582 * The unit can have a prefix but cannot have a value.
583 * @memberof Unit
584 * @param {string} name A string to be tested whether it is a value less unit.
585 * The unit can have prefix, like "cm"
586 * @return {boolean} true if the given string is a unit
587 */
588 Unit.isValuelessUnit = function (name) {
589 return (_findUnit(name) !== null)
590 }
591
592 /**
593 * check if this unit has given base unit
594 * If this unit is a derived unit, this will ALWAYS return false, since by definition base units are not derived.
595 * @memberof Unit
596 * @param {BASE_UNITS | string | undefined} base
597 */
598 Unit.prototype.hasBase = function (base) {
599 if (typeof (base) === 'string') {
600 base = BASE_UNITS[base]
601 }
602
603 if (!base) { return false }
604
605 // All dimensions must be the same
606 for (let i = 0; i < BASE_DIMENSIONS.length; i++) {
607 if (Math.abs((this.dimensions[i] || 0) - (base.dimensions[i] || 0)) > 1e-12) {
608 return false
609 }
610 }
611 return true
612 }
613
614 /**
615 * Check if this unit has a base or bases equal to another base or bases
616 * For derived units, the exponent on each base also must match
617 * @memberof Unit
618 * @param {Unit} other
619 * @return {boolean} true if equal base
620 */
621 Unit.prototype.equalBase = function (other) {
622 // All dimensions must be the same
623 for (let i = 0; i < BASE_DIMENSIONS.length; i++) {
624 if (Math.abs((this.dimensions[i] || 0) - (other.dimensions[i] || 0)) > 1e-12) {
625 return false
626 }
627 }
628 return true
629 }
630
631 /**
632 * Check if this unit equals another unit
633 * @memberof Unit
634 * @param {Unit} other
635 * @return {boolean} true if both units are equal
636 */
637 Unit.prototype.equals = function (other) {
638 return (this.equalBase(other) && equal(this.value, other.value))
639 }
640
641 /**
642 * Multiply this unit with another one
643 * @memberof Unit
644 * @param {Unit} other
645 * @return {Unit} product of this unit and the other unit
646 */
647 Unit.prototype.multiply = function (other) {
648 const res = this.clone()
649
650 for (let i = 0; i < BASE_DIMENSIONS.length; i++) {
651 // Dimensions arrays may be of different lengths. Default to 0.
652 res.dimensions[i] = (this.dimensions[i] || 0) + (other.dimensions[i] || 0)
653 }
654
655 // Append other's units list onto res (simplify later in Unit.prototype.format)
656 for (let i = 0; i < other.units.length; i++) {
657 // Make a deep copy
658 const inverted = {}
659 for (const key in other.units[i]) {
660 inverted[key] = other.units[i][key]
661 }
662 res.units.push(inverted)
663 }
664
665 // If at least one operand has a value, then the result should also have a value
666 if (this.value !== null || other.value !== null) {
667 const valThis = this.value === null ? this._normalize(1) : this.value
668 const valOther = other.value === null ? other._normalize(1) : other.value
669 res.value = multiply(valThis, valOther)
670 } else {
671 res.value = null
672 }
673
674 // Trigger simplification of the unit list at some future time
675 res.isUnitListSimplified = false
676
677 return getNumericIfUnitless(res)
678 }
679
680 /**
681 * Divide this unit by another one
682 * @memberof Unit
683 * @param {Unit} other
684 * @return {Unit} result of dividing this unit by the other unit
685 */
686 Unit.prototype.divide = function (other) {
687 const res = this.clone()
688
689 for (let i = 0; i < BASE_DIMENSIONS.length; i++) {
690 // Dimensions arrays may be of different lengths. Default to 0.
691 res.dimensions[i] = (this.dimensions[i] || 0) - (other.dimensions[i] || 0)
692 }
693
694 // Invert and append other's units list onto res (simplify later in Unit.prototype.format)
695 for (let i = 0; i < other.units.length; i++) {
696 // Make a deep copy
697 const inverted = {}
698 for (const key in other.units[i]) {
699 inverted[key] = other.units[i][key]
700 }
701 inverted.power = -inverted.power
702 res.units.push(inverted)
703 }
704
705 // If at least one operand has a value, the result should have a value
706 if (this.value !== null || other.value !== null) {
707 const valThis = this.value === null ? this._normalize(1) : this.value
708 const valOther = other.value === null ? other._normalize(1) : other.value
709 res.value = divide(valThis, valOther)
710 } else {
711 res.value = null
712 }
713
714 // Trigger simplification of the unit list at some future time
715 res.isUnitListSimplified = false
716
717 return getNumericIfUnitless(res)
718 }
719
720 /**
721 * Calculate the power of a unit
722 * @memberof Unit
723 * @param {number | Fraction | BigNumber} p
724 * @returns {Unit} The result: this^p
725 */
726 Unit.prototype.pow = function (p) {
727 const res = this.clone()
728
729 for (let i = 0; i < BASE_DIMENSIONS.length; i++) {
730 // Dimensions arrays may be of different lengths. Default to 0.
731 res.dimensions[i] = (this.dimensions[i] || 0) * p
732 }
733
734 // Adjust the power of each unit in the list
735 for (let i = 0; i < res.units.length; i++) {
736 res.units[i].power *= p
737 }
738
739 if (res.value !== null) {
740 res.value = pow(res.value, p)
741
742 // only allow numeric output, we don't want to return a Complex number
743 // if (!isNumeric(res.value)) {
744 // res.value = NaN
745 // }
746 // Update: Complex supported now
747 } else {
748 res.value = null
749 }
750
751 // Trigger lazy evaluation of the unit list
752 res.isUnitListSimplified = false
753
754 return getNumericIfUnitless(res)
755 }
756
757 /**
758 * Return the numeric value of this unit if it is dimensionless, has a value, and config.predictable == false; or the original unit otherwise
759 * @param {Unit} unit
760 * @returns {number | Fraction | BigNumber | Unit} The numeric value of the unit if conditions are met, or the original unit otherwise
761 */
762 function getNumericIfUnitless (unit) {
763 if (unit.equalBase(BASE_UNITS.NONE) && unit.value !== null && !config.predictable) {
764 return unit.value
765 } else {
766 return unit
767 }
768 }
769
770 /**
771 * Calculate the absolute value of a unit
772 * @memberof Unit
773 * @param {number | Fraction | BigNumber} x
774 * @returns {Unit} The result: |x|, absolute value of x
775 */
776 Unit.prototype.abs = function () {
777 // This gives correct, but unexpected, results for units with an offset.
778 // For example, abs(-283.15 degC) = -263.15 degC !!!
779 const ret = this.clone()
780 ret.value = ret.value !== null ? abs(ret.value) : null
781
782 for (const i in ret.units) {
783 if (ret.units[i].unit.name === 'VA' || ret.units[i].unit.name === 'VAR') {
784 ret.units[i].unit = UNITS['W']
785 }
786 }
787
788 return ret
789 }
790
791 /**
792 * Convert the unit to a specific unit name.
793 * @memberof Unit
794 * @param {string | Unit} valuelessUnit A unit without value. Can have prefix, like "cm"
795 * @returns {Unit} Returns a clone of the unit with a fixed prefix and unit.
796 */
797 Unit.prototype.to = function (valuelessUnit) {
798 let other
799 const value = this.value === null ? this._normalize(1) : this.value
800 if (typeof valuelessUnit === 'string') {
801 // other = new Unit(null, valuelessUnit)
802 other = Unit.parse(valuelessUnit)
803 if (!this.equalBase(other)) {
804 throw new Error(`Units do not match ('${other.toString()}' != '${this.toString()}')`)
805 }
806 if (other.value !== null) {
807 throw new Error('Cannot convert to a unit with a value')
808 }
809
810 other.value = clone(value)
811 other.fixPrefix = true
812 other.isUnitListSimplified = true
813 return other
814 } else if (type.isUnit(valuelessUnit)) {
815 if (!this.equalBase(valuelessUnit)) {
816 throw new Error(`Units do not match ('${valuelessUnit.toString()}' != '${this.toString()}')`)
817 }
818 if (valuelessUnit.value !== null) {
819 throw new Error('Cannot convert to a unit with a value')
820 }
821 other = valuelessUnit.clone()
822 other.value = clone(value)
823 other.fixPrefix = true
824 other.isUnitListSimplified = true
825 return other
826 } else {
827 throw new Error('String or Unit expected as parameter')
828 }
829 }
830
831 /**
832 * Return the value of the unit when represented with given valueless unit
833 * @memberof Unit
834 * @param {string | Unit} valuelessUnit For example 'cm' or 'inch'
835 * @return {number} Returns the unit value as number.
836 */
837 // TODO: deprecate Unit.toNumber? It's always better to use toNumeric
838 Unit.prototype.toNumber = function (valuelessUnit) {
839 return toNumber(this.toNumeric(valuelessUnit))
840 }
841
842 /**
843 * Return the value of the unit in the original numeric type
844 * @memberof Unit
845 * @param {string | Unit} valuelessUnit For example 'cm' or 'inch'
846 * @return {number | BigNumber | Fraction} Returns the unit value
847 */
848 Unit.prototype.toNumeric = function (valuelessUnit) {
849 let other = this
850 if (valuelessUnit) {
851 // Allow getting the numeric value without converting to a different unit
852 other = this.to(valuelessUnit)
853 }
854
855 other.simplifyUnitListLazy()
856
857 if (other._isDerived()) {
858 return other._denormalize(other.value)
859 } else {
860 return other._denormalize(other.value, other.units[0].prefix.value)
861 }
862 }
863
864 /**
865 * Get a string representation of the unit.
866 * @memberof Unit
867 * @return {string}
868 */
869 Unit.prototype.toString = function () {
870 return this.format()
871 }
872
873 /**
874 * Get a JSON representation of the unit
875 * @memberof Unit
876 * @returns {Object} Returns a JSON object structured as:
877 * `{"mathjs": "Unit", "value": 2, "unit": "cm", "fixPrefix": false}`
878 */
879 Unit.prototype.toJSON = function () {
880 return {
881 mathjs: 'Unit',
882 value: this._denormalize(this.value),
883 unit: this.formatUnits(),
884 fixPrefix: this.fixPrefix
885 }
886 }
887
888 /**
889 * Instantiate a Unit from a JSON object
890 * @memberof Unit
891 * @param {Object} json A JSON object structured as:
892 * `{"mathjs": "Unit", "value": 2, "unit": "cm", "fixPrefix": false}`
893 * @return {Unit}
894 */
895 Unit.fromJSON = function (json) {
896 const unit = new Unit(json.value, json.unit)
897 unit.fixPrefix = json.fixPrefix || false
898 return unit
899 }
900
901 /**
902 * Returns the string representation of the unit.
903 * @memberof Unit
904 * @return {string}
905 */
906 Unit.prototype.valueOf = Unit.prototype.toString
907
908 /**
909 * Attempt to simplify the list of units for this unit according to the dimensions array and the current unit system. After the call, this Unit will contain a list of the "best" units for formatting.
910 * Intended to be evaluated lazily. You must set isUnitListSimplified = false before the call! After the call, isUnitListSimplified will be set to true.
911 */
912 Unit.prototype.simplifyUnitListLazy = function () {
913 if (this.isUnitListSimplified || this.value === null) {
914 return
915 }
916
917 const proposedUnitList = []
918
919 // Search for a matching base
920 let matchingBase
921 for (const key in currentUnitSystem) {
922 if (this.hasBase(BASE_UNITS[key])) {
923 matchingBase = key
924 break
925 }
926 }
927
928 if (matchingBase === 'NONE') {
929 this.units = []
930 } else {
931 let matchingUnit
932 if (matchingBase) {
933 // Does the unit system have a matching unit?
934 if (currentUnitSystem.hasOwnProperty(matchingBase)) {
935 matchingUnit = currentUnitSystem[matchingBase]
936 }
937 }
938 if (matchingUnit) {
939 this.units = [{
940 unit: matchingUnit.unit,
941 prefix: matchingUnit.prefix,
942 power: 1.0
943 }]
944 } else {
945 // Multiple units or units with powers are formatted like this:
946 // 5 (kg m^2) / (s^3 mol)
947 // Build an representation from the base units of the current unit system
948 let missingBaseDim = false
949 for (let i = 0; i < BASE_DIMENSIONS.length; i++) {
950 const baseDim = BASE_DIMENSIONS[i]
951 if (Math.abs(this.dimensions[i] || 0) > 1e-12) {
952 if (currentUnitSystem.hasOwnProperty(baseDim)) {
953 proposedUnitList.push({
954 unit: currentUnitSystem[baseDim].unit,
955 prefix: currentUnitSystem[baseDim].prefix,
956 power: this.dimensions[i] || 0
957 })
958 } else {
959 missingBaseDim = true
960 }
961 }
962 }
963
964 // Is the proposed unit list "simpler" than the existing one?
965 if (proposedUnitList.length < this.units.length && !missingBaseDim) {
966 // Replace this unit list with the proposed list
967 this.units = proposedUnitList
968 }
969 }
970 }
971
972 this.isUnitListSimplified = true
973 }
974
975 Unit.prototype.toSI = function () {
976 const ret = this.clone()
977
978 const proposedUnitList = []
979
980 // Multiple units or units with powers are formatted like this:
981 // 5 (kg m^2) / (s^3 mol)
982 // Build an representation from the base units of the SI unit system
983 for (let i = 0; i < BASE_DIMENSIONS.length; i++) {
984 const baseDim = BASE_DIMENSIONS[i]
985 if (Math.abs(ret.dimensions[i] || 0) > 1e-12) {
986 if (UNIT_SYSTEMS['si'].hasOwnProperty(baseDim)) {
987 proposedUnitList.push({
988 unit: UNIT_SYSTEMS['si'][baseDim].unit,
989 prefix: UNIT_SYSTEMS['si'][baseDim].prefix,
990 power: ret.dimensions[i] || 0
991 })
992 } else {
993 throw new Error('Cannot express custom unit ' + baseDim + ' in SI units')
994 }
995 }
996 }
997
998 // Replace this unit list with the proposed list
999 ret.units = proposedUnitList
1000
1001 ret.isUnitListSimplified = true
1002
1003 return ret
1004 }
1005
1006 /**
1007 * Get a string representation of the units of this Unit, without the value.
1008 * @memberof Unit
1009 * @return {string}
1010 */
1011 Unit.prototype.formatUnits = function () {
1012 // Lazy evaluation of the unit list
1013 this.simplifyUnitListLazy()
1014
1015 let strNum = ''
1016 let strDen = ''
1017 let nNum = 0
1018 let nDen = 0
1019
1020 for (let i = 0; i < this.units.length; i++) {
1021 if (this.units[i].power > 0) {
1022 nNum++
1023 strNum += ' ' + this.units[i].prefix.name + this.units[i].unit.name
1024 if (Math.abs(this.units[i].power - 1.0) > 1e-15) {
1025 strNum += '^' + this.units[i].power
1026 }
1027 } else if (this.units[i].power < 0) {
1028 nDen++
1029 }
1030 }
1031
1032 if (nDen > 0) {
1033 for (let i = 0; i < this.units.length; i++) {
1034 if (this.units[i].power < 0) {
1035 if (nNum > 0) {
1036 strDen += ' ' + this.units[i].prefix.name + this.units[i].unit.name
1037 if (Math.abs(this.units[i].power + 1.0) > 1e-15) {
1038 strDen += '^' + (-this.units[i].power)
1039 }
1040 } else {
1041 strDen += ' ' + this.units[i].prefix.name + this.units[i].unit.name
1042 strDen += '^' + (this.units[i].power)
1043 }
1044 }
1045 }
1046 }
1047 // Remove leading " "
1048 strNum = strNum.substr(1)
1049 strDen = strDen.substr(1)
1050
1051 // Add parans for better copy/paste back into the eval, for example, or for better pretty print formatting
1052 if (nNum > 1 && nDen > 0) {
1053 strNum = '(' + strNum + ')'
1054 }
1055 if (nDen > 1 && nNum > 0) {
1056 strDen = '(' + strDen + ')'
1057 }
1058
1059 let str = strNum
1060 if (nNum > 0 && nDen > 0) {
1061 str += ' / '
1062 }
1063 str += strDen
1064
1065 return str
1066 }
1067
1068 /**
1069 * Get a string representation of the Unit, with optional formatting options.
1070 * @memberof Unit
1071 * @param {Object | number | Function} [options] Formatting options. See
1072 * lib/utils/number:format for a
1073 * description of the available
1074 * options.
1075 * @return {string}
1076 */
1077 Unit.prototype.format = function (options) {
1078 // Simplfy the unit list, if necessary
1079 this.simplifyUnitListLazy()
1080
1081 // Apply some custom logic for handling VA and VAR. The goal is to express the value of the unit as a real value, if possible. Otherwise, use a real-valued unit instead of a complex-valued one.
1082 let isImaginary = false
1083 if (typeof (this.value) !== 'undefined' && this.value !== null && type.isComplex(this.value)) {
1084 // TODO: Make this better, for example, use relative magnitude of re and im rather than absolute
1085 isImaginary = Math.abs(this.value.re) < 1e-14
1086 }
1087
1088 for (const i in this.units) {
1089 if (this.units[i].unit) {
1090 if (this.units[i].unit.name === 'VA' && isImaginary) {
1091 this.units[i].unit = UNITS['VAR']
1092 } else if (this.units[i].unit.name === 'VAR' && !isImaginary) {
1093 this.units[i].unit = UNITS['VA']
1094 }
1095 }
1096 }
1097
1098 // Now apply the best prefix
1099 // Units must have only one unit and not have the fixPrefix flag set
1100 if (this.units.length === 1 && !this.fixPrefix) {
1101 // Units must have integer powers, otherwise the prefix will change the
1102 // outputted value by not-an-integer-power-of-ten
1103 if (Math.abs(this.units[0].power - Math.round(this.units[0].power)) < 1e-14) {
1104 // Apply the best prefix
1105 this.units[0].prefix = this._bestPrefix()
1106 }
1107 }
1108
1109 const value = this._denormalize(this.value)
1110 let str = (this.value !== null) ? format(value, options || {}) : ''
1111 const unitStr = this.formatUnits()
1112 if (this.value && type.isComplex(this.value)) {
1113 str = '(' + str + ')' // Surround complex values with ( ) to enable better parsing
1114 }
1115 if (unitStr.length > 0 && str.length > 0) {
1116 str += ' '
1117 }
1118 str += unitStr
1119
1120 return str
1121 }
1122
1123 /**
1124 * Calculate the best prefix using current value.
1125 * @memberof Unit
1126 * @returns {Object} prefix
1127 * @private
1128 */
1129 Unit.prototype._bestPrefix = function () {
1130 if (this.units.length !== 1) {
1131 throw new Error('Can only compute the best prefix for single units with integer powers, like kg, s^2, N^-1, and so forth!')
1132 }
1133 if (Math.abs(this.units[0].power - Math.round(this.units[0].power)) >= 1e-14) {
1134 throw new Error('Can only compute the best prefix for single units with integer powers, like kg, s^2, N^-1, and so forth!')
1135 }
1136
1137 // find the best prefix value (resulting in the value of which
1138 // the absolute value of the log10 is closest to zero,
1139 // though with a little offset of 1.2 for nicer values: you get a
1140 // sequence 1mm 100mm 500mm 0.6m 1m 10m 100m 500m 0.6km 1km ...
1141
1142 // Note: the units value can be any numeric type, but to find the best
1143 // prefix it's enough to work with limited precision of a regular number
1144 // Update: using mathjs abs since we also allow complex numbers
1145 const absValue = this.value !== null ? abs(this.value) : 0
1146 const absUnitValue = abs(this.units[0].unit.value)
1147 let bestPrefix = this.units[0].prefix
1148 if (absValue === 0) {
1149 return bestPrefix
1150 }
1151 const power = this.units[0].power
1152 let bestDiff = Math.log(absValue / Math.pow(bestPrefix.value * absUnitValue, power)) / Math.LN10 - 1.2
1153 if (bestDiff > -2.200001 && bestDiff < 1.800001) return bestPrefix // Allow the original prefix
1154 bestDiff = Math.abs(bestDiff)
1155 const prefixes = this.units[0].unit.prefixes
1156 for (const p in prefixes) {
1157 if (prefixes.hasOwnProperty(p)) {
1158 const prefix = prefixes[p]
1159 if (prefix.scientific) {
1160 const diff = Math.abs(
1161 Math.log(absValue / Math.pow(prefix.value * absUnitValue, power)) / Math.LN10 - 1.2)
1162
1163 if (diff < bestDiff ||
1164 (diff === bestDiff && prefix.name.length < bestPrefix.name.length)) {
1165 // choose the prefix with the smallest diff, or if equal, choose the one
1166 // with the shortest name (can happen with SHORTLONG for example)
1167 bestPrefix = prefix
1168 bestDiff = diff
1169 }
1170 }
1171 }
1172 }
1173
1174 return bestPrefix
1175 }
1176
1177 /**
1178 * Returns an array of units whose sum is equal to this unit
1179 * @memberof Unit
1180 * @param {Array} [parts] An array of strings or valueless units.
1181 *
1182 * Example:
1183 *
1184 * const u = new Unit(1, 'm')
1185 * u.splitUnit(['feet', 'inch'])
1186 * [ 3 feet, 3.3700787401575 inch ]
1187 *
1188 * @return {Array} An array of units.
1189 */
1190 Unit.prototype.splitUnit = function (parts) {
1191 let x = this.clone()
1192 const ret = []
1193 for (let i = 0; i < parts.length; i++) {
1194 // Convert x to the requested unit
1195 x = x.to(parts[i])
1196 if (i === parts.length - 1) break
1197
1198 // Get the numeric value of this unit
1199 const xNumeric = x.toNumeric()
1200
1201 // Check to see if xNumeric is nearly equal to an integer,
1202 // since fix can incorrectly round down if there is round-off error
1203 const xRounded = round(xNumeric)
1204 let xFixed
1205 const isNearlyEqual = equal(xRounded, xNumeric)
1206 if (isNearlyEqual) {
1207 xFixed = xRounded
1208 } else {
1209 xFixed = fix(x.toNumeric())
1210 }
1211
1212 const y = new Unit(xFixed, parts[i].toString())
1213 ret.push(y)
1214 x = subtract(x, y)
1215 }
1216
1217 // This little bit fixes a bug where the remainder should be 0 but is a little bit off.
1218 // But instead of comparing x, the remainder, with zero--we will compare the sum of
1219 // all the parts so far with the original value. If they are nearly equal,
1220 // we set the remainder to 0.
1221 let testSum = 0
1222 for (let i = 0; i < ret.length; i++) {
1223 testSum = add(testSum, ret[i].value)
1224 }
1225 if (equal(testSum, this.value)) {
1226 x.value = 0
1227 }
1228
1229 ret.push(x)
1230
1231 return ret
1232 }
1233
1234 const PREFIXES = {
1235 NONE: {
1236 '': { name: '', value: 1, scientific: true }
1237 },
1238 SHORT: {
1239 '': { name: '', value: 1, scientific: true },
1240
1241 'da': { name: 'da', value: 1e1, scientific: false },
1242 'h': { name: 'h', value: 1e2, scientific: false },
1243 'k': { name: 'k', value: 1e3, scientific: true },
1244 'M': { name: 'M', value: 1e6, scientific: true },
1245 'G': { name: 'G', value: 1e9, scientific: true },
1246 'T': { name: 'T', value: 1e12, scientific: true },
1247 'P': { name: 'P', value: 1e15, scientific: true },
1248 'E': { name: 'E', value: 1e18, scientific: true },
1249 'Z': { name: 'Z', value: 1e21, scientific: true },
1250 'Y': { name: 'Y', value: 1e24, scientific: true },
1251
1252 'd': { name: 'd', value: 1e-1, scientific: false },
1253 'c': { name: 'c', value: 1e-2, scientific: false },
1254 'm': { name: 'm', value: 1e-3, scientific: true },
1255 'u': { name: 'u', value: 1e-6, scientific: true },
1256 'n': { name: 'n', value: 1e-9, scientific: true },
1257 'p': { name: 'p', value: 1e-12, scientific: true },
1258 'f': { name: 'f', value: 1e-15, scientific: true },
1259 'a': { name: 'a', value: 1e-18, scientific: true },
1260 'z': { name: 'z', value: 1e-21, scientific: true },
1261 'y': { name: 'y', value: 1e-24, scientific: true }
1262 },
1263 LONG: {
1264 '': { name: '', value: 1, scientific: true },
1265
1266 'deca': { name: 'deca', value: 1e1, scientific: false },
1267 'hecto': { name: 'hecto', value: 1e2, scientific: false },
1268 'kilo': { name: 'kilo', value: 1e3, scientific: true },
1269 'mega': { name: 'mega', value: 1e6, scientific: true },
1270 'giga': { name: 'giga', value: 1e9, scientific: true },
1271 'tera': { name: 'tera', value: 1e12, scientific: true },
1272 'peta': { name: 'peta', value: 1e15, scientific: true },
1273 'exa': { name: 'exa', value: 1e18, scientific: true },
1274 'zetta': { name: 'zetta', value: 1e21, scientific: true },
1275 'yotta': { name: 'yotta', value: 1e24, scientific: true },
1276
1277 'deci': { name: 'deci', value: 1e-1, scientific: false },
1278 'centi': { name: 'centi', value: 1e-2, scientific: false },
1279 'milli': { name: 'milli', value: 1e-3, scientific: true },
1280 'micro': { name: 'micro', value: 1e-6, scientific: true },
1281 'nano': { name: 'nano', value: 1e-9, scientific: true },
1282 'pico': { name: 'pico', value: 1e-12, scientific: true },
1283 'femto': { name: 'femto', value: 1e-15, scientific: true },
1284 'atto': { name: 'atto', value: 1e-18, scientific: true },
1285 'zepto': { name: 'zepto', value: 1e-21, scientific: true },
1286 'yocto': { name: 'yocto', value: 1e-24, scientific: true }
1287 },
1288 SQUARED: {
1289 '': { name: '', value: 1, scientific: true },
1290
1291 'da': { name: 'da', value: 1e2, scientific: false },
1292 'h': { name: 'h', value: 1e4, scientific: false },
1293 'k': { name: 'k', value: 1e6, scientific: true },
1294 'M': { name: 'M', value: 1e12, scientific: true },
1295 'G': { name: 'G', value: 1e18, scientific: true },
1296 'T': { name: 'T', value: 1e24, scientific: true },
1297 'P': { name: 'P', value: 1e30, scientific: true },
1298 'E': { name: 'E', value: 1e36, scientific: true },
1299 'Z': { name: 'Z', value: 1e42, scientific: true },
1300 'Y': { name: 'Y', value: 1e48, scientific: true },
1301
1302 'd': { name: 'd', value: 1e-2, scientific: false },
1303 'c': { name: 'c', value: 1e-4, scientific: false },
1304 'm': { name: 'm', value: 1e-6, scientific: true },
1305 'u': { name: 'u', value: 1e-12, scientific: true },
1306 'n': { name: 'n', value: 1e-18, scientific: true },
1307 'p': { name: 'p', value: 1e-24, scientific: true },
1308 'f': { name: 'f', value: 1e-30, scientific: true },
1309 'a': { name: 'a', value: 1e-36, scientific: true },
1310 'z': { name: 'z', value: 1e-42, scientific: true },
1311 'y': { name: 'y', value: 1e-48, scientific: true }
1312 },
1313 CUBIC: {
1314 '': { name: '', value: 1, scientific: true },
1315
1316 'da': { name: 'da', value: 1e3, scientific: false },
1317 'h': { name: 'h', value: 1e6, scientific: false },
1318 'k': { name: 'k', value: 1e9, scientific: true },
1319 'M': { name: 'M', value: 1e18, scientific: true },
1320 'G': { name: 'G', value: 1e27, scientific: true },
1321 'T': { name: 'T', value: 1e36, scientific: true },
1322 'P': { name: 'P', value: 1e45, scientific: true },
1323 'E': { name: 'E', value: 1e54, scientific: true },
1324 'Z': { name: 'Z', value: 1e63, scientific: true },
1325 'Y': { name: 'Y', value: 1e72, scientific: true },
1326
1327 'd': { name: 'd', value: 1e-3, scientific: false },
1328 'c': { name: 'c', value: 1e-6, scientific: false },
1329 'm': { name: 'm', value: 1e-9, scientific: true },
1330 'u': { name: 'u', value: 1e-18, scientific: true },
1331 'n': { name: 'n', value: 1e-27, scientific: true },
1332 'p': { name: 'p', value: 1e-36, scientific: true },
1333 'f': { name: 'f', value: 1e-45, scientific: true },
1334 'a': { name: 'a', value: 1e-54, scientific: true },
1335 'z': { name: 'z', value: 1e-63, scientific: true },
1336 'y': { name: 'y', value: 1e-72, scientific: true }
1337 },
1338 BINARY_SHORT: {
1339 '': { name: '', value: 1, scientific: true },
1340 'k': { name: 'k', value: 1e3, scientific: true },
1341 'M': { name: 'M', value: 1e6, scientific: true },
1342 'G': { name: 'G', value: 1e9, scientific: true },
1343 'T': { name: 'T', value: 1e12, scientific: true },
1344 'P': { name: 'P', value: 1e15, scientific: true },
1345 'E': { name: 'E', value: 1e18, scientific: true },
1346 'Z': { name: 'Z', value: 1e21, scientific: true },
1347 'Y': { name: 'Y', value: 1e24, scientific: true },
1348
1349 'Ki': { name: 'Ki', value: 1024, scientific: true },
1350 'Mi': { name: 'Mi', value: Math.pow(1024, 2), scientific: true },
1351 'Gi': { name: 'Gi', value: Math.pow(1024, 3), scientific: true },
1352 'Ti': { name: 'Ti', value: Math.pow(1024, 4), scientific: true },
1353 'Pi': { name: 'Pi', value: Math.pow(1024, 5), scientific: true },
1354 'Ei': { name: 'Ei', value: Math.pow(1024, 6), scientific: true },
1355 'Zi': { name: 'Zi', value: Math.pow(1024, 7), scientific: true },
1356 'Yi': { name: 'Yi', value: Math.pow(1024, 8), scientific: true }
1357 },
1358 BINARY_LONG: {
1359 '': { name: '', value: 1, scientific: true },
1360 'kilo': { name: 'kilo', value: 1e3, scientific: true },
1361 'mega': { name: 'mega', value: 1e6, scientific: true },
1362 'giga': { name: 'giga', value: 1e9, scientific: true },
1363 'tera': { name: 'tera', value: 1e12, scientific: true },
1364 'peta': { name: 'peta', value: 1e15, scientific: true },
1365 'exa': { name: 'exa', value: 1e18, scientific: true },
1366 'zetta': { name: 'zetta', value: 1e21, scientific: true },
1367 'yotta': { name: 'yotta', value: 1e24, scientific: true },
1368
1369 'kibi': { name: 'kibi', value: 1024, scientific: true },
1370 'mebi': { name: 'mebi', value: Math.pow(1024, 2), scientific: true },
1371 'gibi': { name: 'gibi', value: Math.pow(1024, 3), scientific: true },
1372 'tebi': { name: 'tebi', value: Math.pow(1024, 4), scientific: true },
1373 'pebi': { name: 'pebi', value: Math.pow(1024, 5), scientific: true },
1374 'exi': { name: 'exi', value: Math.pow(1024, 6), scientific: true },
1375 'zebi': { name: 'zebi', value: Math.pow(1024, 7), scientific: true },
1376 'yobi': { name: 'yobi', value: Math.pow(1024, 8), scientific: true }
1377 },
1378 BTU: {
1379 '': { name: '', value: 1, scientific: true },
1380 'MM': { name: 'MM', value: 1e6, scientific: true }
1381 }
1382 }
1383
1384 // Add a prefix list for both short and long prefixes (for example for ohm and bar which support both Mohm and megaohm, mbar and millibar):
1385 PREFIXES.SHORTLONG = {}
1386 for (let key in PREFIXES.SHORT) {
1387 if (PREFIXES.SHORT.hasOwnProperty(key)) {
1388 PREFIXES.SHORTLONG[key] = PREFIXES.SHORT[key]
1389 }
1390 }
1391 for (let key in PREFIXES.LONG) {
1392 if (PREFIXES.LONG.hasOwnProperty(key)) {
1393 PREFIXES.SHORTLONG[key] = PREFIXES.LONG[key]
1394 }
1395 }
1396
1397 /* Internally, each unit is represented by a value and a dimension array. The elements of the dimensions array have the following meaning:
1398 * Index Dimension
1399 * ----- ---------
1400 * 0 Length
1401 * 1 Mass
1402 * 2 Time
1403 * 3 Current
1404 * 4 Temperature
1405 * 5 Luminous intensity
1406 * 6 Amount of substance
1407 * 7 Angle
1408 * 8 Bit (digital)
1409 * For example, the unit "298.15 K" is a pure temperature and would have a value of 298.15 and a dimension array of [0, 0, 0, 0, 1, 0, 0, 0, 0]. The unit "1 cal / (gm °C)" can be written in terms of the 9 fundamental dimensions as [length^2] / ([time^2] * [temperature]), and would a value of (after conversion to SI) 4184.0 and a dimensions array of [2, 0, -2, 0, -1, 0, 0, 0, 0].
1410 *
1411 */
1412
1413 const BASE_DIMENSIONS = ['MASS', 'LENGTH', 'TIME', 'CURRENT', 'TEMPERATURE', 'LUMINOUS_INTENSITY', 'AMOUNT_OF_SUBSTANCE', 'ANGLE', 'BIT']
1414
1415 const BASE_UNITS = {
1416 NONE: {
1417 dimensions: [0, 0, 0, 0, 0, 0, 0, 0, 0]
1418 },
1419 MASS: {
1420 dimensions: [1, 0, 0, 0, 0, 0, 0, 0, 0]
1421 },
1422 LENGTH: {
1423 dimensions: [0, 1, 0, 0, 0, 0, 0, 0, 0]
1424 },
1425 TIME: {
1426 dimensions: [0, 0, 1, 0, 0, 0, 0, 0, 0]
1427 },
1428 CURRENT: {
1429 dimensions: [0, 0, 0, 1, 0, 0, 0, 0, 0]
1430 },
1431 TEMPERATURE: {
1432 dimensions: [0, 0, 0, 0, 1, 0, 0, 0, 0]
1433 },
1434 LUMINOUS_INTENSITY: {
1435 dimensions: [0, 0, 0, 0, 0, 1, 0, 0, 0]
1436 },
1437 AMOUNT_OF_SUBSTANCE: {
1438 dimensions: [0, 0, 0, 0, 0, 0, 1, 0, 0]
1439 },
1440
1441 FORCE: {
1442 dimensions: [1, 1, -2, 0, 0, 0, 0, 0, 0]
1443 },
1444 SURFACE: {
1445 dimensions: [0, 2, 0, 0, 0, 0, 0, 0, 0]
1446 },
1447 VOLUME: {
1448 dimensions: [0, 3, 0, 0, 0, 0, 0, 0, 0]
1449 },
1450 ENERGY: {
1451 dimensions: [1, 2, -2, 0, 0, 0, 0, 0, 0]
1452 },
1453 POWER: {
1454 dimensions: [1, 2, -3, 0, 0, 0, 0, 0, 0]
1455 },
1456 PRESSURE: {
1457 dimensions: [1, -1, -2, 0, 0, 0, 0, 0, 0]
1458 },
1459
1460 ELECTRIC_CHARGE: {
1461 dimensions: [0, 0, 1, 1, 0, 0, 0, 0, 0]
1462 },
1463 ELECTRIC_CAPACITANCE: {
1464 dimensions: [-1, -2, 4, 2, 0, 0, 0, 0, 0]
1465 },
1466 ELECTRIC_POTENTIAL: {
1467 dimensions: [1, 2, -3, -1, 0, 0, 0, 0, 0]
1468 },
1469 ELECTRIC_RESISTANCE: {
1470 dimensions: [1, 2, -3, -2, 0, 0, 0, 0, 0]
1471 },
1472 ELECTRIC_INDUCTANCE: {
1473 dimensions: [1, 2, -2, -2, 0, 0, 0, 0, 0]
1474 },
1475 ELECTRIC_CONDUCTANCE: {
1476 dimensions: [-1, -2, 3, 2, 0, 0, 0, 0, 0]
1477 },
1478 MAGNETIC_FLUX: {
1479 dimensions: [1, 2, -2, -1, 0, 0, 0, 0, 0]
1480 },
1481 MAGNETIC_FLUX_DENSITY: {
1482 dimensions: [1, 0, -2, -1, 0, 0, 0, 0, 0]
1483 },
1484
1485 FREQUENCY: {
1486 dimensions: [0, 0, -1, 0, 0, 0, 0, 0, 0]
1487 },
1488 ANGLE: {
1489 dimensions: [0, 0, 0, 0, 0, 0, 0, 1, 0]
1490 },
1491 BIT: {
1492 dimensions: [0, 0, 0, 0, 0, 0, 0, 0, 1]
1493 }
1494 }
1495
1496 for (let key in BASE_UNITS) {
1497 BASE_UNITS[key].key = key
1498 }
1499
1500 const BASE_UNIT_NONE = {}
1501
1502 const UNIT_NONE = { name: '', base: BASE_UNIT_NONE, value: 1, offset: 0, dimensions: [0, 0, 0, 0, 0, 0, 0, 0, 0] }
1503
1504 const UNITS = {
1505 // length
1506 meter: {
1507 name: 'meter',
1508 base: BASE_UNITS.LENGTH,
1509 prefixes: PREFIXES.LONG,
1510 value: 1,
1511 offset: 0
1512 },
1513 inch: {
1514 name: 'inch',
1515 base: BASE_UNITS.LENGTH,
1516 prefixes: PREFIXES.NONE,
1517 value: 0.0254,
1518 offset: 0
1519 },
1520 foot: {
1521 name: 'foot',
1522 base: BASE_UNITS.LENGTH,
1523 prefixes: PREFIXES.NONE,
1524 value: 0.3048,
1525 offset: 0
1526 },
1527 yard: {
1528 name: 'yard',
1529 base: BASE_UNITS.LENGTH,
1530 prefixes: PREFIXES.NONE,
1531 value: 0.9144,
1532 offset: 0
1533 },
1534 mile: {
1535 name: 'mile',
1536 base: BASE_UNITS.LENGTH,
1537 prefixes: PREFIXES.NONE,
1538 value: 1609.344,
1539 offset: 0
1540 },
1541 link: {
1542 name: 'link',
1543 base: BASE_UNITS.LENGTH,
1544 prefixes: PREFIXES.NONE,
1545 value: 0.201168,
1546 offset: 0
1547 },
1548 rod: {
1549 name: 'rod',
1550 base: BASE_UNITS.LENGTH,
1551 prefixes: PREFIXES.NONE,
1552 value: 5.0292,
1553 offset: 0
1554 },
1555 chain: {
1556 name: 'chain',
1557 base: BASE_UNITS.LENGTH,
1558 prefixes: PREFIXES.NONE,
1559 value: 20.1168,
1560 offset: 0
1561 },
1562 angstrom: {
1563 name: 'angstrom',
1564 base: BASE_UNITS.LENGTH,
1565 prefixes: PREFIXES.NONE,
1566 value: 1e-10,
1567 offset: 0
1568 },
1569
1570 m: {
1571 name: 'm',
1572 base: BASE_UNITS.LENGTH,
1573 prefixes: PREFIXES.SHORT,
1574 value: 1,
1575 offset: 0
1576 },
1577 'in': {
1578 name: 'in',
1579 base: BASE_UNITS.LENGTH,
1580 prefixes: PREFIXES.NONE,
1581 value: 0.0254,
1582 offset: 0
1583 },
1584 ft: {
1585 name: 'ft',
1586 base: BASE_UNITS.LENGTH,
1587 prefixes: PREFIXES.NONE,
1588 value: 0.3048,
1589 offset: 0
1590 },
1591 yd: {
1592 name: 'yd',
1593 base: BASE_UNITS.LENGTH,
1594 prefixes: PREFIXES.NONE,
1595 value: 0.9144,
1596 offset: 0
1597 },
1598 mi: {
1599 name: 'mi',
1600 base: BASE_UNITS.LENGTH,
1601 prefixes: PREFIXES.NONE,
1602 value: 1609.344,
1603 offset: 0
1604 },
1605 li: {
1606 name: 'li',
1607 base: BASE_UNITS.LENGTH,
1608 prefixes: PREFIXES.NONE,
1609 value: 0.201168,
1610 offset: 0
1611 },
1612 rd: {
1613 name: 'rd',
1614 base: BASE_UNITS.LENGTH,
1615 prefixes: PREFIXES.NONE,
1616 value: 5.029210,
1617 offset: 0
1618 },
1619 ch: {
1620 name: 'ch',
1621 base: BASE_UNITS.LENGTH,
1622 prefixes: PREFIXES.NONE,
1623 value: 20.1168,
1624 offset: 0
1625 },
1626 mil: {
1627 name: 'mil',
1628 base: BASE_UNITS.LENGTH,
1629 prefixes: PREFIXES.NONE,
1630 value: 0.0000254,
1631 offset: 0
1632 }, // 1/1000 inch
1633
1634 // Surface
1635 m2: {
1636 name: 'm2',
1637 base: BASE_UNITS.SURFACE,
1638 prefixes: PREFIXES.SQUARED,
1639 value: 1,
1640 offset: 0
1641 },
1642 sqin: {
1643 name: 'sqin',
1644 base: BASE_UNITS.SURFACE,
1645 prefixes: PREFIXES.NONE,
1646 value: 0.00064516,
1647 offset: 0
1648 }, // 645.16 mm2
1649 sqft: {
1650 name: 'sqft',
1651 base: BASE_UNITS.SURFACE,
1652 prefixes: PREFIXES.NONE,
1653 value: 0.09290304,
1654 offset: 0
1655 }, // 0.09290304 m2
1656 sqyd: {
1657 name: 'sqyd',
1658 base: BASE_UNITS.SURFACE,
1659 prefixes: PREFIXES.NONE,
1660 value: 0.83612736,
1661 offset: 0
1662 }, // 0.83612736 m2
1663 sqmi: {
1664 name: 'sqmi',
1665 base: BASE_UNITS.SURFACE,
1666 prefixes: PREFIXES.NONE,
1667 value: 2589988.110336,
1668 offset: 0
1669 }, // 2.589988110336 km2
1670 sqrd: {
1671 name: 'sqrd',
1672 base: BASE_UNITS.SURFACE,
1673 prefixes: PREFIXES.NONE,
1674 value: 25.29295,
1675 offset: 0
1676 }, // 25.29295 m2
1677 sqch: {
1678 name: 'sqch',
1679 base: BASE_UNITS.SURFACE,
1680 prefixes: PREFIXES.NONE,
1681 value: 404.6873,
1682 offset: 0
1683 }, // 404.6873 m2
1684 sqmil: {
1685 name: 'sqmil',
1686 base: BASE_UNITS.SURFACE,
1687 prefixes: PREFIXES.NONE,
1688 value: 6.4516e-10,
1689 offset: 0
1690 }, // 6.4516 * 10^-10 m2
1691 acre: {
1692 name: 'acre',
1693 base: BASE_UNITS.SURFACE,
1694 prefixes: PREFIXES.NONE,
1695 value: 4046.86,
1696 offset: 0
1697 }, // 4046.86 m2
1698 hectare: {
1699 name: 'hectare',
1700 base: BASE_UNITS.SURFACE,
1701 prefixes: PREFIXES.NONE,
1702 value: 10000,
1703 offset: 0
1704 }, // 10000 m2
1705
1706 // Volume
1707 m3: {
1708 name: 'm3',
1709 base: BASE_UNITS.VOLUME,
1710 prefixes: PREFIXES.CUBIC,
1711 value: 1,
1712 offset: 0
1713 },
1714 L: {
1715 name: 'L',
1716 base: BASE_UNITS.VOLUME,
1717 prefixes: PREFIXES.SHORT,
1718 value: 0.001,
1719 offset: 0
1720 }, // litre
1721 l: {
1722 name: 'l',
1723 base: BASE_UNITS.VOLUME,
1724 prefixes: PREFIXES.SHORT,
1725 value: 0.001,
1726 offset: 0
1727 }, // litre
1728 litre: {
1729 name: 'litre',
1730 base: BASE_UNITS.VOLUME,
1731 prefixes: PREFIXES.LONG,
1732 value: 0.001,
1733 offset: 0
1734 },
1735 cuin: {
1736 name: 'cuin',
1737 base: BASE_UNITS.VOLUME,
1738 prefixes: PREFIXES.NONE,
1739 value: 1.6387064e-5,
1740 offset: 0
1741 }, // 1.6387064e-5 m3
1742 cuft: {
1743 name: 'cuft',
1744 base: BASE_UNITS.VOLUME,
1745 prefixes: PREFIXES.NONE,
1746 value: 0.028316846592,
1747 offset: 0
1748 }, // 28.316 846 592 L
1749 cuyd: {
1750 name: 'cuyd',
1751 base: BASE_UNITS.VOLUME,
1752 prefixes: PREFIXES.NONE,
1753 value: 0.764554857984,
1754 offset: 0
1755 }, // 764.554 857 984 L
1756 teaspoon: {
1757 name: 'teaspoon',
1758 base: BASE_UNITS.VOLUME,
1759 prefixes: PREFIXES.NONE,
1760 value: 0.000005,
1761 offset: 0
1762 }, // 5 mL
1763 tablespoon: {
1764 name: 'tablespoon',
1765 base: BASE_UNITS.VOLUME,
1766 prefixes: PREFIXES.NONE,
1767 value: 0.000015,
1768 offset: 0
1769 }, // 15 mL
1770 // {name: 'cup', base: BASE_UNITS.VOLUME, prefixes: PREFIXES.NONE, value: 0.000240, offset: 0}, // 240 mL // not possible, we have already another cup
1771 drop: {
1772 name: 'drop',
1773 base: BASE_UNITS.VOLUME,
1774 prefixes: PREFIXES.NONE,
1775 value: 5e-8,
1776 offset: 0
1777 }, // 0.05 mL = 5e-8 m3
1778 gtt: {
1779 name: 'gtt',
1780 base: BASE_UNITS.VOLUME,
1781 prefixes: PREFIXES.NONE,
1782 value: 5e-8,
1783 offset: 0
1784 }, // 0.05 mL = 5e-8 m3
1785
1786 // Liquid volume
1787 minim: {
1788 name: 'minim',
1789 base: BASE_UNITS.VOLUME,
1790 prefixes: PREFIXES.NONE,
1791 value: 0.00000006161152,
1792 offset: 0
1793 }, // 0.06161152 mL
1794 fluiddram: {
1795 name: 'fluiddram',
1796 base: BASE_UNITS.VOLUME,
1797 prefixes: PREFIXES.NONE,
1798 value: 0.0000036966911,
1799 offset: 0
1800 }, // 3.696691 mL
1801 fluidounce: {
1802 name: 'fluidounce',
1803 base: BASE_UNITS.VOLUME,
1804 prefixes: PREFIXES.NONE,
1805 value: 0.00002957353,
1806 offset: 0
1807 }, // 29.57353 mL
1808 gill: {
1809 name: 'gill',
1810 base: BASE_UNITS.VOLUME,
1811 prefixes: PREFIXES.NONE,
1812 value: 0.0001182941,
1813 offset: 0
1814 }, // 118.2941 mL
1815 cc: {
1816 name: 'cc',
1817 base: BASE_UNITS.VOLUME,
1818 prefixes: PREFIXES.NONE,
1819 value: 1e-6,
1820 offset: 0
1821 }, // 1e-6 L
1822 cup: {
1823 name: 'cup',
1824 base: BASE_UNITS.VOLUME,
1825 prefixes: PREFIXES.NONE,
1826 value: 0.0002365882,
1827 offset: 0
1828 }, // 236.5882 mL
1829 pint: {
1830 name: 'pint',
1831 base: BASE_UNITS.VOLUME,
1832 prefixes: PREFIXES.NONE,
1833 value: 0.0004731765,
1834 offset: 0
1835 }, // 473.1765 mL
1836 quart: {
1837 name: 'quart',
1838 base: BASE_UNITS.VOLUME,
1839 prefixes: PREFIXES.NONE,
1840 value: 0.0009463529,
1841 offset: 0
1842 }, // 946.3529 mL
1843 gallon: {
1844 name: 'gallon',
1845 base: BASE_UNITS.VOLUME,
1846 prefixes: PREFIXES.NONE,
1847 value: 0.003785412,
1848 offset: 0
1849 }, // 3.785412 L
1850 beerbarrel: {
1851 name: 'beerbarrel',
1852 base: BASE_UNITS.VOLUME,
1853 prefixes: PREFIXES.NONE,
1854 value: 0.1173478,
1855 offset: 0
1856 }, // 117.3478 L
1857 oilbarrel: {
1858 name: 'oilbarrel',
1859 base: BASE_UNITS.VOLUME,
1860 prefixes: PREFIXES.NONE,
1861 value: 0.1589873,
1862 offset: 0
1863 }, // 158.9873 L
1864 hogshead: {
1865 name: 'hogshead',
1866 base: BASE_UNITS.VOLUME,
1867 prefixes: PREFIXES.NONE,
1868 value: 0.2384810,
1869 offset: 0
1870 }, // 238.4810 L
1871
1872 // {name: 'min', base: BASE_UNITS.VOLUME, prefixes: PREFIXES.NONE, value: 0.00000006161152, offset: 0}, // 0.06161152 mL // min is already in use as minute
1873 fldr: {
1874 name: 'fldr',
1875 base: BASE_UNITS.VOLUME,
1876 prefixes: PREFIXES.NONE,
1877 value: 0.0000036966911,
1878 offset: 0
1879 }, // 3.696691 mL
1880 floz: {
1881 name: 'floz',
1882 base: BASE_UNITS.VOLUME,
1883 prefixes: PREFIXES.NONE,
1884 value: 0.00002957353,
1885 offset: 0
1886 }, // 29.57353 mL
1887 gi: {
1888 name: 'gi',
1889 base: BASE_UNITS.VOLUME,
1890 prefixes: PREFIXES.NONE,
1891 value: 0.0001182941,
1892 offset: 0
1893 }, // 118.2941 mL
1894 cp: {
1895 name: 'cp',
1896 base: BASE_UNITS.VOLUME,
1897 prefixes: PREFIXES.NONE,
1898 value: 0.0002365882,
1899 offset: 0
1900 }, // 236.5882 mL
1901 pt: {
1902 name: 'pt',
1903 base: BASE_UNITS.VOLUME,
1904 prefixes: PREFIXES.NONE,
1905 value: 0.0004731765,
1906 offset: 0
1907 }, // 473.1765 mL
1908 qt: {
1909 name: 'qt',
1910 base: BASE_UNITS.VOLUME,
1911 prefixes: PREFIXES.NONE,
1912 value: 0.0009463529,
1913 offset: 0
1914 }, // 946.3529 mL
1915 gal: {
1916 name: 'gal',
1917 base: BASE_UNITS.VOLUME,
1918 prefixes: PREFIXES.NONE,
1919 value: 0.003785412,
1920 offset: 0
1921 }, // 3.785412 L
1922 bbl: {
1923 name: 'bbl',
1924 base: BASE_UNITS.VOLUME,
1925 prefixes: PREFIXES.NONE,
1926 value: 0.1173478,
1927 offset: 0
1928 }, // 117.3478 L
1929 obl: {
1930 name: 'obl',
1931 base: BASE_UNITS.VOLUME,
1932 prefixes: PREFIXES.NONE,
1933 value: 0.1589873,
1934 offset: 0
1935 }, // 158.9873 L
1936 // {name: 'hogshead', base: BASE_UNITS.VOLUME, prefixes: PREFIXES.NONE, value: 0.2384810, offset: 0}, // 238.4810 L // TODO: hh?
1937
1938 // Mass
1939 g: {
1940 name: 'g',
1941 base: BASE_UNITS.MASS,
1942 prefixes: PREFIXES.SHORT,
1943 value: 0.001,
1944 offset: 0
1945 },
1946 gram: {
1947 name: 'gram',
1948 base: BASE_UNITS.MASS,
1949 prefixes: PREFIXES.LONG,
1950 value: 0.001,
1951 offset: 0
1952 },
1953
1954 ton: {
1955 name: 'ton',
1956 base: BASE_UNITS.MASS,
1957 prefixes: PREFIXES.SHORT,
1958 value: 907.18474,
1959 offset: 0
1960 },
1961 tonne: {
1962 name: 'tonne',
1963 base: BASE_UNITS.MASS,
1964 prefixes: PREFIXES.SHORT,
1965 value: 1000,
1966 offset: 0
1967 },
1968
1969 grain: {
1970 name: 'grain',
1971 base: BASE_UNITS.MASS,
1972 prefixes: PREFIXES.NONE,
1973 value: 64.79891e-6,
1974 offset: 0
1975 },
1976 dram: {
1977 name: 'dram',
1978 base: BASE_UNITS.MASS,
1979 prefixes: PREFIXES.NONE,
1980 value: 1.7718451953125e-3,
1981 offset: 0
1982 },
1983 ounce: {
1984 name: 'ounce',
1985 base: BASE_UNITS.MASS,
1986 prefixes: PREFIXES.NONE,
1987 value: 28.349523125e-3,
1988 offset: 0
1989 },
1990 poundmass: {
1991 name: 'poundmass',
1992 base: BASE_UNITS.MASS,
1993 prefixes: PREFIXES.NONE,
1994 value: 453.59237e-3,
1995 offset: 0
1996 },
1997 hundredweight: {
1998 name: 'hundredweight',
1999 base: BASE_UNITS.MASS,
2000 prefixes: PREFIXES.NONE,
2001 value: 45.359237,
2002 offset: 0
2003 },
2004 stick: {
2005 name: 'stick',
2006 base: BASE_UNITS.MASS,
2007 prefixes: PREFIXES.NONE,
2008 value: 115e-3,
2009 offset: 0
2010 },
2011 stone: {
2012 name: 'stone',
2013 base: BASE_UNITS.MASS,
2014 prefixes: PREFIXES.NONE,
2015 value: 6.35029318,
2016 offset: 0
2017 },
2018
2019 gr: {
2020 name: 'gr',
2021 base: BASE_UNITS.MASS,
2022 prefixes: PREFIXES.NONE,
2023 value: 64.79891e-6,
2024 offset: 0
2025 },
2026 dr: {
2027 name: 'dr',
2028 base: BASE_UNITS.MASS,
2029 prefixes: PREFIXES.NONE,
2030 value: 1.7718451953125e-3,
2031 offset: 0
2032 },
2033 oz: {
2034 name: 'oz',
2035 base: BASE_UNITS.MASS,
2036 prefixes: PREFIXES.NONE,
2037 value: 28.349523125e-3,
2038 offset: 0
2039 },
2040 lbm: {
2041 name: 'lbm',
2042 base: BASE_UNITS.MASS,
2043 prefixes: PREFIXES.NONE,
2044 value: 453.59237e-3,
2045 offset: 0
2046 },
2047 cwt: {
2048 name: 'cwt',
2049 base: BASE_UNITS.MASS,
2050 prefixes: PREFIXES.NONE,
2051 value: 45.359237,
2052 offset: 0
2053 },
2054
2055 // Time
2056 s: {
2057 name: 's',
2058 base: BASE_UNITS.TIME,
2059 prefixes: PREFIXES.SHORT,
2060 value: 1,
2061 offset: 0
2062 },
2063 min: {
2064 name: 'min',
2065 base: BASE_UNITS.TIME,
2066 prefixes: PREFIXES.NONE,
2067 value: 60,
2068 offset: 0
2069 },
2070 h: {
2071 name: 'h',
2072 base: BASE_UNITS.TIME,
2073 prefixes: PREFIXES.NONE,
2074 value: 3600,
2075 offset: 0
2076 },
2077 second: {
2078 name: 'second',
2079 base: BASE_UNITS.TIME,
2080 prefixes: PREFIXES.LONG,
2081 value: 1,
2082 offset: 0
2083 },
2084 sec: {
2085 name: 'sec',
2086 base: BASE_UNITS.TIME,
2087 prefixes: PREFIXES.LONG,
2088 value: 1,
2089 offset: 0
2090 },
2091 minute: {
2092 name: 'minute',
2093 base: BASE_UNITS.TIME,
2094 prefixes: PREFIXES.NONE,
2095 value: 60,
2096 offset: 0
2097 },
2098 hour: {
2099 name: 'hour',
2100 base: BASE_UNITS.TIME,
2101 prefixes: PREFIXES.NONE,
2102 value: 3600,
2103 offset: 0
2104 },
2105 day: {
2106 name: 'day',
2107 base: BASE_UNITS.TIME,
2108 prefixes: PREFIXES.NONE,
2109 value: 86400,
2110 offset: 0
2111 },
2112 week: {
2113 name: 'week',
2114 base: BASE_UNITS.TIME,
2115 prefixes: PREFIXES.NONE,
2116 value: 7 * 86400,
2117 offset: 0
2118 },
2119 month: {
2120 name: 'month',
2121 base: BASE_UNITS.TIME,
2122 prefixes: PREFIXES.NONE,
2123 value: 2629800, // 1/12th of Julian year
2124 offset: 0
2125 },
2126 year: {
2127 name: 'year',
2128 base: BASE_UNITS.TIME,
2129 prefixes: PREFIXES.NONE,
2130 value: 31557600, // Julian year
2131 offset: 0
2132 },
2133 decade: {
2134 name: 'decade',
2135 base: BASE_UNITS.TIME,
2136 prefixes: PREFIXES.NONE,
2137 value: 315576000, // Julian decade
2138 offset: 0
2139 },
2140 century: {
2141 name: 'century',
2142 base: BASE_UNITS.TIME,
2143 prefixes: PREFIXES.NONE,
2144 value: 3155760000, // Julian century
2145 offset: 0
2146 },
2147 millennium: {
2148 name: 'millennium',
2149 base: BASE_UNITS.TIME,
2150 prefixes: PREFIXES.NONE,
2151 value: 31557600000, // Julian millennium
2152 offset: 0
2153 },
2154
2155 // Frequency
2156 hertz: {
2157 name: 'Hertz',
2158 base: BASE_UNITS.FREQUENCY,
2159 prefixes: PREFIXES.LONG,
2160 value: 1,
2161 offset: 0,
2162 reciprocal: true
2163 },
2164 Hz: {
2165 name: 'Hz',
2166 base: BASE_UNITS.FREQUENCY,
2167 prefixes: PREFIXES.SHORT,
2168 value: 1,
2169 offset: 0,
2170 reciprocal: true
2171 },
2172
2173 // Angle
2174 rad: {
2175 name: 'rad',
2176 base: BASE_UNITS.ANGLE,
2177 prefixes: PREFIXES.SHORT,
2178 value: 1,
2179 offset: 0
2180 },
2181 radian: {
2182 name: 'radian',
2183 base: BASE_UNITS.ANGLE,
2184 prefixes: PREFIXES.LONG,
2185 value: 1,
2186 offset: 0
2187 },
2188 // deg = rad / (2*pi) * 360 = rad / 0.017453292519943295769236907684888
2189 deg: {
2190 name: 'deg',
2191 base: BASE_UNITS.ANGLE,
2192 prefixes: PREFIXES.SHORT,
2193 value: null, // will be filled in by calculateAngleValues()
2194 offset: 0
2195 },
2196 degree: {
2197 name: 'degree',
2198 base: BASE_UNITS.ANGLE,
2199 prefixes: PREFIXES.LONG,
2200 value: null, // will be filled in by calculateAngleValues()
2201 offset: 0
2202 },
2203 // grad = rad / (2*pi) * 400 = rad / 0.015707963267948966192313216916399
2204 grad: {
2205 name: 'grad',
2206 base: BASE_UNITS.ANGLE,
2207 prefixes: PREFIXES.SHORT,
2208 value: null, // will be filled in by calculateAngleValues()
2209 offset: 0
2210 },
2211 gradian: {
2212 name: 'gradian',
2213 base: BASE_UNITS.ANGLE,
2214 prefixes: PREFIXES.LONG,
2215 value: null, // will be filled in by calculateAngleValues()
2216 offset: 0
2217 },
2218 // cycle = rad / (2*pi) = rad / 6.2831853071795864769252867665793
2219 cycle: {
2220 name: 'cycle',
2221 base: BASE_UNITS.ANGLE,
2222 prefixes: PREFIXES.NONE,
2223 value: null, // will be filled in by calculateAngleValues()
2224 offset: 0
2225 },
2226 // arcsec = rad / (3600 * (360 / 2 * pi)) = rad / 0.0000048481368110953599358991410235795
2227 arcsec: {
2228 name: 'arcsec',
2229 base: BASE_UNITS.ANGLE,
2230 prefixes: PREFIXES.NONE,
2231 value: null, // will be filled in by calculateAngleValues()
2232 offset: 0
2233 },
2234 // arcmin = rad / (60 * (360 / 2 * pi)) = rad / 0.00029088820866572159615394846141477
2235 arcmin: {
2236 name: 'arcmin',
2237 base: BASE_UNITS.ANGLE,
2238 prefixes: PREFIXES.NONE,
2239 value: null, // will be filled in by calculateAngleValues()
2240 offset: 0
2241 },
2242
2243 // Electric current
2244 A: {
2245 name: 'A',
2246 base: BASE_UNITS.CURRENT,
2247 prefixes: PREFIXES.SHORT,
2248 value: 1,
2249 offset: 0
2250 },
2251 ampere: {
2252 name: 'ampere',
2253 base: BASE_UNITS.CURRENT,
2254 prefixes: PREFIXES.LONG,
2255 value: 1,
2256 offset: 0
2257 },
2258
2259 // Temperature
2260 // K(C) = °C + 273.15
2261 // K(F) = (°F + 459.67) / 1.8
2262 // K(R) = °R / 1.8
2263 K: {
2264 name: 'K',
2265 base: BASE_UNITS.TEMPERATURE,
2266 prefixes: PREFIXES.NONE,
2267 value: 1,
2268 offset: 0
2269 },
2270 degC: {
2271 name: 'degC',
2272 base: BASE_UNITS.TEMPERATURE,
2273 prefixes: PREFIXES.NONE,
2274 value: 1,
2275 offset: 273.15
2276 },
2277 degF: {
2278 name: 'degF',
2279 base: BASE_UNITS.TEMPERATURE,
2280 prefixes: PREFIXES.NONE,
2281 value: 1 / 1.8,
2282 offset: 459.67
2283 },
2284 degR: {
2285 name: 'degR',
2286 base: BASE_UNITS.TEMPERATURE,
2287 prefixes: PREFIXES.NONE,
2288 value: 1 / 1.8,
2289 offset: 0
2290 },
2291 kelvin: {
2292 name: 'kelvin',
2293 base: BASE_UNITS.TEMPERATURE,
2294 prefixes: PREFIXES.NONE,
2295 value: 1,
2296 offset: 0
2297 },
2298 celsius: {
2299 name: 'celsius',
2300 base: BASE_UNITS.TEMPERATURE,
2301 prefixes: PREFIXES.NONE,
2302 value: 1,
2303 offset: 273.15
2304 },
2305 fahrenheit: {
2306 name: 'fahrenheit',
2307 base: BASE_UNITS.TEMPERATURE,
2308 prefixes: PREFIXES.NONE,
2309 value: 1 / 1.8,
2310 offset: 459.67
2311 },
2312 rankine: {
2313 name: 'rankine',
2314 base: BASE_UNITS.TEMPERATURE,
2315 prefixes: PREFIXES.NONE,
2316 value: 1 / 1.8,
2317 offset: 0
2318 },
2319
2320 // amount of substance
2321 mol: {
2322 name: 'mol',
2323 base: BASE_UNITS.AMOUNT_OF_SUBSTANCE,
2324 prefixes: PREFIXES.SHORT,
2325 value: 1,
2326 offset: 0
2327 },
2328 mole: {
2329 name: 'mole',
2330 base: BASE_UNITS.AMOUNT_OF_SUBSTANCE,
2331 prefixes: PREFIXES.LONG,
2332 value: 1,
2333 offset: 0
2334 },
2335
2336 // luminous intensity
2337 cd: {
2338 name: 'cd',
2339 base: BASE_UNITS.LUMINOUS_INTENSITY,
2340 prefixes: PREFIXES.NONE,
2341 value: 1,
2342 offset: 0
2343 },
2344 candela: {
2345 name: 'candela',
2346 base: BASE_UNITS.LUMINOUS_INTENSITY,
2347 prefixes: PREFIXES.NONE,
2348 value: 1,
2349 offset: 0
2350 },
2351 // TODO: units STERADIAN
2352 // {name: 'sr', base: BASE_UNITS.STERADIAN, prefixes: PREFIXES.NONE, value: 1, offset: 0},
2353 // {name: 'steradian', base: BASE_UNITS.STERADIAN, prefixes: PREFIXES.NONE, value: 1, offset: 0},
2354
2355 // Force
2356 N: {
2357 name: 'N',
2358 base: BASE_UNITS.FORCE,
2359 prefixes: PREFIXES.SHORT,
2360 value: 1,
2361 offset: 0
2362 },
2363 newton: {
2364 name: 'newton',
2365 base: BASE_UNITS.FORCE,
2366 prefixes: PREFIXES.LONG,
2367 value: 1,
2368 offset: 0
2369 },
2370 dyn: {
2371 name: 'dyn',
2372 base: BASE_UNITS.FORCE,
2373 prefixes: PREFIXES.SHORT,
2374 value: 0.00001,
2375 offset: 0
2376 },
2377 dyne: {
2378 name: 'dyne',
2379 base: BASE_UNITS.FORCE,
2380 prefixes: PREFIXES.LONG,
2381 value: 0.00001,
2382 offset: 0
2383 },
2384 lbf: {
2385 name: 'lbf',
2386 base: BASE_UNITS.FORCE,
2387 prefixes: PREFIXES.NONE,
2388 value: 4.4482216152605,
2389 offset: 0
2390 },
2391 poundforce: {
2392 name: 'poundforce',
2393 base: BASE_UNITS.FORCE,
2394 prefixes: PREFIXES.NONE,
2395 value: 4.4482216152605,
2396 offset: 0
2397 },
2398 kip: {
2399 name: 'kip',
2400 base: BASE_UNITS.FORCE,
2401 prefixes: PREFIXES.LONG,
2402 value: 4448.2216,
2403 offset: 0
2404 },
2405
2406 // Energy
2407 J: {
2408 name: 'J',
2409 base: BASE_UNITS.ENERGY,
2410 prefixes: PREFIXES.SHORT,
2411 value: 1,
2412 offset: 0
2413 },
2414 joule: {
2415 name: 'joule',
2416 base: BASE_UNITS.ENERGY,
2417 prefixes: PREFIXES.SHORT,
2418 value: 1,
2419 offset: 0
2420 },
2421 erg: {
2422 name: 'erg',
2423 base: BASE_UNITS.ENERGY,
2424 prefixes: PREFIXES.NONE,
2425 value: 1e-7,
2426 offset: 0
2427 },
2428 Wh: {
2429 name: 'Wh',
2430 base: BASE_UNITS.ENERGY,
2431 prefixes: PREFIXES.SHORT,
2432 value: 3600,
2433 offset: 0
2434 },
2435 BTU: {
2436 name: 'BTU',
2437 base: BASE_UNITS.ENERGY,
2438 prefixes: PREFIXES.BTU,
2439 value: 1055.05585262,
2440 offset: 0
2441 },
2442 eV: {
2443 name: 'eV',
2444 base: BASE_UNITS.ENERGY,
2445 prefixes: PREFIXES.SHORT,
2446 value: 1.602176565e-19,
2447 offset: 0
2448 },
2449 electronvolt: {
2450 name: 'electronvolt',
2451 base: BASE_UNITS.ENERGY,
2452 prefixes: PREFIXES.LONG,
2453 value: 1.602176565e-19,
2454 offset: 0
2455 },
2456
2457 // Power
2458 W: {
2459 name: 'W',
2460 base: BASE_UNITS.POWER,
2461 prefixes: PREFIXES.SHORT,
2462 value: 1,
2463 offset: 0
2464 },
2465 watt: {
2466 name: 'watt',
2467 base: BASE_UNITS.POWER,
2468 prefixes: PREFIXES.LONG,
2469 value: 1,
2470 offset: 0
2471 },
2472 hp: {
2473 name: 'hp',
2474 base: BASE_UNITS.POWER,
2475 prefixes: PREFIXES.NONE,
2476 value: 745.6998715386,
2477 offset: 0
2478 },
2479
2480 // Electrical power units
2481 VAR: {
2482 name: 'VAR',
2483 base: BASE_UNITS.POWER,
2484 prefixes: PREFIXES.SHORT,
2485 value: Complex.I,
2486 offset: 0
2487 },
2488
2489 VA: {
2490 name: 'VA',
2491 base: BASE_UNITS.POWER,
2492 prefixes: PREFIXES.SHORT,
2493 value: 1,
2494 offset: 0
2495 },
2496
2497 // Pressure
2498 Pa: {
2499 name: 'Pa',
2500 base: BASE_UNITS.PRESSURE,
2501 prefixes: PREFIXES.SHORT,
2502 value: 1,
2503 offset: 0
2504 },
2505 psi: {
2506 name: 'psi',
2507 base: BASE_UNITS.PRESSURE,
2508 prefixes: PREFIXES.NONE,
2509 value: 6894.75729276459,
2510 offset: 0
2511 },
2512 atm: {
2513 name: 'atm',
2514 base: BASE_UNITS.PRESSURE,
2515 prefixes: PREFIXES.NONE,
2516 value: 101325,
2517 offset: 0
2518 },
2519 bar: {
2520 name: 'bar',
2521 base: BASE_UNITS.PRESSURE,
2522 prefixes: PREFIXES.SHORTLONG,
2523 value: 100000,
2524 offset: 0
2525 },
2526 torr: {
2527 name: 'torr',
2528 base: BASE_UNITS.PRESSURE,
2529 prefixes: PREFIXES.NONE,
2530 value: 133.322,
2531 offset: 0
2532 },
2533 mmHg: {
2534 name: 'mmHg',
2535 base: BASE_UNITS.PRESSURE,
2536 prefixes: PREFIXES.NONE,
2537 value: 133.322,
2538 offset: 0
2539 },
2540 mmH2O: {
2541 name: 'mmH2O',
2542 base: BASE_UNITS.PRESSURE,
2543 prefixes: PREFIXES.NONE,
2544 value: 9.80665,
2545 offset: 0
2546 },
2547 cmH2O: {
2548 name: 'cmH2O',
2549 base: BASE_UNITS.PRESSURE,
2550 prefixes: PREFIXES.NONE,
2551 value: 98.0665,
2552 offset: 0
2553 },
2554
2555 // Electric charge
2556 coulomb: {
2557 name: 'coulomb',
2558 base: BASE_UNITS.ELECTRIC_CHARGE,
2559 prefixes: PREFIXES.LONG,
2560 value: 1,
2561 offset: 0
2562 },
2563 C: {
2564 name: 'C',
2565 base: BASE_UNITS.ELECTRIC_CHARGE,
2566 prefixes: PREFIXES.SHORT,
2567 value: 1,
2568 offset: 0
2569 },
2570 // Electric capacitance
2571 farad: {
2572 name: 'farad',
2573 base: BASE_UNITS.ELECTRIC_CAPACITANCE,
2574 prefixes: PREFIXES.LONG,
2575 value: 1,
2576 offset: 0
2577 },
2578 F: {
2579 name: 'F',
2580 base: BASE_UNITS.ELECTRIC_CAPACITANCE,
2581 prefixes: PREFIXES.SHORT,
2582 value: 1,
2583 offset: 0
2584 },
2585 // Electric potential
2586 volt: {
2587 name: 'volt',
2588 base: BASE_UNITS.ELECTRIC_POTENTIAL,
2589 prefixes: PREFIXES.LONG,
2590 value: 1,
2591 offset: 0
2592 },
2593 V: {
2594 name: 'V',
2595 base: BASE_UNITS.ELECTRIC_POTENTIAL,
2596 prefixes: PREFIXES.SHORT,
2597 value: 1,
2598 offset: 0
2599 },
2600 // Electric resistance
2601 ohm: {
2602 name: 'ohm',
2603 base: BASE_UNITS.ELECTRIC_RESISTANCE,
2604 prefixes: PREFIXES.SHORTLONG, // Both Mohm and megaohm are acceptable
2605 value: 1,
2606 offset: 0
2607 },
2608 /*
2609 * Unicode breaks in browsers if charset is not specified
2610 Ω: {
2611 name: 'Ω',
2612 base: BASE_UNITS.ELECTRIC_RESISTANCE,
2613 prefixes: PREFIXES.SHORT,
2614 value: 1,
2615 offset: 0
2616 },
2617 */
2618 // Electric inductance
2619 henry: {
2620 name: 'henry',
2621 base: BASE_UNITS.ELECTRIC_INDUCTANCE,
2622 prefixes: PREFIXES.LONG,
2623 value: 1,
2624 offset: 0
2625 },
2626 H: {
2627 name: 'H',
2628 base: BASE_UNITS.ELECTRIC_INDUCTANCE,
2629 prefixes: PREFIXES.SHORT,
2630 value: 1,
2631 offset: 0
2632 },
2633 // Electric conductance
2634 siemens: {
2635 name: 'siemens',
2636 base: BASE_UNITS.ELECTRIC_CONDUCTANCE,
2637 prefixes: PREFIXES.LONG,
2638 value: 1,
2639 offset: 0
2640 },
2641 S: {
2642 name: 'S',
2643 base: BASE_UNITS.ELECTRIC_CONDUCTANCE,
2644 prefixes: PREFIXES.SHORT,
2645 value: 1,
2646 offset: 0
2647 },
2648 // Magnetic flux
2649 weber: {
2650 name: 'weber',
2651 base: BASE_UNITS.MAGNETIC_FLUX,
2652 prefixes: PREFIXES.LONG,
2653 value: 1,
2654 offset: 0
2655 },
2656 Wb: {
2657 name: 'Wb',
2658 base: BASE_UNITS.MAGNETIC_FLUX,
2659 prefixes: PREFIXES.SHORT,
2660 value: 1,
2661 offset: 0
2662 },
2663 // Magnetic flux density
2664 tesla: {
2665 name: 'tesla',
2666 base: BASE_UNITS.MAGNETIC_FLUX_DENSITY,
2667 prefixes: PREFIXES.LONG,
2668 value: 1,
2669 offset: 0
2670 },
2671 T: {
2672 name: 'T',
2673 base: BASE_UNITS.MAGNETIC_FLUX_DENSITY,
2674 prefixes: PREFIXES.SHORT,
2675 value: 1,
2676 offset: 0
2677 },
2678
2679 // Binary
2680 b: {
2681 name: 'b',
2682 base: BASE_UNITS.BIT,
2683 prefixes: PREFIXES.BINARY_SHORT,
2684 value: 1,
2685 offset: 0
2686 },
2687 bits: {
2688 name: 'bits',
2689 base: BASE_UNITS.BIT,
2690 prefixes: PREFIXES.BINARY_LONG,
2691 value: 1,
2692 offset: 0
2693 },
2694 B: {
2695 name: 'B',
2696 base: BASE_UNITS.BIT,
2697 prefixes: PREFIXES.BINARY_SHORT,
2698 value: 8,
2699 offset: 0
2700 },
2701 bytes: {
2702 name: 'bytes',
2703 base: BASE_UNITS.BIT,
2704 prefixes: PREFIXES.BINARY_LONG,
2705 value: 8,
2706 offset: 0
2707 }
2708 }
2709
2710 // aliases (formerly plurals)
2711 const ALIASES = {
2712 meters: 'meter',
2713 inches: 'inch',
2714 feet: 'foot',
2715 yards: 'yard',
2716 miles: 'mile',
2717 links: 'link',
2718 rods: 'rod',
2719 chains: 'chain',
2720 angstroms: 'angstrom',
2721
2722 lt: 'l',
2723 litres: 'litre',
2724 liter: 'litre',
2725 liters: 'litre',
2726 teaspoons: 'teaspoon',
2727 tablespoons: 'tablespoon',
2728 minims: 'minim',
2729 fluiddrams: 'fluiddram',
2730 fluidounces: 'fluidounce',
2731 gills: 'gill',
2732 cups: 'cup',
2733 pints: 'pint',
2734 quarts: 'quart',
2735 gallons: 'gallon',
2736 beerbarrels: 'beerbarrel',
2737 oilbarrels: 'oilbarrel',
2738 hogsheads: 'hogshead',
2739 gtts: 'gtt',
2740
2741 grams: 'gram',
2742 tons: 'ton',
2743 tonnes: 'tonne',
2744 grains: 'grain',
2745 drams: 'dram',
2746 ounces: 'ounce',
2747 poundmasses: 'poundmass',
2748 hundredweights: 'hundredweight',
2749 sticks: 'stick',
2750 lb: 'lbm',
2751 lbs: 'lbm',
2752
2753 kips: 'kip',
2754
2755 acres: 'acre',
2756 hectares: 'hectare',
2757 sqfeet: 'sqft',
2758 sqyard: 'sqyd',
2759 sqmile: 'sqmi',
2760 sqmiles: 'sqmi',
2761
2762 mmhg: 'mmHg',
2763 mmh2o: 'mmH2O',
2764 cmh2o: 'cmH2O',
2765
2766 seconds: 'second',
2767 secs: 'second',
2768 minutes: 'minute',
2769 mins: 'minute',
2770 hours: 'hour',
2771 hr: 'hour',
2772 hrs: 'hour',
2773 days: 'day',
2774 weeks: 'week',
2775 months: 'month',
2776 years: 'year',
2777 decades: 'decade',
2778 centuries: 'century',
2779 millennia: 'millennium',
2780
2781 hertz: 'hertz',
2782
2783 radians: 'radian',
2784 degrees: 'degree',
2785 gradians: 'gradian',
2786 cycles: 'cycle',
2787 arcsecond: 'arcsec',
2788 arcseconds: 'arcsec',
2789 arcminute: 'arcmin',
2790 arcminutes: 'arcmin',
2791
2792 BTUs: 'BTU',
2793 watts: 'watt',
2794 joules: 'joule',
2795
2796 amperes: 'ampere',
2797 coulombs: 'coulomb',
2798 volts: 'volt',
2799 ohms: 'ohm',
2800 farads: 'farad',
2801 webers: 'weber',
2802 teslas: 'tesla',
2803 electronvolts: 'electronvolt',
2804 moles: 'mole'
2805
2806 }
2807
2808 /**
2809 * Calculate the values for the angle units.
2810 * Value is calculated as number or BigNumber depending on the configuration
2811 * @param {{number: 'number' | 'BigNumber'}} config
2812 */
2813 function calculateAngleValues (config) {
2814 if (config.number === 'BigNumber') {
2815 const pi = constants.pi(type.BigNumber)
2816 UNITS.rad.value = new type.BigNumber(1)
2817 UNITS.deg.value = pi.div(180) // 2 * pi / 360
2818 UNITS.grad.value = pi.div(200) // 2 * pi / 400
2819 UNITS.cycle.value = pi.times(2) // 2 * pi
2820 UNITS.arcsec.value = pi.div(648000) // 2 * pi / 360 / 3600
2821 UNITS.arcmin.value = pi.div(10800) // 2 * pi / 360 / 60
2822 } else { // number
2823 UNITS.rad.value = 1
2824 UNITS.deg.value = Math.PI / 180 // 2 * pi / 360
2825 UNITS.grad.value = Math.PI / 200 // 2 * pi / 400
2826 UNITS.cycle.value = Math.PI * 2 // 2 * pi
2827 UNITS.arcsec.value = Math.PI / 648000 // 2 * pi / 360 / 3600
2828 UNITS.arcmin.value = Math.PI / 10800 // 2 * pi / 360 / 60
2829 }
2830
2831 // copy to the full names of the angles
2832 UNITS.radian.value = UNITS.rad.value
2833 UNITS.degree.value = UNITS.deg.value
2834 UNITS.gradian.value = UNITS.grad.value
2835 }
2836
2837 // apply the angle values now
2838 calculateAngleValues(config)
2839
2840 // recalculate the values on change of configuration
2841 math.on('config', function (curr, prev) {
2842 if (curr.number !== prev.number) {
2843 calculateAngleValues(curr)
2844 }
2845 })
2846
2847 /**
2848 * A unit system is a set of dimensionally independent base units plus a set of derived units, formed by multiplication and division of the base units, that are by convention used with the unit system.
2849 * A user perhaps could issue a command to select a preferred unit system, or use the default (see below).
2850 * Auto unit system: The default unit system is updated on the fly anytime a unit is parsed. The corresponding unit in the default unit system is updated, so that answers are given in the same units the user supplies.
2851 */
2852 const UNIT_SYSTEMS = {
2853 si: {
2854 // Base units
2855 NONE: { unit: UNIT_NONE, prefix: PREFIXES.NONE[''] },
2856 LENGTH: { unit: UNITS.m, prefix: PREFIXES.SHORT[''] },
2857 MASS: { unit: UNITS.g, prefix: PREFIXES.SHORT['k'] },
2858 TIME: { unit: UNITS.s, prefix: PREFIXES.SHORT[''] },
2859 CURRENT: { unit: UNITS.A, prefix: PREFIXES.SHORT[''] },
2860 TEMPERATURE: { unit: UNITS.K, prefix: PREFIXES.SHORT[''] },
2861 LUMINOUS_INTENSITY: { unit: UNITS.cd, prefix: PREFIXES.SHORT[''] },
2862 AMOUNT_OF_SUBSTANCE: { unit: UNITS.mol, prefix: PREFIXES.SHORT[''] },
2863 ANGLE: { unit: UNITS.rad, prefix: PREFIXES.SHORT[''] },
2864 BIT: { unit: UNITS.bit, prefix: PREFIXES.SHORT[''] },
2865
2866 // Derived units
2867 FORCE: { unit: UNITS.N, prefix: PREFIXES.SHORT[''] },
2868 ENERGY: { unit: UNITS.J, prefix: PREFIXES.SHORT[''] },
2869 POWER: { unit: UNITS.W, prefix: PREFIXES.SHORT[''] },
2870 PRESSURE: { unit: UNITS.Pa, prefix: PREFIXES.SHORT[''] },
2871 ELECTRIC_CHARGE: { unit: UNITS.C, prefix: PREFIXES.SHORT[''] },
2872 ELECTRIC_CAPACITANCE: { unit: UNITS.F, prefix: PREFIXES.SHORT[''] },
2873 ELECTRIC_POTENTIAL: { unit: UNITS.V, prefix: PREFIXES.SHORT[''] },
2874 ELECTRIC_RESISTANCE: { unit: UNITS.ohm, prefix: PREFIXES.SHORT[''] },
2875 ELECTRIC_INDUCTANCE: { unit: UNITS.H, prefix: PREFIXES.SHORT[''] },
2876 ELECTRIC_CONDUCTANCE: { unit: UNITS.S, prefix: PREFIXES.SHORT[''] },
2877 MAGNETIC_FLUX: { unit: UNITS.Wb, prefix: PREFIXES.SHORT[''] },
2878 MAGNETIC_FLUX_DENSITY: { unit: UNITS.T, prefix: PREFIXES.SHORT[''] },
2879 FREQUENCY: { unit: UNITS.Hz, prefix: PREFIXES.SHORT[''] }
2880 }
2881 }
2882
2883 // Clone to create the other unit systems
2884 UNIT_SYSTEMS.cgs = JSON.parse(JSON.stringify(UNIT_SYSTEMS.si))
2885 UNIT_SYSTEMS.cgs.LENGTH = { unit: UNITS.m, prefix: PREFIXES.SHORT['c'] }
2886 UNIT_SYSTEMS.cgs.MASS = { unit: UNITS.g, prefix: PREFIXES.SHORT[''] }
2887 UNIT_SYSTEMS.cgs.FORCE = { unit: UNITS.dyn, prefix: PREFIXES.SHORT[''] }
2888 UNIT_SYSTEMS.cgs.ENERGY = { unit: UNITS.erg, prefix: PREFIXES.NONE[''] }
2889 // there are wholly 4 unique cgs systems for electricity and magnetism,
2890 // so let's not worry about it unless somebody complains
2891
2892 UNIT_SYSTEMS.us = JSON.parse(JSON.stringify(UNIT_SYSTEMS.si))
2893 UNIT_SYSTEMS.us.LENGTH = { unit: UNITS.ft, prefix: PREFIXES.NONE[''] }
2894 UNIT_SYSTEMS.us.MASS = { unit: UNITS.lbm, prefix: PREFIXES.NONE[''] }
2895 UNIT_SYSTEMS.us.TEMPERATURE = { unit: UNITS.degF, prefix: PREFIXES.NONE[''] }
2896 UNIT_SYSTEMS.us.FORCE = { unit: UNITS.lbf, prefix: PREFIXES.NONE[''] }
2897 UNIT_SYSTEMS.us.ENERGY = { unit: UNITS.BTU, prefix: PREFIXES.BTU[''] }
2898 UNIT_SYSTEMS.us.POWER = { unit: UNITS.hp, prefix: PREFIXES.NONE[''] }
2899 UNIT_SYSTEMS.us.PRESSURE = { unit: UNITS.psi, prefix: PREFIXES.NONE[''] }
2900
2901 // Add additional unit systems here.
2902
2903 // Choose a unit system to seed the auto unit system.
2904 UNIT_SYSTEMS.auto = JSON.parse(JSON.stringify(UNIT_SYSTEMS.si))
2905
2906 // Set the current unit system
2907 let currentUnitSystem = UNIT_SYSTEMS.auto
2908
2909 /**
2910 * Set a unit system for formatting derived units.
2911 * @param {string} [name] The name of the unit system.
2912 */
2913 Unit.setUnitSystem = function (name) {
2914 if (UNIT_SYSTEMS.hasOwnProperty(name)) {
2915 currentUnitSystem = UNIT_SYSTEMS[name]
2916 } else {
2917 throw new Error('Unit system ' + name + ' does not exist. Choices are: ' + Object.keys(UNIT_SYSTEMS).join(', '))
2918 }
2919 }
2920
2921 /**
2922 * Return the current unit system.
2923 * @return {string} The current unit system.
2924 */
2925 Unit.getUnitSystem = function () {
2926 for (const key in UNIT_SYSTEMS) {
2927 if (UNIT_SYSTEMS[key] === currentUnitSystem) {
2928 return key
2929 }
2930 }
2931 }
2932
2933 /**
2934 * Converters to convert from number to an other numeric type like BigNumber
2935 * or Fraction
2936 */
2937 Unit.typeConverters = {
2938 BigNumber: function (x) {
2939 return new type.BigNumber(x + '') // stringify to prevent constructor error
2940 },
2941
2942 Fraction: function (x) {
2943 return new type.Fraction(x)
2944 },
2945
2946 Complex: function (x) {
2947 return x
2948 },
2949
2950 number: function (x) {
2951 return x
2952 }
2953 }
2954
2955 /**
2956 * Retrieve the right convertor function corresponding with the type
2957 * of provided exampleValue.
2958 *
2959 * @param {string} type A string 'number', 'BigNumber', or 'Fraction'
2960 * In case of an unknown type,
2961 * @return {Function}
2962 */
2963 Unit._getNumberConverter = function (type) {
2964 if (!Unit.typeConverters[type]) {
2965 throw new TypeError('Unsupported type "' + type + '"')
2966 }
2967
2968 return Unit.typeConverters[type]
2969 }
2970
2971 // Add dimensions to each built-in unit
2972 for (let key in UNITS) {
2973 const unit = UNITS[key]
2974 unit.dimensions = unit.base.dimensions
2975 }
2976
2977 // Create aliases
2978 for (const name in ALIASES) {
2979 if (ALIASES.hasOwnProperty(name)) {
2980 const unit = UNITS[ALIASES[name]]
2981 const alias = {}
2982 for (let key in unit) {
2983 if (unit.hasOwnProperty(key)) {
2984 alias[key] = unit[key]
2985 }
2986 }
2987 alias.name = name
2988 UNITS[name] = alias
2989 }
2990 }
2991
2992 function assertUnitNameIsValid (name) {
2993 for (let i = 0; i < name.length; i++) {
2994 const c = name.charAt(i)
2995
2996 const isValidAlpha = function (p) {
2997 return /^[a-zA-Z]$/.test(p)
2998 }
2999
3000 const isDigit = function (c) {
3001 return (c >= '0' && c <= '9')
3002 }
3003
3004 if (i === 0 && !isValidAlpha(c)) { throw new Error('Invalid unit name (must begin with alpha character): "' + name + '"') }
3005
3006 if (i > 0 && !(isValidAlpha(c) ||
3007 isDigit(c))) { throw new Error('Invalid unit name (only alphanumeric characters are allowed): "' + name + '"') }
3008 }
3009 }
3010
3011 /**
3012 * Wrapper around createUnitSingle.
3013 * Example:
3014 * createUnit({
3015 * foo: { },
3016 * bar: {
3017 * definition: 'kg/foo',
3018 * aliases: ['ba', 'barr', 'bars'],
3019 * offset: 200
3020 * },
3021 * baz: '4 bar'
3022 * },
3023 * {
3024 * override: true
3025 * })
3026 * @param {object} obj Object map. Each key becomes a unit which is defined by its value.
3027 * @param {object} options
3028 */
3029 Unit.createUnit = function (obj, options) {
3030 if (typeof (obj) !== 'object') {
3031 throw new TypeError("createUnit expects first parameter to be of type 'Object'")
3032 }
3033
3034 // Remove all units and aliases we are overriding
3035 if (options && options.override) {
3036 for (let key in obj) {
3037 if (obj.hasOwnProperty(key)) {
3038 Unit.deleteUnit(key)
3039 }
3040 if (obj[key].aliases) {
3041 for (let i = 0; i < obj[key].aliases.length; i++) {
3042 Unit.deleteUnit(obj[key].aliases[i])
3043 }
3044 }
3045 }
3046 }
3047
3048 // TODO: traverse multiple times until all units have been added
3049 let lastUnit
3050 for (let key in obj) {
3051 if (obj.hasOwnProperty(key)) {
3052 lastUnit = Unit.createUnitSingle(key, obj[key])
3053 }
3054 }
3055 return lastUnit
3056 }
3057
3058 /**
3059 * Create a user-defined unit and register it with the Unit type.
3060 * Example:
3061 * createUnitSingle('knot', '0.514444444 m/s')
3062 * createUnitSingle('acre', new Unit(43560, 'ft^2'))
3063 *
3064 * @param {string} name The name of the new unit. Must be unique. Example: 'knot'
3065 * @param {string, Unit} definition Definition of the unit in terms of existing units. For example, '0.514444444 m / s'.
3066 * @param {Object} options (optional) An object containing any of the following properties:
3067 * prefixes {string} "none", "short", "long", "binary_short", or "binary_long". The default is "none".
3068 * aliases {Array} Array of strings. Example: ['knots', 'kt', 'kts']
3069 * offset {Numeric} An offset to apply when converting from the unit. For example, the offset for celsius is 273.15 and the offset for farhenheit is 459.67. Default is 0.
3070 *
3071 * @return {Unit}
3072 */
3073 Unit.createUnitSingle = function (name, obj, options) {
3074 if (typeof (obj) === 'undefined' || obj === null) {
3075 obj = {}
3076 }
3077
3078 if (typeof (name) !== 'string') {
3079 throw new TypeError("createUnitSingle expects first parameter to be of type 'string'")
3080 }
3081
3082 // Check collisions with existing units
3083 if (UNITS.hasOwnProperty(name)) {
3084 throw new Error('Cannot create unit "' + name + '": a unit with that name already exists')
3085 }
3086
3087 // TODO: Validate name for collisions with other built-in functions (like abs or cos, for example), and for acceptable variable names. For example, '42' is probably not a valid unit. Nor is '%', since it is also an operator.
3088
3089 assertUnitNameIsValid(name)
3090
3091 let defUnit = null // The Unit from which the new unit will be created.
3092 let aliases = []
3093 let offset = 0
3094 let definition
3095 let prefixes
3096 if (obj && obj.type === 'Unit') {
3097 defUnit = obj.clone()
3098 } else if (typeof (obj) === 'string') {
3099 if (obj !== '') {
3100 definition = obj
3101 }
3102 } else if (typeof (obj) === 'object') {
3103 definition = obj.definition
3104 prefixes = obj.prefixes
3105 offset = obj.offset
3106 if (obj.aliases) {
3107 aliases = obj.aliases.valueOf() // aliases could be a Matrix, so convert to Array
3108 }
3109 } else {
3110 throw new TypeError('Cannot create unit "' + name + '" from "' + obj.toString() + '": expecting "string" or "Unit" or "Object"')
3111 }
3112
3113 if (aliases) {
3114 for (let i = 0; i < aliases.length; i++) {
3115 if (UNITS.hasOwnProperty(aliases[i])) {
3116 throw new Error('Cannot create alias "' + aliases[i] + '": a unit with that name already exists')
3117 }
3118 }
3119 }
3120
3121 if (definition && typeof (definition) === 'string' && !defUnit) {
3122 try {
3123 defUnit = Unit.parse(definition, { allowNoUnits: true })
3124 } catch (ex) {
3125 ex.message = 'Could not create unit "' + name + '" from "' + definition + '": ' + ex.message
3126 throw (ex)
3127 }
3128 } else if (definition && definition.type === 'Unit') {
3129 defUnit = definition.clone()
3130 }
3131
3132 aliases = aliases || []
3133 offset = offset || 0
3134 if (prefixes && prefixes.toUpperCase) { prefixes = PREFIXES[prefixes.toUpperCase()] || PREFIXES.NONE } else { prefixes = PREFIXES.NONE }
3135
3136 // If defUnit is null, it is because the user did not
3137 // specify a defintion. So create a new base dimension.
3138 let newUnit = {}
3139 if (!defUnit) {
3140 // Add a new base dimension
3141 const baseName = name + '_STUFF' // foo --> foo_STUFF, or the essence of foo
3142 if (BASE_DIMENSIONS.indexOf(baseName) >= 0) {
3143 throw new Error('Cannot create new base unit "' + name + '": a base unit with that name already exists (and cannot be overridden)')
3144 }
3145 BASE_DIMENSIONS.push(baseName)
3146
3147 // Push 0 onto existing base units
3148 for (const b in BASE_UNITS) {
3149 if (BASE_UNITS.hasOwnProperty(b)) {
3150 BASE_UNITS[b].dimensions[BASE_DIMENSIONS.length - 1] = 0
3151 }
3152 }
3153
3154 // Add the new base unit
3155 let newBaseUnit = { dimensions: [] }
3156 for (let i = 0; i < BASE_DIMENSIONS.length; i++) {
3157 newBaseUnit.dimensions[i] = 0
3158 }
3159 newBaseUnit.dimensions[BASE_DIMENSIONS.length - 1] = 1
3160 newBaseUnit.key = baseName
3161 BASE_UNITS[baseName] = newBaseUnit
3162
3163 newUnit = {
3164 name: name,
3165 value: 1,
3166 dimensions: BASE_UNITS[baseName].dimensions.slice(0),
3167 prefixes: prefixes,
3168 offset: offset,
3169 base: BASE_UNITS[baseName]
3170 }
3171
3172 currentUnitSystem[baseName] = {
3173 unit: newUnit,
3174 prefix: PREFIXES.NONE['']
3175 }
3176 } else {
3177 newUnit = {
3178 name: name,
3179 value: defUnit.value,
3180 dimensions: defUnit.dimensions.slice(0),
3181 prefixes: prefixes,
3182 offset: offset
3183 }
3184
3185 // Create a new base if no matching base exists
3186 let anyMatch = false
3187 for (let i in BASE_UNITS) {
3188 if (BASE_UNITS.hasOwnProperty(i)) {
3189 let match = true
3190 for (let j = 0; j < BASE_DIMENSIONS.length; j++) {
3191 if (Math.abs((newUnit.dimensions[j] || 0) - (BASE_UNITS[i].dimensions[j] || 0)) > 1e-12) {
3192 match = false
3193 break
3194 }
3195 }
3196 if (match) {
3197 anyMatch = true
3198 newUnit.base = BASE_UNITS[i]
3199 break
3200 }
3201 }
3202 }
3203 if (!anyMatch) {
3204 const baseName = name + '_STUFF' // foo --> foo_STUFF, or the essence of foo
3205 // Add the new base unit
3206 let newBaseUnit = { dimensions: defUnit.dimensions.slice(0) }
3207 newBaseUnit.key = baseName
3208 BASE_UNITS[baseName] = newBaseUnit
3209
3210 currentUnitSystem[baseName] = {
3211 unit: newUnit,
3212 prefix: PREFIXES.NONE['']
3213 }
3214
3215 newUnit.base = BASE_UNITS[baseName]
3216 }
3217 }
3218
3219 Unit.UNITS[name] = newUnit
3220
3221 for (let i = 0; i < aliases.length; i++) {
3222 const aliasName = aliases[i]
3223 const alias = {}
3224 for (const key in newUnit) {
3225 if (newUnit.hasOwnProperty(key)) {
3226 alias[key] = newUnit[key]
3227 }
3228 }
3229 alias.name = aliasName
3230 Unit.UNITS[aliasName] = alias
3231 }
3232
3233 return new Unit(null, name)
3234 }
3235
3236 Unit.deleteUnit = function (name) {
3237 delete Unit.UNITS[name]
3238 }
3239
3240 // expose arrays with prefixes, dimensions, units, systems
3241 Unit.PREFIXES = PREFIXES
3242 Unit.BASE_DIMENSIONS = BASE_DIMENSIONS
3243 Unit.BASE_UNITS = BASE_UNITS
3244 Unit.UNIT_SYSTEMS = UNIT_SYSTEMS
3245 Unit.UNITS = UNITS
3246
3247 return Unit
3248}
3249
3250exports.name = 'Unit'
3251exports.path = 'type'
3252exports.factory = factory
3253exports.math = true // request access to the math namespace