Skip to content
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

Merged
merged 4 commits into from
Jan 28, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 133 additions & 52 deletions complex.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -130,6 +128,8 @@

var parse = function(a, b) {

var P = {'re': 0, 'im': 0};

Copy link
Contributor Author

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 to z to reflect its purpose?

if (a === undefined || a === null) {
P['re'] =
P['im'] = 0;
Expand Down Expand Up @@ -229,6 +229,8 @@
// If a calculation is NaN, we treat it as NaN and don't throw
//parser_exit();
}

return P;
};

/**
Expand All @@ -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 = {
Expand Down Expand Up @@ -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']);
},

/**
Expand All @@ -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']);
},

/**
Expand All @@ -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']);
},

/**
Expand All @@ -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)) {
Expand Down Expand Up @@ -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']));
}
}
}
Expand All @@ -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));
Expand Down Expand Up @@ -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;
Expand All @@ -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);
},

/**
Expand Down Expand Up @@ -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'];
},

/**
Expand All @@ -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;
}
Expand Down Expand Up @@ -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}
*/
Expand All @@ -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());
}
};

Expand All @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer calling this ComplexInfinity or simliar to make it clear that this is different from the real infinity that people are used to in JavaScript

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose the same could be said for the NAN. But since they are all CAPS and prefixed by the type (Complex.INFINITY and Complex.NAN) I am not sure the clarification is necessary. Doesn't matter to me though...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if ComplexInfinity is more unambiguous, I like Complex.INFINITY and Complex.NAN better, since you would normally use it exactly as "Complex.INFINITY" and so it states already what ComplexInfinity wants to express.

Copy link
Contributor

Choose a reason for hiding this comment

The 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 NaN rather than NAN so the upper case version may be confusing.

Complex['NAN'] = new Complex(NaN, NaN);
Complex['EPSILON'] = 1e-16;

if (typeof define === 'function' && define['amd']) {
Expand Down
Loading