-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modified the handling of zero and infinity to map to the Riemann sphere. #17
Changes from 2 commits
12c2ad3
2a60eb0
9454b7c
4b2a847
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,8 +37,6 @@ | |
|
||
'use strict'; | ||
|
||
var P = {'re': 0, 'im': 0}; | ||
|
||
var cosh = function(x) { | ||
return (Math.exp(x) + Math.exp(-x)) * 0.5; | ||
}; | ||
|
@@ -130,6 +128,8 @@ | |
|
||
var parse = function(a, b) { | ||
|
||
var P = {'re': 0, 'im': 0}; | ||
|
||
if (a === undefined || a === null) { | ||
P['re'] = | ||
P['im'] = 0; | ||
|
@@ -229,6 +229,8 @@ | |
// If a calculation is NaN, we treat it as NaN and don't throw | ||
//parser_exit(); | ||
} | ||
|
||
return P; | ||
}; | ||
|
||
/** | ||
|
@@ -241,10 +243,10 @@ | |
return new Complex(a, b); | ||
} | ||
|
||
parse(a, b); // mutates P | ||
var z = parse(a, b); // mutates P | ||
|
||
this['re'] = P['re']; | ||
this['im'] = P['im']; | ||
this['re'] = z['re']; | ||
this['im'] = z['im']; | ||
} | ||
|
||
Complex.prototype = { | ||
|
@@ -273,11 +275,21 @@ | |
*/ | ||
'add': function(a, b) { | ||
|
||
parse(a, b); // mutates P | ||
var z = new Complex(a, b); | ||
|
||
// Infinity + Infinity = NaN | ||
if (this.isInfinite() && z.isInfinite()) { | ||
return Complex.NAN; | ||
} | ||
|
||
// Infinity + z = Infinity { where z != Infinity } | ||
if (this.isInfinite() || z.isInfinite()) { | ||
return Complex.INFINITY; | ||
} | ||
|
||
return new Complex( | ||
this['re'] + P['re'], | ||
this['im'] + P['im']); | ||
this['re'] + z['re'], | ||
this['im'] + z['im']); | ||
}, | ||
|
||
/** | ||
|
@@ -287,11 +299,21 @@ | |
*/ | ||
'sub': function(a, b) { | ||
|
||
parse(a, b); // mutates P | ||
var z = new Complex(a, b); | ||
|
||
// Infinity - Infinity = NaN | ||
if (this.isInfinite() && z.isInfinite()) { | ||
return Complex.NAN; | ||
} | ||
|
||
// Infinity - z = Infinity { where z != Infinity } | ||
if (this.isInfinite() || z.isInfinite()) { | ||
return Complex.INFINITY; | ||
} | ||
|
||
return new Complex( | ||
this['re'] - P['re'], | ||
this['im'] - P['im']); | ||
this['re'] - z['re'], | ||
this['im'] - z['im']); | ||
}, | ||
|
||
/** | ||
|
@@ -301,16 +323,26 @@ | |
*/ | ||
'mul': function(a, b) { | ||
|
||
parse(a, b); // mutates P | ||
var z = new Complex(a, b); | ||
|
||
// Infinity * 0 = NaN | ||
if ((this.isInfinite() && z.isZero()) || (this.isZero() && z.isInfinite())) { | ||
return Complex.NAN; | ||
} | ||
|
||
// Infinity * z = Infinity { where z != 0 } | ||
if (this.isInfinite() || z.isInfinite()) { | ||
return Complex.INFINITY; | ||
} | ||
|
||
// Besides the addition/subtraction, this helps having a solution for real Infinity | ||
if (P['im'] === 0 && this['im'] === 0) { | ||
return new Complex(this['re'] * P['re'], 0); | ||
// Short circuit for real values | ||
if (z['im'] === 0 && this['im'] === 0) { | ||
return new Complex(this['re'] * z['re'], 0); | ||
} | ||
|
||
return new Complex( | ||
this['re'] * P['re'] - this['im'] * P['im'], | ||
this['re'] * P['im'] + this['im'] * P['re']); | ||
this['re'] * z['re'] - this['im'] * z['im'], | ||
this['re'] * z['im'] + this['im'] * z['re']); | ||
}, | ||
|
||
/** | ||
|
@@ -320,25 +352,33 @@ | |
*/ | ||
'div': function(a, b) { | ||
|
||
parse(a, b); // mutates P | ||
var z = new Complex(a, b); | ||
|
||
// 0 / 0 = NaN and Infinity / Infinity = NaN | ||
if ((this.isZero() && z.isZero()) || (this.isInfinite() && z.isInfinite())) { | ||
return Complex.NAN; | ||
} | ||
|
||
// Infinity / 0 = Infinity | ||
if (this.isInfinite() || z.isZero()) { | ||
return Complex.INFINITY; | ||
} | ||
|
||
// 0 / Infinity = 0 | ||
if (this.isZero() || z.isInfinite()) { | ||
return Complex.ZERO; | ||
} | ||
|
||
a = this['re']; | ||
b = this['im']; | ||
|
||
var c = P['re']; | ||
var d = P['im']; | ||
var c = z['re']; | ||
var d = z['im']; | ||
var t, x; | ||
|
||
if (0 === d) { | ||
if (0 === c) { | ||
// Divisor is zero | ||
return new Complex( | ||
(a !== 0) ? (a / 0) : 0, | ||
(b !== 0) ? (b / 0) : 0); | ||
} else { | ||
// Divisor is real | ||
return new Complex(a / c, b / c); | ||
} | ||
// Divisor is real | ||
return new Complex(a / c, b / c); | ||
} | ||
|
||
if (Math.abs(c) < Math.abs(d)) { | ||
|
@@ -368,33 +408,33 @@ | |
*/ | ||
'pow': function(a, b) { | ||
|
||
parse(a, b); // mutates P | ||
var z = new Complex(a, b); | ||
|
||
a = this['re']; | ||
b = this['im']; | ||
|
||
if (a === 0 && b === 0) { | ||
return Complex['ZERO']; | ||
if (z.isZero()) { | ||
return Complex['ONE']; | ||
} | ||
|
||
// If the exponent is real | ||
if (P['im'] === 0) { | ||
if (z['im'] === 0) { | ||
|
||
if (b === 0 && a >= 0) { | ||
|
||
return new Complex(Math.pow(a, P['re']), 0); | ||
return new Complex(Math.pow(a, z['re']), 0); | ||
|
||
} else if (a === 0) { // If base is fully imaginary | ||
|
||
switch ((P['re'] % 4 + 4) % 4) { | ||
switch ((z['re'] % 4 + 4) % 4) { | ||
case 0: | ||
return new Complex(Math.pow(b, P['re']), 0); | ||
return new Complex(Math.pow(b, z['re']), 0); | ||
case 1: | ||
return new Complex(0, Math.pow(b, P['re'])); | ||
return new Complex(0, Math.pow(b, z['re'])); | ||
case 2: | ||
return new Complex(-Math.pow(b, P['re']), 0); | ||
return new Complex(-Math.pow(b, z['re']), 0); | ||
case 3: | ||
return new Complex(0, -Math.pow(b, P['re'])); | ||
return new Complex(0, -Math.pow(b, z['re'])); | ||
} | ||
} | ||
} | ||
|
@@ -421,8 +461,8 @@ | |
var arg = Math.atan2(b, a); | ||
var loh = logHypot(a, b); | ||
|
||
a = Math.exp(P['re'] * loh - P['im'] * arg); | ||
b = P['im'] * loh + P['re'] * arg; | ||
a = Math.exp(z['re'] * loh - z['im'] * arg); | ||
b = z['im'] * loh + z['re'] * arg; | ||
return new Complex( | ||
a * Math.cos(b), | ||
a * Math.sin(b)); | ||
|
@@ -1034,8 +1074,8 @@ | |
var a = this['re']; | ||
var b = this['im']; | ||
|
||
if (a === 0 && b === 0) { | ||
return new Complex(Infinity, 0); | ||
if (this.isZero()) { | ||
return Complex.INFINITY; | ||
} | ||
|
||
var d = a * a + b * b; | ||
|
@@ -1055,14 +1095,21 @@ | |
*/ | ||
'inverse': function() { | ||
|
||
// 1 / 0 = Infinity and 1 / Infinity = 0 | ||
if (this.isZero()) { | ||
return Complex.INFINITY; | ||
} | ||
|
||
if (this.isInfinite()) { | ||
return Complex.ZERO; | ||
} | ||
|
||
var a = this['re']; | ||
var b = this['im']; | ||
|
||
var d = a * a + b * b; | ||
|
||
return new Complex( | ||
a !== 0 ? a / d : 0, | ||
b !== 0 ?-b / d : 0); | ||
return new Complex(a / d, -b / d); | ||
}, | ||
|
||
/** | ||
|
@@ -1134,10 +1181,10 @@ | |
*/ | ||
'equals': function(a, b) { | ||
|
||
parse(a, b); // mutates P | ||
var z = new Complex(a, b); | ||
|
||
return Math.abs(P['re'] - this['re']) <= Complex['EPSILON'] && | ||
Math.abs(P['im'] - this['im']) <= Complex['EPSILON']; | ||
return Math.abs(z['re'] - this['re']) <= Complex['EPSILON'] && | ||
Math.abs(z['im'] - this['im']) <= Complex['EPSILON']; | ||
}, | ||
|
||
/** | ||
|
@@ -1161,10 +1208,18 @@ | |
var b = this['im']; | ||
var ret = ''; | ||
|
||
if (isNaN(a) || isNaN(b)) { | ||
if (this.isNaN()) { | ||
return 'NaN'; | ||
} | ||
|
||
if (this.isZero()) { | ||
return '0'; | ||
} | ||
|
||
if (this.isInfinite()) { | ||
return 'Infinity'; | ||
} | ||
|
||
if (a !== 0) { | ||
ret+= a; | ||
} | ||
|
@@ -1215,7 +1270,7 @@ | |
}, | ||
|
||
/** | ||
* Checks if the given complex number is not a number | ||
* Determines whether a complex number is not on the Riemann sphere. | ||
* | ||
* @returns {boolean} | ||
*/ | ||
|
@@ -1224,12 +1279,36 @@ | |
}, | ||
|
||
/** | ||
* Checks if the given complex number is finite | ||
* Determines whether or not a complex number is at the zero pole of the | ||
* Riemann sphere. | ||
* | ||
* @returns {boolean} | ||
*/ | ||
'isZero': function() { | ||
return ( | ||
(this['re'] === 0 || this['re'] === -0) && | ||
(this['im'] === 0 || this['im'] === -0) | ||
); | ||
}, | ||
|
||
/** | ||
* Determines whether a complex number is not at the infinity pole of the | ||
* Riemann sphere. | ||
* | ||
* @returns {boolean} | ||
*/ | ||
'isFinite': function() { | ||
return isFinite(this['re']) && isFinite(this['im']); | ||
}, | ||
|
||
/** | ||
* Determines whether or not a complex number is at the infinity pole of the | ||
* Riemann sphere. | ||
* | ||
* @returns {boolean} | ||
*/ | ||
'isInfinite': function() { | ||
return !(this.isNaN() || this.isFinite()); | ||
} | ||
}; | ||
|
||
|
@@ -1238,6 +1317,8 @@ | |
Complex['I'] = new Complex(0, 1); | ||
Complex['PI'] = new Complex(Math.PI, 0); | ||
Complex['E'] = new Complex(Math.E, 0); | ||
Complex['INFINITY'] = new Complex(Infinity, Infinity); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer calling this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose the same could be said for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess the question is how strongly do you feel about having the uppercase names. People are used to writing |
||
Complex['NAN'] = new Complex(NaN, NaN); | ||
Complex['EPSILON'] = 1e-16; | ||
|
||
if (typeof define === 'function' && define['amd']) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the
P
variable be changed to lowercase now that is is no longer a global? Perhaps change it toz
to reflect its purpose?