///|
/// Cartesian complex value. Values admitted to a Network must be finite.
pub(all) struct Complex {
  re : Double
  im : Double
} derive(ToJson)

///|
pub extend Complex with ToJson::{to_json}

///|
pub fn complex(re : Double, im? : Double = 0.0) -> Complex {
  { re, im, }
}

///|
pub fn Complex::add(a : Complex, b : Complex) -> Complex {
  { re: a.re + b.re, im: a.im + b.im, }
}

///|
pub fn Complex::sub(a : Complex, b : Complex) -> Complex {
  { re: a.re - b.re, im: a.im - b.im, }
}

///|
pub fn Complex::mul(a : Complex, b : Complex) -> Complex {
  { re: a.re * b.re - a.im * b.im, im: a.re * b.im + a.im * b.re, }
}

///|
pub fn Complex::scale(a : Complex, x : Double) -> Complex {
  { re: a.re * x, im: a.im * x, }
}

///|
pub fn Complex::conjugate(a : Complex) -> Complex {
  { re: a.re, im: -a.im, }
}

///|
pub fn Complex::magnitude(a : Complex) -> Double {
  @math.hypot(a.re, a.im)
}

///|
pub fn Complex::phase(a : Complex) -> Double {
  @math.atan2(a.im, a.re)
}

///|
fn finite(x : Double) -> Bool {
  !x.is_nan() && !x.is_inf()
}

///|
fn checked(z : Complex) -> Complex raise {
  if !finite(z.re) || !finite(z.im) {
    raise Failure("non-finite complex result")
  }
  z
}

///|
/// Scaled division avoids squaring the denominator. Overflow is reported,
/// never silently installed in a network. Zero is not replaced by an epsilon.
pub fn Complex::divide(a : Complex, b : Complex) -> Complex raise {
  if !finite(a.re) || !finite(a.im) || !finite(b.re) || !finite(b.im) {
    raise Failure("division requires finite complex values")
  }
  let scale = b.re.abs().max(b.im.abs())
  if scale == 0.0 {
    raise Failure("zero complex denominator")
  }
  let br = b.re / scale
  let bi = b.im / scale
  let ar = a.re / scale
  let ai = a.im / scale
  let den = br * br + bi * bi
  checked({ re: (ar * br + ai * bi) / den, im: (ai * br - ar * bi) / den, })
}