MathComplexOperator
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RicardoSantos

//@version=5

// @description A set of utility functions to handle complex numbers.
library(title='MathComplexOperator')

// references:
//      https://en.wikipedia.org/wiki/Complex_number
//      js: https://rosettacode.org/wiki/Fast_Fourier_transform
//      https://github.com/trekhleb/javascript-algorithms/blob/477f30b0bdac6024874d2976de1eca7afe0176dd/src/algorithms/math/complex-number/ComplexNumber.js#L3
//      https://github.com/infusion/Complex.js/
//      https://github.com/mathnet/mathnet-numerics/blob/a50d68d52def605a53d129cc60ea3371a2e97548/src/Numerics/Complex32.cs



//#region   -> Imports:

import RicardoSantos/DebugConsole/11 as console
import RicardoSantos/CommonTypesMath/1 as TMath
import RicardoSantos/MathComplexCore/3 as complex
import RicardoSantos/MathConstants/1 as mc
import RicardoSantos/MathExtension/1 as me

//#endregion

logger = console.Console.new().init()



//#region   -> Instance Methods:

// conjugate () {
// reference:
//      https://en.cppreference.com/w/c/numeric/complex/conj

// @function Computes the conjugate of complex number by reversing the sign of the imaginary part.
// @param this complex.
// @returns Complex.
export method conjugate (TMath.complex this)=>
    complex.new(
     this.re      , 
     -1 * this.im )

// }
// add () {

// @function Adds complex number other to this, in the form:
//         [_a.real + _b.real, _a.imaginary + _b.imaginary].
// @param this pseudo complex number in the form of a array [real, imaginary].
// @param other pseudo complex number in the form of a array [real, imaginary].
// @returns complex
export method add(TMath.complex this, TMath.complex other)=>
    complex.new(
     this.re + other.re , 
     this.im + other.im )

// }
// subtract () {

// @function Subtract other from this, in the form: 
//         [_a.real - _b.real, _a.imaginary - _b.imaginary].
// @param this complex.
// @param other complex.
// @returns complex
export method subtract (TMath.complex this, TMath.complex other)=>
    complex.new(
     this.re - other.re , 
     this.im - other.im )

// }
// multiply () {

// @function Multiply this with other, in the form: 
//         [(_a.real * _b.real) - (_a.imaginary * _b.imaginary), (_a.real * _b.imaginary) + (_a.imaginary * _b.real)]
// @param this complex.
// @param other complex.
// @returns complex
export method multiply (TMath.complex this, TMath.complex other)=>
    complex.new(
     this.re * other.re - this.im * other.im , 
     this.re * other.im + this.im * other.re )

// }
// divide () {

// @function Divide complex_number _a with _b, in the form: 
//         [(_a.real * _b.real) - (_a.imaginary * _b.imaginary), (_a.real * _b.imaginary) + (_a.imaginary * _b.real)]
// @param this complex.
// @param other complex.
// @returns complex
export method divide (TMath.complex this, TMath.complex other)=>
    TMath.complex _complexDivider = complex.new(other.re, other.im)
    // Multiply dividend by divider's conjugate.
    TMath.complex _finalDivident = multiply(this, conjugate(_complexDivider))
    // Calculating final divider using formula (a + bi)(a − bi) = a^2 + b^2
    float finalDivider = math.pow(_complexDivider.re, 2) + math.pow(_complexDivider.im, 2)
    complex.new(
     _finalDivident.re / finalDivider ,
     _finalDivident.im / finalDivider )

// }
// reciprocal () {
//{ remarks:
//      https://github.com/mathnet/mathnet-numerics/blob/a50d68d52def605a53d129cc60ea3371a2e97548/src/Numerics/Complex32.cs#L744

// @function Computes the reciprocal or inverse of complex_number.
// @param complex_number complex.
// @returns complex
export method reciprocal (TMath.complex this)=>
    if this.is_zero()
        complex.zero()
    else
        divide(complex.one(), this)

// negative () {
// }

// @function Negative of complex_number, in the form: [-_a.real, -_a.imaginary]
// @param complex_number complex.
// @returns complex
export method negative (TMath.complex this)=>
	complex.new(
     -this.re , 
     -this.im )

// }
// inverse () {

// @function Inverse of complex_number, in the form: [1/_a.real, 1/_a.imaginary]
// @param complex_number complex.
// @returns complex
export method inverse (TMath.complex this)=>
	// 1 / 0 = infinity, 1 / infinity = 0
	if this.re != 0 and this.im != 0
    	float _d = this.re * this.re + this.im * this.im
        complex.new(this.re / _d, (0 - this.im) / _d)
    else
        complex.zero()

// }
// exponential () {
// reference:
//      https://en.cppreference.com/w/c/numeric/complex/cexp
//      http://cboard.cprogramming.com/c-programming/89116-how-implement-complex-exponential-functions-c.html#post637921

// @function Exponential of complex_number.
// @param complex_number pseudo complex number in the form of a array [real, imaginary].
// @returns complex
export method exponential (TMath.complex this)=>
	var _er = math.exp(this.re)
    complex.new(_er * math.cos(this.im), _er * math.sin(this.im))

// }
// ceil () {

// @function Ceils complex_number. 
// @param complex_number complex.
// @param  digits int, digits to use as ceiling.
// @returns
//     _complex: pseudo complex number in the form of a array [real, imaginary]
export method ceil (TMath.complex this, int digits)=>
    float _places = math.pow(10, digits)
    complex.new(math.ceil(this.re * _places) / _places, math.ceil(this.im * _places) / _places)

