/** * Math2 * An extended Math library for Javascript * https://github.com/josdejong/math2 * * @version 0.0.2 * @date 2013-02-16 * * @license * Copyright (C) 2013 Jos de Jong * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ (function() { /** * Define math2 namespace */ var math2 = { type: {}, parser: {} }; /** * CommonJS module exports */ if ((typeof module !== 'undefined') && (typeof module.exports !== 'undefined')) { module.exports = math2; } if (typeof exports !== 'undefined') { exports = math2; } /** * AMD module exports */ if (typeof(require) != 'undefined' && typeof(define) != 'undefined') { define(function () { return math2; }); } /** * Browser exports */ if (typeof(window) != 'undefined') { window['math2'] = math2; } var PRECISION = 1E10; /** * Convert a number to a formatted string representation * @param {Number} value The value to be formatted * @param {Number} [digits] number of digits * @return {String} formattedValue The formatted value * @private */ var format = function (value, digits) { if (value === Infinity) { return 'Infinity'; } else if (value === -Infinity) { return '-Infinity'; } else if (value === NaN) { return 'NaN'; } // TODO: what is a nice limit for non-scientific values? var abs = Math.abs(value); if ( (abs > 0.0001 && abs < 1000000) || abs == 0.0 ) { // round the func to a limited number of digits return String(round(value, digits)); } else { // scientific notation var exp = Math.round(Math.log(abs) / Math.LN10); var v = value / (Math.pow(10.0, exp)); return round(v, digits) + 'E' + exp; } }; /** * round a number to the given number of digits, or to the default if * digits is not provided * @param {Number} value * @param {Number} [digits] * @return {Number} roundedValue * @private */ var round = function (value, digits) { digits = (digits != undefined) ? Math.pow(10, digits) : PRECISION; return Math.round(value * digits) / digits; }; /** * Create a semi UUID * source: http://stackoverflow.com/a/105074/1262753 * @return {String} uuid * @private */ var createUUID = function () { var S4 = function () { return Math.floor( Math.random() * 0x10000 /* 65536 */ ).toString(16); }; return ( S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4() ); }; // Internet Explorer 8 and older does not support Array.indexOf, // so we define it here in that case // http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/ if(!Array.prototype.indexOf) { Array.prototype.indexOf = function(obj){ for(var i = 0; i < this.length; i++){ if(this[i] == obj){ return i; } } return -1; }; try { console.log("Warning: Ancient browser detected. Please update your browser"); } catch (err) { } } // Internet Explorer 8 and older does not support Array.forEach, // so we define it here in that case // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach if (!Array.prototype.forEach) { Array.prototype.forEach = function(fn, scope) { for(var i = 0, len = this.length; i < len; ++i) { fn.call(scope || this, this[i], i, this); } } } /** * @License Apache 2.0 License * * @Author Jos de Jong * @Date 2011-12-03 * */ /** * @constructor math2.type.Unit * * @param {Number} [value] A value for the unit, like 5.2 * @param {String} [prefixUnit] A unit like "cm" or "inch" */ function Unit(value, prefixUnit) { if (this.constructor != Unit) { throw new Error('Unit constructor must be called with the new operator'); } this.value = 1; this.unit = Unit.UNIT_NONE; this.prefix = Unit.PREFIX_NONE; // link to a list with supported prefixes this.hasUnit = false; this.hasValue = false; this.fixPrefix = false; // is set true by the method "x In unit"s this._init(value, prefixUnit); } math2.type.Unit = Unit; /** * create a copy of this unit * @return {Unit} copy */ Unit.prototype.copy = function () { var clone = new Unit(); for (var p in this) { if (this.hasOwnProperty(p)) { clone[p] = this[p]; } } return clone; }; /** * check if a text ends with a certain string * @param {String} text * @param {String} search */ // TODO: put the endsWith method in another Unit.endsWith = function(text, search) { var start = text.length - search.length; var end = text.length; return (text.substring(start, end) === search); }; /** * Initialize a unit and value * @param {Number} [value] * @param {String} [unit] A string containing unit (and prefix), like "cm" * @private */ Unit.prototype._init = function (value, unit) { // find the unit and prefix from the string if (unit !== undefined) { var UNITS = Unit.UNITS; var found = false; for (var i = 0, iMax = UNITS.length; i < iMax; i++) { var UNIT = UNITS[i]; if (Unit.endsWith(unit, UNIT.name) ) { var prefixLen = (unit.length - UNIT.name.length); var prefixName = unit.substring(0, prefixLen); var prefix = UNIT.prefixes[prefixName]; if (prefix !== undefined) { // store unit, prefix, and value this.unit = UNIT; this.prefix = prefix; this.hasUnit = true; found = true; break; } } } if (!found) { throw new Error('String "' + unit + '" is no unit'); } } if (value !== undefined) { this.value = this._normalize(value); this.hasValue = true; } else { this.value = this._normalize(1); } }; /** * Normalize a value, based on its currently set unit * @param {Number} value * @return {Number} normalized value * @private */ Unit.prototype._normalize = function(value) { return (value + this.unit.offset) * this.unit.value * this.prefix.value; }; /** * Unnormalize a value, based on its currently set unit * @param {Number} value * @param {Number} prefixValue Optional prefixvalue to be used * @return {Number} unnormalized value * @private */ Unit.prototype._unnormalize = function (value, prefixValue) { if (prefixValue === undefined) { return value / this.unit.value / this.prefix.value - this.unit.offset; } else { return value / this.unit.value / prefixValue - this.unit.offset; } }; /** * Test if the given expression is a unit * @param {String} unit A unit with prefix, like "cm" * @return {Boolean} true if the given string is a unit */ Unit.isUnit = function (unit) { var UNITS = Unit.UNITS; var num = UNITS.length; for (var i = 0; i < num; i++) { var UNIT = UNITS[i]; if (Unit.endsWith(unit, UNIT.name) ) { var prefixLen = (unit.length - UNIT.name.length); if (prefixLen == 0) { return true; } var prefixName = unit.substring(0, prefixLen); var prefix = UNIT.prefixes[prefixName]; if (prefix !== undefined) { return true; } } } return false; }; /** * check if this unit has given base unit * @param {math2.type.Unit.BASE_UNITS} base */ Unit.prototype.hasBase = function(base) { if (this.unit.base === undefined) { return (base === undefined); } return (this.unit.base === base); }; /** * Check if this unit has a base equal to another base * @param {math2.type.Unit} other * @return {Boolean} true if equal base */ Unit.prototype.equalBase = function(other) { return (this.unit.base === other.unit.base); }; /** * Check if this unit equals another unit * @param {Unit} other * @return {Boolean} true if both units are equal */ Unit.prototype.equals = function(other) { return (this.equalBase(other) && this.value == other.value); }; /** * Get string representation * @return {String} */ Unit.prototype.toString = function() { if (!this.fixPrefix) { // find the best prefix value (resulting in the value of which // the absolute value of the log10 is closest to zero, // though with a little offset of 1.5 for nicer values: 999m // is still displayed as 999m, and 1000m as 1km) var bestPrefix = Unit.PREFIX_NONE; var bestDiff = Math.abs( Math.log(this.value / bestPrefix.value) / Math.LN10 - 1.5); // TODO: 1000m is still displayed as 1000m, 1001m correctly as 1.001km // TODO: working wrong with prefixes below zero, should do + 1.5 offset? var prefixes = this.unit.prefixes; for (var p in prefixes) { if (prefixes.hasOwnProperty(p)) { var prefix = prefixes[p]; if (prefix.scientific) { var diff = Math.abs( Math.log(this.value / prefix.value) / Math.LN10 - 1.5); if (diff < bestDiff) { bestPrefix = prefix; bestDiff = diff; } } } } var value = this._unnormalize(this.value, bestPrefix.value); return format(value) + ' ' + bestPrefix.name + this.unit.name; } else { var value = this._unnormalize(this.value); return format(value) + ' ' + this.prefix.name + this.unit.name; } }; Unit.PREFIXES = { 'NONE': { '': {'name': '', 'value': 1, 'scientific': true} }, 'SHORT': { '': {'name': '', 'value': 1, 'scientific': true}, 'da': {'name': 'da', 'value': 1e1, 'scientific': false}, 'h': {'name': 'h', 'value': 1e2, 'scientific': false}, 'k': {'name': 'k', 'value': 1e3, 'scientific': true}, 'M': {'name': 'M', 'value': 1e6, 'scientific': true}, 'G': {'name': 'G', 'value': 1e9, 'scientific': true}, 'T': {'name': 'T', 'value': 1e12, 'scientific': true}, 'P': {'name': 'P', 'value': 1e15, 'scientific': true}, 'E': {'name': 'E', 'value': 1e18, 'scientific': true}, 'Z': {'name': 'Z', 'value': 1e21, 'scientific': true}, 'Y': {'name': 'Y', 'value': 1e24, 'scientific': true}, 'd': {'name': 'd', 'value': 1e-1, 'scientific': false}, 'c': {'name': 'c', 'value': 1e-2, 'scientific': false}, 'm': {'name': 'm', 'value': 1e-3, 'scientific': true}, // 'µ': {'name': 'µ', 'value': 1e-6, 'scientific': true}, 'u': {'name': 'u', 'value': 1e-6, 'scientific': true}, 'n': {'name': 'n', 'value': 1e-9, 'scientific': true}, 'p': {'name': 'p', 'value': 1e-12, 'scientific': true}, 'f': {'name': 'f', 'value': 1e-15, 'scientific': true}, 'a': {'name': 'a', 'value': 1e-18, 'scientific': true}, 'z': {'name': 'z', 'value': 1e-21, 'scientific': true}, 'y': {'name': 'y', 'value': 1e-24, 'scientific': true} }, 'LONG': { '': {'name': '', 'value': 1, 'scientific': true}, 'deca': {'name': 'deca', 'value': 1e1, 'scientific': false}, 'hecto': {'name': 'hecto', 'value': 1e2, 'scientific': false}, 'kilo': {'name': 'kilo', 'value': 1e3, 'scientific': true}, 'mega': {'name': 'mega', 'value': 1e6, 'scientific': true}, 'giga': {'name': 'giga', 'value': 1e9, 'scientific': true}, 'tera': {'name': 'tera', 'value': 1e12, 'scientific': true}, 'peta': {'name': 'peta', 'value': 1e15, 'scientific': true}, 'exa': {'name': 'exa', 'value': 1e18, 'scientific': true}, 'zetta': {'name': 'zetta', 'value': 1e21, 'scientific': true}, 'yotta': {'name': 'yotta', 'value': 1e24, 'scientific': true}, 'deci': {'name': 'deci', 'value': 1e-1, 'scientific': false}, 'centi': {'name': 'centi', 'value': 1e-2, 'scientific': false}, 'milli': {'name': 'milli', 'value': 1e-3, 'scientific': true}, 'micro': {'name': 'micro', 'value': 1e-6, 'scientific': true}, 'nano': {'name': 'nano', 'value': 1e-9, 'scientific': true}, 'pico': {'name': 'pico', 'value': 1e-12, 'scientific': true}, 'femto': {'name': 'femto', 'value': 1e-15, 'scientific': true}, 'atto': {'name': 'atto', 'value': 1e-18, 'scientific': true}, 'zepto': {'name': 'zepto', 'value': 1e-21, 'scientific': true}, 'yocto': {'name': 'yocto', 'value': 1e-24, 'scientific': true} }, 'BINARY_SHORT': { '': {'name': '', 'value': 1, 'scientific': true}, 'k': {'name': 'k', 'value': 1024, 'scientific': true}, 'M': {'name': 'M', 'value': Math.pow(1024, 2), 'scientific': true}, 'G': {'name': 'G', 'value': Math.pow(1024, 3), 'scientific': true}, 'T': {'name': 'T', 'value': Math.pow(1024, 4), 'scientific': true}, 'P': {'name': 'P', 'value': Math.pow(1024, 5), 'scientific': true}, 'E': {'name': 'E', 'value': Math.pow(1024, 6), 'scientific': true}, 'Z': {'name': 'Z', 'value': Math.pow(1024, 7), 'scientific': true}, 'Y': {'name': 'Y', 'value': Math.pow(1024, 8), 'scientific': true}, 'Ki': {'name': 'Ki', 'value': 1024, 'scientific': true}, 'Mi': {'name': 'Mi', 'value': Math.pow(1024, 2), 'scientific': true}, 'Gi': {'name': 'Gi', 'value': Math.pow(1024, 3), 'scientific': true}, 'Ti': {'name': 'Ti', 'value': Math.pow(1024, 4), 'scientific': true}, 'Pi': {'name': 'Pi', 'value': Math.pow(1024, 5), 'scientific': true}, 'Ei': {'name': 'Ei', 'value': Math.pow(1024, 6), 'scientific': true}, 'Zi': {'name': 'Zi', 'value': Math.pow(1024, 7), 'scientific': true}, 'Yi': {'name': 'Yi', 'value': Math.pow(1024, 8), 'scientific': true} }, 'BINARY_LONG': { '': {'name': '', 'value': 1, 'scientific': true}, 'kilo': {'name': 'kilo', 'value': 1024, 'scientific': true}, 'mega': {'name': 'mega', 'value': Math.pow(1024, 2), 'scientific': true}, 'giga': {'name': 'giga', 'value': Math.pow(1024, 3), 'scientific': true}, 'tera': {'name': 'tera', 'value': Math.pow(1024, 4), 'scientific': true}, 'peta': {'name': 'peta', 'value': Math.pow(1024, 5), 'scientific': true}, 'exa': {'name': 'exa', 'value': Math.pow(1024, 6), 'scientific': true}, 'zetta': {'name': 'zetta', 'value': Math.pow(1024, 7), 'scientific': true}, 'yotta': {'name': 'yotta', 'value': Math.pow(1024, 8), 'scientific': true}, 'kibi': {'name': 'kibi', 'value': 1024, 'scientific': true}, 'mebi': {'name': 'mebi', 'value': Math.pow(1024, 2), 'scientific': true}, 'gibi': {'name': 'gibi', 'value': Math.pow(1024, 3), 'scientific': true}, 'tebi': {'name': 'tebi', 'value': Math.pow(1024, 4), 'scientific': true}, 'pebi': {'name': 'pebi', 'value': Math.pow(1024, 5), 'scientific': true}, 'exi': {'name': 'exi', 'value': Math.pow(1024, 6), 'scientific': true}, 'zebi': {'name': 'zebi', 'value': Math.pow(1024, 7), 'scientific': true}, 'yobi': {'name': 'yobi', 'value': Math.pow(1024, 8), 'scientific': true} } }; Unit.PREFIX_NONE = {'name': '', 'value': 1, 'scientific': true}; Unit.BASE_UNITS = { 'NONE': {}, 'LENGTH': {}, // meter 'MASS': {}, // kilogram 'TIME': {}, // second 'CURRENT': {}, // ampere 'TEMPERATURE': {}, // kelvin 'LUMINOUS_INTENSITY': {}, // candela 'AMOUNT_OF_SUBSTANCE': {}, // mole 'FORCE': {}, // Newton 'SURFACE': {}, // m2 'VOLUME': {}, // m3 'ANGLE': {}, // rad 'BIT': {} // bit (digital) }; var BASE_UNITS = Unit.BASE_UNITS; var PREFIXES = Unit.PREFIXES; Unit.BASE_UNIT_NONE = {}; Unit.UNIT_NONE = {'name': '', 'base': Unit.BASE_UNIT_NONE, 'value': 1, 'offset': 0}; Unit.UNITS = [ // length {'name': 'meter', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.LONG, 'value': 1, 'offset': 0}, {'name': 'inch', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 0.0254, 'offset': 0}, {'name': 'foot', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 0.3048, 'offset': 0}, {'name': 'yard', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 0.9144, 'offset': 0}, {'name': 'mile', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 1609.344, 'offset': 0}, {'name': 'link', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 0.201168, 'offset': 0}, {'name': 'rod', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 5.029210, 'offset': 0}, {'name': 'chain', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 20.1168, 'offset': 0}, {'name': 'angstrom', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 1e-10, 'offset': 0}, {'name': 'm', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.SHORT, 'value': 1, 'offset': 0}, //{'name': 'in', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 0.0254, 'offset': 0}, not supported, In is an operator {'name': 'ft', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 0.3048, 'offset': 0}, {'name': 'yd', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 0.9144, 'offset': 0}, {'name': 'mi', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 1609.344, 'offset': 0}, {'name': 'li', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 0.201168, 'offset': 0}, {'name': 'rd', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 5.029210, 'offset': 0}, {'name': 'ch', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 20.1168, 'offset': 0}, {'name': 'mil', 'base': BASE_UNITS.LENGTH, 'prefixes': PREFIXES.NONE, 'value': 0.0000254, 'offset': 0}, // 1/1000 inch // Surface {'name': 'm2', 'base': BASE_UNITS.SURFACE, 'prefixes': PREFIXES.SHORT, 'value': 1, 'offset': 0}, {'name': 'sqin', 'base': BASE_UNITS.SURFACE, 'prefixes': PREFIXES.NONE, 'value': 0.00064516, 'offset': 0}, // 645.16 mm2 {'name': 'sqft', 'base': BASE_UNITS.SURFACE, 'prefixes': PREFIXES.NONE, 'value': 0.09290304, 'offset': 0}, // 0.09290304 m2 {'name': 'sqyd', 'base': BASE_UNITS.SURFACE, 'prefixes': PREFIXES.NONE, 'value': 0.83612736, 'offset': 0}, // 0.83612736 m2 {'name': 'sqmi', 'base': BASE_UNITS.SURFACE, 'prefixes': PREFIXES.NONE, 'value': 2589988.110336, 'offset': 0}, // 2.589988110336 km2 {'name': 'sqrd', 'base': BASE_UNITS.SURFACE, 'prefixes': PREFIXES.NONE, 'value': 25.29295, 'offset': 0}, // 25.29295 m2 {'name': 'sqch', 'base': BASE_UNITS.SURFACE, 'prefixes': PREFIXES.NONE, 'value': 404.6873, 'offset': 0}, // 404.6873 m2 {'name': 'sqmil', 'base': BASE_UNITS.SURFACE, 'prefixes': PREFIXES.NONE, 'value': 6.4516e-10, 'offset': 0}, // 6.4516 * 10^-10 m2 // Volume {'name': 'm3', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.SHORT, 'value': 1, 'offset': 0}, {'name': 'L', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.SHORT, 'value': 0.001, 'offset': 0}, // litre {'name': 'litre', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.LONG, 'value': 0.001, 'offset': 0}, {'name': 'cuin', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 1.6387064e-5, 'offset': 0}, // 1.6387064e-5 m3 {'name': 'cuft', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.028316846592, 'offset': 0}, // 28.316 846 592 L {'name': 'cuyd', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.764554857984, 'offset': 0}, // 764.554 857 984 L {'name': 'teaspoon', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.000005, 'offset': 0}, // 5 mL {'name': 'tablespoon', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.000015, 'offset': 0}, // 15 mL //{'name': 'cup', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.000240, 'offset': 0}, // 240 mL // not possible, we have already another cup // Liquid volume {'name': 'minim', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.00000006161152, 'offset': 0}, // 0.06161152 mL {'name': 'fluiddram', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.0000036966911, 'offset': 0}, // 3.696691 mL {'name': 'fluidounce', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.00002957353, 'offset': 0}, // 29.57353 mL {'name': 'gill', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.0001182941, 'offset': 0}, // 118.2941 mL {'name': 'cup', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.0002365882, 'offset': 0}, // 236.5882 mL {'name': 'pint', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.0004731765, 'offset': 0}, // 473.1765 mL {'name': 'quart', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.0009463529, 'offset': 0}, // 946.3529 mL {'name': 'gallon', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.003785412, 'offset': 0}, // 3.785412 L {'name': 'beerbarrel', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.1173478, 'offset': 0}, // 117.3478 L {'name': 'oilbarrel', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.1589873, 'offset': 0}, // 158.9873 L {'name': 'hogshead', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.2384810, 'offset': 0}, // 238.4810 L //{'name': 'min', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.00000006161152, 'offset': 0}, // 0.06161152 mL // min is already in use as minute {'name': 'fldr', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.0000036966911, 'offset': 0}, // 3.696691 mL {'name': 'floz', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.00002957353, 'offset': 0}, // 29.57353 mL {'name': 'gi', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.0001182941, 'offset': 0}, // 118.2941 mL {'name': 'cp', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.0002365882, 'offset': 0}, // 236.5882 mL {'name': 'pt', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.0004731765, 'offset': 0}, // 473.1765 mL {'name': 'qt', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.0009463529, 'offset': 0}, // 946.3529 mL {'name': 'gal', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.003785412, 'offset': 0}, // 3.785412 L {'name': 'bbl', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.1173478, 'offset': 0}, // 117.3478 L {'name': 'obl', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.1589873, 'offset': 0}, // 158.9873 L //{'name': 'hogshead', 'base': BASE_UNITS.VOLUME, 'prefixes': PREFIXES.NONE, 'value': 0.2384810, 'offset': 0}, // 238.4810 L // TODO: hh? // Mass {'name': 'g', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.SHORT, 'value': 0.001, 'offset': 0}, {'name': 'gram', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.LONG, 'value': 0.001, 'offset': 0}, {'name': 'ton', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.SHORT, 'value': 907.18474, 'offset': 0}, {'name': 'tonne', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.SHORT, 'value': 1000, 'offset': 0}, {'name': 'grain', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.NONE, 'value': 64.79891e-6, 'offset': 0}, {'name': 'dram', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.NONE, 'value': 1.7718451953125e-3, 'offset': 0}, {'name': 'ounce', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.NONE, 'value': 28.349523125e-3, 'offset': 0}, {'name': 'poundmass', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.NONE, 'value': 453.59237e-3, 'offset': 0}, {'name': 'hundredweight', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.NONE, 'value': 45.359237, 'offset': 0}, {'name': 'stick', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.NONE, 'value': 115e-3, 'offset': 0}, {'name': 'gr', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.NONE, 'value': 64.79891e-6, 'offset': 0}, {'name': 'dr', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.NONE, 'value': 1.7718451953125e-3, 'offset': 0}, {'name': 'oz', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.NONE, 'value': 28.349523125e-3, 'offset': 0}, {'name': 'lbm', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.NONE, 'value': 453.59237e-3, 'offset': 0}, {'name': 'cwt', 'base': BASE_UNITS.MASS, 'prefixes': PREFIXES.NONE, 'value': 45.359237, 'offset': 0}, // Time {'name': 's', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.SHORT, 'value': 1, 'offset': 0}, {'name': 'min', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.NONE, 'value': 60, 'offset': 0}, {'name': 'h', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.NONE, 'value': 3600, 'offset': 0}, {'name': 'seconds', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.LONG, 'value': 1, 'offset': 0}, {'name': 'second', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.LONG, 'value': 1, 'offset': 0}, {'name': 'sec', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.LONG, 'value': 1, 'offset': 0}, {'name': 'minutes', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.NONE, 'value': 60, 'offset': 0}, {'name': 'minute', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.NONE, 'value': 60, 'offset': 0}, {'name': 'hours', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.NONE, 'value': 3600, 'offset': 0}, {'name': 'hour', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.NONE, 'value': 3600, 'offset': 0}, {'name': 'day', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.NONE, 'value': 86400, 'offset': 0}, {'name': 'days', 'base': BASE_UNITS.TIME, 'prefixes': PREFIXES.NONE, 'value': 86400, 'offset': 0}, // Angles {'name': 'rad', 'base': BASE_UNITS.ANGLE, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 0}, {'name': 'deg', 'base': BASE_UNITS.ANGLE, 'prefixes': PREFIXES.NONE, 'value': 0.017453292519943295769236907684888, 'offset': 0}, // deg = rad / (2*pi) * 360 = rad / 0.017453292519943295769236907684888 {'name': 'grad', 'base': BASE_UNITS.ANGLE, 'prefixes': PREFIXES.NONE, 'value': 0.015707963267948966192313216916399, 'offset': 0}, // grad = rad / (2*pi) * 400 = rad / 0.015707963267948966192313216916399 {'name': 'cycle', 'base': BASE_UNITS.ANGLE, 'prefixes': PREFIXES.NONE, 'value': 6.2831853071795864769252867665793, 'offset': 0}, // cycle = rad / (2*pi) = rad / 6.2831853071795864769252867665793 // Electric current {'name': 'A', 'base': BASE_UNITS.CURRENT, 'prefixes': PREFIXES.SHORT, 'value': 1, 'offset': 0}, {'name': 'ampere', 'base': BASE_UNITS.CURRENT, 'prefixes': PREFIXES.LONG, 'value': 1, 'offset': 0}, // Temperature // K(C) = °C + 273.15 // K(F) = (°F + 459.67) / 1.8 // K(R) = °R / 1.8 {'name': 'K', 'base': BASE_UNITS.TEMPERATURE, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 0}, {'name': 'degC', 'base': BASE_UNITS.TEMPERATURE, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 273.15}, {'name': 'degF', 'base': BASE_UNITS.TEMPERATURE, 'prefixes': PREFIXES.NONE, 'value': 1/1.8, 'offset': 459.67}, {'name': 'degR', 'base': BASE_UNITS.TEMPERATURE, 'prefixes': PREFIXES.NONE, 'value': 1/1.8, 'offset': 0}, {'name': 'kelvin', 'base': BASE_UNITS.TEMPERATURE, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 0}, {'name': 'celsius', 'base': BASE_UNITS.TEMPERATURE, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 273.15}, {'name': 'fahrenheit', 'base': BASE_UNITS.TEMPERATURE, 'prefixes': PREFIXES.NONE, 'value': 1/1.8, 'offset': 459.67}, {'name': 'rankine', 'base': BASE_UNITS.TEMPERATURE, 'prefixes': PREFIXES.NONE, 'value': 1/1.8, 'offset': 0}, // amount of substance {'name': 'mol', 'base': BASE_UNITS.AMOUNT_OF_SUBSTANCE, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 0}, {'name': 'mole', 'base': BASE_UNITS.AMOUNT_OF_SUBSTANCE, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 0}, // luminous intensity {'name': 'cd', 'base': BASE_UNITS.LUMINOUS_INTENSITY, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 0}, {'name': 'candela', 'base': BASE_UNITS.LUMINOUS_INTENSITY, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 0}, // TODO: units STERADIAN //{'name': 'sr', 'base': BASE_UNITS.STERADIAN, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 0}, //{'name': 'steradian', 'base': BASE_UNITS.STERADIAN, 'prefixes': PREFIXES.NONE, 'value': 1, 'offset': 0}, // Force {'name': 'N', 'base': BASE_UNITS.FORCE, 'prefixes': PREFIXES.SHORT, 'value': 1, 'offset': 0}, {'name': 'newton', 'base': BASE_UNITS.FORCE, 'prefixes': PREFIXES.LONG, 'value': 1, 'offset': 0}, {'name': 'lbf', 'base': BASE_UNITS.FORCE, 'prefixes': PREFIXES.NONE, 'value': 4.4482216152605, 'offset': 0}, {'name': 'poundforce', 'base': BASE_UNITS.FORCE, 'prefixes': PREFIXES.NONE, 'value': 4.4482216152605, 'offset': 0}, // Binary {'name': 'b', 'base': BASE_UNITS.BIT, 'prefixes': PREFIXES.BINARY_SHORT, 'value': 1, 'offset': 0}, {'name': 'bits', 'base': BASE_UNITS.BIT, 'prefixes': PREFIXES.BINARY_LONG, 'value': 1, 'offset': 0}, {'name': 'B', 'base': BASE_UNITS.BIT, 'prefixes': PREFIXES.BINARY_SHORT, 'value': 8, 'offset': 0}, {'name': 'bytes', 'base': BASE_UNITS.BIT, 'prefixes': PREFIXES.BINARY_LONG, 'value': 8, 'offset': 0} ]; /** * @constructor math2.type.Complex * * @param {Number} [re] * @param {Number} [im] */ function Complex(re, im) { if (this.constructor != Complex) { throw new Error('Complex constructor must be called with the new operator'); } /** * @type {Number} */ this.re = re || 0; /** * @type {Number} */ this.im = im || 0; } math2.type.Complex = Complex; /** * Create a copy of the complex value * @return {Complex} copy */ Complex.prototype.copy = function () { return new Complex(this.re, this.im); }; /** * Get string representation of the Complex value * @return {String} str */ Complex.prototype.toString = function () { var str = ''; if (this.im === 0) { // real value str = format(this.re); } else if (this.re === 0) { // purely complex value if (this.im === 1) { str = 'i'; } else if (this.im === -1) { str = '-i'; } else { str = format(this.im) + 'i'; } } else { // complex value if (this.im > 0) { if (this.im == 1) { str = format(this.re) + ' + i'; } else { str = format(this.re) + ' + ' + format(this.im) + 'i'; } } else { if (this.im == -1) { str = format(this.re) + ' - i'; } else { str = format(this.re) + ' - ' + format(Math.abs(this.im)) + 'i'; } } } return str; }; /** * Type documentation */ Complex.doc = { 'name': 'Complex', 'category': 'type', 'syntax': [ 'a + bi', 'a + b * i' ], 'description': 'A complex value a + bi, ' + 'where a is the real part and b is the complex part, ' + 'and i is the imaginary number defined as sqrt(-1).', 'examples': [ '2 + 3i', 'sqrt(-4)', '(1.2 -5i) * 2' ], 'seealso': [ 'abs', 'arg', 'conj', 'im', 're' ] }; /** * Generic type methods */ /** * Test whether value is a Number * @param {*} value * @return {Boolean} isNumber */ function isNumber(value) { return (value instanceof Number) || (typeof value == 'number'); } /** * Test whether value is a Complex value * @param {*} value * @return {Boolean} isComplex */ function isComplex(value) { return (value instanceof Complex); } /** * Test whether value is a Unit * @param {*} value * @return {Boolean} isUnit */ function isUnit(value) { return (value instanceof Unit); } /** * Get the type of an object. * @param {*} obj * @return {String} type */ function type (obj) { var t = typeof obj; if (t == 'object') { if (obj == null) { return 'null'; } if (obj instanceof Array) { return 'array'; } if (obj && obj.constructor && obj.constructor.name) { return obj.constructor.name; } } return t; } /** * Math2 Constants */ math2.E = Math.E; math2.LN2 = Math.LN2; math2.LN10 = Math.LN10; math2.LOG2E = Math.LOG2E; math2.LOG10E = Math.LOG10E; math2.PI = Math.PI; math2.SQRT1_2 = Math.SQRT1_2; math2.SQRT2 = Math.SQRT2; math2.I = new Complex(0, -1); // lower case constants math2.pi = math2.PI; math2.e = math2.E; math2.i = math2.I; /** * Helper methods for functions */ /** * Create a TypeError with message: * 'Function does not support a parameter of type '; * @param {String} fn * @param {*} value * @return {TypeError | Error} error */ function newUnsupportedTypeError(fn, value) { var t = type(value); var msg = 'Function ' + fn + ' does not support a parameter of type ' + t; if ((typeof TypeError) != 'undefined') { return new TypeError(msg); } else { return new Error(msg); } } /** * Change the unit of a value. x in unit or in(x, unit) * @param {Unit} x * @param {Unit} unit * @return {Unit} res */ function unit_in(x, unit) { if (isUnit(x)) { // Test if unit has no value if (unit.hasValue) { throw new Error('Cannot convert to a unit with a value'); } // Test if unit has a unit if (!unit.hasUnit) { throw new Error('Unit expected on the right hand side of function in'); } var res = unit.copy(); res.value = x.value; res.fixPrefix = true; return res; } // TODO: implement array support // TODO: implement matrix support throw newUnsupportedTypeError('in', x); } math2['in'] = unit_in; /** * Function documentation */ unit_in.doc ={ 'name': 'in', 'category': 'Units', 'syntax': [ 'x in unit', 'in(x, unit)' ], 'description': 'Change the unit of a value.', 'examples': [ '5 inch in cm', '3.2kg in g', '16 bytes in bits' ], 'seealso': [] }; /** * Calculate the sine of a value, sin(x) * @param {Number | Complex | Unit} x * @return {Number | Complex} res */ function sin(x) { if (isNumber(x)) { return Math.sin(x); } if (isComplex(x)) { return new Complex( 0.5 * Math.sin(x.re) * (Math.exp(-x.im) + Math.exp( x.im)), 0.5 * Math.cos(x.re) * (Math.exp( x.im) - Math.exp(-x.im)) ); } if (isUnit(x)) { if (!x.hasBase(Unit.BASE_UNITS.ANGLE)) { throw new TypeError ('Unit in function cos is no angle'); } return Math.sin(x.value); } // TODO: implement array support // TODO: implement matrix support throw newUnsupportedTypeError('sin', x); } math2.sin = sin; /** * Function documentation */ sin.doc = { 'name': 'sin', 'category': 'Trigonometry', 'syntax': [ 'sin(x)' ], 'description': 'Compute the sine of x in radians.', 'examples': [ 'sin(2)', 'sin(pi / 4) ^ 2', 'sin(90 deg)', 'sin(30 deg)', 'sin(0.2)^2 + cos(0.2)^2' ], 'seealso': [ 'asin', 'cos', 'tan' ] }; /** * Calculate the cosine of a value, cos(x) * @param {Number | Complex | Unit} x * @return {Number | Complex} res */ function cos(x) { if (isNumber(x)) { return Math.cos(x); } if (isComplex(x)) { // cos(z) = (exp(iz) + exp(-iz)) / 2 return new Complex( 0.5 * Math.cos(x.re) * (Math.exp(-x.im) + Math.exp(x.im)), 0.5 * Math.sin(x.re) * (Math.exp(-x.im) - Math.exp(x.im)) ); } if (isUnit(x)) { if (!x.hasBase(Unit.BASE_UNITS.ANGLE)) { throw new TypeError ('Unit in function cos is no angle'); } return Math.cos(x.value); } // TODO: implement array support // TODO: implement matrix support throw newUnsupportedTypeError('cos', x); } math2.cos = cos; /** * Function documentation */ cos.doc = { 'name': 'cos', 'category': 'Trigonometry', 'syntax': [ 'cos(x)' ], 'description': 'Compute the cosine of x in radians.', 'examples': [ 'cos(2)', 'cos(pi / 4) ^ 2', 'cos(180 deg)', 'cos(60 deg)', 'sin(0.2)^2 + cos(0.2)^2' ], 'seealso': [ 'acos', 'sin', 'tan' ] }; /** * Calculate the tangent of a value, tan(x) * @param {Number | Complex | Unit} x * @return {Number | Complex} res */ function tan(x) { if (isNumber(x)) { return Math.tan(x); } if (isComplex(x)) { var den = Math.exp(-4.0 * x.im) + 2.0 * Math.exp(-2.0 * x.im) * Math.cos(2.0 * x.re) + 1.0; return new Complex( 2.0 * Math.exp(-2.0 * x.im) * Math.sin(2.0 * x.re) / den, (1.0 - Math.exp(-4.0 * x.im)) / den ); } if (isUnit(x)) { if (!x.hasBase(Unit.BASE_UNITS.ANGLE)) { throw new TypeError ('Unit in function tan is no angle'); } return Math.tan(x.value); } // TODO: implement array support // TODO: implement matrix support throw newUnsupportedTypeError('tan', x); } math2.tan = tan; /** * Function documentation */ tan.doc = { 'name': 'tan', 'category': 'Trigonometry', 'syntax': [ 'tan(x)' ], 'description': 'Compute the tangent of x in radians.', 'examples': [ 'tan(0.5)', 'sin(0.5) / cos(0.5)', 'tan(pi / 4)', 'tan(45 deg)' ], 'seealso': [ 'atan', 'sin', 'cos' ] }; /** * Calculate the exponent of a value, exp(x) * @param {Number | Complex} x * @return {Number | Complex} res */ function exp (x) { if (isNumber(x)) { return Math.exp(x); } if (isComplex(x)) { var r = Math.exp(x.re); return new Complex( r * Math.cos(x.im), r * Math.sin(x.im) ); } // TODO: implement array support // TODO: implement matrix support throw newUnsupportedTypeError('exp', x); } math2.exp = exp; /** * Function documentation */ exp.doc = { 'name': 'exp', 'category': 'Arithmetic', 'syntax': [ 'exp(x)' ], 'description': 'Calculate the exponent of a value.', 'examples': [ 'exp(1.3)', 'e ^ 1.3', 'log(exp(1.3))', 'x = 2.4', '(exp(i*x) == cos(x) + i*sin(x)) # Euler\'s formula' ], 'seealso': [ 'square', 'multiply', 'log' ] }; /** * Calculate the square root of a value * @param {Number | Complex} x * @return {Number | Complex} res */ function sqrt (x) { if (isNumber(x)) { if (x >= 0) { return Math.sqrt(x); } else { return sqrt(new Complex(x, 0)); } } if (isComplex(x)) { var r = Math.sqrt(x.re * x.re + x.im * x.im); if (x.im >= 0.0) { return new Complex( 0.5 * Math.sqrt(2.0 * (r + x.re)), 0.5 * Math.sqrt(2.0 * (r - x.re)) ); } else { return new Complex( 0.5 * Math.sqrt(2.0 * (r + x.re)), -0.5 * Math.sqrt(2.0 * (r - x.re)) ); } } // TODO: implement array support // TODO: implement matrix support throw newUnsupportedTypeError('sqrt', x); } math2.sqrt = sqrt; /** * Function documentation */ sqrt.doc = { 'name': 'sqrt', 'category': 'Arithmetic', 'syntax': [ 'sqrt(x)' ], 'description': 'Compute the square root value. ' + 'If x = y * y, then y is the square root of x.', 'examples': [ 'sqrt(25)', '5 * 5', 'sqrt(-1)' ], 'seealso': [ 'square', 'multiply' ] }; /** * Calculate the square root of a value * @param {Number | Complex} x * @return {Number | Complex} res */ function abs(x) { if (isNumber(x)) { return Math.abs(x); } if (isComplex(x)) { return Math.sqrt(x.re * x.re + x.im * x.im); } // TODO: implement array support // TODO: implement matrix support throw newUnsupportedTypeError('abs', x); } math2.abs = abs; /** * Function documentation */ abs.doc = { 'name': 'abs', 'category': 'Arithmetic', 'syntax': [ 'abs(x)' ], 'description': 'Compute the absolute value.', 'examples': [ 'abs(3.5)', 'abs(-4.2)' ], 'seealso': ['sign'] }; /** * Calculate the natural logarithm of a value, log(x) * @param {Number | Complex} x * @return {Number | Complex} res */ function log(x) { if (isNumber(x)) { if (x >= 0) { return Math.log(x); } else { // negative value -> complex value computation return log(new Complex(x, 0)); } } if (isComplex(x)) { return new Complex ( Math.log(Math.sqrt(x.re * x.re + x.im * x.im)), Math.atan2(x.im, x.re) ); } // TODO: implement array support // TODO: implement matrix support throw newUnsupportedTypeError('log', x); } math2.log = log; /** * Function documentation */ log.doc = { 'name': 'log', 'category': 'Arithmetic', 'syntax': [ 'log(x)' ], 'description': 'Compute the natural logarithm of a value.', 'examples': [ 'log(3.5)', 'a = log(2.4)', 'exp(a)', 'log(1000) / log(10)' ], 'seealso': [ 'exp', 'logb', 'log10' ] }; })();