Repository: josdejong/mathjs
Pull request #852 (2017-05-10 13:47:49)

support to compare complex numbers #837
Allows comparison of Complex Numbers with number, BigNumber, and Complex Number

This behavior is reflected in the following functions
`compare`, `larger`, `largerEq`, `smaller`, `smallerEq`, `max`, `min` and `median`

Note: `median` gives a Complex Number as result if the first argument is a Complex Number


Patch:
diff --git a/docs/datatypes/complex_numbers.md b/docs/datatypes/complex_numbers.md
index 6b2172bbe..faafa0d89 100644
--- a/docs/datatypes/complex_numbers.md
+++ b/docs/datatypes/complex_numbers.md
@@ -120,4 +120,8 @@ Get the polar coordinates of the complex number, returns
 ### complex.toString()
 
 Returns a string representation of the complex number, formatted
-  as `a + bi` where `a` is the real part and `b` the imaginary part.
\ No newline at end of file
+  as `a + bi` where `a` is the real part and `b` the imaginary part.
+
+### complex.compare(a: complex, b: complex)
+
+Returns the comparision result of the complex number, similar to [compare](../reference/functions/compare.md).
\ No newline at end of file
diff --git a/examples/complex_numbers.js b/examples/complex_numbers.js
index f4c8e3f17..d6a9cb999 100644
--- a/examples/complex_numbers.js
+++ b/examples/complex_numbers.js
@@ -44,6 +44,10 @@ print(c);                      // 1 + i
 var d = math.complex(3, 4);
 console.log(d.abs(), d.arg());      // radius = 5, phi = 0.9272952180016122
 