// }
// radius () {

// @function Radius(magnitude) of complex_number, in the form: [sqrt(pow(complex_number))]
//     This is defined as its distance from the origin (0,0) of the complex plane.
// @param complex_number complex.
// @returns float value with radius.
export method radius (TMath.complex this)=>
    math.sqrt(math.pow(this.re, 2) + math.pow(this.im, 2))
//{ usage:
// logger.queue_one(__C, str.format('radius of complex number "5.2+3.0i" = {0, number, #.####}', radius(new(5.2, 3.0)))) // 6.0

// }
// magnitude () {
//{ remarks:
//      https://github.com/mathnet/mathnet-numerics/blob/a50d68d52def605a53d129cc60ea3371a2e97548/src/Numerics/Complex32.cs#L179

// @function magnitude(absolute value) of complex_number, should be the same as the radius.
// @param complex_number complex.
// @returns float.
export method magnitude (TMath.complex this)=>
    float _a = math.abs(this.re)
    float _b = math.abs(this.im)
    if _a > _b
        float _tmp = _b / _a
        _a * math.sqrt(1.0 * math.pow(_tmp, 2))
    else if _a == 0.0
        _b
    else
        float _tmp = _a / _b
        _b * math.sqrt(1.0 * math.pow(_tmp, 2))
//{ usage:
//      does not seem to be correct..
// logger.queue_one(__C, str.format('magnitude of complex number "5.2+3.0i" = {0, number, #.####} // should be 6', magnitude(new(5.2, 3.0))))

// }
// magnitude_squared () {
// remarks:
//      https://github.com/mathnet/mathnet-numerics/blob/a50d68d52def605a53d129cc60ea3371a2e97548/src/Numerics/Complex32.cs#L179

// @function magnitude(absolute value) of complex_number, should be the same as the radius.
// @param complex_number complex.
// @returns float.
export method magnitude_squared (TMath.complex this)=>
    float _re = math.pow(this.re, 2)
    float _im = math.pow(this.im, 2)
    _re + _im
//{ usage:
// logger.queue_one(__C, str.format('magnitude squared of complex number "5.2+3.0i" = {0, number, #.####}', magnitude_squared(new(5.2, 3.0)))) // 36.0

// }
// sign () {
// remarks:
//      https://github.com/mathnet/mathnet-numerics/blob/a50d68d52def605a53d129cc60ea3371a2e97548/src/Numerics/Complex32.cs#L218

// @function Unity of complex numbers.
// @param complex_number complex.
// @returns float array, complex number.
export method sign (TMath.complex this)=>
    if this.re >= 1.0e307 and this.im >= 1.0e307
        complex.new(mc.Sqrt1Over2(), mc.Sqrt1Over2())
    else if this.re >= 1.0e307 and this.im <= -1.0e307
        complex.new(mc.Sqrt1Over2(), -mc.Sqrt1Over2())
    else if this.re <= -1.0e307 and this.im >= 1.0e307
        complex.new(-mc.Sqrt1Over2(), mc.Sqrt1Over2())
    else if this.re <= -1.0e307 and this.im <= -1.0e307
        complex.new(-mc.Sqrt1Over2(), -mc.Sqrt1Over2())
    else
        float _mod = me.hypotenuse(this.re, this.im)
        if _mod == 0.0
            complex.zero()
        else
            complex.new(this.re / _mod, this.im / _mod)

// usage:
// logger.queue_one(__C, str.format('sign of complex number "5.2+3.0i" = {0}', to_string(sign(new(5.2, 3.0)), '#.##'))) // "0.87+0.5i"

// }

//#endregion


if barstate.islastconfirmedhistory
    A = complex.new(7.0, -6.0)
    B = complex.new(-3.0, 4.0)
    logger.queue_one(str.format('input parameters: (A: {0}, B: {1})', complex.to_string(A, '#.##'), complex.to_string(B, '#.##')))

    logger.queue_one(str.format('\n##   Operators       ##\n', ''))
    logger.queue_one(str.format('A + B = {0}', complex.to_string(add(A, B), '#.##')))
    logger.queue_one(str.format('A - B = {0}', complex.to_string(subtract(A, B), '#.##')))
    logger.queue_one(str.format('A * B = {0}', complex.to_string(multiply(A, B), '#.##')))
    logger.queue_one(str.format('A / B = {0}', complex.to_string(divide(A, B), '#.##')))

    logger.queue_one(str.format('\n##   Properties      ##\n', ''))
    logger.queue_one(str.format('Conjugate of A: {0}', complex.to_string(conjugate(A), '#.##')))
    logger.queue_one(str.format('Reciprocal of A = {0}', complex.to_string(reciprocal(A), '#.##')))
    logger.queue_one(str.format('Negative of A = {0}', complex.to_string(negative(A), '#.##')))
    logger.queue_one(str.format('Inverse of A = {0}', complex.to_string(inverse(A), '#.##')))
    logger.queue_one(str.format('Exponential of A = {0}', complex.to_string(exponential(A), '#.##')))
    logger.queue_one(str.format('Ceiling of A = {0}', complex.to_string(ceil(A, 1), '#.##')))

    logger.queue_one(str.format('Radius of A = {0}', radius(A)))
    logger.queue_one(str.format('Magnitude of A = {0} //should be 6.0?', magnitude(A)))
    logger.queue_one(str.format('Magnitude Squared of A = {0}', magnitude_squared(A)))
    logger.queue_one(str.format('Sign of A = {0}', complex.to_string(sign(A), '#.##')))

// update log
logger.update()
