r"""
Interface to Axiom

TODO:

- Evaluation using a file is not done. Any input line with more than a
  few thousand characters would hang the system, so currently it
  automatically raises an exception.

- All completions of a given command.

- Interactive help.

Axiom is a free GPL-compatible (modified BSD license) general
purpose computer algebra system whose development started in 1973
at IBM. It contains symbolic manipulation algorithms, as well as
implementations of special functions, including elliptic functions
and generalized hypergeometric functions. Moreover, Axiom has
implementations of many functions relating to the invariant theory
of the symmetric group `S_n.` For many links to Axiom
documentation see http://wiki.axiom-developer.org.

AUTHORS:

- Bill Page (2006-10): Created this (based on Maxima interface)

  .. note::

     Bill Page put a huge amount of effort into the Sage Axiom
     interface over several days during the Sage Days 2 coding
     sprint. This is contribution is greatly appreciated.

- William Stein (2006-10): misc touchup.

- Bill Page (2007-08): Minor modifications to support axiom4sage-0.3

.. note::

   The axiom4sage-0.3.spkg is based on an experimental version of the
   FriCAS fork of the Axiom project by Waldek Hebisch that uses
   pre-compiled cached Lisp code to build Axiom very quickly with
   clisp.

If the string "error" (case insensitive) occurs in the output of
anything from axiom, a RuntimeError exception is raised.

EXAMPLES: We evaluate a very simple expression in axiom.

::

    sage: axiom('3 * 5')                     #optional - axiom
    15
    sage: a = axiom(3) * axiom(5); a         #optional - axiom
    15

The type of a is AxiomElement, i.e., an element of the axiom
interpreter.

::

    sage: type(a)                            #optional - axiom
    <class 'sage.interfaces.axiom.AxiomElement'>
    sage: parent(a)                          #optional - axiom
    Axiom

The underlying Axiom type of a is also available, via the type
method::

    sage: a.type()                           #optional - axiom
    PositiveInteger

We factor `x^5 - y^5` in Axiom in several different ways.
The first way yields a Axiom object.

::

    sage: F = axiom.factor('x^5 - y^5'); F      #optional - axiom
               4      3    2 2    3     4
    - (y - x)(y  + x y  + x y  + x y + x )
    sage: type(F)                               #optional - axiom
    <class 'sage.interfaces.axiom.AxiomElement'>
    sage: F.type()                              #optional - axiom
    Factored Polynomial Integer

Note that Axiom objects are normally displayed using "ASCII art".

::

    sage: a = axiom(2/3); a          #optional - axiom
      2
      -
      3
    sage: a = axiom('x^2 + 3/7'); a      #optional - axiom
       2   3
      x  + -
           7

The ``axiom.eval`` command evaluates an expression in
axiom and returns the result as a string. This is exact as if we
typed in the given line of code to axiom; the return value is what
Axiom would print out.

::

    sage: print axiom.eval('factor(x^5 - y^5)')   #optional - axiom
               4      3    2 2    3     4
    - (y - x)(y  + x y  + x y  + x y + x )
    Type: Factored Polynomial Integer

We can create the polynomial `f` as a Axiom polynomial,
then call the factor method on it. Notice that the notation
``f.factor()`` is consistent with how the rest of Sage
works.

::

    sage: f = axiom('x^5 - y^5')                  #optional - axiom
    sage: f^2                                     #optional - axiom
       10     5 5    10
      y   - 2x y  + x
    sage: f.factor()                              #optional - axiom
               4      3    2 2    3     4
    - (y - x)(y  + x y  + x y  + x y + x )

Control-C interruption works well with the axiom interface, because
of the excellent implementation of axiom. For example, try the
following sum but with a much bigger range, and hit control-C.

::

    sage:  f = axiom('(x^5 - y^5)^10000')       # not tested
    Interrupting Axiom...
    ...
    <type 'exceptions.TypeError'>: Ctrl-c pressed while running Axiom

::

    sage: axiom('1/100 + 1/101')                  #optional - axiom
       201
      -----
      10100
    sage: a = axiom('(1 + sqrt(2))^5'); a         #optional - axiom
         +-+
      29\|2  + 41

TESTS: We check to make sure the subst method works with keyword
arguments.

::

    sage: a = axiom(x+2); a  #optional - axiom
    x + 2
    sage: a.subst(x=3)       #optional - axiom
    5

We verify that Axiom floating point numbers can be converted to
Python floats.

::

    sage: float(axiom(2))     #optional - axiom
    2.0
"""

###########################################################################
#       Copyright (C) 2008 Mike Hansen <mhansen@gmail.com>
#                     2007 Bill Page
#                     2006 William Stein <wstein@gmail.com>
#
#  Distributed under the terms of the GNU General Public License (GPL)
#  The full text of the GPL is available at:
#
#                  http://www.gnu.org/licenses/
###########################################################################

import os, re

from expect import Expect, ExpectElement, FunctionElement, ExpectFunction
from sage.misc.misc import verbose, DOT_SAGE
from pexpect import EOF
from sage.misc.multireplace import multiple_replace

# The Axiom commands ")what thing det" ")show Matrix" and ")display
# op det" commands, gives a list of all identifiers that begin in
# a certain way.  This could maybe be useful somehow... (?)  Also
# axiom has a lot a lot of ways for getting documentation from the
# system -- this could also be useful.

class PanAxiom(Expect):
    """
    Interface to a PanAxiom interpreter.
    """
    def __init__(self, name='axiom', command='axiom -nox -noclef',
                 script_subdirectory=None, logfile=None,
                 server=None, server_tmpdir=None,
                 init_code=[')lisp (si::readline-off)']):
        """
        Create an instance of the Axiom interpreter.

        TESTS::

            sage: axiom == loads(dumps(axiom))
            True
        """
        eval_using_file_cutoff = 200
        self.__eval_using_file_cutoff = eval_using_file_cutoff
        self._COMMANDS_CACHE = '%s/%s_commandlist_cache.sobj'%(DOT_SAGE, name)
        Expect.__init__(self,
                        name = name,
                        prompt = '\([0-9]+\) -> ',
                        command = command,
                        maxread = 10,
                        script_subdirectory = script_subdirectory,
                        server=server,
                        server_tmpdir=server_tmpdir,
                        restart_on_ctrlc = False,
                        verbose_start = False,
                        init_code = init_code,
                        logfile = logfile,
                        eval_using_file_cutoff=eval_using_file_cutoff)
        self._prompt_wait = self._prompt

    def _start(self):
        """
        Start the Axiom interpreter.

        EXAMPLES::

            sage: a = Axiom()
            sage: a.is_running()
            False
            sage: a._start()     #optional - axiom
            sage: a.is_running() #optional - axiom
            True
            sage: a.quit()       #optional - axiom
        """
        Expect._start(self)
        out = self._eval_line(')set functions compile on', reformat=False)
        out = self._eval_line(')set output length 245', reformat=False)
        out = self._eval_line(')set message autoload off', reformat=False)

    def _read_in_file_command(self, filename):
        r"""
        EXAMPLES::

            sage: axiom._read_in_file_command('test.input')
            ')read test.input \n'
            sage: axiom._read_in_file_command('test')
            Traceback (most recent call last):
            ...
            ValueError: the filename must end with .input

        ::

            sage: filename = tmp_filename(ext='.input')
            sage: f = open(filename, 'w')
            sage: f.write('xx := 22;\n')
            sage: f.close()
            sage: axiom.read(filename)    # optional - axiom
            sage: axiom.get('xx')         #optional
            '22'
        """
        if not filename.endswith('.input'):
            raise ValueError, "the filename must end with .input"

        # For some reason this trivial comp
        # keeps certain random freezes from occurring.  Do not remove this.
        # The space before the \n is also important.
        return ')read %s \n'%filename

    def _quit_string(self):
        """
        Returns the string used to quit Axiom.

        EXAMPLES::

            sage: axiom._quit_string()
            ')lisp (quit)'
            sage: a = Axiom()
            sage: a.is_running()
            False
            sage: a._start()     #optional - axiom
            sage: a.is_running() #optional - axiom
            True
            sage: a.quit()       #optional - axiom
            sage: a.is_running() #optional - axiom
            False
        """
        return ')lisp (quit)'

    def _commands(self):
        """
        Returns a list of commands available. This is done by parsing the
        result of the first section of the output of ')what things'.

        EXAMPLES::

            sage: cmds = axiom._commands() #optional - axiom
            sage: len(cmds) > 100  #optional - axiom
            True
            sage: '<' in cmds      #optional - axiom
            True
            sage: 'factor' in cmds #optional - axiom
            True
        """
        s = self.eval(")what things")
        start = '\r\n\r\n#'
        i = s.find(start)
        end = "To get more information about"
        j = s.find(end)
        s = s[i+len(start):j].split()
        return s

    def trait_names(self, verbose=True, use_disk_cache=True):
        """
        Returns a list of all the commands defined in Axiom and optionally
        (per default) store them to disk.

        EXAMPLES::

            sage: c = axiom.trait_names(use_disk_cache=False, verbose=False) #optional - axiom
            sage: len(c) > 100  #optional - axiom
            True
            sage: 'factor' in c  #optional - axiom
            True
            sage: '**' in c     #optional - axiom
            False
            sage: 'upperCase?' in c  #optional - axiom
            False
            sage: 'upperCase_q' in c #optional - axiom
            True
            sage: 'upperCase_e' in c #optional - axiom
            True
        """
        try:
            return self.__trait_names
        except AttributeError:
            import sage.misc.persist
            if use_disk_cache:
                try:
                    self.__trait_names = sage.misc.persist.load(self._COMMANDS_CACHE)
                    return self.__trait_names
                except IOError:
                    pass
            if verbose:
                print "\nBuilding %s command completion list (this takes"%(self)
                print "a few seconds only the first time you do it)."
                print "To force rebuild later, delete %s."%self._COMMANDS_CACHE
            v = self._commands()

            #Process we now need process the commands to strip out things which
            #are not valid Python identifiers.
            import re
            valid = re.compile('[^a-zA-Z0-9_]+')
            names = [x for x in v if valid.search(x) is None]

            #Change everything that ends with ? to _q and
            #everything that ends with ! to _e
            names += [x[:-1]+"_q" for x in v if x.endswith("?")]
            names += [x[:-1]+"_e" for x in v if x.endswith("!")]

            self.__trait_names = names
            if len(v) > 200:
                # Axiom is actually installed.
                sage.misc.persist.save(v, self._COMMANDS_CACHE)
            return names

    def set(self, var, value):
        """
        Set the variable var to the given value.

        EXAMPLES::

            sage: axiom.set('xx', '2')    #optional - axiom
            sage: axiom.get('xx')         #optional - axiom
            '2'

            sage: fricas.set('xx', '2')    #optional - fricas
            sage: fricas.get('xx')         #optional - fricas
            '2'

        """
        cmd = '%s := %s'%(var, value)
        out = self._eval_line(cmd, reformat=False)

        if out.find("error") != -1:
            raise TypeError, "Error executing code in Axiom\nCODE:\n\t%s\nAxiom ERROR:\n\t%s"%(cmd, out)

    def get(self, var):
        r"""
        Get the string value of the Axiom variable var.

        EXAMPLES::

            sage: axiom.set('xx', '2')    #optional - axiom
            sage: axiom.get('xx')         #optional - axiom
            '2'
            sage: a = axiom('(1 + sqrt(2))^5') #optional - axiom
            sage: axiom.get(a.name())          #optional - axiom
            '     +-+\r\r\n  29\\|2  + 41'
        """
        s = self._eval_line(str(var))
        i = s.rfind('Type:')
        s = s[:i].rstrip().lstrip("\n")
        if '\n' not in s:
            s = s.strip()
        return s

    def _eval_line(self, line, reformat=True, allow_use_file=False,
                   wait_for_prompt=True, restart_if_needed=False):
        """
        EXAMPLES::

            sage: print axiom._eval_line('2+2')  #optional - axiom
              4
                                                       Type: PositiveInteger
        """
        if not wait_for_prompt:
            return Expect._eval_line(self, line)
        line = line.rstrip().rstrip(';')
        if line == '':
            return ''
        if len(line) > 3000:
            raise NotImplementedError, "evaluation of long input lines (>3000 characters) in Axiom not yet implemented."
        if self._expect is None:
            self._start()
        if allow_use_file and self.__eval_using_file_cutoff and \
                            len(line) > self.__eval_using_file_cutoff:
            return self._eval_line_using_file(line)
        try:
            E = self._expect
            # debug
            self._synchronize(cmd='1+%s\n')
            verbose("in = '%s'"%line,level=3)
            E.sendline(line)
            self._expect.expect(self._prompt)
            out = self._expect.before
            # debug
            verbose("out = '%s'"%out,level=3)
        except EOF:
          if self._quit_string() in line:
             return ''
        except KeyboardInterrupt:
            self._keyboard_interrupt()

        if '>> Error detected within library code:' in out or \
           'Cannot find a definition or applicable library operation named' in out:
            raise RuntimeError, out

        if not reformat:
            return out
        if 'error' in out:
            return out
        #out = out.lstrip()
        i = out.find('\n')
        out = out[i+1:]
        outs = out.split("\n")
        i = 0
        outline = ''
        for line in outs:
            line = line.rstrip()
            # print "'%s'"%line
            if line[:4] == '   (':
                i = line.find('(')
                i += line[i:].find(')')
                if line[i+1:] == "":
                    i = 0
                    outs = outs[1:]
                break;
        out = "\n".join(line[i+1:] for line in outs[1:])
        return out

    # define relational operators
    def _equality_symbol(self):
        """equality symbol

        EXAMPLES::

            sage: a = axiom(x==6); a    #optional axiom
            x= 6
            sage: a = fricas(x==6); a   #optional fricas
            x= 6
        """
        return "="

