packages feed

symtegration-0.6.1: test/Symtegration/Integration/PowersSpec.hs

-- |
-- Description: Tests of Symtegration.Integration.Powers
-- Copyright: Copyright 2024 Yoo Chung
-- License: Apache-2.0
-- Maintainer: dev@chungyc.org
module Symtegration.Integration.PowersSpec (spec) where

import Data.Map qualified as Map
import Data.Ratio (denominator, numerator)
import Data.Text (Text)
import Symtegration.Integration.Powers
import Symtegration.Integration.Properties
import Symtegration.Symbolic
import Symtegration.Symbolic.Arbitrary
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck

spec :: Spec
spec = parallel $ do
  prop "consistent with derivative of integral" $ \(Pow e) x ->
    antiderivativeProperty integrate (Map.singleton var x) e x

  prop "integrates constant symbol" $
    forAll (arbitrarySymbol `suchThat` (/= Symbol var)) $ \c ->
      integrate var c `shouldSatisfy` flip elem (map Just [Symbol var * c, c * Symbol var])

  describe "ignores constants" $ do
    prop "with integer power" $ \n ->
      forAll (arbitrarySymbol `suchThat` (/= Symbol var)) $ \c ->
        integrate var (c :*: Number n) `shouldBe` Nothing

    prop "with fraction" $ \n m ->
      forAll (arbitrarySymbol `suchThat` (/= Symbol var)) $ \c ->
        integrate var (c :*: (Number n :/: Number m)) `shouldBe` Nothing

newtype Pow = Pow Expression deriving (Eq, Show)

instance Arbitrary Pow where
  arbitrary =
    Pow
      <$> frequency
        [ (1, pure $ Symbol var :**: Number (-1)),
          (2, (\n -> Symbol var :**: Number n) <$> genExponent),
          (10, (\(m, n) -> Symbol var :**: (Number m :/: Number n)) <$> genFractionalExponent)
        ]
    where
      genExponent = resize 4 arbitrarySizedIntegral
      genFractionalExponent = resize 4 $ do
        q <- arbitrarySizedFractional
        return (numerator q, denominator q)

var :: Text
var = "x"