///|
// =====================================================================
// poly.mbt — 單變量多項式空間 P_n(係數陣列,索引 = 次數)
// 內積:⟨p,q⟩ = ∫₀¹ p(x)q(x) dx(精確計算,非數值積分)
// =====================================================================

/// 去掉高次零係數
fn poly_trim(p : Array[Double]) -> Array[Double] {
  let mut n = p.length()
  while n > 1 && p[n - 1] == 0.0 {
    n = n - 1
  }
  let out : Array[Double] = []
  for i = 0; i < n; i = i + 1 {
    out.push(p[i])
  }
  out
}

/// P-1 零多項式
pub fn poly_zero() -> Array[Double] {
  [0.0]
}

/// P-2 常數多項式
pub fn poly_const(c : Double) -> Array[Double] {
  [c]
}

/// P-3 次數(零多項式定為 0)
pub fn poly_degree(p : Array[Double]) -> Int {
  poly_trim(p).length() - 1
}

/// P-4 首項係數
pub fn poly_leading(p : Array[Double]) -> Double {
  let t = poly_trim(p)
  t[t.length() - 1]
}

/// P-5 加法
pub fn poly_add(a : Array[Double], b : Array[Double]) -> Array[Double] {
  let n = if a.length() > b.length() { a.length() } else { b.length() }
  let out = Array::make(n, 0.0)
  for i = 0; i < a.length(); i = i + 1 {
    out[i] = out[i] + a[i]
  }
  for i = 0; i < b.length(); i = i + 1 {
    out[i] = out[i] + b[i]
  }
  poly_trim(out)
}

/// P-6 減法
pub fn poly_sub(a : Array[Double], b : Array[Double]) -> Array[Double] {
  poly_add(a, poly_scale(-1.0, b))
}

/// P-7 純量乘法
pub fn poly_scale(c : Double, p : Array[Double]) -> Array[Double] {
  let out : Array[Double] = []
  for x in p {
    out.push(c * x)
  }
  poly_trim(out)
}

/// P-8 乘法(卷積)
pub fn poly_mul(a : Array[Double], b : Array[Double]) -> Array[Double] {
  let out = Array::make(a.length() + b.length() - 1, 0.0)
  for i = 0; i < a.length(); i = i + 1 {
    for j = 0; j < b.length(); j = j + 1 {
      out[i + j] = out[i + j] + a[i] * b[j]
    }
  }
  poly_trim(out)
}

/// P-9 導數
pub fn poly_deriv(p : Array[Double]) -> Array[Double] {
  if p.length() <= 1 {
    poly_zero()
  } else {
    let out : Array[Double] = []
    for i = 1; i < p.length(); i = i + 1 {
      out.push(i.to_double() * p[i])
    }
    poly_trim(out)
  }
}

/// P-10 不定積分(常數項為 0)
pub fn poly_integral(p : Array[Double]) -> Array[Double] {
  let out = Array::make(p.length() + 1, 0.0)
  for i = 0; i < p.length(); i = i + 1 {
    out[i + 1] = p[i] / (i.to_double() + 1.0)
  }
  poly_trim(out)
}

/// P-11 Horner 求值
pub fn poly_eval(p : Array[Double], x : Double) -> Double {
  let mut acc = 0.0
  for i = p.length() - 1; i >= 0; i = i - 1 {
    acc = acc * x + p[i]
  }
  acc
}

/// P-12 L²[0,1] 內積(精確):Σ aᵢbⱼ/(i+j+1)
pub fn poly_inner(a : Array[Double], b : Array[Double]) -> Double {
  let mut s = 0.0
  for i = 0; i < a.length(); i = i + 1 {
    for j = 0; j < b.length(); j = j + 1 {
      s = s + a[i] * b[j] / (i.to_double() + j.to_double() + 1.0)
    }
  }
  s
}

/// P-13 P_n 內積空間字典(次數 ≤ n)
pub fn poly_ip(n : Int) -> IPSp[Array[Double]] {
  ips_make(
    {
      zero: poly_zero(),
      add: poly_add,
      neg: fn(p) { poly_scale(-1.0, p) },
      scale: poly_scale,
    },
    poly_inner,
  )
}

/// P-14 移位 Legendre 多項式:Gram–Schmidt 於 {1,x,…,x^n}
pub fn shifted_legendre(n : Int) -> Array[Array[Double]] {
  let ip = poly_ip(n)
  let basis : Array[Array[Double]] = []
  for k = 0; k <= n; k = k + 1 {
    let m = Array::make(k + 1, 0.0)
    m[k] = 1.0
    basis.push(m)
  }
  ip_orthonormal(ip, basis, 1.0e-10)
}

/// P-15 帶餘除法:p = q·quot + rem(deg rem < deg q)
pub fn poly_divmod(
  p : Array[Double],
  q : Array[Double],
) -> (Array[Double], Array[Double]) {
  let mut rem = poly_trim(p.copy())
  let qt = poly_trim(q)
  let dq = poly_degree(qt)
  let lc = poly_leading(qt)
  let mut quot = poly_zero()
  while poly_degree(rem) >= dq && poly_leading(rem) != 0.0 {
    let d = poly_degree(rem) - dq
    let c = poly_leading(rem) / lc
    let term = Array::make(d + 1, 0.0)
    term[d] = c
    quot = poly_add(quot, term)
    rem = poly_sub(rem, poly_mul(term, qt))
  }
  (poly_trim(quot), poly_trim(rem))
}

