///|
/// Compute arctangent of `x`
///
/// # Examples
///
/// ```moonbit nocheck
/// assert_eq(atan(0), 0)
/// assert_eq(atan(1), 0.7853981633974483)
/// assert_eq(atan(2), 1.1071487177940904)
/// assert_eq(atan(-1),-0.7853981633974483)
/// assert_eq(atan(-2),-1.1071487177940904)
/// assert_eq(atan(@double.infinity), 1.5707963267948966)
/// assert_eq(atan(@double.neg_infinity), -1.5707963267948966)
/// ```
///
/// # Accuracy
///
/// 1 ulp (unit in the last place).
///
/// # Special Cases
/// 
/// 1. atan(NaN) = NaN
/// 2. atan(+Inf) = +π/2
/// 3. atan(-Inf) = -π/2
pub fn atan(x : Double) -> Double {
  if x.is_nan() || x == 0.0 {
    return x
  }
  let atan_hi = [
    4.63647609000806093515e-01, 7.85398163397448278999e-01, 9.82793723247329054082e-01,
    1.57079632679489655800e+00,
  ]
  let atan_lo = [
    2.26987774529616870924e-17, 3.06161699786838301793e-17, 1.39033110312309984516e-17,
    6.12323399573676603587e-17,
  ]
  let a_t = [
    3.33333333333329318027e-01, -1.99999999998764832476e-01, 1.42857142725034663711e-01,
    -1.11111104054623557880e-01, 9.09088713343650656196e-02, -7.69187620504482999495e-02,
    6.66107313738753120669e-02, -5.83357013379057348645e-02, 4.97687799461593236017e-02,
    -3.65315727442169155270e-02, 1.62858201153657823623e-02,
  ]
  let one = 1.0
  let huge = 1.0e300
  let ix = __hi(x).reinterpret_as_int() & 0x7fffffff
  let mut id = 0
  let mut z = 0.0
  let mut w = 0.0
  let mut x = x
  let x_is_neg = x < 0.0
  if ix >= 0x44100000 {
    if x > 0 {
      return atan_hi[3] + atan_lo[3]
    } else {
      return -atan_hi[3] - atan_lo[3]
    }
  }
  if ix < 0x3fdc0000 {
    if ix < 0x3e200000 {
      if huge + x > one {
        return x
      }
    }
    id = -1
  } else {
    x = x.abs()
    if ix < 0x3ff30000 {
      if ix < 0x3fe60000 {
        id = 0
        x = (2.0 * x - one) / (2.0 + x)
      } else {
        id = 1
        x = (x - one) / (x + one)
      }
    } else if ix < 0x40038000 {
      id = 2
      x = (x - 1.5) / (one + 1.5 * x)
    } else {
      id = 3
      x = -1.0 / x
    }
  }
  z = x * x
  w = z * z
  let s1 = z *
    (
      a_t[0] +
      w * (a_t[2] + w * (a_t[4] + w * (a_t[6] + w * (a_t[8] + w * a_t[10]))))
    )
  let s2 = w *
    (a_t[1] + w * (a_t[3] + w * (a_t[5] + w * (a_t[7] + w * a_t[9]))))
  if id < 0 {
    x - x * (s1 + s2)
  } else {
    z = atan_hi[id] - (x * (s1 + s2) - atan_lo[id] - x)
    if x_is_neg {
      -z
    } else {
      z
    }
  }
}

