-- |
-- Module: Symtegration.Symbolic.Simplify.Fraction
-- Description: Cancel out common numeric factors in fractions.
-- Copyright: Copyright 2025 Yoo Chung
-- License: Apache-2.0
-- Maintainer: dev@chungyc.org
module Symtegration.Symbolic.Simplify.Fraction (simplify) where

import Symtegration.Symbolic

-- $setup
-- >>> import Symtegration.Symbolic
-- >>> import Symtegration.Symbolic.Haskell

-- | Cancel out common numeric factors in fractions.
--
-- >>> toHaskell $ simplify $ 10 / 20
-- "1 / 2"
--
-- >>> toHaskell $ simplify $ Number (-15) / Number (-10)
-- "3 / 2"
--
-- >>> toHaskell $ simplify $ (12 * "x") / (4 * "y")
-- "(3 * x) / (1 * y)"
--
-- >> toHaskell $ simplify $ (15 * "x" + 20 * "y") / (5 * "z" - 35 * "u")
-- "(3 * x + 4 * y) / (1 * z - 7 * u)"
--
-- Assumes numeric folding and algebraic ring ordering has been applied.
simplify :: Expression -> Expression
simplify :: Expression -> Expression
simplify e :: Expression
e@(Expression
_ :/: Number Integer
0) = Expression
e
simplify (Number Integer
n :/: Number Integer
m)
  | Integer
m Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> Integer
0 = Integer -> Expression
Number (Integer
n Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
g) Expression -> Expression -> Expression
:/: Integer -> Expression
Number (Integer
m Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
g)
  | Bool
otherwise = Integer -> Expression
Number ((-Integer
n) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
g) Expression -> Expression -> Expression
:/: Integer -> Expression
Number ((-Integer
m) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
g)
  where
    g :: Integer
g = Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
gcd Integer
n Integer
m
simplify (Expression
x :/: Expression
y) = Integer -> Expression -> Expression
divideFactor Integer
g Expression
x' Expression -> Expression -> Expression
:/: Integer -> Expression -> Expression
divideFactor Integer
g Expression
y'
  where
    g :: Integer
g
      | (Number Integer
n) <- Expression
y, Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Integer
0 = Integer -> Integer
forall a. Num a => a -> a
negate (Integer -> Integer) -> Integer -> Integer
forall a b. (a -> b) -> a -> b
$ Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
gcd (Expression -> Integer
commonFactor Expression
x') Integer
n
      | Bool
otherwise = Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
gcd (Expression -> Integer
commonFactor Expression
x') (Expression -> Integer
commonFactor Expression
y')
    x' :: Expression
x' = Expression -> Expression
simplify Expression
x
    y' :: Expression
y' = Expression -> Expression
simplify Expression
y
simplify ((Expression
1 :/: Expression
x) :*: Expression
y) = (Expression
1 Expression -> Expression -> Expression
:/: Integer -> Expression -> Expression
divideFactor Integer
g Expression
x') Expression -> Expression -> Expression
:*: Integer -> Expression -> Expression
divideFactor Integer
g Expression
y'
  where
    g :: Integer
g = Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
gcd (Expression -> Integer
commonFactor Expression
x') (Expression -> Integer
commonFactor Expression
y')
    x' :: Expression
x' = Expression -> Expression
simplify Expression
x
    y' :: Expression
y' = Expression -> Expression
simplify Expression
y
simplify (UnaryApply UnaryFunction
func Expression
x) = UnaryFunction -> Expression -> Expression
UnaryApply UnaryFunction
func (Expression -> Expression) -> Expression -> Expression
forall a b. (a -> b) -> a -> b
$ Expression -> Expression
simplify Expression
x
simplify (BinaryApply BinaryFunction
func Expression
x Expression
y) = BinaryFunction -> Expression -> Expression -> Expression
BinaryApply BinaryFunction
func (Expression -> Expression
simplify Expression
x) (Expression -> Expression
simplify Expression
y)
simplify Expression
e = Expression
e

-- | Finds a common factor which multiplies each term in an expression.
-- Ignores terms not in algebraic ring ordering or includes direct negations of numbers.
commonFactor :: Expression -> Integer
commonFactor :: Expression -> Integer
commonFactor (Number Integer
n) = Integer
n
commonFactor (Expression
x :+: Expression
y) = Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
gcd (Expression -> Integer
commonFactor Expression
x) (Expression -> Integer
commonFactor Expression
y)
commonFactor (Expression
x :-: Expression
y) = Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
gcd (Expression -> Integer
commonFactor Expression
x) (Expression -> Integer
commonFactor Expression
y)
commonFactor (Number Integer
n :*: Expression
_) = Integer
n
commonFactor Expression
_ = Integer
1

-- | Divides each term in an expression by a common factor.
-- Specialized for dividing factors found by 'commonFactor.
divideFactor :: Integer -> Expression -> Expression
divideFactor :: Integer -> Expression -> Expression
divideFactor Integer
0 Expression
e = Expression
e
divideFactor Integer
1 Expression
e = Expression
e
divideFactor Integer
g (Number Integer
n) = Integer -> Expression
Number (Integer -> Expression) -> Integer -> Expression
forall a b. (a -> b) -> a -> b
$ Integer
n Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
g
divideFactor Integer
g (Expression
x :+: Expression
y) = Integer -> Expression -> Expression
divideFactor Integer
g Expression
x Expression -> Expression -> Expression
:+: Integer -> Expression -> Expression
divideFactor Integer
g Expression
y
divideFactor Integer
g (Number Integer
n :*: Expression
x) = Integer -> Expression
Number (Integer
n Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
g) Expression -> Expression -> Expression
:*: Expression
x
divideFactor Integer
g Expression
e = Expression
e Expression -> Expression -> Expression
:/: Integer -> Expression
Number Integer
g