http://mathling.com/core/complex  library module

http://mathling.com/core/complex


Complex numbers

Copyright© Mary Holstege 2021-2023
CC-BY (https://creativecommons.org/licenses/by/4.0/)

October 2021
Status: Stable

Imports

http://mathling.com/core/utilities
import module namespace util="http://mathling.com/core/utilities"
       at "../core/utilities.xqy"
http://mathling.com/core/config
import module namespace config="http://mathling.com/core/config"
       at "../core/config.xqy"
http://mathling.com/core/errors
import module namespace errors="http://mathling.com/core/errors"
       at "../core/errors.xqy"
http://mathling.com/geometric/point
import module namespace point="http://mathling.com/geometric/point"
       at "../geo/point.xqy"

Variables

Variable: $i as map(xs:string,item()*)

Variable: $one as map(xs:string,item()*)

Functions

Function: complex
declare function complex($real as xs:double, $imaginary as xs:double) as map(xs:string,item()*)


complex()
Construct a new complex number.

Params
  • real as xs:double: the real part of the number
  • imaginary as xs:double: the imaginary part of the number
Returns
  • map(xs:string,item()*): complex number
declare function this:complex(
  $real as xs:double,
  $imaginary as xs:double
) as map(xs:string,item()*)
{
  point:point($real, $imaginary)
}

Function: as-vector
declare function as-vector($complex as map(xs:string,item()*)) as xs:double*


as-vector()
Cast to the vector form of a complex number.

Params
  • complex as map(xs:string,item()*): complex number
Returns
  • xs:double*: complex number vector
declare function this:as-vector(
  $complex as map(xs:string,item()*)
) as xs:double*
{
  point:pcoordinates($complex)
}

Function: vector
declare function vector($complex as xs:double*) as map(xs:string,item()*)


vector()
Cast from the vector form of a complex number.

Params
  • complex as xs:double*: complex number vector
Returns
  • map(xs:string,item()*): complex number
declare function this:vector(
  $complex as xs:double*
) as map(xs:string,item()*)
{
  point:vector($complex)
}

Function: as-complex
declare function as-complex($real as xs:double) as map(xs:string,item()*)


as-complex()
Cast a real number to a complex.

Params
  • real as xs:double: the real part of the number
Returns
  • map(xs:string,item()*): complex number
declare function this:as-complex(
  $real as xs:double
) as map(xs:string,item()*)
{
  point:point($real, 0)
}

Function: is-real
declare function is-real($z as map(xs:string,item()*)) as xs:boolean


is-real()
Test whether the number has no imaginary part. (Within ε).

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • xs:boolean: true if complex number has only a real part
declare function this:is-real(
  $z as map(xs:string,item()*)
) as xs:boolean
{
  abs(this:pi($z)) < $config:ε
}

Function: as-real
declare function as-real($z as map(xs:string,item()*)) as xs:double


as-real()
Cast a complex number to a double, if possible. Raises error if not.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • xs:double: double
declare function this:as-real(
  $z as map(xs:string,item()*)
) as xs:double
{
  if (this:is-real($z)) then this:pr($z)
  else errors:error("UTIL-CAST", ($z,'complex'))
}
Errors

UTIL-CAST if there is an imaginary part

Function: complex-rφd
declare function complex-rφd($r as xs:double, $φ as xs:double) as map(xs:string,item()*)


complex-rφd()
Construct a new complex number using radius and phase angle
(polar repesentation re^iφ).

Params
  • r as xs:double: modulus of complex number
  • φ as xs:double: angle of complex number (degrees)
Returns
  • map(xs:string,item()*): complex number
declare function this:complex-rφd(
  $r as xs:double,
  $φ as xs:double
) as map(xs:string,item()*)
{
  let $φR := util:radians($φ)
  return
    point:point($r * math:cos($φR), $r * math:sin($φR))
}

Function: complex-rφ
declare function complex-rφ($r as xs:double, $φ as xs:double) as map(xs:string,item()*)


complex-rφ()
Construct a new complex number using radius and phase angle
(polar repesentation re^iφ).

Params
  • r as xs:double: modulus of complex number
  • φ as xs:double: angle of complex number (radians)
Returns
  • map(xs:string,item()*): complex number
declare function this:complex-rφ(
  $r as xs:double,
  $φ as xs:double
) as map(xs:string,item()*)
{
  point:point($r * math:cos($φ), $r * math:sin($φ))
}

Function: pr
declare function pr($z as map(xs:string,item()*)) as xs:double


pr()
Accessor for real part of the complex number.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • xs:double: raw real part
declare function this:pr($z as map(xs:string,item()*)) as xs:double
{
  point:px($z)
}

Function: pi
declare function pi($z as map(xs:string,item()*)) as xs:double


pi()
Accessor for imaginary part of the complex number.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • xs:double: raw imaginary part
declare function this:pi($z as map(xs:string,item()*)) as xs:double
{
  point:py($z)
}

Function: r
declare function r($z as map(xs:string,item()*)) as xs:integer


r()
Accessor for real part of the complex number, as integer.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • xs:integer: real part, rounded
declare function this:r($z as map(xs:string,item()*)) as xs:integer
{
  point:x($z)
}

Function: i
declare function i($z as map(xs:string,item()*)) as xs:integer


i()
Accessor for imaginary part of the complex number, as integer.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • xs:integer: imaginary part, rounded
declare function this:i($z as map(xs:string,item()*)) as xs:integer
{
  point:y($z)
}

Function: add
declare function add($z1 as map(xs:string,item()*), $z2 as map(xs:string,item()*)) as map(xs:string,item()*)


add()
Add two complex numbers.

Params
  • z1 as map(xs:string,item()*): one complex number
  • z2 as map(xs:string,item()*): another complex number
Returns
  • map(xs:string,item()*): z1+z2
declare function this:add(
  $z1 as map(xs:string,item()*),
  $z2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  $z1=>point:add($z2)
}

Function: sum
declare function sum($zs as map(xs:string,item()*)*) as map(xs:string,item()*)


sum()
Add several complex numbers in one go.

Params
  • zs as map(xs:string,item()*)*
Returns
  • map(xs:string,item()*)
declare function this:sum(
  $zs as map(xs:string,item()*)*
) as map(xs:string,item()*)
{
  point:sum($zs)
}

Function: sub
declare function sub($z1 as map(xs:string,item()*), $z2 as map(xs:string,item()*)) as map(xs:string,item()*)


sub()
Subtract one complex numbers from another.

Params
  • z1 as map(xs:string,item()*): the first complex number
  • z2 as map(xs:string,item()*): the complex number to subtract from the first complex number
Returns
  • map(xs:string,item()*): z1-z2
declare function this:sub(
  $z1 as map(xs:string,item()*),
  $z2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  $z1=>point:sub($z2)
}

Function: minus
declare function minus($z as map(xs:string,item()*)) as map(xs:string,item()*)


minus()
Negation

Params
  • z as map(xs:string,item()*)1 the first complex number
Returns
  • map(xs:string,item()*): -z1
declare function this:minus(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  $z=>point:times(-1)
}

Function: multiply
declare function multiply($z1 as map(xs:string,item()*), $z2 as map(xs:string,item()*)) as map(xs:string,item()*)


multiply()
Multiply two complex numbers.

Params
  • z1 as map(xs:string,item()*): one complex number
  • z2 as map(xs:string,item()*): another complex number
Returns
  • map(xs:string,item()*): z1*z2
declare function this:multiply(
  $z1 as map(xs:string,item()*),
  $z2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: (a + bi)*(c + di) = (ac - bd) + (ad + bc)i :)
  point:point(
    this:pr($z1)*this:pr($z2) - this:pi($z1)*this:pi($z2),
    this:pr($z1)*this:pi($z2) + this:pi($z1)*this:pr($z2)
  )
}