/// P-16 餘式
pub fn poly_mod(p : Array[Double], q : Array[Double]) -> Array[Double] {
  poly_divmod(p, q).1
}

/// P-17 最大公因式(歐幾里得演算法,首一化)
pub fn poly_gcd(a : Array[Double], b : Array[Double]) -> Array[Double] {
  let mut x = poly_trim(a.copy())
  let mut y = poly_trim(b.copy())
  while poly_leading(y) != 0.0 {
    let r = poly_mod(x, y)
    x = y
    y = r
  }
  // 首一化
  let lc = poly_leading(x)
  poly_scale(1.0 / lc, x)
}

/// P-18 Lagrange 插值:過點 (xs[i], ys[i])
pub fn poly_lagrange(xs : Array[Double], ys : Array[Double]) -> Array[Double] {
  let n = xs.length()
  let mut out = poly_zero()
  for i = 0; i < n; i = i + 1 {
    // L_i(x) = Π_{j≠i} (x − x_j)/(x_i − x_j)
    let mut li = poly_const(1.0)
    let mut denom = 1.0
    for j = 0; j < n; j = j + 1 {
      if j != i {
        li = poly_mul(li, [-xs[j], 1.0])
        denom = denom * (xs[i] - xs[j])
      }
    }
    li = poly_scale(ys[i] / denom, li)
    out = poly_add(out, li)
  }
  out
}

/// P-19 Newton 插值(均差表)
pub fn poly_newton(xs : Array[Double], ys : Array[Double]) -> Array[Double] {
  let n = xs.length()
  let dd = ys.copy()
  let coeffs : Array[Double] = []
  for k = 0; k < n; k = k + 1 {
    coeffs.push(dd[k])
    for i = n - 1; i > k; i = i - 1 {
      dd[i] = (dd[i] - dd[i - 1]) / (xs[i] - xs[i - k - 1])
    }
  }
  // 展開 Π (x − x_i)
  let mut out = poly_zero()
  for k = 0; k < n; k = k + 1 {
    let mut basis = poly_const(1.0)
    for i = 0; i < k; i = i + 1 {
      basis = poly_mul(basis, [-xs[i], 1.0])
    }
    out = poly_add(out, poly_scale(coeffs[k], basis))
  }
  out
}

/// P-20 複合 p∘q
pub fn poly_compose(p : Array[Double], q : Array[Double]) -> Array[Double] {
  let mut acc = poly_zero()
  for i = p.length() - 1; i >= 0; i = i - 1 {
    acc = poly_add(poly_mul(acc, q), poly_const(p[i]))
  }
  acc
}

/// P-21 由根構造 Π(x − rᵢ)
pub fn poly_from_roots(rs : Array[Double]) -> Array[Double] {
  let mut out = poly_const(1.0)
  for r in rs {
    out = poly_mul(out, [-r, 1.0])
  }
  out
}

/// P-22 二次方程實/複根(回傳 (重數意義下) 兩複數根)
pub fn poly_roots_quadratic(a : Double, b : Double, c : Double) -> (C, C) {
  let disc = b * b - 4.0 * a * c
  if disc >= 0.0 {
    let sd = disc.sqrt()
    (cfrom_real((-b + sd) / (2.0 * a)), cfrom_real((-b - sd) / (2.0 * a)))
  } else {
    let sd = (-disc).sqrt()
    (
      cnew(-b / (2.0 * a), sd / (2.0 * a)),
      cnew(-b / (2.0 * a), -sd / (2.0 * a)),
    )
  }
}

/// P-23 Durand–Kerner 求全部複根(首一化後迭代)
pub fn poly_roots_dk(p : Array[Double], iters : Int) -> Array[C] {
  let t = poly_trim(p.copy())
  let n = poly_degree(t)
  let lc = poly_leading(t)
  let roots : Array[C] = []
  if n == 0 {
    return roots
  }
  // 初值:圓上分散點
  for k = 0; k < n; k = k + 1 {
    let ang = 2.0 * @math.PI * k.to_double() / n.to_double() + 0.4
    roots.push(cpolar(1.0 + 0.1 * k.to_double(), ang))
  }
  for _it = 0; _it < iters; _it = _it + 1 {
    for i = 0; i < n; i = i + 1 {
      // p(z_i)/lc
      let mut num = czero()
      for k = poly_degree(t); k >= 0; k = k - 1 {
        num = cadd(cmul(num, roots[i]), cfrom_real(t[k] / lc))
      }
      let mut den = cone()
      for j = 0; j < n; j = j + 1 {
        if j != i {
          den = cmul(den, csub(roots[i], roots[j]))
        }
      }
      roots[i] = csub(roots[i], cdiv(num, den))
    }
  }
  roots
}

/// P-24 多項式空間之維數(次數 ≤ n 者為 n+1)
pub fn poly_dim(n : Int) -> Int {
  n + 1
}