class Axiom(PanAxiom):
    def __reduce__(self):
        """
        EXAMPLES::

            sage: axiom.__reduce__()
            (<function reduce_load_Axiom at 0x...>, ())
            sage: f, args = _
            sage: f(*args)
            Axiom
        """
        return reduce_load_Axiom, tuple([])

    def _function_class(self):
        """
        Return the AxiomExpectFunction class.

        EXAMPLES::

            sage: axiom._function_class()
            <class 'sage.interfaces.axiom.AxiomExpectFunction'>
            sage: type(axiom.gcd)
            <class 'sage.interfaces.axiom.AxiomExpectFunction'>
        """
        return AxiomExpectFunction

    def _object_class(self):
        """
        EXAMPLES::

            sage: axiom._object_class()
            <class 'sage.interfaces.axiom.AxiomElement'>
            sage: type(axiom(2)) #optional - axiom
            <class 'sage.interfaces.axiom.AxiomElement'>
        """
        return AxiomElement

    def _function_element_class(self):
        """
        Returns the Axiom function element class.

        EXAMPLES::

            sage: axiom._function_element_class()
            <class 'sage.interfaces.axiom.AxiomFunctionElement'>
            sage: type(axiom(2).gcd) #optional - axiom
            <class 'sage.interfaces.axiom.AxiomFunctionElement'>
        """
        return AxiomFunctionElement

    def console(self):
        """
        Spawn a new Axiom command-line session.

        EXAMPLES::

            sage: axiom.console() #not tested
                                    AXIOM Computer Algebra System
                                    Version: Axiom (January 2009)
                           Timestamp: Sunday January 25, 2009 at 07:08:54
            -----------------------------------------------------------------------------
               Issue )copyright to view copyright notices.
               Issue )summary for a summary of useful system commands.
               Issue )quit to leave AXIOM and return to shell.
            -----------------------------------------------------------------------------
        """
        axiom_console()

class PanAxiomElement(ExpectElement):
    def __call__(self, x):
        """
        EXAMPLES::

            sage: f = axiom(x+2) #optional - axiom
            sage: f(2)           #optional - axiom
            4
        """
        self._check_valid()
        P = self.parent()
        return P('%s(%s)'%(self.name(), x))

    def __cmp__(self, other):
        """
        EXAMPLES::

            sage: two = axiom(2)  #optional - axiom
            sage: two == 2        #optional - axiom
            True
            sage: two == 3        #optional - axiom
            False
            sage: two < 3         #optional - axiom
            True
            sage: two > 1         #optional - axiom
            True

            sage: a = axiom(1); b = axiom(2)  #optional - axiom
            sage: a == b                      #optional - axiom
            False
            sage: a < b                       #optional - axiom
            True
            sage: a > b                       #optional - axiom
            False
            sage: b < a                       #optional - axiom
            False
            sage: b > a                       #optional - axiom
            True

        We can also compare more complicated object such as functions::

            sage: f = axiom('sin(x)'); g = axiom('cos(x)')    #optional - axiom
            sage: f == g                                      #optional - axiom
            False

        """
        P = self.parent()
        if 'true' in P.eval("(%s = %s) :: Boolean"%(self.name(),other.name())):
            return 0
        elif 'true' in P.eval("(%s < %s) :: Boolean"%(self.name(), other.name())):
            return -1
        elif 'true' in P.eval("(%s > %s) :: Boolean"%(self.name(),other.name())):
            return 1

        # everything is supposed to be comparable in Python, so we define
        # the comparison thus when no comparable in interfaced system.
        if (hash(self) < hash(other)):
            return -1
        else:
            return 1

    def type(self):
        """
        Returns the type of an AxiomElement.

        EXAMPLES::

            sage: axiom(x+2).type()  #optional - axiom
            Polynomial Integer
        """
        P = self._check_valid()
        s = P._eval_line(self.name())
        i = s.rfind('Type:')
        return P(s[i+5:].strip())

    def __len__(self):
        """
        Return the length of a list.

        EXAMPLES::

            sage: v = axiom('[x^i for i in 0..5]')            # optional - axiom
            sage: len(v)                                      # optional - axiom
            6
        """
        P = self._check_valid()
        s = P.eval('# %s '%self.name())
        i = s.rfind('Type')
        return int(s[:i-1])

    def __getitem__(self, n):
        r"""
        Return the n-th element of this list.

        .. note::

           Lists are 1-based.

        EXAMPLES::

            sage: v = axiom('[i*x^i for i in 0..5]'); v          # optional - axiom
                     2   3   4   5
              [0,x,2x ,3x ,4x ,5x ]
            sage: v[4]                                           # optional - axiom
                3
              3x
            sage: v[1]                                           # optional - axiom
            0
            sage: v[10]                                          # optional - axiom
            Traceback (most recent call last):
            ...
            IndexError: index out of range
        """
        n = int(n)
        if n <= 0 or n > len(self):
            raise IndexError, "index out of range"
        P = self._check_valid()
        if not isinstance(n, tuple):
            return P.new('%s(%s)'%(self._name, n))
        else:
            return P.new('%s(%s)'%(self._name, str(n)[1:-1]))

    def comma(self, *args):
        """
        Returns a Axiom tuple from self and args.

        EXAMPLES::

            sage: two = axiom(2)  #optional - axiom
            sage: two.comma(3)    #optional - axiom
            [2,3]
            sage: two.comma(3,4)  #optional - axiom
            [2,3,4]
            sage: _.type()        #optional - axiom
            Tuple PositiveInteger

            sage: two = fricas(2)  #optional - fricas
            sage: two.comma(3)     #optional - fricas
            [2,3]
            sage: two.comma(3,4)   #optional - fricas
            [2,3,4]
            sage: _.type()         #optional - fricas
            Tuple(PositiveInteger)

        """
        P = self._check_valid()
        args = list(args)
        for i, arg in enumerate(args):
            if not isinstance(arg, AxiomElement) or arg.parent() is not P:
                args[i] = P(arg)
        cmd = "(" + ",".join([x.name() for x in [self]+args]) + ")"
        return P(cmd)

    def _latex_(self):
        r"""
        EXAMPLES::

            sage: a = axiom(1/2) #optional - axiom
            sage: latex(a)       #optional - axiom
            \frac{1}{2}

            sage: a = fricas(1/2) #optional - fricas
            sage: latex(a)        #optional - fricas
            1 \over 2
        """
        self._check_valid()
        P = self.parent()
        s = P._eval_line('outputAsTex(%s)'%self.name(), reformat=False)
        if not '$$' in s:
            raise RuntimeError, "Error texing axiom object."
        i = s.find('$$')
        j = s.rfind('$$')
        s = s[i+2:j]
        s = multiple_replace({'\r':'', '\n':' ',
                              ' \\sp ':'^',
                              '\\arcsin ':'\\sin^{-1} ',
                              '\\arccos ':'\\cos^{-1} ',
                              '\\arctan ':'\\tan^{-1} '},
            re.sub(r'\\leqno\(.*?\)','',s)) # no eq number!
        return s

    def as_type(self, type):
        """
        Returns self as type.

        EXAMPLES::

            sage: a = axiom(1.2); a            #optional - axiom
            1.2
            sage: a.as_type(axiom.DoubleFloat) #optional - axiom
            1.2
            sage: _.type()                     #optional - axiom
            DoubleFloat

        ::

            sage: a = fricas(1.2); a            #optional - fricas
            1.2
            sage: a.as_type(fricas.DoubleFloat) #optional - fricas
            1.2
            sage: _.type()                      #optional - fricas
            DoubleFloat

        """
        P = self._check_valid()
        type = P(type)
        return P.new("%s :: %s"%(self.name(), type.name()))

    def unparsed_input_form(self):
        """
        Get the linear string representation of this object, if possible
        (often it isn't).

        EXAMPLES::

            sage: a = axiom(x^2+1); a     #optional - axiom
               2
              x  + 1
            sage: a.unparsed_input_form() #optional - axiom
            'x*x+1'

            sage: a = fricas(x^2+1)       #optional - fricas
            sage: a.unparsed_input_form() #optional - fricas
            'x^2+1'
        """
        P = self._check_valid()
        s = P.eval('unparse(%s::InputForm)'%self._name)
        if 'translation error' in s or 'Cannot convert' in s:
            raise NotImplementedError
        s = multiple_replace({'\r\n':'', # fix stupid Fortran-ish
                              'DSIN(':'sin(',
                              'DCOS(':'cos(',
                              'DTAN(':'tan(',
                              'DSINH(':'sinh('}, s)
        r = re.search(r'"(.*)"',s)
        if r:
            return r.groups(0)[0]
        else:
            return s

    def _sage_(self):
        """
        Convert self to a Sage object.

        EXAMPLES::

            sage: a = axiom(1/2); a #optional - axiom
              1
              -
              2
            sage: a.sage()          #optional - axiom
            1/2
            sage: _.parent()        #optional - axiom
            Rational Field

            sage: gp(axiom(1/2))    #optional - axiom
            1/2

            sage: fricas(1/2).sage() #optional - fricas
            1/2

        DoubleFloat's in Axiom are converted to be in RDF in Sage.

        ::

            sage: axiom(2.0).as_type('DoubleFloat').sage()  #optional - axiom
            2.0
            sage: _.parent() #optional - axiom
            Real Double Field

            sage: axiom(2.1234)._sage_() #optional - axiom
            2.12340000000000
            sage: _.parent()             #optional - axiom
            Real Field with 53 bits of precision
            sage: a = RealField(100)(pi)
            sage: axiom(a)._sage_()      #optional - axiom
            3.1415926535897932384626433833
            sage: _.parent()             #optional - axiom
            Real Field with 100 bits of precision
            sage: axiom(a)._sage_() == a #optional - axiom
            True
            sage: axiom(2.0)._sage_() #optional - axiom
            2.00000000000000
            sage: _.parent() #optional  - axiom
            Real Field with 53 bits of precision

        We can also convert Axiom's polynomials to Sage polynomials.
            sage: a = axiom(x^2 + 1)   #optional - axiom
            sage: a.type()             #optional - axiom
            Polynomial Integer
            sage: a.sage()             #optional - axiom
            x^2 + 1
            sage: _.parent()           #optional - axiom
            Univariate Polynomial Ring in x over Integer Ring
            sage: axiom('x^2 + y^2 + 1/2').sage()    #optional - axiom
            y^2 + x^2 + 1/2
            sage: _.parent()                         #optional - axiom
            Multivariate Polynomial Ring in y, x over Rational Field

        """
        P = self._check_valid()
        type = str(self.type())

        if type in ["Type", "Domain"]:
            return self._sage_domain()

        if type == "Float":
            from sage.rings.all import RealField, ZZ
            prec = max(self.mantissa().length()._sage_(), 53)
            R = RealField(prec)
            x,e,b = self.unparsed_input_form().lstrip('float(').rstrip(')').split(',')
            return R(ZZ(x)*ZZ(b)**ZZ(e))
        elif type == "DoubleFloat":
            from sage.rings.all import RDF
            return RDF(repr(self))
        elif type.startswith('Polynomial'):
            from sage.rings.all import PolynomialRing
            base_ring = P(type.lstrip('Polynomial '))._sage_domain()
            vars = str(self.variables())[1:-1]
            R = PolynomialRing(base_ring, vars)
            return R(self.unparsed_input_form())

        #If all else fails, try using the unparsed input form
        try:
            import sage.misc.sage_eval
            return sage.misc.sage_eval.sage_eval(self.unparsed_input_form())
        except StandardError:
            raise NotImplementedError

    def _sage_domain(self):
        """
        A helper function for converting Axiom domains to the corresponding
        Sage object.

        EXAMPLES::

            sage: axiom('Integer').sage()  #optional - axiom
            Integer Ring
            sage: fricas('Integer').sage() #optional - fricas
            Integer Ring

            sage: axiom('Fraction Integer').sage()  #optional - axiom
            Rational Field
            sage: fricas('Fraction Integer').sage() #optional - fricas
            Rational Field

            sage: axiom('DoubleFloat').sage()  #optional - axiom
            Real Double Field
            sage: fricas('DoubleFloat').sage() #optional - fricas
            Real Double Field

        """
        P = self._check_valid()
        name = str(self)
        if name == 'Integer':
            from sage.rings.all import ZZ
            return ZZ
        elif name == 'DoubleFloat':
            from sage.rings.all import RDF
            return RDF
        elif name.startswith('Fraction '):
            return P(name.lstrip('Fraction '))._sage_domain().fraction_field()

        raise NotImplementedError

