egison-5.1.0: lib/math/algebra/root.egi
--
--
-- Algebra
--
--
--
-- Root
--
declare mathfunc rt
declare apply rt n x :=
if isInteger n
then
if n = 1
then x
else
match x as mathValue with
| #0 -> 0
| ?isMonomial -> rtMonomial n x
| poly $xs / poly $ys ->
let xd := reduce gcdForMathValue xs
yd := reduce gcdForMathValue ys
d := rtMonomial n (xd / yd)
in d *' rt'' n (sum' (map (/' xd) xs) /' sum' (map (/' yd) ys))
| _ -> rt'' n x
else rt'' n x
def rtMonomial (n: MathValue) (x: MathValue) : MathValue :=
rtTerm n (numerator x * denominator x ^ (n - 1)) / denominator x
def rtTerm (n: MathValue) (x: MathValue) : MathValue :=
match x as termExpr with
| term $a _ ->
let rtm1 (n: MathValue) : MathValue := match n as integer with
| #1 -> -1
| #2 -> i
| ?isOdd -> -1
| _ -> undefined
in if a < 0 then rtm1 n *' rtPositiveTerm n (- x) else rtPositiveTerm n x
def rtPositiveTerm (n: MathValue) (x: MathValue) : MathValue :=
match (n, x) as (mathValue, mathValue) with
| (#3, $a * #i * $r) -> (- i) * rt 3 (a *' r)
| (_, $a * (apply1 #sqrt $b) * $r) -> rt (n * 2) (a ^' 2 *' b) *' rt n r
| (_, $a * (apply2 #rt $n' $b) * $r) -> rt (n * n') (a ^' n' *' b) *' rt n r
| (_, _) -> rtPositiveTerm1 n x
where
rtPositiveTerm1 (n: MathValue) (x: MathValue) : MathValue :=
let f (xs: [(MathValue, MathValue)]) : (MathValue, MathValue) :=
match xs as assocMultiset mathValue with
| [] -> (1, 1)
| ($p, $k) :: $rs ->
let (a, b) := f rs
in (p ^' i.quotient k n *' a, p ^' (k % n) *' b)
g (n: MathValue) (x: MathValue) : MathValue :=
let d := match x as termExpr with
| term $m $xs ->
gcdForMathValue n (reduce gcdForMathValue (map snd (toAssoc (pF m) ++ xs)))
in rt'' (n / d) (rt d x)
in match x as termExpr with
| term $m $xs ->
match f (toAssoc (pF (abs m)) ++ xs) as (integer, integer) with
| ($a, #1) -> a
| ($a, $b) -> a *' g n b
def rt'' (n: MathValue) (x: MathValue) : MathValue :=
match (n, x) as (integer, integer) with
| (#2, _) ->
-- Principal-branch normalization for constant radicands
-- (design/cas-simplification.md 3.7, option A): a symbol-free
-- radicand certified negative by interval arithmetic becomes
-- i * sqrt(-x). Every surviving sqrt atom then has a positive
-- radicand, which makes the pair merge sqrt a * sqrt b =
-- sqrt (a b) sound on principal branches (the formal merge is
-- off by a sign when both radicands are negative).
if signOfConst x = "neg"
then i *' sqrt (- x)
else sqrtDenest x
| (_, _) -> 'rt n x
-- Depth-2 denesting (design/cas-simplification.md 3.6; the classic
-- Borodin-Fagin-Hopcroft-Tompa condition):
-- sqrt (a + b sqrt c), with integers a > 0, b /= 0, c > 0 and
-- a^2 - b^2 c = d^2 a perfect square, denests to
-- sqrt ((a+d)/2) + sign(b) * sqrt ((a-d)/2).
-- Example: sqrt (9 - 4 sqrt 5) = sqrt 5 - 2. Anything outside this
-- shape (or with a non-square a^2 - b^2 c, e.g. sqrt (-10 - 2 sqrt 5))
-- stays symbolic. The perfect-square test factorizes a^2 - b^2 c, so
-- it is guarded by a size cap.
def sqrtDenest (x: MathValue) : MathValue :=
match x as mathValue with
| poly [term $a [], term $b [(apply1 #sqrt $c, #1)]] ->
if isInteger a && isInteger b && isInteger c && a > 0 && c > 0
then
let r := a^2 - b^2 * c
in if r > 0 && r <= 1000000000000
then sqrtDenest' x a b r
else 'sqrt x
else 'sqrt x
| _ -> 'sqrt x
def sqrtDenest' (x: MathValue) (a: MathValue) (b: MathValue) (r: MathValue) : MathValue :=
let facs := toAssoc (pF r)
in if all (\(_, k) -> i.modulo k 2 = 0) facs
then
let d := foldl (\acc (p, k) -> acc * p ^ (i.quotient k 2)) 1 facs
s := if b > 0 then 1 else -1
in sqrt ((a + d) / 2) + s * sqrt ((a - d) / 2)
else 'sqrt x
-- sqrt is split into `declare mathfunc` (declares the function name and
-- registers default symbolic behaviour) plus `declare apply` (the
-- algorithmic simplification at application time). Pattern rewrites such
-- as (sqrt $x)^2 = x live separately as `declare rule auto` declarations
-- in lib/math/normalize.egi.
declare mathfunc sqrt
declare apply sqrt x :=
let m := numerator x
n := denominator x
in rt 2 (m *' n) /' n
declare mathfunc rtu
declare apply rtu n :=
if isInteger n
then match n as integer with
| #1 -> 1
| #2 -> -1
| #3 -> w
| #4 -> i
| _ -> 'rtu n
else 'rtu n
def rtOfUnity : MathValue -> MathValue := rtu