Function: pow
declare function pow($z as map(xs:string,item()*), $pow as xs:integer) as map(xs:string,item()*)


pow()
Raise a complex number to the given (positive integer) power.
A convenience function for repeated multiplication. For general powers
use ppow()

Params
  • z as map(xs:string,item()*): the complex number
  • pow as xs:integer: the power to raise it to
Returns
  • map(xs:string,item()*): z^pow
declare function this:pow(
  $z as map(xs:string,item()*),
  $pow as xs:integer
) as map(xs:string,item()*)
{
  if (this:pi($z)=0) then this:complex(math:pow(this:pr($z), $pow), 0)
  else
  switch ($pow)
  case 0 return $this:one
  case 1 return $z
  case 2 return $z=>this:multiply($z)
  case 3 return $z=>this:multiply($z)=>this:multiply($z)
  default return
    (: General: 
     : a + ib = r(cosφ + isinφ) r² = a² + b², φ = atan(b/a)
     : (a + ib)^N = r^N(cos(Nφ) + isin(Nφ))
     :)
    let $r := this:pr($z)*this:pr($z) + this:pi($z)*this:pi($z)
    let $φ := math:atan2(this:pi($z), this:pr($z))
    let $r_N := math:pow($r,$pow)
    return
      this:complex($r_N*math:cos($pow*$φ), $r_N*math:sin($pow*$φ))
}

Function: ppow
declare function ppow($z as map(xs:string,item()*), $pow as map(xs:string,item()*)) as map(xs:string,item()*)


ppow()
Principle power for non-integer powers.

Params
  • z as map(xs:string,item()*): the complex number
  • pow as map(xs:string,item()*): the power to raise it to
Returns
  • map(xs:string,item()*): z^pow
declare function this:ppow(
  $z as map(xs:string,item()*),
  $pow as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: a^b = e^(b log(a)) :)
  this:exp(this:log($z)=>this:multiply($pow))
}

Function: log
declare function log($z as map(xs:string,item()*)) as map(xs:string,item()*)


log()
Natural log of complex number.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • map(xs:string,item()*): log(z)
declare function this:log(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:complex(math:log(this:modulus($z)), this:angle($z)=>util:radians())
}

Function: sqrt
declare function sqrt($z as map(xs:string,item()*)) as map(xs:string,item()*)


sqrt()
Principal square root of complex number.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • map(xs:string,item()*): √z
declare function this:sqrt(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  if (this:pi($z) = 0) then (
    if (this:pr($z) >= 0) 
    then this:complex(math:sqrt(this:pr($z)), 0)
    else this:complex(0, math:sqrt(-this:pr($z)))
  ) else (
    let $r := this:modulus($z)
    return
      this:complex(
        math:sqrt(($r + this:pr($z)) div 2),
        util:sign(this:pi($z)) * math:sqrt(($r - this:pr($z)) div 2)
      )
  )
}

Function: cbrt
declare function cbrt($z as map(xs:string,item()*)) as map(xs:string,item()*)


cbrt()
Principal cubic root of a complex number. (Highest real part.)
Roots for n integer: for 0 ≤ k < n
z^(1/n) = r^(1/n)(cos((φ + 2kπ)/n) + i sin((φ + 2kπ)/n))

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • map(xs:string,item()*): z^1/3
declare function this:cbrt(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  if (this:pi($z) = 0) then (
    if (this:pr($z) >= 0)
    then this:complex(util:cbrt(this:pr($z)), 0)
    else this:complex(-util:cbrt(-this:pr($z)), 0)
  ) else (
    let $r := this:modulus($z)
    let $φ := this:angle($z)=>util:radians()
    let $rcbrt := util:cbrt($r)
    let $k := 
      head(for $k in 0 to 2
      order by math:cos(($φ + 2*$k*math:pi()) div 3) descending
      return $k)
    return (
      this:complex(
        $rcbrt * math:cos(($φ + 2*$k*math:pi()) div 3),
        $rcbrt * math:sin(($φ + 2*$k*math:pi()) div 3)
      )
    )
  )
}

Function: sin
declare function sin($z as map(xs:string,item()*)) as map(xs:string,item()*)


