Source: support-lib.js

/**
 * Universidad de La Laguna
 * Escuela Superior de Ingeniería y Tecnología
 * Grado en Ingeniería Informática
 * Procesadores de Lenguajes
 *
 * @author Juan Rodríguez Suárez
 * @since Mar 04 2024
 * @desc Contains some support functions to work with complex numbers.
 */

const Complex = require('complex.js');

Complex.prototype.factorial = function() {
  if (this.im !== 0) throw new Error(`Imaginary part must be zero. Instead is ${this.im}`);
  let n = this.re;
  if (!Number.isInteger(n)) throw new Error(`Not an Integer number ${n}`);
  if ( n < 0) throw new Error(`Factorial of negative number ${n}`);
  let result = Complex(1);
  if (n === 0) return result;
  for (let i = 1; i <= n; i++) {
    result = result.mul(i);
  }
  return Complex({re: result.re, im: this.im});
};

/**
 * @brief Returns the maximum of two complex numbers
 * @param {Complex} a - The first number
 * @param {Complex} b - The second number
 * @returns {Complex} The maximum
 */
const max = function(a, b) {
  return a.re > b.re ? a : a.re === b.re && a.im >= b.im ? a : b;
}

/**
 * @brief Returns the minimum of two complex numbers
 * @param {Complex} a - The first number
 * @param {Complex} b - The second number
 * @returns {Complex} The minimum
 */
const min = function(a, b) {
  return a.re < b.re ? a : a.re === b.re && a.im <= b.im ? a : b;
}

/**
 * @brief Prints a complex number
 * @param {Complex} x - The input
 * @returns {Complex} The input
 */
const print = x => { console.log(x.toString()); return x; } 

Complex.prototype.lessThan = function(other) {
  return this.re < other.re || (this.re === other.re && this.im < other.im);
}

const OPERATORS = new Set(['add', 'sub', 'mul', 'div', 'pow', 'equals', 'neg', 'lessThan']);

let oldComplex = Object.create(null);
for (let op of OPERATORS) {
  oldComplex[op] = Complex.prototype[op];
  Complex.prototype[op] = function(other) {
    try {
      if (typeof other === 'function') return other[op](this);
      if (typeof other === 'boolean') return this[op](Complex(Number(other)));
      return oldComplex[op].call(this, other);
    }
    catch (e) {
      throw new Error(`Complex numbers do not support the operation ${op} for ${other}\n${e}`);
    }
  }
}

for (let op of OPERATORS) {
  Boolean.prototype[op] = function(other) {
    return Complex(Number(this))[op](other);
  }
  Function.prototype[op] = function(other) {
    switch (typeof other) {
      case 'boolean':
        return (...args) => this(...args)[op](Complex(Number(other)));
      case 'object':
        if (other instanceof Complex) {
          return (...args) => this(...args)[op](other);
        } else {
          throw new Error(`Function does not support the operation ${op} for ${other}`);
        }
      case 'function':
        try {
          return (...args) => this(...args)[op](other(...args));
        } catch (e) {
          throw new Error(`Function does not support the operation ${op} for function ${other}`);
        }
      case 'undefined':
        return (...args) => this(...args)[op]();
      default:
        throw new Error(`Unsupported ${op} for type ${typeof other}`);
    }
  }
}

module.exports = {
  print,
  max,
  min,
  Complex
};