defmodule Complex do @moduledoc """ *Complex* is a library for types and mathematical functions for complex numbers. Each complex number is represented as a structure holding the real and imaginary part. There are functions for creation and manipulation of them. Unfortunately since there is n operator overloading in Elixir the math functions (add, subtract, etc.) are implemented as add/2, sub/2, etc. ## Examples iex> Complex.new(3, 4) %Complex{im: 4, re: 3} iex> Complex.imag() %Complex{im: 1.0, re: 0.0} """ @vsn 1 import Kernel, except: [abs: 1, div: 2] @typedoc """ General type for complex numbers """ @type complex :: %Complex{re: number, im: number} defstruct re: 0, im: 0 @doc """ Returns a new complex with specified real and imaginary components. The imaginary part defaults to zero so a "real" number can be created with new/1 #### See also [imag/0](#imag/0) [fromPolar/2](#fromPolar/2) #### Examples iex> Complex.new(3, 4) %Complex{im: 4, re: 3} iex> Complex.new(2) %Complex{im: 0, re: 2} """ @spec new(number, number) :: complex def new(re, im \\ 0), do: %Complex{re: re, im: im} @doc """ Returns a new complex representing the pure imaginary number sqrt(-1). #### See also [new/2](#new/2) [fromPolar/2](#fromPolar/2) #### Examples iex> Complex.imag() %Complex{im: 1.0, re: 0.0} """ @spec imag() :: complex def imag(), do: %Complex{re: 0.0, im: 1.0} @doc """ Returns a new complex number described by the supplied polar coordinates. That is, the complex (real and imaginary) will have radius (magnitude) r and angle (phase) phi. #### See also [new/2](#new/2) [imag/0](#imag/0) #### Examples iex> Complex.fromPolar(1, :math.pi/2) %Complex{im: 1.0, re: 6.123233995736766e-17} """ @spec fromPolar(number, number) :: complex def fromPolar(r, phi) do new(r*:math.cos(phi), r*:math.sin(phi)) end @doc """ Returns the phase angle of the supplied complex. #### See also [new/2](#new/2) [fromPolar/2](#fromPolar/2) #### Examples iex> Complex.phase( Complex.fromPolar(1,:math.pi/2) ) 1.5707963267948966 """ @spec phase(complex) :: float def phase(z = %Complex{}) do :math.atan2(z.im, z.re) end @doc """ Returns the polar coordinates of the supplied complex. That is, the returned tuple {r,phi} is the magnitude and phase of z. #### See also [fromPolar/2](#fromPolar/2) #### Examples iex> Complex.getPolar( Complex.fromPolar(1,:math.pi/2) ) {1.0, 1.5707963267948966} """ @spec getPolar(complex) :: {float,float} def getPolar(z = %Complex{}) do {abs(z), phase(z)} end @doc """ Returns a new complex that is the sum of the provided complex numbers. #### See also [div/2](#div/2), [mult/2](#mult/2), [sub/2](#sub/2) #### Examples iex> Complex.add( Complex.fromPolar(1, :math.pi/2), Complex.fromPolar(1, :math.pi/2) ) %Complex{im: 2.0, re: 1.2246467991473532e-16} """ @spec add(complex, complex) :: complex def add(%Complex{re: r1, im: i1}, %Complex{re: r2, im: i2}) do new(r1+r2, i1+i2) end @doc """ Returns a new complex that is the difference of the provided complex numbers. #### See also [add/2](#add/2), [div/2](#div/2), [mult/2](#mult/2) #### Examples iex> Complex.sub( Complex.fromPolar(1, :math.pi/2), Complex.fromPolar(1, :math.pi/2) ) %Complex{im: 0.0, re: 0.0} """ @spec sub(complex, complex) :: complex def sub(%Complex{re: r1, im: i1}, %Complex{re: r2, im: i2}) do new(r1-r2, i1-i2) end @doc """ Returns a new complex that is the product of the provided complex numbers. #### See also [add/2](#add/2), [div/2](#div/2), [sub/2](#sub/2) #### Examples iex> Complex.mult( Complex.fromPolar(1, :math.pi/2), Complex.fromPolar(1, :math.pi/2) ) %Complex{im: 1.2246467991473532e-16, re: -1.0} iex> Complex.mult( Complex.imag(), Complex.imag() ) %Complex{im: 0.0, re: -1.0} """ @spec mult(complex, complex) :: complex def mult(%Complex{re: r1, im: i1}, %Complex{re: r2, im: i2}) do new(r1*r2 - i1*i2, i1*r2 + r1*i2) end @doc """ Returns a new complex that is the ratio (division) of the provided complex numbers. #### See also [add/2](#add/2), [mult/2](#mult/2), [sub/2](#sub/2) #### Examples iex> Complex.div( Complex.fromPolar(1, :math.pi/2), Complex.fromPolar(1, :math.pi/2) ) %Complex{im: 0.0, re: 1.0} """ @spec div(complex, complex) :: complex def div(%Complex{re: r1, im: i1}, %Complex{re: r2, im: i2}) do if Kernel.abs(r2) < Kernel.abs(i2) do r = r2 / i2 den = i2 + r*r2 new((r1*r + i1)/den, (i1*r - r1)/den) else r = i2 / r2 den = r2 + r*i2 new((r1 + r*i1)/den, (i1 - r*r1)/den) end end @doc """ Returns a new complex that is the magnitude (length)) of the provided complex number. #### See also [new/2](#new/2), [phase/1](#phase/1) #### Examples iex> Complex.abs( Complex.fromPolar(1, :math.pi/2) ) 1.0 """ @spec abs(complex) :: number def abs(%Complex{re: r, im: i}) do # optimized by checking special cases (sqrt is expensive) x = Kernel.abs(r) y = Kernel.abs(i) cond do x == 0.0 -> y y == 0.0 -> x x > y -> x * :math.sqrt(1.0 + (y/x)*(y/x)) true -> y * :math.sqrt(1.0 + (x/y)*(x/y)) end end @doc """ Returns a new complex that is the complex conjugate of the provided complex number. #### See also [abs/2](#abs/2), [phase/1](#phase/1) #### Examples iex> Complex.conjugate( Complex.new(1,2) ) %Complex{im: -2, re: 1} """ @spec conjugate(complex) :: complex def conjugate(%Complex{re: r, im: i}) do new(r,-i) end @doc """ Returns a new complex that is the complex square root of the provided complex number. #### See also [abs/2](#abs/2), [phase/1](#phase/1) #### Examples iex> Complex.sqrt( Complex.fromPolar(2,:math.pi) ) %Complex{im: 1.4142135623730951, re: 8.659560562354933e-17} """ @spec sqrt(complex) :: complex def sqrt(z = %Complex{re: r, im: i}) do if z.re == 0.0 and z.im == 0.0 do new(z.re,z.im) else x = Kernel.abs(r) y = Kernel.abs(i) w = if x >= y do :math.sqrt(x) * :math.sqrt(0.5 * (1.0 + :math.sqrt(1.0 + (y/x)*(y/x)))) else :math.sqrt(y) * :math.sqrt(0.5 * ((x/y) + :math.sqrt(1.0 + (x/y)*(x/y)))) end if z.re >= 0.0 do new(w, z.im/(2*w)) else i2 = if z.im >= 0.0 do w else -w end new(z.im/(2*i2), i2) end end end @doc """ Returns a new complex that is the complex exponential of the provided complex number. That is, e raised to the power z. #### See also [ln/1](#ln/1) #### Examples iex> Complex.exp( Complex.fromPolar(2,:math.pi) ) %Complex{im: 3.3147584285483636e-17, re: 0.1353352832366127} """ @spec exp(complex) :: complex def exp(z = %Complex{}) do rho = :math.exp(z.re) theta = z.im new(rho*:math.cos(theta), rho*:math.sin(theta)) end @doc """ Returns a new complex that is the complex natural log of the provided complex number. That is, log base e of z. #### See also [exp/1](#exp/1) #### Examples iex> Complex.ln( Complex.fromPolar(2,:math.pi) ) %Complex{im: 3.141592653589793, re: 0.6931471805599453} """ @spec ln(complex) :: complex def ln(z = %Complex{}) do new(:math.log(abs(z)), :math.atan2(z.im,z.re)) end @doc """ Returns a new complex that is the complex log base 10 of the provided complex number. #### See also [ln/1](#ln/1) #### Examples iex> Complex.log10( Complex.fromPolar(2,:math.pi) ) %Complex{im: 1.3643763538418412, re: 0.30102999566398114} """ @spec log10(complex) :: complex def log10(z = %Complex{}) do div( ln(z), new(:math.log(10.0),0.0) ) end @doc """ Returns a new complex that is the complex log base 2 of the provided complex number. #### See also [ln/1](#ln/1), [log10/1](#log10/1) #### Examples iex> Complex.log2( Complex.fromPolar(2,:math.pi) ) %Complex{im: 4.532360141827194, re: 1.0} """ @spec log2(complex) :: complex def log2(z = %Complex{}) do div( ln(z), new(:math.log(2.0),0.0) ) end @doc """ Returns a new complex that is the provided parameter a raised to the complex power b. #### See also [ln/1](#ln/1), [log10/1](#log10/1) #### Examples iex> Complex.pow( Complex.fromPolar(2,:math.pi), Complex.imag() ) %Complex{im: 0.027612020368333014, re: 0.03324182700885666} """ @spec pow(complex, complex) :: complex def pow(x = %Complex{}, y = %Complex{}) do cond do x.re == 0.0 and x.im == 0.0 -> if y.re == 0.0 and y.im == 0.0 do new(1.0,0.0) else new(0.0,0.0) end y.re == 1.0 and y.im == 0.0 -> x y.re == -1.0 and y.im == 0.0 -> div(new(1.0,0.0),x) true -> rho = :math.sqrt(x.re*x.re + x.im*x.im) theta = :math.atan2(x.im,x.re) s = :math.pow(rho,y.re) * :math.exp(-y.im*theta) r = y.re*theta + y.im*:math.log(rho) new(s*:math.cos(r), s*:math.sin(r)) end end @doc """ Returns a new complex that is the sine of the provided parameter. #### See also [cos/1](#cos/1), [tan/1](#tan/1) #### Examples iex> Complex.sin( Complex.fromPolar(2,:math.pi) ) %Complex{im: -1.0192657827055095e-16, re: -0.9092974268256817} """ @spec sin(complex) :: complex def sin(z = %Complex{}) do new(:math.sin(z.re)*:math.cosh(z.im), :math.cos(z.re)*:math.sinh(z.im)) end @doc """ Returns a new complex that is the "negation" of the provided parameter. That is, the real and imaginary parts are negated. #### See also [neq/2](#new/2), [imag/0](#imag/0) #### Examples iex> Complex.neg( Complex.new(3,5) ) %Complex{im: -5, re: -3} """ @spec neg(complex) :: complex def neg(z = %Complex{}) do new(-z.re, -z.im) end @doc """ Returns a new complex that is the inverse sine (i.e., arcsine) of the provided parameter. #### See also [sin/1](#sin/1) #### Examples iex> Complex.asin( Complex.fromPolar(2,:math.pi) ) %Complex{im: 1.3169578969248164, re: -1.5707963267948963} iex> Complex.sin( Complex.asin(Complex.new(2,3)) ) %Complex{im: 3.000000000000001, re: 1.9999999999999991} """ @spec asin(complex) :: complex def asin(z = %Complex{}) do i = new(0.0,1.0) # result = -i*ln(i*z + sqrt(1.0-z*z)) # result = -i*ln(t1 + sqrt(t2)) t1 = mult(i,z) t2 = sub( new(1.0,0.0), mult(z,z) ) mult( neg(i), ln( add(t1, sqrt(t2)) ) ) end @doc """ Returns a new complex that is the cosine of the provided parameter. #### See also [sin/1](#sin/1), [tan/1](#tan/1) #### Examples iex> Complex.cos( Complex.fromPolar(2,:math.pi) ) %Complex{im: 2.2271363664699914e-16, re: -0.4161468365471424} """ @spec cos(complex) :: complex def cos(z = %Complex{}) do new(:math.cos(z.re)*:math.cosh(z.im), -:math.sin(z.re)*:math.sinh(z.im)) end @doc """ Returns a new complex that is the inverse cosine (i.e., arccosine) of the provided parameter. #### See also [cos/1](#cos/1) #### Examples iex> Complex.acos( Complex.fromPolar(2,:math.pi) ) %Complex{im: 1.3169578969248164, re: -3.141592653589793} iex> Complex.cos( Complex.acos(Complex.new(2,3)) ) %Complex{im: 3.0, re: 2.0000000000000004} """ @spec acos(complex) :: complex def acos(z = %Complex{}) do i = new(0.0,1.0) one = new(1.0,0.0) # result = -i*ln(z + sqrt(z*z-1.0)) # result = -i*ln(z + sqrt(t1)) t1 = sub( mult(z,z), one ) mult( neg(i), ln( add(z, sqrt(t1)) ) ) end @doc """ Returns a new complex that is the tangent of the provided parameter. #### See also [sin/1](#sin/1), [cos/1](#cos/1) #### Examples iex> Complex.tan( Complex.fromPolar(2,:math.pi) ) %Complex{im: 1.4143199004457917e-15, re: 2.185039863261519} """ @spec tan(complex) :: complex def tan(z = %Complex{}) do div(sin(z), cos(z)) end @doc """ Returns a new complex that is the inverse tangent (i.e., arctangent) of the provided parameter. #### See also [tan/1](#tan/1) #### Examples iex> Complex.atan( Complex.fromPolar(2,:math.pi) ) %Complex{im: 0.0, re: -1.1071487177940904} iex> Complex.tan( Complex.atan(Complex.new(2,3)) ) %Complex{im: 3.0, re: 2.0} """ @spec atan(complex) :: complex def atan(z = %Complex{}) do i = new(0.0,1.0) # result = 0.5*i*(ln(1-i*z)-ln(1+i*z)) t1 = mult(new(0.5,0.0),i) t2 = sub( new(1.0,0.0), mult(i,z) ) t3 = add( new(1.0,0.0), mult(i,z) ) mult(t1, sub(ln(t2),ln(t3))) end @doc """ Returns a new complex that is the cotangent of the provided parameter. #### See also [sin/1](#sin/1), [cos/1](#cos/1), [tan/1](#tan/1) #### Examples iex> Complex.cot( Complex.fromPolar(2,:math.pi) ) %Complex{im: -2.9622992129532336e-16, re: 0.45765755436028577} """ @spec cot(complex) :: complex def cot(z = %Complex{}) do div(cos(z), sin(z)) end @doc """ Returns a new complex that is the inverse cotangent (i.e., arccotangent) of the provided parameter. #### See also [cot/1](#cot/1) #### Examples iex> Complex.acot( Complex.fromPolar(2,:math.pi) ) %Complex{im: -9.71445146547012e-17, re: -0.46364760900080615} iex> Complex.cot( Complex.acot(Complex.new(2,3)) ) %Complex{im: 2.9999999999999996, re: 1.9999999999999991} """ @spec acot(complex) :: complex def acot(z = %Complex{}) do i = new(0.0,1.0) # result = 0.5*i*(ln(1-i/z)-ln(1+i/z)) t1 = mult(new(0.5,0.0),i) t2 = sub( new(1.0,0.0), div(i,z) ) t3 = add( new(1.0,0.0), div(i,z) ) mult(t1, sub(ln(t2),ln(t3))) end @doc """ Returns a new complex that is the secant of the provided parameter. #### See also [sin/1](#sin/1), [cos/1](#cos/1), [tan/1](#tan/1) #### Examples iex> Complex.sec( Complex.fromPolar(2,:math.pi) ) %Complex{im: -1.2860374461837126e-15, re: -2.402997961722381} """ @spec sec(complex) :: complex def sec(z = %Complex{}) do div( new(1.0,0.0), cos(z) ) end @doc """ Returns a new complex that is the inverse secant (i.e., arcsecant) of the provided parameter. #### See also [sec/1](#sec/1) #### Examples iex> Complex.asec( Complex.fromPolar(2,:math.pi) ) %Complex{im: 0.0, re: 2.0943951023931957} iex> Complex.sec( Complex.asec(Complex.new(2,3)) ) %Complex{im: 2.9999999999999987, re: 1.9999999999999984} """ @spec asec(complex) :: complex def asec(z = %Complex{}) do i = new(0.0,1.0) one = new(1.0,0.0) # result = -i*ln(i*sqrt(1-1/(z*z))+1/z) # result = -i*ln(i*sqrt(1-t2)+t1) t1 = div(one,z) t2 = div(one, mult(z,z)) # result = -i*ln(i*sqrt(t3)+t1) # result = -i*ln(t4+t1) t3 = sub(one,t2) t4 = mult(i,sqrt(t3)) mult(neg(i), ln(add(t4,t1))) end @doc """ Returns a new complex that is the cosecant of the provided parameter. #### See also [sec/1](#sec/1), [sin/1](#sin/1), [cos/1](#cos/1), [tan/1](#tan/1) #### Examples iex> Complex.csc( Complex.fromPolar(2,:math.pi) ) %Complex{im: 1.2327514463765779e-16, re: -1.0997501702946164} """ @spec csc(complex) :: complex def csc(z = %Complex{}) do one = new(1.0,0.0) div(one, sin(z)) end @doc """ Returns a new complex that is the inverse cosecant (i.e., arccosecant) of the provided parameter. #### See also [sec/1](#sec/1) #### Examples iex> Complex.acsc( Complex.fromPolar(2,:math.pi) ) %Complex{im: 0.0, re: -0.5235987755982988} iex> Complex.csc( Complex.acsc(Complex.new(2,3)) ) %Complex{im: 2.9999999999999996, re: 1.9999999999999993} """ @spec acsc(complex) :: complex def acsc(z = %Complex{}) do i = new(0.0,1.0) one = new(1.0,0.0) # result = -i*ln(sqrt(1-1/(z*z))+i/z) # result = -i*ln(sqrt(1-t2)+t1) t1 = div(i,z) t2 = div(one, mult(z,z)) # result = -i*ln(sqrt(t3)+t1) # result = -i*ln(t4+t1) t3 = sub(one,t2) t4 = sqrt(t3) mult(neg(i), ln(add(t4,t1))) end @doc """ Returns a new complex that is the hyperbolic sine of the provided parameter. #### See also [cosh/1](#cosh/1), [tanh/1](#tanh/1) #### Examples iex> Complex.sinh( Complex.fromPolar(2,:math.pi) ) %Complex{im: 9.214721821703068e-16, re: -3.626860407847019} """ @spec sinh(complex) :: complex def sinh(z = %Complex{}) do p5 = new(0.5,0.0) mult(p5, sub(exp(z),exp(neg(z)))) end @doc """ Returns a new complex that is the inverse hyperbolic sine (i.e., arcsinh) of the provided parameter. #### See also [sinh/1](#sinh/1) #### Examples iex> Complex.asinh( Complex.fromPolar(2,:math.pi) ) %Complex{im: 1.0953573965284052e-16, re: -1.4436354751788099} iex> Complex.sinh( Complex.asinh(Complex.new(2,3)) ) %Complex{im: 3.0, re: 2.000000000000001} """ @spec asinh(complex) :: complex def asinh(z = %Complex{}) do one = new(1.0,0.0) # result = ln(z+sqrt(z*z+1)) # result = ln(z+sqrt(t1)) # result = ln(t2) t1 = add(mult(z,z),one) t2 = add(z,sqrt(t1)) ln(t2) end @doc """ Returns a new complex that is the hyperbolic cosine of the provided parameter. #### See also [sinh/1](#sinh/1), [tanh/1](#tanh/1) #### Examples iex> Complex.cosh( Complex.fromPolar(2,:math.pi) ) %Complex{im: -8.883245978848233e-16, re: 3.7621956910836314} """ @spec cosh(complex) :: complex def cosh(z = %Complex{}) do p5 = new(0.5,0.0) mult(p5, add(exp(z),exp(neg(z)))) end @doc """ Returns a new complex that is the inverse hyperbolic cosine (i.e., arccosh) of the provided parameter. #### See also [cosh/1](#cosh/1) #### Examples iex> Complex.acosh( Complex.fromPolar(2,:math.pi) ) %Complex{im: -3.141592653589793, re: -1.3169578969248164} iex> Complex.cosh( Complex.acosh(Complex.new(2,3)) ) %Complex{im: 3.0, re: 2.0} """ @spec acosh(complex) :: complex def acosh(z = %Complex{}) do one = new(1.0,0.0) # result = ln(z+sqrt(z*z-1)) # result = ln(z+sqrt(t1)) # result = ln(t2) t1 = sub(mult(z,z),one) t2 = add(z,sqrt(t1)) ln(t2) end @doc """ Returns a new complex that is the hyperbolic tangent of the provided parameter. #### See also [sinh/1](#sinh/1), [cosh/1](#cosh/1) #### Examples iex> Complex.tanh( Complex.fromPolar(2,:math.pi) ) %Complex{im: 1.7304461302709572e-17, re: -0.964027580075817} """ @spec tanh(complex) :: complex def tanh(z = %Complex{}) do div(sinh(z), cosh(z)) end @doc """ Returns a new complex that is the inverse hyperbolic tangent (i.e., arctanh) of the provided parameter. #### See also [tanh/1](#tanh/1) #### Examples iex> Complex.atanh( Complex.fromPolar(2,:math.pi) ) %Complex{im: 1.5707963267948966, re: -0.5493061443340549} iex> Complex.tanh( Complex.atanh(Complex.new(2,3)) ) %Complex{im: 2.999999999999999, re: 1.9999999999999987} """ @spec atanh(complex) :: complex def atanh(z = %Complex{}) do one = new(1.0,0.0) p5 = new(0.5,0.0) # result = 0.5*(ln((1+z)/(1-z))) # result = 0.5*(ln(t2/t1)) # result = 0.5*(ln(t3)) t1 = sub(one,z) t2 = add(one,z) t3 = div(t2,t1) mult(p5,ln(t3)) end @doc """ Returns a new complex that is the hyperbolic secant of the provided parameter. #### See also [sinh/1](#sinh/1), [cosh/1](#cosh/1), [tanh/1](#tanh/1) #### Examples iex> Complex.sech( Complex.fromPolar(2,:math.pi) ) %Complex{im: 6.27608655779184e-17, re: 0.2658022288340797} """ @spec sech(complex) :: complex def sech(z = %Complex{}) do two = new(2.0,0.0) div(two, add(exp(z),exp(neg(z)))) end @doc """ Returns a new complex that is the inverse hyperbolic secant (i.e., arcsech) of the provided parameter. #### See also [sech/1](#sech/1) #### Examples iex> Complex.asech( Complex.fromPolar(2,:math.pi) ) %Complex{im: -2.0943951023931953, re: 0.0} iex> Complex.sech( Complex.asech(Complex.new(2,3)) ) %Complex{im: 2.999999999999999, re: 2.0} """ @spec asech(complex) :: complex def asech(z = %Complex{}) do one = new(1.0,0.0) # result = ln(1/z+sqrt(1/z+1)*sqrt(1/z-1)) # result = ln(t1+sqrt(t1+1)*sqrt(t1-1)) # result = ln(t1+t2*t3) t1 = div(one,z) t2 = sqrt( add(t1,one) ) t3 = sqrt( sub(t1,one) ) ln( add(t1,mult(t2,t3)) ) end @doc """ Returns a new complex that is the hyperbolic cosecant of the provided parameter. #### See also [sinh/1](#sinh/1), [cosh/1](#cosh/1), [tanh/1](#tanh/1) #### Examples iex> Complex.csch( Complex.fromPolar(2,:math.pi) ) %Complex{im: -7.00520014334671e-17, re: -0.2757205647717832} """ @spec csch(complex) :: complex def csch(z = %Complex{}) do two = new(2.0,0.0) div(two, sub(exp(z),exp(neg(z)))) end @doc """ Returns a new complex that is the inverse hyperbolic cosecant (i.e., arccsch) of the provided parameter. #### See also [csch/1](#csch/1) #### Examples iex> Complex.acsch( Complex.fromPolar(2,:math.pi) ) %Complex{im: -5.4767869826420256e-17, re: -0.48121182505960336} iex> Complex.csch( Complex.acsch(Complex.new(2,3)) ) %Complex{im: 3.0000000000000018, re: 1.9999999999999982} """ @spec acsch(complex) :: complex def acsch(z = %Complex{}) do one = new(1.0,0.0) # result = ln(1/z+sqrt(1/(z*z)+1)) # result = ln(t1+sqrt(t2+1)) # result = ln(t1+t3) t1 = div(one,z) t2 = div(one,mult(z,z)) t3 = sqrt( add(t2,one) ) ln( add(t1,t3) ) end @doc """ Returns a new complex that is the hyperbolic cotangent of the provided parameter. #### See also [sinh/1](#sinh/1), [cosh/1](#cosh/1), [tanh/1](#tanh/1) #### Examples iex> Complex.coth( Complex.fromPolar(2,:math.pi) ) %Complex{im: -1.8619978115303632e-17, re: -1.037314720727548} """ @spec coth(complex) :: complex def coth(z = %Complex{}) do div(cosh(z), sinh(z)) end @doc """ Returns a new complex that is the inverse hyperbolic cotangent (i.e., arccoth) of the provided parameter. #### See also [coth/1](#coth/1) #### Examples iex> Complex.acoth( Complex.fromPolar(2,:math.pi) ) %Complex{im: -8.164311994315688e-17, re: -0.5493061443340548} iex> Complex.coth( Complex.acoth(Complex.new(2,3)) ) %Complex{im: 2.999999999999999, re: 2.0} """ @spec acoth(complex) :: complex def acoth(z = %Complex{}) do one = new(1.0,0.0) p5 = new(0.5,0.0) # result = 0.5*(ln(1+1/z)-ln(1-1/z)) # result = 0.5*(ln(1+t1)-ln(1-t1)) # result = 0.5*(ln(t2)-ln(t3)) t1 = div(one,z) t2 = add(one,t1) t3 = sub(one,t1) mult(p5, sub(ln(t2),ln(t3))) end end