///|
test "atan" {
  fn assert_atan_ulp(input, expect) raise {
    assert_ulp(expect, atan(input), ATAN_MAX_ULP)
  }

  assert_atan_ulp(0, 0)
  assert_atan_ulp(1, 0.7853981633974483)
  assert_atan_ulp(2, 1.1071487177940906)
  assert_atan_ulp(3, 1.2490457723982544)
  assert_atan_ulp(4, 1.3258176636680326)
  assert_atan_ulp(5, 1.373400766945016)
  assert_atan_ulp(6, 1.4056476493802699)
  assert_atan_ulp(7, 1.4288992721907328)
  assert_atan_ulp(8, 1.446441332248135)
  assert_atan_ulp(9, 1.460139105621001)
  assert_atan_ulp(10, 1.4711276743037345)
  assert_atan_ulp(11, 1.4801364395941514)
  assert_atan_ulp(12, 1.4876550949064553)
  assert_atan_ulp(13, 1.4940244355251187)
  assert_atan_ulp(14, 1.4994888620096063)
  assert_atan_ulp(15, 1.5042281630190728)
  assert_atan_ulp(-1, -0.7853981633974483)
  assert_atan_ulp(-2, -1.1071487177940906)
  assert_atan_ulp(-3, -1.2490457723982544)
  assert_atan_ulp(-4, -1.3258176636680326)
  assert_atan_ulp(-5, -1.373400766945016)
  assert_atan_ulp(-6, -1.4056476493802699)
  assert_atan_ulp(-7, -1.4288992721907328)
  assert_atan_ulp(-8, -1.446441332248135)
  assert_atan_ulp(-9, -1.460139105621001)
  assert_atan_ulp(-10, -1.4711276743037345)
  assert_atan_ulp(-11, -1.4801364395941514)
  assert_atan_ulp(-12, -1.4876550949064553)
  assert_atan_ulp(-13, -1.4940244355251187)
  assert_atan_ulp(-14, -1.4994888620096063)
  assert_atan_ulp(-15, -1.5042281630190728)
  assert_atan_ulp(0.12, 0.11942892601833845)
  assert_atan_ulp(0.23, 0.2260683879938839)
  assert_atan_ulp(0.34, 0.3277385067805555)
  assert_atan_ulp(0.45, 0.4228539261329407)
  assert_atan_ulp(0.56, 0.5104883219167758)
  assert_atan_ulp(0.67, 0.590306746935372)
  assert_atan_ulp(0.78, 0.6624262938331512)
  assert_atan_ulp(0.89, 0.7272626879966904)
  assert_atan_ulp(0.9, 0.7328151017865067)
  assert_atan_ulp(-0.87, -0.7159911144163001)
  assert_atan_ulp(-0.76, -0.6498704494119476)
  assert_atan_ulp(-0.65, -0.5763752205911837)
  assert_atan_ulp(-0.54, -0.49513326346840414)
  assert_atan_ulp(-0.43, -0.40609805831761564)
  assert_atan_ulp(-0.32, -0.3097029445424562)
  assert_atan_ulp(-0.21, -0.206992194219821)
  assert_atan_ulp(-0.1, -0.09966865249116204)
  assert_atan_ulp(0.12345, 0.12282855068741544)
  assert_atan_ulp(0.23456, 0.2303949468807241)
  assert_atan_ulp(0.34567, 0.33281216252994195)
  assert_atan_ulp(0.45678, 0.42847785122558607)
  assert_atan_ulp(0.56789, 0.5164745097842471)
  assert_atan_ulp(0.6789, 0.5964240851188558)
  assert_atan_ulp(0.78901, 0.6680037059425237)
  assert_atan_ulp(0.89012, 0.7273296445553981)
  assert_atan_ulp(0.90123, 0.7334942443279985)
  assert_atan_ulp(-0.98765, -0.7791848758086322)
  assert_atan_ulp(-0.87654, -0.7197015466378203)
  assert_atan_ulp(-0.76543, -0.6533033928690293)
  assert_atan_ulp(-0.65432, -0.5794061351524672)
  assert_atan_ulp(-0.54321, -0.49761521700698697)
  assert_atan_ulp(-0.4321, -0.4078690083022377)
  assert_atan_ulp(-0.32109, -0.3106913832920783)
  assert_atan_ulp(-0.21098, -0.20793061638727955)
  assert_atan_ulp(-0.10987, -0.10943107915405893)
  assert_atan_ulp(1.234, 0.8897624489591893)
  assert_atan_ulp(2.345, 1.1677072684050145)
  assert_atan_ulp(3.456, 1.2891368759113806)
  assert_atan_ulp(4.567, 1.3552362032374148)
  assert_atan_ulp(5.678, 1.3964657470487327)
  assert_atan_ulp(6.789, 1.4245508462879888)
  assert_atan_ulp(7.89, 1.4447258005561798)
  assert_atan_ulp(8.901, 1.4589185265134277)
  assert_atan_ulp(9.012, 1.4602852545944818)
  assert_atan_ulp(10.123, 1.4723308430472324)
  assert_atan_ulp(11.234, 1.4820148389775378)
  assert_atan_ulp(12.345, 1.489968353486419)
  assert_atan_ulp(13.456, 1.4966163991626389)
  assert_atan_ulp(-1.234, -0.8897624489591893)
  assert_atan_ulp(-2.345, -1.1677072684050145)
  assert_atan_ulp(-3.456, -1.2891368759113806)
  assert_atan_ulp(-4.567, -1.3552362032374148)
  assert_atan_ulp(-5.678, -1.3964657470487327)
  assert_atan_ulp(-6.789, -1.4245508462879888)
  assert_atan_ulp(-7.89, -1.4447258005561798)
  assert_atan_ulp(-8.901, -1.4589185265134277)
  assert_atan_ulp(-9.012, -1.4602852545944818)
  assert_atan_ulp(-10.123, -1.4723308430472324)
  assert_atan_ulp(-11.234, -1.4820148389775378)
  assert_atan_ulp(-12.345, -1.489968353486419)
  assert_atan_ulp(-13.456, -1.4966163991626389)
  assert_atan_ulp(3.1415926, 1.2626272507486684)
  assert_atan_ulp(2.7182818, 1.2182829016248764)
  assert_atan_ulp(1.6180339, 1.0172219433679828)
  assert_atan_ulp(1.4142136, 0.9553166306668107)
  assert_atan_ulp(1.7320508, 1.0471975493043784)
  assert_atan_ulp(0.5772157, 0.5234978428250526)
  assert_atan_ulp(0.3010299, 0.2924013888286663)
  assert_atan_ulp(0.6931472, 0.6061119478640076)
  assert_atan_ulp(0.4342945, 0.40971675933419394)
  assert_atan_ulp(0.0000001, 0.00000009999999999999966)
  assert_atan_ulp(1000000000, 1.5707963257948967)
  assert_atan_ulp(31415926535897.1234, 1.5707963267948648)
  assert_atan_ulp(-27182818284590.5678, -1.57079632679486)
  assert_atan_ulp(@double.max_value, 1.5707963267948966)
  assert_atan_ulp(@double.min_value, -1.5707963267948966)
  assert_atan_ulp(@double.infinity, 1.5707963267948966)
  assert_atan_ulp(@double.neg_infinity, -1.5707963267948966)
  assert_atan_ulp(@double.not_a_number, @double.not_a_number)
}