packages feed

symtegration-0.6.1: src/Symtegration/Integration/Parts.hs

-- |
-- Module: Symtegration.Integration.Parts
-- Description: Integration by parts.
-- Copyright: Copyright 2025 Yoo Chung
-- License: Apache-2.0
-- Maintainer: dev@chungyc.org
module Symtegration.Integration.Parts (integrate) where

import Control.Applicative (asum, (<|>))
import Data.Text (Text)
import Symtegration.Differentiation
import Symtegration.Symbolic
import Symtegration.Symbolic.Simplify

-- $setup
-- >>> import Symtegration.Integration.Powers qualified as Powers
-- >>> import Symtegration.Integration.Trigonometric qualified as Trigonometric
-- >>> import Symtegration.Symbolic.Haskell
-- >>> import Symtegration.Symbolic.Simplify

-- | Integrates by parts.
--
-- Specifically, if for
--
-- \[ \int f g \, dx \]
--
-- it is the case that we can find \(F = \int f \, dx\) and \(\int F \frac{dg}{dx} \, dx\),
-- then we can derive the integral as
--
-- \[ \int f g \, dx = F g - \int F \frac{dg}{dx} \, dx \]
--
-- >>> let directMethods = [Powers.integrate, Trigonometric.integrate]
-- >>> toHaskell . simplify <$> integrate directMethods "x" ("x" * cos "x")
-- Just "x * sin x + cos x"
integrate ::
  -- | Integration algorithms to try on the parts.
  [Text -> Expression -> Maybe Expression] ->
  -- | Symbol for the variable.
  Text ->
  -- | Expression to integrate.
  Expression ->
  -- | Integral, if derived.
  Maybe Expression
integrate fs v (x :*: y) = integrate' fs v x y <|> integrate' fs v y x
integrate _ _ _ = Nothing

-- | The actual work of integrating by parts, except it tries the parts in only one order.
integrate' ::
  -- | Integration algorithms to try on the parts.
  [Text -> Expression -> Maybe Expression] ->
  -- | Symbol for the variable.
  Text ->
  -- | The part to be integrated.
  Expression ->
  -- | The part to be differentiated.
  Expression ->
  -- | Integral, if derived.
  Maybe Expression
integrate' fs v x y = do
  ix <- integrate'' x
  iixdy <- integrate'' $ simplifyForVariable v $ ix * differentiate v y
  return $ ix * y - iixdy
  where
    integrate'' z = asum $ map (\f -> f v z) fs