class AxiomElement(PanAxiomElement):
    pass

class PanAxiomFunctionElement(FunctionElement):
    def __init__(self, object, name):
        """
        TESTS::

            sage: a = axiom('"Hello"') #optional - axiom
            sage: a.upperCase_q        #optional - axiom
            upperCase?
            sage: a.upperCase_e        #optional - axiom
            upperCase!
            sage: a.upperCase_e()      #optional - axiom
            "HELLO"
        """
        if name.endswith("_q"):
            name = name[:-2] + "?"
        elif name.endswith("_e"):
            name = name[:-2] + "!"
        FunctionElement.__init__(self, object, name)

class AxiomFunctionElement(PanAxiomFunctionElement):
    pass

class PanAxiomExpectFunction(ExpectFunction):
    def __init__(self, parent, name):
        """
        TESTS::

            sage: axiom.upperCase_q
            upperCase?
            sage: axiom.upperCase_e
            upperCase!
        """
        if name.endswith("_q"):
            name = name[:-2] + "?"
        elif name.endswith("_e"):
            name = name[:-2] + "!"
        ExpectFunction.__init__(self, parent, name)

class AxiomExpectFunction(PanAxiomExpectFunction):
    pass

def is_AxiomElement(x):
    """
    Returns True of x is of type AxiomElement.

    EXAMPLES::

        sage: from sage.interfaces.axiom import is_AxiomElement
        sage: is_AxiomElement(axiom(2)) #optional - axiom
        True
        sage: is_AxiomElement(2)
        False
    """
    return isinstance(x, AxiomElement)

#Instances
axiom = Axiom(name='axiom')

def reduce_load_Axiom():
    """
    Returns the Axiom interface object defined in
    sage.interfaces.axiom.

    EXAMPLES::

        sage: from sage.interfaces.axiom import reduce_load_Axiom
        sage: reduce_load_Axiom()
        Axiom
    """
    return axiom

import os
def axiom_console():
    """
    Spawn a new Axiom command-line session.

    EXAMPLES::

        sage: axiom_console() #not tested
                                AXIOM Computer Algebra System
                                Version: Axiom (January 2009)
                       Timestamp: Sunday January 25, 2009 at 07:08:54
        -----------------------------------------------------------------------------
           Issue )copyright to view copyright notices.
           Issue )summary for a summary of useful system commands.
           Issue )quit to leave AXIOM and return to shell.
        -----------------------------------------------------------------------------

    """
    os.system('axiom -nox')


import spectrum_functions
import unittest
import numpy as np

class SpectrumFunctionsTest(unittest.TestCase):

    def test2DSpecX(self):
        '''an error is raised if check_spectrum is given a 2D x input'''
        x = np.arange(10).reshape((2, 5))
        y = np.arange(10)
        with self.assertRaisesRegex(ValueError,
           'x is not a 1 dimensional array'):
           spectrum_functions.check_spectrum(x, y)

    def test2DSpecY(self):
        '''an error is raised if check_spectrum is given a 2D y input'''
        x = np.arange(10)
        y = np.arange(10).reshape((2, 5))
        with self.assertRaisesRegex(ValueError,
           'y is not a 1 dimensional array'):
           spectrum_functions.check_spectrum(x, y)

    def testXYlenDifferent(self):
        '''an error is raised if x and y are not the same length as each
        other'''
        x = np.arange(10)
        y = np.arange(9)
        with self.assertRaisesRegex(ValueError,
           'x and y are not the same length'):
           spectrum_functions.check_spectrum(x, y)

    def testSliceRange(self):
        '''slice_range works properly in normal use case'''
        x = np.arange(10)
        y = np.arange(10)
        start_stop = [5, 8]
        self.assertTrue(np.array_equal(
                            np.array([5, 6, 7, 8]),
                            spectrum_functions.slice_range(x, start_stop, y)
                        ))

    def testSliceRangeYCal(self):
        '''slice_range works properly with y non-integer values'''
        x = np.arange(10)
        y = np.arange(10)/10.
        start_stop = [0.3, 0.7]
        self.assertTrue(np.array_equal(
                            np.array([3, 4, 5, 6, 7]),
                            spectrum_functions.slice_range(x, start_stop, y)
                        ))

    def testSliceRangeStopOutside(self):
        '''slice_range works properly when stop is greater than max(y)'''
        x = np.arange(10)
        y = np.arange(10)
        start_stop = [5, 12]
        self.assertTrue(np.array_equal(
                            np.array([5, 6, 7, 8, 9]),
                            spectrum_functions.slice_range(x, start_stop, y)
                        ))

    def testSliceRangeStartOutside(self):
        '''slice_range works properly when stop is greater than max(y)'''
        x = np.arange(10)
        y = np.arange(10)
        start_stop = [-2, 5]
        self.assertTrue(np.array_equal(
                            np.array([0, 1, 2, 3, 4, 5]),
                            spectrum_functions.slice_range(x, start_stop, y)
                        ))

    def testSliceRangeYNegative(self):
        '''slice_range works properly when y contains negative values'''
        x = np.arange(10)
        y = np.arange(10) - 5
        start_stop = [-7, 0]
        self.assertTrue(np.array_equal(
                            np.array([0, 1, 2, 3, 4, 5]),
                            spectrum_functions.slice_range(x, start_stop, y)
                        ))

    def testSliceRangeStartStopDims(self):
        '''raise error in slice_range if start_stop are not a 2 element list or
        tuple or array'''
        x = np.arange(10)
        y = np.arange(10) - 5
        start_stop = [-7, 0, 5]
        with self.assertRaisesRegex(ValueError,
           'start_stop is not a 2 element list'):
           spectrum_functions.slice_range(x, start_stop, y)

    def testSliceRangeYDecreasing(self):
        '''slice_range works when y is monotonically decreasing'''
        x = np.arange(10)
        y = np.flip(np.arange(10))
        start_stop = [5, 2]
        self.assertTrue(np.array_equal(
            np.array([4, 5, 6, 7]),
            spectrum_functions.slice_range(x, start_stop, y)
            ))

    def testNormalize1Index(self):
        '''Normalize works as expected when a single index is given'''
        x = np.arange(10)
        ind = 2
        np.testing.assert_allclose(
            x/2.,
            spectrum_functions.normalize(x, ind)
            )

    def testNormalizeFloatIndex(self):
        '''Normalize throws an error when given a float index'''
        x = np.arange(10)
        ind = 2.4
        with self.assertRaises(ValueError):
           spectrum_functions.normalize(x, ind)

    def testNormalize1IndexTuple(self):
        '''Normalize throws an error if a single index inside a sequence
         is given'''
        x = np.arange(10)
        ind = [3]
        with self.assertRaises(ValueError):
           spectrum_functions.normalize(x, ind)

    def testNormalize2Indices(self):
        '''Normalize works as expected when two indices are given'''
        x = np.arange(10)
        ind = (2, 5)
        np.testing.assert_allclose(
            x/9.,
            spectrum_functions.normalize(x, ind)
            )

    def testNormalizeMoreIndices(self):
        '''Normalize raises an error if more than two indices are passed as
        input'''
        x = np.arange(10)
        ind = (2, 5, 3)
        with self.assertRaises(ValueError):
           spectrum_functions.normalize(x, ind)

    def testFindFWHMInt(self):
        '''
        find_fw finds the right fw given a simple function
        '''
        y = np.array([1, 1, 2, 4, 2, 1, 1])
        x = np.arange(7)
        fwhm = 2.
        self.assertEqual(fwhm, spectrum_functions.find_fw(y, 1, 3, 0.5))

    def testFindFWHMDecimal(self):
        '''
        find_fw finds the right fw given a simple function, answer is a fraction of the dispersion
        '''
        y = np.array([1, 1, 2, 5, 2, 1, 1])
        x = np.arange(7)
        fwhm = 5/3.
        np.testing.assert_almost_equal(spectrum_functions.find_fw(y, 1, 3, 0.5), fwhm)

    def testFindFWAsymmetrical(self):
        '''
        find_fw finds the right fw given an asymmetrical function
        '''
        y = np.array([1, 1, 3, 5, 2, 1, 1])
        x = np.arange(7)
        fwhm = 2.5 / 3 + 1 + 0.25
        self.assertEqual(fwhm, spectrum_functions.find_fw(y, 1, 3, 0.5))

    def testFindFWAsymmetricalRight(self):
        '''
        find_fw finds the right fw given an asymmetrical function, higher on the right side
        '''
        y = np.array([1, 1, 2, 5, 3, 1, 1])
        x = np.arange(7)
        fwhm = 2.5 / 3 + 1 + 0.25
        self.assertEqual(fwhm, spectrum_functions.find_fw(y, 1, 3, 0.5))

if __name__ == '__main__':
    unittest.main()


# Migrating from Python 2 to Python 3
# Examples of Chapter 1

# The next code gets an error in Python 2
# (If you try with the GUI, this error was fixed at the lastest versions)

print 'Spain in Spanish is written España'

# In Python 3, dont gets a error

print('Spain in Spanish is written España')

#
# Print is a function:
#

# Python 2:

print "hello world"

# Python 3

print("hello world")

#
# Divisions:
#

# In Python 2

1/2 # returns 1
1/2.0 # return 0.5

# In Python 3

1/2 #return 0.5
4/2 #return 2.0

#
# Input:
#

# In Python 2:

my_input=input("Introduce anything: ")
type(my_input) # return <type 'int'>

my_input=raw_input("Introduce anything: ")
type(my_input) # return <type 'str'>

# In Python 3:

my_input=input("Introduce anything: ")
type(my_input) #return <class 'str'>

#
# Next is a function
#