+// comparision operations
+console.log('\ncomparision operations');
+console.log('compare - ', math.compare(a, b)); // returns 1 as 5 > 3
+console.log('larger - ', math.larger(a, b)); // returns true as 5 > 3
 
 /**
  * Helper function to output a value in the console. Value will be formatted.
diff --git a/lib/function/relational/compare.js b/lib/function/relational/compare.js
index 4dfee049c..c393ea8fa 100644
--- a/lib/function/relational/compare.js
+++ b/lib/function/relational/compare.js
@@ -68,9 +68,7 @@ function factory (type, config, load, typed) {
       return new type.Fraction(x.compare(y));
     },
 
-    'Complex, Complex': function () {
-      throw new TypeError('No ordering relation is defined for complex numbers');
-    },
+    'Complex, Complex': type.Complex.compare,
 
     'Unit, Unit': function (x, y) {
       if (!x.equalBase(y)) {
diff --git a/lib/function/relational/larger.js b/lib/function/relational/larger.js
index ee0a1c9a7..741e44c24 100644
--- a/lib/function/relational/larger.js
+++ b/lib/function/relational/larger.js
@@ -63,8 +63,8 @@ function factory (type, config, load, typed) {
       return x.compare(y) === 1;
     },
 
-    'Complex, Complex': function () {
-      throw new TypeError('No ordering relation is defined for complex numbers');
+    'Complex, Complex': function (x, y) {
+      return type.Complex.compare(x, y) > 0;
     },
 
     'Unit, Unit': function (x, y) {
diff --git a/lib/function/relational/largerEq.js b/lib/function/relational/largerEq.js
index 721bd43d3..526704e28 100644
--- a/lib/function/relational/largerEq.js
+++ b/lib/function/relational/largerEq.js
@@ -59,8 +59,8 @@ function factory (type, config, load, typed) {
       return x.compare(y) !== -1;
     },
 
-    'Complex, Complex': function () {
-      throw new TypeError('No ordering relation is defined for complex numbers');
+    'Complex, Complex': function (x, y) {
+      return type.Complex.compare(x, y) >= 0;
     },
 
     'Unit, Unit': function (x, y) {
diff --git a/lib/function/relational/smaller.js b/lib/function/relational/smaller.js
index e2d0bede0..351f2a19d 100644
--- a/lib/function/relational/smaller.js
+++ b/lib/function/relational/smaller.js
@@ -64,7 +64,7 @@ function factory (type, config, load, typed) {
     },
 
     'Complex, Complex': function (x, y) {
-      throw new TypeError('No ordering relation is defined for complex numbers');
+      return type.Complex.compare(x, y) < 0;
     },
 
     'Unit, Unit': function (x, y) {
diff --git a/lib/function/relational/smallerEq.js b/lib/function/relational/smallerEq.js
index 304271b32..e60c4c8b8 100644
--- a/lib/function/relational/smallerEq.js
+++ b/lib/function/relational/smallerEq.js
@@ -58,8 +58,8 @@ function factory (type, config, load, typed) {
       return x.compare(y) !== 1;
     },
 
-    'Complex, Complex': function () {
-      throw new TypeError('No ordering relation is defined for complex numbers');
+    'Complex, Complex': function (x, y) {
+      return type.Complex.compare(x, y) <= 0;
     },
 
     'Unit, Unit': function (x, y) {
diff --git a/lib/function/statistics/median.js b/lib/function/statistics/median.js
index 32fa7d2c0..e269d9f81 100644
--- a/lib/function/statistics/median.js
+++ b/lib/function/statistics/median.js
@@ -97,14 +97,14 @@ function factory (type, config, load, typed) {
 
   // helper function to type check the middle value of the array
   var middle = typed({
-    'number | BigNumber | Unit': function (value) {
+    'number | BigNumber | Complex | Unit': function (value) {
       return value;
     }
   });
 
   // helper function to type check the two middle value of the array
   var middle2 = typed({
-    'number | BigNumber | Unit, number | BigNumber | Unit': function (left, right) {
+    'number | BigNumber | Complex | Unit, number | BigNumber | Complex | Unit': function (left, right) {
       return divide(add(left, right), 2);
     }
   });
diff --git a/lib/type/complex/Complex.js b/lib/type/complex/Complex.js
index ba8cac2bb..e5fdfe70c 100644
--- a/lib/type/complex/Complex.js
+++ b/lib/type/complex/Complex.js
@@ -163,6 +163,31 @@ function factory (type, config, load, typed, math) {
     }
   });
 
+  /**
+   * Compare two complex numbers, `a` and `b`:
+   *
+   * - Returns 1 when the real part of `a` is larger than the real part of `b`
+   * - Returns -1 when the real part of `a` is smaller than the real part of `b`
+   * - Returns 1 when the real parts are equal
+   *   and the imaginary part of `a` is larger than the imaginary part of `b`
+   * - Returns -1 when the real parts are equal
+   *   and the imaginary part of `a` is smaller than the imaginary part of `b`
+   * - Returns 0 when both real and imaginary parts are equal.
+   *
+   * @params {Complex} a
+   * @params {Complex} b
+   * @returns {number} Returns the comparison result: -1, 0, or 1
+   */
+  Complex.compare = function (a, b) {
+    if (a.re > b.re) { return 1; }
+    if (a.re < b.re) { return -1; }
+
+    if (a.im > b.im) { return 1; }
+    if (a.im < b.im) { return -1; }
+
+    return 0;
+  }
+
   return Complex;
 }
 


Test patch:
diff --git a/test/function/relational/compare.test.js b/test/function/relational/compare.test.js
index f36045029..5fb202015 100644
--- a/test/function/relational/compare.test.js
+++ b/test/function/relational/compare.test.js
@@ -192,12 +192,31 @@ describe('compare', function() {
     assert.equal(mymath.compare(math.bignumber(1), math.bignumber(0.991)), 0);
   });
 