sin()
Sine of complex number.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • map(xs:string,item()*): sin(z)
declare function this:sin(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: 
   : sin(x + iy) = sin(x)cosh(y) + i cos(x)sinh(y)
   :)
  this:complex(
    math:sin(this:pr($z))*util:cosh(this:pi($z)),
    math:cos(this:pr($z))*util:sinh(this:pi($z))
  )
}

Function: cos
declare function cos($z as map(xs:string,item()*)) as map(xs:string,item()*)


cos()
Cosine of complex number.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • map(xs:string,item()*): cos(z)
declare function this:cos(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: 
   : cos(x + iy) = cos(x)cosh(y) - i sin(x)sinh(y)
   :)
  this:complex(
    math:cos(this:pr($z))*util:cosh(this:pi($z)),
    -math:sin(this:pr($z))*util:sinh(this:pi($z))
  )
}

Function: sinh
declare function sinh($z as map(xs:string,item()*)) as map(xs:string,item()*)


sinh()
Hyperbolic sine.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • map(xs:string,item()*): sinh(z)
declare function this:sinh($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  (: sinh(x + iy) = sinh(x)cos(y) + cosh(x)sin(y) :)
  this:complex(
    util:sinh(this:pr($z))*math:cos(this:pi($z)),
    util:cosh(this:pr($z))*math:sin(this:pi($z))
  )
}

Function: cosh
declare function cosh($z as map(xs:string,item()*)) as map(xs:string,item()*)


cosh()
Hyperbolic cosine.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • map(xs:string,item()*): cosh(z)
declare function this:cosh($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  (: cosh(x + iy) = cosh(x)cos(y) - sinh(x)sin(y) :)
  this:complex(
    util:cosh(this:pr($z))*math:cos(this:pi($z)),
    -util:sinh(this:pr($z))*math:sin(this:pi($z))
  )
}

Function: asin
declare function asin($z as map(xs:string,item()*)) as map(xs:string,item()*)


asin()
arcsin(z): -i ln(iz + √(1 - z²))

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • map(xs:string,item()*): asin(z)
declare function this:asin(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:log(
    this:multiply($this:i, $z)=>this:add(
      this:sqrt(
        $this:one=>this:sub(
          this:pow($z, 2)
        )
      )
    )
  )=>this:multiply(this:minus($this:i))
}

Function: acos
declare function acos($z as map(xs:string,item()*)) as map(xs:string,item()*)


acos()
arccos(z): (1/i) ln(z + √(z² - 1))

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • map(xs:string,item()*): acos(z)
declare function this:acos(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:log(
    $z=>this:add(
      this:sqrt(
        this:pow($z, 2)=>this:sub($this:one)
      )
    )
  )=>this:multiply( this:reciprocal($this:i) )
}

Function: asinh
declare function asinh($z as map(xs:string,item()*)) as map(xs:string,item()*)


asinh()
Inverse hyperbolic sine: asinh(z) = ln(z + √(z²+1))

Params
  • z as map(xs:string,item()*)
Returns
  • map(xs:string,item()*)
declare function this:asinh(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:log(
    $z=>this:add(
      this:sqrt(
        this:pow($z, 2)=>this:add($this:one)
      )
    )
  )
}

Function: acosh
declare function acosh($z as map(xs:string,item()*)) as map(xs:string,item()*)


acosh()
Inverse hyperbolic cosine: acosh(z) = ln(z + √(z²-1))

Params
  • z as map(xs:string,item()*)
Returns
  • map(xs:string,item()*)
declare function this:acosh(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:log(
    $z=>this:add(
      this:sqrt(
        this:pow($z, 2)=>this:sub($this:one)
      )
    )
  )
}

Function: exp
declare function exp($z as map(xs:string,item()*)) as map(xs:string,item()*)


exp()
Exponential of z: e^z

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • map(xs:string,item()*): e^z
declare function this:exp(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:complex(
    math:exp(this:pr($z)) * math:cos(this:pi($z)),
    math:exp(this:pr($z)) * math:sin(this:pi($z))
  )
}

Function: times
declare function times($z as map(xs:string,item()*), $k as xs:double) as map(xs:string,item()*)


times()
Multiply a complex by a constant.

Params
  • z as map(xs:string,item()*): the complex number
  • k as xs:double: the constant to multiply by
Returns
  • map(xs:string,item()*): k*z
declare function this:times(
  $z as map(xs:string,item()*),
  $k as xs:double
) as map(xs:string,item()*)
{
  $z=>point:times($k)
}

Function: plus
declare function plus($z as map(xs:string,item()*), $k as xs:double) as map(xs:string,item()*)


plus()
Add a constant to a complex number. (i.e. add complex number ($k, 0))

Params
  • z as map(xs:string,item()*): the complex number
  • k as xs:double: the constant to add
Returns
  • map(xs:string,item()*): z+k
declare function this:plus(
  $z as map(xs:string,item()*),
  $k as xs:double
) as map(xs:string,item()*)
{
  $z=>this:add(this:complex($k, 0))
}

Function: divide
declare function divide($z1 as map(xs:string,item()*), $z2 as map(xs:string,item()*)) as map(xs:string,item()*)


divide()
Divide two complex numbers.

Params
  • z1 as map(xs:string,item()*): one complex number
  • z2 as map(xs:string,item()*): another complex number
Returns
  • map(xs:string,item()*): z1/z2
declare function this:divide(
  $z1 as map(xs:string,item()*),
  $z2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: (a + bi)/(c + di) = ((ac + bd) + i(bc - ad))/(c² + d²) :)
  let $r2 := this:pr($z2)*this:pr($z2) + this:pi($z2)*this:pi($z2)
  return
    point:point(
      (this:pr($z1)*this:pr($z2) + this:pi($z1)*this:pi($z2)) div $r2,
      (this:pi($z1)*this:pr($z2) - this:pr($z1)*this:pi($z2)) div $r2
    )
}

Function: divide-by-i
declare function divide-by-i($z as map(xs:string,item()*)) as map(xs:string,item()*)