# In Python 2:

x = iter([1, 2, 3])
x.next() # is valid
next(x) # is valid

# In Python 3:

x = iter([1, 2, 3])
x.next() # NOT is valid
next(x) # is valid

#
# Differences in type comparison
#

# In Python 2:

(1,2)>[1,2] # return True
[1,2,3,4,5] > "hey" # return False
"Hey" > 0 # return True

# In Python 3:

(1,2)>[1,2] # gets errror
[1,2,3,4,5] > "hey" # gets errror
"Hey" > 0 # gets errror

#
#Exceptions
#

# In Python 2:

raise IOError, "activate file exception" # is correct
raise IOError("activate file exception") # is correct

# In Python 3:

raise IOError, "activate file exception" # gets errror
raise IOError("activate file exception") # is correct

#
# news in Python 3.0
#

# new unicode identifiers:
España=True
π = math.pi

# Keyword-Only Arguments

def f(a,*b,c):
 return a+len(b)+c

f(1,c=2) # return 3
f(1,2,c=2) # return 4

# Advanced unpacking

 a, b, *rest = range(10)

#
# news in Python 3.1
#

# Format function

format(1234567, ',d') # return '1,234,567'
format(1234567.89, ',.2f') # return '1,234,567.89'

# with statement

with open('my_file.txt') as infile, open('a.out', 'w') as outfile:
     for line in infile:
         if '<critical>' in line:
             outfile.write(line)

#
# news in Python 3.2
#

# New module argparse

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('action',choices = ['curl'])
parser.add_argument('-m',required = False)
parser.add_argument('targets',metavar = 'HOSTNAME',nargs = '+')

cmd = 'curl -m 10 pythonconverter.com'
result = parser.parse_args(cmd.split())
result.action # return 'curl'
result.targets # return ['pythonconverter.com']
result.m # return '10'

#
# news in Python 3.3
#

# yield from

def g(x):
 yield from range(x, 0, -1)
 yield from range(x, 10, 1)
print(list(g(5))) # return [5, 4, 3, 2, 1, 5, 6, 7, 8, 9]

# Managing the IP address

import ipaddress
ipaddress.ip_address('192.168.0.1') # return IPv4Address('192.168.0.1')

#
# news in Python 3.5
#

# New operator for matrix multiplication: @

import numpy
x = numpy.ones(3)
x # return array([ 1., 1., 1.])
m = numpy.eye(3)
m # return
#array([[ 1., 0., 0.],
#[ 0., 1., 0.],
#[ 0., 0., 1.]])

x @ m # return
array([ 1., 1., 1.])

# Additional Unpacking Generalizations

print(*[1], *[2], 3) # return 1 2 3

dict(**{'x': 1}, y=2, **{'z': 3}) # return {'x': 1, 'y': 2, 'z': 3}

#
# news in Python 3.6
#

1_000_000 # return 1000000

version = "3"
f"Python {version}" # return 'Python 3'


#!/usr/bin/env python
# coding: utf-8

# **Tools - matplotlib**
#
# *This notebook demonstrates how to use the matplotlib library to plot beautiful graphs.*

# # Table of Contents
#

1  Plotting your first graph

2  Line style and color

3  Saving a figure

4  Subplots

5  Multiple figures

6  Pyplot's state machine: implicit vs explicit

7  Pylab vs Pyplot vs Matplotlib

8  Drawing text

9  Legends

10  Non linear scales

11  Ticks and tickers

12  Polar projection

13  3D projection

14  Scatter plot

15  Lines

16  Histograms

17  Images

18  Animations

19  Saving animations to video files

20  What next?

# # Plotting your first graph

# First we need to import the `matplotlib` library.

# In[1]:

import matplotlib

# Matplotlib can output graphs using various backend graphics libraries, such as Tk, wxPython, etc.  When running python using the command line, the graphs are typically shown in a separate window. In a Jupyter notebook, we can simply output the graphs within the notebook itself by running the `%matplotlib inline` magic command.

# In[2]:

get_ipython().run_line_magic('matplotlib', 'inline')
# matplotlib.use("TKAgg")  # use this instead in your program if you want to use Tk as your graphics backend.

# Now let's plot our first graph! :)

# In[4]:

import matplotlib.pyplot as plt
plt.plot([1, 2, 4, 9, 5, 3])
plt.show()

# Yep, it's as simple as calling the `plot` function with some data, and then calling the `show` function!
#
# If the `plot` function is given one array of data, it will use it as the coordinates on the vertical axis, and it will just use each data point's index in the array as the horizontal coordinate.
# You can also provide two arrays: one for the horizontal axis `x`, and the second for the vertical axis `y`:

# In[5]:

plt.plot([-3, -2, 5, 0], [1, 6, 4, 3])
plt.show()

# The axes automatically match the extent of the data.  We would like to give the graph a bit more room, so let's call the `axis` function to change the extent of each axis `[xmin, xmax, ymin, ymax]`.

# In[6]:

plt.plot([-3, -2, 5, 0], [1, 6, 4, 3])
plt.axis([-4, 6, 0, 7])
plt.show()

# Now, let's plot a mathematical function. We use NumPy's `linspace` function to create an array `x` containing 500 floats ranging from -2 to 2, then we create a second array `y` computed as the square of `x` (to learn about NumPy, read the [NumPy tutorial](tools_numpy.ipynb)).

# In[7]:

import numpy as np
x = np.linspace(-2, 2, 500)
y = x**2

plt.plot(x, y)
plt.show()

# That's a bit dry, let's add a title, and x and y labels, and draw a grid.

# In[8]:

plt.plot(x, y)
plt.title("Square function")
plt.xlabel("x")
plt.ylabel("y = x**2")
plt.grid(True)
plt.show()

# # Line style and color

# By default, matplotlib draws a line between consecutive points.

# In[9]:

plt.plot([0, 100, 100, 0, 0, 100, 50, 0, 100], [0, 0, 100, 100, 0, 100, 130, 100, 0])
plt.axis([-10, 110, -10, 140])
plt.show()

# You can pass a 3rd argument to change the line's style and color.
# For example `"g--"` means "green dashed line".

# In[10]:

plt.plot([0, 100, 100, 0, 0, 100, 50, 0, 100], [0, 0, 100, 100, 0, 100, 130, 100, 0], "g--")
plt.axis([-10, 110, -10, 140])
plt.show()

# You can plot multiple lines on one graph very simply: just pass `x1, y1, [style1], x2, y2, [style2], ...`
#
# For example:

# In[11]:

plt.plot([0, 100, 100, 0, 0], [0, 0, 100, 100, 0], "r-", [0, 100, 50, 0, 100], [0, 100, 130, 100, 0], "g--")
plt.axis([-10, 110, -10, 140])
plt.show()

# Or simply call `plot` multiple times before calling `show`.

# In[12]:

plt.plot([0, 100, 100, 0, 0], [0, 0, 100, 100, 0], "r-")
plt.plot([0, 100, 50, 0, 100], [0, 100, 130, 100, 0], "g--")
plt.axis([-10, 110, -10, 140])
plt.show()

# You can also draw simple points instead of lines. Here's an example with green dashes, red dotted line and blue triangles.
# Check out [the documentation](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plot) for the full list of style & color options.

# In[13]:

x = np.linspace(-1.4, 1.4, 30)
plt.plot(x, x, 'g--', x, x**2, 'r:', x, x**3, 'b^')
plt.show()

# The plot function returns a list of `Line2D` objects (one for each line).  You can set extra attributes on these lines, such as the line width, the dash style or the alpha level.  See the full list of attributes in [the documentation](http://matplotlib.org/users/pyplot_tutorial.html#controlling-line-properties).

# In[14]:

x = np.linspace(-1.4, 1.4, 30)
line1, line2, line3 = plt.plot(x, x, 'g--', x, x**2, 'r:', x, x**3, 'b^')
line1.set_linewidth(3.0)
line1.set_dash_capstyle("round")
line3.set_alpha(0.2)
plt.show()

# # Saving a figure
# Saving a figure to disk is as simple as calling [`savefig`](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.savefig) with the name of the file (or a file object). The available image formats depend on the graphics backend you use.

# In[15]:

x = np.linspace(-1.4, 1.4, 30)
plt.plot(x, x**2)
plt.savefig("my_square_function.png", transparent=True)

# # Subplots
# A matplotlib figure may contain multiple subplots. These subplots are organized in a grid. To create a subplot, just call the `subplot` function, and specify the number of rows and columns in the figure, and the index of the subplot you want to draw on (starting from 1, then left to right, and top to bottom). Note that pyplot keeps track of the currently active subplot (which you can get a reference to by calling `plt.gca()`), so when you call the `plot` function, it draws on the *active* subplot.
#

# In[16]:

x = np.linspace(-1.4, 1.4, 30)
plt.subplot(2, 2, 1)  # 2 rows, 2 columns, 1st subplot = top left
plt.plot(x, x)
plt.subplot(2, 2, 2)  # 2 rows, 2 columns, 2nd subplot = top right
plt.plot(x, x**2)
plt.subplot(2, 2, 3)  # 2 rows, 2 columns, 3rd subplot = bottow left
plt.plot(x, x**3)
plt.subplot(2, 2, 4)  # 2 rows, 2 columns, 4th subplot = bottom right
plt.plot(x, x**4)
plt.show()

# * Note that `subplot(223)` is a shorthand for `subplot(2, 2, 3)`.

# It is easy to create subplots that span across multiple grid cells like so:

# In[17]:

plt.subplot(2, 2, 1)  # 2 rows, 2 columns, 1st subplot = top left
plt.plot(x, x)
plt.subplot(2, 2, 2)  # 2 rows, 2 columns, 2nd subplot = top right
plt.plot(x, x**2)
plt.subplot(2, 1, 2)  # 2 rows, *1* column, 2nd subplot = bottom
plt.plot(x, x**3)
plt.show()

# If you need more complex subplot positionning, you can use `subplot2grid` instead of `subplot`. You specify the number of rows and columns in the grid, then your subplot's position in that grid (top-left = (0,0)), and optionally how many rows and/or columns it spans.  For example:

# In[18]:

plt.subplot2grid((3,3), (0, 0), rowspan=2, colspan=2)
plt.plot(x, x**2)
plt.subplot2grid((3,3), (0, 2))
plt.plot(x, x**3)
plt.subplot2grid((3,3), (1, 2), rowspan=2)
plt.plot(x, x**4)
plt.subplot2grid((3,3), (2, 0), colspan=2)
plt.plot(x, x**5)
plt.show()

# If you need even more flexibility in subplot positioning, check out the [GridSpec documentation](http://matplotlib.org/users/gridspec.html)

# # Multiple figures
# It is also possible to draw multiple figures. Each figure may contain one or more subplots. By default, matplotlib creates `figure(1)` automatically. When you switch figure, pyplot keeps track of the currently active figure (which you can get a reference to by calling `plt.gcf()`), and the active subplot of that figure becomes the current subplot.

# In[19]:

x = np.linspace(-1.4, 1.4, 30)

plt.figure(1)
plt.subplot(211)
plt.plot(x, x**2)
plt.title("Square and Cube")
plt.subplot(212)
plt.plot(x, x**3)

plt.figure(2, figsize=(10, 5))
plt.subplot(121)
plt.plot(x, x**4)
plt.title("y = x**4")
plt.subplot(122)
plt.plot(x, x**5)
plt.title("y = x**5")

plt.figure(1)      # back to figure 1, current subplot is 212 (bottom)
plt.plot(x, -x**3, "r:")

plt.show()

# # Pyplot's state machine: implicit *vs* explicit
# So far we have used Pyplot's state machine which keeps track of the currently active subplot. Every time you call the `plot` function, pyplot just draws on the currently active subplot. It also does some more magic, such as automatically creating a figure and a subplot when you call `plot`, if they don't exist yet. This magic is convenient in an interactive environment (such as Jupyter).
#
# But when you are writing a program, *explicit is better than implicit*. Explicit code is usually easier to debug and maintain, and if you don't believe me just read the 2nd rule in the Zen of Python:

