///|
/// digamma(x) computes the digamma function of x.
///
/// digamma defined as $\psi(x) = \frac{d}{dx} \log \Gamma(x)$
///
/// # Reference
///
/// `boost::special_functions::digamma`
///
/// # Examples
///
/// ```moonbit nocheck
/// assert_eq(digamma(1.0), -0.5772156649015328);
/// assert_eq(digamma(2.0), 0.422784335098467);
/// assert_eq(digamma(-0.5), 0.036489973978576895);
/// ```
///
/// # Special Cases
///
/// 1. digamma(0) = -∞
/// 2. digamma(x) = nan for x < 0 and x is an integer
/// 3. digamma(inf) = inf
/// 4. digamma(-inf) = nan
/// 3. digamma(nan) = nan
///
/// # Accuracy
///
/// 1023 ulp.
pub fn digamma(x : Double) -> Double {
  let pi : Double = 3.14159265358979323846
  let pos_inf : Double = 0x7ff0_0000_0000_0000UL.reinterpret_as_double()
  let mut x = x
  if x == 0.0 {
    return copysign(pos_inf, -x)
  }
  let mut result = 0.0
  if x < 0.0 {
    let x_is_integer = x == x.trunc()
    if x_is_integer {
      return @double.not_a_number
    }
    let r = x - x.trunc()
    result = -pi / tan(pi * r)
    x = 1.0 - x
  }
  while x < 10.0 {
    result -= 1.0 / x
    x += 1.0
  }
  if x == 10.0 {
    return result + 2.25175258906672110764
  }
  let mut y = 0.0
  if x < 1.0e17 {
    let a = [
      8.33333333333333333333E-2, -2.10927960927960927961E-2, 7.57575757575757575758E-3,
      -4.16666666666666666667E-3, 3.96825396825396825397E-3, -8.33333333333333333333E-3,
      8.33333333333333333333E-2,
    ]
    let z = 1.0 / (x * x)
    let mut polevl_result = 0.0
    for i = 0; i <= 6; i = i + 1 {
      polevl_result = polevl_result * z + a[i]
    }
    y = z * polevl_result
  }
  log(x) - 0.5 / x - y + result
}

///|
test "digamma" {
  fn assert_digamma_ulp(input, expect) raise {
    assert_ulp(expect, digamma(input), DIGAMMA_MAX_ULP)
  }

  assert_digamma_ulp(-0.8, -4.039039896592188)
  assert_digamma_ulp(-0.7, -2.073952793628704)
  assert_digamma_ulp(-0.6, -0.8947178779184495)
  assert_digamma_ulp(-0.5, 0.0364899739785769)
  assert_digamma_ulp(-0.4, 0.9593807861068102)
  assert_digamma_ulp(-0.3, 2.113309779635399)
  assert_digamma_ulp(-0.2, 4.034991433293861)
  assert_digamma_ulp(-0.1, 9.245073050052952)
  assert_digamma_ulp(-0, @double.infinity)
  assert_digamma_ulp(-3.141592653589793, 7.885952385385497)
  assert_digamma_ulp(-1.570796326794897, 0.0268072380899067)
  assert_digamma_ulp(-0.7853981633974483, -3.657242587377795)
  assert_digamma_ulp(0, @double.neg_infinity)
  assert_digamma_ulp(0.1, -10.42375494041108)
  assert_digamma_ulp(0.2, -5.289039896592188)
  assert_digamma_ulp(0.3, -3.502524222200133)
  assert_digamma_ulp(0.4, -2.561384544585115)
  assert_digamma_ulp(0.5, -1.963510026021423)
  assert_digamma_ulp(0.6, -1.540619213893191)
  assert_digamma_ulp(0.7, -1.220023553697935)
  assert_digamma_ulp(0.8, -0.9650085667061385)
  assert_digamma_ulp(0.9, -0.7549269499470515)
  assert_digamma_ulp(1, -0.5772156649015328)
  assert_digamma_ulp(3.141592653589793, 0.9772133079420071)
  assert_digamma_ulp(1.570796326794897, 0.1006733764274022)
  assert_digamma_ulp(0.7853981633974483, -0.9990655126841155)
  assert_digamma_ulp(-1, @double.not_a_number)
  assert_digamma_ulp(-2, @double.not_a_number)
  assert_digamma_ulp(-3, @double.not_a_number)
  assert_digamma_ulp(-4, @double.not_a_number)
  assert_digamma_ulp(-5, @double.not_a_number)
  assert_digamma_ulp(-6, @double.not_a_number)
  assert_digamma_ulp(-7, @double.not_a_number)
  assert_digamma_ulp(-8, @double.not_a_number)
  assert_digamma_ulp(-9, @double.not_a_number)
  assert_digamma_ulp(1, -0.5772156649015328)
  assert_digamma_ulp(2, 0.422784335098467)
  assert_digamma_ulp(3, 0.922784335098467)
  assert_digamma_ulp(4, 1.2561176684318)
  assert_digamma_ulp(5, 1.5061176684318)
  assert_digamma_ulp(6, 1.7061176684318)
  assert_digamma_ulp(7, 1.872784335098467)
  assert_digamma_ulp(8, 2.01564147795561)
  assert_digamma_ulp(9, 2.14064147795561)
  assert_digamma_ulp(10, 2.251752589066721)
  assert_digamma_ulp(100, 4.600161852738088)
  assert_digamma_ulp(1000, 6.907255195648812)
  assert_digamma_ulp(10000, 9.21029037114285)
  assert_digamma_ulp(2.5, 0.7031566406452434)
  assert_digamma_ulp(3.4, 1.069567836367265)
  assert_digamma_ulp(5.3, 1.570410931624808)
  assert_digamma_ulp(6.2, 1.74174182168953)
  assert_digamma_ulp(7.1, 1.888022386658046)
  assert_digamma_ulp(8.9, 2.128820766044583)
  assert_digamma_ulp(9.800000000000001, 2.230495182536802)
  assert_digamma_ulp(10.7, 2.322787537024018)
  assert_digamma_ulp(101.6, 4.616114202447252)
  assert_digamma_ulp(1.542, 0.07503762854823659)
  assert_digamma_ulp(2.846, 0.8600612578163895)
  assert_digamma_ulp(7.881, 1.999671515765365)
  assert_digamma_ulp(3.772, 1.189232567530244)
  assert_digamma_ulp(-1.542, 0.3066001878215694)
  assert_digamma_ulp(-2.846, -4.767348874721767)
  assert_digamma_ulp(-7.881, -5.881611053411542)
  assert_digamma_ulp(-3.772, -2.154553439810062)
  assert_digamma_ulp(-1, @double.not_a_number)
  assert_digamma_ulp(0, @double.neg_infinity)
  assert_digamma_ulp(-0, @double.infinity)
  assert_digamma_ulp(@double.not_a_number, @double.not_a_number)
  assert_digamma_ulp(@double.infinity, @double.infinity)
  assert_digamma_ulp(@double.neg_infinity, @double.not_a_number)
}