packages feed

egison-5.1.0: lib/math/common/arithmetic.egi

--
--
-- Arithmetic Operation
--
--
declare symbol i, w, e, π: MathValue

def (+') : MathValue -> MathValue -> MathValue := i.+
def (-') : MathValue -> MathValue -> MathValue := i.-
def (*') : MathValue -> MathValue -> MathValue := i.*
def (/') : MathValue -> MathValue -> MathValue := i./

def plusForMathValue (x: MathValue) (y: MathValue) : MathValue :=
  mathNormalize (x +' y)

def minusForMathValue (x: MathValue) (y: MathValue) : MathValue :=
  mathNormalize (x -' y)

def multForMathValue (x: MathValue) (y: MathValue) : MathValue :=
  mathNormalize (x *' y)

def divForMathValue (x: MathValue) (y: MathValue) : MathValue :=
  x /' y

def sum {AddMonoid a} (xs: [a]) : a := foldl (+) zero xs
def sum' (xs: [MathValue]) : MathValue := foldl (+') 0 xs

def product {MulMonoid a} (xs: [a]) : a := foldl (*) one xs
def product' (xs: [MathValue]) : MathValue := foldl (*') 1 xs

def power (x: MathValue) (n: MathValue) : MathValue := mathNormalize (power' x n)
-- power' must avoid any operator that dispatches through mathNormalize so it
-- can be safely called from within `mathNormalize` itself (e.g. inside a
-- declare-rule RHS). foldl/take used `n - 1` whose `-` resolves to
-- `minusForMathValue` -> `mathNormalize` and creates a cycle. Use direct
-- recursion with `i.-` (integer subtraction primitive) and `*'` (the
-- un-normalised multiplication).
def power' (x: MathValue) (n: MathValue) : MathValue :=
  if n = 0
    then 1
    else x *' power' x (i.- n 1)

def exp (x: MathValue) : MathValue := 'exp x

def (^) (x: MathValue) (n: MathValue) : MathValue :=
  if x = e
    then exp n
    else if isRational n
      then if n >= 0
        then if isInteger n then power x n else '(^) x n
        else 1 / x ^ i.neg n
      else '(^) x n

def (^') (x: MathValue) (n: MathValue) : MathValue :=
  if x = e
    then exp n
    else if isRational n
      then if n >= 0
        then if isInteger n then power' x n else '(^) x n
        else 1 /' x ^' i.neg n
      else '(^) x n

def gcdForMathValue (x: MathValue) (y: MathValue) : MathValue :=
  match (x, y) as (termExpr, termExpr) with
    | (_, #0) -> x
    | (#0, _) -> y
    | (term $a $xs, term $b $ys) ->
      -- After Term widening (2026-05-06), `$a` and `$b` are statically MathValue.
      -- Only compute integer gcd when both are integers; otherwise fall back to 1.
      (if isInteger a && isInteger b then gcd' (i.abs a) (i.abs b) else 1)
        *' foldl (*') 1 (map (\(s, n) -> s ^' n) (AC.intersect xs ys))
    | _ -> 1  -- fallback when neither is in term form (e.g., level-3/4 raw poly after tower fix)

def gcd' (x: Integer) (y: Integer) : Integer :=
  match (x, y) as (integer, integer) with
    | (_, #0) -> x
    | (#0, _) -> y
    | (_, ?(>= x)) -> gcd' (i.modulo y x) x
    | (_, _) -> gcd' y x

def P./ fx gx x :=
  let xs := reverse (coefficients fx x)
      ys := reverse (coefficients gx x)
      (zs, rs) := L./ xs ys
   in ( sum' (map2 (\c n -> c *' x ^' n) (reverse zs) nats0)
      , sum' (map2 (\c n -> c *' x ^' n) (reverse rs) nats0) )