r"""
Symbolic Minimum and Maximum

Sage provides a symbolic maximum and minimum due to the fact that the Python
builtin max and min are not able to deal with variables as users might expect.
These functions wait to evaluate if there are variables.

Here you can see some differences::

   sage: max(x, x^2)
   x
   sage: max_symbolic(x, x^2)
   max(x, x^2)
   sage: f(x) = max_symbolic(x, x^2); f(1/2)
   1/2

This works as expected for more than two entries::

   sage: max(3, 5, x)
   5
   sage: min(3, 5, x)
   3
   sage: max_symbolic(3, 5, x)
   max(x, 5)
   sage: min_symbolic(3, 5, x)
   min(x, 3)

"""
###############################################################################
#   Sage: Open Source Mathematical Software
#       Copyright (C) 2010 Burcin Erocal <burcin@erocal.org>
#  Distributed under the terms of the GNU General Public License (GPL),
#  version 2 or any later version.  The full text of the GPL is available at:
#                  http://www.gnu.org/licenses/
###############################################################################

from sage.symbolic.function import BuiltinFunction
from sage.symbolic.expression import Expression
from sage.symbolic.ring import SR

from builtins import max as builtin_max, min as builtin_min

class MinMax_base(BuiltinFunction):
    def eval_helper(self, this_f, builtin_f, initial_val, args):
        """
        EXAMPLES::

            sage: max_symbolic(3, 5, x)  # indirect doctest
            max(x, 5)
            sage: max_symbolic([5.0r])   # indirect doctest
            5.0
            sage: min_symbolic(3, 5, x)
            min(x, 3)
            sage: min_symbolic([5.0r])   # indirect doctest
            5.0

        """
        # __call__ ensures that if args is a singleton, the element is iterable
        arg_is_iter = False
        if len(args) == 1:
            arg_is_iter = True
            args = args[0]

        symb_args = []
        res = initial_val
        num_non_symbolic_args = 0
        for x in args:
            if isinstance(x, Expression):
                symb_args.append(x)
            else:
                num_non_symbolic_args += 1
                if res is None:
                    res = x
                else:
                    res = builtin_f(res, x)

        # if no symbolic arguments, return the result
        if len(symb_args) == 0:
            return res

        # if all arguments were symbolic return
        if num_non_symbolic_args <= 1 and not arg_is_iter:
            return None

        if res is not None:
            symb_args.append(res)
        return this_f(*symb_args)

    def __call__(self, *args, **kwds):
        """
        EXAMPLES::

            sage: max_symbolic(3, 5, x)
            max(x, 5)
            sage: max_symbolic(3, 5, x, hold=True)
            max(3, 5, x)
            sage: max_symbolic([3, 5, x])
            max(x, 5)

        ::

            sage: min_symbolic(3, 5, x)
            min(x, 3)
            sage: min_symbolic(3, 5, x, hold=True)
            min(3, 5, x)
            sage: min_symbolic([3, 5, x])
            min(x, 3)

        TESTS:

        We get an exception if no arguments are given::

            sage: max_symbolic()
            Traceback (most recent call last):
            ...
            ValueError: number of arguments must be > 0

        Check if a single argument which is not iterable works::

            sage: max_symbolic(None)
            Traceback (most recent call last):
            ...
            TypeError: 'NoneType' object is not iterable
            sage: max_symbolic(5)
            Traceback (most recent call last):
            ...
            TypeError: 'sage.rings.integer.Integer' object is not iterable
            sage: max_symbolic(x)
            Traceback (most recent call last):
            ...
            TypeError: 'sage.symbolic.expression.Expression' object is not iterable
            sage: min_symbolic(5)
            Traceback (most recent call last):
            ...
            TypeError: 'sage.rings.integer.Integer' object is not iterable
            sage: min_symbolic(x)
            Traceback (most recent call last):
            ...
            TypeError: 'sage.symbolic.expression.Expression' object is not iterable
        """
        if len(args) == 0:
            raise ValueError("number of arguments must be > 0")
        if len(args) == 1:
            try:
                args = (SR._force_pyobject(iter(args[0])),)
            except TypeError:
                raise

        try:
            return BuiltinFunction.__call__(self, *args, **kwds)
        except ValueError:
            pass

