-- |
-- Module: Symtegration.Integration.Factor
-- Description: Factor a term into constant and non-constant parts.
-- Copyright: Copyright 2024 Yoo Chung
-- License: Apache-2.0
-- Maintainer: dev@chungyc.org
module Symtegration.Integration.Factor (factor, isConstant) where

import Data.Text (Text)
import Symtegration.Symbolic
import Symtegration.Symbolic.Simplify

-- $setup
-- >>> import Symtegration

-- | Factor a multiplicative term into a constant portion and the variable-dependent portion.
-- E.g., \(2a x \sin x\) into \(2a\) and \(x \sin x\) when the variable is \(x\).
--
-- >>> let s (x, y) = (toHaskell $ simplify x, toHaskell $ simplify y)
-- >>> s $ factor "x" $ 2 * ("a" * sin "x")
-- ("2 * a","sin x")
-- >>> s $ factor "x" $ "a" / "x"
-- ("a","1 / x")
--
-- Assumes algebraic ring ordering has been applied to the term.
factor ::
  -- | Symbol for the variable.
  Text ->
  -- | Term to separate into constant and non-constant portions.
  Expression ->
  (Expression, Expression)
factor :: Text -> Expression -> (Expression, Expression)
factor Text
_ e :: Expression
e@(Number Integer
_) = (Expression
e, Integer -> Expression
Number Integer
1)
factor Text
v e :: Expression
e@(Symbol Text
s) | Text
v Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
s = (Integer -> Expression
Number Integer
1, Expression
e) | Bool
otherwise = (Expression
e, Integer -> Expression
Number Integer
1)
factor Text
v e :: Expression
e@(UnaryApply UnaryFunction
_ Expression
x) | Text -> Expression -> Bool
isConstant Text
v Expression
x = (Expression
e, Integer -> Expression
Number Integer
1) | Bool
otherwise = (Integer -> Expression
Number Integer
1, Expression
e)
factor Text
v e :: Expression
e@(Expression
x :*: (Expression
y :*: Expression
z))
  | Text -> Expression -> Bool
isConstant Text
v Expression
x, Text -> Expression -> Bool
isConstant Text
v Expression
y, Text -> Expression -> Bool
isConstant Text
v Expression
z = (Expression
e, Integer -> Expression
Number Integer
1)
  | Text -> Expression -> Bool
isConstant Text
v Expression
x, Text -> Expression -> Bool
isConstant Text
v Expression
y = (Text -> Expression -> Expression
simplifyForVariable Text
v (Expression -> Expression) -> Expression -> Expression
forall a b. (a -> b) -> a -> b
$ Expression
x Expression -> Expression -> Expression
:*: (Expression
y Expression -> Expression -> Expression
:*: Expression
c), Expression
z')
  | Text -> Expression -> Bool
isConstant Text
v Expression
x = (Text -> Expression -> Expression
simplifyForVariable Text
v (Expression -> Expression) -> Expression -> Expression
forall a b. (a -> b) -> a -> b
$ Expression
x Expression -> Expression -> Expression
:*: Expression
d, Expression
y')
  | Bool
otherwise = (Integer -> Expression
Number Integer
1, Expression
e)
  where
    (Expression
c, Expression
z') = Text -> Expression -> (Expression, Expression)
factor Text
v Expression
z
    (Expression
d, Expression
y') = Text -> Expression -> (Expression, Expression)
factor Text
v (Expression -> (Expression, Expression))
-> Expression -> (Expression, Expression)
forall a b. (a -> b) -> a -> b
$ Expression
y Expression -> Expression -> Expression
:*: Expression
z
factor Text
v e :: Expression
e@(Expression
x :*: Expression
y)
  | Text -> Expression -> Bool
isConstant Text
v Expression
x, Text -> Expression -> Bool
isConstant Text
v Expression
y = (Expression
e, Integer -> Expression
Number Integer
1)
  | Text -> Expression -> Bool
isConstant Text
v Expression
x = (Expression
x, Expression
y)
  | Bool
otherwise = (Integer -> Expression
Number Integer
1, Expression
e)
factor Text
v (Expression
x :/: Expression
y) = (Expression -> Expression
simplify (Expression -> Expression) -> Expression -> Expression
forall a b. (a -> b) -> a -> b
$ Expression
constX Expression -> Expression -> Expression
:/: Expression
constY, Expression -> Expression
simplify (Expression -> Expression) -> Expression -> Expression
forall a b. (a -> b) -> a -> b
$ Expression
varX Expression -> Expression -> Expression
:/: Expression
varY)
  where
    (Expression
constX, Expression
varX) = Text -> Expression -> (Expression, Expression)
factor Text
v Expression
x
    (Expression
constY, Expression
varY) = Text -> Expression -> (Expression, Expression)
factor Text
v Expression
y
factor Text
v Expression
e | Text -> Expression -> Bool
isConstant Text
v Expression
e = (Expression
e, Integer -> Expression
Number Integer
1) | Bool
otherwise = (Integer -> Expression
Number Integer
1, Expression
e)

-- | Returns whether an expression contains the variable.
--
-- >>> isConstant "x" $ 1 + "x"
-- False
-- >>> isConstant "x" $ 1 + "a"
-- True
isConstant ::
  -- | Symbol for the variable.
  Text ->
  -- | Expression to check.
  Expression ->
  -- | Whether the expression is a constant.
  Bool
isConstant :: Text -> Expression -> Bool
isConstant Text
_ (Number Integer
_) = Bool
True
isConstant Text
v (Symbol Text
s) = Text
s Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
v
isConstant Text
v (UnaryApply UnaryFunction
_ Expression
x) = Text -> Expression -> Bool
isConstant Text
v Expression
x
isConstant Text
v (BinaryApply BinaryFunction
_ Expression
x Expression
y) = Text -> Expression -> Bool
isConstant Text
v Expression
x Bool -> Bool -> Bool
&& Text -> Expression -> Bool
isConstant Text
v Expression
y