divide-by-i()
Divide complex numbers by i.

Params
  • z as map(xs:string,item()*)
Returns
  • map(xs:string,item()*): z/i
declare function this:divide-by-i(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: (a + bi)/(c + di) = ((ac + bd) + i(bc - ad))/(c² + d²) :)
  (: (a + bi)/(0 + 1i) = ((0a + b1) + i(b0 - a1))/(0² + 1²) = b - ai :)
  point:point( this:pi($z), -this:pr($z) )
}

Function: modulus
declare function modulus($z as map(xs:string,item()*)) as xs:double


modulus()
Modulus of a complex number. Given z = re^iφ, the r.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • xs:double: modulus
declare function this:modulus(
  $z as map(xs:string,item()*)
) as xs:double
{
  math:sqrt(this:pr($z)*this:pr($z) + this:pi($z)*this:pi($z))
}

Function: modsq
declare function modsq($z as map(xs:string,item()*)) as xs:double


modsq()
Modulus of a complex number, squared. Given z = re^iφ, r². Used for cases
where we want to avoid a lot of square root calculations.

Params
  • z as map(xs:string,item()*): the complex number
Returns
  • xs:double: modulus²
declare function this:modsq(
  $z as map(xs:string,item()*)
) as xs:double
{
  this:pr($z)*this:pr($z) + this:pi($z)*this:pi($z)
}

Function: angle
declare function angle($z as map(xs:string,item()*)) as xs:double


angle()
The angle of the complex number. Given z = re^iφ, the φ in degrees.

Params
  • z as map(xs:string,item()*): the complex numbers
Returns
  • xs:double: phase angle, in degrees
declare function this:angle(
  $z as map(xs:string,item()*)
) as xs:double
{
  math:atan2(this:pi($z), this:pr($z))=>util:degrees()=>util:remap-degrees()
}

Function: normalize
declare function normalize($z as map(xs:string,item()*)) as map(xs:string,item()*)


normalize()
Normalize the complex number so modulus is 1.

Params
  • z as map(xs:string,item()*): the complex number to normalize
Returns
  • map(xs:string,item()*): complex number
declare function this:normalize($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  let $l := this:modulus($z)
  return this:complex(this:pr($z) div $l, this:pi($z) div $l)
}

Function: is-gauss-prime
declare function is-gauss-prime($z as map(xs:string,item()*)) as xs:boolean


is-gauss-prime()
Test whether complex number is Gaussian prime; snaps to integral value.

Params
  • z as map(xs:string,item()*): complex number to test
Returns
  • xs:boolean: true if z is Gaussian prime
declare function this:is-gauss-prime($z as map(xs:string,item()*)) as xs:boolean
{
  let $a := this:r($z)
  let $b := this:i($z)
  return (
    if ($a * $b != 0) then (
     util:is-prime($a*$a + $b*$b)
    ) else (
      let $c := abs($a + $b)
      return util:is-prime($c) and $c mod 4 = 3
    )
  )
}

Function: conjugate
declare function conjugate($z as map(xs:string,item()*)) as map(xs:string,item()*)


conjugate()
Return the complex conjugate

Params
  • z as map(xs:string,item()*): complex number
Returns
  • map(xs:string,item()*): conj(z)
declare function this:conjugate($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  this:complex(this:pr($z), -this:pi($z))
}

Function: reciprocal
declare function reciprocal($z as map(xs:string,item()*)) as map(xs:string,item()*)


reciprocal()
The reciprocal of z: 1 / z

Params
  • z as map(xs:string,item()*): complex number
Returns
  • map(xs:string,item()*): 1/z
declare function this:reciprocal($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  let $r := this:pr($z)*this:pr($z) + this:pi($z)*this:pi($z)
  return
   this:complex(this:pr($z) div $r, -this:pi($z) div $r)
}

Function: abs
declare function abs($z as map(xs:string,item()*)) as map(xs:string,item()*)


abs()
The absolute value of z

Params
  • z as map(xs:string,item()*): complex number
Returns
  • map(xs:string,item()*): abs(z)
declare function this:abs($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  this:complex(abs(this:pr($z)), abs(this:pi($z)))
}

Function: quote
declare function quote($zs as map(xs:string,item()*)*) as xs:string


quote()
Return a string value of the complex numbers

Params
  • zs as map(xs:string,item()*)*: complex numbers
Returns
  • xs:string: string
declare function this:quote($zs as map(xs:string,item()*)*) as xs:string
{
  string-join(
    for $z in $zs return (
      let $r := this:pr($z)
      let $i := this:pi($z)
      return
        if ($r = 0 and $i = 0) then "0"
        else if ($r = 0) then $i||"i"
        else if ($i = 0) then string($r)
        else if ($i < 0) then $r||$i||"i"
        else $r||"+"||$i||"i"
    )
    ,
    " "
  )
}

Function: unquote
declare function unquote($zs as xs:string) as map(xs:string,item()*)


unquote()
Parse a string value of a complex number to produce that complex number.
Number is of the form 33+12i

Params
  • zs as xs:string: the string representation of the number
Returns
  • map(xs:string,item()*): complex number
declare function this:unquote($zs as xs:string) as map(xs:string,item()*)
{
  (: Cases:
   :  2
   : -2
   :     i
   :    3i
   :   -3i
   :  2+3i
   : -2+3i
   :  2-3i
   : -2-3i
   :  2+ i
   :  2- i
   : -2+ i
   : -2- i
   :)
  let $z := number($zs)
  return
    if ($z = $z) then this:complex($z, 0) (: 2 | -2 :)
    else if ($zs = "i") then this:complex(0, 1) (: i :)
    else (
      let $parts := analyze-string($zs, "[-+]?[0123456789.]+")
      return (
        switch (count($parts/fn:match))
        case 0 return
          if ($parts/fn:non-match=("i","+i"))
          then this:complex(0, 1)
          else if ($parts/fn:non-match="-i")
          then this:complex(0, -1)
          else errors:error("UTIL-BADCOMPLEX", $zs)
        case 1 return
          let $next := $parts/fn:match/following-sibling::fn:non-match[1]
          return
            if (empty($next))
            then this:complex(number($parts/fn:match), 0)
            else if ($next="+i")
            then this:complex(number($parts/fn:match), 1)
            else if ($next="-i")
            then this:complex(number($parts/fn:match), -1)
            else this:complex(0, number($parts/fn:match))
        case 2 return
          this:complex(number($parts/fn:match[1]), number($parts/fn:match[2]))
        default return errors:error("UTIL-BADCOMPLEX", $zs)
      )
    )
}