# In[20]:

import this

# Fortunately, Pyplot allows you to ignore the state machine entirely, so you can write beautifully explicit code. Simply call the `subplots` function and use the figure object and the list of axes objects that are returned. No more magic! For example:

# In[21]:

x = np.linspace(-2, 2, 200)
fig1, (ax_top, ax_bottom) = plt.subplots(2, 1, sharex=True)
fig1.set_size_inches(10,5)
line1, line2 = ax_top.plot(x, np.sin(3*x**2), "r-", x, np.cos(5*x**2), "b-")
line3, = ax_bottom.plot(x, np.sin(3*x), "r-")
ax_top.grid(True)

fig2, ax = plt.subplots(1, 1)
ax.plot(x, x**2)
plt.show()

# For consistency, we will continue to use pyplot's state machine in the rest of this tutorial, but we recommend using the object-oriented interface in your programs.
#
# # Pylab *vs* Pyplot *vs* Matplotlib
#
# There is some confusion around the relationship between pylab, pyplot and matplotlib. It's simple: matplotlib is the full library, it contains everything including pylab and pyplot.
#
# Pyplot provides a number of tools to plot graphs, including the state-machine interface to the underlying object-oriented plotting library.
#
# Pylab is a convenience module that imports matplotlib.pyplot and NumPy in a single name space. You will find many examples using pylab, but it is no longer recommended (because *explicit* imports are better than *implicit* ones).

# # Drawing text
# You can call `text` to add text at any location in the graph. Just specify the horizontal and vertical coordinates and the text, and optionally some extra attributes.  Any text in matplotlib may contain TeX equation expressions, see [the documentation](http://matplotlib.org/users/mathtext.html) for more details.

# In[22]:

x = np.linspace(-1.5, 1.5, 30)
px = 0.8
py = px**2

plt.plot(x, x**2, "b-", px, py, "ro")

plt.text(0, 1.5, "Square function\n$y = x^2$", fontsize=20, color='blue', horizontalalignment="center")
plt.text(px - 0.08, py, "Beautiful point", ha="right", weight="heavy")
plt.text(px, py, "x = %0.2f\ny = %0.2f"%(px, py), rotation=50, color='gray')

plt.show()

# * Note: `ha` is an alias for `horizontalalignment`
#
# For more text properties, visit [the documentation](http://matplotlib.org/users/text_props.html#text-properties).
#
# It is quite frequent to annotate elements of a graph, such as the beautiful point above. The `annotate` function makes this easy: just indicate the location of the point of interest, and the position of the text, plus optionally some extra attributes for the text and the arrow.

# In[23]:

plt.plot(x, x**2, px, py, "ro")
plt.annotate("Beautiful point", xy=(px, py), xytext=(px-1.3,py+0.5),
                           color="green", weight="heavy", fontsize=14,
                           arrowprops={"facecolor": "lightgreen"})
plt.show()

# You can also add a bounding box around your text by using the `bbox` attribute:

# In[24]:

plt.plot(x, x**2, px, py, "ro")

bbox_props = dict(boxstyle="rarrow,pad=0.3", ec="b", lw=2, fc="lightblue")
plt.text(px-0.2, py, "Beautiful point", bbox=bbox_props, ha="right")

bbox_props = dict(boxstyle="round4,pad=1,rounding_size=0.2", ec="black", fc="#EEEEFF", lw=5)
plt.text(0, 1.5, "Square function\n$y = x^2$", fontsize=20, color='black', ha="center", bbox=bbox_props)

plt.show()

# Just for fun, if you want an [xkcd](http://xkcd.com)-style plot, just draw within a `with plt.xkcd()` section:

# In[25]:

with plt.xkcd():
    plt.plot(x, x**2, px, py, "ro")

    bbox_props = dict(boxstyle="rarrow,pad=0.3", ec="b", lw=2, fc="lightblue")
    plt.text(px-0.2, py, "Beautiful point", bbox=bbox_props, ha="right")

    bbox_props = dict(boxstyle="round4,pad=1,rounding_size=0.2", ec="black", fc="#EEEEFF", lw=5)
    plt.text(0, 1.5, "Square function\n$y = x^2$", fontsize=20, color='black', ha="center", bbox=bbox_props)

    plt.show()

# # Legends
# The simplest way to add a legend is to set a label on all lines, then just call the `legend` function.

# In[26]:

x = np.linspace(-1.4, 1.4, 50)
plt.plot(x, x**2, "r--", label="Square function")
plt.plot(x, x**3, "g-", label="Cube function")
plt.legend(loc="best")
plt.grid(True)
plt.show()

# # Non linear scales
# Matplotlib supports non linear scales, such as logarithmic or logit scales.

# In[27]:

x = np.linspace(0.1, 15, 500)
y = x**3/np.exp(2*x)

plt.figure(1)
plt.plot(x, y)
plt.yscale('linear')
plt.title('linear')
plt.grid(True)

plt.figure(2)
plt.plot(x, y)
plt.yscale('log')
plt.title('log')
plt.grid(True)

plt.figure(3)
plt.plot(x, y)
plt.yscale('logit')
plt.title('logit')
plt.grid(True)

plt.figure(4)
plt.plot(x, y - y.mean())
plt.yscale('symlog', linthreshy=0.05)
plt.title('symlog')
plt.grid(True)

plt.show()

# # Ticks and tickers
# The axes have little marks called "ticks".  To be precise, "ticks" are the *locations* of the marks (eg. (-1, 0, 1)), "tick lines" are the small lines drawn at those locations, "tick labels" are the labels drawn next to the tick lines, and "tickers" are objects that are capable of deciding where to place ticks. The default tickers typically do a pretty good job at placing ~5 to 8 ticks at a reasonable distance from one another.
#
# But sometimes you need more control (eg. there are too many tick labels on the logit graph above). Fortunately, matplotlib gives you full control over ticks.  You can even activate minor ticks.
#
#

# In[28]:

x = np.linspace(-2, 2, 100)

plt.figure(1, figsize=(15,10))
plt.subplot(131)
plt.plot(x, x**3)
plt.grid(True)
plt.title("Default ticks")

ax = plt.subplot(132)
plt.plot(x, x**3)
ax.xaxis.set_ticks(np.arange(-2, 2, 1))
plt.grid(True)
plt.title("Manual ticks on the x-axis")

ax = plt.subplot(133)
plt.plot(x, x**3)
plt.minorticks_on()
ax.tick_params(axis='x', which='minor', bottom='off')
ax.xaxis.set_ticks([-2, 0, 1, 2])
ax.yaxis.set_ticks(np.arange(-5, 5, 1))
ax.yaxis.set_ticklabels(["min", -4, -3, -2, -1, 0, 1, 2, 3, "max"])
plt.title("Manual ticks and tick labels\n(plus minor ticks) on the y-axis")

plt.grid(True)

plt.show()

# # Polar projection
# Drawing a polar graph is as easy as setting the `projection` attribute to `"polar"` when creating the subplot.

# In[29]:

radius = 1
theta = np.linspace(0, 2*np.pi*radius, 1000)

plt.subplot(111, projection='polar')
plt.plot(theta, np.sin(5*theta), "g-")
plt.plot(theta, 0.5*np.cos(20*theta), "b-")
plt.show()

# # 3D projection
#
# Plotting 3D graphs is quite straightforward. You need to import `Axes3D`, which registers the `"3d"` projection. Then create a subplot setting the `projection` to `"3d"`. This returns an `Axes3DSubplot` object, which you can use to call `plot_surface`, giving x, y, and z coordinates, plus optional attributes.

# In[30]:

from mpl_toolkits.mplot3d import Axes3D

x = np.linspace(-5, 5, 50)
y = np.linspace(-5, 5, 50)
X, Y = np.meshgrid(x, y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)

figure = plt.figure(1, figsize = (12, 4))
subplot3d = plt.subplot(111, projection='3d')
surface = subplot3d.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=matplotlib.cm.coolwarm, linewidth=0.1)
plt.show()

# Another way to display this same data is *via* a contour plot.

# In[31]:

plt.contourf(X, Y, Z, cmap=matplotlib.cm.coolwarm)
plt.colorbar()
plt.show()

# # Scatter plot

# To draw a scatter plot, simply provide the x and y coordinates of the points.

# In[32]:

from numpy.random import rand
x, y = rand(2, 100)
plt.scatter(x, y)
plt.show()

# You may also optionally provide the scale of each point.

# In[33]:

x, y, scale = rand(3, 100)
scale = 500 * scale ** 5
plt.scatter(x, y, s=scale)
plt.show()

# And as usual there are a number of other attributes you can set, such as the fill and edge colors and the alpha level.

# In[34]:

for color in ['red', 'green', 'blue']:
    n = 100
    x, y = rand(2, n)
    scale = 500.0 * rand(n) ** 5
    plt.scatter(x, y, s=scale, c=color, alpha=0.3, edgecolors='blue')

plt.grid(True)

plt.show()

# # Lines
# You can draw lines simply using the `plot` function, as we have done so far. However, it is often convenient to create a utility function that plots a (seemingly) infinite line across the graph, given a slope and an intercept. You can also use the `hlines` and `vlines` functions that plot horizontal and vertical line segments.
# For example:

# In[35]:

from numpy.random import randn

def plot_line(axis, slope, intercept, **kargs):
    xmin, xmax = axis.get_xlim()
    plt.plot([xmin, xmax], [xmin*slope+intercept, xmax*slope+intercept], **kargs)

x = randn(1000)
y = 0.5*x + 5 + randn(1000)*2
plt.axis([-2.5, 2.5, -5, 15])
plt.scatter(x, y, alpha=0.2)
plt.plot(1, 0, "ro")
plt.vlines(1, -5, 0, color="red")
plt.hlines(0, -2.5, 1, color="red")
plot_line(axis=plt.gca(), slope=0.5, intercept=5, color="magenta")
plt.grid(True)
plt.show()

# # Histograms

# In[36]:

data = [1, 1.1, 1.8, 2, 2.1, 3.2, 3, 3, 3, 3]
plt.subplot(211)
plt.hist(data, bins = 10, rwidth=0.8)

plt.subplot(212)
plt.hist(data, bins = [1, 1.5, 2, 2.5, 3], rwidth=0.95)
plt.xlabel("Value")
plt.ylabel("Frequency")

plt.show()

# In[37]:

data1 = np.random.randn(400)
data2 = np.random.randn(500) + 3
data3 = np.random.randn(450) + 6
data4a = np.random.randn(200) + 9
data4b = np.random.randn(100) + 10

plt.hist(data1, bins=5, color='g', alpha=0.75, label='bar hist') # default histtype='bar'
plt.hist(data2, color='b', alpha=0.65, histtype='stepfilled', label='stepfilled hist')
plt.hist(data3, color='r', histtype='step', label='step hist')
plt.hist((data4a, data4b), color=('r','m'), alpha=0.55, histtype='barstacked', label=('barstacked a', 'barstacked b'))

plt.xlabel("Value")
plt.ylabel("Frequency")
plt.legend()
plt.grid(True)
plt.show()

# # Images
# Reading, generating and plotting images in matplotlib is quite straightforward.
#
# To read an image, just import the `matplotlib.image` module, and call its `imread` function, passing it the file name (or file object). This returns the image data, as a NumPy array. Let's try this with the `my_square_function.png` image we saved earlier.

# In[38]:

import matplotlib.image as mpimg

img = mpimg.imread('my_square_function.png')
print(img.shape, img.dtype)

# We have loaded a 288x432 image. Each pixel is represented by a 4-element array: red, green, blue, and alpha levels, stored as 32-bit floats between 0 and 1.  Now all we need to do is to call `imshow`:

# In[39]:

plt.imshow(img)
plt.show()

# Tadaaa! You may want to hide the axes when you are displaying an image:

# In[40]:

plt.imshow(img)
plt.axis('off')
plt.show()

# It's just as easy to generate your own image:

# In[41]:

img = np.arange(100*100).reshape(100, 100)
print(img)
plt.imshow(img)
plt.show()