class MaxSymbolic(MinMax_base):
    def __init__(self):
        r"""
        Symbolic `\max` function.

        The Python builtin `\max` function doesn't work as expected when symbolic
        expressions are given as arguments. This function delays evaluation
        until all symbolic arguments are substituted with values.

        EXAMPLES::

            sage: max_symbolic(3, x)
            max(3, x)
            sage: max_symbolic(3, x).subs(x=5)
            5
            sage: max_symbolic(3, 5, x)
            max(x, 5)
            sage: max_symbolic([3, 5, x])
            max(x, 5)

        TESTS::

            sage: loads(dumps(max_symbolic(x, 5)))
            max(x, 5)
            sage: latex(max_symbolic(x, 5))
            \max\left(x, 5\right)
            sage: max_symbolic(x, 5)._sympy_()
            Max(5, x)
        """
        BuiltinFunction.__init__(self, 'max', nargs=0, latex_name=r"\max",
                                 conversions=dict(sympy='Max'))

    def _eval_(self, *args):
        """
        EXAMPLES::

            sage: t = max_symbolic(x, 5); t
            max(x, 5)
            sage: t.subs(x=3)  # indirect doctest
            5
            sage: max_symbolic(5, 3)
            5
            sage: u = max_symbolic(*(list(range(10)) + [x])); u
            max(x, 9)
            sage: u.subs(x=-1)
            9
            sage: u.subs(x=10)
            10
            sage: max_symbolic([0, x])
            max(x, 0)

        TESTS::

            sage: max_symbolic()
            Traceback (most recent call last):
            ...
            ValueError: number of arguments must be > 0
        """
        return self.eval_helper(max_symbolic, builtin_max, None, args)

    def _evalf_(self, *args, **kwds):
        """
        EXAMPLES::

            sage: t = max_symbolic(sin(x), cos(x))
            sage: t.subs(x=1).n(200)
            0.84147098480789650665250232163029899962256306079837106567275
            sage: var('y')
            y
            sage: t = max_symbolic(sin(x), cos(x), y)
            sage: u = t.subs(x=1); u
            max(sin(1), cos(1), y)
            sage: u.n()
            Traceback (most recent call last):
            ...
            TypeError: cannot evaluate symbolic expression numerically

        ::

            sage: f = max_symbolic(sin(x), cos(x))
            sage: r = integral(f, x, 0, 1); r
            sqrt(2) - cos(1)
            sage: r.n()
            0.873911256504955
        """
        return max_symbolic(args)


max_symbolic = MaxSymbolic()


class MinSymbolic(MinMax_base):
    def __init__(self):
        r"""
        Symbolic `\min` function.

        The Python builtin `\min` function doesn't work as expected when symbolic
        expressions are given as arguments. This function delays evaluation
        until all symbolic arguments are substituted with values.

        EXAMPLES::

            sage: min_symbolic(3, x)
            min(3, x)
            sage: min_symbolic(3, x).subs(x=5)
            3
            sage: min_symbolic(3, 5, x)
            min(x, 3)
            sage: min_symbolic([3, 5, x])
            min(x, 3)

        TESTS::

            sage: loads(dumps(min_symbolic(x, 5)))
            min(x, 5)
            sage: latex(min_symbolic(x, 5))
            \min\left(x, 5\right)
            sage: min_symbolic(x, 5)._sympy_()
            Min(5, x)
        """
        BuiltinFunction.__init__(self, 'min', nargs=0, latex_name=r"\min",
                                 conversions=dict(sympy='Min'))

    def _eval_(self, *args):
        """
        EXAMPLES::

            sage: t = min_symbolic(x, 5); t
            min(x, 5)
            sage: t.subs(x=3)  # indirect doctest
            3
            sage: min_symbolic(5, 3)
            3
            sage: u = min_symbolic(*(list(range(10)) + [x])); u
            min(x, 0)
            sage: u.subs(x=-1)
            -1
            sage: u.subs(x=10)
            0
            sage: min_symbolic([3, x])
            min(x, 3)

        TESTS::

            sage: min_symbolic()
            Traceback (most recent call last):
            ...
            ValueError: number of arguments must be > 0
        """
        return self.eval_helper(min_symbolic, builtin_min, float('inf'), args)

    def _evalf_(self, *args, **kwds):
        """
        EXAMPLES::

            sage: t = min_symbolic(sin(x), cos(x))
            sage: t.subs(x=1).n(200)
            0.54030230586813971740093660744297660373231042061792222767010
            sage: var('y')
            y
            sage: t = min_symbolic(sin(x), cos(x), y)
            sage: u = t.subs(x=1); u
            min(sin(1), cos(1), y)
            sage: u.n()
            Traceback (most recent call last):
            ...
            TypeError: cannot evaluate symbolic expression numerically
        """
        return min_symbolic(args)

min_symbolic = MinSymbolic()