Function: eq
declare function eq($z as map(xs:string,item()*), $val as xs:double) as xs:boolean


eq()
Is the complex number equal to the double value? That is, does it have
no imaginary part and a real part equal to the value?

Params
  • z as map(xs:string,item()*): one complex number
  • val as xs:double: a double value
Returns
  • xs:boolean: real(z)=val and imaginary(z)=0 (within epsilon)
declare function this:eq($z as map(xs:string,item()*), $val as xs:double) as xs:boolean
{
  this:pr($z)=$val and this:is-real($z)
}

Function: same
declare function same($z1 as map(xs:string,item()*), $z2 as map(xs:string,item()*)) as xs:boolean


same()
Are the two complex numbers the same?

Params
  • z1 as map(xs:string,item()*): one complex number
  • z2 as map(xs:string,item()*): another complex number
Returns
  • xs:boolean: z1=z2
declare function this:same($z1 as map(xs:string,item()*), $z2 as map(xs:string,item()*)) as xs:boolean
{
  this:pr($z1)=this:pr($z2) and
  this:pi($z1)=this:pi($z2)
}

Function: decimal
declare function decimal($zs as map(xs:string,item()*)*, $digits as xs:integer) as map(xs:string,item()*)*


decimal()
Cast the values to a values with the given number of digits.

Params
  • zs as map(xs:string,item()*)*: the complex numbers
  • digits as xs:integer: how many digits to preserve
Returns
  • map(xs:string,item()*)*: complex numbers
declare function this:decimal($zs as map(xs:string,item()*)*, $digits as xs:integer) as map(xs:string,item()*)*
{
  point:decimal($zs, $digits)
}

Original Source Code

xquery version "3.1";
(:~
 : Complex numbers
 :
 : Copyright© Mary Holstege 2021-2023
 : CC-BY (https://creativecommons.org/licenses/by/4.0/)
 : @since October 2021
 : @custom:Status Stable
 :)
module namespace this="http://mathling.com/core/complex"; 

import module namespace config="http://mathling.com/core/config"
       at "../core/config.xqy";
import module namespace errors="http://mathling.com/core/errors"
       at "../core/errors.xqy";
import module namespace util="http://mathling.com/core/utilities"
       at "../core/utilities.xqy";
import module namespace point="http://mathling.com/geometric/point"
       at "../geo/point.xqy";

declare namespace map="http://www.w3.org/2005/xpath-functions/map";
declare namespace math="http://www.w3.org/2005/xpath-functions/math";

declare variable $this:i as map(xs:string,item()*) := this:complex(0, 1);
declare variable $this:one as map(xs:string,item()*) := this:complex(1, 0);

(:~
 : complex()
 : Construct a new complex number.
 :
 : @param $real: the real part of the number
 : @param $imaginary: the imaginary part of the number
 : @return complex number
 :)
declare function this:complex(
  $real as xs:double,
  $imaginary as xs:double
) as map(xs:string,item()*)
{
  point:point($real, $imaginary)
};

(:~
 : as-vector()
 : Cast to the vector form of a complex number.
 : @param $complex: complex number
 : @return complex number vector
 :)
declare function this:as-vector(
  $complex as map(xs:string,item()*)
) as xs:double*
{
  point:pcoordinates($complex)
};

(:~
 : vector()
 : Cast from the vector form of a complex number.
 : @param $complex: complex number vector
 : @return complex number
 :)
declare function this:vector(
  $complex as xs:double*
) as map(xs:string,item()*)
{
  point:vector($complex)
};
 
(:~
 : as-complex()
 : Cast a real number to a complex.
 :
 : @param $real: the real part of the number
 : @return complex number
 :)
declare function this:as-complex(
  $real as xs:double
) as map(xs:string,item()*)
{
  point:point($real, 0)
};

(:~
 : is-real()
 : Test whether the number has no imaginary part. (Within ε). 
 :
 : @param $z: the complex number
 : @return true if complex number has only a real part
 :)
declare function this:is-real(
  $z as map(xs:string,item()*)
) as xs:boolean
{
  abs(this:pi($z)) < $config:ε
};

(:~
 : as-real()
 : Cast a complex number to a double, if possible. Raises error if not.
 :
 : @param $z: the complex number
 : @return double
 : @error UTIL-CAST if there is an imaginary part
 :)
declare function this:as-real(
  $z as map(xs:string,item()*)
) as xs:double
{
  if (this:is-real($z)) then this:pr($z)
  else errors:error("UTIL-CAST", ($z,'complex'))
};

(:~
 : complex-rφd()
 : Construct a new complex number using radius and phase angle 
 : (polar repesentation re^iφ).
 :
 : @param $r: modulus of complex number
 : @param $φ: angle of complex number (degrees)
 : @return complex number
 :)
declare function this:complex-rφd(
  $r as xs:double,
  $φ as xs:double
) as map(xs:string,item()*)
{
  let $φR := util:radians($φ)
  return
    point:point($r * math:cos($φR), $r * math:sin($φR))
};

(:~
 : complex-rφ()
 : Construct a new complex number using radius and phase angle 
 : (polar repesentation re^iφ).
 :
 : @param $r: modulus of complex number
 : @param $φ: angle of complex number (radians)
 : @return complex number
 :)
declare function this:complex-rφ(
  $r as xs:double,
  $φ as xs:double
) as map(xs:string,item()*)
{
  point:point($r * math:cos($φ), $r * math:sin($φ))
};

(:~
 : pr()
 : Accessor for real part of the complex number.
 :
 : @param $z: the complex number
 : @return raw real part
 :)
declare function this:pr($z as map(xs:string,item()*)) as xs:double
{
  point:px($z)
};

(:~
 : pi()
 : Accessor for imaginary part of the complex number.
 :
 : @param $z: the complex number
 : @return raw imaginary part
 :)
declare function this:pi($z as map(xs:string,item()*)) as xs:double
{
  point:py($z)
};

(:~
 : r()
 : Accessor for real part of the complex number, as integer.
 :
 : @param $z: the complex number
 : @return real part, rounded
 :)
declare function this:r($z as map(xs:string,item()*)) as xs:integer
{
  point:x($z)
};

(:~
 : i()
 : Accessor for imaginary part of the complex number, as integer.
 :
 : @param $z: the complex number
 : @return imaginary part, rounded
 :)
declare function this:i($z as map(xs:string,item()*)) as xs:integer
{
  point:y($z)
};

(:~
 : add()
 : Add two complex numbers.
 :
 : @param $z1: one complex number
 : @param $z2: another complex number
 : @return z1+z2
 :)
declare function this:add(
  $z1 as map(xs:string,item()*),
  $z2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  $z1=>point:add($z2)
};

(:~
 : sum()
 : Add several complex numbers in one go.
 :)
declare function this:sum(
  $zs as map(xs:string,item()*)*
) as map(xs:string,item()*)
{
  point:sum($zs)
};

(:~
 : sub()
 : Subtract one complex numbers from another.
 :
 : @param $z1: the first complex number
 : @param $z2: the complex number to subtract from the first complex number
 : @return z1-z2
 :)
declare function this:sub(
  $z1 as map(xs:string,item()*),
  $z2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  $z1=>point:sub($z2)
};

(:~
 : minus()
 : Negation
 :
 : @param $z1 the first complex number
 : @return -z1
 :)
declare function this:minus(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  $z=>point:times(-1)
};

(:~
 : multiply()
 : Multiply two complex numbers.
 :
 : @param $z1: one complex number
 : @param $z2: another complex number
 : @return z1*z2
 :)
declare function this:multiply(
  $z1 as map(xs:string,item()*),
  $z2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: (a + bi)*(c + di) = (ac - bd) + (ad + bc)i :)
  point:point(
    this:pr($z1)*this:pr($z2) - this:pi($z1)*this:pi($z2),
    this:pr($z1)*this:pi($z2) + this:pi($z1)*this:pr($z2)
  )
};