# As we did not provide RGB levels, the `imshow` function automatically maps values to a color gradient. By default, the color gradient goes from blue (for low values) to red (for high values), but you can select another color map.  For example:

# In[42]:

plt.imshow(img, cmap="hot")
plt.show()

# You can also generate an RGB image directly:

# In[43]:

img = np.empty((20,30,3))
img[:, :10] = [0, 0, 0.6]
img[:, 10:20] = [1, 1, 1]
img[:, 20:] = [0.6, 0, 0]
plt.imshow(img)
plt.show()

# Since the `img` array is just quite small (20x30), when the `imshow` function displays it, it grows the image to the figure's size. By default it uses [bilinear interpolation](https://en.wikipedia.org/wiki/Bilinear_interpolation) to fill the added pixels. This is why the edges look blurry.
# You can select another interpolation algorithm, such as copying the color of the nearest pixel:

# In[44]:

plt.imshow(img, interpolation="nearest")
plt.show()

# # Animations
# Although matplotlib is mostly used to generate images, it is also capable of displaying animations, depending on the Backend you use. In a Jupyter notebook, we need to use the `nbagg` backend to use interactive matplotlib features, including animations. We also need to import `matplotlib.animation`.

# In[45]:

get_ipython().run_line_magic('matplotlib', 'nbagg')
import matplotlib.animation as animation

# In this example, we start by creating data points, then we create an empty plot, we define the update function that will be called at every iteration of the animation, and finally we add an animation to the plot by creating a `FuncAnimation` instance.
#
# The `FuncAnimation` constructor takes a figure, an update function and optional arguments. We specify that we want a 100-frame long animation, with 20ms between each frame. At each iteration, `FuncAnimation` calls our update function and passes it the frame number `num` (from 0 to 99 in our case) followed by the extra arguments that we specified with `fargs`.
#
# Our update function simply sets the line data to be the first `num` data points (so the data gets drawn gradually), and just for fun we also add a small random number to each data point so that the line appears to wiggle.

# In[46]:

x = np.linspace(-1, 1, 100)
y = np.sin(x**2*25)
data = np.array([x, y])

fig = plt.figure()
line, = plt.plot([], [], "r-") # start with an empty plot
plt.axis([-1.1, 1.1, -1.1, 1.1])
plt.plot([-0.5, 0.5], [0, 0], "b-", [0, 0], [-0.5, 0.5], "b-", 0, 0, "ro")
plt.grid(True)
plt.title("Marvelous animation")

# this function will be called at every iteration
def update_line(num, data, line):
    line.set_data(data[..., :num] + np.random.rand(2, num) / 25)  # we only plot the first `num` data points.
    return line,

line_ani = animation.FuncAnimation(fig, update_line, frames=100, fargs=(data, line), interval=67)
plt.show()

# # Saving animations to video files
# Matplotlib relies on 3rd-party libraries to write videos such as [FFMPEG](https://www.ffmpeg.org/) or `mencoder`. In this example we will be using FFMPEG so be sure to install it first.

# In[47]:

Writer = animation.writers['ffmpeg']
writer = Writer(fps=15, metadata=dict(artist='Me'), bitrate=1800)
line_ani.save('my_wiggly_animation.mp4', writer=writer)

# # What next?
# Now you know all the basics of matplotlib, but there are many more options available. The best way to learn more, is to visit the [gallery](http://matplotlib.org/gallery.html), look at the images, choose a plot that you are interested in, then just copy the code in a Jupyter notebook and play around with it.


r"""
Tables

Display a rectangular array as a table, either in plain text, LaTeX,
or html.  See the documentation for :class:`table` for details and
examples.

AUTHORS:

- John H. Palmieri (2012-11)
"""

from io import StringIO

from sage.structure.sage_object import SageObject
from sage.misc.cachefunc import cached_method