-  it('should throw an error when comparing complex numbers', function() {
-    assert.throws(function () {compare(complex(1,1), complex(1,2));}, TypeError);
-    assert.throws(function () {compare(complex(2,1), 3);}, TypeError);
-    assert.throws(function () {compare(3, complex(2,4));}, TypeError);
-    assert.throws(function () {compare(math.bignumber(3), complex(2,4));}, TypeError);
-    assert.throws(function () {compare(complex(2,4), math.bignumber(3));}, TypeError);
+  describe('Complex Numbers', function () {
+
+    it('should compare complex numbers', function() {
+      assert.equal(compare(complex(1,1), complex(1,1)), 0);
+      assert.equal(compare(complex(2,1), complex(1,2)), 1);
+      assert.equal(compare(complex(0,1), complex(1,2)), -1);
+    });
+
+    it('should compare complex number and number', function() {
+      assert.equal(compare(complex(1,0), 1), 0);
+      assert.equal(compare(complex(2,1), 1), 1);
+      assert.equal(compare(complex(0,1), 1), -1);
+      assert.equal(compare(1, complex(1,0)), 0);
+      assert.equal(compare(1, complex(2,1)), -1);
+      assert.equal(compare(1, complex(0,1)), 1);
+    });
+
+    it('should compare complex number and bignumber', function() {
+      assert.equal(compare(complex(1,0), math.bignumber(1)), 0);
+      assert.equal(compare(complex(2,1), math.bignumber(1)), 1);
+      assert.equal(compare(complex(0,1), math.bignumber(1)), -1);
+      assert.equal(compare(math.bignumber(1), complex(1,0)), 0);
+      assert.equal(compare(math.bignumber(1), complex(2,1)), -1);
+      assert.equal(compare(math.bignumber(1), complex(0,1)), 1);
+    });
   });
 
   it('should throw an error if matrices are different sizes', function() {
diff --git a/test/function/relational/larger.test.js b/test/function/relational/larger.test.js
index e6145c0f6..1015060a7 100644
--- a/test/function/relational/larger.test.js
+++ b/test/function/relational/larger.test.js
@@ -191,12 +191,31 @@ describe('larger', function() {
     });
   });
 
-  it('should throw an error when comparing complex numbers', function() {
-    assert.throws(function () {larger(complex(1,1), complex(1,2));}, TypeError);
-    assert.throws(function () {larger(complex(2,1), 3);}, TypeError);
-    assert.throws(function () {larger(3, complex(2,4));}, TypeError);
-    assert.throws(function () {larger(math.bignumber(3), complex(2,4));}, TypeError);
-    assert.throws(function () {larger(complex(2,4), math.bignumber(3));}, TypeError);
+  describe('Complex Numbers', function () {
+
+    it('should compare complex numbers', function() {
+      assert.equal(larger(complex(1,1), complex(1,1)), false);
+      assert.equal(larger(complex(2,1), complex(1,2)), true);
+      assert.equal(larger(complex(0,1), complex(1,2)), false);
+    });
+
+    it('should compare complex number and number', function() {
+      assert.equal(larger(complex(1,0), 1), false);
+      assert.equal(larger(complex(2,1), 1), true);
+      assert.equal(larger(complex(0,1), 1), false);
+      assert.equal(larger(1, complex(1,0)), false);
+      assert.equal(larger(1, complex(2,1)), false);
+      assert.equal(larger(1, complex(0,1)), true);
+    });
+
+    it('should compare complex number and bignumber', function() {
+      assert.equal(larger(complex(1,0), math.bignumber(1)), false);
+      assert.equal(larger(complex(2,1), math.bignumber(1)), true);
+      assert.equal(larger(complex(0,1), math.bignumber(1)), false);
+      assert.equal(larger(math.bignumber(1), complex(1,0)), false);
+      assert.equal(larger(math.bignumber(1), complex(2,1)), false);
+      assert.equal(larger(math.bignumber(1), complex(0,1)), true);
+    });
   });
 
   it('should throw an error if matrices are different sizes', function() {
diff --git a/test/function/relational/largerEq.test.js b/test/function/relational/largerEq.test.js
index d5a224bf9..9728746a0 100644
--- a/test/function/relational/largerEq.test.js
+++ b/test/function/relational/largerEq.test.js
@@ -192,12 +192,31 @@ describe('largerEq', function() {
     });
   });
 
-  it('should throw an error when comparing complex numbers', function() {
-    assert.throws(function () {largerEq(complex(1,1), complex(1,2));}, TypeError);
-    assert.throws(function () {largerEq(complex(2,1), 3);}, TypeError);
-    assert.throws(function () {largerEq(3, complex(2,4));}, TypeError);
-    assert.throws(function () {largerEq(math.bignumber(3), complex(2,4));}, TypeError);
-    assert.throws(function () {largerEq(complex(2,4), math.bignumber(3));}, TypeError);
+  describe('Complex Numbers', function () {
+
+    it('should compare complex numbers', function() {
+      assert.equal(largerEq(complex(1,1), complex(1,1)), true);
+      assert.equal(largerEq(complex(2,1), complex(1,2)), true);
+      assert.equal(largerEq(complex(0,1), complex(1,2)), false);
+    });
+
+    it('should compare complex number and number', function() {
+      assert.equal(largerEq(complex(1,0), 1), true);
+      assert.equal(largerEq(complex(2,1), 1), true);
+      assert.equal(largerEq(complex(0,1), 1), false);
+      assert.equal(largerEq(1, complex(1,0)), true);
+      assert.equal(largerEq(1, complex(2,1)), false);
+      assert.equal(largerEq(1, complex(0,1)), true);
+    });
+
+    it('should compare complex number and bignumber', function() {
+      assert.equal(largerEq(complex(1,0), math.bignumber(1)), true);
+      assert.equal(largerEq(complex(2,1), math.bignumber(1)), true);
+      assert.equal(largerEq(complex(0,1), math.bignumber(1)), false);
+      assert.equal(largerEq(math.bignumber(1), complex(1,0)), true);
+      assert.equal(largerEq(math.bignumber(1), complex(2,1)), false);
+      assert.equal(largerEq(math.bignumber(1), complex(0,1)), true);
+    });
   });
 
   it('should throw an error if comparing two matrices of different sizes', function() {
diff --git a/test/function/relational/smaller.test.js b/test/function/relational/smaller.test.js
index 819b32ff4..af1904a44 100644
--- a/test/function/relational/smaller.test.js
+++ b/test/function/relational/smaller.test.js
@@ -197,12 +197,31 @@ describe('smaller', function() {
     });
   });
 
-  it('should throw an error when comparing complex numbers', function() {
-    assert.throws(function () {smaller(complex(1,1), complex(1,2));}, TypeError);
-    assert.throws(function () {smaller(complex(2,1), 3);}, TypeError);
-    assert.throws(function () {smaller(3, complex(2,4));}, TypeError);
-    assert.throws(function () {smaller(math.bignumber(3), complex(2,4));}, TypeError);
-    assert.throws(function () {smaller(complex(2,4), math.bignumber(3));}, TypeError);
+  describe('Complex Numbers', function () {
+
+    it('should compare complex numbers', function() {
+      assert.equal(smaller(complex(1,1), complex(1,1)), false);
+      assert.equal(smaller(complex(2,1), complex(1,2)), false);
+      assert.equal(smaller(complex(0,1), complex(1,2)), true);
+    });
+
+    it('should compare complex number and number', function() {
+      assert.equal(smaller(complex(1,0), 1), false);
+      assert.equal(smaller(complex(2,1), 1), false);
+      assert.equal(smaller(complex(0,1), 1), true);
+      assert.equal(smaller(1, complex(1,0)), false);
+      assert.equal(smaller(1, complex(2,1)), true);
+      assert.equal(smaller(1, complex(0,1)), false);
+    });
+
+    it('should compare complex number and bignumber', function() {
+      assert.equal(smaller(complex(1,0), math.bignumber(1)), false);
+      assert.equal(smaller(complex(2,1), math.bignumber(1)), false);
+      assert.equal(smaller(complex(0,1), math.bignumber(1)), true);
+      assert.equal(smaller(math.bignumber(1), complex(1,0)), false);
+      assert.equal(smaller(math.bignumber(1), complex(2,1)), true);
+      assert.equal(smaller(math.bignumber(1), complex(0,1)), false);
+    });
   });
 
   it('should throw an error with two matrices of different sizes', function () {
diff --git a/test/function/relational/smallerEq.test.js b/test/function/relational/smallerEq.test.js
index bd93a146c..0607bda6e 100644
--- a/test/function/relational/smallerEq.test.js
+++ b/test/function/relational/smallerEq.test.js
@@ -197,12 +197,31 @@ describe('smallerEq', function() {
     });
   });
 
-  it('should throw an error when comparing complex numbers', function() {
-    assert.throws(function () {smallerEq(complex(1,1), complex(1,2));}, TypeError);
-    assert.throws(function () {smallerEq(complex(2,1), 3);}, TypeError);
-    assert.throws(function () {smallerEq(3, complex(2,4));}, TypeError);
-    assert.throws(function () {smallerEq(math.bignumber(3), complex(2,4));}, TypeError);
-    assert.throws(function () {smallerEq(complex(2,4), math.bignumber(3));}, TypeError);
+  describe('Complex Numbers', function () {
+
+    it('should compare complex numbers', function() {
+      assert.equal(smallerEq(complex(1,1), complex(1,1)), true);
+      assert.equal(smallerEq(complex(2,1), complex(1,2)), false);
+      assert.equal(smallerEq(complex(0,1), complex(1,2)), true);
+    });
+
+    it('should compare complex number and number', function() {
+      assert.equal(smallerEq(complex(1,0), 1), true);
+      assert.equal(smallerEq(complex(2,1), 1), false);
+      assert.equal(smallerEq(complex(0,1), 1), true);
+      assert.equal(smallerEq(1, complex(1,0)), true);
+      assert.equal(smallerEq(1, complex(2,1)), true);
+      assert.equal(smallerEq(1, complex(0,1)), false);
+    });
+
+    it('should compare complex number and bignumber', function() {
+      assert.equal(smallerEq(complex(1,0), math.bignumber(1)), true);
+      assert.equal(smallerEq(complex(2,1), math.bignumber(1)), false);
+      assert.equal(smallerEq(complex(0,1), math.bignumber(1)), true);
+      assert.equal(smallerEq(math.bignumber(1), complex(1,0)), true);
+      assert.equal(smallerEq(math.bignumber(1), complex(2,1)), true);
+      assert.equal(smallerEq(math.bignumber(1), complex(0,1)), false);
+    });
   });
 
   it('should throw an error with two matrices of different sizes', function () {
diff --git a/test/function/statistics/max.test.js b/test/function/statistics/max.test.js
index a09b3c53e..857f917b1 100644
--- a/test/function/statistics/max.test.js
+++ b/test/function/statistics/max.test.js
@@ -68,14 +68,31 @@ describe('max', function() {
         [[2, 4, 6], [7, 9, 11]]);
   });
 
-  it('should throw an error when called with complex numbers', function() {
-    assert.throws(function () {max(new Complex(2,3), new Complex(2,1))}, TypeError);
-    assert.throws(function () {max(new Complex(2,3), new Complex(2,5))}, TypeError);
-
-    assert.throws(function () {max(new Complex(3,4), 4)}, TypeError);
-    assert.throws(function () {max(new Complex(3,4), 5)}, TypeError);
-    assert.throws(function () {max(5, new Complex(3,4))}, TypeError);
-    assert.throws(function () {max(new Complex(3,4), 6)}, TypeError);
+  describe('Complex Numbers', function () {
+
+    it('should return the complex number with larger value', function() {
+      assert.deepEqual(max(new Complex(1,1), new Complex(1,1)), new Complex(1,1));
+      assert.deepEqual(max(new Complex(2,1), new Complex(1,2)), new Complex(2,1));
+      assert.deepEqual(max(new Complex(0,1), new Complex(1,2)), new Complex(1,2));
+    });
+
+    it('should return the larger between complex number and number', function() {
+      assert.deepEqual(max(new Complex(1,0), 1), new Complex(1,0));
+      assert.deepEqual(max(new Complex(2,1), 1), new Complex(2,1));
+      assert.equal(max(new Complex(0,1), 1), 1);
+      assert.equal(max(1, new Complex(1,0)), 1);
+      assert.deepEqual(max(1, new Complex(2,1)), new Complex(2,1));
+      assert.equal(max(1, new Complex(0,1)), 1);
+    });
+
+    it('should return the larger between complex number and bignumber', function() {
+      assert.deepEqual(max(new Complex(1,0), math.bignumber(1)), new Complex(1,0));
+      assert.deepEqual(max(new Complex(2,1), math.bignumber(1)), new Complex(2,1));
+      assert.equal(max(new Complex(0,1), math.bignumber(1)), 1);
+      assert.equal(max(math.bignumber(1), new Complex(1,0)), 1);
+      assert.deepEqual(max(math.bignumber(1), new Complex(2,1)), new Complex(2,1));
+      assert.equal(max(math.bignumber(1), new Complex(0,1)), 1);
+    });
   });
 
   it('should throw an error when called multiple arrays or matrices', function() {
diff --git a/test/function/statistics/median.test.js b/test/function/statistics/median.test.js
index cac362d82..dae174448 100644
--- a/test/function/statistics/median.test.js
+++ b/test/function/statistics/median.test.js
@@ -68,6 +68,10 @@ describe('median', function() {
     ])), 3.5);
   });
 
+  it('should return the median for complex numbers', function() {
+    assert.deepEqual(median(new Complex(2, 3), new Complex(-1, 2)), new Complex(0.5, 2.5));
+  });
+
   it('should throw an error if called with invalid number of arguments', function() {
     assert.throws(function() {median()});
     assert.throws(function() {median([], 2, 3)});
@@ -84,7 +88,6 @@ describe('median', function() {
 
   it('should throw an error if called with unsupported type of arguments', function() {
     assert.throws(function () {median(new Date(), 2, 3)}, /TypeError: Unexpected type of argument/);
-    assert.throws(function () {median(new Complex(2,3), new Complex(-1,2))}, /TypeError: No ordering relation is defined for complex numbers/);
   });
 
   it('should throw an error if called with an empty array', function() {
diff --git a/test/function/statistics/min.test.js b/test/function/statistics/min.test.js
index 2c254e51f..30e517e73 100644
--- a/test/function/statistics/min.test.js
+++ b/test/function/statistics/min.test.js
@@ -77,14 +77,31 @@ describe('min', function() {
       [[1, 2], [3,4], [5,6]]);
   });
 
-  it('should throw an error when called with complex numbers', function() {
-    assert.throws(function () {min(new Complex(2,3), new Complex(2,1))}, TypeError);
-    assert.throws(function () {min(new Complex(2,3), new Complex(2,5))}, TypeError);
-
-    assert.throws(function () {min(new Complex(3,4), 4)}, TypeError);
-    assert.throws(function () {min(new Complex(3,4), 5)}, TypeError);
-    assert.throws(function () {min(5, new Complex(3,4))}, TypeError);
-    assert.throws(function () {min(new Complex(3,4), 6)}, TypeError);
+  describe('Complex Numbers', function () {
+
+    it('should return the complex number with smaller value', function() {
+      assert.deepEqual(min(new Complex(1,1), new Complex(1,2)), new Complex(1,1));
+      assert.deepEqual(min(new Complex(2,1), new Complex(1,2)), new Complex(1,2));
+      assert.deepEqual(min(new Complex(0,1), new Complex(1,2)), new Complex(0,1));
+    });
+
+    it('should return the smaller between complex number and number', function() {
+      assert.deepEqual(min(new Complex(1,0), 1), new Complex(1,0));
+      assert.equal(min(new Complex(2,1), 1), 1);
+      assert.deepEqual(min(new Complex(0,1), 1), new Complex(0,1));
+      assert.equal(min(1, new Complex(1,0)), 1);
+      assert.equal(min(1, new Complex(2,1)), 1);
+      assert.deepEqual(min(1, new Complex(0,1)), new Complex(0,1));
+    });
+
+    it('should return the smaller between complex number and bignumber', function() {
+      assert.deepEqual(min(new Complex(1,0), math.bignumber(1)), new Complex(1,0));
+      assert.equal(min(new Complex(2,1), math.bignumber(1)), 1);
+      assert.deepEqual(min(new Complex(0,1), math.bignumber(1)), new Complex(0,1));
+      assert.equal(min(math.bignumber(1), new Complex(1,0)), 1);
+      assert.equal(min(math.bignumber(1), new Complex(2,1)), 1);
+      assert.deepEqual(min(math.bignumber(1), new Complex(0,1)), new Complex(0,1));
+    });
   });
 
   it('should throw an error when called multiple arrays or matrices', function() {
diff --git a/test/type/complex/Complex.test.js b/test/type/complex/Complex.test.js
index bd16e9081..bcf6463cb 100644
--- a/test/type/complex/Complex.test.js
+++ b/test/type/complex/Complex.test.js
@@ -299,4 +299,10 @@ describe('Complex', function () {
     assert.strictEqual(c2.im, 0);
   });
 
+  it('compare', function () {
+    assert.deepEqual(Complex.compare(new Complex(3, 4), new Complex(2, 4)), 1);
+    assert.deepEqual(Complex.compare(new Complex(2, 4), new Complex(2, 4)), 0);
+    assert.deepEqual(Complex.compare(new Complex(2, 4), new Complex(2, 7)), -1);
+  });
+
 });