(:~
 : pow()
 : Raise a complex number to the given (positive integer) power.
 : A convenience function for repeated multiplication. For general powers
 : use ppow()
 : 
 : @param $z: the complex number
 : @param $pow: the power to raise it to
 : @return z^pow
 :)
declare function this:pow(
  $z as map(xs:string,item()*),
  $pow as xs:integer
) as map(xs:string,item()*)
{
  if (this:pi($z)=0) then this:complex(math:pow(this:pr($z), $pow), 0)
  else
  switch ($pow)
  case 0 return $this:one
  case 1 return $z
  case 2 return $z=>this:multiply($z)
  case 3 return $z=>this:multiply($z)=>this:multiply($z)
  default return
    (: General: 
     : a + ib = r(cosφ + isinφ) r² = a² + b², φ = atan(b/a)
     : (a + ib)^N = r^N(cos(Nφ) + isin(Nφ))
     :)
    let $r := this:pr($z)*this:pr($z) + this:pi($z)*this:pi($z)
    let $φ := math:atan2(this:pi($z), this:pr($z))
    let $r_N := math:pow($r,$pow)
    return
      this:complex($r_N*math:cos($pow*$φ), $r_N*math:sin($pow*$φ))
};

(:~
 : ppow()
 : Principle power for non-integer powers.
 : @param $z: the complex number
 : @param $pow: the power to raise it to
 : @return z^pow
 :)
declare function this:ppow(
  $z as map(xs:string,item()*),
  $pow as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: a^b = e^(b log(a)) :)
  this:exp(this:log($z)=>this:multiply($pow))
};