class table(SageObject):
    r"""
    Display a rectangular array as a table, either in plain text, LaTeX,
    or html.

    INPUT:

    - ``rows`` (default ``None``) - a list of lists (or list of tuples,
      etc.), containing the data to be displayed.
    - ``columns`` (default ``None``) - a list of lists (etc.), containing
      the data to be displayed, but stored as columns. Set either ``rows``
      or ``columns``, but not both.
    - ``header_row`` (default ``False``) - if ``True``, first row is
      highlighted.
    - ``header_column`` (default ``False``) - if ``True``, first column is
      highlighted.
    - ``frame`` (default ``False``) - if ``True``, put a box around each
      cell.
    - ``align`` (default 'left') - the alignment of each entry: either
      'left', 'center', or 'right'

    EXAMPLES::

        sage: rows = [['a', 'b', 'c'], [100,2,3], [4,5,60]]
        sage: table(rows)
          a     b   c
          100   2   3
          4     5   60
        sage: latex(table(rows))
        \begin{tabular}{lll}
        a & b & c \\
        $100$ & $2$ & $3$ \\
        $4$ & $5$ & $60$ \\
        \end{tabular}

    If ``header_row`` is ``True``, then the first row is highlighted. If
    ``header_column`` is ``True``, then the first column is
    highlighted. If ``frame`` is ``True``, then print a box around every
    "cell". ::

        sage: table(rows, header_row=True)
          a     b   c
        +-----+---+----+
          100   2   3
          4     5   60
        sage: latex(table(rows, header_row=True))
        \begin{tabular}{lll}
        a & b & c \\ \hline
        $100$ & $2$ & $3$ \\
        $4$ & $5$ & $60$ \\
        \end{tabular}
        sage: table(rows=rows, frame=True)
        +-----+---+----+
        | a   | b | c  |
        +-----+---+----+
        | 100 | 2 | 3  |
        +-----+---+----+
        | 4   | 5 | 60 |
        +-----+---+----+
        sage: latex(table(rows=rows, frame=True))
        \begin{tabular}{|l|l|l|} \hline
        a & b & c \\ \hline
        $100$ & $2$ & $3$ \\ \hline
        $4$ & $5$ & $60$ \\ \hline
        \end{tabular}
        sage: table(rows, header_column=True, frame=True)
        +-----++---+----+
        | a   || b | c  |
        +-----++---+----+
        | 100 || 2 | 3  |
        +-----++---+----+
        | 4   || 5 | 60 |
        +-----++---+----+
        sage: latex(table(rows, header_row=True, frame=True))
        \begin{tabular}{|l|l|l|} \hline
        a & b & c \\ \hline \hline
        $100$ & $2$ & $3$ \\ \hline
        $4$ & $5$ & $60$ \\ \hline
        \end{tabular}
        sage: table(rows, header_column=True)
          a   | b   c
          100 | 2   3
          4   | 5   60

    The argument ``header_row`` can, instead of being ``True`` or
    ``False``, be the contents of the header row, so that ``rows``
    consists of the data, while ``header_row`` is the header
    information.  The same goes for ``header_column``. Passing lists
    for both arguments simultaneously is not supported. ::

        sage: table([(x,n(sin(x), digits=2)) for x in [0..3]], header_row=["$x$", r"$\sin(x)$"], frame=True)
        +-----+-----------+
        | $x$ | $\sin(x)$ |
        +=====+===========+
        | 0   | 0.00      |
        +-----+-----------+
        | 1   | 0.84      |
        +-----+-----------+
        | 2   | 0.91      |
        +-----+-----------+
        | 3   | 0.14      |
        +-----+-----------+

    You can create the transpose of this table in several ways, for
    example, "by hand," that is, changing the data defining the table::

        sage: table(rows=[[x for x in [0..3]], [n(sin(x), digits=2) for x in [0..3]]], header_column=['$x$', r'$\sin(x)$'], frame=True)
        +-----------++------+------+------+------+
        | $x$       || 0    | 1    | 2    | 3    |
        +-----------++------+------+------+------+
        | $\sin(x)$ || 0.00 | 0.84 | 0.91 | 0.14 |
        +-----------++------+------+------+------+

    or by passing the original data as the ``columns`` of the table
    and using ``header_column`` instead of ``header_row``::

        sage: table(columns=[(x,n(sin(x), digits=2)) for x in [0..3]], header_column=['$x$', r'$\sin(x)$'], frame=True)
        +-----------++------+------+------+------+
        | $x$       || 0    | 1    | 2    | 3    |
        +-----------++------+------+------+------+
        | $\sin(x)$ || 0.00 | 0.84 | 0.91 | 0.14 |
        +-----------++------+------+------+------+

    or by taking the :meth:`transpose` of the original table::

        sage: table(rows=[(x,n(sin(x), digits=2)) for x in [0..3]], header_row=['$x$', r'$\sin(x)$'], frame=True).transpose()
        +-----------++------+------+------+------+
        | $x$       || 0    | 1    | 2    | 3    |
        +-----------++------+------+------+------+
        | $\sin(x)$ || 0.00 | 0.84 | 0.91 | 0.14 |
        +-----------++------+------+------+------+

    In either plain text or LaTeX, entries in tables can be aligned to the
    left (default), center, or right::

        sage: table(rows, align='left')
          a     b   c
          100   2   3
          4     5   60
        sage: table(rows, align='center')
          a    b   c
         100   2   3
          4    5   60
        sage: table(rows, align='right', frame=True)
        +-----+---+----+
        |   a | b |  c |
        +-----+---+----+
        | 100 | 2 |  3 |
        +-----+---+----+
        |   4 | 5 | 60 |
        +-----+---+----+

    To generate HTML you should use ``html(table(...))``::

        sage: data = [["$x$", r"$\sin(x)$"]] + [(x,n(sin(x), digits=2)) for x in [0..3]]
        sage: output = html(table(data, header_row=True, frame=True))
        sage: type(output)
        <class 'sage.misc.html.HtmlFragment'>
        sage: print(output)

        \(x\)
        \(\sin(x)\)

        \(0\)
        \(0.00\)

        \(1\)
        \(0.84\)

        \(2\)
        \(0.91\)

        \(3\)
        \(0.14\)

    It is an error to specify both ``rows`` and ``columns``::

        sage: table(rows=[[1,2,3], [4,5,6]], columns=[[0,0,0], [0,0,1024]])
        Traceback (most recent call last):
        ...
        ValueError: Don't set both 'rows' and 'columns' when defining a table.

        sage: table(columns=[[0,0,0], [0,0,1024]])
        0 0
        0 0
        0 1024

    Note that if ``rows`` is just a list or tuple, not nested, then
    it is treated as a single row::

        sage: table([1,2,3])
        1   2   3

    Also, if you pass a non-rectangular array, the longer rows or
    columns get truncated::

        sage: table([[1,2,3,7,12], [4,5]])
        1   2
        4   5
        sage: table(columns=[[1,2,3], [4,5,6,7]])
        1   4
        2   5
        3   6

    TESTS::

        sage: TestSuite(table([["$x$", r"$\sin(x)$"]] +
        ....:                  [(x,n(sin(x), digits=2)) for x in [0..3]],
        ....:                 header_row=True, frame=True)).run()

    .. automethod:: _rich_repr_
    """
    def __init__(self, rows=None, columns=None, header_row=False,
                 header_column=False, frame=False, align='left'):
        r"""
        EXAMPLES::

            sage: table([1,2,3], frame=True)
            +---+---+---+
            | 1 | 2 | 3 |
            +---+---+---+
        """
        # If both rows and columns are set, raise an error.
        if rows and columns:
            raise ValueError("Don't set both 'rows' and 'columns' when defining a table.")
        # If columns is set, use its transpose for rows.
        if columns:
            rows = list(zip(*columns))
        # Set the rest of the options.
        self._options = {}
        if header_row is True:
            self._options['header_row'] = True
        elif header_row:
            self._options['header_row'] = True
            rows = [header_row] + rows
        else:
            self._options['header_row'] = False
        if header_column is True:
            self._options['header_column'] = True
        elif header_column:
            self._options['header_column'] = True
            rows = [(a,) + tuple(x) for (a,x) in zip(header_column, rows)]
        else:
            self._options['header_column'] = False

        self._options['frame'] = frame
        self._options['align'] = align
        # Store rows as a tuple.
        if not isinstance(rows[0], (list, tuple)):
            rows = (rows,)
        self._rows = tuple(rows)

    def __eq__(self, other):
        r"""
        Two tables are equal if and only if their data rowss and
        their options are the same.

        EXAMPLES::

            sage: rows = [['a', 'b', 'c'], [1,plot(sin(x)),3], [4,5,identity_matrix(2)]]
            sage: T = table(rows, header_row=True)
            sage: T2 = table(rows, header_row=True)
            sage: T is T2
            False
            sage: T == T2
            True
            sage: T2.options(frame=True)
            sage: T == T2
            False
        """
        return (self._rows == other._rows and self.options() == other.options())

    def options(self, **kwds):
        r"""
        With no arguments, return the dictionary of options for this
        table. With arguments, modify options.

        INPUT:

        - ``header_row`` - if True, first row is highlighted.
        - ``header_column`` - if True, first column is highlighted.
        - ``frame`` - if True, put a box around each cell.
        - ``align`` - the alignment of each entry: either 'left',
          'center', or 'right'

        EXAMPLES::

            sage: T = table([['a', 'b', 'c'], [1,2,3]])
            sage: T.options()['align'], T.options()['frame']
            ('left', False)
            sage: T.options(align='right', frame=True)
            sage: T.options()['align'], T.options()['frame']
            ('right', True)

        Note that when first initializing a table, ``header_row`` or
        ``header_column`` can be a list. In this case, during the
        initialization process, the header is merged with the rest of
        the data, so changing the header option later using
        ``table.options(...)`` doesn't affect the contents of the
        table, just whether the row or column is highlighted. When
        using this :meth:`options` method, no merging of data occurs,
        so here ``header_row`` and ``header_column`` should just be
        ``True`` or ``False``, not a list. ::

            sage: T = table([[1,2,3], [4,5,6]], header_row=['a', 'b', 'c'], frame=True)
            sage: T
            +---+---+---+
            | a | b | c |
            +===+===+===+
            | 1 | 2 | 3 |
            +---+---+---+
            | 4 | 5 | 6 |
            +---+---+---+
            sage: T.options(header_row=False)
            sage: T
            +---+---+---+
            | a | b | c |
            +---+---+---+
            | 1 | 2 | 3 |
            +---+---+---+
            | 4 | 5 | 6 |
            +---+---+---+

        If you do specify a list for ``header_row``, an error is raised::

            sage: T.options(header_row=['x', 'y', 'z'])
            Traceback (most recent call last):
            ...
            TypeError: header_row should be either True or False.
        """
        if kwds:
            for option in ['align', 'frame']:
                if option in kwds:
                    self._options[option] = kwds[option]
            for option in ['header_row', 'header_column']:
                if option in kwds:
                    if not kwds[option]:
                        self._options[option] = kwds[option]
                    elif kwds[option] is True:
                        self._options[option] = kwds[option]
                    else:
                        raise TypeError("%s should be either True or False." % option)
        else:
            return self._options

    def transpose(self):
        r"""
        Return a table which is the transpose of this one:
        rows and columns have been interchanged. Several of the
        properties of the original table are preserved: whether a
        frame is present and any alignment setting. On the other hand,
        header rows are converted to header columns, and vice versa.

        EXAMPLES::

            sage: T = table([[1,2,3], [4,5,6]])
            sage: T.transpose()
              1   4
              2   5
              3   6
            sage: T = table([[1,2,3], [4,5,6]], header_row=['x', 'y', 'z'], frame=True)
            sage: T.transpose()
            +---++---+---+
            | x || 1 | 4 |
            +---++---+---+
            | y || 2 | 5 |
            +---++---+---+
            | z || 3 | 6 |
            +---++---+---+
        """
        return table(list(zip(*self._rows)),
                     header_row=self._options['header_column'],
                     header_column=self._options['header_row'],
                     frame=self._options['frame'],
                     align=self._options['align'])

    @cached_method
    def _widths(self):
        r"""
        The maximum widths for (the string representation of) each
        column. Used by the :meth:`_repr_` method.

        EXAMPLES::

            sage: table([['a', 'bb', 'ccccc'], [10, -12, 0], [1, 2, 3]])._widths()
            (2, 3, 5)
        """
        nc = len(self._rows[0])

        widths = [0] * nc
        for row in self._rows:
            w = []
            for (idx, x) in zip(range(nc), row):
                w.append(max(widths[idx], len(str(x))))
            widths = w
        return tuple(widths)

    def _repr_(self):
        r"""
        String representation of a table.

        The class docstring has many examples; here is one more.

        EXAMPLES::

            sage: table([['a', 'bb', 'ccccc'], [10, -12, 0], [1, 2, 3]], align='right') # indirect doctest
               a    bb   ccccc
              10   -12       0
               1     2       3
        """
        rows = self._rows
        nc = len(rows[0])
        if len(rows) == 0 or nc == 0:
            return ""

        frame_line = "+" + "+".join("-" * (x+2) for x in self._widths()) + "+\n"

        if self._options['header_column'] and self._options['frame']:
            frame_line = "+" + frame_line[1:].replace('+', '++', 1)

        if self._options['frame']:
            s = frame_line
        else:
            s = ""

        if self._options['header_row']:
            s += self._str_table_row(rows[0], header_row=True)
            rows = rows[1:]

        for row in rows:
            s += self._str_table_row(row, header_row=False)
        return s.strip("\n")

    def _rich_repr_(self, display_manager, **kwds):
        """
        Rich Output Magic Method

        See :mod:`sage.repl.rich_output` for details.

        EXAMPLES::

            sage: from sage.repl.rich_output import get_display_manager
            sage: dm = get_display_manager()
            sage: t = table([1, 2, 3])
            sage: t._rich_repr_(dm)    # the doctest backend does not support html
        """
        OutputHtml = display_manager.types.OutputHtml
        if OutputHtml in display_manager.supported_output():
            return OutputHtml(self._html_())

    def _str_table_row(self, row, header_row=False):
        r"""
        String representation of a row of a table. Used by the
        :meth:`_repr_` method.

        EXAMPLES::

            sage: T = table([['a', 'bb', 'ccccc'], [10, -12, 0], [1, 2, 3]], align='right')
            sage: T._str_table_row([1,2,3])
            '   1     2       3\n'
            sage: T._str_table_row([1,2,3], True)
            '   1     2       3\n+----+-----+-------+\n'
            sage: T.options(header_column=True)
            sage: T._str_table_row([1,2,3], True)
            '   1 |   2       3\n+----+-----+-------+\n'
            sage: T.options(frame=True)
            sage: T._str_table_row([1,2,3], False)
            '|  1 ||   2 |     3 |\n+----++-----+-------+\n'

        Check that :trac:`14601` has been fixed::

            sage: table([['111111', '222222', '333333']])._str_table_row([False,True,None], False)
            '  False    True     None\n'
        """
        frame = self._options['frame']
        widths = self._widths()
        frame_line = "+" + "+".join("-" * (x+2) for x in widths) + "+\n"

        align = self._options['align']
        if align == 'right':
            align_char = '>'
        elif align == 'center':
            align_char = '^'
        else:
            align_char = '<'

        s = ""
        if frame:
            s += "| "
        else:
            s += "  "

        if self._options['header_column']:
            if frame:
                frame_line = "+" + frame_line[1:].replace('+', '++', 1)
            s += ("{!s:" + align_char + str(widths[0]) + "}").format(row[0])
            if frame:
                s += " || "
            else:
                s += " | "
            row = row[1:]
            widths = widths[1:]

        for (entry, width) in zip(row, widths):
            s += ("{!s:" + align_char + str(width) + "}").format(entry)
            if frame:
                s += " | "
            else:
                s += "   "
        s = s.rstrip(' ')
        s += "\n"
        if frame and header_row:
            s += frame_line.replace('-', '=')
        elif frame or header_row:
            s += frame_line
        return s

    def _latex_(self):
        r"""
        LaTeX representation of a table.

        If an entry is a Sage object, it is replaced by its LaTeX
        representation, delimited by dollar signs (i.e., ``x`` is
        replaced by ``$latex(x)$``). If an entry is a string, the
        dollar signs are not automatically added, so tables can
        include both plain text and mathematics.

        OUTPUT:

        String.

        EXAMPLES::

            sage: from sage.misc.table import table
            sage: a = [[r'$\sin(x)$', '$x$', 'text'], [1,34342,3], [identity_matrix(2),5,6]]
            sage: latex(table(a)) # indirect doctest
            \begin{tabular}{lll}
            $\sin(x)$ & $x$ & text \\
            $1$ & $34342$ & $3$ \\
            $\left(\begin{array}{rr}
            1 & 0 \\
            0 & 1
            \end{array}\right)$ & $5$ & $6$ \\
            \end{tabular}
            sage: latex(table(a, frame=True, align='center'))
            \begin{tabular}{|c|c|c|} \hline
            $\sin(x)$ & $x$ & text \\ \hline
            $1$ & $34342$ & $3$ \\ \hline
            $\left(\begin{array}{rr}
            1 & 0 \\
            0 & 1
            \end{array}\right)$ & $5$ & $6$ \\ \hline
            \end{tabular}
        """
        from .latex import latex, LatexExpr

        rows = self._rows
        nc = len(rows[0])
        if len(rows) == 0 or nc == 0:
            return ""

        align_char = self._options['align'][0]   # 'l', 'c', 'r'
        if self._options['frame']:
            frame_char = '|'
            frame_str = ' \\hline'
        else:
            frame_char = ''
            frame_str = ''
        if self._options['header_column']:
            head_col_char = '|'
        else:
            head_col_char = ''
        if self._options['header_row']:
            head_row_str = ' \\hline'
        else:
            head_row_str = ''

        # table header
        s = "\\begin{tabular}{"
        s += frame_char + align_char + frame_char + head_col_char
        s += frame_char.join([align_char] * (nc-1))
        s += frame_char + "}" + frame_str + "\n"
        # first row
        s += " & ".join(LatexExpr(x) if isinstance(x, (str, LatexExpr))
                      else '$' + latex(x).strip() + '$' for x in rows[0])
        s += " \\\\" + frame_str + head_row_str + "\n"
        # other rows
        for row in rows[1:]:
            s += " & ".join(LatexExpr(x) if isinstance(x, (str, LatexExpr))
                          else '$' + latex(x).strip() + '$' for x in row)
            s += " \\\\" + frame_str + "\n"
        s += "\\end{tabular}"
        return s

    def _html_(self):
        r"""
        HTML representation of a table.

        Strings of html will be parsed for math inside dollar and
        double-dollar signs.  2D graphics will be displayed in the
        cells.  Expressions will be latexed.

        The ``align`` option for tables is ignored in HTML
        output. Specifying ``header_column=True`` may not have any
        visible effect in the Sage notebook, depending on the version
        of the notebook.

        OUTPUT:

        A :class:`~sage.misc.html.HtmlFragment` instance.

        EXAMPLES::

            sage: T = table([[r'$\sin(x)$', '$x$', 'text'], [1,34342,3], [identity_matrix(2),5,6]])
            sage: T._html_()
            '
'
            sage: print(T._html_())

            \(\sin(x)\)
            \(x\)
            text

            \(1\)
            \(34342\)
            \(3\)

            \(\left(\begin{array}{rr}
            1 & 0 \\
            0 & 1
            \end{array}\right)\)
            \(5\)
            \(6\)

        Note that calling ``html(table(...))`` has the same effect as
        calling ``table(...)._html_()``::

            sage: T = table([["$x$", r"$\sin(x)$"]] + [(x,n(sin(x), digits=2)) for x in [0..3]], header_row=True, frame=True)
            sage: T
            +-----+-----------+
            | $x$ | $\sin(x)$ |
            +=====+===========+
            | 0   | 0.00      |
            +-----+-----------+
            | 1   | 0.84      |
            +-----+-----------+
            | 2   | 0.91      |
            +-----+-----------+
            | 3   | 0.14      |
            +-----+-----------+
            sage: print(html(T))

            \(x\)
            \(\sin(x)\)

            \(0\)
            \(0.00\)

            \(1\)
            \(0.84\)

            \(2\)
            \(0.91\)

            \(3\)
            \(0.14\)

        """
        from itertools import cycle
        rows = self._rows
        header_row = self._options['header_row']
        if self._options['frame']:
            frame = 'border="1"'
        else:
            frame = ''
        s = StringIO()
        if rows:
            s.writelines([
                # If the table has < 100 rows, don't truncate the output in the notebook
                '
\n' if len(rows) <= 100 else '
' ,
                '
\n'.format(frame),
                '
\n',
            ])
            # First row:
            if header_row:
                s.write('
\n')
                self._html_table_row(s, rows[0], header=header_row)
                s.write('
\n')
                rows = rows[1:]

            # Other rows:
            for row_class, row in zip(cycle(["row-a", "row-b"]), rows):
                s.write('
\n'.format(row_class))
                self._html_table_row(s, row, header=False)
                s.write('
\n')
            s.write('
\n
\n
')
        return s.getvalue()

    def _html_table_row(self, file, row, header=False):
        r"""
        Write table row

        Helper method used by the :meth:`_html_` method.

        INPUT:

        - ``file`` -- file-like object. The table row data will be
          written to it.

        - ``row`` -- a list with the same number of entries as each row
          of the table.

        - ``header`` -- bool (default False). If True, treat this as a
          header row, using ```` instead of ````.

        OUTPUT:

        This method returns nothing. All output is written to ``file``.

        Strings are written verbatim unless they seem to be LaTeX
        code, in which case they are enclosed in a ``script`` tag
        appropriate for MathJax. Sage objects are printed using their
        LaTeX representations.

        EXAMPLES::

            sage: T = table([['a', 'bb', 'ccccc'], [10, -12, 0], [1, 2, 3]])
            sage: from io import StringIO
            sage: s = StringIO()
            sage: T._html_table_row(s, ['a', 2, '$x$'])
            sage: print(s.getvalue())
            a
            \(2\)
            \(x\)
        """
        from sage.plot.all import Graphics
        from .latex import latex
        from .html import math_parse
        import types

        if isinstance(row, types.GeneratorType):
            row = list(row)
        elif not isinstance(row, (list, tuple)):
            row = [row]

        align_char = self._options['align'][0]   # 'l', 'c', 'r'

        if align_char == 'l':
            style = 'text-align:left'
        elif align_char == 'c':
            style = 'text-align:center'
        elif align_char == 'r':
            style = 'text-align:right'
        else:
            style = ''

        style_attr = f' style="{style}"' if style else ''

        column_tag = f'%s	\n' if header else f'%s	\n'

        if self._options['header_column']:
            first_column_tag = '%s	\n' if header else '%s	\n'
        else:
            first_column_tag = column_tag

        # first entry of row
        entry = row[0]
        if isinstance(entry, Graphics):
            file.write(first_column_tag % entry.show(linkmode = True))
        elif isinstance(entry, str):
            file.write(first_column_tag % math_parse(entry))
        else:
            file.write(first_column_tag % (r'\(%s\)' % latex(entry)))

        # other entries
        for column in range(1, len(row)):
            if isinstance(row[column], Graphics):
                file.write(column_tag % row[column].show(linkmode = True))
            elif isinstance(row[column], str):
                file.write(column_tag % math_parse(row[column]))
            else:
                file.write(column_tag % (r'\(%s\)' % latex(row[column])))


from sympy.core.basic import Basic
from sympy.printing import pprint

import random

def interactive_traversal(expr):
    """Traverse a tree asking a user which branch to choose. """

    RED, BRED = '\033[0;31m', '\033[1;31m'
    GREEN, BGREEN = '\033[0;32m', '\033[1;32m'
    YELLOW, BYELLOW = '\033[0;33m', '\033[1;33m'  # noqa
    BLUE, BBLUE = '\033[0;34m', '\033[1;34m'      # noqa
    MAGENTA, BMAGENTA = '\033[0;35m', '\033[1;35m'# noqa
    CYAN, BCYAN = '\033[0;36m', '\033[1;36m'      # noqa
    END = '\033[0m'

    def cprint(*args):
        print("".join(map(str, args)) + END)

    def _interactive_traversal(expr, stage):
        if stage > 0:
            print()

        cprint("Current expression (stage ", BYELLOW, stage, END, "):")
        print(BCYAN)
        pprint(expr)
        print(END)

        if isinstance(expr, Basic):
            if expr.is_Add:
                args = expr.as_ordered_terms()
            elif expr.is_Mul:
                args = expr.as_ordered_factors()
            else:
                args = expr.args
        elif hasattr(expr, "__iter__"):
            args = list(expr)
        else:
            return expr

        n_args = len(args)

        if not n_args:
            return expr

        for i, arg in enumerate(args):
            cprint(GREEN, "[", BGREEN, i, GREEN, "] ", BLUE, type(arg), END)
            pprint(arg)
            print()

        if n_args == 1:
            choices = '0'
        else:
            choices = '0-%d' % (n_args - 1)

        try:
            choice = input("Your choice [%s,f,l,r,d,?]: " % choices)
        except EOFError:
            result = expr
            print()
        else:
            if choice == '?':
                cprint(RED, "%s - select subexpression with the given index" %
                       choices)
                cprint(RED, "f - select the first subexpression")
                cprint(RED, "l - select the last subexpression")
                cprint(RED, "r - select a random subexpression")
                cprint(RED, "d - done\n")

                result = _interactive_traversal(expr, stage)
            elif choice in ('d', ''):
                result = expr
            elif choice == 'f':
                result = _interactive_traversal(args[0], stage + 1)
            elif choice == 'l':
                result = _interactive_traversal(args[-1], stage + 1)
            elif choice == 'r':
                result = _interactive_traversal(random.choice(args), stage + 1)
            else:
                try:
                    choice = int(choice)
                except ValueError:
                    cprint(BRED,
                           "Choice must be a number in %s range\n" % choices)
                    result = _interactive_traversal(expr, stage)
                else:
                    if choice < 0 or choice >= n_args:
                        cprint(BRED, "Choice must be in %s range\n" % choices)
                        result = _interactive_traversal(expr, stage)
                    else:
                        result = _interactive_traversal(args[choice], stage + 1)

        return result

    return _interactive_traversal(expr, 0)


import numpy as np

a = np.arange(10) * 10
print(a)
# [ 0 10 20 30 40 50 60 70 80 90]

print(a[5])
# 50

print(a[8])
# 80

print(a[[5, 8]])
# [50 80]

print(a[[5, 4, 8, 0]])
# [50 40 80  0]

print(a[[5, 5, 5, 5]])
# [50 50 50 50]

idx = np.array([[5, 4], [8, 0]])
print(idx)
# [[5 4]
#  [8 0]]

print(a[idx])
# [[50 40]
#  [80  0]]

# print(a[[[5, 4], [8, 0]]])
# IndexError: too many indices for array

print(a[[[[5, 4], [8, 0]]]])
# [[50 40]
#  [80  0]]

a_2d = np.arange(12).reshape((3, 4))
print(a_2d)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

print(a_2d[0])
# [0 1 2 3]

print(a_2d[2])
# [ 8  9 10 11]

print(a_2d[[2, 0]])
# [[ 8  9 10 11]
#  [ 0  1  2  3]]

print(a_2d[[2, 2, 2]])
# [[ 8  9 10 11]
#  [ 8  9 10 11]
#  [ 8  9 10 11]]

print(a_2d[:, 1])
# [1 5 9]

print(a_2d[:, 3])
# [ 3  7 11]

print(a_2d[:, 1:2])
# [[1]
#  [5]
#  [9]]

print(a_2d[:, [3, 1]])
# [[ 3  1]
#  [ 7  5]
#  [11  9]]

print(a_2d[:, [3, 3, 3]])
# [[ 3  3  3]
#  [ 7  7  7]
#  [11 11 11]]

print(a_2d[0, 1])
# 1

print(a_2d[2, 3])
# 11

print(a_2d[[0, 2], [1, 3]])
# [ 1 11]

# index
# [[0, 1] [2, 3]]

# print(a_2d[[0, 2, 1], [1, 3]])
# IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (3,) (2,)

print(a_2d[[[0, 0], [2, 2]], [[1, 3], [1, 3]]])
# [[ 1  3]
#  [ 9 11]]

# index
# [[0, 1] [0, 3]
#  [2, 1] [2, 3]]

print(a_2d[[[0], [2]], [1, 3]])
# [[ 1  3]
#  [ 9 11]]

idxs = np.ix_([0, 2], [1, 3])
print(idxs)
# (array([[0],
#        [2]]), array([[1, 3]]))

print(type(idxs))
# <class 'tuple'>

print(type(idxs[0]))
# <class 'numpy.ndarray'>

print(idxs[0])
# [[0]
#  [2]]

print(idxs[1])
# [[1 3]]

print(a_2d[np.ix_([0, 2], [1, 3])])
# [[ 1  3]
#  [ 9 11]]

print(a_2d[np.ix_([2, 0], [3, 3, 3])])
# [[11 11 11]
#  [ 3  3  3]]

print(a_2d[[0, 2]][:, [1, 3]])
# [[ 1  3]
#  [ 9 11]]

a_2d = np.arange(12).reshape((3, 4))
print(a_2d)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

a_2d[np.ix_([0, 2], [1, 3])] = 100
print(a_2d)
# [[  0 100   2 100]
#  [  4   5   6   7]
#  [  8 100  10 100]]

a_2d[np.ix_([0, 2], [1, 3])] = [100, 200]
print(a_2d)
# [[  0 100   2 200]
#  [  4   5   6   7]
#  [  8 100  10 200]]

a_2d[np.ix_([0, 2], [1, 3])] = [[100, 200], [300, 400]]
print(a_2d)
# [[  0 100   2 200]
#  [  4   5   6   7]
#  [  8 300  10 400]]

print(a_2d[[0, 2]][:, [1, 3]])
# [[100 200]
#  [300 400]]

a_2d[[0, 2]][:, [1, 3]] = 0
print(a_2d)
# [[  0 100   2 200]
#  [  4   5   6   7]
#  [  8 300  10 400]]

a_2d = np.arange(12).reshape((3, 4))
print(a_2d)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

a_2d[[2, 0]] = [[100, 200, 300, 400], [500, 600, 700, 800]]
print(a_2d)
# [[500 600 700 800]
#  [  4   5   6   7]
#  [100 200 300 400]]

a_2d[[2, 2]] = [[-1, -2, -3, -4], [-5, -6, -7, -8]]
print(a_2d)
# [[500 600 700 800]
#  [  4   5   6   7]
#  [ -5  -6  -7  -8]]

a_2d = np.arange(12).reshape((3, 4))
print(a_2d)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

a_fancy = a_2d[np.ix_([0, 2], [1, 3])]
print(a_fancy)
# [[ 1  3]
#  [ 9 11]]

a_fancy[0, 0] = 100
print(a_fancy)
# [[100   3]
#  [  9  11]]

print(a_2d)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

a_2d = np.arange(12).reshape((3, 4))
print(a_2d)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

print(a_2d[[2, 0], ::-1])
# [[11 10  9  8]
#  [ 3  2  1  0]]

print(a_2d[::2, [3, 0, 1]])
# [[ 3  0  1]
#  [11  8  9]]


import glob
import numpy as np
import matplotlib.pyplot as plt

import analysis_tools

filenames = sorted(glob.glob('inflammation*.csv'))

for f in filenames[:3]:
    print(f)
    analyse(f)
    detect_problems(f)


#! usr/bin/env python3
import numpy as np#the numpy library
import matplotlib.pyplot as plt#Matplotlib's pyplot
import sys#gives access to a c-like sys library
import os#gives access to operating system
print(sys.argv)#prints any command line arguments, incl program name
print(os.getcwd())#prints the current working directory