///|
// =====================================================================
// mpoly.mbt — 多變量多項式空間 R[x_0,…,x_{n-1}]
// 表示:項之陣列 (指數向量, 係數),按字典序正規化
// =====================================================================

/// 多變量多項式
pub struct MPoly {
  nvars : Int
  terms : Array[(Array[Int], Double)]
}

/// 指數向量字典序比較
fn expo_cmp(a : Array[Int], b : Array[Int]) -> Int {
  let mut i = 0
  while i < a.length() && a[i] == b[i] {
    i = i + 1
  }
  if i == a.length() {
    0
  } else if a[i] < b[i] {
    -1
  } else {
    1
  }
}

/// MP-1 正規化:排序、合併同類項、去零
pub fn mpoly_normalize(
  nvars : Int,
  terms : Array[(Array[Int], Double)],
) -> MPoly {
  let t = terms.copy()
  // 插入排序(按指數向量字典序)
  for i = 1; i < t.length(); i = i + 1 {
    let key = t[i]
    let mut j = i - 1
    while j >= 0 && expo_cmp(t[j].0, key.0) > 0 {
      t[j + 1] = t[j]
      j = j - 1
    }
    t[j + 1] = key
  }
  let out : Array[(Array[Int], Double)] = []
  for term in t {
    if term.1 != 0.0 {
      if out.length() > 0 && expo_cmp(out[out.length() - 1].0, term.0) == 0 {
        let last = out[out.length() - 1]
        out[out.length() - 1] = (last.0, last.1 + term.1)
      } else {
        out.push(term)
      }
    }
  }
  // 合併後可能產生零
  let final : Array[(Array[Int], Double)] = []
  for term in out {
    if term.1 != 0.0 {
      final.push(term)
    }
  }
  { nvars, terms: final }
}

/// MP-2 零多項式
pub fn mpoly_zero(nvars : Int) -> MPoly {
  { nvars, terms: [] }
}

/// MP-3 單項式 c·x^e
pub fn mpoly_monomial(e : Array[Int], c : Double) -> MPoly {
  mpoly_normalize(e.length(), [(e.copy(), c)])
}

/// MP-4 加法
pub fn mpoly_add(a : MPoly, b : MPoly) -> MPoly {
  let terms : Array[(Array[Int], Double)] = []
  for t in a.terms {
    terms.push(t)
  }
  for t in b.terms {
    terms.push(t)
  }
  mpoly_normalize(a.nvars, terms)
}

/// MP-5 純量乘法
pub fn mpoly_scale(c : Double, p : MPoly) -> MPoly {
  let terms : Array[(Array[Int], Double)] = []
  for t in p.terms {
    terms.push((t.0, c * t.1))
  }
  mpoly_normalize(p.nvars, terms)
}

/// MP-6 乘法
pub fn mpoly_mul(a : MPoly, b : MPoly) -> MPoly {
  let terms : Array[(Array[Int], Double)] = []
  for ta in a.terms {
    for tb in b.terms {
      let e : Array[Int] = []
      for i = 0; i < a.nvars; i = i + 1 {
        e.push(ta.0[i] + tb.0[i])
      }
      terms.push((e, ta.1 * tb.1))
    }
  }
  mpoly_normalize(a.nvars, terms)
}

/// MP-7 單項式求值
pub fn monomial_eval(e : Array[Int], x : Array[Double]) -> Double {
  let mut p = 1.0
  for i = 0; i < e.length(); i = i + 1 {
    for _k = 0; _k < e[i]; _k = _k + 1 {
      p = p * x[i]
    }
  }
  p
}

/// MP-8 多項式求值
pub fn mpoly_eval(p : MPoly, x : Array[Double]) -> Double {
  let mut s = 0.0
  for t in p.terms {
    s = s + t.1 * monomial_eval(t.0, x)
  }
  s
}

/// MP-9 偏導 ∂/∂x_i
pub fn mpoly_partial(p : MPoly, i : Int) -> MPoly {
  let terms : Array[(Array[Int], Double)] = []
  for t in p.terms {
    if t.0[i] > 0 {
      let e = t.0.copy()
      e[i] = e[i] - 1
      terms.push((e, t.1 * t.0[i].to_double()))
    }
  }
  mpoly_normalize(p.nvars, terms)
}

/// MP-10 梯度(全部偏導)
pub fn mpoly_gradient(p : MPoly) -> Array[MPoly] {
  let out : Array[MPoly] = []
  for i = 0; i < p.nvars; i = i + 1 {
    out.push(mpoly_partial(p, i))
  }
  out
}

/// MP-11 總次數
pub fn mpoly_total_degree(p : MPoly) -> Int {
  let mut d = 0
  for t in p.terms {
    let mut s = 0
    for e in t.0 {
      s = s + e
    }
    if s > d {
      d = s
    }
  }
  d
}

/// MP-12 係數內積(單項式基下之 ℓ² 內積)
pub fn mpoly_inner(a : MPoly, b : MPoly) -> Double {
  let mut s = 0.0
  let mut i = 0
  let mut j = 0
  while i < a.terms.length() && j < b.terms.length() {
    let c = expo_cmp(a.terms[i].0, b.terms[j].0)
    if c == 0 {
      s = s + a.terms[i].1 * b.terms[j].1
      i = i + 1
      j = j + 1
    } else if c < 0 {
      i = i + 1
    } else {
      j = j + 1
    }
  }
  s
}

/// MP-13 單項式計數:n 變量次數 ≤ d 之單項式數 = C(n+d, d)
pub fn monomial_count(n : Int, d : Int) -> Int {
  // C(n+d, d) 以乘法公式計算
  let mut num = 1.0
  let mut den = 1.0
  for k = 1; k <= d; k = k + 1 {
    num = num * (n + k).to_double()
    den = den * k.to_double()
  }
  (num / den + 0.5).trunc().to_int()
}

/// MP-14 多項式空間之內積字典(限制於次數 ≤ d 之子空間)
pub fn mpoly_ip(nvars : Int) -> IPSp[MPoly] {
  ips_make(
    {
      zero: mpoly_zero(nvars),
      add: mpoly_add,
      neg: fn(p) { mpoly_scale(-1.0, p) },
      scale: mpoly_scale,
    },
    mpoly_inner,
  )
}

/// MP-15 齊次化:引入新變量 x_n 使總次數齊次
pub fn mpoly_homogenize(p : MPoly) -> MPoly {
  let d = mpoly_total_degree(p)
  let terms : Array[(Array[Int], Double)] = []
  for t in p.terms {
    let mut s = 0
    for e in t.0 {
      s = s + e
    }
    let e = t.0.copy()
    e.push(d - s)
    terms.push((e, t.1))
  }
  mpoly_normalize(p.nvars + 1, terms)
}