(: https://math.stackexchange.com/questions/1103102/how-to-raise-1-to-non-integer-powers :)
(: http://www.milefoot.com/math/complex/functionsofi.htm :)
(: http://en.wikipedia.org/wiki/Complex_number :)

(:~
 : log()
 : Natural log of complex number.
 :
 : @param $z: the complex number
 : @return log(z)
 :)
declare function this:log(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:complex(math:log(this:modulus($z)), this:angle($z)=>util:radians())
};

(:~
 : sqrt()
 : Principal square root of complex number.
 :
 : @param $z: the complex number
 : @return √z
 :)
declare function this:sqrt(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  if (this:pi($z) = 0) then (
    if (this:pr($z) >= 0) 
    then this:complex(math:sqrt(this:pr($z)), 0)
    else this:complex(0, math:sqrt(-this:pr($z)))
  ) else (
    let $r := this:modulus($z)
    return
      this:complex(
        math:sqrt(($r + this:pr($z)) div 2),
        util:sign(this:pi($z)) * math:sqrt(($r - this:pr($z)) div 2)
      )
  )
};

(:~
 : cbrt()
 : Principal cubic root of a complex number. (Highest real part.)
 : Roots for n integer: for 0 ≤ k < n
 : z^(1/n) = r^(1/n)(cos((φ + 2kπ)/n) + i sin((φ + 2kπ)/n)) 
 :
 : @param $z: the complex number
 : @return z^1/3
 :)
declare function this:cbrt(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  if (this:pi($z) = 0) then (
    if (this:pr($z) >= 0)
    then this:complex(util:cbrt(this:pr($z)), 0)
    else this:complex(-util:cbrt(-this:pr($z)), 0)
  ) else (
    let $r := this:modulus($z)
    let $φ := this:angle($z)=>util:radians()
    let $rcbrt := util:cbrt($r)
    let $k := 
      head(for $k in 0 to 2
      order by math:cos(($φ + 2*$k*math:pi()) div 3) descending
      return $k)
    return (
      this:complex(
        $rcbrt * math:cos(($φ + 2*$k*math:pi()) div 3),
        $rcbrt * math:sin(($φ + 2*$k*math:pi()) div 3)
      )
    )
  )
};

(:~
 : sin()
 : Sine of complex number.
 :
 : @param $z: the complex number
 : @return sin(z)
 :)
declare function this:sin(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: 
   : sin(x + iy) = sin(x)cosh(y) + i cos(x)sinh(y)
   :)
  this:complex(
    math:sin(this:pr($z))*util:cosh(this:pi($z)),
    math:cos(this:pr($z))*util:sinh(this:pi($z))
  )
};

(:~
 : cos()
 : Cosine of complex number.
 :
 : @param $z: the complex number
 : @return cos(z)
 :)
declare function this:cos(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: 
   : cos(x + iy) = cos(x)cosh(y) - i sin(x)sinh(y)
   :)
  this:complex(
    math:cos(this:pr($z))*util:cosh(this:pi($z)),
    -math:sin(this:pr($z))*util:sinh(this:pi($z))
  )
};

(:~ 
 : sinh()
 : Hyperbolic sine.
 :
 : @param $z: the complex number
 : @return sinh(z)
 :)
declare function this:sinh($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  (: sinh(x + iy) = sinh(x)cos(y) + cosh(x)sin(y) :)
  this:complex(
    util:sinh(this:pr($z))*math:cos(this:pi($z)),
    util:cosh(this:pr($z))*math:sin(this:pi($z))
  )
};

(:~ 
 : cosh()
 : Hyperbolic cosine.
 :
 : @param $z: the complex number
 : @return cosh(z)
 :)
declare function this:cosh($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  (: cosh(x + iy) = cosh(x)cos(y) - sinh(x)sin(y) :)
  this:complex(
    util:cosh(this:pr($z))*math:cos(this:pi($z)),
    -util:sinh(this:pr($z))*math:sin(this:pi($z))
  )
};

(:~
 : asin()
 : arcsin(z): -i ln(iz + √(1 - z²))
 :
 : @param $z: the complex number
 : @return asin(z)
 :)
 declare function this:asin(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:log(
    this:multiply($this:i, $z)=>this:add(
      this:sqrt(
        $this:one=>this:sub(
          this:pow($z, 2)
        )
      )
    )
  )=>this:multiply(this:minus($this:i))
};

(:~
 : acos()
 : arccos(z): (1/i) ln(z + √(z² - 1))
 :
 : @param $z: the complex number
 : @return acos(z)
 :)
declare function this:acos(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:log(
    $z=>this:add(
      this:sqrt(
        this:pow($z, 2)=>this:sub($this:one)
      )
    )
  )=>this:multiply( this:reciprocal($this:i) )
};

(:~
 : asinh()
 : Inverse hyperbolic sine: asinh(z) = ln(z + √(z²+1))
 :)
declare function this:asinh(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:log(
    $z=>this:add(
      this:sqrt(
        this:pow($z, 2)=>this:add($this:one)
      )
    )
  )
};

(:~
 : acosh()
 : Inverse hyperbolic cosine: acosh(z) = ln(z + √(z²-1))
 :)
declare function this:acosh(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:log(
    $z=>this:add(
      this:sqrt(
        this:pow($z, 2)=>this:sub($this:one)
      )
    )
  )
};

(:~
 : exp()
 : Exponential of z: e^z
 :
 : @param $z: the complex number
 : @return e^z
 :)
declare function this:exp(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:complex(
    math:exp(this:pr($z)) * math:cos(this:pi($z)),
    math:exp(this:pr($z)) * math:sin(this:pi($z))
  )
};

(:~
 : times()
 : Multiply a complex by a constant.
 :
 : @param $z: the complex number
 : @param $k: the constant to multiply by
 : @return k*z
 :)
declare function this:times(
  $z as map(xs:string,item()*),
  $k as xs:double
) as map(xs:string,item()*)
{
  $z=>point:times($k)
};

(:~
 : plus()
 : Add a constant to a complex number. (i.e. add complex number ($k, 0))
 :
 : @param $z: the complex number
 : @param $k: the constant to add
 : @return z+k
 :)
declare function this:plus(
  $z as map(xs:string,item()*),
  $k as xs:double
) as map(xs:string,item()*)
{
  $z=>this:add(this:complex($k, 0))
};

(:~
 : divide()
 : Divide two complex numbers.
 :
 : @param $z1: one complex number
 : @param $z2: another complex number
 : @return z1/z2
 :)
declare function this:divide(
  $z1 as map(xs:string,item()*),
  $z2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: (a + bi)/(c + di) = ((ac + bd) + i(bc - ad))/(c² + d²) :)
  let $r2 := this:pr($z2)*this:pr($z2) + this:pi($z2)*this:pi($z2)
  return
    point:point(
      (this:pr($z1)*this:pr($z2) + this:pi($z1)*this:pi($z2)) div $r2,
      (this:pi($z1)*this:pr($z2) - this:pr($z1)*this:pi($z2)) div $r2
    )
};

(:~
 : divide-by-i()
 : Divide complex numbers by i.
 :
 : @param $z
 : @return z/i
 :)
declare function this:divide-by-i(
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: (a + bi)/(c + di) = ((ac + bd) + i(bc - ad))/(c² + d²) :)
  (: (a + bi)/(0 + 1i) = ((0a + b1) + i(b0 - a1))/(0² + 1²) = b - ai :)
  point:point( this:pi($z), -this:pr($z) )
};

(:~
 : modulus()
 : Modulus of a complex number. Given z = re^iφ, the r.
 :
 : @param $z: the complex number
 : @return modulus
 :)
declare function this:modulus(
  $z as map(xs:string,item()*)
) as xs:double
{
  math:sqrt(this:pr($z)*this:pr($z) + this:pi($z)*this:pi($z))
};

