Author : Syed Faizan  3rd Year IIIT Pune  Github : faizan2700 Purpose : You have one function fx which takes float integer and returns float you have to integrate the function in limits a to b. The approximation proposed by Thomas Simpsons in 1743 is one way to calculate integration.  read article : https:cpalgorithms.comnummethodssimpsonintegration.html  simpsonintegration takes function,lowerlimita,upperlimitb,precision and returns the integration of function in given limit. constants the more the number of steps the more accurate Summary of Simpson Approximation : By simpsons integration : 1. integration of fxdx with limit a to b is  fx0  4  fx1  2  fx2  4  fx3  2  fx4.....  fxn where x0  a xi  a  i  h xn  b Args: function : the function which's integration is desired a : the lower limit of integration b : upper limit of integration precision : precision of the result,error required default is 4 Returns: result : the value of the approximated integration of function in range a to b Raises: AssertionError: function is not callable AssertionError: a is not float or integer AssertionError: function should return float or integer AssertionError: b is not float or integer AssertionError: precision is not positive integer  simpsonintegrationlambda x : xx,1,2,3 2.333  simpsonintegrationlambda x : xx,'wronginput',2,3 Traceback most recent call last: ... AssertionError: a should be float or integer your input : wronginput  simpsonintegrationlambda x : xx,1,'wronginput',3 Traceback most recent call last: ... AssertionError: b should be float or integer your input : wronginput  simpsonintegrationlambda x : xx,1,2,'wronginput' Traceback most recent call last: ... AssertionError: precision should be positive integer your input : wronginput  simpsonintegration'wronginput',2,3,4 Traceback most recent call last: ... AssertionError: the functionobject passed should be callable your input : ...  simpsonintegrationlambda x : xx,3.45,3.2,1 2.8  simpsonintegrationlambda x : xx,3.45,3.2,0 Traceback most recent call last: ... AssertionError: precision should be positive integer your input : 0  simpsonintegrationlambda x : xx,3.45,3.2,1 Traceback most recent call last: ... AssertionError: precision should be positive integer your input : 1 just applying the formula of simpson for approximate integration written in mentioned article in first comment of this file and above this function
# constants
# the more the number of steps the more accurate
N_STEPS = 1000


def f(x: float) -> float:
    return x * x


"""
Summary of Simpson Approximation :

By simpsons integration :
1. integration of fxdx with limit a to b is =
    f(x0) + 4 * f(x1) + 2 * f(x2) + 4 * f(x3) + 2 * f(x4)..... + f(xn)
where x0 = a
xi = a + i * h
xn = b
"""


def simpson_integration(function, a: float, b: float, precision: int = 4) -> float:
    """
    Args:
        function : the function which's integration is desired
        a : the lower limit of integration
        b : upper limit of integration
        precision : precision of the result,error required default is 4

    Returns:
        result : the value of the approximated integration of function in range a to b

    Raises:
        AssertionError: function is not callable
        AssertionError: a is not float or integer
        AssertionError: function should return float or integer
        AssertionError: b is not float or integer
        AssertionError: precision is not positive integer

    >>> simpson_integration(lambda x : x*x,1,2,3)
    2.333

    >>> simpson_integration(lambda x : x*x,'wrong_input',2,3)
    Traceback (most recent call last):
        ...
    AssertionError: a should be float or integer your input : wrong_input

    >>> simpson_integration(lambda x : x*x,1,'wrong_input',3)
    Traceback (most recent call last):
        ...
    AssertionError: b should be float or integer your input : wrong_input

    >>> simpson_integration(lambda x : x*x,1,2,'wrong_input')
    Traceback (most recent call last):
        ...
    AssertionError: precision should be positive integer your input : wrong_input
    >>> simpson_integration('wrong_input',2,3,4)
    Traceback (most recent call last):
        ...
    AssertionError: the function(object) passed should be callable your input : ...

    >>> simpson_integration(lambda x : x*x,3.45,3.2,1)
    -2.8

    >>> simpson_integration(lambda x : x*x,3.45,3.2,0)
    Traceback (most recent call last):
        ...
    AssertionError: precision should be positive integer your input : 0

    >>> simpson_integration(lambda x : x*x,3.45,3.2,-1)
    Traceback (most recent call last):
        ...
    AssertionError: precision should be positive integer your input : -1

    """
    assert callable(
        function
    ), f"the function(object) passed should be callable your input : {function}"
    assert isinstance(a, (float, int)), f"a should be float or integer your input : {a}"
    assert isinstance(function(a), (float, int)), (
        "the function should return integer or float return type of your function, "
        f"{type(a)}"
    )
    assert isinstance(b, (float, int)), f"b should be float or integer your input : {b}"
    assert (
        isinstance(precision, int) and precision > 0
    ), f"precision should be positive integer your input : {precision}"

    # just applying the formula of simpson for approximate integration written in
    # mentioned article in first comment of this file and above this function

    h = (b - a) / N_STEPS
    result = function(a) + function(b)

    for i in range(1, N_STEPS):
        a1 = a + h * i
        result += function(a1) * (4 if i % 2 else 2)

    result *= h / 3
    return round(result, precision)


if __name__ == "__main__":
    import doctest

    doctest.testmod()
