* math.js: - Refactored to Module Pattern - Added general operations - Added math/complex.js, math/rational.js - Added unit test * math/algebra.js - Refactored to Module Pattern - Added jsx.math.Tensor, jsx.math.Vector - Added jsx.math,Matrix prototype methods - Use basic operations * math/float.js - Added jsx.math.convertAngle(); deprecates jsx.math.sin(), .sinX(), .cos(), cosX(), and .tanX() * math/integer.js - Started refactoring to Module Pattern - Added Math.primes()
/trunk/math/float.js |
---|
4,12 → 4,12 |
* @requires types.js |
* |
* @section Copyright & Disclaimer |
* |
* |
* @author |
* (C) 2000-2012 Thomas Lahn <math.js@PointedEars.de> |
* |
* @partof PointedEars' JavaScript Extensions (JSX) |
* |
* |
* JSX is free software: you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation, either version 3 of the License, or |
42,7 → 42,7 |
/** |
* Returns the numerical value of an object. |
* |
* |
* @param obj : Object |
* @return {number} |
* <code>NaN</code> if <var>obj</var> does not refer to an object |
55,18 → 55,18 |
&& typeof obj.valueOf == "function") |
? obj.valueOf() |
: value; |
if (typeof value == "number") |
{ |
return value; |
} |
return NaN; |
}; |
/** |
* Returns the value of the smallest argument. |
* |
* |
* @return {number} |
* The value of the smallest argument. |
* If an argument is an object but not an <code>Array</code>, |
80,11 → 80,11 |
*/ |
jsx.math.min = (function () { |
var getValue = jsx.math.getValue; |
return function () { |
var result = Number.POSITIVE_INFINITY; |
var min_el; |
for (var i = 0, len = arguments.length; i < len; ++i) |
{ |
var a = arguments[i]; |
123,7 → 123,7 |
result = a; |
} |
} |
return result; |
}; |
}()); |
130,7 → 130,7 |
/** |
* Returns the value of the greatest argument. |
* |
* |
* @return {number} |
* The value of the greatest argument. |
* If an argument is an object but not an <code>Array</code>, |
144,10 → 144,10 |
*/ |
jsx.math.max = (function () { |
var getValue = jsx.math.getValue; |
return function () { |
var result = Number.NEGATIVE_INFINITY; |
for (var i = 0, len = arguments.length; i < len; ++i) |
{ |
var a = arguments[i], max_el; |
186,7 → 186,7 |
result = a; |
} |
} |
return result; |
}; |
}()); |
193,7 → 193,7 |
/** |
* Returns the average value of the arguments. |
* |
* |
* @return number |
* The average value of the arguments. |
* If an argument is an object but not an <code>Array</code>, |
206,11 → 206,11 |
*/ |
jsx.math.avg = (function () { |
var getValue = jsx.math.getValue; |
return function () { |
var sum = 0; |
var count = 0; |
for (var i = 0, len = arguments.length; i < len; i++) |
{ |
var a = arguments[i]; |
245,7 → 245,7 |
sum += parseFloat(a); |
} |
} |
return (sum / count); |
}; |
}()); |
252,7 → 252,7 |
/** |
* Returns the arithmetic median of the arguments. |
* |
* |
* The [arithmetic (one-dimensional)] median is [defined] as |
* the numerical value separating the higher half of a sample |
* from the lower half of a sample. If there is an even number |
259,7 → 259,7 |
* of observations, then there is no single middle value; the |
* median is then […] defined to be the mean of the two middle |
* values. (From Wikipedia, the free encyclopedia) |
* |
* |
* @return number |
* The arithmetic median of the arguments. |
* If an argument is an object but not an <code>Array</code>, |
272,11 → 272,11 |
*/ |
jsx.math.median = (function () { |
var getValue = jsx.math.getValue; |
return function () { |
var values = []; |
var result; |
for (var i = 0, len = arguments.length; i < len; i++) |
{ |
var a = arguments[i]; |
307,9 → 307,9 |
values.push(parseFloat(a)); |
} |
} |
values.sort(function (a, b) { return a - b; }); |
len = values.length; |
if (len > 1) |
{ |
409,7 → 409,7 |
they could succeed |
*/ |
} |
var e = Math.pow(10, n); |
var k = (Math.round(x * e) / e).toString(); |
417,7 → 417,7 |
and not all JavaScript capable browsers support it. |
Use the String(...) function instead. |
*/ |
if (k.indexOf('.') == -1){k += '.'; |
/* Sometimes it is not desired to have the decimal point |
when dealing with integers. The function does not allow |
470,7 → 470,7 |
{ |
iSigDecimals = 0; |
} |
/* |
* Returns the number itself when called with invalid arguments, |
* so further calculations will not fail because of a wrong |
488,14 → 488,14 |
{ |
i = String(n).length - 1; |
} |
if (String(n).substring(0, i).length <= -iSigDecimals) |
{ |
return n; |
} |
var k = Math.round(n * e) / e; |
if (arguments.length < 3) |
{ |
iForceDecimals = 0; |
508,7 → 508,7 |
{ |
k += "."; |
} |
for (i = k.slice(k.indexOf(".") + 1).length; |
i < iForceDecimals; |
i++) |
516,7 → 516,7 |
k += "0"; |
} |
} |
if (bForceLeadingZero && String(k).charAt(0) == ".") |
{ |
k = "0" + k; |
536,7 → 536,7 |
k = k.substring(0, i) + sDecSeparator + k.slice(i + 1); |
} |
} |
return k; |
}; |
568,7 → 568,7 |
var |
currentPeriod = s.substring(2, i), |
rx = new RegExp("^\\d*\\.\\d*(" + currentPeriod + ")+$"); |
if (rx.test(s)) |
{ |
return currentPeriod; |
589,13 → 589,13 |
* II: 10x = 1.111111111111111 |
* II - I: 9x = 1 |
* x = 1/9 |
* |
* |
* 1. Y = periodLength(X) |
* 2. Z = X * 10^Y |
* 3. A = Z - X |
* 4. RESULT = 'A "/" (10^Y - 1)' |
*/ |
var y = jsx.math.getPeriod(fDec).length; |
var z = fDec * Math.pow(10, y); |
var dividend = Math.round(z - fDec); |
608,9 → 608,9 |
dividend /= d; |
divisor /= d; |
} |
var result = dividend + "/" + divisor; |
return result; |
}; |
622,106 → 622,61 |
jsx.math.UNIT_GRAD = 2; |
/** |
* Unlike the {@link js#Math built-in methods}, the following |
* functions accept a second argument to determine if the argument |
* should be handled as radian (dtRad == 0 [default]; |
* x = n*[0..2*Math.PI], degree (dtDeg == 1; x = n*[0..360]) |
* or gradian (dtGrad == 2; x = n*[0..400] gon) value. |
* Converts an angle to another unit |
* |
* @param {Number} value |
* Angle to convert |
* @param {number} unit2 |
* Target unit. Use the {@link jsx.math.UNIT_RAD jsx.math.UNIT_*}) |
* properties. |
* @param {number} unit1 |
* Source unit. The default is radians ({@link jsx.math.UNIT_RAD}). |
*/ |
/** |
* Returns the sine of an angle. |
* |
* Call this method instead of <code>jsx.math.sinX()</code>, |
* which is deprecated. |
* |
* @param x : number |
* @param iArgType : number |
* @return number |
* The sine of <var>x</var> |
*/ |
jsx.math.sin = function(x, iArgType) { |
switch (iArgType) |
jsx.math.convertAngle = function (value, unit2, unit1) { |
switch (unit1) |
{ |
case jsx.math.UNIT_DEG: |
x = x/180 * Math.PI; |
value = value/180 * Math.PI; |
break; |
case jsx.math.UNIT_GRAD: |
x = x/200 * Math.PI; |
value = value/200 * Math.PI; |
break; |
} |
return Math.sin(x); |
}; |
/* (non-JSdoc) |
* @deprecated |
*/ |
jsx.math.sinX = jsx.math.sin; |
/** |
* Returns the cosine of an angle. |
* |
* Call this method instead of <code>jsx.math.cosX()</code>, |
* which is deprecated. |
* |
* @param x : number |
* @param iArgType : number |
* @return number |
* The cosine of <var>x</var> |
*/ |
jsx.math.cos = function(x, iArgType) { |
switch (iArgType) |
switch (unit2) |
{ |
case jsx.math.UNIT_DEG: |
x = x/180 * Math.PI; |
value = value / Math.PI * 180; |
break; |
case jsx.math.UNIT_GRAD: |
x = x/200 * Math.PI; |
value = value / Math.PI * 200; |
} |
return Math.cos(x); |
return value; |
}; |
/* (non-JSdoc) |
* @deprecated |
*/ |
jsx.math.cosX = jsx.math.cos; |
/** |
* Returns the tangent of an angle. |
* |
* |
* Call this method instead of <code>jsx.math.tanX()</code>, |
* which is deprecated. |
* |
* @param x : number |
* @param iArgType : number |
* @return number |
* The tangent of <var>x</var>. If @link{js#Math.tan()} is |
* undefined, it uses @link{jsx.math#sin()} and |
* @link{jsx.math#cos()}. |
* |
* @param {number} x |
* @return {number} |
* The tangent of <var>x</var>. If {@link Math.tan()} is |
* not a function, it uses {@link Math.sin()} and |
* {@link Math.cos()}. |
* @requires jsx.object#isMethod() |
*/ |
jsx.math.tan = function(x, iArgType) { |
var jsx_object = jsx.object; |
switch (iArgType) |
jsx.math.tan = function (x) { |
if (typeof Math.tan == "function") |
{ |
case jsx.math.UNIT_DEG: |
x = x/180 * Math.PI; |
break; |
case jsx.math.UNIT_GRAD: |
x = x/200 * Math.PI; |
} |
if (jsx_object.isMethod(Math, "tan")) |
{ |
return Math.tan(x); |
} |
return (jsx.math.sin(x) / jsx.math.cos(x)); |
return (Math.sin(x) / Math.cos(x)); |
}; |
/* (non-JSdoc) |
729,89 → 684,6 |
*/ |
jsx.math.tanX = jsx.math.tan; |
/** @subsection Complex numbers */ |
/** |
* @param nRe : number |
* @param nIm : number |
*/ |
jsx.math.Complex = function (nRe, nIm) { |
Number.call(this); |
this.re = Number(nRe) || 0; |
this.im = Number(nIm) || 0; |
}; |
jsx.math.Complex.extend(Number); |
/** |
* @param a : Complex |
* @param b : Complex |
* @return Complex |
* The complex sum of <var>a</var> and <var>b</var> |
*/ |
jsx.math.addComplex = |
jsx.math.Complex.prototype.add = function (a, b) { |
var result = null; |
var math = jsx.math; |
if (this instanceof math.Complex) |
{ |
b = a; |
a = this; |
} |
if (a && b) |
{ |
if (!(a instanceof math.Complex)) |
{ |
a = new math.Complex(a); |
} |
if (!(b instanceof math.Complex)) |
{ |
b = new math.Complex(b); |
} |
return new math.Complex(a.re + b.re, a.im + b.im); |
} |
}; |
/** |
* @param a : Complex |
* @param b : Complex |
* @return Complex |
* The complex product of <var>a</var> and <var>b</var> |
*/ |
jsx.math.mulComplex = |
jsx.math.Complex.prototype.mul = function(a, b) { |
var result = null; |
if (this instanceof jsx.math.Complex) |
{ |
b = a; |
a = this; |
} |
if (a && b) |
{ |
if (!(a instanceof jsx.math.Complex)) |
{ |
a = new jsx.math.Complex(a); |
} |
if (!(b instanceof jsx.math.Complex)) |
{ |
b = new jsx.math.Complex(b); |
} |
// a.re, a.im b.re, b.im |
// (a, b ) * (c, d ) = (a * c - b * d, a * d + b * c) |
return new jsx.math.Complex( |
a.re * b.re - a.im * b.im, |
a.re * b.im + a.im * b.re); |
} |
}; |
/* |
* TODO: Hyperbolic functions |
*/ |
/trunk/math/complex.js |
---|
0,0 → 1,218 |
/** |
* <title>PointedEars' JSX: Math Library: Complex arithmetics</title> |
* @requires math.js |
* |
* @section Copyright & Disclaimer |
* |
* @author |
* (C) 2013 Thomas Lahn <math.js@PointedEars.de> |
* |
* @partof PointedEars' JavaScript Extensions (JSX) |
* |
* JSX is free software: you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation, either version 3 of the License, or |
* (at your option) any later version. |
* |
* JSX is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
* GNU General Public License for more details. |
* |
* You should have received a copy of the GNU General Public License |
* along with JSX. If not, see <http://www.gnu.org/licenses/>. |
*/ |
/** |
* @namespace |
*/ |
jsx.math.complex = (/** @constructor */ function () { |
/* Imports */ |
var _isObject = jsx.object.isObject; |
var _math = jsx.math; |
var _add = _math.add; |
var _sub = _math.sub; |
var _mul = _math.mul; |
var _div = _math.div; |
var _pow = _math.pow; |
var _sqrt = _math.sqrt; |
/* Private variables */ |
var _rx_decimal_integer_literal = /(?:0|[1-9]\d*)/; |
var _rx_exponent_part = /[eE][+-]?\d+/; |
var _rx_decimal_literal = new RegExp( |
_rx_decimal_integer_literal.source + "(?:\\.\\d*)?(?:" + _rx_exponent_part.source + ")?"); |
var _rx_hex_integer_literal = /0[xX][0-9a-fA-F]+(?:\.[0-9a-fA-F]*)?/; |
var _rx_numeric_literal = new RegExp( |
"((" + _rx_hex_integer_literal.source + ")|" + _rx_decimal_literal.source + ")"); |
var _rx_complex_literal = new RegExp( |
"^\\s*" + _rx_numeric_literal.source |
+ "\\s*(?:([+-])\\s*" + _rx_numeric_literal.source + "[ij])?"); |
/** |
* A complex number consists of a real and an imaginary part. |
* |
* It can be visualized as being an point in a plane with a |
* two-dimensional Cartesian coordinate system in which |
* the real part is measured on one axis and the imaginary |
* part on the other. |
* |
* @function |
*/ |
var _Complex = jsx.object.extend( |
/** |
* @constructor |
* @param {Number} re |
* @param {Number} im |
*/ |
function (re, im) { |
if (!(this instanceof _Complex)) |
{ |
return new _Complex(re, im); |
} |
this.re = _isObject(re) ? re : +re; |
this.im = _isObject(im) ? im : +im; |
}, |
{ |
/** |
* @memberOf jsx.math.complex.Complex |
* @param {String} s |
*/ |
parse: function (s) { |
var m = s.match(_rx_complex_literal); |
if (!m || !m[1]) |
{ |
return Number.NaN; |
} |
return new _Complex( |
parseFloat(m[1], m[2] ? 16 : 10), |
(m[3] == "-" ? -1 : 1) * parseFloat(m[4], m[5] ? 16 : 10) || 0); |
} |
} |
).extend(Number, { |
/** |
* @memberOf jsx.math.complex.Complex.prototype |
* @param {_Complex} a |
* @param {_Complex} b |
* @return {_Complex} |
* The complex sum of <var>a</var> and <var>b</var> |
*/ |
add: function (operand) { |
if (!(operand instanceof _Complex)) |
{ |
operand = new _Complex(operand); |
} |
return new _Complex(_add(this.re, operand.re), _add(this.im, operand.im)); |
}, |
/** |
* @param {_Complex} a |
* @param {_Complex} b |
* @return {_Complex} |
* The complex product of <var>a</var> and <var>b</var> |
*/ |
mul: function (operand) { |
var result = null; |
if (!(operand instanceof _Complex)) |
{ |
operand = new _Complex(operand); |
} |
// a.re, a.im b.re, b.im |
// (a, b ) * (c, d ) = (a * c - b * d, a * d + b * c) |
return new _Complex( |
_sub(_mul(this.re, operand.re), _mul(this.im, operand.im)), |
_add(_mul(this.re, operand.im), _mul(this.im, operand.re))); |
}, |
sqrt: function () { |
function sgn (x) |
{ |
if (x == 0) |
{ |
return 0; |
} |
return x / Math.abs(x); |
} |
if (this.im == 0) |
{ |
return _sqrt(this.re); |
} |
return new _Complex( |
_sqrt( |
_div( |
_add( |
this.re, |
_sqrt(_add(_pow(this.re, 2), _pow(this.im, 2))) |
), |
2 |
) |
), |
_mul( |
sgn(this.im), |
_sqrt( |
_div( |
_add( |
_mul(this.re, -1), |
_sqrt(_add(_pow(this.re, 2), _pow(this.im, 2))) |
), |
2 |
) |
) |
) |
); |
}, |
/** |
* Returns this object as a string. |
* |
* (<code>{re: 1, im: 2}</code> → <code>"1 + 2j"</code>) |
* |
* @param {string} imUnit |
* The unit to use for the imaginary part. The default |
* is <code>"j"</code> (see above), but sometimes people |
* prefer <code>"i"</code> and can pass that instead. |
* @return {string} |
*/ |
toString: function (imUnit) { |
if (!imUnit) |
{ |
imUnit = "j"; |
} |
var re = this.re; |
var im = this.im; |
return ((re || "") |
+ (im |
? (re && im >= 0 ? "+" : "") + im + imUnit |
: "")) |
|| "0"; |
}, |
/** |
* @return {number|_Complex} |
*/ |
valueOf: function () { |
if (!this.im) |
{ |
return new this.re; |
} |
return this; |
} |
}); |
return { |
/** |
* @memberOf jsx.math.complex |
*/ |
Complex: _Complex |
}; |
}()); |
Property changes: |
Added: svn:mime-type |
## -0,0 +1 ## |
+text/plain |
\ No newline at end of property |
Index: trunk/math/rational.js |
=================================================================== |
--- trunk/math/rational.js (nonexistent) |
+++ trunk/math/rational.js (revision 525) |
@@ -0,0 +1,376 @@ |
+/** |
+ * <title>PointedEars' JSX: Math Library: Rational arithmetics</title> |
+ * @requires object.js |
+ * @requires types.js |
+ * |
+ * @section Copyright & Disclaimer |
+ * |
+ * @author |
+ * (C) 2013 Thomas Lahn <math.js@PointedEars.de> |
+ * |
+ * @partof PointedEars' JavaScript Extensions (JSX) |
+ * |
+ * JSX is free software: you can redistribute it and/or modify |
+ * it under the terms of the GNU General Public License as published by |
+ * the Free Software Foundation, either version 3 of the License, or |
+ * (at your option) any later version. |
+ * |
+ * JSX is distributed in the hope that it will be useful, |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ * GNU General Public License for more details. |
+ * |
+ * You should have received a copy of the GNU General Public License |
+ * along with JSX. If not, see <http://www.gnu.org/licenses/>. |
+ */ |
+ |
+if (typeof jsx == "undefined") |
+{ |
+ /** |
+ * @namespace |
+ */ |
+ var jsx = {}; |
+} |
+ |
+if (typeof jsx.math == "undefined") |
+{ |
+ /** |
+ * @namespace |
+ */ |
+ jsx.math = {}; |
+} |
+ |
+/** |
+ * @namespace |
+ */ |
+jsx.math.rational = (function () { |
+ /* Imports */ |
+ var _gcd; |
+ function _get_gcd () |
+ { |
+ if (!_gcd) |
+ { |
+ _gcd = jsx.math.integer.gcd; |
+ } |
+ |
+ return _gcd; |
+ } |
+ |
+ /** |
+ * Returns the value of this fraction as a {@link Number}. |
+ * |
+ * @return {number} |
+ */ |
+ function _toNumber () |
+ { |
+ return this.numerator / this.denominator; |
+ } |
+ |
+ var _rx = /((\d+)\s+)?(\d+)[\/∕](\d+)/; |
+ |
+ var _Fraction = jsx.object.extend( |
+ /** |
+ * A fraction is a rational number, a numerator divided by |
+ * a denominator. |
+ * |
+ * @constructor |
+ * @param {jsx.math.rational.Fraction|Number|String} numerator |
+ * @param {Number} denominator |
+ * @param {boolean} _dontReduce |
+ * <code>true</code> if the fraction should not be reduced |
+ * even though {@link jsx.math.rational.Fraction.autoreduce} |
+ * is set. Required for proper operation of some calling methods. |
+ */ |
+ function jsx_math_rational_Fraction (numerator, denominator, _dontReduce) { |
+ /* Called as a function? */ |
+ if (!(this instanceof _Fraction)) |
+ { |
+ return new _Fraction(numerator, denominator, _dontReduce); |
+ } |
+ |
+ if (numerator instanceof _Fraction) |
+ { |
+ /* Clone object */ |
+ var num = numerator.numerator; |
+ var denom = numerator.denominator; |
+ } |
+ else if (numerator && typeof numerator.valueOf() == "string") |
+ { |
+ /* Parse fraction string */ |
+ var m; |
+ if ((m = numerator.match(_rx))) |
+ { |
+ var integer_part = m[2]; |
+ num = m[3]; |
+ denom = m[4]; |
+ |
+ if (integer_part) |
+ { |
+ num = (+num) + (integer_part) * denom; |
+ } |
+ } |
+ else |
+ { |
+ jsx.throwThis(jsx.InvalidArgumentError); |
+ } |
+ } |
+ else |
+ { |
+ num = numerator; |
+ denom = (typeof denominator != "undefined" ? denominator : 1); |
+ } |
+ |
+ this.numerator = +num; |
+ this.denominator = +denom; |
+ |
+ if (_Fraction.autoReduce && !_dontReduce) |
+ { |
+ return this.reduce(); |
+ } |
+ }, |
+ { |
+ /** |
+ * If <code>true</code> calls |
+ * {@link jsx.math.rational.Fraction.prototype.reduce() reduce()} |
+ * on results automatically. The default is <code>false</code> |
+ * for better performance. |
+ * |
+ * @memberOf jsx.math.rational.Fraction |
+ */ |
+ autoReduce: false, |
+ |
+ /** |
+ * Converts two fractions into like quantities |
+ * and returns the result. |
+ * |
+ * <code>(</code>1∕2<code>,</code> 2∕3<code>)</code> |
+ * → <code>[</code>3∕6<code>,</code> 4∕6<code>]</code> |
+ * |
+ * @param {_Fraction} a |
+ * @param {_Fraction} b |
+ * @return {Array[_Fraction, _Fraction]} |
+ */ |
+ like: function (a, b) { |
+ if (!(a instanceof _Fraction)) |
+ { |
+ a = new _Fraction(a); |
+ } |
+ |
+ if (!(b instanceof _Fraction)) |
+ { |
+ b = new _Fraction(b); |
+ } |
+ |
+ var denom = a.denominator * b.denominator; |
+ |
+ return [ |
+ new _Fraction(a.numerator * b.denominator, denom, true), |
+ new _Fraction(b.numerator * a.denominator, denom, true) |
+ ]; |
+ } |
+ } |
+ ).extend(null, { |
+ /* Unary operators */ |
+ /** |
+ * Returns the complement of this fraction with regard to addition. |
+ * |
+ * 1∕2 → −1∕2 |
+ * |
+ * @memberOf jsx.math.rational.Fraction.prototype |
+ * @return {_Fraction} |
+ */ |
+ negate: function () { |
+ return new _Fraction(-this.numerator, this.denominator); |
+ }, |
+ |
+ /* Binary operators */ |
+ /** |
+ * Adds two fractions and returns the result. |
+ * |
+ * (1∕2) + (2∕3) → 7∕6 |
+ * |
+ * @param {_Fraction} operand |
+ * @return {_Fraction} |
+ */ |
+ add: function (operand) { |
+ if (!(operand instanceof _Fraction)) |
+ { |
+ operand = new _Fraction(operand); |
+ } |
+ |
+ var likes = _Fraction.like(this, operand); |
+ |
+ return new _Fraction( |
+ likes[0].numerator + likes[1].numerator, |
+ likes[0].denominator |
+ ); |
+ }, |
+ |
+ /** |
+ * Subtracts a fraction from another and returns the result. |
+ * |
+ * (2∕3) − (1∕2) ≡ (2∕3) + (−1∕2) → 1∕6 |
+ * |
+ * @param {_Fraction} operand |
+ * @return {_Fraction} |
+ */ |
+ subtract: function (operand) { |
+ if (!(operand instanceof _Fraction)) |
+ { |
+ operand = new _Fraction(operand); |
+ } |
+ |
+ return this.add(this, operand.negate()); |
+ }, |
+ |
+ /** |
+ * Multiplies two fractions and returns the result. |
+ * |
+ * (1∕2) × (2∕3) → 2∕6 |
+ * |
+ * @param {_Fraction} operand |
+ * @return {_Fraction} |
+ */ |
+ multiply: function (operand) { |
+ if (!(operand instanceof _Fraction)) |
+ { |
+ operand = new _Fraction(operand); |
+ } |
+ |
+ return new _Fraction( |
+ this.numerator * operand.numerator, |
+ this.denominator * operand.denominator |
+ ); |
+ }, |
+ |
+ /** |
+ * Divides a fraction by another and returns the result. |
+ * |
+ * (1∕2) ∕ (4∕6) ≡ (1∕2) × (6∕4) → 6∕8 |
+ * @param {_Fraction} operand |
+ * @return {_Fraction} |
+ */ |
+ divide: function (operand) { |
+ if (!(operand instanceof _Fraction)) |
+ { |
+ operand = new _Fraction(operand); |
+ } |
+ |
+ return new _Fraction( |
+ this.numerator * operand.denominator, |
+ this.denominator * operand.numerator |
+ ); |
+ }, |
+ |
+ /* Equivalent transformations */ |
+ /** |
+ * Extends a fraction by a factor. |
+ * |
+ * 1∕2 × 2 → 2∕4 |
+ * |
+ * @param {Number} factor |
+ * @return {_Fraction} |
+ * @see #reduce() |
+ */ |
+ extend: function (factor) { |
+ return this.multiply(new _Fraction(factor, factor)); |
+ }, |
+ |
+ /** |
+ * Reduces a fraction to its simplest equivalent. |
+ * |
+ * 4∕8 → 1∕2 |
+ * |
+ * @return {_Fraction} |
+ * @see #extend() |
+ */ |
+ reduce: function () { |
+ var num = this.numerator; |
+ var denom = this.denominator; |
+ var gcd = _get_gcd()(num, denom); |
+ |
+ if (gcd > 1) |
+ { |
+ return new _Fraction(num / gcd, denom / gcd, true); |
+ } |
+ |
+ return new _Fraction(this, null, true); |
+ }, |
+ |
+ /** |
+ * Returns the reciprocal of this fraction. |
+ * |
+ * 2∕3 → 3∕2 |
+ * |
+ * @return {_Fraction} |
+ */ |
+ reciprocal: function () { |
+ return new _Fraction(this.denominator, this.numerator); |
+ }, |
+ |
+ toNumber: _toNumber, |
+ |
+ /* Conversion */ |
+ /** |
+ * Returns the string representation of this fraction. |
+ * |
+ * @return {string} |
+ */ |
+ toString: function () { |
+ var num = this.numerator; |
+ var denom = this.denominator; |
+ return (num ? num + (denom != 1 ? "/" + denom : "") : "0"); |
+ }, |
+ |
+ /** |
+ * Returns this fraction as a vulgar fraction. |
+ * |
+ * 5∕3 → <code>[1, </code>2∕3<code>]</code> |
+ * |
+ * @return {Array[int, _Fraction]} |
+ */ |
+ toVulgar: function () { |
+ var integer_part = |
+ (this.numerator - (this.numerator % this.denominator)) |
+ / this.denominator; |
+ |
+ return [ |
+ integer_part, |
+ new _Fraction( |
+ this.numerator - (integer_part * this.denominator), |
+ this.denominator |
+ ) |
+ ]; |
+ }, |
+ |
+ /** |
+ * Returns this fraction as a vulgar fraction string. |
+ * |
+ * 5∕3 → <code>"1 2∕3"</code> |
+ * |
+ * @return {string} |
+ */ |
+ toVulgarString: function () { |
+ var vulgar = this.toVulgar(); |
+ var integer_part = vulgar[0]; |
+ var fraction_part = vulgar[1]; |
+ |
+ return (integer_part ? integer_part : "") |
+ + (fraction_part.numerator |
+ ? (integer_part ? " " : "") + fraction_part.toString() |
+ : (integer_part ? "" : "0")); |
+ }, |
+ |
+ /** |
+ * @see #toNumber() |
+ */ |
+ valueOf: _toNumber |
+ }); |
+ |
+ return { |
+ /** |
+ * @memberOf jsx.math.rational |
+ */ |
+ Fraction: _Fraction |
+ }; |
+}()); |
\ No newline at end of file |
/trunk/math/rational.js |
---|
Property changes: |
Added: svn:mime-type |
## -0,0 +1 ## |
+text/plain |
\ No newline at end of property |
Index: trunk/math/algebra.js |
=================================================================== |
--- trunk/math/algebra.js (revision 524) |
+++ trunk/math/algebra.js (revision 525) |
@@ -1,15 +1,14 @@ |
/** |
* <title>PointedEars' JSX: Math Library: Linear Algebra</title> |
* @requires object.js |
- * @requires types.js |
* |
* @section Copyright & Disclaimer |
- * |
+ * |
* @author |
- * (C) 2000-2011 Thomas Lahn <math.js@PointedEars.de> |
+ * (C) 2000-2011, 2013 Thomas Lahn <js@PointedEars.de> |
* |
* @partof PointedEars' JavaScript Extensions (JSX) |
- * |
+ * |
* JSX is free software: you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation, either version 3 of the License, or |
@@ -24,42 +23,446 @@ |
* along with JSX. If not, see <http://www.gnu.org/licenses/>. |
*/ |
-/** @subsection Matrix Operations */ |
+(function () { |
+ /* Imports */ |
+ var _isArray = jsx.object.isArray; |
+ var _get = jsx.array.get; |
+ var _math = jsx.math; |
+ var _add = _math.add; |
+ var _sub = _math.sub; |
+ var _mul = _math.mul; |
+ var _pow = _math.pow; |
+ var _sqrt = _math.sqrt; |
-/** |
- * Creates a <code>Matrix</code> object encapsulating an m × n matrix |
- * represented by an array of arrays. |
- * |
- * Different to a "multi-dimensional" array, a <code>Matrix</code>'s |
- * elements are indexed (like in math) starting from 1, i. e. the |
- * first element of the first row has the coordinates <code>[1, 1]</code>. |
- * |
- * @param rows : optional Array |
- * The array containing the elements of the new matrix or the number |
- * of rows of the new matrix. |
- * If not provided, the matrix has only one element, <code>0</code>. |
- * @param columns: optional Number |
- * The number of columns of the matrix, if <var>rows</var> is not an |
- * <code>Array</code> |
- * @param fill: optional Number |
- * The number the matrix should be filled with, if <var>rows</var> |
- * is not an <code>Array</code>. The default is <code>0</code>. |
- */ |
-jsx.math.Matrix = (function () { |
- var isMethod = jsx.object.isMethod; |
+ var _Tensor = ( |
+ /** |
+ * Creates a <code>Tensor</code> object encapsulating |
+ * an tensor represented by an array of arrays. |
+ * |
+ * A tensor is a generalization of scalars, vectors, and |
+ * matrices to an arbitrary number of indices. The rank |
+ * of a tensor specifies the number of its indices: A scalar |
+ * is a tensor of rank 0 (no index), a vector of rank 1, |
+ * a matrix of rank 2, aso. |
+ * |
+ * @constructor |
+ * @param {Array} components |
+ * @return {jsx.math.Tensor} when called as a factory |
+ */ |
+ function (components) { |
+ if (!(this instanceof _Tensor)) |
+ { |
+ return new _Tensor(components); |
+ } |
- return function (rows, columns, fill) { |
- this.data = [0]; |
+ /** |
+ * The components of this tensor |
+ * @type Array |
+ */ |
+ this.data = components || [0]; |
+ } |
+ ).extend(null, { |
+ /* Initialisers */ |
- if (rows) |
+ /** |
+ * Fills this tensor with components. |
+ * |
+ * @memberOf jsx.math.Tensor.prototype |
+ * @param {Array} dimensions (optional) |
+ * The array containing the dimensions of the tensor. |
+ * If not provided, the tensor is the scalar <code>0</code>. |
+ * @param {Number|Function} fill (optional) |
+ * The value the tensor should be filled with. If a function, |
+ * the return value of the function for each component, whereas |
+ * the function is passed the indices of the component as |
+ * argument list. The default is <code>0</code>. |
+ * @todo |
+ */ |
+ fill: function (dimensions, fill) { |
+ return jsx.throwThis(_math.NotImplementedError); |
+ }, |
+ |
+ /* Information methods */ |
+ |
+ /** |
+ * Returns a component of this tensor by coordinates, |
+ * or a copy of of it as an <code>Array</code>. |
+ * |
+ * @param {Array} coords (optional) |
+ * @return {any|Array} |
+ */ |
+ "get": function (coords) { |
+ var data = this.data.map(function (row) { |
+ return _isArray(row) ? row.slice() : row; |
+ }).slice(); |
+ |
+ if (_isArray(coords)) |
+ { |
+ for (var i = 0, len = coords.length; i < len; ++i) |
+ { |
+ var coord = coords[i]; |
+ |
+ data = data[coord]; |
+ |
+ if (!data) |
+ { |
+ break; |
+ } |
+ } |
+ } |
+ |
+ return data; |
+ }, |
+ |
+ /** |
+ * Sets a component of this tensor by coordinates. |
+ * |
+ * @param {Array} coords |
+ * @param {any} value |
+ * @return {any} |
+ * The new component value |
+ */ |
+ "set": function (coords, value) { |
+ var tmp = this.data; |
+ |
+ var i = 0; |
+ for (var len = coords.length; i < len - 1; ++i) |
+ { |
+ var coord = coords[i]; |
+ if (coord < 0) |
+ { |
+ jsx.throwThis("jsx.math.CoordinateError"); |
+ return; |
+ } |
+ |
+ if (i < len - 1) |
+ { |
+ if (!_isArray(tmp[coord])) |
+ { |
+ tmp[coord] = []; |
+ } |
+ |
+ tmp = tmp[coord]; |
+ } |
+ } |
+ |
+ var last_coord = _get(coords, -1); |
+ tmp[last_coord] = value; |
+ |
+ return tmp[last_coord]; |
+ }, |
+ |
+ /** |
+ * Returns the size of this tensor, i.e. the |
+ * maximum of used indexes + 1, per index. |
+ * |
+ * @return {Array} |
+ * @todo |
+ */ |
+ size: function () { |
+ return jsx.throwThis(_math.NotImplementedError); |
+ |
+ var data = this.data; |
+ var sizes = [data.length]; |
+ var index = 1; |
+ |
+ while (_isArray(data = data[0])) |
+ { |
+ sizes[i++] = Math.max.apply(data.map(function (e) { |
+ return e.length; |
+ })); |
+ } |
+ |
+ return sizes; |
+ }, |
+ |
+ /** |
+ * Returns the rank of this tensor, i.e. the |
+ * maximum number of used indexes. |
+ * |
+ * @return {number} |
+ */ |
+ rank: function () { |
+ return this.size().length; |
+ }, |
+ |
+ /* Basic operations */ |
+ |
+ /** |
+ * Returns the sum of this tensor and compatible one. |
+ * |
+ * <p>Example:</p><pre> |
+ * [[[[1]], …], …] + [[[[1]], …], …] = [[[[2]], …], …] |
+ * a_0,0,0,0=1 + b_0,0,0,0=1 = c_0,0,0,0=2</pre> |
+ * @param {jsx.math.Tensor} tensor2 |
+ * @return {jsx.math.Tensor} |
+ */ |
+ add: function (tensor2) { |
+ var data = this.get(); |
+ var data2 = tensor2.get(); |
+ |
+ for (var i = data.length; i--;) |
+ { |
+ var row = data[i]; |
+ if (typeof row == "number") |
+ { |
+ data[i] = _add(data[i], data2[i]); |
+ } |
+ else |
+ { |
+ for (var j = row.length; j--;) |
+ { |
+ if (_isArray(row[j])) |
+ { |
+ row[j] = _Tensor(row[j]).add(_Tensor(data2[i][j])).get(); |
+ } |
+ else |
+ { |
+ row[j] = _add(row[j], data2[i][j]); |
+ } |
+ } |
+ } |
+ } |
+ |
+ return new this.constructor(data); |
+ }, |
+ |
+ /** |
+ * Returns the difference between this tensor and a compatible one. |
+ * |
+ * @param {jsx.math.Tensor|Number} m2 |
+ * @return {jsx.math.Tensor} |
+ */ |
+ sub: function (tensor2) { |
+ return this.add(_mul(tensor2, -1)); |
+ } |
+ }); |
+ |
+ var _Vector = (function () { |
+ /** |
+ * Returns the result of multiplication of this vector with a value. |
+ * <p> |
+ * Returns the result of multiplication of this vector |
+ * by a scalar, or the cross product of this vector and |
+ * another vector. |
+ * </p><p> |
+ * The cross product is only defined for three-dimensional vectors. |
+ * It is a vector that is orthogonal to both the operand vectors, |
+ * considering their direction. |
+ * </p> |
+ * @param {Number|jsx.math.Vector} value |
+ * @return {jsx.math.Vector} |
+ * <code>this</code> × <code><var>vector2</var></code> |
+ * @throws {jsx.InvalidArgumentError} |
+ * if the vectors are not compatible |
+ */ |
+ function _cross (value) |
{ |
- if (jsx.types.isArray(rows)) |
+ var data = this.get(); |
+ |
+ if (!(value instanceof _Matrix)) |
{ |
- if (isMethod(rows, "slice")) |
+ /* Scalar multiplication */ |
+ for (var i = data.length; i--;) |
{ |
+ for (var j = data[i].length; j--;) |
+ { |
+ data[i][j] = _mul(data[i][j], value); |
+ } |
+ } |
+ |
+ return new _Matrix(data); |
+ } |
+ |
+ var data2 = value.get(); |
+ |
+ var len = data.length; |
+ var len2 = data2.length; |
+ if (len != 3 || len2 != 3 || len != len2) |
+ { |
+ return jsx.throwThis(jsx.InvalidArgumentError, |
+ [null, |
+ this + " and " + value, |
+ "two three-dimensional vectors"]); |
+ } |
+ |
+ return new Vector([ |
+ _sub(_mul(data[1], data2[2]), _mul(data[2], data2[1])), |
+ _sub(_mul(data[0], data2[2]), _mul(data[2], data2[0])), |
+ _sub(_mul(data[0], data2[1]), _mul(data[1], data2[0])) |
+ ]); |
+ } |
+ |
+ var _Vector = ( |
+ /** |
+ * @constructor |
+ */ |
+ function jsx_math_Vector (components) { |
+ if (!(this instanceof _Vector)) |
+ { |
+ return new _Vector(components); |
+ } |
+ |
+ this.data = components || [0]; |
+ } |
+ ).extend(_Tensor, { |
+ /* Information methods */ |
+ |
+ /** |
+ * Returns the magnitude (or length) of this vector. |
+ * |
+ * @memberOf jsx.math.Vector.prototype |
+ * @return {number} |
+ */ |
+ mag: function () { |
+ var sum = 0; |
+ |
+ var components = this.get(); |
+ for (var i = components.length; i--;) |
+ { |
+ sum = _add(sum, _pow(components[i], 2)); |
+ } |
+ |
+ return _sqrt(sum); |
+ }, |
+ |
+ /** |
+ * Returns the angle between this vector and another vector. |
+ * |
+ * @param {jsx.math.Vector} vector2 |
+ * @return {number} |
+ * Angle between this vector and <code><var>vector2</var></code> |
+ * in radians. |
+ */ |
+ angle: function (vector2) { |
+ return Math.acos(this.dot(vector2) / (this.mag() * vector2.mag())); |
+ }, |
+ |
+ /* Operations */ |
+ |
+ cross: _cross, |
+ mul: _cross, |
+ |
+ /** |
+ * Returns the dot product of this vector and another vector. |
+ * <p> |
+ * The dot product is defined for two vectors of arbitrary, |
+ * but equal dimension. It is a scalar that can be used to |
+ * determine the angle between the two vectors. |
+ * </p> |
+ * @param {jsx.math.Vector} vector2 |
+ * @return {any} <code><var>this</var></code> · <code><var>vector2</var></code> |
+ * @see #angle(jsx.math.Vector) |
+ * @throws {jsx.InvalidArgumentErrror} |
+ * if the dimensions of the vectors are not equal |
+ */ |
+ dot: function (vector2) { |
+ var result = 0; |
+ var data = this.get(); |
+ var data2 = vector2.get(); |
+ |
+ for (var i = data.length; i--;) |
+ { |
+ result = _add(result, _mul(data[i], data2[i])); |
+ } |
+ |
+ return result; |
+ }, |
+ |
+ /** |
+ * Returns the Hadamard/Schur/entrywise product of two matrices. |
+ * |
+ * @param {jsx.math.Vector} vector2 |
+ * @return {jsx.math.Vector} |
+ */ |
+ mulEntrywise: function (vector2) { |
+ var data = this.get(); |
+ var data2 = vector2.get(); |
+ |
+ for (var i = data.length; i--;) |
+ { |
+ data[i] *= data2[i]; |
+ } |
+ |
+ return new _Vector(data); |
+ } |
+ }); |
+ |
+ return _Vector; |
+ }()); |
+ |
+ /** @subsection Matrix algrebra */ |
+ |
+ var _Matrix = jsx.object.extend( |
+ /** |
+ * Creates a <code>Matrix</code> object encapsulating |
+ * an m × n matrix and associated operations. |
+ * |
+ * The matrix components are represented by |
+ * an <Code>Array</code> of <code>Array</code>s. |
+ * |
+ * @constructor |
+ * @param {Array|Number|Null|Undefined} rows (optional) |
+ * The <code>Array</code> of <code>Arrays</code> containing |
+ * the components of the new matrix, or the number of its rows. |
+ * The default (used when the argument is a null-value), |
+ * or <code>null</code> is <code>1</code>, which creates a |
+ * matrix that transforms like a scalar if |
+ * <code><var>columns</var> is a null-value, |
+ * and like a row vector if <code><var>columns</var> > 1</code>. |
+ * @param {Number|Null|Undefined} columns (optional) |
+ * The number of columns of the matrix; ignored if <var>rows</var> |
+ * is an <code>Array</code>. Otherwise, the default is <code>1</code>, |
+ * which creates a matrix that transforms like a scalar if |
+ * <code><var>rows</var> is a null-value</code>, and like a |
+ * column vector if <code><var>rows</var> > 1</code>. |
+ * @param {Number|Function|any} fill (optional) |
+ * The value the matrix should be filled with. A <code>Number</code>, |
+ * or a <code>Function</code> returning a <code>Number</code>, |
+ * is preferred (as being ideal for further computations), |
+ * but not required. |
+ * |
+ * If a <code>Function</code>, the return value of the |
+ * <code>Function</code> for each component is used, where |
+ * the <code>Function</code> is passed the indices of |
+ * the component as arguments, and the <code>this</code> |
+ * value is set to the new instance. (For example, specify |
+ * <code><var>rows</var> === <var>columns</var></code> |
+ * and {@link jsx.math.Matrix.KRONECKER_DELTA} |
+ * to create an identity/unit matrix.) |
+ * The default is <code>undefined</code>. |
+ * @return {jsx.math.Matrix} when called as a factory |
+ * @throws {jsx.math.DimensionError} |
+ * if the matrix has less than 1 row or a row has less |
+ * than 1 column. |
+ */ |
+ function jsx_math_Matrix (rows, columns, fill) { |
+ if (!(this instanceof _Matrix)) |
+ { |
+ return _Matrix.construct(arguments); |
+ } |
+ |
+ this.data = []; |
+ |
+ if (_isArray(rows)) |
+ { |
+ if (rows.length < 1) |
+ { |
+ return jsx.throwThis(_math.DimensionError); |
+ } |
+ |
+ /* Set Matrix from Array */ |
+ if (typeof rows.slice == "function") |
+ { |
for (var i = rows.length; i--;) |
{ |
- this.data[i] = rows[i].slice(); |
+ var row = rows[i]; |
+ if (row.length < 1) |
+ { |
+ return jsx.throwThis(_math.DimensionError); |
+ } |
+ |
+ this.data[i] = _isArray(row) ? row.slice() : row; |
} |
} |
else |
@@ -67,6 +470,11 @@ |
for (var i = rows.length; i--;) |
{ |
var row = rows[i]; |
+ if (row.length < 1) |
+ { |
+ return jsx.throwThis(_math.DimensionError); |
+ } |
+ |
this.data[i] = []; |
for (var j = row.length; j--;) |
{ |
@@ -77,209 +485,486 @@ |
} |
else |
{ |
+ /* Build Matrix by dimensions */ |
var a = []; |
- var tmp = []; |
- tmp.length = columns; |
- |
- if (typeof fill == "undefined") |
+ /* null or undefined */ |
+ if (rows == null) |
{ |
- fill = 0; |
+ rows = 1; |
} |
- |
- for (var j = 0; j < columns; ++j) |
+ |
+ if (!rows || rows < 1) |
{ |
- tmp[j] = fill; |
+ return jsx.throwThis(_math.DimensionError); |
} |
+ if (columns == null) |
+ { |
+ columns = 1; |
+ } |
+ |
+ if (columns < 1) |
+ { |
+ return jsx.throwThis(_math.DimensionError); |
+ } |
+ |
for (var i = 0; i < rows; ++i) |
{ |
- if (i == 0) |
+ var tmp = []; |
+ tmp.length = columns; |
+ |
+ for (var j = 0; j < columns; ++j) |
{ |
+ tmp[j] = (typeof fill == "function" |
+ ? fill.call(this, i, j) |
+ : fill); |
+ } |
+ |
+ if (tmp.length > 1) |
+ { |
a.push(tmp); |
} |
else |
{ |
- a.push(tmp.slice()); |
+ a.push(tmp[0]); |
} |
} |
this.data = a; |
} |
+ }, |
+ { |
+ /** |
+ * The Kronecker delta function. |
+ * |
+ * Takes two arguments, <var>i</var> amd <var>j</var>, and |
+ * returns <code>1</code> if they are equal after implicit |
+ * type conversion, <code>0</code> otherwise. |
+ * Useful for creating identity/unit matrices. |
+ * |
+ * @param {Number|any} i |
+ * @param {Number|any} j |
+ * @memberOf jsx.math.Matrix |
+ * @return {number} |
+ * @see jsx.matrix.Matrix(Number, Number, Function) |
+ */ |
+ KRONECKER_DELTA: function (i, j) { return +(i == j); } |
} |
- }; |
-}()); |
+ ).extend(_Tensor, { |
+ /* Information methods */ |
-/** |
- * @param A |
- * @return number |
- * the row dimension of <code>A</code>; |
- * 1 if <code>A</code> is a scalar, |
- * greater than 1 if <code>A</code> is a vector or a matrix. |
- * |
- * <pre> |
- * Term X x dimRow(x) |
- * -------------------------------------------------------- |
- * scalar 1 1 1 |
- * |
- * mX1 col vector (1) |
- * (2) [1, 2, ..., m] m |
- * (.) |
- * (m) |
- * |
- * 1Xn row vector (1 2 ... n) [[1, 2, ..., n]] 1 |
- * |
- * (1 2 ... n) [[1, 2, ..., n], |
- * mXn matrix (2 . ... .) [2, ... ], m |
- * (. . ... .) [... ], |
- * (m . ... .) [m, ... ]] |
- * </pre> |
- */ |
-jsx.math.Matrix.dimRow = function(A) { |
- return (Array.isArray(A) |
- ? A.length |
- : 1); |
-}; |
+ /** |
+ * Returns the size of this matrix, i.e. the |
+ * numbers of rows, and the numbers of columns per row. |
+ * |
+ * @memberOf jsx.math.Matrix.prototype |
+ * @return {Array} |
+ */ |
+ size: function () { |
+ var data = this.data; |
+ var _size = [data.length, data[0].length]; |
+ _size.rows = _size[0]; |
+ _size.columns = _size[1]; |
-/** |
- * @param a |
- * @return number |
- * The column dimension of <code>A</code>, provided all |
- * rows of <code>A</code> have the same length (as the first one); |
- * 0 if <code>A</code> is a scalar, |
- * greater than 0 if <code>A</code> is a vector or a matrix. |
- * |
- * <pre> |
- * Term X x dimCol(x) |
- * -------------------------------------------------------- |
- * scalar 1 1 0 |
- * |
- * mX1 col vector (1) |
- * (2) [1, 2, ..., m] 1 |
- * (.) |
- * (m) |
- * |
- * 1Xn row vector (1 2 ... n) [[1, 2, ..., n]] n |
- * |
- * (1 2 ... n) [[1, 1, ..., n], |
- * mXn matrix (2 . ... .) [2, ... ], n |
- * (. . ... .) [... ], |
- * (m . ... .) [m, ... ]] |
- * </pre> |
- */ |
-jsx.math.Matrix.dimCol = function(a) { |
- return ( |
- typeof (a = a[0]) != "undefined" |
- ? (Array.isArray(a[0]) ? a[0].length : 1) |
- : 0); |
-}; |
+ return _size; |
+ }, |
-/** |
- * @param a |
- * @return the square root of the product of A's row dimension |
- * and its column dimension. The return value indicates |
- * whether a matrix A is square or not; for square matrices, |
- * the return value is an integer. |
- * @see jsx.math#add() |
- */ |
-jsx.math.Matrix.dim = function(a) { |
- return Math.sqrt(jsx.math.Matrix.dimRow(a) * jsx.math.Matrix.dimCol(a)); |
-}; |
+ /* Basic operations */ |
-jsx.math.Matrix.prototype = { |
- constructor: Math.Matrix, |
- |
- putValue: function (coords, value) { |
- var tmp = this.data; |
- |
- for (var i = 0, len = coords.length; i < len - 1; ++i) |
- { |
- var arg = coords[i] - 1; |
- if (arg < 0) |
+ /** |
+ * Returns the result of multiplication of this matrix with a value. |
+ * <p> |
+ * Returns the result of multiplication of this matrix |
+ * by a scalar, or of linear algebraic matrix multiplication |
+ * with another, compatible matrix. |
+ * </p> |
+ * @param {Number|jsx.math.Matrix} value |
+ * @return {jsx.math.Matrix} <code>this</code> × <code><var>value</var></code> |
+ * @throws {jsx.InvalidArgumentError} |
+ * if the matrices are not compatible (the row dimension |
+ * of this matrix must equal the column dimension of |
+ * the other one). |
+ */ |
+ mul: function (value) { |
+ var data = this.get(); |
+ |
+ if (!(value instanceof _Matrix)) |
{ |
- jsx.throwThis("jsx.math.CoordinateError"); |
- return; |
+ /* Scalar multiplication */ |
+ for (var i = data.length; i--;) |
+ { |
+ for (var j = data[i].length; j--;) |
+ { |
+ data[i][j] = _mul(data[i][j], value); |
+ } |
+ } |
+ |
+ return new _Matrix(data); |
} |
- |
- if (typeof tmp[arg] == "undefined") |
+ |
+ var data2 = value.get(); |
+ var m = new _Matrix(); |
+ var num_rows = data.length; |
+ var num_rows2 = data2.length; |
+ |
+ for (var k = 0, num_cols2 = data2[0].length; k < num_cols2; ++k) |
{ |
- tmp[arg] = []; |
+ for (var i = 0; i < num_rows; ++i) |
+ { |
+ var this_row = data[i]; |
+ var sum = 0; |
+ |
+ for (var j = 0, num_cols = this_row.length; j < num_cols; ++j) |
+ { |
+ if (j > num_rows2 - 1) |
+ { |
+ return jsx.throwThis(jsx.InvalidArgumentError, |
+ ["Incompatible matrices", |
+ "m1.size() === [m, n]; m2.size() === [n, x]", |
+ this.size() + ", " + value.size()]); |
+ } |
+ |
+ sum = _add(sum, _mul(data[i][j], data2[j][k])); |
+ } |
+ |
+ m.set([i, k], sum); |
+ } |
} |
- tmp = tmp[arg]; |
- } |
- |
- var lastCoord = coords.slice(coords.length - 1); |
- if (lastCoord < 1) |
- { |
- jsx.throwThis("jsx.math.CoordinateError"); |
- return; |
- } |
- |
- tmp[lastCoord - 1] = value; |
+ return m; |
+ }, |
- return tmp[lastCoord - 1]; |
- }, |
+ /** |
+ * Returns the Hadamard/Schur/entrywise product of two matrices. |
+ * |
+ * @param {jsx.math.Matrix} m2 |
+ * @return {jsx.math.Matrix} |
+ */ |
+ mulEntrywise: function (matrix2) { |
+ var data = this.get(); |
+ var data2 = matrix2.get(); |
- getValue: function (coords) { |
- var tmp = this.data; |
+ for (var i = data.length; i--;) |
+ { |
+ for (var j = data[i].length; j--;) |
+ { |
+ data[i][j] = _mul(data[i][j], data2[i][j]); |
+ } |
+ } |
- for (var i = 0, len = coords.length; i < len; ++i) |
- { |
- var arg = coords[i] - 1; |
- tmp = tmp[arg]; |
+ return new _Matrix(data); |
+ }, |
- if (typeof tmp == "undefined") |
+ /** |
+ * Returns the Kronecker product of two matrices. |
+ * |
+ * @param {Matrix} matrix2 |
+ * @return {jsx.math.Matrix} |
+ * @todo |
+ */ |
+ mulKronecker: function (matrix2) { |
+ var rows = this.get(); |
+ var rows2 = matrix2.get(); |
+ var size = this.size(); |
+ var size2 = matrix2.size(); |
+ var new_matrix = new _Matrix(size.rows * size2.rows, size.columns * size2.columns); |
+ var new_data = new_matrix.get(); |
+ var new_size = new_matrix.size(); |
+ |
+// return jsx.throwThis(jsx.math.NotImplementedError); |
+ |
+ var new_row = new_size.rows - 1; |
+ |
+ for (var i = rows.length; i--;) |
{ |
- break; |
+ var row = rows[i]; |
+ var len2 = row.length; |
+ |
+ for (var m = rows2.length; m--;) |
+ { |
+ var new_column = new_size.columns - 1; |
+ var m2_row = rows2[m]; |
+ var len4 = m2_row.length; |
+ |
+ for (var j = len2; j--;) |
+ { |
+ for (var n = len4; n--;) |
+ { |
+ new_data[new_row][new_column--] = _mul(row[j], m2_row[n]); |
+ } |
+ } |
+ |
+ --new_row; |
+ } |
} |
- } |
- return tmp; |
- }, |
+ return new _Matrix(new_data); |
+ }, |
- inc: function (coords) { |
- var |
- v = +this.getValue.apply(this, coords); |
+ /** |
+ * Increases a component of this matrix by 1 |
+ */ |
+ inc: function (coords) { |
+ var v = +this.get.apply(this, coords); |
- return this.putValue.apply( |
- this, coords.concat((isNaN(v) ? 0 : +v) + 1)); |
- }, |
- |
- toString: |
+ return this.set.apply(this, coords.concat((isNaN(v) ? 0 : v) + 1)); |
+ }, |
+ |
/** |
- * Returns the matrix converted to string, i.e. |
- * elements of arrays arranged in rows and columns. |
+ * Divides each component of this matrix by a value, |
+ * and returns the resulting matrix. |
+ * |
+ * @param {Number} value |
+ * @return {jsx.math.Matrix} |
+ */ |
+ div: function (value) { |
+ if (value instanceof _Matrix) |
+ { |
+ return jsx.throwThis(jsx.InvalidArgumentError, [null, "Number", value]); |
+ } |
+ |
+ return this.mul(1 / value); |
+ }, |
+ |
+ /** |
+ * Applies a mapping callback to each component of this matrix |
+ * and returns the result. |
+ * |
+ * @param {Function} callback |
+ * Called for each component, with the component, its |
+ * coordinates as an <code>Array</code>, and this |
+ * <code>Matrix</code> as argument list. Its return value |
+ * is used as the returned new component value. |
+ * @param {Object} thisValue (optional) |
+ * The callbacks <code>this</code> value. The default |
+ * is this <code>Matrix</code>. |
+ * @return {jsx.math.Matrix} |
+ */ |
+ map: function (callback, thisValue) { |
+ return new _Matrix(this.get().map(function (row, rowIndex, matrix) { |
+ return row.map(function (component, columnIndex) { |
+ return callback.call(thisValue, component, [rowIndex, columnIndex], matrix); |
+ }); |
+ })); |
+ }, |
+ |
+ /** |
+ * Computes the square root of each component of this matrix |
+ * and returns the result. |
+ * |
+ * @return {jsx.math.Matrix} |
+ */ |
+ sqrt: function () { |
+ return this.map(function (component) { |
+ return _sqrt(component); |
+ }); |
+ }, |
+ |
+ /** |
+ * Returns the transpose of this matrix |
+ * |
+ * @return {jsx.math.Matrix} |
+ */ |
+ transpose: function () { |
+ var size = this.size(); |
+ var transposed = new _Matrix(size.columns, size.rows).get(); |
+ |
+ var data = this.get(); |
+ for (var i = data.length; i--;) |
+ { |
+ var row = data[i]; |
+ |
+ for (var j = row.length; j--;) |
+ { |
+ transposed[j][i] = row[j]; |
+ } |
+ } |
+ |
+ return new _Matrix(transposed); |
+ }, |
+ |
+ /* Row operations */ |
+ |
+ /** |
+ * Switches two rows of a matrix and returns the result. |
+ * |
+ * @param {Number} row1 |
+ * @param {Number} row2 |
+ * @return {jsx.math.Matrix} |
+ */ |
+ switchRows: function (row1, row2) { |
+ var rows = this.get(); |
+ |
+ var tmp = rows[row1]; |
+ rows[row1] = rows[row2]; |
+ rows[row2] = tmp; |
+ |
+ return new _Matrix(rows); |
+ }, |
+ |
+ /** |
+ * Multiplies each element in a row by a non-zero constant. |
+ * |
+ * @param {Number} rowIndex |
+ * @param {Number} factor |
+ * @return {jsx.math.Matrix} |
+ */ |
+ mulRow: function (rowIndex, factor) { |
+ if (factor == 0) |
+ { |
+ return jsx.throwThis(jsx.InvalidArgumentError, |
+ ["Factor must not be 0"]); |
+ } |
+ |
+ var rows = this.get(); |
+ var row = rows[rowIndex]; |
+ |
+ if (!row) |
+ { |
+ return jsx.throwThis(jsx.InvalidArgumentError, |
+ ["No such row index", rowIndex]); |
+ } |
+ |
+ for (var j = row.length; j--;) |
+ { |
+ row[j] = _mul(row[j], factor); |
+ } |
+ |
+ return new _Matrix(rows); |
+ }, |
+ |
+ /* Methods for square matrices */ |
+ |
+ /* Information methods */ |
+ |
+ /** |
+ * Returns the trace of this matrix. |
+ * |
+ * The trace of a matrix is the sum of its main diagonal |
+ * components. It is therefore only defined for square |
+ * matrices. |
+ * |
+ * @return {number} |
+ * @throws {TypeError} |
+ * if the matrix has no or broken rows, or is not square |
+ */ |
+ trace: function () { |
+ var result; |
+ var data = this.data; |
+ |
+ for (var i = 0, len = data.length; i < len; ++i) |
+ { |
+ var row = data[i]; |
+ if (!row) |
+ { |
+ return jsx.throwThis("TypeError", "No or broken rows"); |
+ } |
+ |
+ var component = row[i]; |
+ if (!component) |
+ { |
+ return jsx.throwThis("TypeError", "Not a square matrix"); |
+ } |
+ |
+ if (typeof result == "undefined") |
+ { |
+ result = component; |
+ } |
+ else |
+ { |
+ result = _add(result, component); |
+ } |
+ } |
+ |
+ return result; |
+ }, |
+ |
+ /** |
+ * Returns the determinant of this matrix. |
+ * |
+ * @return {Number} |
+ * <code>NaN</code> if one or more components could not |
+ * be converted to <code>Number</code>, or more than one |
+ * component was not finite. <code>Infinity</code> or |
+ * <code>-Infinity</code> if one component was not finite. |
+ * @throws {TypeError} if the Matrix is not square |
+ */ |
+ det: function () { |
+ var result = 0; |
+ var data = this.data; |
+ var num_rows = data.length; |
+ var first_row = data[0]; |
+ var num_cols = first_row.length; |
+ |
+ if (num_rows != num_cols) |
+ { |
+ return jsx.throwThis("TypeError", "Not a square matrix"); |
+ } |
+ |
+ if (num_cols == 2) |
+ { |
+ return data[0][0] * data[1][1] - data[0][1] * data[1][0]; |
+ } |
+ |
+ for (var col = 0; col < num_cols; ++col) |
+ { |
+ var product1 = data[0][col]; |
+ var product2 = product1; |
+ var offset = 0; |
+ |
+ for (var row = 1; row < num_cols; ++row) |
+ { |
+ ++offset; |
+ var this_row = data[row]; |
+ product1 = _mul(product1, this_row[(col + offset) % num_cols]); |
+ product2 = _mul(product2, _get(this_row, col - offset)); |
+ } |
+ |
+ result = _add(result, _sub(product1, product2)); |
+ } |
+ |
+ return result; |
+ }, |
+ |
+ /** |
+ * Returns the human-readable string representation of this |
+ * matrix, i.e. elements of arrays arranged in rows and columns. |
* Makes use of string.js#format() if available. |
- * |
- * @param m : optional Matrix |
- * @return string |
+ * |
+ * @param {String} prefix (optional) |
+ * Prefix for the second, third, aso. row. <code>" "</code> |
+ * is useful in consoles where the output is prefixed by |
+ * <kbd>"</kbd>. The default is the empty string. |
+ * @return {string} |
* @todo |
*/ |
- function(m) { |
- if (!m) |
+ toString: function (prefix) { |
+ var m; |
+ if ((m = this.get())) |
{ |
- m = this; |
- } |
- |
- if ((m = m.data)) |
- { |
var |
as = [], |
- bHasFormat = (typeof format != "undefined"), |
- maxLen; |
- |
- if (bHasFormat) |
+ /* FIXME */ |
+ _sprintf = jsx.object.getFeature(jsx, "string", "sprintf"), |
+ max_len; |
+ |
+ if (_sprintf) |
{ |
- maxLen = Math.max(m); |
+ /* TODO: Calculate optimum width per column */ |
+ max_len = Math.max.apply(Math, m.map(function (row) { |
+ return Math.max.apply(Math, row.map(function (component) { |
+ return component.toString().length; |
+ })); |
+ })); |
} |
- |
+ |
for (var i = 0, len = m.length; i < len; i++) |
{ |
var row = m[i]; |
- if (bHasFormat) |
+ if (_sprintf) |
{ |
- as[i] = format("%*$s", row, maxLen + 1); |
+ as[i] = _sprintf("%*a", row, max_len + 1); |
} |
else |
{ |
@@ -286,241 +971,133 @@ |
as[i] = row.join(" "); |
} |
} |
- }n |
- |
- return as.join("\n"); |
+ } |
+ |
+ return as.join("\n" + (prefix || "")); |
}, |
- minor: |
/** |
- * Returns the minor of the matrix, i.e. the matrix produced |
- * by removing the original's first row and i-th column. |
- * |
- * @param i : number |
- * @param m : optional Matrix |
- * @return string |
- * @todo |
+ * Returns the minor of this matrix. |
+ * |
+ * @param {Number} column |
+ * @return {jsx.math.Matrix} |
+ * The matrix produced by removing this matrix's first row |
+ * and <code><var>column</var></code>-th column (counting |
+ * from 0). |
*/ |
- function (i, m) { |
- if (!m) |
+ minor: function (column) { |
+ var data = this.get().slice(1); |
+ |
+ for (var j = data.length; j--;) |
{ |
- m = this; |
+ data[j].splice(column, 1); |
} |
- |
- if ((m = m.data)) |
- { |
- m = new Math.Matrix(m); |
- m.data = m.data.slice(1); |
- var j; |
- for (m, j = (m = m.data).length; j--;) |
- { |
- m[j].splice(i, 1); |
- } |
- } |
- |
- return m; |
+ |
+ return new _Matrix(data); |
} |
-}; |
+ }); |
-/** |
- * @param a |
- * @param b |
- * @return Array |
- * The sum of the matrixes <var>a</var> and <var>b</var> |
- */ |
-jsx.math.add = function(a, b) { |
- /* |
- * x00 x01 x02 y00 y01 y02 x00+y00 x01+y01 x02+y02 |
- * x10 x11 x12 + y10 y11 y12 = x10+y10 x11+y11 x12+y12 |
- * x20 x21 x22 y20 y21 y22 x20+y20 x21+y21 x22+y22 |
+ jsx.object.extend(jsx.math, { |
+ Tensor: _Tensor, |
+ Vector: _Vector, |
+ Matrix: _Matrix |
+ }); |
+ |
+ /** |
+ * Computes the intersection of two orthotopes (intervals, rectangles, |
+ * rectangular cuboids, etc.) |
+ * <pre> |
+ * ,------------------. |
+ * | ,----+------. |
+ * | |/ / | | |
+ * | | / /| | |
+ * | `----+------' |
+ * `------------------' |
+ * </pre> |
+ * <p> |
+ * Each orthotope is given as a list of Arrays of numbers (which may |
+ * be encapsulated in a Vector), where each Array or Vector consists |
+ * of the coordinates of the adjacent vertices of the orthotope |
+ * (for a rectangle, the x and y coordinates of the left top and |
+ * right bottom corners). FIXME: Not reliable in 3D+! |
+ * </p><p> |
+ * If you need to compute the intersection of more than two |
+ * orthotopes, you can compute the intersection of their |
+ * intersections. |
+ * </p> |
+ * @param orthotope1 : Array[Array[double]|Math.Vector] |
+ * First orthotope |
+ * @param orthotope2 : Array[Array[double]|Math.Vector] |
+ * Second orthotope |
+ * @return Array[Array|Vector] |
+ * An Array of two Arrays or Vectors of, each of which holds one |
+ * vector pointing to an adjacent vertex of the resulting orthotope |
+ * that includes all points that the input orthotopes have in common. |
*/ |
- var result = new Array(); |
+ _math.intersect = function (orthotope1, orthotope2) { |
+ var result = [[], []]; |
- var dimARow, |
- dimACol = jsx.math.dimCol(a), |
- dimBRow, |
- dimBCol = Math.dimCol(b); |
- if ((dimARow = jsx.math.dimRow(a)) == (dimBRow = Math.dimRow(b)) |
- && (dimACol == dimBCol)) |
- { |
- for (var i = 0; i < dimARow; i++) |
+ var ortho1LeftTop = orthotope1[0]; |
+ for (var i = ortho1LeftTop.length; i--;) |
{ |
- result[i] = []; |
- for (var j = 0; j < dimACol; j++) |
- { |
- result[i][j] = a[i][j] + b[i][j]; |
- } |
+ result[0][i] = (ortho1LeftTop[i] < orthotope2[0][i]) |
+ ? orthotope2[0][i] |
+ : ortho1LeftTop[i]; |
} |
- } |
- else |
- { |
- throwException(new Math.InvalidOperandError( |
- "First matrix's dimension (" + dimARow + "," + dimACol |
- + ") != second matrix's dimension (" + dimBRow + ", " + dimBCol + ")")); |
- return null; |
- } |
- |
- return result; |
-}; |
-/** |
- * This routine uses the dimensions of <var>a</var> and <var>b</var> |
- * to choose the corresponding multiplication routine. The argument |
- * dimensions, the dimension of the corresponding result, and the |
- * multiplication routine that is called are shown in the following |
- * table. |
- <pre> |
- B |
+ var ortho1RightBottom = orthotope1[1]; |
+ for (i = ortho1RightBottom.length; i--;) |
+ { |
+ result[1][i] = (ortho1RightBottom[i] > orthotope2[1][i]) |
+ ? orthotope2[1][i] |
+ : ortho1RightBottom[i]; |
+ } |
- A qXn 1Xn qX1 |
- Matrix row Vector col Vector scalar |
+ return result; |
+ }; |
- mXq mXn Matrix ERROR mX1 col Vector mXq Matrix |
- Matrix MatrixMatrixMultiply MatrixVectorMultiply MatrixScalarMultiply |
+ /** |
+ * @constructor |
+ * @extends jsx.InvalidArgumentError |
+ */ |
+ _math.DimensionError = function () { |
+ arguments.callee._super.call(this, "Dimension must be 1 or greater"); |
+ }; |
- 1Xq 1Xn row Vector ERROR scalar 1Xq Vector |
- row Vector VectorMatrixMultiply VectorScalarMultiply |
+ _math.DimensionError.extend(jsx.InvalidArgumentError, { |
+ /** |
+ * @memberOf jsx.math.DimensionError.prototype |
+ */ |
+ name: "jsx.math.DimensionError" |
+ }); |
- mX1 ERROR mXn Matrix (outer product) ERROR mX1 Vector |
- col Vector OuterProductMatrix VectorScalarMultiply |
+ /** |
+ * @constructor |
+ * @extends jsx.InvalidArgumentError |
+ */ |
+ _math.CoordinateError = function () { |
+ arguments.callee._super.call(this, "Coordinate must be 0 or greater"); |
+ }; |
- qXn Matrix 1Xn row Vector qX1 col Vector scalar |
- scalar MatrixScalarMultiply VectorScalarMultiply VectorScalarMultiply standard multipliction |
- </pre> |
+ _math.CoordinateError.extend(jsx.InvalidArgumentError, { |
+ /** |
+ * @memberOf jsx.math.CoordinateError.prototype |
+ */ |
+ name: "jsx.math.CoordinateError" |
+ }); |
- * @param a |
- * @param b |
- * @return number|Array |
- * @throws Math#InvalidOperandError |
- */ |
-jsx.math.multiply = function(a, b) { |
- /* |
- * a00 a01 ... b00 b01 ... |
- * a10 a11 ... * b10 b11 ... |
- * ... ... ... ... ... ... |
- * |
- * a00*b00+a01*b10+...*... a00*b01+a01*b11+...*... a00*...+a01*...+...*... |
- * = a10*b10+a11*b10+...*... a10*b01+a11*b11+...*... a00*...+a01*...+...*... |
- * ...*...+...*...+...+... ...*b01+...*b11+...*... a00*...+a01*...+...*... |
+ /** |
+ * @constructor |
+ * @extends jsx.Error |
*/ |
- |
- var dimRowA = Math.dimRow(a); |
- var dimColA = Math.dimCol(a); |
- var dimRowB = Math.dimRow(b); |
- var dimColB = Math.dimCol(b); |
- if ((dimRowA && dimColA) || (dimRowB && dimColB)) |
- { |
- if (dimRowA || dimRowB) |
- { |
-// if (dimRowX && d |
- var result = matrixMatrixMultiply(a, b); |
- } |
- else if (Array.isArray(a) && !Array.isArray(b)) |
- { |
- if (Array.isArray(a[0])) |
- { |
- // ... |
- } |
- result = matrix; |
- } |
- |
- result = new Array(); |
- // matrixMultiply |
- } |
- else |
- { |
- result = a * b; |
- } |
- |
- var x_len = a.length; |
- var y_len = b.length; |
- for (var i = 0, j, xi_len, sum = 0, k; |
- i < x_len; |
- i++) |
- { |
- result[i] = new Array(); |
- xi_len = a[i].length; |
- for (j = 0; |
- j < xi_len; |
- j++, sum = 0) |
- { |
- if (y_len != xi_len) |
- { |
- jsx.throwThis(new jsx.math.InvalidOperandError( |
- "First matrix's column dimension (" + xi_len |
- + ") != second matrix's row dimension (" + y_len + ")")); |
- return null; |
- } |
- sum += a[i][k] + b[k][i]; |
- } |
- result[i][j] = sum; |
- } |
- |
- if (result.length == 1 && result[i].length == 1) |
- { |
- result = result[0][0]; |
- } |
- |
- return result; |
-}; |
+ _math.NotImplementedError = function () { |
+ arguments.callee._super.call(this, "Not implemented"); |
+ }; |
-/** |
- * Computes the intersection of two orthotopes (intervals, rectangles, |
- * rectangular cuboids, etc.) |
- * <pre> |
- * ,------------------. |
- * | ,----+------. |
- * | |/ / | | |
- * | | / /| | |
- * | `----+------' |
- * `------------------' |
- * </pre> |
- * <p> |
- * Each orthotope is given as a list of Arrays of numbers (which may |
- * be encapsulated in a Vector), where each Array or Vector consists |
- * of the coordinates of the adjacent vertices of the orthotope |
- * (for a rectangle, the x and y coordinates of the left top and |
- * right bottom corners). FIXME: Not reliable in 3D+! |
- * </p><p> |
- * If you need to compute the intersection of more than two |
- * orthotopes, you can compute the intersection of their |
- * intersections. |
- * </p> |
- * @param orthotope1 : Array[Array[double]|Math.Vector] |
- * First orthotope |
- * @param orthotope2 : Array[Array[double]|Math.Vector] |
- * Second orthotope |
- * @return Array[Array|Vector] |
- * An Array of two Arrays or Vectors of, each of which holds one |
- * vector pointing to an adjacent vertex of the resulting orthotope |
- * that includes all points that the input orthotopes have in common. |
- */ |
-jsx.math.intersect = function (orthotope1, orthotope2) { |
- var result = [[], []]; |
- |
- var ortho1LeftTop = orthotope1[0]; |
- for (var i = ortho1LeftTop.length; i--;) |
- { |
- result[0][i] = (ortho1LeftTop[i] < orthotope2[0][i]) |
- ? orthotope2[0][i] |
- : ortho1LeftTop[i]; |
- } |
- |
- var ortho1RightBottom = orthotope1[1]; |
- for (i = ortho1RightBottom.length; i--;) |
- { |
- result[1][i] = (ortho1RightBottom[i] > orthotope2[1][i]) |
- ? orthotope2[1][i] |
- : ortho1RightBottom[i]; |
- } |
- |
- return result; |
-}; |
- |
-jsx.math.CoordinateError = function () { |
- arguments.callee._super.call(this, "Coordinate must be 1 or greater") |
-}; |
- |
-jsx.math.CoordinateError.extend(jsx.InvalidArgumentError, { |
- name: "jsx.math.CoordinateError" |
-}); |
\ No newline at end of file |
+ _math.NotImplementedError.extend(jsx.Error, { |
+ /** |
+ * @memberOf jsx.math.NotImplementedError.prototype |
+ */ |
+ name: "jsx.math.NotImplementedError" |
+ }); |
+}()); |
\ No newline at end of file |
/trunk/math/integer.js |
---|
4,12 → 4,12 |
* @requires types.js |
* |
* @section Copyright & Disclaimer |
* |
* |
* @author |
* (C) 2000-2011 Thomas Lahn <math.js@PointedEars.de> |
* |
* @partof PointedEars' JavaScript Extensions (JSX) |
* |
* |
* JSX is free software: you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* the Free Software Foundation, either version 3 of the License, or |
24,43 → 24,66 |
* along with JSX. If not, see <http://www.gnu.org/licenses/>. |
*/ |
/** |
* Returns the greatest common divisor (GCD) of two integers |
* <code>a</code> and <code>b</code>, implementing (the optimized form of) the |
* Euclidean algorithm (also called Euclid's algorithm). The |
* GCD of two integer arguments is the largest positive integer |
* that both arguments are integer multiples of, i.e. by which |
* either argument can be divided without remainder. Since |
* integers are required, the floor of <code>a</code> and <code>b</code> will |
* be used for computation. |
* |
* @param a : number |
* @param b : number |
* @return type number |
* The GCD of <code>a</code> and <code>b</code>; |
* <code>NaN</code> if an argument is not a number. |
* @see Math#floor(number) |
*/ |
Math.gcd = function(a, b) { |
if (isNaN(a) || isNaN(b)) |
{ |
return Number.NaN; |
} |
if (typeof jsx == "undefined") |
{ |
var jsx = {}; |
} |
a = Math.floor(a); |
b = Math.floor(b); |
var c; |
if (typeof jsx.math == "undefined") |
{ |
jsx.math = {}; |
} |
while (b != 0) |
{ |
c = a % b; |
a = b; |
b = c; |
jsx.math.integer = { |
/** |
* Returns the greatest common divisor (GCD) of two integers |
* <code>a</code> and <code>b</code>, implementing (the optimized form of) the |
* Euclidean algorithm (also called Euclid's algorithm). The |
* GCD of two integer arguments is the largest positive integer |
* that both arguments are integer multiples of, i.e. by which |
* either argument can be divided without remainder. Since |
* integers are required, the floor of <code>a</code> and <code>b</code> will |
* be used for computation. |
* |
* @memberOf jsx.math.integer |
* @param a : number |
* @param b : number |
* @return type number |
* The GCD of <code>a</code> and <code>b</code>; |
* <code>NaN</code> if an argument is not a number. |
* @see Math#floor(number) |
*/ |
gcd: function (a, b) { |
if (isNaN(a) || isNaN(b)) |
{ |
return Number.NaN; |
} |
a = Math.floor(a); |
b = Math.floor(b); |
var c; |
while (b != 0) |
{ |
c = a % b; |
a = b; |
b = c; |
} |
return Math.abs(a); |
} |
return Math.abs(a); |
}; |
if (jsx.options.augmentBuiltins) |
{ |
jsx.object.extend(Math, { |
/** |
* @memberOf Math |
*/ |
gcd: jsx.math.integer.gcd |
}); |
} |
/** |
* Returns the greatest common divisor (GCD) of two or more |
* integers, implementing (the optimized form of) the Euclidean |
104,7 → 127,7 |
{ |
return Number.NaN; |
} |
if (!a || !b) |
{ |
return 0; |
112,7 → 135,7 |
a = Math.floor(a); |
b = Math.floor(b); |
return ((a * b) / Math.gcd(a, b)); |
}; |
167,7 → 190,7 |
* Unlike common recursive implementations, the algorithm is |
* implemented iterative here, saving stack space and thus |
* allowing larger factorials to be computed. |
* |
* |
* @throws Math#OverflowError |
*/ |
Math.fac = function(n) { |
188,7 → 211,7 |
break; |
} |
} |
return result; |
}; |
225,7 → 248,7 |
* roots of certain negative values, like root(-9, |
* 1/3) cannot be computed (results in NaN there) |
* but should be. |
* |
* |
* @author |
* (c) 2000-2004 Thomas Lahn <math.js@PointedEars.de> |
* @param nBase : number |
267,7 → 290,7 |
"power(" + nBase + ", " + nExponent + ")")); |
} |
} |
return result; |
}; |
319,3 → 342,54 |
Math.rec = function(x) { |
return (1 / x); |
}; |
/** |
* Finds prime numbers within a range |
* |
* @param {int} upperBounds |
* The upper bounds of the half-open interval [2, ceil(upperBounds)), |
* that is, the smallest integer that should not be considered |
* in the search. |
* @return {Object} |
* An object whose property names are the primes within range. |
* Use {@link Object#keys()} to get an {@link Array} of |
* <code><em>string</em></code> values; use |
* {@link Array.prototype#map} to get an <code>Array</code> |
* of {@link Number} values. |
* NOTE: As of ECMAScript Edition 5.1, an <code>Array</code> |
* is limited to 2^32-1 elements. Use {@link jsx.array.BigArray} |
* to process this <code>Object</code> efficiently. |
*/ |
Math.primes = function (upperBounds) { |
var not_prime = Object.create(null); |
var prime = Object.create(null); |
var i = 2; |
var upperLimit = Math.sqrt(upperLimit); |
while (i < upperLimit) |
{ |
prime[i] = true; |
for (var mult = i * i; mult < upperBounds; mult += i) |
{ |
not_prime[mult] = true; |
} |
while (not_prime[++i]) |
{ |
; |
} |
} |
for (; i < upperBounds; ++i) |
{ |
if (!not_prime[i]) |
{ |
prime[i] = true; |
} |
delete not_prime[i]; |
} |
return prime; |
}; |
/trunk/math.js |
---|
6,7 → 6,7 |
* @section Copyright & Disclaimer |
* |
* @author |
* (C) 2000-2011, 2013 Thomas Lahn <math.js@PointedEars.de> |
* (C) 2000-2011, 2013, 2014 Thomas Lahn <math.js@PointedEars.de> |
* |
* @partof PointedEars' JavaScript Extensions (JSX) |
* |
31,110 → 31,223 |
* Refer math.htm file for general documentation. |
*/ |
if (typeof jsx == "undefined") |
{ |
/** |
* @namespace |
*/ |
var jsx = {}; |
} |
/** |
* @namespace |
*/ |
jsx.math = { |
version: "1.16.$Rev$", |
copyright: "Copyright \xA9 1999-2011, 2013", |
author: "Thomas Lahn", |
email: "math.js@PointedEars.de", |
path: "http://pointedears.de/scripts/" |
}; |
// jsx.math.docURL = jsx.math.path + "math.htm"; |
jsx.math = (/** @constructor */ function () { |
/* Imports */ |
var _isNativeMethod = jsx.object.isNativeMethod; |
/* User interface language */ |
jsx.math.language = "en"; |
/* Private variables */ |
/** @section Exceptions */ |
/** |
* @function |
*/ |
var _MathError = ( |
/** |
* @constructor |
* @param {string} sError |
* @param {string} sMessage |
*/ |
function (sError, sMessage) { |
this.constructor._super.call(this, |
"math.js Error: " |
+ (typeof sError[jsx.math.language] != "undefined" |
? sError[jsx.math.language] |
: sError["en"]) |
+ " " + (sMessage || "")); |
} |
).extend(jsx.Error); |
jsx.math.msgInvArg = { |
'en': 'Invalid function argument', |
'de': 'Ungültiges Funktionsargument' |
}; |
var _msgInvArg = { |
'en': 'Invalid function argument', |
'de': 'Ungültiges Funktionsargument' |
}; |
jsx.math.msgArgMissing = { |
'en': 'Required argument missing', |
'de': 'Erforderliches Argument fehlt' |
}; |
var _msgArgMissing = { |
'en': 'Required argument missing', |
'de': 'Erforderliches Argument fehlt' |
}; |
jsx.math.msgInvOp = { |
'en': 'Invalid operand', |
'de': 'Ungültiger Operand' |
}; |
var _msgInvOp = { |
'en': 'Invalid operand', |
'de': 'Ungültiger Operand' |
}; |
jsx.math.msgOverflow = { |
'en': 'Overflow', |
'de': 'Überlauf' |
}; |
var _msgOverflow = { |
'en': 'Overflow', |
'de': 'Überlauf' |
}; |
jsx.math.msgUnderflow = { |
'en': 'Underflow', |
'de': 'Unterlauf' |
}; |
var _msgUnderflow = { |
'en': 'Underflow', |
'de': 'Unterlauf' |
}; |
jsx.math.msgDivByZero = { |
'en': 'Division by zero', |
'de': 'Division durch Null' |
}; |
var _msgDivByZero = { |
'en': 'Division by zero', |
'de': 'Division durch Null' |
}; |
/** |
* @param sError |
* @param sMessage |
*/ |
jsx.math.MathError = function(sError, sMessage) { |
this.constructor._super.call(this, |
"math.js Error: " |
+ (typeof sError[jsx.math.language] != "undefined" |
? sError[jsx.math.language] |
: sError["en"]) |
+ " " + (sMessage || "")); |
}; |
jsx.math.MathError.extend(jsx.Error); |
return { |
/** |
* @memberOf jsx.math |
*/ |
version: "1.16.$Rev$", |
copyright: "Copyright \xA9 1999-2011, 2013", |
author: "Thomas Lahn", |
email: "math.js@PointedEars.de", |
path: "http://pointedears.de/scripts/", |
/** |
* @param sMethodCall |
* @param iErrorCode |
* @todo |
*/ |
jsx.math.InvalidArgumentError = function(sMethodCall, iErrorCode) { |
var sSubErrType = jsx.math.msgInvArg; |
/** User interface language */ |
language: "en", |
switch (iErrorCode) |
{ |
case -1: |
sSubErrType = jsx.math.msgArgMissing; |
break; |
} |
/** @section Exceptions */ |
jsx.math.MathError.call(this, sSubErrType); |
}; |
msgInvArg: _msgInvArg, |
msgArgMissing: _msgArgMissing, |
msgInvOp: _msgInvOp, |
msgOverflow: _msgOverflow, |
msgUnderflow: _msgUnderflow, |
msgDivByZero: _msgDivByZero, |
jsx.math.InvalidOperandError = function() { |
jsx.math.MathError.call(this, jsx.math.msgInvOp); |
}; |
MathError: _MathError, |
jsx.math.OverflowError = function() { |
jsx.math.MathError.call(jsx.math.msgOverflow); |
}; |
/** |
* @param {string} sMethodCall |
* @param {int} iErrorCode |
* @todo |
*/ |
InvalidArgumentError: ( |
function (sMethodCall, iErrorCode) { |
var sSubErrType = _msgInvArg; |
jsx.math.UnderflowError = function() { |
jsx.math.MathError.call(jsx.math.msgUnderflow); |
}; |
switch (iErrorCode) |
{ |
case -1: |
sSubErrType = _msgArgMissing; |
break; |
} |
jsx.math.DivisionByZeroError = function() { |
jsx.math.MathError.call(jsx.math.msgDivByZero); |
}; |
_MathError.call(this, sSubErrType); |
} |
).extend(_MathError), |
InvalidOperandError: function() { |
_MathError.call(this, _msgInvOp); |
}, |
OverflowError: (function() { |
_MathError.call(_msgOverflow); |
}).extend(_MathError), |
UnderflowError: (function() { |
_MathError.call(_msgUnderflow); |
}).extend(_MathError), |
DivisionByZeroError: (function() { |
_MathError.call(_msgDivByZero); |
}).extend(_MathError), |
/** |
* General addition |
* |
* @param op1 |
* @param op2 |
* @return {any} |
*/ |
add: function (op1, op2) { |
if (_isNativeMethod(op1, "add")) |
{ |
return op1.add(op2); |
} |
return op1 + op2; |
}, |
/** |
* General subtraction |
* |
* @param op1 |
* @param op2 |
* @return {any} |
*/ |
sub: function (op1, op2) { |
if (_isNativeMethod(op1, "sub")) |
{ |
return op1.sub(op2); |
} |
return op1 - op2; |
}, |
/** |
* General multiplication |
* |
* @param op1 |
* @param op2 |
* @return {any} |
*/ |
mul: function (op1, op2) { |
if (_isNativeMethod(op1, "mul")) |
{ |
return op1.mul(op2); |
} |
return op1 * op2; |
}, |
/** |
* General division |
* |
* @param op1 |
* @param op2 |
* @return {any} |
*/ |
div: function (op1, op2) { |
if (_isNativeMethod(op1, "div")) |
{ |
return op1.div(op2); |
} |
return op1 / op2; |
}, |
/** |
* General exponentiation |
* |
* @param op1 |
* @param op2 |
* @return {any} |
*/ |
pow: function (op1, op2) { |
if (_isNativeMethod(op1, "pow")) |
{ |
return op1.pow(op2); |
} |
return Math.pow(op1, op2); |
}, |
/** |
* General square root |
* |
* @param op |
* @return {any} |
*/ |
sqrt: function (op) { |
if (_isNativeMethod(op, "sqrt")) |
{ |
return op.sqrt(); |
} |
return Math.sqrt(op); |
} |
}; |
}()); |
// jsx.math.docURL = jsx.math.path + "math.htm"; |
/* |
* TODO: |
/trunk/test/math-test.js |
---|
0,0 → 1,320 |
function runTests () |
{ |
var assert = jsx.test.assert; |
var assertObjectEquals = jsx.test.assertObjectEquals; |
var assertArrayEquals = jsx.test.assertArrayEquals; |
jsx.test.runner.run({ |
tests: [ |
{ |
file: "math/float.js", |
feature: "jsx.math.getValue(falseValue)", |
description: "return <code>NaN</code>", |
code: function () { |
assert(isNaN(jsx.math.getValue())); |
assert(isNaN(jsx.math.getValue(false))); |
assert(isNaN(jsx.math.getValue(0))); |
assert(isNaN(jsx.math.getValue(null))); |
assert(isNaN(jsx.math.getValue(''))); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.getValue(obj)", |
description: "<code>obj.valueOf()</code> does not exist, return <code>NaN</code>", |
code: function () { |
assert(isNaN(jsx.math.getValue(jsx.object.getDataObject()))); |
assert(isNaN(jsx.math.getValue({valueOf: 42}))); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.getValue(obj)", |
description: "<code>obj.valueOf()</code> does not return number, return <code>NaN</code>", |
code: function () { |
assert(isNaN(jsx.math.getValue({valueOf: function () { return false; }}))); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.getValue(obj)", |
description: "<code>obj.valueOf()</code> returns number, return the return value", |
code: function () { |
assert(jsx.math.getValue({valueOf: function () { return 42; }}) === 42); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min()", |
description: "No arguments, return <code>Number.POSITIVE_INFINITY</code>", |
code: function () { |
assert(jsx.math.min() === Number.POSITIVE_INFINITY); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min(number)", |
description: "return <code>number</code>", |
code: function () { |
assert(jsx.math.min(42) === 42); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min(-1, 1, 0)", |
description: "return <code>-1</code>", |
code: function () { |
assert(jsx.math.min(-1, 1, 0) === -1); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min(0, -1, 1)", |
description: "return <code>-1</code>", |
code: function () { |
assert(jsx.math.min(0, -1, 1) === -1); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min(1, 0, -1)", |
description: "return <code>-1</code>", |
code: function () { |
assert(jsx.math.min(1, 0, -1) === -1); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min([-1, 1, 0])", |
description: "return <code>-1</code>", |
code: function () { |
assert(jsx.math.min([-1, 1, 0]) === -1); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min(-2, [-1, 1, 0])", |
description: "return <code>-2</code>", |
code: function () { |
assert(jsx.math.min(-2, [-1, 1, 0]) === -2); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min([-1, 1, 0], -2)", |
description: "return <code>-2</code>", |
code: function () { |
assert(jsx.math.min([-1, 1, 0], -2) === -2); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min([-1, 1, -2], 0)", |
description: "return <code>-2</code>", |
code: function () { |
assert(jsx.math.min([-1, 1, -2], 0) === -2); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min(1, {valueOf: function () { return -1; }, x: -2})", |
description: "return <code>-1</code>", |
code: function () { |
assert(jsx.math.min(1, {valueOf: function () { return -1; }, x: -2}) === -1); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min(0, {x: -1, y: -2, z: 1})", |
description: "return <code>-2</code>", |
code: function () { |
assert(jsx.math.min(0, {x: -1, y: -2, z: 1}) === -2); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.min(0, {x: {y: -2}, z: -1})", |
description: "return <code>-2</code>", |
code: function () { |
assert(jsx.math.min(0, {x: {y: -2}, z: -1}) === -2); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max()", |
description: "No arguments, return <code>Number.NEGATIVE_INFINITY</code>", |
code: function () { |
assert(jsx.math.max() === Number.NEGATIVE_INFINITY); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max(number)", |
description: "return <code>number</code>", |
code: function () { |
assert(jsx.math.max(42) === 42); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max(-1, 1, 0)", |
description: "return <code>1</code>", |
code: function () { |
assert(jsx.math.max(-1, 1, 0) === 1); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max(0, -1, 1)", |
description: "return <code>1</code>", |
code: function () { |
assert(jsx.math.max(0, -1, 1) === 1); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max(1, 0, -1)", |
description: "return <code>1</code>", |
code: function () { |
assert(jsx.math.max(1, 0, -1) === 1); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max([1, -1, 0])", |
description: "return <code>1</code>", |
code: function () { |
assert(jsx.math.max([1, -1, 0]) === 1); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max(2, [-1, 1, 0])", |
description: "return <code>2</code>", |
code: function () { |
assert(jsx.math.max(2, [-1, 1, 0]) === 2); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max([1, -1, 0], 2)", |
description: "return <code>2</code>", |
code: function () { |
assert(jsx.math.max([1, -1, 0], 2) === 2); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max([1, -1, 2], 0)", |
description: "return <code>2</code>", |
code: function () { |
assert(jsx.math.max([1, -1, 2], 0) === 2); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max(-1, {valueOf: function () { return 1; }, x: 0})", |
description: "return <code>1</code>", |
code: function () { |
assert(jsx.math.max(-1, {valueOf: function () { return 1; }, x: 0}) === 1); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max(0, {x: 1, y: 2, z: -1})", |
description: "return <code>2</code>", |
code: function () { |
assert(jsx.math.max(0, {x: 1, y: 2, z: -1}) === 2); |
} |
}, |
{ |
file: "math/float.js", |
feature: "jsx.math.max(-1, {x: {y: 1}, z: 0})", |
description: "return <code>1</code>", |
code: function () { |
assert(jsx.math.max(-1, {x: {y: 1}, z: 0}) === 1); |
} |
}, |
{ |
file: "math/integer.js", |
feature: "Math.primes(10)", |
description: "return <code>{2: true, 3: true, 5: true, 7: true}</code>", |
code: function () { |
assertObjectEquals( |
{2: true, 3: true, 5: true, 7: true}, |
Math.primes(10)); |
} |
}, |
{ |
file: "math/algebra.js", |
feature: "new jsx.math.Matrix()", |
description: "return <code>[undefined] : Matrix</code>", |
code: function () { |
assertArrayEquals([void 0], new jsx.math.Matrix().get()); |
} |
}, |
{ |
file: "math/algebra.js", |
feature: "new jsx.math.Matrix(0)", |
description: "throw <code>jsx.math.DimensionError</code>", |
code: function () { |
var fail = false; |
jsx.tryThis( |
function () { |
new jsx.math.Matrix(0); |
}, |
function (e) { |
if (e.constructor == jsx.math.DimensionError) |
{ |
fail = true; |
jsx.dmsg(e); |
} |
else |
{ |
jsx.rethrowThis(e); |
} |
} |
); |
assert(fail); |
} |
}, |
{ |
file: "math/algebra.js", |
feature: "new jsx.math.Matrix(1)", |
description: "return <code>[undefined] : Matrix</code>", |
code: function () { |
assertArrayEquals([void 0], new jsx.math.Matrix(1).get()); |
} |
}, |
{ |
file: "math/algebra.js", |
feature: "new jsx.math.Matrix(1, 0)", |
description: "throw <code>jsx.math.DimensionError</code>", |
code: function () { |
var fail = jsx.tryThis( |
function () { |
return !(new jsx.math.Matrix(1, 0)); |
}, |
function (e) { |
jsx.dmsg(e); |
return (e instanceof jsx.math.DimensionError); |
} |
); |
assert(fail); |
} |
} |
] |
}); |
} |
// var d = new Date(), n = 0.09; |
// alert([ |
// n + " + 0.09 = " + (new Float(n)).add(0.09), |
// n + " + 0.09 = " + (n + 0.09), |
// (new Date() - d) + " ms" |
// ].join("\n")); |
Property changes: |
Added: svn:mime-type |
## -0,0 +1 ## |
+text/plain |
\ No newline at end of property |