(:~
 : modsq()
 : Modulus of a complex number, squared. Given z = re^iφ, r². Used for cases
 : where we want to avoid a lot of square root calculations.
 :
 : @param $z: the complex number
 : @return modulus²
 :)
declare function this:modsq(
  $z as map(xs:string,item()*)
) as xs:double
{
  this:pr($z)*this:pr($z) + this:pi($z)*this:pi($z)
};

(:~
 : angle()
 : The angle of the complex number. Given z = re^iφ, the φ in degrees.
 :
 : @param $z: the complex numbers
 : @return phase angle, in degrees
 :)
declare function this:angle(
  $z as map(xs:string,item()*)
) as xs:double
{
  math:atan2(this:pi($z), this:pr($z))=>util:degrees()=>util:remap-degrees()
};

(:~
 : normalize()
 : Normalize the complex number so modulus is 1.
 :
 : @param $z: the complex number to normalize
 : @return complex number
 :)
declare function this:normalize($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  let $l := this:modulus($z)
  return this:complex(this:pr($z) div $l, this:pi($z) div $l)
};

(:~
 : is-gauss-prime()
 : Test whether complex number is Gaussian prime; snaps to integral value.
 :
 : @param $z: complex number to test
 : @return true if z is Gaussian prime
 :)
declare function this:is-gauss-prime($z as map(xs:string,item()*)) as xs:boolean
{
  let $a := this:r($z)
  let $b := this:i($z)
  return (
    if ($a * $b != 0) then (
     util:is-prime($a*$a + $b*$b)
    ) else (
      let $c := abs($a + $b)
      return util:is-prime($c) and $c mod 4 = 3
    )
  )
};

(:~
 : conjugate()
 : Return the complex conjugate
 :
 : @param $z: complex number
 : @return conj(z)
 :)
declare function this:conjugate($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  this:complex(this:pr($z), -this:pi($z))
};

(:~
 : reciprocal()
 : The reciprocal of z: 1 / z
 :
 : @param $z: complex number
 : @return 1/z
 :)
declare function this:reciprocal($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  let $r := this:pr($z)*this:pr($z) + this:pi($z)*this:pi($z)
  return
   this:complex(this:pr($z) div $r, -this:pi($z) div $r)
};

(:~
 : abs()
 : The absolute value of z
 :
 : @param $z: complex number
 : @return abs(z)
 :)
declare function this:abs($z as map(xs:string,item()*)) as map(xs:string,item()*)
{
  this:complex(abs(this:pr($z)), abs(this:pi($z)))
};

(:~
 : quote()
 : Return a string value of the complex numbers
 :
 : @param $zs: complex numbers
 : @return string
 :)
declare function this:quote($zs as map(xs:string,item()*)*) as xs:string
{
  string-join(
    for $z in $zs return (
      let $r := this:pr($z)
      let $i := this:pi($z)
      return
        if ($r = 0 and $i = 0) then "0"
        else if ($r = 0) then $i||"i"
        else if ($i = 0) then string($r)
        else if ($i < 0) then $r||$i||"i"
        else $r||"+"||$i||"i"
    )
    ,
    " "
  )
};

(:~
 : unquote()
 : Parse a string value of a complex number to produce that complex number.
 : Number is of the form 33+12i
 :
 : @param $zs: the string representation of the number
 : @return complex number
 :)
declare function this:unquote($zs as xs:string) as map(xs:string,item()*)
{
  (: Cases:
   :  2
   : -2
   :     i
   :    3i
   :   -3i
   :  2+3i
   : -2+3i
   :  2-3i
   : -2-3i
   :  2+ i
   :  2- i
   : -2+ i
   : -2- i
   :)
  let $z := number($zs)
  return
    if ($z = $z) then this:complex($z, 0) (: 2 | -2 :)
    else if ($zs = "i") then this:complex(0, 1) (: i :)
    else (
      let $parts := analyze-string($zs, "[-+]?[0123456789.]+")
      return (
        switch (count($parts/fn:match))
        case 0 return
          if ($parts/fn:non-match=("i","+i"))
          then this:complex(0, 1)
          else if ($parts/fn:non-match="-i")
          then this:complex(0, -1)
          else errors:error("UTIL-BADCOMPLEX", $zs)
        case 1 return
          let $next := $parts/fn:match/following-sibling::fn:non-match[1]
          return
            if (empty($next))
            then this:complex(number($parts/fn:match), 0)
            else if ($next="+i")
            then this:complex(number($parts/fn:match), 1)
            else if ($next="-i")
            then this:complex(number($parts/fn:match), -1)
            else this:complex(0, number($parts/fn:match))
        case 2 return
          this:complex(number($parts/fn:match[1]), number($parts/fn:match[2]))
        default return errors:error("UTIL-BADCOMPLEX", $zs)
      )
    )
};

(:~
 : eq()
 : Is the complex number equal to the double value? That is, does it have
 : no imaginary part and a real part equal to the value?
 :
 : @param $z: one complex number
 : @param $val: a double value
 : @return real(z)=val and imaginary(z)=0 (within epsilon)
 :)
declare function this:eq($z as map(xs:string,item()*), $val as xs:double) as xs:boolean
{
  this:pr($z)=$val and this:is-real($z)
};

(:~
 : same()
 : Are the two complex numbers the same?
 :
 : @param $z1: one complex number
 : @param $z2: another complex number
 : @return z1=z2
 :)
declare function this:same($z1 as map(xs:string,item()*), $z2 as map(xs:string,item()*)) as xs:boolean
{
  this:pr($z1)=this:pr($z2) and
  this:pi($z1)=this:pi($z2)
};

(:~
 : decimal()
 : Cast the values to a values with the given number of digits.
 :
 : @param $zs: the complex numbers
 : @param $digits: how many digits to preserve
 : @return complex numbers
 :)
declare function this:decimal($zs as map(xs:string,item()*)*, $digits as xs:integer) as map(xs:string,item()*)*
{
  point:decimal($zs, $digits)
};