Setting up the public repository for the SAGE implementation Differentially Definable...
authorAntonio Jimenez Pastor <ajpastor@risc.uni-linz.ac.at>
Thu, 8 Mar 2018 10:39:24 +0000 (11:39 +0100)
committerAntonio Jimenez Pastor <ajpastor@risc.uni-linz.ac.at>
Thu, 8 Mar 2018 10:39:24 +0000 (11:39 +0100)
50 files changed:
Makefile [new file with mode: 0644]
SPKG.txt [new file with mode: 0644]
ajpastor/__init__.py [new file with mode: 0644]
ajpastor/dd_functions/__init__.py [new file with mode: 0644]
ajpastor/dd_functions/ddExamples.py [new file with mode: 0644]
ajpastor/dd_functions/ddFunction.py [new file with mode: 0644]
ajpastor/dd_functions/symbolic.py [new file with mode: 0644]
ajpastor/lazy/__init__.py [new file with mode: 0644]
ajpastor/lazy/conversion.py [new file with mode: 0644]
ajpastor/lazy/lazyFracField.py [new file with mode: 0644]
ajpastor/lazy/lazyIDElements.py [new file with mode: 0644]
ajpastor/lazy/lazyRing.py [new file with mode: 0644]
ajpastor/lazy/lazyToPoly.py [new file with mode: 0644]
ajpastor/misc/__init__.py [new file with mode: 0644]
ajpastor/misc/bareiss.py [new file with mode: 0644]
ajpastor/misc/cached_property.py [new file with mode: 0644]
ajpastor/misc/dinamic_string.py [new file with mode: 0644]
ajpastor/misc/euclidean.py [new file with mode: 0644]
ajpastor/misc/hermite.py [new file with mode: 0644]
ajpastor/misc/matrix.py [new file with mode: 0644]
ajpastor/misc/nullspace_wrapper.py [new file with mode: 0644]
ajpastor/misc/restore.py [new file with mode: 0644]
ajpastor/misc/ring_w_sequence.py [new file with mode: 0644]
ajpastor/misc/storj_villard.py [new file with mode: 0644]
ajpastor/misc/timing.py [new file with mode: 0644]
ajpastor/misc/verbose.py [new file with mode: 0644]
ajpastor/operator/__init__.py [new file with mode: 0644]
ajpastor/operator/directStepOperator.py [new file with mode: 0644]
ajpastor/operator/fullLazyOperator.py [new file with mode: 0644]
ajpastor/operator/lazyStepOperator.py [new file with mode: 0644]
ajpastor/operator/listOperator.py [new file with mode: 0644]
ajpastor/operator/operator.py [new file with mode: 0644]
ajpastor/operator/oreOperator.py [new file with mode: 0644]
ajpastor/operator/polynomialLazyOperator.py [new file with mode: 0644]
ajpastor/operator/twoStepsOperator.py [new file with mode: 0644]
ajpastor/tests/__init__.py [new file with mode: 0644]
ajpastor/tests/dd_functions/__init__.py [new file with mode: 0644]
ajpastor/tests/dd_functions/bessel.py [new file with mode: 0644]
ajpastor/tests/dd_functions/chebyshev.py [new file with mode: 0644]
ajpastor/tests/dd_functions/ddFunction.py [new file with mode: 0644]
ajpastor/tests/dd_functions/ddFunction2.py [new file with mode: 0644]
ajpastor/tests/dd_functions/examples.py [new file with mode: 0644]
ajpastor/tests/dd_functions/hypergeometric.py [new file with mode: 0644]
ajpastor/tests/dd_functions/identities.py [new file with mode: 0644]
dependencies [new file with mode: 0644]
package-version.txt [new file with mode: 0644]
releases/diff_defined_functions__0.0.zip [new file with mode: 0644]
releases/old/diff_defined_functions__0.0__18.03.08_11:36:53.zip [new file with mode: 0644]
setup.py [new file with mode: 0644]
type [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..83b59bb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+SHELL:=/bin/bash
+BASE=some
+ZIP=diff_defined_functions# ZIP name
+VERSION=$(shell cat ./package-version.txt)
+
+all: 
+       sage -sh sage-pip-install .
+       
+create:
+       @echo "Creating main directories..."
+       @mkdir -p ./$(BASE)
+       @mkdir -p ./releases/old
+       @echo "from pkgutil import extend_path;" > ./$(BASE)/__init__.py
+       @echo "__path__ = extend_path(__path__, __name__);" >> ./$(BASE)/__init__.py
+       
+zip:
+       @echo "Compressing the project into file" $(ZIP)".zip"...
+       @zip -r ./releases/old/$(ZIP)__$(VERSION)__`date +'%y.%m.%d_%H:%M:%S'`.zip $(BASE)
+       @rm -f ./releases/$(ZIP)__$(VERSION).zip
+       zip -r ./releases/$(ZIP)__$(VERSION).zip $(BASE)
+       @echo $(VERSION)
+           
diff --git a/SPKG.txt b/SPKG.txt
new file mode 100644 (file)
index 0000000..6ce83f5
--- /dev/null
+++ b/SPKG.txt
@@ -0,0 +1,30 @@
+= diff_defined_functions =
+
+== Description ==
+
+A Sage implementation of Differentially Definable Functions.
+
+Main features for the Diff. Definable functions. Allows the user to compute
+the closure properties of these class of functions where the coefficients of
+the differential equations belong to an arbitrary differential ring.
+
+Give access to the objects DFinite and DDFinite, that are representations
+of the D-Finite and DD-Finite differential rings.
+
+== License ==
+
+Distributed under the terms of the GNU General Public License (GPL)
+
+http://www.gnu.org/licenses/
+
+== Upstream Contact ==
+
+Antonio Jimenez-Pastor <antonio.jimenez-pastor@dk-compmath.jku.at>
+
+== Dependencies ==
+
+none.
+
+== Special Update/Build Instructions ==
+
+none.
diff --git a/ajpastor/__init__.py b/ajpastor/__init__.py
new file mode 100644 (file)
index 0000000..6e3977c
--- /dev/null
@@ -0,0 +1,2 @@
+from pkgutil import extend_path;
+__path__ = extend_path(__path__, __name__);
diff --git a/ajpastor/dd_functions/__init__.py b/ajpastor/dd_functions/__init__.py
new file mode 100644 (file)
index 0000000..b793718
--- /dev/null
@@ -0,0 +1,16 @@
+try:
+    from .ddFunction import *
+except Exception:
+    print "Error loading module dd_functions.ddFunction";
+try:
+    from .ddExamples import *;
+except Exception:
+    print "Error loading module dd_functions.ddExamples";
+try:
+    from .symbolic import *;
+except Exception:
+    print "Error loading module dd_functions.symbolic";
+
+from pkgutil import extend_path;
+__path__ = extend_path(__path__, __name__);
+
diff --git a/ajpastor/dd_functions/ddExamples.py b/ajpastor/dd_functions/ddExamples.py
new file mode 100644 (file)
index 0000000..e1f10d3
--- /dev/null
@@ -0,0 +1,499 @@
+
+# This file was *autogenerated* from the file ./ddExamples.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_3 = Integer(3); _sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0); _sage_const_6 = Integer(6)
+from ajpastor.dd_functions.ddFunction import *;
+
+from ajpastor.misc.dinamic_string import *;
+#from ajpastor.dd_functions.ddParametrizedFunction import *;
+
+## Global variables (PUBLIC)
+DFinite_examples = {};
+DDFinite_examples = {};
+
+## Global variables (PRIVATE)
+__example_names = {};
+
+##################################################################################
+##################################################################################
+###
+### Predefined examples
+###
+##################################################################################
+##################################################################################
+def DD_EXAMPLES_LOAD():
+    global DFinite_examples; global DDFinite_examples;
+    global __example_names;
+
+    s = DFinite.element([_sage_const_1 ,_sage_const_0 ,_sage_const_1 ],[_sage_const_0 ,_sage_const_1 ], name=DinamicString("sin(_1)", "x"));
+    c = DFinite.element([_sage_const_1 ,_sage_const_0 ,_sage_const_1 ],[_sage_const_1 ,_sage_const_0 ], name=DinamicString("cos(_1)", "x"));
+    sh = DFinite.element([-_sage_const_1 ,_sage_const_0 ,_sage_const_1 ],[_sage_const_0 ,_sage_const_1 ], name=DinamicString("sinh(_1)", "x"));
+    ch = DFinite.element([-_sage_const_1 ,_sage_const_0 ,_sage_const_1 ],[_sage_const_1 ,_sage_const_0 ], name=DinamicString("cosh(_1)", "x"));
+    ln = DFinite.element([_sage_const_1 ,_sage_const_0 ,(x+_sage_const_1 )],[_sage_const_0 ,_sage_const_1 ], name=DinamicString("log(_1-1)", "x"));
+    e = DFinite.element([-_sage_const_1 ,_sage_const_1 ],[_sage_const_1 ], name=DinamicString("exp(_1)", "x"));
+    tan = DDFinite.element([-_sage_const_2 ,_sage_const_0 ,c**_sage_const_2 ],[_sage_const_0 ,_sage_const_1 ], name=DinamicString("tan(_1)", "x"));
+    
+    ## Defining D-Finite Examples
+    DFinite_examples['e'] = e;
+    DFinite_examples['ln'] = ln;
+    DFinite_examples['sin'] = s;
+    DFinite_examples['cos'] = c;
+    DFinite_examples['sinh'] = sh;
+    DFinite_examples['cosh'] = ch;
+    P = DFiniteP.parameters()[_sage_const_0 ];
+    DFinite_examples['bessel'] = DFiniteP.element([x**_sage_const_2 -P**_sage_const_2 ,x,x**_sage_const_2 ], name=DinamicString("bessel_J(_1,_2)", ["P","x"]));
+    DFinite_examples['legendre'] = DFiniteP.element([P*(P+_sage_const_1 ), -_sage_const_2 *x,_sage_const_1 -x**_sage_const_2 ], name=DinamicString("legendre_P(_1,_2)", ["P","x"]));
+    DFinite_examples['chebyshev1'] = DFiniteP.element([P**_sage_const_2 ,-x,(_sage_const_1 -x**_sage_const_2 )], name=DinamicString("chebyshev_T(_1,_2)", ["P","x"]));
+    DFinite_examples['chebyshev2'] = DFiniteP.element([P*(P+_sage_const_2 ),-_sage_const_3 *x,_sage_const_1 -x**_sage_const_2 ], name=DinamicString("chebyshev_U(_1,_2)", ["P","x"]));
+    
+    ## Defining DD-Finite Examples
+    DDFinite_examples['esin'] = DDFinite.element([_sage_const_1 ,-c], [_sage_const_1 ], name=DinamicString("exp(sin(_1))", "x"));
+    DDFinite_examples['sine'] = DDFinite.element([e**_sage_const_2 ,-_sage_const_1 ,_sage_const_1 ],[_sage_const_0 ,_sage_const_1 ], name=DinamicString("sin(exp(_1))", "x"));
+    DDFinite_examples['tan'] = [tan, DDFinite.element([_sage_const_0 ,-_sage_const_2 *s*c,c**_sage_const_2 ], [_sage_const_0 ,_sage_const_1 ], name=DinamicString("tan(_1)", "x"))];
+    DDFinite_examples['bernoulli'] = DDFinite.element([x*e-e+_sage_const_1 ,x*(e-_sage_const_1 )],[_sage_const_1 ,-_sage_const_1 /_sage_const_2 ,_sage_const_1 /_sage_const_6 ,_sage_const_0 ]);
+    
+    ## Defining some names
+    __example_names['exp'] = 'e';
+    __example_names['log'] = 'ln';
+    __example_names['sen'] = 'sin';
+    __example_names['tg'] = 'tan';
+
+def DFinite_example(input, n=_sage_const_0 ):
+    if(DFinite_examples.has_key(input)):
+        res = DFinite_examples[input];
+    elif (__example_names.has_key(input) and DFinite_examples.has_key(__example_names[input])):
+        res = DFinite_examples[__example_names[input]];
+    else:
+        raise ValueError('The DD-Function by name %s does not exist' %(input));
+        
+    if(type(res)==list):
+        return res[n];
+    else:
+        return res;
+    
+
+def DDFinite_example(input, n = _sage_const_0 ):
+    if(DDFinite_examples.has_key(input)):
+        res = DDFinite_examples[input];
+    elif (__example_names.has_key(input) and DDFinite_examples.has_key(__example_names[input])):
+        res = DDFinite_examples[__example_names[input]];
+    else:
+        raise ValueError('The DD-Function by name %s does not exist' %(input));
+        
+    if(type(res)==list):
+        return res[n];
+    else:
+        return res;
+    
+def DDFunction_example(input, n=_sage_const_0 ):
+    try:
+        return DFinite_example(input, n);
+    except Exception:
+        pass;
+    try:
+        return DDFinite_example(input, n);
+    except Exception:
+        pass;
+        
+    raise ValueError('No DD-Function by name %s exist' %(input));
+
+##################################################################################
+##################################################################################
+###
+### Trigonometric and Hyperbolic trigonometric Functions
+###
+##################################################################################
+##################################################################################
+@cached_function
+def Sin(input, ddR = None):
+    from ajpastor.dd_functions.ddFunction import DDFunction;
+    if(isinstance(input, DDFunction)):
+        return Sin(x)(input);
+    f,dR = __decide_parent(input, ddR);
+    
+    evaluate = lambda p : dR.getSequenceElement(p,_sage_const_0 );
+    if(evaluate(f) != _sage_const_0 ):
+        raise ValueError("Impossible to compute sin(f) with f(0) != 0");
+    
+    df = dR.base_derivation(f);
+    df2 = dR.base_derivation(df);
+    
+    newName = repr(f);
+    if(hasattr(f, "_DDFunction__name") and (not(f._DDFunction__name is None))):
+        newName = f._DDFunction__name;
+    
+    return dR.element([df**_sage_const_3 ,-df2,df],[_sage_const_0 ,evaluate(df),evaluate(df2)], name=DinamicString("sin(_1)", newName)); 
+
+@cached_function    
+def Cos(input, ddR = None):
+    from ajpastor.dd_functions.ddFunction import DDFunction;
+    if(isinstance(input, DDFunction)):
+        return Cos(x)(input);
+    f,dR = __decide_parent(input, ddR);
+    
+    evaluate = lambda p : dR.getSequenceElement(p,_sage_const_0 );
+    if(evaluate(f) != _sage_const_0 ):
+        raise ValueError("Impossible to compute cos(f) with f(0) != 0");
+    
+    df = dR.base_derivation(f);
+    df2 = dR.base_derivation(df);
+    
+    newName = repr(f);
+    if(hasattr(f, "_DDFunction__name") and (not(f._DDFunction__name is None))):
+        newName = f._DDFunction__name;
+    
+    return dR.element([df**_sage_const_3 ,-df2,df],[_sage_const_1 ,_sage_const_0 ,-evaluate(df)**_sage_const_2 ], name=DinamicString("cos(_1)",newName)); 
+
+@cached_function
+def Tan(input, ddR = None):
+    from ajpastor.dd_functions.ddFunction import DDFunction;
+    if(isinstance(input, DDFunction)):
+        return Tan(x)(input);
+    if(input == x):
+        return DDFinite_example('tan');
+    g, dR = __decide_parent(input, ddR,_sage_const_2 );
+    
+    
+    evaluate = lambda p : dR.getSequenceElement(p,_sage_const_0 );
+    if(evaluate(g) != _sage_const_0 ):
+        raise ValueError("Impossible to compute tan(f) with f(0) != 0");
+    
+    dg = dR.base_derivation(g); ddg = dR.base_derivation(dg);
+    a = Cos(input)**_sage_const_2 ; b = dR.base().zero(); c = dR.base()(-_sage_const_2 );
+    
+    ### First we compute the new linear differential operator
+    newOperator = dR.element([dg**_sage_const_3 *c,dg**_sage_const_2 *b-ddg*a,dg*a]).equation;
+        
+    ### Now, we compute the initial values required
+    required = newOperator.get_jp_fo()+_sage_const_1 ;
+        
+    init_tan = Tan(x).getInitialValueList(required);
+    init_input = [factorial(i)*dR.base().getSequenceElement(g,i) for i in range(required)];
+        
+    newInit = [init_tan[_sage_const_0 ]]+[sum([init_tan[j]*bell_polynomial(i,j)(*init_input[_sage_const_1 :i-j+_sage_const_2 ]) for j in range(_sage_const_1 ,i+_sage_const_1 )]) for i in range(_sage_const_1 ,required)]; ## See Faa di Bruno's formula
+    
+    result = dR.element(newOperator,newInit);
+    
+    newName = repr(input);
+    if(hasattr(input, "_DDFunction__name") and (not(input._DDFunction__name is None))):
+        newName = input._DDFunction__name;
+    
+    result._DDFunction__name = DinamicString("tan(_1)",newName);
+    return result;
+
+@cached_function    
+def Sinh(input, ddR = None):
+    from ajpastor.dd_functions.ddFunction import DDFunction;
+    if(isinstance(input, DDFunction)):
+        return Sinh(x)(input);
+    f,dR = __decide_parent(input, ddR);
+    
+    evaluate = lambda p : dR.getSequenceElement(p,_sage_const_0 );
+    if(evaluate(f) != _sage_const_0 ):
+        raise ValueError("Impossible to compute sin(f) with f(0) != 0");
+    
+    df = dR.base_derivation(f);
+    df2 = dR.base_derivation(df);
+    
+    newName = repr(f);
+    if(hasattr(f, "_DDFunction__name") and (not(f._DDFunction__name is None))):
+        newName = f._DDFunction__name;
+    
+    return dR.element([-df**_sage_const_3 ,-df2,df],[_sage_const_0 ,evaluate(df),evaluate(df2)], name=DinamicString("sinh(_1)",newName)); 
+
+@cached_function    
+def Cosh(input, ddR = None):
+    from ajpastor.dd_functions.ddFunction import DDFunction;
+    if(isinstance(input, DDFunction)):
+        return Cosh(x)(input);
+    f,dR = __decide_parent(input, ddR);
+    
+    evaluate = lambda p : dR.getSequenceElement(p,_sage_const_0 );
+    if(evaluate(f) != _sage_const_0 ):
+        raise ValueError("Impossible to compute cos(f) with f(0) != 0");
+    
+    df = dR.base_derivation(f);
+    df2 = dR.base_derivation(df);
+    
+    newName = repr(f);
+    if(hasattr(f, "_DDFunction__name") and (not(f._DDFunction__name is None))):
+        newName = f._DDFunction__name;
+    
+    return dR.element([-df**_sage_const_3 ,-df2,df],[_sage_const_1 ,_sage_const_0 ,evaluate(df)**_sage_const_2 ], name=DinamicString("cosh(_1)", newName)); 
+
+##################################################################################
+##################################################################################
+###
+### Exponential and Logarithm Functions
+###
+##################################################################################
+##################################################################################   
+@cached_function  
+def Log(input, ddR = None):
+    from ajpastor.dd_functions.ddFunction import DDFunction;
+    if(isinstance(input, DDFunction)):
+        return Log(x+_sage_const_1 )(input);
+    f,dR = __decide_parent(input, ddR);
+    
+    evaluate = lambda p : dR.getSequenceElement(p,_sage_const_0 );
+    if(evaluate(f) != _sage_const_1 ):
+        raise ValueError("Impossible to compute ln(f) with f(0) != 1");
+    
+    df = dR.base_derivation(f);
+    df2 = dR.base_derivation(df);
+    
+    newName = repr(f);
+    if(hasattr(f, "_DDFunction__name") and (not(f._DDFunction__name is None))):
+        newName = f._DDFunction__name;
+    
+    return dR.element([_sage_const_0 ,df**_sage_const_2 -df2*f,df*f],[_sage_const_0 ,evaluate(df), evaluate(df2)-evaluate(df)**_sage_const_2 ], name=DinamicString("log(_1)",newName));
+    
+@cached_function 
+def Log1(input, ddR = None):
+    from ajpastor.dd_functions.ddFunction import DDFunction;
+    if(isinstance(input, DDFunction)):
+        return Log1(x)(input);
+    f,dR = __decide_parent(input, ddR);
+    
+    evaluate = lambda p : dR.getSequenceElement(p,_sage_const_0 );
+    if(evaluate(f) != _sage_const_0 ):
+        raise ValueError("Impossible to compute cos(f) with f(0) != 0");
+    
+    df = dR.base_derivation(f);
+    df2 = dR.base_derivation(df);
+    
+    f1 = f+_sage_const_1 ;
+    
+    newName = repr(f);
+    if(hasattr(f, "_DDFunction__name") and (not(f._DDFunction__name is None))):
+        newName = f._DDFunction__name;
+    
+    return dR.element([_sage_const_0 ,df**_sage_const_2 -df2*f1,df*f1],[_sage_const_0 ,evaluate(df), evaluate(df2)-evaluate(df)**_sage_const_2 ], name=DinamicString("log(_1+1)", newName)); 
+    
+@cached_function 
+def Exp(input, ddR = None):
+    from ajpastor.dd_functions.ddFunction import DDFunction;
+    if(isinstance(input, DDFunction)):
+        return Exp(x)(input);
+    f,dR = __decide_parent(input, ddR);
+    
+    evaluate = lambda p : dR.getSequenceElement(p,_sage_const_0 );
+    if(evaluate(f) != _sage_const_0 ):
+        raise ValueError("Impossible to compute exp(f) with f(0) != 0");
+    
+    newName = repr(f);
+    if(hasattr(f, "_DDFunction__name") and (not(f._DDFunction__name is None))):
+        newName = f._DDFunction__name;
+        
+    return dR.element([-dR.base_derivation(f),_sage_const_1 ],[_sage_const_1 ], name=DinamicString("exp(_1)", newName));
+
+##################################################################################
+##################################################################################
+###
+### Special Functions
+###
+##################################################################################
+##################################################################################    
+### Bessel Functions
+@cached_function 
+def BesselD(input, kind = _sage_const_1 ):
+    if(input is None):
+        return DDFunction_example('bessel');
+    elif(kind == _sage_const_1 ):
+        try:
+            alpha = QQ(input);
+            if(alpha < _sage_const_0 ):
+                raise ValueError("Impossible to manage Bessel functions of first kind with negative order");
+                
+            P = DFiniteP.parameters()[_sage_const_0 ];
+            
+            func = DDFunction_example('bessel')(**{str(P):alpha});
+            if(alpha in ZZ):
+                return func.change_init_values([_sage_const_0  for i in range(alpha)] + [_sage_const_1 /_sage_const_2 **alpha, _sage_const_0 , -((alpha+_sage_const_2 )/(_sage_const_2 **(alpha+_sage_const_2 )))], name = "bessel_J(%d,x)" %input);
+            else:
+                return func.change_init_values([_sage_const_0 ], name = DinamicString("bessel_J(_1,_2)", [str(input), "x"]));
+        except TypeError:
+            raise TypeError("Impossible to manage Bessel functions of first kind with irrational order");
+    else:
+        raise ValueError("Impossible to manage Bessel functions of %dth kind" %(kind));
+
+### Legendre Polynomials     
+__legendre_initials = [[_sage_const_1 ,_sage_const_0 ],[_sage_const_0 ,_sage_const_1 ]];   
+@cached_function 
+def LegendreD(input):
+    global __legendre_initials;
+    if(input is None):
+        return DDFunction_example('legendre');
+
+    try:
+        n = ZZ(input);
+        if(n < _sage_const_0 ):
+            raise ValueError("Impossible to create a Legendre polynomial of negative index");
+            
+        P = DFiniteP.parameters()[_sage_const_0 ];  
+        func = DDFunction_example('legendre')(**{str(P):n});
+        for i in range(len(__legendre_initials), n+_sage_const_1 ):
+            prev = __legendre_initials[-_sage_const_1 ];
+            prev2 = __legendre_initials[-_sage_const_2 ];
+            __legendre_initials += [[-(i-_sage_const_1 )*prev2[_sage_const_0 ]/i,((_sage_const_2 *i-_sage_const_1 )*prev[_sage_const_0 ] - (i-_sage_const_1 )*prev2[_sage_const_1 ])/i]];
+        return func.change_init_values(__legendre_initials[n], name=DinamicString("legendre_P(_1,_2)", [str(input), "x"]));
+    except TypeError as e:
+        #raise TypeError("Impossible to create a Legendre polynomial of rational index");
+        raise e;
+
+### Chebyshev Polynomials        
+__chebyshev_initials = [[],[[_sage_const_1 ,_sage_const_0 ],[_sage_const_0 ,_sage_const_1 ]],[[_sage_const_1 ,_sage_const_0 ],[_sage_const_0 ,_sage_const_2 ]]];
+@cached_function    
+def ChebyshevD(input, kind = _sage_const_1 ):
+    global __chebyshev_initials;
+    if(input is None):
+        return DDFunction_example('chebyshev%d' %kind);
+
+    try:
+        n = ZZ(input);
+        if(n < _sage_const_0 ):
+            raise ValueError("Impossible to create a Legendre polynomial of negative index");
+            
+        P = DFiniteP.parameters()[_sage_const_0 ];
+        ## Building the differential equation
+        name = None;
+        if(kind == _sage_const_1 ):
+            func = DDFunction_example('chebyshev1')(**{str(P):n});
+            name = DinamicString("chebyshev_T(_1,_2)", [str(input), "x"]);
+        elif(kind == _sage_const_2 ):
+            func = DDFunction_example('chebyshev2')(**{str(P):n});
+            name = DinamicString("chebyshev_U(_1,_2)", [str(input), "x"]);
+        else:
+            raise ValueError("Impossible to manage Chebyshev polynomial of %d-th kind" %(kind));
+            
+        ## Computing initial values
+        for i in range(len(__chebyshev_initials[kind]), n+_sage_const_1 ):
+            prev = __chebyshev_initials[kind][-_sage_const_1 ];
+            prev2 = __chebyshev_initials[kind][-_sage_const_2 ];
+            __chebyshev_initials[kind] += [[-prev2[_sage_const_0 ], _sage_const_2 *prev[_sage_const_0 ]-prev2[_sage_const_1 ]]];
+        return func.change_init_values(__chebyshev_initials[kind][n],name);
+    except TypeError as e:
+        raise e;    
+
+### Hypergeometric Functions
+__CACHED_HYPERGEOMETRIC = {};
+
+def HypergeometricFunction(a,b,c, init = _sage_const_1 ):
+    return GenericHypergeometricFunction([a,b],[c],init);
+
+def GenericHypergeometricFunction(num=[],den=[],init=_sage_const_1 ):
+    if (not (isinstance(num,list) or isinstance(num,set) or isinstance(num,tuple))):
+        num = [num];
+    else:
+        num = list(num);
+    if (not (isinstance(den,list) or isinstance(den,set) or isinstance(den,tuple))):
+        den = [den];
+    else:
+        den = list(den);
+        
+    ## Cleaning repeated values 
+    i = _sage_const_0 ;
+    while(i < len(num) and len(den) > _sage_const_0 ):
+        if(num[i] in den):
+            den.remove(num[i]);
+            num.remove(num[i]);
+        else:
+            i += _sage_const_1 ;
+    
+    ## Sort list for cannonical input
+    num.sort(); den.sort();
+    
+    ## Casting to tuples to have hash  
+    num = tuple(num); den = tuple(den);
+    
+    ## Checking the function is cached
+    global __CACHED_HYPERGEOMETRIC;
+    if(not((num,den,init) in __CACHED_HYPERGEOMETRIC)):
+        ## Building differential operator
+        get_op = lambda p : DFinite.element(p).equation;
+        op_num = x*prod(get_op([el,x]) for el in num);
+        op_den = x*get_op([_sage_const_0 ,_sage_const_1 ])*prod(get_op([el-_sage_const_1 ,x]) for el in den);
+        op = op_num - op_den;
+        
+        f = DFinite.element(op);
+        
+        initVals = [init];
+        
+        if(init == _sage_const_1 ):
+            __CACHED_HYPERGEOMETRIC[(num,den,init)] = f.change_init_values([_sage_const_1 ],name=DinamicString("hypergeometric(_1,_2,_3)", [str(num),str(den),"x"]));
+        else:
+            __CACHED_HYPERGEOMETRIC[(num,den,init)] = f.change_init_values([init],name=DinamicString("%d*(hypergeometric(_1,_2,_3))", [str(num),str(den),"x"]));
+        
+    ## Return the cached element
+    return __CACHED_HYPERGEOMETRIC[(num,den,init)];
+    
+### Mathieu's Functions
+@cached_function
+def MathieuD(a=None,q=None,init=()):
+    params =[];
+    if(a is None):
+        params += ['a'];
+    if(q is None):
+        params += ['q'];
+    
+    destiny_ring = DDFinite; ra = a; rq = q;
+    if(len(params) > _sage_const_0 ):
+        destiny_ring = ParametrizedDDRing(DDFinite, params);
+        if(len(params) == _sage_const_2 ):
+            ra,rq = destiny_ring.parameters();
+        elif('a' in params):
+            ra = destiny_ring.parameters()[_sage_const_0 ]; rq = q;
+        else:
+            rq = destiny_ring.parameters()[_sage_const_0 ]; ra = a;
+        
+    return destiny_ring.element([ra-_sage_const_2 *rq*Cos(_sage_const_2 *x), _sage_const_0 , _sage_const_1 ], init, name=DinamicString("Mathieu(_1,_2;_3)(_4)", [repr(ra),repr(rq),str(list(init[:_sage_const_2 ])),"x"]));
+
+@cached_function
+def MathieuSin(a=None,q=None):
+    return MathieuD(a,q,(_sage_const_0 ,_sage_const_1 ));
+    
+@cached_function
+def MathieuCos(a=None,q=None):
+    return MathieuD(a,q,(_sage_const_1 ,_sage_const_0 ));
+    
+##################################################################################
+##################################################################################
+###
+### Particular differential Equations
+###
+##################################################################################
+##################################################################################  
+### Federschwinger // Swing with mass
+## f'' + 2a f' + b^2f = ksin(cx)
+@cached_function
+def Federschwinger(a,b,c,k,init=(_sage_const_0 ,_sage_const_0 )):
+    return DDFinite.element([b**_sage_const_2 ,_sage_const_2 *a,_sage_const_1 ], init, k*Sin(c*x));
+    
+##################################################################################
+##################################################################################
+###
+### Private methods
+###
+##################################################################################
+##################################################################################    
+def __decide_parent(input, parent = None, depth = _sage_const_1 ):
+    if(parent is None):
+        R = input.parent();
+        if(isinstance(R, sage.symbolic.ring.SymbolicRing)):
+            parameters = set([str(el) for el in input.variables()])-set(['x']);
+            if(len(parameters) > _sage_const_0 ):
+                parent = ParametrizedDDRing(DDRing(DFinite, depth=depth), parameters);
+            else:
+                parent = DDRing(PolynomialRing(QQ,x), depth=depth);
+        else:
+            try:
+                parent = DDRing(R, depth = depth);
+            except Exception:
+                raise TypeError("The object provided is not in a valid Parent");
+    
+    return parent.base()(input), parent;
+    
+#### Usual running after defining everything
+DD_EXAMPLES_LOAD();
+
diff --git a/ajpastor/dd_functions/ddFunction.py b/ajpastor/dd_functions/ddFunction.py
new file mode 100644 (file)
index 0000000..206f9e9
--- /dev/null
@@ -0,0 +1,2065 @@
+
+# This file was *autogenerated* from the file ./ddFunction.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_3 = Integer(3); _sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0); _sage_const_5 = Integer(5); _sage_const_4 = Integer(4); _sage_const_100 = Integer(100); _sage_const_12 = Integer(12); _sage_const_11 = Integer(11); _sage_const_1en10 = RealNumber('1e-10')
+
+# Python imports
+import warnings;
+
+#SAGE imports 
+from sage.rings.ring import IntegralDomain;
+from sage.rings.polynomial.polynomial_ring import is_PolynomialRing as isPolynomial;
+from sage.structure.element import IntegralDomainElement;
+from sage.categories.integral_domains import IntegralDomains;
+from sage.categories.pushout import ConstructionFunctor;
+
+#ajpastor imports
+from ajpastor.operator.operator import Operator;
+from ajpastor.operator.oreOperator import w_OreOperator;
+from ajpastor.operator.directStepOperator import DirectStepOperator;
+from ajpastor.operator.fullLazyOperator import FullLazyOperator;
+
+from ajpastor.misc.dinamic_string import *;
+from ajpastor.misc.cached_property import derived_property;
+from ajpastor.misc.ring_w_sequence import Ring_w_Sequence;
+from ajpastor.misc.ring_w_sequence import Wrap_w_Sequence_Ring;
+from ajpastor.misc.ring_w_sequence import sequence;
+
+
+#####################################################
+### Definition of the particular warnings we are interested to raise
+#####################################################
+class DDFunctionWarning(RuntimeWarning):
+    pass;
+
+class MergingRingWarning(DDFunctionWarning):
+    pass;
+    
+class TooDeepWarning(DDFunctionWarning):
+    pass;
+    
+class NoOreAlgebraWarning(DDFunctionWarning):
+    pass;
+
+warnings.simplefilter('always', DDFunctionWarning);
+    
+
+## Auxiliar derivative function
+def _aux_derivation(p):
+    try:
+        R = p.parent();
+        return derivative(p,R(x));
+    except Exception:
+        return _sage_const_0 ;
+        
+#####################################################
+### Class for DD-Rings
+#####################################################
+class DDRing (Ring_w_Sequence, IntegralDomain):
+    Element = None;
+    
+    _Default_Base = PolynomialRing(QQ,x);
+    _Default_Depth = _sage_const_1 ;
+    _Default_Base_Field = QQ;
+    _Default_Invertibility = None;
+    _Default_Derivation = None;
+    _Default_Operator = None;
+    _Default_Parameters = [
+        ("base", _Default_Base),
+        ("depth", _Default_Depth),
+        ("base_field", _Default_Base_Field),
+        ("invertibility", _Default_Invertibility),
+        ("derivation", _Default_Derivation),
+        ("default_operator", _Default_Operator)];
+    
+    _CACHED_DD_RINGS = {};
+    
+    _Default_variable = 'x';
+    
+    #################################################
+    ### Static methods
+    #################################################
+    @staticmethod
+    def __get_gens__(parent):
+        current = parent; 
+        const = parent.construction(); 
+        gens = [el for el in parent.gens()];
+        
+        while((not(const is None)) and (not (_sage_const_1  in gens))):
+            current = const[_sage_const_1 ];
+            gens += [el for el in current.gens()];
+            const = current.construction();
+            
+        while(_sage_const_1  in gens):
+            gens.remove(_sage_const_1 );
+            
+        return tuple(set(gens));
+        
+    #################################################
+    ### Builder
+    #################################################
+    @staticmethod
+    def __classcall__(cls, *args, **kwds):
+        final_args = [];
+                
+        ## We first compile the parameters the user send
+        current = _sage_const_0 ;
+        if(len(args) != _sage_const_1  or args[_sage_const_0 ] != None):
+            for i in range(len(args)):
+                final_args += [[DDRing._Default_Parameters[i][_sage_const_0 ], args[i]]];
+            current = len(args);
+        
+        for i in range(current, len(DDRing._Default_Parameters)):
+            final_args += [[DDRing._Default_Parameters[i][_sage_const_0 ], kwds.get(DDRing._Default_Parameters[i][_sage_const_0 ],DDRing._Default_Parameters[i][_sage_const_1 ])]];
+            
+        ## Managing the depth over DDRings
+        if(isinstance(final_args[_sage_const_0 ][_sage_const_1 ], DDRing)):
+            final_args[_sage_const_1 ][_sage_const_1 ] = final_args[_sage_const_1 ][_sage_const_1 ]+final_args[_sage_const_0 ][_sage_const_1 ].depth();
+            final_args[_sage_const_0 ][_sage_const_1 ] = final_args[_sage_const_0 ][_sage_const_1 ]._DDRing__original;
+            
+        ## Casting to tuple (so is hashable)
+        to_hash = tuple(tuple(el) for el in final_args[:_sage_const_3 ]);
+        final_args = tuple(tuple(el) for el in final_args);
+        
+        if(final_args[_sage_const_1 ][_sage_const_1 ] == _sage_const_0 ):
+            return final_args[_sage_const_0 ][_sage_const_1 ];
+            
+        if(not cls in DDRing._CACHED_DD_RINGS):
+            DDRing._CACHED_DD_RINGS[cls] = {};
+        if(not (to_hash in DDRing._CACHED_DD_RINGS[cls])):
+            tmp = IntegralDomain.__new__(cls);
+            DDRing.__init__(tmp,**dict(final_args));
+            DDRing._CACHED_DD_RINGS[cls][to_hash] = tmp;
+       
+        return DDRing._CACHED_DD_RINGS[cls][to_hash];
+    
+    def __init__(self, base=_Default_Base, depth = _Default_Depth, base_field = _Default_Base_Field, invertibility = _Default_Invertibility, derivation = _Default_Derivation, default_operator = _Default_Operator):
+        '''
+            Class representing the Ring of Differentially Definable Functions with coefficients over a base ring. It can be built using different parameter configurations:
+                - ``base``: is the ring where the coefficients of the differential equation will be. It is set by default as `\mathbb{Q}[x]`.
+                - ``invertibility``: a function to decide if the argument is a unit in the ring we are building.
+                - ``derivation``: a function to derivate elements in the coefficients ring.
+                
+            More formally, ``(base,derivation)`` should be a Differential Ring and the result (represented by this class) will be another Differential Ring which extends the original.
+        '''
+        ## Other attributes initialization
+        self.__variables = None;
+        
+        if(depth > _sage_const_1 ):
+            DDRing.__init__(self,DDRing(base, depth-_sage_const_1 , base_field, invertibility, derivation, default_operator), _sage_const_1 , base_field, invertibility, derivation, default_operator);
+        else:
+            ### We call the builders of the superclasses
+            Ring_w_Sequence.__init__(self, base, method = lambda p,n : self(p).getSequenceElement(n));
+            IntegralDomain.__init__(self, base, category=IntegralDomains());
+            
+            ### We assign the operator class
+            if(default_operator is None):
+                ## In this case we add an default Operator
+                if(isinstance(base, DDRing)):
+                    self.default_operator = FullLazyOperator;
+                else:
+                    self.default_operator = DirectStepOperator;
+            elif(issubclass(default_operator, Operator)):
+                self.default_operator = default_operator;
+            else:
+                raise TypeError("Invalid type for Operators in this ring. Must inherit from class Operator.");
+            
+            ### In order to get Initial Values, we keep the original field base 
+            # If the base ring is already a DDRing, we take the correspond ring from base
+            if isinstance(base, DDRing):
+                self.base_field = base.base_field;
+                self.__depth = base.__depth + _sage_const_1 ;
+                self.__original = base.__original;
+            # Otherwise, we set this simplest ring as our current coefficient ring
+            else:
+                self.base_field = base_field;
+                self.__depth = _sage_const_1 ;
+                self.__original = base;
+            
+            ### Saving the invertibility criteria
+            if(invertibility is None):
+                try:
+                    self_var = self.variables(True,False)[_sage_const_0 ];
+                    self.base_inversionCriteria = lambda p : p(**{self_var : _sage_const_0 }) == _sage_const_0 ;
+                except IndexError:
+                    self.base_inversionCriteria = lambda p : self.base()(p)==_sage_const_0 ;
+            else:
+                self.base_inversionCriteria = invertibility;
+            
+            ### Saving the base derivation
+            if(derivation is None):
+                try:
+                    self_var = self.variables(True,False)[_sage_const_0 ];
+                    self.base_derivation = lambda p : p.derivative(self_var);
+                except IndexError:
+                    self.base_derivation = lambda p : _sage_const_0 ;
+            else:
+                self.base_derivation = derivation;
+            
+            
+            
+            ### Setting new coercions
+            current = self;
+            while(not(current.base() == current)):
+                current = current.base();
+                morph = DDSimpleMorphism(self, current);
+                current.register_conversion(morph);
+        
+        
+        
+    #################################################
+    ### Coercion methods
+    #################################################
+    def _coerce_map_from_(self, S):
+        coer = None;
+        if(isinstance(S, DDRing)):
+            coer = self.base()._coerce_map_from_(S.base());
+        elif(S == self.base()):
+            coer = True;
+        elif(isinstance(S, sage.symbolic.ring.SymbolicRing)):
+            coer = True;
+        else:
+            coer = self.base()._coerce_map_from_(S);
+            
+        if(not(coer is False) and not(coer is None)):
+            return True;
+        return None;
+        
+    def __is_polynomial(self, S):
+        from sage.rings.polynomial.polynomial_ring import is_PolynomialRing as isUniPolynomial;
+        from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing as isMPolynomial;
+        
+        return isUniPolynomial(S) or isMPolynomial(S);
+        
+    def _pushout_(self, S):
+        if(isinstance(S, DDRing)):
+            from sage.categories.pushout import pushout;
+            if(isinstance(S, ParametrizedDDRing) and isinstance(self, ParametrizedDDRing)):
+                if(len(set(self.parameters(True)).intersection(S.parameters(True))) > _sage_const_0 ):
+                    import traceback;
+                    entry = traceback.extract_stack(limit=_sage_const_3 )[-_sage_const_3 ];
+                    warnings.warn_explicit("Merging rings with common parameters. \n\t* %s\n\t* %s\n--Are you sure they are the same?" %(repr(self),repr(S)), MergingRingWarning, *(list(entry[:_sage_const_3 ])));
+                pushout_ddRing = pushout(self.base_ddRing(),S.base_ddRing());
+                return ParametrizedDDRing(pushout_ddRing, set(self.parameters(True)).union(S.parameters(True)));
+            elif(isinstance(S, ParametrizedDDRing)):
+                return S._pushout_(self);
+            elif(isinstance(self, ParametrizedDDRing)):
+                pushout_ddRing = pushout(self.base_ddRing(),S);
+                return ParametrizedDDRing(pushout_ddRing, self.parameters(True));
+            else:
+                pushout_base = pushout(self.original_ring(),S.original_ring());
+                pushout_field = pushout(self.base_field, S.base_field);
+                return DDRing(pushout_base,depth = max(self.depth(),S.depth()),base_field=pushout_field);
+                
+        const = S.construction();
+        if((not(const is None)) and (isinstance(const[_sage_const_0 ], sage.categories.pushout.FractionField))):
+            S = S.base();
+        if(self.__is_polynomial(S)):
+            all_params = set(str(el) for el in DDRing.__get_gens__(S));
+            params = all_params - set([str(el) for el in self.variables(True)]);
+            variables = all_params - params;
+            
+            base_ring = self;
+            if(len(variables) > _sage_const_0 ):
+                base_ring = DDRing(PolynomialRing(self.base_field, list(variables)), depth=self.depth());
+            
+            if(len(params) > _sage_const_0 ):
+                return ParametrizedDDRing(base_ring, params);
+            else:
+                return base_ring;
+                
+        return None;
+        
+    def _has_coerce_map_from(self, S):
+        coer =  self._coerce_map_from_(S);
+        return (not(coer is False) and not(coer is None));
+        
+    def _element_constructor_(self, *args, **kwds):
+        el_class = self.element_class;
+        if(len(args) < _sage_const_1 ):
+            raise ValueError("Impossible to build a lazy element without arguments");
+        
+        if(args[_sage_const_0 ] is self):
+            X = args[_sage_const_1 ];
+        else:
+            X = args[_sage_const_0 ];
+           
+        try:
+            if(isinstance(X, DDFunction)):
+                if(X.parent() is self):
+                    return X;
+                else:
+                    return self.element([coeff for coeff in X.equation.getCoefficients()], X.getInitialValueList(X.equation.get_jp_fo()+_sage_const_1 ), name=X._DDFunction__name);
+            else:
+                try:
+                    X = self.base()(X);
+                    el = self.element([-self.base_derivation(X), X]);
+                    name = str(X);
+                    try:
+                        name = X._DDFunction__name;
+                    except:
+                        pass;
+                    return el.change_init_values([sequence(X,i)*factorial(i) for i in range(el.equation.get_jp_fo()+_sage_const_1 )], name=name);
+                except AttributeError:
+                    return self.element([_sage_const_1 ],[], self.base()(X), name=str(X));
+        except TypeError as e:
+            #try:
+            #    if(isinstance(X.parent(),sage.symbolic.ring.SymbolicRing)):
+            #        #print "Casting a symbolic"
+            #        new_parent = ParametrizedDDRing(self,X.variables())
+            #        el = new_parent(X);
+            #        print new_parent;
+            #        print el;
+            #        return el
+            #except:
+            raise TypeError("Cannot cast an element to a DD-Function of (%s):\n\tElement: %s (%s)\n\tReason: %s" %(self, X, type(X), e));
+            
+    def gens(self):
+        return ();
+    
+    def ngens(self):
+        return _sage_const_0 ;
+            
+    def construction(self):
+        return (DDRingFunctor(self.depth(), self.base_field), self.__original);
+        
+    def __contains__(self, X):
+        try:
+            return (X.parent() is self) or (self._has_coerce_map_from(X.parent()));
+        except AttributeError:
+            try:
+                self(X)
+                return True;
+            except Exception:
+                return False;
+    
+    #################################################
+    ### Magic python methods
+    #################################################
+    def __eq__(self, other):
+        if(isinstance(other, DDRing)):
+            return self._has_coerce_map_from(other) and other._has_coerce_map_from(self);
+        return False;
+     
+    ## Other magic methods   
+    def _repr_(self):
+        '''
+            Method implementing the __repr__ magic python method. Returns a string telling that self is a DDRing and which Ring is its base.
+        '''
+        return "DD-Ring over (%s)" %(self.base());
+        
+    def _to_command_(self):
+        return "DDRing(%s)" %command(self.base());
+            
+    #################################################
+    ### Integral Domain and Ring methods
+    #################################################
+    def _an_element_(self):
+        '''
+            Method inherited from Ring SAGE class. It returns an example of object that is in the DDRing. It simply returns the 1 element.        
+        '''
+        return self.one();
+        
+    def characteristic(self):
+        '''
+            Method inherited from the Ring SAGE class. It returns the characteristic of the coefficient ring.
+        '''
+        return self.base().characteristic();
+        
+    def base_ring(self):
+        return self.base_field;
+        
+    def original_ring(self):
+        return self.__original;
+        
+    def depth(self):
+        return self.__depth;
+        
+    def to_depth(self, dest):
+        return DDRing(self.original_ring(), depth = dest, base_field = self.base_field, invertibility = self.base_inversionCriteria, derivation = self.base_derivation, default_operator = self.default_operator);
+        
+    def is_field(self):
+        return False;
+        
+    def is_finite(self):
+        return False;
+        
+    def is_noetherian(self):
+        return True;
+
+    #################################################
+    ### DDRings methods
+    #################################################
+    def invertible(self,x):
+        '''
+            Method to make easier call the inversibility criteria specified when a DDRing was created.
+            
+            INPUT:
+                - ``x``: an element of self.
+            OUTPUT:
+                - True if ``x`` is in self and it is a unit.
+                - False otherwise.
+        '''
+        return self.base_inversionCriteria(x);
+        
+    def element(self,coefficients=[],init=[],inhomogeneous=_sage_const_0 , check_init=True, name=None):
+        '''
+            Method to create a DDFunction contained in self with some coefficients, inhomogeneous term and initial values. This method just call the builder of DDFunction just puting the base argument as self.
+        '''
+        return DDFunction(self,coefficients,init,inhomogeneous, check_init=check_init, name=name);
+        
+    def eval(self, element, X=None, **input):
+        if not element in self:
+            raise TypeError('The element we want to evaluate (%s) is not an element of this ring (%s)'%(element,self));
+        element = self(element);
+            
+        rx = X;
+        self_var = str(self.variables(True)[_sage_const_0 ]);
+        if((rx is None) and (self_var in input)):
+            rx = input.pop(self_var);
+        if not (rx is None):
+            if(rx == _sage_const_0 ):
+                return element.getInitialValue(_sage_const_0 );
+            elif(rx in self.base_field):
+                return element.numerical_solution(rx,**input)[_sage_const_0 ];
+            try:
+                return element.compose(rx);
+            except ValueError:
+                pass;
+        elif(len(input) == _sage_const_0 ):
+            return element;
+        
+        raise NotImplementedError("Not implemented evaluation of an element of this ring (%s) with the parameters %s and %s" %(self,rx,input));
+        
+    
+    def variables(self, as_symbolic=False, fill = True):
+        if(self.__variables is None):
+            self.__variables = [];
+            current = self.base();
+            while(current != self.base_field):
+                self.__variables += [var(str(el)) for el in current.gens()];
+                current = current.base();
+            self.__variables = tuple(set(self.__variables));
+        
+        if(as_symbolic):
+            if(len(self.__variables) == _sage_const_0  and fill):
+                return tuple([var(DDRing._Default_variable)]);
+            return self.__variables;
+        else:
+            if(len(self.__variables) == _sage_const_0  and fill):
+                return tuple([self.base()(var(DDRing._Default_variable))]);
+            return tuple(self.base()(el) for el in self.__variables);
+        
+    #def getBaseMatrixSpace(self,nrows, ncols):
+    #    '''
+    #        Method to get a MatrixSpace with coefficients in the coefficient Ring of the current DDRing with an specified dimension.
+    #        
+    #        INPUT:
+    #            - ``nrows``: number or rows of the new MatrixSpace.
+    #            - ``rcols``: number of columns of the new MatrixSpace.
+    #    '''
+    #    return self.matrixSpace.matrix_space(nrows,ncols);
+        
+        
+#############################################################################
+###
+### Ring class for Parametrized DD Functions
+###
+#############################################################################
+class ParametrizedDDRing(DDRing):
+
+    @staticmethod
+    def __classcall__(cls, *args, **kwds):
+        ## In order to call the __classcall__ of DDRing we treat the arguments received
+        if(len(args) != _sage_const_2 ):
+            raise TypeError("Incompatible number of arguments for ParametrizedDDRing");
+            
+        base_ddRing = args[_sage_const_0 ]; parameters = args[_sage_const_1 ];
+        
+         ## First we get the new variables
+        if ((not(type(parameters) == tuple)) and (not(type(parameters) == list)) and (not(type(parameters) == set))):
+            parameters = [parameters];
+        else:
+            parameters = list(parameters);
+        if(len(parameters) == _sage_const_0 ):
+            raise TypeError("The list of variables can not be empty");
+        
+        for i in range(len(parameters)):
+            if(type(parameters[i]) == str):
+                parameters[i] = var(parameters[i]);
+            elif(type(parameters[i]) != sage.symbolic.expression.Expression):
+                raise TypeError("All values of the second argument must be strings or SAGE symbolic variables");
+        parameters = tuple(set(parameters));
+        
+        ## We consider not the case the base ring is already parametrized
+        if(isinstance(base_ddRing, ParametrizedDDRing)):
+            parameters = tuple(set(parameters).union(base_ddRing.parameters(True)));
+            base_ddRing = base_ddRing.base_ddRing();
+            
+        ## We rebuild now the base ring for the DDRing operator
+        base_field = base_ddRing.base_field;
+        constructions = [base_ddRing.construction()]; # Here is the DD-Ring operator
+            
+        while(constructions[-_sage_const_1 ][_sage_const_1 ] != base_field):
+            constructions += [constructions[-_sage_const_1 ][_sage_const_1 ].construction()];
+             
+        new_basic_field = PolynomialRing(base_field, parameters).fraction_field();  
+        current = new_basic_field;
+        for i in range(_sage_const_1 ,len(constructions)):
+            current = constructions[-i][_sage_const_0 ](current);
+            
+        if(constructions[_sage_const_0 ][_sage_const_0 ].depth() > _sage_const_1 ):
+            base_ring = ParametrizedDDRing(DDRing(base_ddRing.original_ring(),depth=constructions[_sage_const_0 ][_sage_const_0 ].depth()-_sage_const_1 ), parameters);
+            ring = DDRing.__classcall__(cls, base_ring, _sage_const_1 , base_field = new_basic_field, default_operator = base_ddRing.default_operator);
+            Ring_w_Sequence.__init__(ring, base_ring, method = lambda p,n : ring(p).getSequenceElement(n));
+            IntegralDomain.__init__(ring, base_ring, category=IntegralDomains());
+        else:
+            ring = DDRing.__classcall__(cls, current, depth = constructions[_sage_const_0 ][_sage_const_0 ].depth(), base_field = new_basic_field, default_operator = base_ddRing.default_operator);
+            
+        ring.__init__(base_ddRing, parameters);
+        return ring;
+        
+    def __init__(self, base_ddRing, parameters):
+        '''
+            This class represent a generilized concept of DDRing. If `R` is a domain of the power series space (`K[[x]]`), and `D(R)` is its associated DDRing, then we can consider new constants elements and consider `D(R)`  but with the basic field be `K(var_1,...,var_m)`
+            
+            INPUT:
+                - base_ddRing: Base DDRing where this new ring will be based. This parameter can be got using method ``getBaRing``.
+                - variables: a list of variables or strings which will be the names of the new variables.
+        '''
+        ## Checking the first argument
+        if ((not(isinstance(base_ddRing, DDRing))) or (isinstance(base_ddRing, ParametrizedDDRing))):
+            raise TypeError("Needed a DDRing as first argument for create a Parametrized DDRing");
+        
+        self.__parameters = tuple(set(parameters));
+                    
+        self.__baseDDRing = base_ddRing;
+            
+    def _coerce_map_from_(self, S):
+        coer = super(ParametrizedDDRing, self)._coerce_map_from_(S);
+        if(not(coer)):
+            coer = self.__baseDDRing._coerce_map_from_(S);
+            
+        return not(not(coer));
+            
+    def construction(self):
+        return (ParametrizedDDRingFunctor(self.depth(), self.base_field, set(self.__parameters)), self.__baseDDRing);
+            
+    def base_ddRing(self):
+        '''
+            Method that retrieves the original DDRing where this parametrized space is based on.
+            
+            OUTPUT:
+                - DDRing where elements of ``self`` would be if we substitute the parameters by elements of the base ring.
+        '''
+        return self.__baseDDRing;
+        
+    def _repr_(self):
+        res = "(%s" %str(self.parameters()[_sage_const_0 ]);
+        for i in range(_sage_const_1 ,len(self.parameters())):
+            res += ", " + str(self.parameters()[i]);
+        res += ")";
+        
+        if(len(self.parameters()) > _sage_const_1 ):
+            return "%s with parameters %s" %(self.base_ddRing(),res);
+        else:
+            return "%s with parameter %s" %(self.base_ddRing(),res);
+    
+    @cached_method
+    def parameters(self, as_symbolic = False):
+        if(as_symbolic):
+            return self.__parameters;
+        else:
+            return [self.base_field(el) for el in self.__parameters];
+            
+    def gens(self):
+        return self.parameters(True);
+        
+    def ngens(self):
+        return len(self.parameters());
+            
+    #def variables(self, as_symbolic=False):
+    #    return self.base_ddRing().variables(as_symbolic);
+            
+    # Override to_depth method from DDRing
+    def to_depth(self, dest):
+        return ParametrizedDDRing(self.base_ddRing().to_depth(dest), self.parameters(True));
+            
+    # Override eval method from DDRing
+    def eval(self, element, X=None, **input):
+        rx = X;
+        self_var = str(self.variables(True)[_sage_const_0 ]);
+        if(X is None and self_var in input):
+            rx = input.pop(self_var);
+        ### If X is not None, then we evaluate the variable of the ring
+        if(not (rx is None)):
+            if(len(input) > _sage_const_0 ):
+                return self.eval(element, **input)(**{self_var:rx});
+            else:
+                return super(ParametrizedDDRing, self).eval(element, rx);
+        elif(len(input) > _sage_const_0 ):
+            ### Otherwise, we evaluate the parameters
+            element = self(element);
+            
+            ### Getting the final parameters
+            original_parameters = set(str(el) for el in self.parameters());
+            used_parameters = set(input.keys());
+            got_parameters = reduce(lambda p,q : p.union(q), [set(str(el) for el in SR(value).variables()) for value in input.values()]);
+            
+            destiny_parameters = (original_parameters - used_parameters).union(got_parameters);
+                        
+            if(self_var in destiny_parameters):
+                raise TypeError("Parameters can only be evaluated to constants.\n\t- Got: %s" %(input));
+            
+            if(len(destiny_parameters) == _sage_const_0 ):
+                destiny_ring = self.base_ddRing();
+            else:
+                destiny_ring = ParametrizedDDRing(self.base_ddRing(), tuple(destiny_parameters));
+                
+            new_equation = destiny_ring.element([el(**input) for el in element.equation.getCoefficients()]).equation;
+            
+            new_init = [el(**input) for el in element.getInitialValueList(new_equation.get_jp_fo()+_sage_const_1 )];
+            new_name = None;
+            if(element._DDFunction__name is not None):
+                new_name = din_m_replace(element._DDFunction__name, {key: str(input[key]) for key in input}, True);
+            return destiny_ring.element(new_equation,new_init,name=new_name);
+        return element;
+            
+        
+#####################################################
+### Class for DD-Functions
+#####################################################
+class DDFunction (IntegralDomainElement):
+    #####################################
+    ### Init and Interface methods
+    #####################################
+    def __init__(self, parent, input = _sage_const_0 , init_values = [], inhomogeneous = _sage_const_0 , check_init = True, name = None):
+        '''
+            Method to initialize a DD-Function insade the DD-Ring given in 'parent'
+            
+            The object will represent a function 'f' such that
+                - input (f) = inhomogeneous;
+                - f^{(i)}(0) = init_values[i]
+        '''        
+        # We check that the argument is a DDRing
+        if(not isinstance(parent, DDRing)):
+            raise TypeError("A DD-Function must be an element of a DD-Ring");
+        ### We call superclass builder
+        IntegralDomainElement.__init__(self, parent);
+        
+        ## Checking we have some input (if not, we assume the input for zero)
+        ## We take care of the following arguments
+        ##     input -> equation
+        ##     init_values -> init
+        ##     inhomogeneous -> inhom
+        zero = False;
+        if((type(input) == list and len(input) == _sage_const_0 ) or input == _sage_const_0 ):
+            zero = True;
+        elif(type(input) == list and all(el == _sage_const_0  for el in input)):
+            zero = True;
+        elif((type(input) == list and len(input) == _sage_const_1 ) or (isinstance(input, Operator) and input.getOrder() == _sage_const_0 )):
+            zero = (inhomogeneous == _sage_const_0 );
+            
+        if(zero):
+            ### The equation is zero, we need inhomogeneous equals to zero
+            if(not inhomogeneous == _sage_const_0 ):
+                raise ValueError("Incompatible equation (%s) and inhomogeneous term (%s)" %(input, inhomogeneous));
+            else:
+            ### Also, all the initial values must be zero
+                for el in init_values:
+                    if (not el == _sage_const_0 ):
+                        raise ValueError("Incompatible equation (%s) and initial values (%s)" %(input, init_values));
+                init = [_sage_const_0 ];
+                equation = [_sage_const_0 ,_sage_const_1 ];
+                inhom = _sage_const_0 ;
+        else:
+            equation = input;
+            init = [el for el in init_values];
+            inhom = inhomogeneous;
+        
+        ### Cached elements
+        self.__pows = {_sage_const_0 :_sage_const_1 , _sage_const_1 :self}; # Powers-cache
+        self.__derivative = None; # The derivative of a function
+        
+        ### Assigning the differential operator
+        ### We will save the leading coefficient of the equation (lc) to future uses.
+        # We create the operator using the structure given by our DDRing
+        try:
+            self.equation = self.__buildOperator(equation);
+        except Exception as e:
+            print e;
+            raise TypeError("The input for this operator is not valid");
+            
+        ### Managing the inhomogeneous term
+        # We cast the inhomogeneous term to an element of self.parent()
+        # If that is not zero, then we compute the new operator and initial values needed
+        # to define the equation.
+        if(inhom != _sage_const_0 ):
+            ## Getting the basic elements
+            inhom = self.parent()(inhom);
+            field = parent.base_field;
+            
+            ## Getting the homogeneous differential equation
+            new_eq = inhom.equation*self.equation;
+            
+            ## Get the number of elements to check
+            ## If init is too big, we use all the elements for a better checking
+            n_init = new_eq.get_jp_fo()+_sage_const_1 ;
+            to_check = max(n_init, len(init));
+            
+            ## Getting the matrix of the current equation
+            M = Matrix(field, self.equation.get_recursion_matrix(to_check-_sage_const_1 -self.equation.forward_order));
+            v = vector(field, inhom.getSequenceList(M.nrows()));
+            
+            ## Solving the system MX = v
+            X = M * BackslashOperator() * v;
+            ker = M.right_kernel_matrix();
+            
+            ## Choosing a solution such X[i]==init[i]
+            diff = vector(field, [init[i]-X[i] for i in range(len(init))]);
+            N = Matrix(field, [[v[i] for i in range(len(init))] for v in ker]).transpose();
+
+            try:
+                to_sum = N * BackslashOperator() * diff;
+            except Exception:
+                raise ValueError("There is no function satisfying\n(%s)(f) == %s\nwith initial values %s" %(self.equation,inhom,init)); 
+            
+            ## Putting the new values for the equation and initial values
+            init = X+sum([to_sum[i]*ker[i] for i in range(len(to_sum))]);
+            init = [init[i]*factorial(i) for i in range(len(init))];
+            self.equation = new_eq;
+            
+        
+        ## After creating the original operator, we check we can not extract an "x" factor
+        coeff_gcd = _sage_const_1 ;
+        if(isPolynomial(self.parent().base())):
+            l = [];
+            for el in self.equation.getCoefficients():
+                l += el.coefficients(x);
+            
+            coeff_gcd = gcd(l);
+            if(coeff_gcd == _sage_const_0 ):
+                coeff_gcd = _sage_const_1 ;
+        g = coeff_gcd*gcd(self.equation.getCoefficients());
+        if(g != _sage_const_1  and g != _sage_const_0 ):
+            coeffs = [coeff/g for coeff in self.equation.getCoefficients()];
+            self.equation = self.__buildOperator(coeffs);
+                
+        ### Managing the initial values
+        init = [self.parent().base_field(str(el)) for el in init];
+        if(check_init):
+            self.__calculatedSequence = {};
+            if(len(init) > self.equation.get_jp_fo()):
+                initSequence = [init[i]/factorial(i) for i in range(len(init))];
+                M = self.equation.get_recursion_matrix(len(initSequence)-self.equation.forward_order-_sage_const_1 );
+                if(M*vector(initSequence) == _sage_const_0 ):
+                    for i in range(len(initSequence)):
+                        self.__calculatedSequence[i] = initSequence[i];
+                else:
+                    raise ValueError("There is no such function satisfying %s whith initial values %s"%(self.equation,init));
+            else:
+                for i in range(len(init)):
+                    try:
+                        get_init = self.getInitialValue(i);
+                        if(not (get_init == init[i])):
+                            raise ValueError("There is no such function satisfying %s whith such initial values (index %d:%s)"%(self.equation,i,init[i]));
+                    except TypeError:
+                        self.__calculatedSequence[i] = init[i]/factorial(i);
+        else:
+            self.__calculatedSequence = {i:init[i]/factorial(i) for i in range(len(init))};
+        
+        
+        ### Other attributes for DDFunctions
+        ### Setting up the name for the function
+        self.__name = name;
+        self.__built = None;
+            
+    def __buildOperator(self, coeffs):
+        if(isinstance(coeffs, self.parent().default_operator)):
+            return coeffs;
+        elif(type(coeffs) == list):
+            new_coeffs = [];
+            for el in coeffs:
+                try:
+                    new_coeffs += [self.parent().base()(el)];
+                except TypeError:
+                    try:
+                        new_coeffs += [self.parent().base()(str(el))];
+                    except:
+                        new_coeffs += [el];
+            coeffs = new_coeffs;
+                
+        return self.parent().default_operator(self.parent().base(), coeffs, self.parent().base_derivation);
+        
+    def getOperator(self):
+        '''
+            Getter method for the Linear Differential operator associated with the DDFunction.
+        '''
+        return self.equation;
+        
+    def getOrder(self):
+        '''
+            Getter method for the order of the equation that defines the DDFunction.
+        '''
+        return self.equation.getOrder();
+        
+    @derived_property
+    def ps_order(self):
+        if(self.is_null):
+            return -_sage_const_1 ;
+        else:
+            i = _sage_const_0 ;
+            while(self.getInitialValue(i) == _sage_const_0 ):
+                i += _sage_const_1 ;
+            
+            return i;    
+        
+    def getSequenceElement(self, n):
+        try:
+            ## If we have computed it, we just return
+            return self.__calculatedSequence[n];
+        except KeyError:                
+            ## Otherwise, we compute such element
+            
+            
+            if(n > self.equation.get_jp_fo()):
+                ## If the required value is "too far" we can get quickly the equation
+                rec = self.equation.get_recursion_row(n-self.equation.forward_order);
+            else:
+                ## Otherwise, we try to get as usual the value
+                d = self.getOrder();
+                i = max(n-d,_sage_const_0 );                      
+                rec = self.equation.get_recursion_row(i);
+                while(rec[n] == _sage_const_0  and i <= self.equation.jp_value):                   
+                    i += _sage_const_1 ;                           
+                    rec = self.equation.get_recursion_row(i);
+                if(rec[n] == _sage_const_0 ):
+                    raise TypeError("Impossible to compute recursively the required value");
+                ## Checking that we only need previous elements
+                for i in range(n+_sage_const_1 , len(rec)):
+                    if(not (rec[i] == _sage_const_0 )):
+                        raise TypeError("Impossible to compute recursively the required value");
+            
+            ## We do this operation in a loop to avoid computing initial values 
+            ## if they are not needed
+            res = self.parent().base_field.zero();
+            for i in range(n):
+                if(not (rec[i] == _sage_const_0 )):
+                    res -= rec[i]*(self.getSequenceElement(i));
+                    
+            self.__calculatedSequence[n] = self.parent().base_field(res/rec[n]);
+            
+        return self.__calculatedSequence[n];
+        
+    def getSequenceList(self, n):
+        '''
+            Extra method that returns the list of the first `n` initial coefficients of the power-serie expansion of the function. If it is not possible to get so many values, the method DO NOT return an error. It returns just a truncated list.
+        '''
+        ### We check the argument is correct
+        if n < _sage_const_0 :
+            raise ValueError("The argument must be a non-negative integer");
+        
+        res = [];
+        for i in range(n):
+            try:
+                res += [self.getSequenceElement(i)];
+            except TypeError:
+                break;
+        return res;
+        
+    def getInitialValue(self, n):
+        '''
+            Getter method for an initial value.
+                - ``n``: order of the initial value expected to obtain, i.e. the return will be `D^n(f)(0)`.
+                
+            This method can raise an error if the initial value has not been provided and it is impossible to get it.
+        '''
+        return self.getSequenceElement(n)*factorial(n);
+        
+    def getInitialValueList(self, n):
+        '''
+            Extra method that returns the list of the first `n` initial values for the function. If it is not possible to get so many values, the method DO NOT return an error. It returns just a truncated list.
+        '''
+        ### We check the argument is correct
+        if n < _sage_const_0 :
+            raise ValueError("The argument must be a non-negative integer");
+        
+        res = [];
+        for i in range(n):
+            try:
+                res += [self.getInitialValue(i)];
+            except TypeError:
+                break;
+        return res;
+        
+    @cached_method
+    def size(self):
+        to_sum = [self.getOrder()];
+        if(isinstance(self.parent().base(), DDRing)):
+            to_sum += [el.size() for el in self.equation.getCoefficients()];
+        else:
+            for coeff in self.equation.getCoefficients():
+                try:
+                    if(coeff != _sage_const_0 ):
+                        to_sum += [coeff.degree()]
+                except Exception:
+                    pass;           
+        return sum(to_sum);
+        
+    def built(self):
+        return self.__built;
+        
+    def change_built(self, type, data):
+        if(type == "derivative"):
+            ## Nothing to check
+            pass;
+        elif(type == "polynomial"):
+            ## Check that the polynomial has appropriate variables
+            polynomial, variables = data;
+            vars_in_pol = None;
+            if(polynomial.parent().is_field()):
+                poly_ring = polynomial.parent().base();
+                vars_in_pol = tuple(set(poly_ring(polynomial.numerator()).variables()).union(set(poly_ring(polynomial.denominator()).variables())));
+            else:
+                vars_in_pol = polynomial.variables();
+                
+            for var in vars_in_pol:
+                if(not str(var) in variables):
+                    raise TypeError("The variables in the polynomial does not appear in the given map.\n\t- Polynomial: %s\n\t- Map: %s" %(polynomial, variables));
+                            
+        self.__built = tuple([type,data]);
+        
+    def change_name(self, new_name):
+        self.__name = new_name;
+            
+    #####################################
+    ### Arithmetic methods
+    #####################################
+    def scale_operator(self, r):
+        r = self.parent().base()(r);
+        
+        return self.parent().element(r*self.getOperator(), self.getInitialValueList(self.getOrder()), check_init=False);
+        
+    def change_init_values(self,newInit,name=None):
+        newElement = self.parent().element(self.getOperator(), newInit,name=name);
+        
+        ## Returning the new created element
+        return newElement;
+        
+    @derived_property
+    def inverse(self):
+        '''
+            Method that compute and return a DD-Function `f` such `f*self == 1`, i.e. this method computes the multiplicative inverse of `self`.
+        '''
+        if(self.getInitialValue(_sage_const_0 ) == _sage_const_0 ):
+            raise ValueError("Can not invert a function with initial value 0 --> That is not a power serie");
+        
+        coeffs = self.getOperator().getCoefficients();
+        
+        ### Computing the new name
+        newName = None;
+        if(not(self.__name is None)):
+            newName = DinamicString("1/(_1)",self.__name);
+        if(self.getOrder() == _sage_const_0 ):
+            raise ZeroDivisionError("Impossible to invert the zero function");
+        elif(self.getOrder() == _sage_const_1 ):
+            return self.parent().element([-coeffs[_sage_const_0 ],coeffs[_sage_const_1 ]], [_sage_const_1 /self.getInitialValue(_sage_const_0 )], check_init=False, name=newName);
+        else:
+            newDDRing = DDRing(self.parent());
+            return newDDRing.element([self.derivative(), self], [_sage_const_1 /self.getInitialValue(_sage_const_0 )], check_init=False, name=newName);
+    
+    def add(self, other):
+        '''
+            Method that adds two DDFunctions.
+            
+            INPUT:
+                - ``other``: a DDFunction which will be added to `self`.
+                
+            WARNING:
+                - This method usually returns a new object different from `self` or `other`. But if one of the parameters is zero, then the output will be the other function, the SAME object.
+                - Whenever an error occurs, a NotImplemented error will be returned in order to Python can make the correspondent call to _radd_ method of `other` if necessary.
+        '''
+        ### We check some simplifications: if one of the functions is zero, then we can return the other
+        if(self.is_null):
+            return other;
+        elif (other.is_null):
+            return self;
+                        
+        ### Computing the new name
+        newName = None;
+        if(not(self.__name is None) and (not(other.__name is None))):
+            if(other.__name[_sage_const_0 ] == '-'):
+                newName = DinamicString("_1_2", [self.__name, other.__name]); 
+            else:
+                newName = DinamicString("_1+_2", [self.__name, other.__name]); 
+                
+        
+        ## We check other simplifications: if the elements are constants
+        if(self.is_constant or other.is_constant):
+            result = None;
+            if(self.is_constant and other.is_constant):
+                parent = self.parent();
+                newOperator = [_sage_const_0 ,_sage_const_1 ];
+                newInit = [self(_sage_const_0 )+other(_sage_const_0 )];
+                result = parent.element(newOperator, newInit, check_init = False, name=newName);
+            elif(other.is_constant):
+                parent = self.parent();
+                newOperator = parent.element(self.equation, inhomogeneous=other(_sage_const_0 )*self.equation.getCoefficient(_sage_const_0 )).equation;
+                newInit = [self(_sage_const_0 )+other(_sage_const_0 )] + [self.getInitialValue(i) for i in range(_sage_const_1 ,newOperator.get_jp_fo()+_sage_const_1 )];
+                result = parent.element(newOperator, newInit, check_init = False, name=newName);
+                result.change_built("polynomial", (PolynomialRing(self.parent().base_field,'x1')("x1+%s" %other(_sage_const_0 )), {'x1':self}));
+            elif(self.is_constant):
+                parent = other.parent();
+                newOperator = parent.element(other.equation, inhomogeneous=self(_sage_const_0 )*other.equation.getCoefficient(_sage_const_0 )).equation;
+                newInit = [self(_sage_const_0 )+other(_sage_const_0 )] + [other.getInitialValue(i) for i in range(_sage_const_1 ,newOperator.get_jp_fo()+_sage_const_1 )];
+                result = parent.element(newOperator, newInit, check_init = False, name=newName)
+                result.change_built("polynomial", (PolynomialRing(self.parent().base_field,'x1')("x1+%s" %self(_sage_const_0 )), {'x1':other}));
+            return result;
+        
+        ## We build the new operator
+        if(self.equation == other.equation):
+            newOperator = self.equation;
+        else:
+            newOperator = self.equation.add_solution(other.equation);
+            
+        ### Getting the needed initial values for the solution
+        needed_initial = newOperator.get_jp_fo()+_sage_const_1 ;
+        
+        ### Getting as many initial values as posible until the new order
+        op1Init = self.getInitialValueList(needed_initial);
+        op2Init = other.getInitialValueList(needed_initial);
+        newInit = [op1Init[i] + op2Init[i] for i in range(min(len(op1Init),len(op2Init)))];
+                   
+        result = self.parent().element(newOperator, newInit, check_init=False, name=newName);
+        result.change_built("polynomial", (PolynomialRing(self.parent().base_field,['x1','x2'])('x1+x2'), {'x1':self, 'x2': other}));
+        return result;
+    
+    def sub(self, other):
+        '''
+            Method that substract two DDFunctions.
+            
+            INPUT:
+                - ``other``: a DDFunction which will be substracted to `self`.
+                
+            WARNING:
+                - Whenever an error occurs, a NotImplemented error will be returned in order to Python can make the correspondent call to _rsub_ method of `other` if necessary.
+        '''
+        if(self.is_null):
+            return -other;
+        elif (other.is_null):
+            return self;
+            
+            
+        ### Computing the new name
+        newName = None;
+        if(not(self.__name is None) and (not(other.__name is None))):
+            newName = DinamicString("_1-_2", [self.__name, other.__name]); 
+        
+        ## We check other simplifications: if the elements are constants
+        if(self.is_constant or other.is_constant):
+            result = None;
+            if(self.is_constant and other.is_constant):
+                parent = self.parent();
+                newOperator = [_sage_const_0 ,_sage_const_1 ];
+                newInit = [self(_sage_const_0 )-other(_sage_const_0 )];
+                result = parent.element(newOperator, newInit, check_init = False, name=newName);
+            elif(other.is_constant):
+                parent = self.parent();
+                newOperator = parent.element(self.equation, inhomogeneous=other(_sage_const_0 )*self.equation.getCoefficient(_sage_const_0 )).equation;
+                newInit = [self(_sage_const_0 )-other(_sage_const_0 )] + [self.getInitialValue(i) for i in range(_sage_const_1 ,newOperator.get_jp_fo()+_sage_const_1 )];
+                result = parent.element(newOperator, newInit, check_init = False, name=newName);
+                result.change_built("polynomial", (PolynomialRing(self.parent().base_field,'x1')("x1-%s" %other(_sage_const_0 )), {'x1':self}));
+            elif(self.is_constant):
+                parent = other.parent();
+                newOperator = parent.element(other.equation, inhomogeneous=self(_sage_const_0 )*other.equation.getCoefficient(_sage_const_0 )).equation;
+                newInit = [self(_sage_const_0 )-other(_sage_const_0 )] + [-other.getInitialValue(i) for i in range(_sage_const_1 ,newOperator.get_jp_fo()+_sage_const_1 )];
+                result = parent.element(newOperator, newInit, check_init = False, name=newName)
+                result.change_built("polynomial", (PolynomialRing(self.parent().base_field,'x1')("-x1+%s" %self(_sage_const_0 )), {'x1':other}));
+            return result;
+           
+        ## We build the new operator
+        if(self.equation == other.equation):
+            newOperator = self.equation;
+        else:
+            newOperator = self.equation.add_solution(other.equation);
+            
+        ### Getting the needed initial values for the solution
+        needed_initial = newOperator.get_jp_fo()+_sage_const_1 ;
+        
+        ### Getting as many initial values as posible until the new order
+        op1Init = self.getInitialValueList(needed_initial);
+        op2Init = other.getInitialValueList(needed_initial);
+        newInit = [op1Init[i] - op2Init[i] for i in range(min(len(op1Init),len(op2Init)))];
+                           
+        result = self.parent().element(newOperator, newInit, check_init=False, name=newName);
+        result.change_built("polynomial", (PolynomialRing(self.parent().base_field,['x1','x2'])('x1-x2'), {'x1':self, 'x2': other}));
+        return result;
+    
+    def mult(self, other):
+        '''
+            Method that calculate the product of two DDFunctions.
+            
+            INPUT:
+                - ``other``: a DDFunction which will be multiplied to `self`.
+                
+            WARNING:
+                - This method always returns a new object different from `self` or `other`.
+                - Whenever an error occurs, a NotImplemented error will be returned in order to Python can make the correspondent call to _rmult_ method of `other` if necessary.
+        '''
+        ### We check some simplifications: if one of the functions is zero, then we can return directly 0
+        if ((other.is_null) or (self.is_null)):
+            return _sage_const_0 ;
+        if(self.is_one):
+            return other;
+        elif(other.is_one):
+            return self;
+        elif(self.is_constant and other.is_constant):
+            return self.getInitialValue(_sage_const_0 )*other.getInitialValue(_sage_const_0 );
+        elif(self.is_constant):
+            return other.scalar(self.getInitialValue(_sage_const_0 ));
+        elif(other.is_constant):
+            return self.scalar(other.getInitialValue(_sage_const_0 ));
+            
+        ### We build the new operator
+        newOperator = self.equation.mult_solution(other.equation);
+        
+        ### Getting the needed initial values for the solution
+        needed_initial = newOperator.get_jp_fo()+_sage_const_1 ;
+               
+        ### Getting as many initial values as posible until the new order
+        op1Init = self.getInitialValueList(needed_initial);
+        op2Init = other.getInitialValueList(needed_initial);
+        newInit = [sum([binomial(i,j)*op1Init[j] * op2Init[i-j] for j in range(i+_sage_const_1 )]) for i in range(min(len(op1Init),len(op2Init)))];
+        
+        ### Computing the new name
+        newName = None;
+        if(not(self.__name is None) and (not(other.__name is None))):
+            newName = DinamicString("(_1)*(_2)", [self.__name, other.__name]); 
+            
+        result = self.parent().element(newOperator, newInit, check_init=False, name=newName);
+        result.change_built("polynomial", (PolynomialRing(self.parent().base_field,['x1','x2'])('x1*x2'), {'x1':self, 'x2': other}));
+        return result;
+    
+    def scalar(self, r):
+        '''
+            This method returns the original function multiplied by a constant, i.e. `D(k) = 0`. 
+            
+            INPUT:
+                - ``k``: scalar from the coefficient ring of `self`. It MUST be a constant.
+                
+            OUTPUT:
+                This method will raise TypeError if the value is not a constant. In order to do so, this method will cast the argument into the coefficient ring and try to derivate it. Just if the result is zero, the method will return a new DDFunction representing the function `k\cdot(self)`.
+                
+            INFO:
+                - This method must return always true for the following statements:
+                    - ``self.scalar(k) == self.mult(DDFunction.getConstantFunction(k))``.
+        '''
+        ### We first check if the scalar is zero, because the return is direct.
+        if(r == _sage_const_0 ):
+            return self.parent().zero();
+
+        ### Otherwise, we check that the element is a constant or not because the algorithm is 
+        ### much easier
+        r = self.parent().base()(r);
+        if(self.parent().base_derivation(r) == _sage_const_0 ):
+            if(r == _sage_const_1 ):
+                return self;
+            n = self.equation.get_jp_fo()+_sage_const_1 ;
+            coeffs = self.getOperator().getCoefficients();
+            init = self.getInitialValueList(n);
+            
+            if(isinstance(r, DDFunction)):
+                r = r.getInitialValue(_sage_const_0 );
+            
+            ### Computing the new name
+            newName = None;
+            if(not(self.__name is None)):
+                if(r == -_sage_const_1 ):
+                    newName = DinamicString("-(_1)" ,self.__name);
+                else:
+                    newName = DinamicString("(_1)*(_2)", [repr(r), self.__name]);
+                   
+            result = self.parent().element(self.equation, [r*el for el in init], check_init=False, name=newName);
+            result.change_built("polynomial", (PolynomialRing(self.parent().base_field,['x1'])('(%s)*x1' %repr(r)), {'x1':self}));
+            return result;       
+        else:
+            return self.mult(self.parent()(r));
+    
+    @derived_property        
+    def zero_extraction(self):
+        if(self == self.parent().zero()):
+            return (_sage_const_0 ,self);
+        else:
+            n = _sage_const_0 ;
+            while(self.getInitialValue(n) == _sage_const_0 ):
+                n = n+_sage_const_1 ;
+                
+            if(n == _sage_const_0 ):
+                return (_sage_const_0 ,self);
+            else:
+                d = self.getOrder();
+                coeffs = self.getOperator().getCoefficients();
+                newEquation = self.parent().element([sum([coeffs[j+l]*(binomial(j+l, j)*falling_factorial(n,l)*x**(n-l)) for l in range(min(n,d-j)+_sage_const_1 )]) for j in range(d+_sage_const_1 )], check_init = False).equation;
+            newInit = [factorial(i)*self.getSequenceElement(i+n) for i in range(newEquation.get_jp_fo()+_sage_const_1 )];
+            
+            ### Computing the new name
+            newName = None;
+            if(not(self.__name is None)):
+                newName = DinamicString("(_1)/(_2^%d)" %n, [self.__name, "x"]); 
+               
+            result = self.parent().element(newEquation, newInit, check_init=False, name=newName);
+            result.change_built("polynomial", (PolynomialRing(self.parent().base_field,['x','x1']).fraction_field()('x1/(x^%d)' %n), {'x':self.parent()(x),'x1':self}));
+            return (n,result);
+        
+    def divide(self, other):
+        if(self.is_null):
+            return self.parent().zero();
+        if(other.is_constant):
+            return self.scalar(_sage_const_1 /other.getInitialValue(_sage_const_0 ));
+            
+        s_ze = self.zero_extraction;
+        o_ze = other.zero_extraction;
+        
+        if(s_ze[_sage_const_0 ] >= o_ze[_sage_const_0 ]):
+            result = self.parent()(x)**(s_ze[_sage_const_0 ]-o_ze[_sage_const_0 ])*(s_ze[_sage_const_1 ]*o_ze[_sage_const_1 ].inverse);
+            
+            ### Computing the new name
+            newName=None;
+            if(not(self.__name is None) and (not(other.__name is None))):
+                newName = DinamicString("(_1)/(_2)", [self.__name, other.__name]);
+                
+            result.__name = newName;
+            return result;
+        else:
+            raise ValueError("Impossible perform a division if the x factor of the denominator (%d) is greater than in the numerator (%d)"%(o_ze[_sage_const_0 ],s_ze[_sage_const_0 ]));
+            
+    def gcd(self, other):
+        if(other in self.parent()):
+            other = self.parent()(other);
+        elif(self in other.parent()):
+            return other.gcd(self);
+        
+        if(self.is_null):
+            return other;
+        elif(other.is_null):
+            return self;
+        
+        se = self.zero_extraction;
+        oe = other.zero_extraction;
+        
+        return self.parent()(x**min(se[_sage_const_0 ],oe[_sage_const_0 ])*gcd(se[_sage_const_1 ].getInitialValue(_sage_const_0 ),oe[_sage_const_1 ].getInitialValue(_sage_const_0 )));
+    
+    #####################################
+    ### Differential methods
+    #####################################
+    def derivative(self, *args):
+        '''
+        Method to get a DDFunction `g` that satisfies `D(self) = g`.
+        
+        INPUT:
+            - ``args``: ignored input
+        '''
+        if(self.__derivative is None):
+            if(self.is_constant):
+                ### Special case: is a constant
+                self.__derivative = self.parent()(_sage_const_0 );
+            else:
+                ### We get the new operator
+                newOperator = self.equation.derivative_solution();
+            
+                ### We get the required initial values (depending of the order of the next derivative)
+                initList = self.getInitialValueList(newOperator.get_jp_fo()+_sage_const_2 );
+                newInit = [initList[i+_sage_const_1 ] for i in range(min(len(initList)-_sage_const_1 ,newOperator.get_jp_fo()+_sage_const_1 ))];
+                
+                ### Computing the new name
+                newName = None;
+                if(not(self.__name is None)):
+                    if(self.__name[-_sage_const_1 ] == "'"):
+                        newName = DinamicString("_1'", self.__name);
+                    else:
+                        newName = DinamicString("(_1)'", self.__name); 
+                
+                ### We create the next derivative with the equation, initial values
+                self.__derivative = self.parent().element(newOperator, newInit, check_init=False, name=newName);
+                self.__derivative.change_built("derivative",tuple([self]));
+                
+            
+        return self.__derivative;
+        
+    def integrate(self, constant=_sage_const_0 ):
+        '''
+        Method to get a DDFunction `g` that satisfies `D(g) = self` and `g(0) = constant`.
+        
+        INPUT:
+            - ``constant``: element which will define the initial value of the returned primitive.
+        '''
+        ### First we calculate the new linear differential operator
+        newOperator = self.equation.integral_solution();
+        
+        ### We get the initial values for the integral just adding at the begining of the list the constant value
+        # If not enough initial values can be given, we create the integral with so many initial values we have
+        newInit = [self.parent().base_field(constant)] + self.getInitialValueList(newOperator.get_jp_fo()+_sage_const_1 );
+        
+        ### Computing the new name
+        newName = None;
+        if(not(self.__name is None)):
+            if(constant == _sage_const_0 ):
+                newName = DinamicString("int(_1)", self.__name); 
+            else:
+                newName = DinamicString("int(_1) + _2", [self.__name, repr(constant)]);
+        
+        return self.parent().element(newOperator, newInit, check_init=False, name=newName);
+        
+    def compose(self, element):
+        '''
+        Method to get a DDFunction `g` that is the composition of `self` with `other`. It return the composition in the corresponding ring.
+        '''
+        ## Checking we fall in teh cases already implemented
+        if((not (element in self.parent().original_ring())) and (self.getOrder() > _sage_const_3 )):
+            raise NotImplementedError("Composition not yet implemented with elements not in %s for functions of order greater than 2 (got %d)" %(self.parent().original_ring(),self.getOrder()));
+        
+        ## If we have the basic function we return the same element
+        ## Also, if self is a constant, the composition is again the constant
+        self_var = self.parent().variables(True)[_sage_const_0 ];
+        if(self_var == element or self.is_constant):
+            return self;
+        
+        ## Otherwise, we check that the value at 0 is zero
+        init = element(**{str(self_var):_sage_const_0 });
+        if(init != _sage_const_0 ):
+            raise ValueError("Impossible to compose a power series with other that has non-zero initial value.\n\t- Get: %s" %init);
+        
+        ## We compute the differential operator case by case
+        newOperator = None;
+        destiny_ring = None;
+        if(not(element in self.parent().original_ring())):
+            ## Particular cases: we have a formula
+            ## First, we get the pushout of both parents
+            ## This will be a DDRing always
+            from sage.categories.pushout import pushout;
+            mix_ring = pushout(self.parent(), element.parent());
+            
+            ## We compute the depth in the DDRing structure of 'element'
+            element_depth = _sage_const_0 ;
+            if(isinstance(element.parent(),DDRing)):
+                element_depth = element.parent().depth();
+                
+            ## We build the destiny DDRing
+            destiny_ring = mix_ring.to_depth(self.parent().depth() + element_depth);
+            if(destiny_ring.depth() >= _sage_const_3 ):
+                warnings.warn("Reached a too high depth in the recursion (%d). This may not finish." %destiny_ring.depth(), TooDeepWarning, stacklevel=_sage_const_2 );
+            #element = destiny_ring.base()(element);
+            
+            if(element in destiny_ring.original_ring()):
+                return destiny_ring(self).compose(element);
+            elif(self.getOrder() == _sage_const_0 ):
+                raise ValueError("Impossible Error: Non-constant element of order zero!!");
+            elif(self.getOrder() == _sage_const_1 ):
+                a = self.equation.getCoefficient(_sage_const_1 )(x=element);
+                b = self.equation.getCoefficient(_sage_const_0 )(x=element);
+                dg = destiny_ring.base_derivation(element);
+                newOperator = destiny_ring.element([dg*b,a]).equation;
+            elif(self.getOrder() == _sage_const_2 ):
+                a = self.equation.getCoefficient(_sage_const_2 )(x=element);
+                b = self.equation.getCoefficient(_sage_const_1 )(x=element);
+                c = self.equation.getCoefficient(_sage_const_0 )(x=element);
+                dg = destiny_ring.base_derivation(element);
+                ddg = destiny_ring.base_derivation(dg);
+                newOperator = destiny_ring.element([dg**_sage_const_3 *c,dg**_sage_const_2 *b-ddg*a,dg*a]).equation;
+            elif(self.getOrder() == _sage_const_3 ):             
+                a = self.equation.getCoefficient(_sage_const_3 )(x=element);
+                b = self.equation.getCoefficient(_sage_const_2 )(x=element);
+                c = self.equation.getCoefficient(_sage_const_1 )(x=element);
+                d = self.equation.getCoefficient(_sage_const_0 )(x=element);
+                dg = destiny_ring.base_derivation(element);
+                ddg = destiny_ring.base_derivation(dg);
+                dddg = destiny_ring.base_derivation(ddg);
+                newOperator = destiny_ring.element([dg**_sage_const_5 *d, dg**_sage_const_4 *c-ddg*dg**_sage_const_2 *b-dg*dddg*a-_sage_const_3 *ddg**_sage_const_2 *a,dg**_sage_const_3 *b-_sage_const_3 *ddg*dg*a,dg**_sage_const_2 *a]).equation;
+        else:
+            newOperator = self.equation.compose_solution(element);
+            destiny_ring = self.parent();
+            
+        
+        ### We compute the initial values required
+        required = newOperator.get_jp_fo()+_sage_const_1 ;
+        
+        f = self.getInitialValueList(required);
+        try:
+            ## Case element is simpler than self
+            g = [factorial(i)*self.parent().getSequenceElement(element,i) for i in range(required)];
+        except TypeError:
+            ## Case element has higher complexity than self
+            g = [factorial(i)*element.parent().getSequenceElement(element,i) for i in range(required)];
+        
+        newInit = [f[_sage_const_0 ]]+[sum([f[j]*bell_polynomial(i,j)(*g[_sage_const_1 :i-j+_sage_const_2 ]) for j in range(_sage_const_1 ,i+_sage_const_1 )]) for i in range(_sage_const_1 ,required)]; ## See Faa di Bruno's formula
+        
+        ### Computing the new name
+        newName = None;
+        if(not(self.__name is None)):
+        #    if(isinstance(element, DDFunction) and not(element.__name is None)):
+        #        newName = self.__name.replace("exp", "####").replace("x", element.__name).replace("####", "exp"); 
+        #    else:
+        #        newName = self.__name.replace("exp", "####").replace("x", repr(element)).replace("####", "exp"); 
+            if(isinstance(element, DDFunction) and (not (element.__name is None))):
+                newName = din_m_replace(self.__name, {"x":element.__name}, True);
+            elif(not isinstance(element, DDFunction)):
+                newName = din_m_replace(self.__name, {"x":repr(element)}, True);
+        
+        return destiny_ring.element(newOperator, newInit, check_init=False, name=newName);
+        
+    #####################################
+    ### Equality methods
+    #####################################  
+    @derived_property  
+    def is_null(self): 
+        '''
+            Cached property to check whether self is zero in its ring or not.
+            
+            The method used is comparing the first initial values of the function and check they are zero. If some initial value can not be computed, then False is returned.
+        '''
+        return self.is_fully_defined and all(self.getInitialValue(i) == _sage_const_0  for i in range(self.equation.get_jp_fo()+_sage_const_1 ));
+        
+    @derived_property
+    def is_one(self):
+        '''
+            Cached property to check whether self is one on its ring or not.
+            
+            The metod used is checking that the element is a constant and its first initial value is one.
+        '''
+        return (self.is_constant and self.getInitialValue(_sage_const_0 ) == _sage_const_1 );
+        
+    @derived_property
+    def is_constant(self):
+        '''
+            Cached property to check whether self is a constant or not.
+            
+            We check enought initial values to know (without computing the derivative) if it is zero or not.
+        '''
+        ### OPTION 1
+        ## Test zero and if not, check if a constant can be solution
+        try:
+            if(not self.is_null):
+                return self.equation[_sage_const_0 ] == _sage_const_0  and all(self.getInitialValue(i) == _sage_const_0  for i in range(_sage_const_1 , self.equation.get_jp_fo()+_sage_const_1 ));
+            return True;
+        except TypeError:
+            return False;
+        ### OPTION 2 (nor proved)
+        ## Test only initial values for 'self'
+        #try:
+        #    return all(self.getInitialValue(i) == 0 for i in range(1,self.equation.get_jp_fo()+3));
+        #except TypeError:
+        #    return False;
+        ### OPTION 3 (too costly)
+        ## Test the derivative
+        #return self.derivative() == 0;
+        ### OPTION 4 (too costly)
+        ## Test the difference with the cosntant term
+        #return (self - self(0)) == 0
+    
+    @derived_property
+    def is_fully_defined(self):
+        '''
+            Cached property yo check whether the function is fully defined or not.
+            
+            This means that, given some initial values and the differential equation, the solution of such problem is unique (True) or not (False)
+        '''
+        max_init = self.equation.get_jp_fo()+_sage_const_1 ;
+        return len(self.getInitialValueList(max_init)) == max_init;
+        
+    def equals(self,other): ### TO REVIEW
+        '''
+            Method that checks if two DDFunctions are equal (as power-series). In orther to do so, we substract one to the other and check if the result is zero.
+            
+            INPUT:
+                - ``other``: a DDFunction that can be substracted of ``self``.
+                
+            OUTPUT:
+                - True if the object ``other`` represents the same function as ``self``.
+                - False otherwise.
+        '''
+        try:
+            if(not isinstance(other, DDFunction)):
+                other = self.parent()(other);
+            
+            ## Quick check of initial values
+            if(not self.quick_equals(other)):
+                return False;
+            
+            ## Special case: trying equality with zero
+            if(other.is_null):
+                return self.is_null;
+            elif(self.is_null):
+                return False;
+                
+            ## Special case with constants
+            if(other.is_constant):
+                return self.is_constant and self(_sage_const_0 ) == other(_sage_const_0 );
+            elif(self.is_constant):
+                return False;
+                
+            if(not (self.is_fully_defined and other.is_fully_defined)):
+                return (self.equation == other.equation) and (self.getInitialValueList(self.equation.get_jp_fo()+_sage_const_1 ) == other.getInitialValueList(other.equation.get_jp_fo()+_sage_const_1 ));
+             
+            ### THREE OPTIONS
+            ## 1. Compare with the JP-Values of each function
+            #m = self.equation.get_jp_fo()+other.equation.get_jp_fo()+1;
+            #if(not all(self.getInitialValue(i) == other.getInitialValue(i) for i in range(m))):
+            #    return False;
+            
+            ## 2. Substract and check zero equality
+            return (self-other).is_null;
+            
+            ## 3. Check adding orders and if they are equal, substract operators and check as many initial values as needed
+            #m = self.getOrder()+other.getOrder()+1;
+            #if(not all(self.getInitialValue(i) == other.getInitialValue(i) for i in range(m))):
+            #    return False;
+            #   
+            #M = self.equation.add_solution(other.equation).get_jp_fo()+1;
+            #return all(self.getInitialValue(i) == other.getInitialValue(i) for i in range(m,M));
+        except Exception:
+            ### If an error occur, then other can not be substracted from self, so they can not be equals
+            return False;
+            
+    def numerical_solution(self, value, delta=_sage_const_1en10 , max_depth=_sage_const_100 ):
+        try:
+            ## We try to delegate the computation to the operator (in case of OreOperators)
+            if(self.equation.getCoefficients()[-_sage_const_1 ](_sage_const_0 ) != _sage_const_0 ):
+                sol = self.equation.operator.numerical_solution(self.getSequenceList(self.getOrder()), [_sage_const_0 ,value],delta);
+                return sol.center(), sol.diameter();
+            else:
+                return self.__basic_numerical_solution(value, delta, max_depth);
+        except AttributeError:
+            ## The operator has no numerical solution method (i.e. it is not an OreOperator
+            ## We compute the basic numerical approximation
+            return self.__basic_numerical_solution(value, delta, max_depth);            
+            
+    def __basic_numerical_solution(self, value, delta,max_depth):
+        res = _sage_const_0 ;
+        to_mult = _sage_const_1 ;
+        to_sum = _sage_const_0 ;
+        step = _sage_const_0 ;
+        find = _sage_const_0 ;
+        while(find < _sage_const_5  and step < max_depth):
+            to_sum = self.getSequenceElement(step)*to_mult;
+            res += to_sum;
+            if(self.getSequenceElement(step) != _sage_const_0  and abs(to_sum) < delta):
+                find += _sage_const_1 ;
+            elif(self.getSequenceElement(step) != _sage_const_0 ):
+                find = _sage_const_0 ;
+            
+            step += _sage_const_1 ;
+            to_mult *= value;
+        return float(res),float(abs(to_sum));
+        
+    def to_symbolic(self):
+        evaluation = sage_eval(str(self.__name).replace("'", ".derivative()").replace("^", "**"), locals=globals());
+        if(isinstance(evaluation, sage.symbolic.expression.Expression)):
+            evaluation = evaluation.simplify_full();
+            
+        return evaluation;
+            
+    def quick_equals(self,other): ### TO REVIEW
+        '''
+            Method that checks if two DDFunctions are equal (as power-series). In orther to do so, we substract one to the other and check if the result is zero.
+            
+            INPUT:
+                - ``other``: a DDFunction that can be substracted of ``self``.
+                
+            OUTPUT:
+                - True if the object ``other`` could represent the same function as ``self``.
+                - False if we are sure ``self`` is different from ``other``.
+        '''
+        try:
+            if(not isinstance(other, DDFunction)):
+                other = self.parent()(other);
+                
+            m = self.equation.get_jp_fo()+other.equation.get_jp_fo()+_sage_const_1 ;
+            return self.getInitialValueList(m) == other.getInitialValueList(m);
+        except Exception:
+            return False
+        
+    #####################################
+    ### Magic Python methods
+    #####################################
+    ### Magic arithmetic
+    ## Special case for Simbolic Ring
+    def __check_symbolic(self, other):
+        try:
+            if(other.parent() is SR):
+                try:
+                    return self.parent()(other);
+                except:
+                    return ParametrizedDDRing(self.parent(),other.variables())(other);
+        except Exception as e:
+            pass;
+        return other;
+    
+    def __add__(self, other):
+        return super(DDFunction,self).__add__(self.__check_symbolic(other));
+        
+    def __sub__(self, other):
+        return super(DDFunction,self).__sub__(self.__check_symbolic(other));
+        
+    def __mul__(self, other):
+        return super(DDFunction,self).__mul__(self.__check_symbolic(other));
+        
+    def __div__(self, other):
+        return super(DDFunction,self).__div__(self.__check_symbolic(other));
+        
+    def __radd__(self, other):
+        return super(DDFunction,self).__add__(self.__check_symbolic(other));
+        
+    def __rsub__(self, other):
+        return super(DDFunction,self).__sub__(self.__check_symbolic(other));
+        
+    def __rmul__(self, other):
+        return super(DDFunction,self).__mul__(self.__check_symbolic(other));
+        
+    def __rdiv__(self, other):
+        return super(DDFunction,self).__div__(self.__check_symbolic(other));
+        
+    def __iadd__(self, other):
+        return super(DDFunction,self).__add__(self.__check_symbolic(other));
+        
+    def __isub__(self, other):
+        return super(DDFunction,self).__sub__(self.__check_symbolic(other));
+        
+    def __imul__(self, other):
+        return super(DDFunction,self).__mul__(self.__check_symbolic(other));
+        
+    def __idiv__(self, other):
+        return super(DDFunction,self).__div__(self.__check_symbolic(other));
+    
+    
+    ## Addition
+    
+    def _add_(self, other):
+        try:
+            return self.add(other);
+        except Exception:
+            return NotImplemented;
+            
+    ## Substraction
+    def _sub_(self, other):
+        try:
+            return self.sub(other);
+        except Exception:
+            return NotImplemented;
+            
+    ## Multiplication
+    def _mul_(self, other):
+        try:
+            if(not isinstance(other, DDFunction)):
+                return self.scalar(other);
+                
+            return self.mult(other);
+        except Exception:
+            return NotImplemented;
+            
+    def _div_(self, other):
+        try:
+            return self.divide(other);
+        except ValueError:
+            return NotImplemented;
+  
+    # Integer powering
+    def __pow__(self, other):
+        try:
+            return self.__pows[other];
+        except KeyError:
+            f = self;
+            if(f.is_null): ## Trivial case when order is 0
+                self.__pows[other] = f;
+            elif(other in ZZ): ## Trying integer power
+                other = int(other);
+                if(other >= _sage_const_0 ):
+                    a = other >> _sage_const_1 ;
+                    b = a+(other&_sage_const_1 );
+                    self.__pows[other] = self.__pow__(a)*self.__pow__(b);
+                    newName = None;
+                    if(not(self.__name is None)):
+                        newName = DinamicString("(_1)^%d" %(other), self.__name);
+                    self.__pows[other].__name = newName;
+                else:
+                    try:
+                        inverse = self.inverse;
+                    except Exception:
+                        raise ZeroDivisionError("Impossible to compute the inverse");
+                    return inverse.__pow__(-other);
+            else: ## Trying a power on the basic field
+                try:
+                    newDDRing = DDRing(self.parent());
+                    other = self.parent().base_ring()(other);
+                    self.__pows[other] = newDDRing.element([(-other)*f.derivative(),f], [el**other for el in f.getInitialValueList(_sage_const_1 )], check_init=False);
+                    
+                    newName = None;
+                    if(not(self.__name is None)):
+                        newName = DinamicString("(_1)^%s" %(other), self.__name);
+                    self.__pows[other].__name = newName;
+                except TypeError:
+                    raise TypeError("Impossible to compute (%s)^(%s) within the basic field %s" %(f.getInitialValue(_sage_const_0 ), other, f.parent().base_ring()));
+                except ValueError:
+                    raise NotImplementedError("Powering to an element of %s not implemented" %(other.parent()));
+        return self.__pows[other];
+        
+            
+    ### Magic equality
+    def __eq__(self,other):
+        if (other is self):
+            return True;
+        if((type(other) == sage.rings.integer.Integer or type(other) == int) and other == _sage_const_0 ):
+            return self.is_null;
+        return self.equals(other);
+        
+    ### Magic use
+    def __call__(self, X=None, **input):
+        '''
+            Method that return the value of the function in the point given. As the function as a power-serie may not converge, this method only works if the argument is 0.
+            Further implementation can be done for DDFunctions that we know about the convergence.
+        '''
+        if ((not(X is None)) and X == _sage_const_0 ):
+            return self.getInitialValue(_sage_const_0 );
+        
+        return self.parent().eval(self, X, **input);
+        
+    ### Magic representation
+    def __hash__(self):
+        '''
+            Hash method for DDFunctions.
+        '''
+        return int("%d%d%d" %(self.getOrder(),self.equation.get_jp_fo(),self.size()));
+        #return sum([hash(coeff) for coeff in self.getOperator().getCoefficients()]);
+
+    def __getitem__(self, key):
+        '''
+            GetItem method for DDFunctions. It allows the user do ``self[i]`` for `i \geq -1`.
+            
+            INPUT:
+                - ``key``: an integer.
+            
+            RETURN:
+                - The result is the coefficient of `D^{key}(f)` on the equation.
+        '''
+        return self.getOperator().getCoefficient(key);
+
+    def __missing__(self, key):
+        '''
+            Missing method for DDFuntions.
+        '''
+        return _sage_const_0 ;
+        
+    def __str__(self, detail=True):
+        '''
+            String method for DDFunctions. It prints all information of the DDFunction.
+        '''
+        #if(self.is_constant):
+        #    return (self.getInitialValue(0)).__str__();
+            
+        if(detail and (not(self.__name is None))):
+            res = "%s %s in %s:\n" %(self.__critical_numbers__(),repr(self),self.parent());
+        else:
+            res = "%s" %(repr(self));
+            if(res[_sage_const_0 ] != '('):
+                res += " in %s" %(self.parent());
+            res += ":\n";
+        res += '-------------------------------------------------\n\t';
+        res += '-- Equation (self = f):\n'
+        j = _sage_const_0 ;
+        while ((j < self.getOrder()) and (self.getOperator().getCoefficient(j) == _sage_const_0 )):
+            j += _sage_const_1 ;
+        res += self.__get_line__(self.getOperator().getCoefficient(j), j, detail, True);
+        for i in range(j+_sage_const_1 ,self.getOrder()+_sage_const_1 ):
+            if(self.getOperator().getCoefficient(i) != _sage_const_0 ):
+                res += '\n';
+                res += self.__get_line__(self.getOperator().getCoefficient(i), i, detail);
+
+        res += '\n\t\t = 0 \n\t';
+
+        res += '-- Initial values:\n\t'
+        res += format(self.getInitialValueList(self.equation.get_jp_fo()+_sage_const_1 ));
+        
+        res += '\n-------------------------------------------------';
+        
+        return res;
+        
+    def __get_line__(self, element, der, detail, first = False):
+        res = "";
+        string = repr(element);
+        crit = None;
+        
+        ## Getting the basic elements for the line
+        if(isinstance(element, DDFunction) and (not(element.__name is None))):
+            crit = element.__critical_numbers__();
+        else:
+            ind = string.find(")")+_sage_const_1 ;
+            crit = string[:ind];
+            string = string[ind:];
+            
+        ## Printing critical numbers if detail is required
+        if(detail and len(crit) > _sage_const_0 ):
+            res += crit + "\t";
+            if(len(res.expandtabs()) < len("\t\t".expandtabs())):
+                res += "\t";
+        else:
+            res += "\t\t";
+        
+        ## Adding the arithmetic symbol
+        if(not(first) and string[_sage_const_0 ] != '-'):
+            res += '+ ';
+        elif(string[_sage_const_0 ] == '-'):
+            res += '- ';
+            if(string[_sage_const_1 ] == '('):
+                string = string[_sage_const_1 :-_sage_const_1 ];
+            else:
+                string = string[_sage_const_1 :];
+        else:
+            res += '  ';
+            
+        ## Adding the function (with its derivative if needed)
+        if(der > _sage_const_1 ):
+            res += "D^%d(f) " %der;
+        elif(der == _sage_const_1 ):
+            res += "  D(f) ";
+        else:
+            res += "    f  ";
+        
+        ## Adding the coefficient
+        if(string != "1"):
+            res += "* (%s)" %string;
+        
+        return res;
+        
+    def __repr__(self):
+        '''
+            Representation method for DDFunctions. It prints basic information of the DDFunction.
+        '''
+        if(self.is_constant):
+            return str(self.getInitialValue(_sage_const_0 ));
+        if(self.__name is None):
+            return "(%s:%s:%s)DD-Function in (%s)" %(self.getOrder(),self.equation.get_jp_fo(),self.size(),self.parent());   
+        else:
+            return str(self.__name);
+            
+    def __critical_numbers__(self):
+        return "(%d:%d:%d)" %(self.getOrder(),self.equation.get_jp_fo(),self.size());
+    
+    def _to_command_(self):
+        if(self.__name is None):
+            return "%s.element(%s,%s)" %(command(self.parent()), _command_list(self.getOperator().getCoefficients()), self.getInitialValueList(self.getOrder()));
+        else:
+            return "%s.element(%s,%s,name='%s')" %(command(self.parent()), _command_list(self.getOperator().getCoefficients()), self.getInitialValueList(self.getOrder()),str(self.__name));
+        
+    #####################################       
+    ### Other methods
+    #####################################    
+    def __nonzero__(self):
+        return not (self.is_null);
+        
+#####################################################
+### Construction Functor for DD-Ring
+#####################################################
+class DDRingFunctor (ConstructionFunctor):
+    def __init__(self, depth, base_field):
+        self.rank = _sage_const_11 ;
+        self.__depth = depth;
+        self.__base_field = base_field;
+        super(DDRingFunctor, self).__init__(IntegralDomains(),IntegralDomains());
+        
+    ### Methods to implement
+    def _coerce_into_domain(self, x):
+        if(x not in self.domain()):
+            raise TypeError("The object [%s] is not an element of [%s]" %(x, self.domain()));
+        if(isinstance(x, DDRing)):
+            return x.base();
+        return x;
+        
+    def _apply_functor(self, x):
+        return DDRing(x,self.__depth,self.__base_field);
+        
+    def _repr_(self):
+        return "DDRing(*,%d,%s)" %(self.__depth, self.__base_field);
+        
+    def depth(self):
+        return self.__depth;
+        
+class ParametrizedDDRingFunctor (DDRingFunctor):
+    def __init__(self, depth, base_field, var = {}):
+        self.rank = _sage_const_12 ;
+        self.__vars = var;
+        super(ParametrizedDDRingFunctor, self).__init__(depth, base_field);
+        
+    ### Methods to implement
+    def _coerce_into_domain(self, x):
+        if(x not in self.domain()):
+            raise TypeError("The object [%s] is not an element of [%s]" %(x, self.domain()));
+        return x;
+        
+    def _apply_functor(self, x):
+        return ParametrizedDDRing(x, self.__vars);
+        
+    def merge(self, other):
+        if(isinstance(other, ParametrizedDDRingFunctor)):
+            return ParametrizedDDRingFunctor(self.__vars.union(other.__vars));
+        return None;
+        
+    def __repr__(self):
+        return super(ParametrizedDDRingFunctor, self).__repr__() + " - " + repr(self.__vars);
+        
+#####################################################
+### General Morphism for return to basic rings
+#####################################################
+class DDSimpleMorphism (sage.categories.map.Map):
+    def __init__(self, domain, codomain):
+        super(DDSimpleMorphism, self).__init__(domain, codomain);
+        
+    def _call_(self, p):
+        if(p.is_constant):
+            return self.codomain()(p.getInitialValue(_sage_const_0 ));
+        
+        raise ValueError("Impossible perform this coercion: non-constant element");
+
+        
+        
+#####################################       
+### Changes to fit the SAGE Categories Framework
+#####################################
+DDRing.Element = DDFunction;
+
+def zero_extraction(el):
+    try:
+        R = el.parent();
+        if(isinstance(R, DDRing)):
+            return el.zero_extraction;
+        elif(x in R.gens()):
+            n = _sage_const_0 ;
+            val = el(**{'x':_sage_const_0 });
+            while(el != R.zero() and val == _sage_const_0 ):
+                n += _sage_const_1 ;
+                el = R(el/x);
+                val = el(**{'x':_sage_const_0 });
+                
+            return (n, el);
+    except AttributeError as e:
+        return (_sage_const_0 ,el);
+
+###################################################################################################
+### PRIVAME MODULE METHODS
+###################################################################################################
+def _indices(string, sep):
+    try:
+        index = string.index(sep);
+        return [index] + [el+index+len(sep) for el in _indices(string[index+len(sep):],sep)];
+    except ValueError:
+        return [];
+
+def _m_indices(string, *seps):
+    ## We assume no possible overlapping can occur between elements in seps
+    all_index = [];
+    for sep in seps:
+        all_index += [(el,sep) for el in _indices(string,sep)];
+    all_index.sort();
+    
+    return all_index;
+
+def _m_replace(string, to_replace, escape=("(",")")):
+    ## Checking if ther are scape characters
+    if(not escape is None):
+        pos_init = string.find(escape[_sage_const_0 ]);
+        if(pos_init >= _sage_const_0 ):
+            ## Escape initial character found, skipping outside the escape
+            pos_end = len(string);
+            pos_end = string.rfind(escape[_sage_const_1 ]);
+            if(pos_end <= pos_init):
+                pos_end = len(string);
+                
+            return string[:pos_init+_sage_const_1 ] + _m_replace(string[pos_init+_sage_const_1 :pos_end], to_replace, None) + string[pos_end:];
+    
+    ## No escape characters: doing normal computation
+    all_index = _m_indices(string, *to_replace.keys());
+    res = ""; current_index = _sage_const_0 ;
+    for el in all_index:
+        res += string[current_index:el[_sage_const_0 ]] + to_replace[el[_sage_const_1 ]];
+        current_index = el[_sage_const_0 ]+len(el[_sage_const_1 ]);
+    res += string[current_index:]
+    return res;
+
+###################################################################################################
+### COMMAND CONVERSION OF DD_FUNCTIONS
+###################################################################################################
+def command(e):
+    try:
+        return e._to_command_();
+    except AttributeError:
+        from sage.rings.polynomial import polynomial_ring as Uni_Polynomial;
+        if(e in IntegralDomains()):
+            if(e is QQ):
+                return "QQ";
+            if(Uni_Polynomial.is_PolynomialRing(e)):
+                return "PolynomialRing(%s,%s)" %(command(e.base()), [str(var) for var in e.gens()]);
+        return str(e);
+        
+def _command_list(elements):
+    res = "[";
+    for i in range(len(elements)-_sage_const_1 ):
+        res += command(elements[i]) + ", ";
+    if(len(elements) > _sage_const_0 ):
+        res += command(elements[-_sage_const_1 ]);
+    res += "]";
+    
+    return res;
+    
+###################################################################################################
+### STANDARD PACKAGES VARIABLES & GETTERS
+###################################################################################################
+
+# Some global pre-defined DD-Rings
+DFinite = None;
+try:
+    DFinite = DDRing(PolynomialRing(QQ,x), default_operator=w_OreOperator);
+except ImportError:
+    ## No ore_algebra package found
+    warnings.warn("Package ore_algebra was not found. It is hightly recommended to get this package built by M. Kauers and M. Mezzaroba (http://marc.mezzarobba.net/code/ore_algebra-analytic/ or http://www.kauers.de/software.html)", NoOreAlgebraWarning);
+    DFinite = DDRing(PolynomialRing(QQ,x));
+DDFinite = DDRing(DFinite);
+#CFinite = DDRing(QQ, default_operator=w_OreOperator);
+DFiniteP = ParametrizedDDRing(DFinite, [var('P')]);
+
+def __get_recurrence(f):
+    if(not f in DFinite):
+        raise TypeError("The function must be holonomic");
+    
+    f = DFinite(f);
+    
+    op = f.equation;
+    m = max([el.degree() for el in op.getCoefficients()]);
+    bound = op.getOrder() + m;
+    
+    num_of_bck = bound - op.getOrder();
+    m = None;
+    for i in range(num_of_bck, _sage_const_0 , -_sage_const_1 ):
+        if(op.backward(i) != _sage_const_0 ):
+            m = -i;
+            break;
+    
+    if(m is None):
+        for i in range(op.getOrder()+_sage_const_1 ):
+            if(op.forward(i) != _sage_const_0 ):
+                m = i;
+                break;
+    
+    n = op._Operator__polynomialRing.gens()[_sage_const_0 ];
+    
+    rec = [op.get_recursion_polynomial(i)(n=n+m) for i in range(m,op.forward_order + _sage_const_1 )];
+    rec_gcd = gcd(rec);
+    rec = [el/rec_gcd for el in rec];
+    
+    return (rec, f.getSequenceList(op.forward_order-m));
+DFinite.get_recurrence = __get_recurrence;
+
+###################################################################################################
+### PACKAGE ENVIRONMENT VARIABLES
+###################################################################################################
+__all__ = ["DDRing", "DFinite", "DDFinite", "command", "zero_extraction" , "ParametrizedDDRing", "DFiniteP"];
+  
+
diff --git a/ajpastor/dd_functions/symbolic.py b/ajpastor/dd_functions/symbolic.py
new file mode 100644 (file)
index 0000000..8e7e773
--- /dev/null
@@ -0,0 +1,153 @@
+
+# This file was *autogenerated* from the file ./symbolic.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+
+from .ddFunction import *;
+from .ddFunction import DDFunction;
+from .ddExamples import *;
+
+from ajpastor.misc.verbose import *;
+
+###################################################################################################
+### SYMBOLIC CONVERSION OF DD_FUNCTIONS
+###################################################################################################
+def symbolic(function):
+    if(isinstance(function, DDFunction)):
+        try:
+            return function.to_symbolic();
+        except Exception as e:
+            return e;
+    return None;
+    
+#@wverbose
+def from_symbolic(exp, dR = DFinite):
+    exp = SR(exp);
+    ## Base case: The expression is a polynomial
+    try:
+        polynomial = dR.original_ring()(str(exp));
+        #sverbose("A polynomial found");
+        return polynomial;
+    except Exception:
+        pass;
+    
+    ## Other cases: We choose between operands
+    try:
+        import types;
+        operator = exp.operator();
+        if(operator is None):
+            raise ValueError("Impossible to cast: reach bottom without polynomial");
+        
+        ## Getting the name
+        name = None;
+        try:
+            name = operator.__name__;
+        except AttributeError:
+            name = operator.name();
+            
+        #sverbose("Operator name: %s" %name);
+            
+        ## Converting the operands
+        operands = [from_symbolic(el, dR) for el in exp.operands()];
+                
+        ## Choosing the operation
+        ### Python types
+        if(name == "list"):
+            #sverbose("Returning list");
+            return operands;
+        elif(name == "tuple"):
+            #sverbose("Returning tuple");
+            return tuple(operands);
+            #sverbose("Returning set");
+        elif(name == "set"):
+            return set(operands);
+        ### Arithmetic
+        elif(name == "add_vararg"):
+            #sverbose("Returning sum");
+            return sum(operands);
+        elif(name == "mul_vararg"):
+            #sverbose("Returning mult");
+            for el in operands:
+                print el
+            return prod(operands);
+        elif(name == "pow"):
+            try:
+                exponent = int(operands[_sage_const_1 ]);
+                #if(exponent < 0 and (not(isinstance(operands[0], DDFunction)))):
+                #    operands[0] = dR(operands[0]);
+            except ValueError:
+                pass;
+            #sverbose("Returning pow");
+            return pow(operands[_sage_const_0 ],operands[_sage_const_1 ]);
+        ### Trigonometric
+        elif(name == "sin"):
+            #sverbose("Returning sin");
+            #sverbose("%s" %str(operands[0]));
+            return Sin(operands[_sage_const_0 ]);
+        elif(name == "cos"):
+            #sverbose("Returning cos");
+            return Cos(operands[_sage_const_0 ]);
+        elif(name == "sinh"):
+            #sverbose("Returning sinh");
+            return Sinh(operands[_sage_const_0 ]);
+        elif(name == "cosh"):
+            #sverbose("Returning cosh");
+            return Cosh(operands[_sage_const_0 ]);
+        elif(name == "tan"):
+            #sverbose("Returning tan");
+            return Tan(operands[_sage_const_0 ]);
+        ### Logarithmic and exponential
+        elif(name == "log"):
+            #sverbose("Returning log");
+            return Log(operands[_sage_const_0 ]);
+        elif(name == "exp"):
+            #sverbose("Returning exp");
+            return Exp(operands[_sage_const_0 ]);
+        ### Special functions
+        elif(name == "bessel_J"):
+            return BesselD(operands[_sage_const_0 ])(operands[_sage_const_1 ]);
+        elif(name == "legendre_P"):
+            return LegendreD(operands[_sage_const_0 ])(operands[_sage_const_1 ]);
+        elif(name == "chebyshev_T"):
+            return ChebyshevD(operands[_sage_const_0 ],_sage_const_1 )(operands[_sage_const_1 ]);
+        elif(name == "chebyshev_U"):
+            return ChebyshevD(operands[_sage_const_1 ],_sage_const_2 )(operands[_sage_const_1 ]);
+        ### Hypergeometric functions
+        elif(name == "hypergeometric"):
+            return GenericHypergeometricFunction(operands[_sage_const_0 ],operands[_sage_const_1 ])(operands[_sage_const_2 ]);
+        
+        raise TypeError("No valid operator found");
+    except AttributeError:
+        return None; # Impossible to get the name of the operator
+        
+def symbolic_simplify(function):
+    '''
+    An inplace simplification using the symbolic SAGE expressions
+    '''
+    ## If the input is not a DDFunction, we just return it
+    if(isinstance(function, DDFunction)):
+        ## First, we check the function itself
+        symbolic = from_symbolic(function.to_symbolic(), function.parent());
+        if(not isinstance(symbolic, DDFunction)):
+            symbolic = function.parent()(symbolic);
+            
+        if(symbolic.size() < function.size()):
+            function.equation = symbolic.equation;
+        if(len(repr(symbolic)) < len(repr(function))):
+            function._DDFunction__name = repr(symbolic);
+            
+        ## Now we check the coefficients of the equation
+        symbolic_simplify_coefficients(function);
+    return function;
+    
+def symbolic_simplify_coefficients(function):
+    if(isinstance(function, DDFunction)):
+        for i in range(function.getOrder()+_sage_const_1 ):
+            symbolic_simplify(function.equation.getCoefficient(i));
+        
+###################################################################################################
+### PACKAGE ENVIRONMENT VARIABLES
+###################################################################################################
+__all__ = ["symbolic", "from_symbolic", "symbolic_simplify", "symbolic_simplify_coefficients"];
+
diff --git a/ajpastor/lazy/__init__.py b/ajpastor/lazy/__init__.py
new file mode 100644 (file)
index 0000000..e689411
--- /dev/null
@@ -0,0 +1,4 @@
+from lazyToPoly import LazyToPoly;
+
+from pkgutil import extend_path;
+__path__ = extend_path(__path__, __name__);
diff --git a/ajpastor/lazy/conversion.py b/ajpastor/lazy/conversion.py
new file mode 100644 (file)
index 0000000..80c3ef8
--- /dev/null
@@ -0,0 +1,321 @@
+
+# This file was *autogenerated* from the file ./conversion.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+from sage.rings.polynomial.polynomial_ring import is_PolynomialRing as isUniPolynomial;
+from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing as isMPolynomial;
+
+class ConversionSystem(object):
+    ## Main bulder
+    def __init__(self, base):
+        '''
+            Builder for a Conversion system.
+            
+            Take the following input:
+                - `base`: a ring (checked or raise a TypeError)
+        '''
+        
+        ## Starting the method
+        if(not base in IntegralDomains()):
+            raise TypeError("Imposible to build a conversion systom from something that is not a Ring");
+    
+        ## Initializing variables
+        self.__base = base;
+        
+        self.__relations = None;
+        self.__rel_ideal = None;
+            
+    ## Public getters
+    def base(self):
+        return self.__base;
+        
+    def is_polynomial(self):
+        '''
+            Returns a Boolean value that show if there are variables in this conversion system.
+        '''
+        return (isUniPolynomial(self.poly_ring()) or isMPolynomial(self.poly_ring()));
+        
+    def poly_ring(self):
+        '''
+            Returns the polynomial ring where the conversion system works.
+        '''
+        raise NotImplementedError("Abstract method not implemented 'poly_ring()'");
+        
+    def poly_field(self):
+        '''
+            Returns the polynomial fraction field where the conversion system works.
+        '''
+        raise NotImplementedError("Abstract method not implemented 'poly_field()'");
+                
+    def map_of_vars(self):
+        '''
+            Returns a Python dictionary which maps the variables of `self.poly_ring()` to the real elements of `self.base()`
+        '''
+        raise NotImplementedError("Abstract method not implemented 'map_of_vars()'");
+        
+    ## Pulbic methods
+    def add_relations(self, *relations):
+        if(self.is_polynomial()):
+            try:
+                ## Adding the new relations
+                self._relations(); # We make sure the relations are initialized
+                    
+                self.__add_relation(relations);
+                    
+                ## Changing the ideal and computing a groebner basis
+                if(len(self.__relations) >= _sage_const_1 ):
+                    self.__rel_ideal = ideal(self.poly_ring(), self.__relations);
+                    self.__relations = self.__rel_ideal.groebner_basis();
+            except TypeError as e:
+                raise e;
+    
+    def clean_relations(self):
+        self.__relations = [];
+                
+    def to_poly(self, element):
+        '''
+            This method cast an element in `self.base()` or `self.base().fraction_field()` to a polynomial in the conversion system. This method can receive different types on elments. 
+            
+            The types allowed for the argument poly are:
+                - Elements in `self.base()`
+                - Elements in `self.base().fraction_field()`
+                - List, Sets or Tuples
+                - Matrices with polynomials recognized
+                - Vectors with polynomials recognized
+                
+            Returns an element in `self.poly_ring()` or `self.poly_field()`.
+            
+            If the type is not valid, a TypeError exception will be risen. 
+            If it is not possible to cast the element, a ValueError exception will be risen.
+        '''
+        if(element in self.base()):
+            return self._to_poly_element(element);
+        elif(element in self.base().fraction_field()):
+            n = self.to_poly(element.numerator());
+            d = self.to_poly(element.denominator());
+            return self.poly_field()(n/d);
+        elif(isinstance(element, sage.matrix.matrix.Matrix)):
+            R = self.poly_ring();
+            if(element.parent().base().is_field()):
+                R = self.poly_field();
+            return Matrix(R, [self.to_poly(row) for row in element]);
+        elif(isinstance(element, sage.modules.free_module_element.FreeModuleElement)):
+            R = self.poly_ring();
+            if(element.parent().base().is_field()):
+                R = self.poly_field();
+            return vector(R, [self.to_poly(el) for el in element]);
+        elif(isinstance(element, list)):
+            return [self.to_poly(el) for el in element];
+        elif(isinstance(element, set)):
+            return set([self.to_poly(el) for el in element]);
+        elif(isinstance(relation, tuple)):
+            return tuple([self.to_poly(el) for el in element]);
+        else:
+            raise TypeError("Wrong type: Impossible to get polynomial value of element (%s)" %(element));
+        
+    def to_real(self, poly):
+        '''
+            This method cast a polynomial recognized in the conversion system to a real element in `self.base()`. This method can receive different types on elments. 
+            
+            The types allowed for the argument poly are:
+                - Elements in `self.poly_ring()`
+                - Elements in `self.poly_field()`
+                - List, Sets or Tuples
+                - Matrices with polynomials recognized
+                - Vectors with polynomials recognized
+                
+            Returns an element in `self.base()` or `self.base().fraction_field()`.
+            
+            If the type is not valid, a TypeError exception will be risen.
+        '''
+        if(poly in self.poly_ring()):
+            if(not self.is_polynomial()):
+                return self.base()(poly);
+            return self._to_real_element(self.poly_ring()(poly));
+        elif(poly in self.poly_field()):
+            n = self.to_real(poly.numerator());
+            d = self.to_real(poly.denominator());
+            return n/d;
+        elif(isinstance(poly, sage.matrix.matrix.Matrix)):
+            R = self.base();
+            if(poly.parent().base().is_field()):
+                R = R.fraction_field();
+            return Matrix(R, [self.to_real(row) for row in poly]);
+        elif(isinstance(poly, sage.modules.free_module_element.FreeModuleElement)):
+            R = self.base();
+            if(poly.parent().base().is_field()):
+                R = R.fraction_field();
+            return vector(R, [self.to_real(el) for el in poly]);
+        elif(isinstance(poly, list)):
+            return [self.to_real(el) for el in poly];
+        elif(isinstance(poly, set)):
+            return set([self.to_real(el) for el in poly]);
+        elif(isinstance(poly, tuple)):
+            return tuple([self.to_real(el) for el in poly]);
+        else:
+            raise TypeError("Wrong type: Impossible to get real value of element (%s)" %(poly));
+            
+    def simplify(self, element):
+        '''
+            Simplify the element receive using the relations known for the Conversion System.
+            
+            Several types of input are allowed:
+                - An element of `self.poly_ring()` or `self.poly_field()`.
+                - A List, a Set or a Tuple
+                - Matrices or vectors with polynomials rocignized by the Conversion system
+                - Elements in `self.base()`
+        '''
+        if(element in self.poly_ring()):
+            try:
+                return self.poly_ring()(element).reduce(self._relations());
+            except AttributeError:
+                return self.poly_ring()(element);
+        elif(element in self.poly_field()):
+            element = self.poly_field()(element);
+            n = self.simplify(element.numerator());
+            d = self.simplify(element.denominator());
+            return n/d;
+        elif(isinstance(element, list)):
+            return [self.simplify(el) for el in element];
+        elif(isinstance(element, set)):
+            return set([self.simplify(el) for el in element]);
+        elif(isinstance(element, tuple)):
+            return tuple([self.simplify(el) for el in element]);
+        elif(isinstance(element, sage.matrix.matrix.Matrix)):
+            R = self.poly_ring();
+            if(element.parent().base().is_field()):
+                R = self.poly_field();
+            return Matrix(R, [[self.simplify(el) for el in row] for row in element]);
+        elif(isinstance(element, sage.modules.free_module_element.FreeModuleElement)):
+            R = self.poly_ring();
+            if(element.parent().base().is_field()):
+                R = self.poly_field();
+            return vector(R, [self.simplify(el) for el in element]);
+        elif(element in self.base()):
+            return self.to_real(self.simplify(self.to_poly(element)));
+        else:
+            return element;
+            
+    def mix_conversion(self, conversion):
+        '''
+            Method that mixes two conversion system of the same class. It is also allowed to mix `self` with an element of `self.base().fraction_field()`
+        '''
+        if(isinstance(conversion, self.__class__) or (conversion in self.base().fraction_field())):
+            return self._mix_conversion(conversion);
+        elif(isinstance(conversion, ConversionSystem) and isinstance(self, conversion.__class__)):
+            return conversion._mix_conversion(self);
+                
+    ## Protected methods
+    def _change_poly_ring(self, new_ring):
+        if(not (self.poly_ring() is new_ring)):
+            if(not(self.__relations is None)):
+                self.__relations = [new_ring(el) for el in self.__relations];
+                self.__rel_ideal = ideal(self.poly_ring(), []);
+                
+    def _relations(self):
+        '''
+            Returns a Groebner Basis of the relations ideals known in this conversion system.
+        '''
+        if(self.__relations is None):
+            self.__relations = [];
+            self.__rel_ideal = ideal(self.poly_ring(), []);
+            
+        return self.__relations;
+        
+    def _rel_ideal(self):
+        '''
+            Returns the ideal object of relations known in this conversion system.
+        '''
+        return self.__rel_ideal;
+        
+    def _to_poly_element(self, element):
+        '''
+            Method that cast an element in `self.base()` to a polynomial in the conversion system.
+            
+            This method must be implemented in each specific type of conversion system.
+            
+            Raise a ValueError if the element can not be casted.
+        '''
+        raise NotImplementedError("Abstract method not implemented '_to_poly_element(element)'");
+        
+    def _to_real_element(self, polynomial):
+        '''
+            Method that receives a polynomial in the variables on the conversion system and plug in the real values of those variables.
+            
+            It returns an element in self.base().
+            
+            This method can be overwritten if needed.
+        '''
+        variables = polynomial.parent().gens();
+        multi = (len(variables) > _sage_const_1 );
+        res = self.base().zero();
+        for (k,v) in polynomial.dict().items():
+            term = self.base().one();
+            ## We distinguish between several variables and just one
+            ## because the return of poly.dict() is different
+            if(multi):
+                for i in range(len(variables)):
+                    term *= (self.map_of_vars()[str(variables[i])]**k[i]);
+            else:
+                term *= self.map_of_vars()[str(variables[_sage_const_0 ])]**k;
+                
+            res += term*self.base()(v);
+            
+        return res;
+        
+    def _mix_conversion(self, conversion):
+        '''
+            Method that, assuming `conversion` is a compatible Conversion System, mix `self` with `conversion` to a new Conversion System of the same type of `self` that can represent objects in any conversion system.
+            
+            This method must be implemented in each particular class extending ConversionSystem.
+        '''
+        raise NotImplementedError("Abstract method not implemented '_mix_conversion(conversion)'");
+                
+    ## Private methods
+    def __add_relation(self, relation):
+        '''
+        General method for adding relations that accepts any kind of argument posible.
+        
+        Allowed input types:
+            - List, Set or Tuple
+            - Polynomials.
+            - Fractions of polynomials.
+            - Any element that can be converted using `self`.
+        '''
+        if(isinstance(relation, list) or isinstance(relation, set) or isinstance(relation, tuple)):
+            for el in relation:
+                self.__add_relation(el);
+        elif(relation in self.poly_ring()):
+            reduced = self.simplify(relation);
+            if(reduced != _sage_const_0 ):
+                self.__relations += [reduced];
+        elif(relation in self.poly_field()):
+            reduced = self.simplify(relation.numerator());
+            if(reduced != _sage_const_0 ):
+                self.__relations += [reduced];
+        else:
+            try:
+                self.__add_relation(self.to_poly(relation));
+            except Exception:
+                raise TypeError("Impossible to add a non-polynomical relation");
+                
+    ## Magic Python methods
+    def __repr__(self):
+        if(self.is_polynomial()):
+            return "Conversion system with %d variables" %self.poly_ring().ngens();
+        else:
+            return "(Empty) Conversion system";
+    
+    def __str__(self):
+        out = "";
+        out += repr(self) + "\n";
+        if(self.is_polynomial()):
+            out += "\tFrom: %s\n" %self.base();
+            out += "\tTo  : %s\n" %self.poly_ring();
+            out += "Map of the variables:\n";
+            for gen in self.poly_ring().gens():
+                out += "%s\t->\t%s\n" %(gen, repr(self.map_of_vars()[str(gen)]));
+        return out;
+        
+
diff --git a/ajpastor/lazy/lazyFracField.py b/ajpastor/lazy/lazyFracField.py
new file mode 100644 (file)
index 0000000..4a156f2
--- /dev/null
@@ -0,0 +1,324 @@
+
+# This file was *autogenerated* from the file ./lazyFracField.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0); _sage_const_25 = Integer(25)
+
+# Some needed imports
+from sage.structure.element import FieldElement;
+from sage.rings.ring import Field;
+from sage.structure.unique_representation import UniqueRepresentation;
+from sage.categories.quotient_fields import QuotientFields;
+from sage.categories.pushout import ConstructionFunctor;
+
+# risc.ajpastor imports
+from .lazyIDElements import *;
+
+#####################################################
+### Class for Lazy Fraction Field Elements
+#####################################################
+class LazyFFElement(FieldElement):
+    def __init__(self, parent,n,d=None):
+        if(not isinstance(parent, LazyFracField)):
+            raise TypeError("The parent of a fraction must be a Fraction Field");
+            
+        B = parent.base(); # B is the lazy domain
+        ### Checking/Adapting numerator
+        if(not(n in B)):
+            raise TypeError("Element for numerator must be in %s" %(repr(B)));
+        elif(not (isinstance(n, LazyIDElement))):
+            n = B(n);
+            
+        ### Checking/Adapting denominator
+        if(d is None):
+            d = B.one();
+        elif(not (d in B)):
+            raise TypeError("Element for denominator must be in %s" %(repr(B)));
+        elif(d == B.zero()):
+            raise ZeroDivisionError("The denominator can not be zero");
+        elif(not (isinstance(d, LazyIDElement))):
+            d = B(d);
+        elif(isinstance(d, SumLIDElement)):
+            #raise TypeError("No additions are allowed as denominators in a Lazy Fraction Field");
+            #d = B(d.raw());
+            pass;
+            
+        self.__n = n;
+        self.__d = d;
+        FieldElement.__init__(self,parent);
+        
+    def numerator(self):
+        return self.__n;
+        
+    def denominator(self):
+        return self.__d;
+        
+    def _repr_(self):
+        return "(%s):(%s)"%(repr(self.numerator()),repr(self.denominator()));
+        
+    def __str__(self):
+        return "(%s):(%s)"%(str(self.numerator()),str(self.denominator()));
+        
+    def _add_(self, other):
+        if(not(other in self.parent())):
+            return NotImplemented;
+        if(not isinstance(other, LazyFFElement)):
+            other = self.parent()(other);
+            
+        N = self.numerator()*other.denominator()+self.denominator()*other.numerator();
+        D = self.denominator()*other.denominator();
+        return LazyFFElement(self.parent(), N, D).simplify();
+        
+    def _sub_(self, other):
+        if(not(other in self.parent())):
+            return NotImplemented;
+        if(not isinstance(other, LazyFFElement)):
+            other = self.parent()(other);
+            
+        N = self.numerator()*other.denominator()-self.denominator()*other.numerator();
+        D = self.denominator()*other.denominator();
+        return LazyFFElement(self.parent(), N, D).simplify();
+        
+    def _neg_(self):
+        return LazyFFElement(self.parent(), -self.numerator(), self.denominator());
+        
+    def _mul_(self, other):
+        if(not(other in self.parent())):
+            return NotImplemented;
+        if(not isinstance(other, LazyFFElement)):
+            other = self.parent()(other);
+            
+        N = self.numerator()*other.numerator();
+        D = self.denominator()*other.denominator();
+        return LazyFFElement(self.parent(), N, D).simplify();
+        
+    def _div_(self, other):
+        if(not(other in self.parent())):
+            return NotImplemented;
+        if(not isinstance(other, LazyFFElement)):
+            other = self.parent()(other);
+            
+        N = self.numerator()*other.denominator();
+        D = self.denominator()*other.numerator();
+        return LazyFFElement(self.parent(), N, D).simplify();
+        
+    ###############################
+    ### Equality methods
+    ############################### 
+    def __eq__(self, other):
+        self.simplify();
+        try:
+            if(isinstance(other, LazyFFElement)):
+                other.simplify();
+                return (self.numerator() == other.numerator()) and (self.denominator() == other.denominator());
+            elif(isinstance(other, LazyIDElement)):
+                other = other.simplify();
+                
+            return (self.numerator() == other) and (self.denominator() == self.parent().base().one());
+        except Exception as e:
+            return False;
+        
+        
+    ###############################
+    ### Other methods
+    ############################### 
+    def raw(self):
+        self.simplify();
+        if(self.denominator().raw() == _sage_const_1 ):
+            return self.numerator().raw();
+        raise ValueError("Impossible to compute an element within the domain: non 1 denominator");
+    
+    def simplify(self):
+        n = self.numerator().simplify();
+        d = self.denominator().simplify();
+        
+        ## Special case: numerator is zero --> Return the zero element
+        if(n == self.parent().base().zero()):
+            self.__n = self.parent().base().zero();
+            self.__d = self.parent().base().one();
+            return self;
+            
+        ## Special case: numerator and denominator are the same --> Return the one element
+        if(n == d):
+            self.__n = self.parent().base().one();
+            self.__d = self.parent().base().one();
+            return self;
+            
+        ## Generic algorithm: compute gcd of numerator and denominator and divide by it
+        num_divisor = n.max_divisor();
+        den_divisor = d.max_divisor();
+        common = num_divisor.gcd(den_divisor);
+        
+        n = n.divide(common);
+        d = d.divide(common);
+        
+        ## Special case for sign: check if the denominator has all negative. In that case
+        ## change the sign of the numerator.
+        if(isinstance(d, SumLIDElement)):
+            toChange = True;
+            for key in d.__struct__():
+                toChange = (d.__struct__()[key] < _sage_const_0 );
+                if(not toChange):
+                    break;
+            
+            if(toChange):
+                n = -n;
+                d = -d;
+                
+        self.__n = n;
+        self.__d = d;
+        return self;
+        
+    def reduce(self):
+        return self.simplify();
+        
+    def derivative(self, *input):
+        try:
+            N = self.numerator().derivative(*input)*self.denominator()-self.denominator().derivative(*input)*self.numerator();
+            D = self.denominator()**_sage_const_2 ;
+            
+            return LazyFFElement(self.parent(), N, D).simplify();
+        except AttributeError:
+            raise AttributeError("Impossible derivate elements of %s" %(self.parent().base()));
+            
+    def get_basic_elements(self):
+        return self.numerator().get_basic_elements().union(self.denominator().get_basic_elements());
+        
+#####################################################
+### Class for Lazy Fraction Fields
+#####################################################
+class LazyFracField(UniqueRepresentation, Field):
+    Element = LazyFFElement;
+
+    def __init__(self, base):
+        if base not in IntegralDomains():
+            raise ValueError("%s is no integral domain" % base);
+        if (not isinstance(base, LazyIntegralDomain)):
+            base = GetLazyDomain(base);
+        Field.__init__(self, base, category=QuotientFields());
+        
+        self.base().register_conversion(LFFSimpleMorphism(self, self.base()));
+        self.base().base().register_conversion(LFFSimpleMorphism(self, self.base().base()));
+        try:
+            self.base().base().fraction_field().register_conversion(LFFSimpleMorphism(self, self.base().base().fraction_field()));
+        except Exception:
+            pass;
+        
+    ### Coercion methods
+    def _coerce_map_from_(self, S):
+        coer = None;
+        if(isinstance(S, LazyFracField)):
+            coer = self.base()._coerce_map_from_(S.base());
+        elif(S in QuotientFields()):
+            coer = self.base()._coerce_map_from_(S.base());
+        elif(S == self.base()):
+            coer = True;
+        else:
+            coer = self.base()._coerce_map_from_(S);
+            
+        if(not(coer is False) and not(coer is None)):
+            return True;
+        return None;
+        
+    def _has_coerce_map_from(self, S):
+        coer =  self._coerce_map_from_(S);
+        return (not(coer is False) and not(coer is None));
+        
+    def _element_constructor_(self, *args, **kwds):
+        el_class = self.element_class;
+        if(len(args) < _sage_const_1 ):
+            raise ValueError("Impossible to build a lazy element without arguments");
+        
+        i = _sage_const_0 ;
+        if(args[_sage_const_0 ] is self):
+            i = _sage_const_1 ;
+            
+        X = args[i];
+        Y = None;
+        
+        if(len(args) > i+_sage_const_1 ):
+            Y = args[i+_sage_const_1 ];
+        
+        try:
+            if(isinstance(X, el_class)):
+                if((not(Y is None)) and (Y in self)):
+                    if(isinstance(Y, el_class)):
+                        return X/Y;
+                    else:
+                        return X/(el_class(self,_sage_const_1 ,Y));
+            else:
+                try:
+                    if(X.parent() in QuotientFields()):
+                        return self._element_constructor_(X.numerator(), X.denominator());
+                except AttributeError:
+                    pass;
+                return el_class(self, X,Y);
+        except TypeError as e:
+            raise TypeError("Can not cast %s to a Lazy Fraction of %s because:\n\t- %s" %(repr(X), repr(self.base()), e));
+            
+    def construction(self):
+        return (LazyFracFieldFunctor(), self.base());
+        
+    def __contains__(self, X):
+        try:
+            return (X.parent() is self) or (self._has_coerce_map_from(X.parent()));
+        except AttributeError:
+            try:
+                self(X)
+                return True;
+            except Exception:
+                return False;
+         
+    def _repr_(self):
+        return "LazyFractionField for(%s)"%repr(self.base().base());
+        
+    def base_ring(self):
+        return self.base().base_ring();
+        
+    def characteristic(self):
+        return self.base().characteristic();
+        
+    def _an_element_(self):
+        return self.one();
+        
+    def element(self, n,d = None):
+        return LazyFFElement(self, n,d);
+        
+## Changes in LazyIntegralDomain
+
+LazyIntegralDomain.Fraction_Field = LazyFracField;
+        
+#####################################################
+### Construction Functor for LID
+#####################################################
+class LazyFracFieldFunctor (ConstructionFunctor):
+    def __init__(self):
+        self.rank = _sage_const_25 ;
+        super(LazyFracFieldFunctor, self).__init__(IntegralDomains(),QuotientFields());
+        
+    ### Methods to implement
+    def _coerce_into_domain(self, x):
+        if(x not in self.domain()):
+            raise TypeError("The object [%s] is not an element of [%s]" %(x, self.domain()));
+        if(isinstance(x, LazyFracField)):
+            return x.base();
+        return x;
+        
+    def _apply_functor(self, x):
+        return LazyIntegralDomain(x);
+        
+#####################################################
+### General Morphism for return to basic rings
+#####################################################
+class LFFSimpleMorphism (sage.categories.map.Map):
+    def __init__(self, domain, codomain):
+        super(LFFSimpleMorphism, self).__init__(domain, codomain);
+        
+    def _call_(self, p):
+        if(self.codomain() in QuotientFields()):
+            return self.codomain()(p.numerator().raw(), p.denominator().raw());
+        elif(self.codomain() in IntegralDomains()):
+            return self.codomain()((p.numerator().divide(p.denominator())).raw());
+            
+        raise TypeError("Impossible the conversion of %s to %s" (p, self.codomain()));
+
diff --git a/ajpastor/lazy/lazyIDElements.py b/ajpastor/lazy/lazyIDElements.py
new file mode 100644 (file)
index 0000000..790ec61
--- /dev/null
@@ -0,0 +1,1082 @@
+
+# This file was *autogenerated* from the file ./lazyIDElements.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_10 = Integer(10); _sage_const_0 = Integer(0); _sage_const_20 = Integer(20)
+
+# Some needed imports
+from sage.rings.ring import IntegralDomain;
+from sage.structure.element import IntegralDomainElement;
+from sage.categories.integral_domains import IntegralDomains;
+from sage.categories.pushout import ConstructionFunctor;
+
+_MAX_INTEGER = Integer(_sage_const_2 **_sage_const_10 -_sage_const_1 );
+
+#####################################################
+### Class for Lazy Integral Domain Elements
+#####################################################
+class LazyIDElement(IntegralDomainElement):
+    def __init__(self, parent):
+        if(not (isinstance(parent, LazyIntegralDomain))):
+            parent = GetLazyDomain(parent);
+    
+        self.__raw = None;
+        self.__max_divisor = None;
+    
+        IntegralDomainElement.__init__(self, parent);
+        
+    ###############################
+    ### Public methods
+    ############################### 
+    def base(self):
+        return self.parent().base();
+    
+    def raw(self):
+        '''
+        Method that computes (if needed) and returns an element of `self.base()` that is equal to `self`.
+        '''
+        if(self.__raw is None):
+            self.__raw = self.base()(self.__compute_raw_element__());
+            
+        return self.__raw;
+        
+    def max_divisor(self):
+        '''
+        Method that computes (if needed) and returns a SumLIDElement which we know we can divide `self`.
+        '''
+        if(self.__max_divisor is None):
+            self.__max_divisor = self.__compute_max_divisor__();
+            
+        return self.__max_divisor;
+        
+    def gcd(self,*input):
+        '''
+        Method that computes a SumLIDElement which we know we can divide `self` and every element on `input`.
+        '''
+        return self.max_divisor().gcd(*input);
+        
+    def lcm(self, *input):
+        '''
+        Method that computes a lazyElement such every element of input and self are divisors
+        '''
+        ## Checking the arguments
+        if(len(input) == _sage_const_1  and type(input[_sage_const_0 ]) == list):
+            input = input[_sage_const_0 ];
+            
+        if(len(input) == _sage_const_0 ):
+            return self;
+        
+        smallLcm = (self*input[_sage_const_0 ]).divide(self.gcd(input[_sage_const_0 ]));
+        if(len(input) == _sage_const_1 ):
+            return smallLcm;
+        
+        return smallLcm.lcm(input[_sage_const_1 :]);
+        
+    def is_multiple_of(self,element):
+        '''
+        Method that returns whether `self` can be divided by `element`.
+        '''
+        if(self == element or element == self.base().one()):
+            return True;
+            
+        return self.__inner_is_multiple__(element);
+            
+    def divide(self,element):
+        '''
+        Methdo that performs (if possible) the division of `self` by `element`.
+        '''
+        if((not element in self.base()) and (not isinstance(element, LazyIDElement))):
+            raise TypeError("Impossible divide by a %s" %(type(element)));
+        if(not self.is_multiple_of(element)):
+            raise ValueError("Impossible divide [%s] by [%s] within the domain" %(self, element));
+        
+        element = self.parent()(element);
+        
+        if(self == self.parent().zero()):
+            return self;
+        elif(self == element):
+            return self.parent().one();
+        elif(element == self.base().one()):
+            return self;
+        
+        return self.__compute_division__(element);
+        
+    def simplify(self):
+        '''
+        Method that simplifies (if possible) the current element. It is a change inside the structure and the return is the result of the changes
+        '''
+        return self;
+        
+    def derivative(self, *input):
+        '''
+        Method that computes the lazy derivative of an element. It assumes that the parent ring has a derivation and the arguments for such derivation are provided in 'input'.
+        '''
+        raise AttributeError("Method not implemented");
+    
+    def is_zero(self):
+        if(self.__raw is None):
+            return self.__inner_is_zero__();
+        return self.raw() == self.base().zero();
+        
+    def is_one(self):
+        if(self.__raw is None):
+            return self.__inner_is_one__();
+        return self.raw() == self.base().one();        
+        
+    ###############################
+    ### Arithmetic methods
+    ###############################
+    def _add_(self, other):
+        if(isinstance(other, LazyIDElement)):
+            if(other.is_zero()):
+                return self;
+            try:
+                return self.__inner_add__(other);
+            except NotImplementedError as e:
+                pass;
+            
+        return NotImplemented;
+        
+    def _sub_(self,other):
+        return self.__add__(-other);
+        
+    def _neg_(self):
+        try:
+            return self.__inner_neg__();
+        except NotImplementedError:
+            pass;
+            
+        return NotImplemented;
+        
+    def _mul_(self,other):
+        if(isinstance(other, LazyIDElement)):
+            if(other.is_zero()):
+                return self.parent().zero();
+            if(other.is_one()):
+                return self;
+            try:
+                return self.__inner_mul__(other);
+            except NotImplementedError:
+                pass;
+            
+        return NotImplemented;
+        
+    def _pow_(self,i):
+        try:
+            return self.__inner_pow__(i);
+        except NotImplementedError:
+            return NotImplemented;
+       
+    ###############################
+    ### Representation methods
+    ###############################
+    def _repr_(self):
+        return self.__inner_printing__(repr);
+    
+    def __str__(self):
+        return self.__inner_printing__(str);
+        
+    def __inner_printing__(self, method):
+        return "LazyElement(%s)" %(method(self.raw())); 
+        
+    ###############################
+    ### Abstract methods definition
+    ############################### 
+    def get_basic_elements(self):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+    
+    def __compute_raw_element__(self):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    def __change_domain__(self, newParent):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    def __compute_max_divisor__(self):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    def __struct__(self):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    def __inner_is_multiple__(self, element):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    def __compute_division__(self, element):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    def __inner_is_zero__(self):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    def __inner_is_one__(self):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    def __inner_add__(self, other):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    def __inner_neg__(self):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    def __inner_mul__(self, other):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    def __inner_pow__(self, other):
+        raise NotImplementedError("This method has not been implemented for this type of LazyElement");
+        
+    ###############################
+    ### Private methods definition
+    ############################### 
+    def _is_pure_in_base(self,element):
+        return ((not isinstance(element, LazyIDElement)) and (element in self.base()));
+
+#####################################################
+### Class for Simple LazyIDElements
+#####################################################
+class SimpleLIDElement(LazyIDElement):
+    def __init__(self, parent, element):
+        super(SimpleLIDElement, self).__init__(parent);
+                
+        if(isinstance(element, LazyIDElement)):
+            element = element.raw();
+        
+        self.__element = element;
+        self.raw();
+       
+    ###############################
+    ### Overriden methods
+    ############################### 
+    def derivative(self, *input):
+        return SimpleLIDElement(self.parent(), self.raw().derivative(*input));
+        
+    ###############################
+    ### Abstract methods implementation
+    ############################### 
+    def get_basic_elements(self):
+        try:
+            self.parent().base_ring()(self.raw());
+        except Exception:
+            return {self};
+        return set();
+            
+    def __compute_raw_element__(self):
+        return self.__element;
+        
+    def __change_domain__(self, newParent):
+        if(newParent == self.parent()):
+            return self;
+        return SimpleLIDElement(newParent, self.raw());
+        
+    def __compute_max_divisor__(self):
+        return SumLIDElement(self.parent(), ProductLIDElement(self.parent(),self)); 
+              
+    def __struct__(self):
+        return self.raw();
+        
+    def __inner_is_multiple__(self, element):
+        return False;
+        
+    def __compute_division__(self, element):
+        if(isinstance(element, SimpleLIDElement)):
+            inner = element;
+            number = _sage_const_1 ;
+        elif(isinstance(element, ProductLIDElement)):
+            inner = element;
+            number = _sage_const_1 ;
+        elif(isinstance(element, SumLIDElement)):
+            inner = element.__struct__().keys()[_sage_const_0 ];
+            number = element.__struct__()[inner];
+        
+        if(number == _sage_const_1 ):
+            if(inner == self.parent().one()):
+                return self;
+            else:
+                return self.parent().one();
+        elif(number == -_sage_const_1 ):
+            if(inner == self.parent().one()):
+                return -self;
+            else:
+                return -self.parent().one();
+        raise ValueError("Impossible Exception: can not perform division");
+        
+    def __inner_add__(self, other):
+        if(isinstance(other, SumLIDElement)):
+            return SumLIDElement(self.parent(), self, other.__struct__());
+        else:
+            return SumLIDElement(self.parent(), self, other);
+        
+    def __inner_neg__(self):
+        return SumLIDElement(self.parent(), {self:-_sage_const_1 });
+        
+    def __inner_mul__(self, other):
+        if(isinstance(other, SumLIDElement)):
+            other_dic = other.__struct__();
+            new_dic = {};
+            for key in other_dic:
+                new_dic[key*self] = other_dic[key];
+                
+            return SumLIDElement(self.parent(), new_dic);
+        elif(isinstance(other, ProductLIDElement)):
+            return ProductLIDElement(self.parent(), self, other.__struct__());
+        else:
+            return ProductLIDElement(self.parent(), self, other);
+        
+    def __inner_pow__(self, other):
+        return ProductLIDElement(self.parent(), {self:other});
+       
+    ###############################
+    ### Equality methods
+    ############################### 
+    def __eq__(self, other):
+        if(self._is_pure_in_base(other)):
+            other = self.parent()(other);
+        if(isinstance(other, LazyIDElement)):
+            if(isinstance(other, SimpleLIDElement)):
+                return self.raw() == other.raw();
+            else:
+                return other.__eq__(self);
+                
+        return False;
+            
+    def __hash__(self):
+        return hash(self.raw())%_MAX_INTEGER;
+       
+    ###############################
+    ### Representation methods
+    ###############################
+    def __inner_printing__(self, method):
+        return "Simple(%s)" %(method(self.raw())); 
+        
+#####################################################
+### Class for Product of LazyIDElements
+#####################################################
+class ProductLIDElement(LazyIDElement):
+    def __init__(self, parent, *input):
+        super(ProductLIDElement, self).__init__(parent);
+        
+        self.__factors = {};
+        
+        for el in input:
+            self.__add_element(el);
+        self.__simplified = False;
+        #self.simplify();
+            
+    def __add_element(self, el, n=_sage_const_1 ):
+        ## Checking the n argument
+        if(n == _sage_const_0 ):
+            return;
+        elif(n < _sage_const_0 ):
+            raise ValueError("Imposible have a negative exponent");
+            
+        ## If we are zero, we do nothing
+        zero = self.parent().zero();
+        one = self.parent().one();
+        if(zero in self.__struct__()):
+            return;
+            
+        ## Other structures checking
+        if(isinstance(el,list)):
+            for aux in el:
+                self.__add_element(aux,n);
+        elif(isinstance(el, dict)):
+            for aux in el:
+                self.__add_element(aux,el[aux]);
+        ## Checking if the element is zero 9simplify operations
+        elif(isinstance(el,LazyIDElement) and zero == el):
+            self.__factors = {zero : _sage_const_1 };
+            return;
+        elif((not isinstance(el, LazyIDElement)) and zero.raw() == el):
+            self.__factors = {zero : _sage_const_1 };
+            return;
+        ## We do nothing if we are adding +1
+        elif(isinstance(el,LazyIDElement) and one == el):
+            return;
+        elif((not isinstance(el, LazyIDElement)) and one.raw() == el):
+            return;
+        ## Otherwise, we do as usual
+        elif(self._is_pure_in_base(el)):
+            self.__add_element(SimpleLIDElement(self.parent(),el),n);
+        elif(isinstance(el,SimpleLIDElement)):
+            self.__factors[el] = self.__factors.get(el,_sage_const_0 )+n;
+        else:
+            raise Exception("Impossible to put a %s in a Product Lazy Element" %type(el));
+          
+    ###############################
+    ### Overriden methods
+    ###############################
+    def simplify(self):
+        if(not self.__simplified):
+            self.__simplified = True;
+            current_dic = self.__struct__();
+            
+            new_dic = {};
+            
+            for key in current_dic:
+                current = current_dic[key];
+                s_key = key.simplify();
+                if(s_key.is_zero()):
+                    self.__factors = {self.parent().zero():_sage_const_1 };
+                    return self.parent().zero();
+                if((not (s_key.is_one())) and (current > _sage_const_0 )):
+                    new_dic[s_key] = new_dic.get(s_key, _sage_const_0 ) + current_dic[key];
+               
+            mone = SimpleLIDElement(self.parent(),-self.base().one());
+            if(new_dic.has_key(mone)):     
+                new_dic[mone] = new_dic[mone]%_sage_const_2 ;        
+            
+            self.__factors = new_dic;
+        
+        if(len(self.__factors) == _sage_const_0 ): 
+            return self.parent().one();
+            
+        return self;
+        
+    def derivative(self, *input):
+        self.simplify();
+        
+        resList = [];
+        aux_dic = {};
+        current_dic = self.__struct__();
+        
+        #Preparing the aux_dic variable
+        for key in current_dic:
+            aux_dic[key] = current_dic[key];
+            
+        # Iterating for each element
+        for key in current_dic:
+            # Creating the new element to the sum
+            der = key.derivative(*input);
+            aux_dic[key] = max(_sage_const_0 ,aux_dic[key]-_sage_const_1 );
+            aux_dic[der] = aux_dic.get(der, _sage_const_0 )+_sage_const_1 ;
+            resList += [{ProductLIDElement(self.parent(), aux_dic) : current_dic[key]}];
+            
+            #Restoring the aux_dic
+            aux_dic[key] = current_dic[key];
+            if(current_dic.has_key(der)):
+                aux_dic[der] = aux_dic[der]-_sage_const_1 ;
+            else:
+                del aux_dic[der];
+            
+        return SumLIDElement(self.parent(), resList);
+            
+    ###############################
+    ### Abstract methods implementation
+    ###############################   
+    def get_basic_elements(self):
+        res = set();
+        for key in self.__struct__().keys():
+            res = res.union(key.get_basic_elements());
+        return res;
+         
+    def __compute_raw_element__(self):
+        res = self.base().one();
+        
+        current_dic = self.__struct__();
+        for key in current_dic:
+            res *= (key.raw())**current_dic[key];
+            
+        return res;
+        
+    def __change_domain__(self, newParent):
+        if(newParent == self.parent()):
+            return self;
+            
+        new_dic = {};
+        current_dic = self.__struct__();
+        
+        for key in current_dic:
+            new_dic[key.__change_domain(newParent)] = current_dic[key];
+        
+        return ProductLIDElement(newParent, new_dic);
+        
+    def __compute_max_divisor__(self):
+        return SumLIDElement(self.parent(), self); 
+        
+    def __struct__(self):
+        return self.__factors;
+        
+    def __inner_is_multiple__(self, element):
+        if(self._is_pure_in_base(element)):
+            element = self.parent()(element);
+            
+        if(isinstance(element, LazyIDElement)):
+            if(element.is_one()):
+                return True;
+            elif(isinstance(element, SimpleLIDElement)):
+                return not(self.__struct__().get(element) is None);
+            elif(isinstance(element, ProductLIDElement)):
+                return self.gcd(element) == element;
+            elif(isinstance(element, SumLIDElement)):
+                el_dic = element.__struct__();
+                if(len(el_dic) == _sage_const_1 ):
+                    key = el_dic.keys()[_sage_const_0 ];
+                    if(el_dic[key] == _sage_const_1 ):
+                        return self.is_multiple_of(key);
+            
+        return False;
+        
+    def __compute_division__(self, element):
+        if(not isinstance(element, LazyIDElement)):
+            return self.__compute_division__(SimpleLIDElement(self.parent(),element));
+        
+        if(isinstance(element, SimpleLIDElement)):
+            inner = element;
+            number = _sage_const_1 ;
+        elif(isinstance(element, ProductLIDElement)):
+            inner = element;
+            number = _sage_const_1 ;
+        elif(isinstance(element, SumLIDElement)):
+            inner = element.__struct__().keys()[_sage_const_0 ];
+            number = element.__struct__()[inner];
+        
+        if(number != _sage_const_1  and number != -_sage_const_1 ):
+            raise ValueError("Impossible Exception: can not perform division");
+        
+        new_dic = {};
+        current_dic = self.__struct__();
+        if(isinstance(inner, SimpleLIDElement)):
+            for key in current_dic:
+                new_dic[key] = current_dic[key];
+            new_dic[inner] = new_dic.get(inner,_sage_const_0 )-_sage_const_1 ;
+            if(new_dic[inner] == _sage_const_0 ):
+                del new_dic[inner];
+        elif(isinstance(inner, ProductLIDElement)):
+            for key in current_dic:
+                value = current_dic[key]-inner.__struct__().get(key,_sage_const_0 );
+                if(value != _sage_const_0 ):
+                    new_dic[key] = value;
+        else:
+            raise TypeError("Error with types wile dividing in a Produc Lazy Element (%s)" %type(element));
+                
+        if(number == _sage_const_1 ):
+            return ProductLIDElement(self.parent(), new_dic).simplify();
+        else: ## number == -1
+            return (-ProductLIDElement(self.parent(), new_dic)).simplify();
+        
+    def __inner_is_zero__(self):
+        zero = self.parent().zero();
+        return self.__struct__().has_key(zero);
+        
+    def __inner_is_one__(self):
+        self.simplify();
+        return (len(self.__struct__()) == _sage_const_0 );
+            
+        
+    def __inner_add__(self, other):
+        if(isinstance(other, SumLIDElement)):
+            return SumLIDElement(self.parent(), self, other.__struct__());
+        else:
+            return SumLIDElement(self.parent(), self, other);
+        
+    def __inner_neg__(self):
+        return SumLIDElement(self.parent(), {self:-_sage_const_1 });
+        #return self*(-self.parent().one());
+        
+    def __inner_mul__(self, other):
+        if(isinstance(other, SumLIDElement)):
+            other_dic = other.__struct__();
+            new_dic = {};
+            for key in other_dic:
+                new_dic[key*self] = other_dic[key];
+                
+            return SumLIDElement(self.parent(), new_dic);
+        elif(isinstance(other, ProductLIDElement)):
+            return ProductLIDElement(self.parent(), self.__struct__(), other.__struct__());
+        else:
+            return ProductLIDElement(self.parent(), self.__struct__(), other);
+        
+    def __inner_pow__(self, other):
+        current_dic = self.__struct__();
+        new_dic = {};
+        
+        for key in current_dic:
+            new_dic[key] = current_dic[key]*other;
+        return ProductLIDElement(self.parent(), new_dic);
+       
+    ###############################
+    ### Equality methods
+    ############################### 
+    def __eq__(self, other):
+        if(self._is_pure_in_base(other)):
+            other = self.parent()(other);
+            
+        current_dic = self.__struct__();
+        if(len(current_dic) == _sage_const_0 ):
+            return other == _sage_const_1 ;
+        if(isinstance(other, LazyIDElement)):
+            if(isinstance(other, SimpleLIDElement)):
+                return (len(current_dic) == _sage_const_1  and current_dic.get(other,_sage_const_0 ) == _sage_const_1 );
+            elif(isinstance(other, ProductLIDElement)):
+                other_dir = other.__struct__();
+                if(len(other_dir) == len(current_dic)):
+                    for key in current_dic:
+                        if(not(current_dic[key] == other_dir.get(key,_sage_const_0 ))):
+                            return False;
+                    return True;
+            elif(isinstance(other, SumLIDElement)):
+                return other.__eq__(self);
+                
+        return False;
+            
+    def __hash__(self):
+        res = _sage_const_1 ;
+        current_dic = self.__struct__();
+        
+        for key in current_dic:
+            res *= (hash(key)**current_dic[key] % _MAX_INTEGER);
+        return res;
+       
+    ###############################
+    ### Representation methods
+    ###############################
+    def __inner_printing__(self, method):
+        res = "ProductOf(";
+        current_dic = self.__struct__();
+        if(len(current_dic) != _sage_const_0 ):
+            first = True;
+            for key in current_dic:
+                if(not first):
+                    res += " * ";
+                else:
+                    first = False;
+                res += "(%s)^%s" %(method(key),current_dic[key]);
+        res += ")";
+        return res; 
+            
+#####################################################
+### Class for Sum of LazyIDElements
+#####################################################
+class SumLIDElement(LazyIDElement):
+    def __init__(self, parent, *input):
+        super(SumLIDElement, self).__init__(parent);
+        
+        self.__summands = {};
+        self.__simplified = False;
+        
+        for el in input:
+            self.__add_element(el);
+        #self.simplify();
+            
+    def __add_element(self, el, n=_sage_const_1 ):
+        ## Checking the n argument
+        if(n == _sage_const_0 ):
+            return;
+            
+        zero = self.parent().zero();
+        
+        ## Checking other structures
+        if(isinstance(el,list)):
+            for aux in el:
+                self.__add_element(aux,n);
+        elif(isinstance(el, dict)):
+            for aux in el:
+                self.__add_element(aux,el[aux]*n);
+        ## We do nothing if we want to add the zero element
+        elif(isinstance(el,LazyIDElement) and zero == el):
+            return;
+        elif((not isinstance(el, LazyIDElement)) and zero.raw() == el):
+            return;
+        ## Otherwise, we do as usual
+        elif(self._is_pure_in_base(el)):
+            self.__add_element(SimpleLIDElement(self.parent(),el),n);
+        elif(isinstance(el,SimpleLIDElement) or isinstance(el, ProductLIDElement)):
+            self.__summands[el] = self.__summands.get(el,_sage_const_0 )+n;
+        elif(isinstance(el, SumLIDElement)):
+            self.__add_element(el.__struct__(),n);
+        else:
+            raise Exception("Impossible to put a %s in a Sum Lazy Element" %type(el));
+      
+    ###############################
+    ### Overriden methods
+    ###############################      
+    def gcd(self,*input):
+        max_divisors = [];
+        for el in input:
+            if(self._is_pure_in_base(el)):
+                el = self.parent()(el);
+                
+            if(isinstance(el, LazyIDElement)):
+                max_divisors += [el.max_divisor()];
+            else:
+                raise TypeError("Impossible compute gcd with %s" %(type(el)));
+                
+        myProd = self.__struct__().keys()[_sage_const_0 ];
+        products = [el.__struct__().keys()[_sage_const_0 ] for el in max_divisors];
+        vals = [self.__struct__()[myProd]] + [max_divisors[i].__struct__()[products[i]] for i in range(len(products))];
+        val_gcd = gcd(vals);
+        
+        new_dic = {}
+        current_dic = myProd.__struct__();
+        for key in current_dic:
+            minimum = current_dic[key];
+            for el in products:
+                minimum = min(minimum, el.__struct__().get(key,_sage_const_0 ));
+                if(minimum == _sage_const_0 ):
+                    break;
+            if(minimum > _sage_const_0 ):
+                new_dic[key] = minimum;
+        return SumLIDElement(self.parent(), {ProductLIDElement(self.parent(),new_dic) : val_gcd});
+    
+      
+    def simplify(self):
+        if(not self.__simplified):
+            self.__simplified = True;
+            current_dic = self.__struct__();
+                
+            new_dic = {};
+            for key in current_dic:
+                current = current_dic[key];
+                s_key = key.simplify();
+                if((not (s_key.is_zero())) and (not(current == _sage_const_0 ))):
+                    new_dic[s_key] = new_dic.get(s_key,_sage_const_0 ) + current;
+                    
+            self.__summands = new_dic;
+        
+        if(len(self.__summands) == _sage_const_0 ): 
+            return self.parent().zero();
+            
+        return self;
+        
+    def derivative(self, *input):
+        current_dic = self.__struct__();
+        new_dic = {};
+        
+        for key in current_dic:
+            der = key.derivative(*input);
+            new_dic[der] = new_dic.get(der,_sage_const_0 )+current_dic[key];
+            
+        return SumLIDElement(self.parent(), new_dic);
+        
+    ###############################
+    ### Abstract methods implementation
+    ###############################  
+    def get_basic_elements(self):
+        res = set();
+        for key in self.__struct__().keys():
+            res = res.union(key.get_basic_elements());
+        return res;
+         
+    def __compute_raw_element__(self):
+        res = self.base().zero();
+        
+        current_dic = self.__struct__();
+        for key in current_dic:
+            res += (key.raw())*current_dic[key];
+            
+        return res;
+        
+    def __change_domain__(self, newParent):
+        if(newParent == self.parent()):
+            return self;
+            
+        new_dic = {};
+        current_dic = self.__struct__();
+        
+        for key in current_dic:
+            new_dic[key.__change_domain(newParent)] = current_dic[key];
+        
+        return SumLIDElement(newParent, new_dic);
+        
+    def __compute_max_divisor__(self):
+        current_dic = self.__struct__();
+        max_divisors = [SumLIDElement(self.parent(), {key.max_divisor() : current_dic[key]}) for key in current_dic];
+        
+        if(len(max_divisors) > _sage_const_0 ):
+            return max_divisors[_sage_const_0 ].gcd(*max_divisors[_sage_const_1 :]);
+            
+        return SumLIDElement(self.parent(), ProductLIDElement(self.parent())); 
+              
+    def __struct__(self):
+        return self.__summands;
+        
+    def __inner_is_multiple__(self, element):
+        max_divisor = self.max_divisor();
+        inner = max_divisor.__struct__().keys()[_sage_const_0 ];
+        if(isinstance(element, SimpleLIDElement)):
+            return inner.is_multiple_of(element);
+        elif(isinstance(element, ProductLIDElement)):
+            return inner.is_multiple_of(element);
+        elif(isinstance(element, SumLIDElement)):
+            if(len(element.__struct__()) == _sage_const_1 ):
+                number = max_divisor.__struct__()[inner];
+                other_inner = element.__struct__().keys()[_sage_const_0 ];
+                other_number = element.__struct__()[other_inner];
+                
+                return (Integer(other_number).divides(number)) and (inner.is_multiple_of(other_inner));
+            
+        return False;
+        
+    def __compute_division__(self, element):
+        if(isinstance(element, SimpleLIDElement)):
+            inner = element;
+            number = _sage_const_1 ;
+        elif(isinstance(element, ProductLIDElement)):
+            inner = element;
+            number = _sage_const_1 ;
+        elif(isinstance(element, SumLIDElement)):
+            inner = element.__struct__().keys()[_sage_const_0 ];
+            number = element.__struct__()[inner];
+    
+        new_dic = {};
+        current_dic = self.__struct__();
+        try:
+            for key in current_dic:
+                new_dic[key.__compute_division__(inner)] = Integer(current_dic[key]/number);
+                
+            return SumLIDElement(self.parent(), new_dic);
+        except TypeError:
+            raise ValueError("Impossible Exception: can not perform division because integer coefficients");
+        
+    def __inner_is_zero__(self):
+        self.simplify();
+        return (len(self.__struct__()) == _sage_const_0 );
+        
+    def __inner_is_one__(self):
+        self.simplify();
+        return self == self.parent().one();
+        
+    def __inner_add__(self, other):
+        if(isinstance(other, SumLIDElement)):
+            return SumLIDElement(self.parent(), self.__struct__(), other.__struct__());
+        else:
+            return SumLIDElement(self.parent(), self.__struct__(), other);
+        
+    def __inner_neg__(self):
+        new_dic = {};
+        current_dic = self.__struct__();
+        
+        for key in current_dic:
+            new_dic[key] = -current_dic[key];
+        return SumLIDElement(self.parent(), new_dic);
+        
+    def __inner_mul__(self, other):
+        if(isinstance(other, SumLIDElement)):
+            current_dic = self.__struct__();
+            other_dic = other.__struct__();
+            new_dic = {};
+            for key_current in current_dic:
+                for key_other in other_dic:
+                    el = key_current*key_other;
+                    new_dic[el] = new_dic.get(el,_sage_const_0 )+current_dic[key_current]*other_dic[key_other];
+                
+            return SumLIDElement(self.parent(), new_dic);
+        else:
+            current_dic = self.__struct__();
+            new_dic = {};
+            for key in current_dic:
+                new_dic[key*other] = current_dic[key];
+                
+            return SumLIDElement(self.parent(), new_dic);
+        
+    def __inner_pow__(self, other):
+        aux = self.parent().one();
+        for i in range(other):
+            aux = aux*self;
+        return aux;
+       
+    ###############################
+    ### Equality methods
+    ############################### 
+    def __eq__(self, other):
+        if(self._is_pure_in_base(other)):
+            other = self.parent()(other);
+            
+        current_dic = self.__struct__();
+        if(len(current_dic) == _sage_const_0 ):
+            return other == _sage_const_0 ;
+        elif(len(current_dic) == _sage_const_1  and (not isinstance(other, SumLIDElement))):
+            key = current_dic.keys()[_sage_const_0 ];
+            return (current_dic[key] == _sage_const_1 ) and (key == other);
+        elif(isinstance(other, SumLIDElement)):
+            other_dir = other.__struct__();
+            if(len(other_dir) == len(current_dic)):
+                for key in current_dic:
+                    if(not(current_dic[key] == other_dir.get(key,_sage_const_0 ))):
+                        return False;
+                return True;
+    
+        return False;
+            
+    def __hash__(self):
+        res = _sage_const_0 ;
+        current_dic = self.__struct__();
+        
+        for key in current_dic:
+            res += (hash(key)*current_dic[key] % _MAX_INTEGER);
+        return res;
+        
+    ###############################
+    ### Representation methods
+    ###############################
+    def __inner_printing__(self, method):
+        res = "SumOf(";
+        current_dic = self.__struct__();
+        if(len(current_dic) != _sage_const_0 ):
+            first = True;
+            for key in current_dic:
+                if(not first):
+                    res += " + ";
+                else:
+                    first = False;
+                res += "%s*(%s)" %(current_dic[key],method(key));
+        res += ")";
+        return res; 
+        
+#####################################################
+### Class for Lazy Integral Domain
+#####################################################
+class LazyIntegralDomain(UniqueRepresentation, IntegralDomain):
+    Element = SimpleLIDElement;
+
+    Fraction_Field = None;
+
+    def __init__(self, base):
+        if base not in IntegralDomains():
+            raise ValueError("%s is no integral domain" % base);
+        IntegralDomain.__init__(self, base, category=IntegralDomains());
+        
+        self._zero_element = SimpleLIDElement(self, base.zero());
+        self._one_element = SimpleLIDElement(self, base.one());
+        
+        self.base().register_conversion(LIDSimpleMorphism(self, self.base()));
+        
+        
+    def fraction_field(self):
+        if(not(LazyIntegralDomain.Fraction_Field is None)):
+            return LazyIntegralDomain.Fraction_Field(self);
+        return super(LazyIntegralDomain, self).fraction_field();
+        
+    
+    ### Coercion methods
+    def _coerce_map_from_(self, S):
+        coer = None;
+        if(isinstance(S, LazyIntegralDomain)):
+            coer = self.base()._coerce_map_from_(S.base());
+        elif(S == self.base()):
+            coer = True;
+        else:
+            coer = self.base()._coerce_map_from_(S);
+            
+        if(not(coer is False) and not(coer is None)):
+            return True;
+        return None;
+        
+    def _has_coerce_map_from(self, S):
+        coer =  self._coerce_map_from_(S);
+        return (not(coer is False) and not(coer is None));
+        
+    def _element_constructor_(self, *args, **kwds):
+        if(len(args) < _sage_const_1 ):
+            print args
+            raise ValueError("Impossible to build a lazy element without arguments");
+        
+        i = _sage_const_0 ;
+        if(len(args) >= _sage_const_2 ):
+            if(not (args[_sage_const_0 ] is self)):
+                raise ValueError("RIOKO: What the .... are you sending to this method?");
+            i = _sage_const_1 ;
+        X = args[i];
+        
+        try:
+            if(not isinstance(X, LazyIDElement)):
+                X_is_int = (X in ZZ) or (type(X) == int);
+                if((X_is_int) and (not (self.base() is ZZ))):
+                    if(X == _sage_const_0 ):
+                        return self.zero();
+                    elif(X == _sage_const_1 ):
+                        return self.one();
+                    else:
+                        return SumLIDElement(self, {self.base().one() : ZZ(X)});
+                    
+                return SimpleLIDElement(self, self.base()(X));
+            else:
+                if(X.parent() is self):
+                    return X;
+                elif(self._has_coerce_map_from(X.parent().base())):
+                    return X.__change_domain__(self);
+                else:
+                    return self._element_constructor_(X.raw());
+        except TypeError:
+            raise TypeError("This element can not be casted to %s" %repr(self));
+            
+    def construction(self):
+        return (LazyIntegralDomainFunctor(), self.base());
+        
+    def __contains__(self, X):
+        try:
+            return (X.parent() is self) or (self._has_coerce_map_from(X.parent()));
+        except AttributeError:
+            try:
+                self(X)
+                return True;
+            except Exception:
+                return False;
+      
+    # Other Integral Domain methods   
+    def _repr_(self):
+        return "LazyDomain(%s)"%repr(self.base());
+        
+    def base_ring(self):
+        return self.base().base_ring();
+        
+    def characteristic(self):
+        return self.base().characteristic();
+        
+    def _an_element_(self):
+        return self.one();
+        
+    def element(self, X):
+        return self(X);
+        
+    # Ring is_* methods
+    def is_field(self):
+        return self.base().is_field();
+    
+    def is_finite(self):
+        return self.base().is_finite();
+        
+    def is_integrally_closed(self):
+        return self.base().is_integrally_closed();
+        
+    def is_noetherian(self):
+        return self.base().is_noetherian();
+        
+#####################################################
+### Construction Functor for LID
+#####################################################
+class LazyIntegralDomainFunctor (ConstructionFunctor):
+    def __init__(self):
+        ID = IntegralDomains();
+        self.rank = _sage_const_20 ;
+        super(LazyIntegralDomainFunctor, self).__init__(ID,ID);
+        
+    ### Methods to implement
+    def _coerce_into_domain(self, x):
+        if(x not in self.domain()):
+            raise TypeError("The object [%s] is not an element of [%s]" %(x, self.domain()));
+        if(isinstance(x, LazyIntegralDomain)):
+            return x.base();
+        return x;
+        
+    def _apply_functor(self, x):
+        return LazyIntegralDomain(x);
+        
+#####################################################
+### General Morphism for return to basic rings
+#####################################################
+class LIDSimpleMorphism (sage.categories.map.Map):
+    def __init__(self, domain, codomain):
+        super(LIDSimpleMorphism, self).__init__(domain, codomain);
+        
+    def _call_(self, p):
+        return self.codomain()(p.raw());
+        
+#####################################################
+### Global and static elements
+#####################################################
+__MAP_TO_LAZY_DOMAINS = {};
+def GetLazyDomain(X):
+    global  __MAP_TO_LAZY_DOMAINS;
+    if(not __MAP_TO_LAZY_DOMAINS.has_key(X)):
+        __MAP_TO_LAZY_DOMAINS[X] = LazyIntegralDomain(X);
+        
+    return __MAP_TO_LAZY_DOMAINS[X];
+    
+
diff --git a/ajpastor/lazy/lazyRing.py b/ajpastor/lazy/lazyRing.py
new file mode 100644 (file)
index 0000000..6b7b1e6
--- /dev/null
@@ -0,0 +1,598 @@
+
+# This file was *autogenerated* from the file ./lazyRing.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0); _sage_const_20 = Integer(20)
+from sage.rings.ring import IntegralDomain;
+from sage.structure.element import IntegralDomainElement;
+from sage.categories.integral_domains import IntegralDomains;
+from sage.categories.fields import Fields;
+from sage.categories.pushout import ConstructionFunctor;
+
+from sage.rings.polynomial.polynomial_ring import is_PolynomialRing as isUniPolynomial;
+from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing as isMPolynomial;
+
+from ajpastor.lazy.conversion import ConversionSystem;
+from ajpastor.misc.ring_w_sequence import Ring_w_Sequence;
+
+####################################################################################################
+####################################################################################################
+### ELEMENT CLASS
+####################################################################################################
+####################################################################################################
+class _LazyElement(IntegralDomainElement):
+    def __init__(self, parent, el):
+        if(not (isinstance(parent, LazyRing))):
+            parent = GetLazyDomain(parent);
+    
+        self.__raw = None;
+        self.__poly = None;
+    
+        IntegralDomainElement.__init__(self, parent);
+        
+        try:
+        #if(el in parent.poly_ring()):
+            self.__poly = parent.poly_ring()(str(el));
+        except:
+        #elif(el in parent.poly_field()):
+            try:
+                self.__poly = parent.poly_field()(str(el));
+            except:
+        #else:
+                self.__raw = parent.base()(el);
+                self.__poly = self.parent().to_poly(self.__raw);
+            
+        self.simplify();
+         
+    ################################################################################################
+    ### Methods for a LazyElement
+    ################################################################################################
+    def raw(self):
+        '''
+        Method that computes (if needed) and returns an element of `self.base()` that is equal to `self`.
+        '''
+        if(self.__raw is None):
+            self.__raw = self.parent().to_real(self.__poly);
+            
+        return self.__raw;
+        
+    def poly(self):
+        '''
+        Method that computes (if needed) and returns an polynomial such that the conversion using
+        self.parent() returns self.raw().
+        '''
+        if(self.__poly is None):
+            self.__poly = self.parent().to_poly(self.__raw);
+            
+        return self.__poly;
+        
+    def simplify(self):
+        self.__poly = self.parent().simplify(self.poly());
+        
+    def variables(self):
+        '''
+        Method that returns a tuple with the variables that appear in self.poly().
+        
+        If such polynomial representation is a quotient of polynomials, it take the union of the variables in the numerator and denominator.
+        If such polynomial representation has no variables, returns an empty tuple.
+        '''
+        if(self.poly() not in QQ):
+            if(self.poly() in self.parent().poly_ring()):
+                return self.parent().poly_ring()(self.poly()).variables();
+            else:
+                var_n = self.parent().poly_ring()(self.poly().numerator()).variables();
+                var_d = self.parent().poly_ring()(self.poly().denominator()).variables();
+                
+                return tuple(set(var_n+var_d));
+        return tuple();
+        
+    def derivative(self):
+        '''
+        Method that computes the derivative of an element in the laziest way possible.
+        
+        This method compute the derivative of each of the variables in 'self.poly()' and then build
+        a new polynomial using this expressions.
+        
+        This method rely on the parent method 'get_derivative()' which saves the derivatives of the 
+        variables as a dictionary
+        '''
+        map_of_derivatives = {var : self.parent().get_derivative(var) for var in self.variables()};
+        
+        return _LazyElement(self.parent(),sum(map_of_derivatives[key]*self.parent().poly_field()(self.poly()).derivative(key) for key in map_of_derivatives));
+        
+        return self.parent().zero();
+      
+    ################################################################################################
+    ### Arithmetics methods
+    ################################################################################################
+    def _add_(self, other):
+        if(isinstance(other, _LazyElement)):
+            try:
+                return _LazyElement(self.parent(), self.poly()+self.parent()(other).poly());
+            except NotImplementedError as e:
+                pass;
+            
+        return NotImplemented;
+        
+    def _sub_(self,other):
+        return self.__add__(-other);
+        
+    def _neg_(self):
+        try:
+            return _LazyElement(self.parent(), -self.poly());
+        except NotImplementedError:
+            pass;
+            
+        return NotImplemented;
+        
+    def _mul_(self,other):
+        if(isinstance(other, _LazyElement)):
+            try:
+                return _LazyElement(self.parent(), self.poly()*self.parent()(other).poly());
+            except NotImplementedError as e:
+                pass;
+            
+        return NotImplemented;
+        
+    def _div_(self,other):
+        if(isinstance(other, _LazyElement)):
+            try:
+                return _LazyElement(self.parent(), self.parent().poly_field()(self.poly())/self.parent().poly_field()(self.parent()(other).poly()));
+            except NotImplementedError as e:
+                pass;
+            
+        return NotImplemented;
+        
+    def _pow_(self,i):
+        try:
+            return _LazyElement(self.parent(), self.poly()**i);
+        except NotImplementedError:
+            return NotImplemented;
+            
+    def __eq__(self, other):
+        return (self-other).is_zero();
+        
+    def __call__(self, input):
+        if(self.__raw is None):
+            return self.poly()(**{str(var):self.parent().to_real(var)(input) for var in self.variables()});
+        else:
+            return self.raw()(input);
+        
+    ################################################################################################
+    ### Non-trivial arithmetics methods
+    ################################################################################################
+    def gcd(self,*input):
+        '''
+        Method that a common divisor of 'self' and the input
+        '''
+        if(len(input) > _sage_const_1 ):
+            return self.gcd(input);
+        
+        return _LazyElement(self.parent(), gcd([self.poly()]+[self.parent()(el).poly() for el in input]));
+        
+    def lcm(self,*input):
+        '''
+        Method that a common multiple of 'self' and the input
+        '''
+        if(len(input) > _sage_const_1 ):
+            return self.gcd(input);
+        
+        return _LazyElement(self.parent(), lcm([self.poly()]+[self.parent()(el).poly() for el in input]));
+    
+    def divides(self, other):
+        '''
+        Method that returns True if 'other = a*self'.
+        
+        REMARK: If this methods return False does not mean we can not divide other by self in the level of 'base'.
+        '''   
+        return self.poly().divides(self.parent()(other).poly());
+        
+    #def is_zero_real(self):
+    #    if(not self.is_zero()):
+    #        result = False;
+    #        if(not (self.__raw is None)):
+    #            result = self.raw() == 0;
+    #        else:
+    #            pol = None;
+    #            if(self.poly() in self.parent().poly_ring()):
+    #            else:
+    #                pol = self.parent().poly_ring()(self.poly().numerator());
+    #            
+    #            for factor in pol.factor():
+    #                result = (self.parent().to_real(factor[0]) == 0);
+    #                if(result):
+    #                    self.parent().add_relations(factor[0]);
+    #                    break;
+    #                
+    #    return result;
+        
+    def is_zero(self):
+        result = (self.poly() == _sage_const_0 );
+        if(not result):
+            if(not (self.__raw is None)):
+                result = self.raw() == _sage_const_0 ;
+            else:
+                pol = None;
+                if(self.poly() in self.parent().poly_ring()):
+                    pol = self.parent().poly_ring()(self.poly());
+                else:
+                    pol = self.parent().poly_ring()(self.poly().numerator());
+                
+                factors = None;
+                try:
+                    try:
+                        factors = pol.factor(proof=True);
+                    except NotImplementedError:
+                        factors = pol.factor(proof=False);
+                except:
+                    factors = [(pol,_sage_const_1 )];
+                for factor in factors:
+                    result = (self.parent().to_real(factor[_sage_const_0 ]) == _sage_const_0 );
+                    if(result):
+                        self.parent().add_relations(factor[_sage_const_0 ]);
+                        break;
+                        
+        return result;
+        
+    def is_one(self):
+        try:
+            result = (self.poly() == _sage_const_1 ) or (self.raw() == self.parent().one());
+            if(result):
+                self.parent().add_relations(self.poly()-_sage_const_1 );
+            return result;
+        except TypeError:
+            return False;
+        
+    ################################################################################################
+    ### Representation methods
+    ################################################################################################
+    def __repr__(self):
+        return repr(self.poly());
+        
+    def __str__(self):
+        if(self.__raw is None):
+            return "Lazy Element: %s" %(repr(self));
+        else:
+            return "Lazy Element: %s\n%s" %(repr(self), str(self.raw()));
+
+####################################################################################################
+####################################################################################################
+### RING CLASS
+####################################################################################################
+####################################################################################################
+class LazyRing (UniqueRepresentation, ConversionSystem, IntegralDomain):
+
+    Element = _LazyElement;
+
+    def __init__(self, base, constants=QQ, category=None):
+        ## Checking the arguments
+        if(not (constants in Fields())):
+            raise TypeError("The argument 'constants' must be a Field");
+        if(not (isinstance(base, Ring_w_Sequence))):
+            raise TypeError("The argument 'base' must be a Ring with Sequence");
+            
+        ## Initializing the parent structures
+        ConversionSystem.__init__(self, base);
+        IntegralDomain.__init__(self, base, category);
+        
+        ## Initializing the attributes
+        self.__constants = constants;
+        
+        self.__poly_ring = None;
+        self._change_poly_ring(constants);
+        
+        ## Initializing the map of variables (if there is enough)
+        self.__map_of_vars = {};
+        
+        ## Initializing the map of derivatives
+        self.__map_of_derivatives = {};
+            
+        ## Casting and Coercion system
+        self.base().register_conversion(LRSimpleMorphism(self, self.base()));
+        
+        ## Auxiliary data
+        self.__var_name = "x";
+        self.__version = _sage_const_1 ;
+                 
+    
+    ################################################################################################
+    ### Implementing methods of ConversionSystem
+    ################################################################################################
+    def poly_ring(self):
+        return self.__poly_ring;
+        
+    def poly_field(self):
+        return self.__poly_field;
+                
+    def map_of_vars(self):
+        return self.__map_of_vars;
+        
+    def variables(self):
+        return tuple(self.poly_ring()(key) for key in self.map_of_vars().keys());
+        
+    def _change_poly_ring(self, new_ring):
+        super(LazyRing, self)._change_poly_ring(new_ring);
+        if(not (self.poly_ring() is new_ring)):
+            self.__poly_ring = new_ring;
+            self.__create_poly_field();
+    
+    def _to_poly_element(self, element):
+        if(not (element in self.base())):
+            raise TypeError("Element is not in the base ring.\n\tExpected: %s\n\tGot: %s" %(element.parent(), self.base()));
+            
+        ## We try to cast to a polynomial. If we can do it, it means no further operations has to be done
+        try:
+            return self.poly_ring()(element);
+        except:
+            pass;
+            
+        ## Now we check if the element has a built method
+        try:
+            built = element.built();
+            if(not (built is None)):
+                if(built[_sage_const_0 ] == "derivative"):
+                    if(not(element in built[_sage_const_1 ])):
+                        integral = self(built[_sage_const_1 ][_sage_const_0 ]);
+                        if(not (integral.poly().is_monomial() and integral.poly().degree() == _sage_const_1 )):
+                            return self(built[_sage_const_1 ][_sage_const_0 ]).derivative().poly();
+                elif(built[_sage_const_0 ] == "polynomial"):
+                    ## We check we do not have infinite recursion
+                    if(not element in built[_sage_const_1 ][_sage_const_1 ].values()):
+                        ## We have some building information
+                        polys = {key : self.to_poly(built[_sage_const_1 ][_sage_const_1 ][key]) for key in built[_sage_const_1 ][_sage_const_1 ]};
+                        return built[_sage_const_1 ][_sage_const_0 ](**polys);
+        except AttributeError:
+            pass;
+            
+        ## Otherwise we look for a linear relation between the element and the variables
+        var_found = None;
+        rel = None;
+        for key in self.map_of_vars().keys():
+            rel = self.__find_relation(element, self.map_of_vars()[key]);
+            if(not (rel is None)):
+                var_found = key;
+                break;
+
+        ## If we find no relation, we add a new variable
+        if(rel is None):
+            new_var = None;
+            if(self.poly_ring() is self.__constants):
+                new_var = var("%s0" %self.__var_name);
+                self._change_poly_ring(PolynomialRing(self.__constants,new_var, order="lex"));
+                self.__map_of_vars[str(new_var)] = element;
+            else:
+                new_var = var("%s%d" %(self.__var_name,self.poly_ring().ngens()));
+                self._change_poly_ring(PolynomialRing(self.__constants, [new_var]+list(self.poly_ring().gens()), order="lex"));
+                self.__map_of_vars[str(new_var)] = element;
+                
+            return new_var;
+            
+        ## We try to keep the real representations small
+        try:
+            new_elem = (element-rel[_sage_const_1 ])/rel[_sage_const_0 ];
+            if(new_elem.size() < self.map_of_vars()[var_found].size()):
+                self.__map_of_vars[var_found] = new_elem;
+        except:
+            pass;
+            
+        ## Otherwise, we return the polynomial computed
+        return self.poly_ring()(rel[_sage_const_0 ]*self.poly_ring()(var_found) + rel[_sage_const_1 ]);
+        
+    ################################################################################################
+    ### Other Methods for LazyRing
+    ################################################################################################
+    def sequence(self, el, n):
+        if(not isinstance(el, _LazyElement)):
+            return el.parent().sequence(el,n);
+        
+        return self.base().sequence(el.raw(),n);
+        
+    def clean_ring(self):
+        ## Clean the relations
+        self.clean_relations();
+    
+        ## Return to the basic constant field
+        self._change_poly_ring(self.__constants);
+        
+        ## Deleting the variables created
+        self.__map_of_vars = {};
+        
+        ## Deleting the map of derivatives
+        self.__map_of_derivatives = {};
+        
+        self.__version += _sage_const_1 ;
+        
+    def change_variable_name(self, new_name):
+        self.__var_name = str(new_name);
+        
+    def version(self):
+        return self.__version;
+    
+    def get_derivative(self, el):
+        if(el in self.__map_of_derivatives):
+            return self.__map_of_derivatives[el];
+            
+        if(el in self.__constants):
+            return _sage_const_0 ;
+        else:
+            try:
+                el = self.poly_ring()(el);
+                if(el in self.poly_ring().gens()):
+                    new_poly = self.to_poly(self.map_of_vars()[str(el)].derivative());
+                    self.__map_of_derivatives[el] = new_poly;
+                    return new_poly;
+            except:
+                pass;
+        raise ValueError("Method 'get_derivative' can only be summoned with variables.\nGot: %s -- %s" %(el, el.parent()));
+        
+    ################################################################################################
+    ### Other Integral Domain methods 
+    ################################################################################################
+    def base_ring(self):
+        return self.base().base_ring();
+        
+    def characteristic(self):
+        return self.base().characteristic();
+        
+    def _an_element_(self):
+        return self.one();
+        
+    def element(self, X):
+        return self(X);
+        
+    ################################################################################################
+    ### Other Ring methods 
+    ################################################################################################
+    def is_field(self):
+        return self.base().is_field();
+    
+    def is_finite(self):
+        return self.base().is_finite();
+        
+    def is_integrally_closed(self):
+        return self.base().is_integrally_closed();
+        
+    def is_noetherian(self):
+        return self.base().is_noetherian();
+        
+    ################################################################################################
+    ### Coercion methods
+    ################################################################################################
+    def _coerce_map_from_(self, S):
+        coer = None;
+        if(isinstance(S, LazyRing)):
+            coer = self.base()._coerce_map_from_(S.base());
+        elif(S == self.base()):
+            coer = True;
+        else:
+            coer = self.base()._coerce_map_from_(S);
+            
+        if(not(coer is False) and not(coer is None)):
+            return True;
+        return None;
+        
+    def _has_coerce_map_from(self, S):
+        coer =  self._coerce_map_from_(S);
+        return (not(coer is False) and not(coer is None));
+        
+    def _element_constructor_(self, *args, **kwds):
+        if(len(args) < _sage_const_1 ):
+            print args
+            raise ValueError("Impossible to build an element without arguments");
+        
+        i = _sage_const_0 ;
+        if(len(args) >= _sage_const_2 ):
+            if(not (args[_sage_const_0 ] is self)):
+                raise ValueError("RIOKO: What the .... are you sending to this method?");
+            i = _sage_const_1 ;
+        X = args[i];
+        
+        try:
+            if(not isinstance(X, _LazyElement)):
+                ## If the element is not a LazyElement, then we try to create a new element with it
+                return _LazyElement(self, X);
+            elif (X.parent() is self):
+                return X;
+            else:
+                ## Otherwise, X.parent() may have different variables
+                other = X.parent();
+                pol = X.poly();
+                
+                ## For each variable in X.poly(), we get the new polynomial            
+                translate = {}
+                for var in X.variables():
+                    translate[str(var)] = self.to_poly(other.map_of_vars()[str(var)]);
+                    
+                ## We now plugin the expressions
+                return _LazyElement(self, pol(**translate));
+        except TypeError:
+            raise TypeError("This element can not be casted to %s" %repr(self));
+            
+    def construction(self):
+        return (LazyRingFunctor(), self.base());
+        
+    def __contains__(self, X):
+        try:
+            return (X.parent() is self) or (self._has_coerce_map_from(X.parent()));
+        except AttributeError:
+            try:
+                self(X)
+                return True;
+            except Exception:
+                return False;
+      
+    ################################################################################################
+    ### Representation methods
+    ################################################################################################
+    def __repr__(self):
+        return "Lazy Ring over (%s)" %(repr(self.base()));
+        
+    def __str__(self):
+        final = "%s with %d variables\n{\n" %(self.__repr__(),len(self.__map_of_vars.keys()));
+        for k,v in self.__map_of_vars.items():
+            final += "\t%s : %s,\n" %(k, repr(v));
+        final += "}";
+        return final
+        
+        ################################################################################################
+    ### Private methods
+    ################################################################################################
+    def __create_poly_field(self):
+        if(isUniPolynomial(self.__poly_ring) or (isMPolynomial(self.__poly_ring))):
+            self.__poly_field = self.__poly_ring.fraction_field();
+        else:
+            self.__poly_field = self.__poly_ring;
+            
+    def __find_relation(self, a, b):
+        ## TODO We assume a and b are not constants
+        i = _sage_const_1 ;
+        while(self.sequence(a,i) == _sage_const_0  and self.sequence(b,i) == _sage_const_0 ):
+            i = i + _sage_const_1 ;
+            
+        ai = self.sequence(a,i);
+        bi = self.sequence(b,i);
+        if(bi == _sage_const_0  or ai == _sage_const_0 ):
+            return None;
+            
+        c = ai/bi;
+        d = self.sequence(a,_sage_const_0 )-c*self.sequence(b,_sage_const_0 );
+        
+        if(a == c*b + d):
+            return (c,d);
+        else:
+            return None;
+
+####################################################################################################
+####################################################################################################
+### Construction Functor for LazyRing
+####################################################################################################
+####################################################################################################      
+class LazyRingFunctor (ConstructionFunctor):
+    def __init__(self):
+        ID = IntegralDomains();
+        self.rank = _sage_const_20 ;
+        super(LazyRingFunctor, self).__init__(ID,ID);
+        
+    ### Methods to implement
+    def _coerce_into_domain(self, x):
+        if(x not in self.domain()):
+            raise TypeError("The object [%s] is not an element of [%s]" %(x, self.domain()));
+        if(isinstance(x, LazyRing)):
+            return x.base();
+        return x;
+        
+    def _apply_functor(self, x):
+        return LazyRing(x);
+        
+####################################################################################################
+####################################################################################################
+### General Morphism for return to basic rings
+####################################################################################################
+####################################################################################################     
+class LRSimpleMorphism (sage.categories.map.Map):
+    def __init__(self, domain, codomain):
+        super(LRSimpleMorphism, self).__init__(domain, codomain);
+        
+    def _call_(self, p):
+        return self.codomain()(p.raw());
+
diff --git a/ajpastor/lazy/lazyToPoly.py b/ajpastor/lazy/lazyToPoly.py
new file mode 100644 (file)
index 0000000..f737305
--- /dev/null
@@ -0,0 +1,220 @@
+
+# This file was *autogenerated* from the file ./lazyToPoly.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+
+from .lazyIDElements import *;
+from .lazyFracField import *;
+from .conversion import ConversionSystem;
+
+class LazyToPoly(ConversionSystem):
+    def __init__(self, base, *args):
+
+        if(not isinstance(base, LazyIntegralDomain)):
+            base = LazyIntegralDomain(base);
+            
+        super(LazyToPoly,self).__init__(base);
+        
+        self.__basic = base.base();
+        
+        self.__map_c_to_v = {};
+        self.__map_v_to_c = {};
+        
+        self.__inner_poly_ring = None;
+        for element in args:
+            self.__add_components(element);
+            
+        if(len(self.__map_c_to_v) > _sage_const_0 ):
+            self.__inner_poly_ring = PolynomialRing(self.base().base_ring(), self.__map_v_to_c.keys());
+            self.__is_polynomial = True;
+        else:
+            self.__inner_poly_ring = base.base_ring();
+        self.__inner_poly_field = self.poly_ring().fraction_field();
+        
+        self.__map_v_to_c = {self.poly_ring()(key): self.__map_v_to_c[key] for key in self.__map_v_to_c};
+    
+    ## Abstract methods of ConversionSystem
+    # Public
+    def poly_ring(self):
+        return self.__inner_poly_ring;
+        
+    def poly_field(self):
+        return self.__inner_poly_field;
+    
+    def map_of_vars(self):
+        return self.__map_v_to_c;
+        
+    # Protected
+    def _to_poly_element(self, element):
+        if(not element in self.base()):
+            raise TypeError("The element is not an element in the base ring");
+            
+        if(element in self.__basic):
+            element = self.base()(element);
+            
+        if(isinstance(element, SimpleLIDElement)):
+            basic = element.get_basic_elements();
+            if(len(basic) == _sage_const_0 ):
+                return self.poly_ring()(element.raw());
+            else:
+                # Converting the simple element
+                # Trying to find a variable
+                el = self.__v(element);
+                if(el is None):
+                    raise ValueError("Impossible to cast the element to a polynomial with this conversion system");
+                return self.poly_ring()(el);
+        elif(isinstance(element, ProductLIDElement)):
+            res = self.poly_ring().one();
+            current_dic = element.__struct__();
+            for key in current_dic:
+                res *= self.to_poly(key)**current_dic[key];
+            return res;
+        elif(isinstance(element, SumLIDElement)):
+            res = self.poly_ring().zero();
+            current_dic = element.__struct__();
+            for key in current_dic:
+                res += self.to_poly(key)*current_dic[key];
+            return res;
+        else:
+            raise ValueError("Impossible to cast the element. Not recognized type");
+    
+    ## Mixing conversions systems
+    def _mix_conversion(self, conversion):
+        if(isinstance(conversion, LazyToPoly)):
+            return LazyToPoly(self.base(), self.__map_c_to_v.keys(), conversion.__map_c_to_v.keys());
+        if(conversion in self.base().fraction_field()):
+            if(conversion in self.base()):
+                return LazyToPoly(self.base(), self.__map_c_to_v.keys(), self.base()(conversion));
+            return LazyToPoly(self.base(), self.__map_c_to_v.keys(), self.base()(conversion.numerator()), self.base()(conversion.denominator()));
+            
+    
+    ##########################################
+    ### PRIVATE METHODS
+    ##########################################
+    def __add_components(self, element):
+        '''
+            Auxiliar method for adding new components to the variables (if needed).
+            
+            This method is private and should only be called during the initialization.
+            
+            This method allows to give as inputs:
+                - Elements in `self.base()` or `self.base().fraction_field()`
+                - Lists or Sets
+                - Matrices with elements in `self.base()` or `self.base().fraction_field()`
+        '''
+        try:
+            if(isinstance(element, list) or isinstance(element, set)):
+                for el in element:
+                    self.__add_components(el);
+            else:
+                try:
+                    self.__add_components(element.numerator()); 
+                    self.__add_components(element.denominator());
+                    return;
+                except AttributeError:
+                    pass;
+                try:
+                    self.base().base_ring()(element);
+                except Exception:
+                    if(element in self.base().base()):
+                        self.__add_components(self.base()(element));
+                    elif(isinstance(element, SimpleLIDElement) and len(element.get_basic_elements()) > _sage_const_0 ):
+                        if(not self.base()._coerce_map_from_(element.parent().base())):
+                            raise TypeError("Incompatible lazy element to ring %s" %(self.base()));
+                        if(self.__v(element, check=True) is None):
+                            aux_var = var("x%d" %(len(self.__map_c_to_v)));
+                            self.__map_c_to_v[element] = aux_var;
+                            self.__map_v_to_c[aux_var] = element;
+                    elif(isinstance(element, sage.matrix.matrix.Matrix)):
+                        for row in element:
+                            for el in row:
+                                self.__add_components(el);
+                    else:
+                        self.__add_components(element.get_basic_elements());
+        except AttributeError:
+            raise TypeError("Impossible to polynomize unlazy elements (%s)" %(element));
+            
+    def __v(self, element, check=True):
+        '''
+            Auxiliar method to see if a SimpleLIDElement can be converted into a variable.
+            
+            This method check if `element` satisfies a linear equation of the form
+                ``element == c*var + d``
+            for some elements c,d in `self.base().base_ring()`.
+            
+            If some error occur during this checking (because the elements does not fit the interfaces usually uses here) we only will check the proper equality (``element == var``)
+            
+            RETURN:
+                - A polynomial in `self.poly_ring()` or `self.poly_field()` if we can cast the element or `None` otherwise.
+        '''
+        try:
+            if(element == _sage_const_0 ):
+                return self.poly_ring().zero();
+                
+            ## Getting the unlazy element an the ring self.__basic
+            R = self.__basic;
+            el = R(element.raw());
+            init_value = lambda p,n : R.sequence(p,n)*factorial(n);
+            
+            ## For each lazy element in the conversion system
+            c = None; d = None; comp = None; k = None; ch_key = None;
+            for key in self.__map_c_to_v.keys():
+                k = R(key.raw());
+                ## We compute the ratio c
+                c = None; i = _sage_const_1 ;
+                while(c is None):
+                    el_init = init_value(el, i); k_init = init_value(k,i);
+                    if(el_init == _sage_const_0  and k_init == _sage_const_0 ):
+                        i += _sage_const_1 ;
+                    elif(el_init == _sage_const_0  or k_init == _sage_const_0 ):
+                        break;
+                    else:
+                        c = el_init/k_init;
+                if(not (c is None)):
+                    d = init_value(el, _sage_const_0 ) - c*init_value(k,_sage_const_0 );
+                    comp = (el-d)/c;
+                    if(comp == k):
+                        ch_key = key;
+                        break;
+            
+            ## If we found a conversion (i.e. ch_key variable is set)
+            if(not (ch_key is None)):
+                ## If the size is smaller than before, we change the associated function
+                try:
+                    var = self.__map_c_to_v[key];
+                    if(check and (self.__size(comp) < self.__size(k))):
+                        #print "Changing representative of a variable:\n\t- Before: %s\n\t- Now: %s" %(repr(k),repr(comp));
+                        comp = self.base()(comp);
+                        del self.__map_c_to_v[key];
+                        self.__map_c_to_v[comp] = var;
+                        if(self.__inner_poly_ring is None):
+                            self.__map_v_to_c[var] = comp;
+                        else:
+                            self.__map_v_to_c[self.poly_ring()(var)] = comp;
+                except Exception as e:
+                    print "Exception caught";
+                    print e
+                    pass;
+                ## We return the compute conversion
+                return c*var + d;
+            ## Otherwise, we return None
+            return None;
+                
+            #if(el == c*k+d):
+            #   return c*self.__map_c_to_v[key] + d;
+            ## PREVIOUS VERSION: Just check the equality f == c*g
+            #el = element.raw().zero_extraction;
+            #for key in self.__map_c_to_v.keys():
+            #    k = key.raw().zero_extraction;
+            #    if(el[0] == k[0]):
+            #        l = el[1](0)/k[1](0);
+            #        if(el[1] == l*k[1]):
+            #            return l*self.__map_c_to_v[key]; 
+            #return None;
+        except Exception:
+            return self.__map_c_to_v.get(element);
+        
+    def __size(self, element):
+        return element.size();
+
diff --git a/ajpastor/misc/__init__.py b/ajpastor/misc/__init__.py
new file mode 100644 (file)
index 0000000..13bfa9e
--- /dev/null
@@ -0,0 +1,3 @@
+from pkgutil import extend_path;
+__path__ = extend_path(__path__, __name__);
+
diff --git a/ajpastor/misc/bareiss.py b/ajpastor/misc/bareiss.py
new file mode 100644 (file)
index 0000000..d6f521f
--- /dev/null
@@ -0,0 +1,308 @@
+
+# This file was *autogenerated* from the file ./bareiss.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+from .matrix import swap_rows;
+from .matrix import swap_cols;
+
+from .cached_property import derived_property;
+
+from .verbose import *;
+
+from sage.rings.polynomial.polynomial_ring import is_PolynomialRing as isUniPolynomial;
+from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing as isMPolynomial;
+
+class BareissAlgorithm(object):
+    ### Initialization method
+    def __init__(self, parent, M, method=None, relations = []):
+        '''
+            This class represent the application of the Bareiss Algorithm over a matrix with polynomial coefficients.
+            This class receives several parameters:
+                - `parent`: the polynomial ring where the operations will be done. A fraction field is allowed if the base domain is a polynomial ring.
+                - `M`: the matrix we want to treat with the Algorithm
+                - `method`: a Boolean function
+                
+            This algorithm also works in a quotient of the polynomial ring (K[X]/I) and the method provided must be a method to check if some polynomial is in the ideal I.
+        '''
+        ## Checking the parent parameter
+        if(parent.is_field()):
+            parent = parent.base();
+        if(not (isUniPolynomial(parent) or isMPolynomial(parent))):
+            raise TypeError("The parent for this algorithm must be a polynomial ring.\n\t Got: %s" %parent);
+        self.__parent = parent;
+            
+        ## Checking the matrix input
+        self.base_matrix = Matrix(self.parent(), M);
+        
+        self.change_of_columns = Permutation(range(_sage_const_1 ,self.base_matrix.ncols()));
+        
+        ## Storing the checking method
+        self.__in_ideal = method;
+        
+        ## Cached elements
+        self.__steps = None;
+        self.__actions = None;
+        self.__gb = ideal(self.parent(), relations).groebner_basis();
+        self.__echelon = None;
+        
+        
+    ### Getter methods
+    def parent(self):
+        '''
+            Method to have the same interface that the majority of objects of SAGE. Returns the polynomial ring where the matrix have its coefficients.
+        '''
+        return self.__parent;
+        
+    #################################################
+    ### Linear algebra methods
+    #################################################
+    #@wverbose
+    def echelon_form(self):
+        if(self.__echelon is None):
+            self.__compute_echelon();
+        return self.__echelon;
+        
+    @wverbose
+    def __compute_echelon(self):
+        sverbose("Computing the echelon form of a matrix of size %dx%d" %(self.base_matrix.nrows(), self.base_matrix.ncols()));
+        
+        self.__actions = [("base")];
+        self.__steps = [self.base_matrix];
+        
+        ## If we have initial relations, we perform the first reduction
+        if(self.__have_ideal(self.__gb)):
+            sverbose("Initial reduction of the matrix"); ## Verbose
+            self.__steps += [self.__simplify_matrix(self.__steps[-_sage_const_1 ])];
+            self.__actions += [("f_reduce", self.__gb)];
+        
+        tr = self.__steps[-_sage_const_1 ].nrows(); tc = self.__steps[-_sage_const_1 ].ncols();
+        cr = _sage_const_0 ; cc = _sage_const_0 ; i = -_sage_const_1 ;
+        
+        sverbose("Starting the iterations");
+        sverbose.start_iteration(min(tr,tc)+_sage_const_1 , True, False);
+        while(i < min(tr,tc)):
+            i = i + _sage_const_1 ;
+            
+            sverbose.increase_depth();
+            
+            pivot = self.__choose_pivot(self.__steps[-_sage_const_1 ], i,i);
+            
+            cr,cc = pivot[_sage_const_0 ];
+            new_rels = pivot[_sage_const_2 ];
+            
+            ## If there are new relations, we simplify the matrix
+            if(len(new_rels) > _sage_const_0 ):
+                sverbose("New relations found looking for a pivot");
+                sverbose("\t%s" %new_rels);
+                self.__gb = ideal(self.parent(), tuple(new_rels) + tuple(self.__gb)).groebner_basis();
+                self.__steps += [self.__simplify_matrix(self.__steps[-_sage_const_1 ])];
+                self.__actions += [("reduce", new_rels)];
+                sverbose("New reductions applied");
+                    
+            ## If no pivot was found, we finish
+            if(cr == -_sage_const_1  or cc == -_sage_const_1 ):
+                sverbose("No pivot found.");
+                sverbose.decrease_depth();
+                sverbose.next_iteration();
+                break;
+                    
+            ## If we have a new pivot, we moved everythin so it is in the proper position
+            swap_actions = pivot[_sage_const_1 ];
+            for action in swap_actions:
+                if(action[_sage_const_0 ] == "sw_r"):
+                    self.__steps += [swap_rows(self.__steps[-_sage_const_1 ], action[_sage_const_1 ],action[_sage_const_2 ])];
+                elif(action[_sage_const_0 ] == "sw_c"):
+                    self.__steps += [swap_cols(self.__steps[-_sage_const_1 ], action[_sage_const_1 ],action[_sage_const_2 ])];
+                    self.change_of_columns = Permutation((action[_sage_const_1 ]+_sage_const_1 ,action[_sage_const_2 ]+_sage_const_1 ))*self.change_of_columns;
+                    
+                self.__actions += [action];
+                
+            ## One the pivot is in position, we proceed with the Bareiss elimination
+            M = self.__steps[-_sage_const_1 ];
+            
+                
+            ## We save the new matrix and go on
+            self.__steps += [self.__bareiss(self.__steps[-_sage_const_1 ], i)];
+            self.__actions += [("bareiss", i)];
+            
+            sverbose.decrease_depth();
+            sverbose.next_iteration();
+            
+        sverbose("Performing gcd simplification");
+        gcds = [gcd(row) for row in self.__steps[-_sage_const_1 ]];
+        for i in range(len(gcds)):
+            if(gcds[i] == _sage_const_0 ):
+                gcds[i] = _sage_const_1 ;
+        self.__steps += [Matrix(self.parent(), [[el/gcds[i] for el in self.__steps[-_sage_const_1 ][i]] for i in range(self.__steps[-_sage_const_1 ].nrows())])];
+        self.__actions += [("gcd_simpl")];
+            
+        self.__echelon = self.__steps[-_sage_const_1 ];
+        sverbose("Finished computation of echelon form");
+                                
+        return;
+        
+    @derived_property
+    def rank(self):
+        for i in range(self.base_matrix.nrows()):
+            if(any((el != _sage_const_0 ) for el in self.echelon_form()[-(i+_sage_const_1 )])):
+                return self.base_matrix.nrows()-i;
+                
+    def relations(self):
+        self.echelon_form();
+        return self.__gb;
+                
+    @cached_method
+    def right_kernel_matrix(self):
+        sol_dimension = self.base_matrix.ncols()-self.rank;        
+        M = self.echelon_form();
+        
+        if(sol_dimension > _sage_const_0 ):
+            ## Compute the product of the nice diagonal
+            A = self.__get_lcm([M[i][i] for i in range(self.rank)]);
+            to_mult = [-A/M[i][i] for i in range(self.rank)];
+        
+            ker_basis = [vector(self.parent(), [to_mult[j]*M[j][i+self.rank] for j in range(self.rank)] + [_sage_const_0  for j in range(i)] + [A] + [_sage_const_0  for j in range(i+_sage_const_1 , sol_dimension)]) for i in range(sol_dimension)];
+            
+            ch = self.change_of_columns;
+            ## If there were a change of columns (i.e. ch is not the trivial permutation) 
+            ## we swap the result
+            if(Permutation(range(_sage_const_1 ,self.base_matrix.ncols())) != ch):
+                ch = ch.inverse();
+                rows = [[_sage_const_0  for i in range(M.ncols())] for j in range(len(ker_basis))];
+                for j in range(M.ncols()):
+                    new_col = ch(j+_sage_const_1 )-_sage_const_1 ;
+                    for i in range(len(ker_basis)):
+                        rows[i][new_col] = ker_basis[i][j];
+                
+                ker_basis = [vector(self.parent(), row) for row in rows];            
+            
+            return ker_basis;
+        else:
+            return [vector(self.parent(), [_sage_const_0  for i in range(M.ncols())])];
+        
+    #################################################
+    ### Other cached methods
+    #################################################
+    @cached_method
+    def is_in_ideal(self, p):
+        p = self.parent()(p);
+        
+        try:
+            return self.__in_ideal(p) is True;
+        except Exception:
+            return False;
+          
+    @cached_method
+    def steps(self):
+        '''
+            This method returns a list of pairs with the matrices obtained during the proccess and the steps taken.
+        '''
+        self.echelon_form();
+        
+        return [(self.__steps[i],self.__actions[i]) for i in range(len(self.__steps))];
+        
+    #################################################
+    ### Private methods for Bareiss Algorithm 
+    #################################################
+    @wverbose
+    def __choose_pivot(self, M, ir, ic):
+        '''
+            This method computes the next pivot element for the algorithm and returns the information to prepare the matrix for the next step.
+            The ir and ic are parameters to begin the search from the position (ir,ic)
+        '''
+        
+        sverbose("Looking for a new pivot from position (%d,%d)" %(ir,ic));
+        relations = set();
+        actions = [];
+        end = False;
+        fc = -_sage_const_1 ; fr = -_sage_const_1 ;
+        ## Checking rows
+        for cc in range(ic, M.ncols()):
+            ## Checking columns
+            for cr in range(ir, M.nrows()):
+                sverbose("Checking if position (%d,%d) is zero (%s)" %(cr,cc, M[cr][cc]));
+                to_check = M[cr][cc];
+                if(len(relations) > _sage_const_0 ):
+                    sverbose("Reducing the selected element with the found relations");
+                    to_check = M[cr][cc].reduce(relations);
+                    sverbose("New element to check: %s" %(to_check));
+                    
+                if(not to_check == self.parent().zero()):
+                    if(not self.is_in_ideal(to_check)):
+                        sverbose("Non-zero element found. Pivot found");
+                        end = True;
+                        fr = cr;
+                        break;
+                    else:
+                        sverbose("Non trivial zero found. New relation: %s" %(to_check));
+                        relations.add(to_check);
+            
+            if(end):
+                fc = cc;
+                break;
+        
+        if(fc != -_sage_const_1  and fc != ic):
+            actions += [("sw_c", ic, cc)];
+            for i in range(_sage_const_1 ,(min(cc-ic, M.ncols()-cc))):
+                actions += [("sw_c", ic+i, M.ncols()-i)];
+        
+        if(fr != -_sage_const_1  and fr != ir):
+            actions += [("sw_r", ir, cr)];
+            
+        sverbose("Finished search of pivot: (%s,%s)" %(fr,fc));
+                
+        return ((fr,fc), tuple(actions), relations);
+        
+    def __bareiss(self, M, i):
+        rows = [];
+        for j in range(i):
+            if(M[j][i] != _sage_const_0 ):
+                rows += [[M[j][k]*M[i][i]-M[j][i]*M[i][k] for k in range(M.ncols())]];
+            else:
+                rows += [[M[j][k] for k in range(M.ncols())]];
+        rows += [[M[i][k] for k in range(M.ncols())]];
+        for j in range(i+_sage_const_1 , M.nrows()):
+            if(M[j][i] != _sage_const_0 ):
+                rows += [[M[j][k]*M[i][i]-M[j][i]*M[i][k] for k in range(M.ncols())]];
+            else:
+                rows += [[M[j][k] for k in range(M.ncols())]];
+        
+        try:
+            gcds = [gcd(row) for row in rows];
+            rows = [[el/gcd[i] for el in rows[i]] for i in range(len(rows))];
+        except Exception:
+            pass;
+            
+        return Matrix(self.parent(), rows);
+        
+    def __have_ideal(self, basis):
+        if(len(basis) == _sage_const_1 ):
+            return basis[_sage_const_0 ] != _sage_const_0 ;
+        return len(basis) > _sage_const_0 ;
+        
+    def __simplify_matrix(self, M):
+        rows = [[el for el in row] for row in M];
+        if(self.__have_ideal(self.__gb)):
+            rows = [[el.reduce(self.__gb) for el in row] for row in rows];
+        
+        return Matrix(self.parent(), rows);
+        
+    def __get_lcm(self, input):
+        try:
+            return lcm(input);
+        except AttributeError:
+            ## No lcm for this class, implementing a general lcm
+            try:
+                ## Relying on gcd
+                p = self.__parent;
+                res = p(_sage_const_1 );
+                for el in input:
+                    res = p((res*el)/gcd(res,el));
+                return res;
+            except AttributeError:
+                ## Returning the product of everything
+                return prod(input);
+        
+        
+
diff --git a/ajpastor/misc/cached_property.py b/ajpastor/misc/cached_property.py
new file mode 100644 (file)
index 0000000..761586c
--- /dev/null
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+
+__author__ = 'Daniel Greenfeld'
+__email__ = 'pydanny@gmail.com'
+__version__ = '1.3.0'
+__license__ = 'BSD'
+
+from time import time
+import threading
+
+
+class cached_property(object):
+    """
+    A property that is only computed once per instance and then replaces itself
+    with an ordinary attribute. Deleting the attribute resets the property.
+    Source: https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76
+    """  # noqa
+
+    def __init__(self, func):
+        self.__doc__ = getattr(func, '__doc__')
+        self.func = func
+
+    def __get__(self, obj, cls):
+        if obj is None:
+            return self
+        value = obj.__dict__[self.func.__name__] = self.func(obj)
+        return value
+        
+class derived_property(cached_property):
+    """
+        A cached property that is protected from external assigment
+        
+        Author: Antonio Jimenez
+    """ #noqa
+    def __init__(self, func):
+        super(derived_property, self).__init__(func);
+        
+    def __set__(self, obj, cls):
+        raise AttributeError("This attribute can not be setted");  
+
+class threaded_cached_property(object):
+    """
+    A cached_property version for use in environments where multiple threads
+    might concurrently try to access the property.
+    """
+
+    def __init__(self, func):
+        self.__doc__ = getattr(func, '__doc__')
+        self.func = func
+        self.lock = threading.RLock()
+
+    def __get__(self, obj, cls):
+        if obj is None:
+            return self
+
+        obj_dict = obj.__dict__
+        name = self.func.__name__
+        with self.lock:
+            try:
+                # check if the value was computed before the lock was acquired
+                return obj_dict[name]
+            except KeyError:
+                # if not, do the calculation and release the lock
+                return obj_dict.setdefault(name, self.func(obj))
+
+
+class cached_property_with_ttl(object):
+    """
+    A property that is only computed once per instance and then replaces itself
+    with an ordinary attribute. Setting the ttl to a number expresses how long
+    the property will last before being timed out.
+    """
+
+    def __init__(self, ttl=None):
+        if callable(ttl):
+            func = ttl
+            ttl = None
+        else:
+            func = None
+        self.ttl = ttl
+        self._prepare_func(func)
+
+    def __call__(self, func):
+        self._prepare_func(func)
+        return self
+
+    def __get__(self, obj, cls):
+        if obj is None:
+            return self
+
+        now = time()
+        obj_dict = obj.__dict__
+        name = self.__name__
+        try:
+            value, last_updated = obj_dict[name]
+        except KeyError:
+            pass
+        else:
+            ttl_expired = self.ttl and self.ttl < now - last_updated
+            if not ttl_expired:
+                return value
+
+        value = self.func(obj)
+        obj_dict[name] = (value, now)
+        return value
+
+    def __delete__(self, obj):
+        obj.__dict__.pop(self.__name__, None)
+
+    def __set__(self, obj, value):
+        obj.__dict__[self.__name__] = (value, time())
+
+    def _prepare_func(self, func):
+        self.func = func
+        if func:
+            self.__doc__ = func.__doc__
+            self.__name__ = func.__name__
+            self.__module__ = func.__module__
+
+# Aliases to make cached_property_with_ttl easier to use
+cached_property_ttl = cached_property_with_ttl
+timed_cached_property = cached_property_with_ttl
+
+
+class threaded_cached_property_with_ttl(cached_property_with_ttl):
+    """
+    A cached_property version for use in environments where multiple threads
+    might concurrently try to access the property.
+    """
+
+    def __init__(self, ttl=None):
+        super(threaded_cached_property_with_ttl, self).__init__(ttl)
+        self.lock = threading.RLock()
+
+    def __get__(self, obj, cls):
+        with self.lock:
+            return super(threaded_cached_property_with_ttl, self).__get__(obj,
+                                                                          cls)
+
+# Alias to make threaded_cached_property_with_ttl easier to use
+threaded_cached_property_ttl = threaded_cached_property_with_ttl
+timed_threaded_cached_property = threaded_cached_property_with_ttl
diff --git a/ajpastor/misc/dinamic_string.py b/ajpastor/misc/dinamic_string.py
new file mode 100644 (file)
index 0000000..b9dcab5
--- /dev/null
@@ -0,0 +1,286 @@
+
+################################################################################
+################################################################################
+################################################################################
+### Class DinamicString
+class DinamicString(object):
+
+    ### Static methods and elements
+    @staticmethod
+    def is_string(obj):
+        '''Method that check if `obj` is str or DinamicString'''
+        return type(obj) == str or isinstance(obj, DinamicString);
+        
+    @staticmethod
+    def is_list(obj):
+        '''Method that check if `obj` is list, set or tuple'''
+        return type(obj) == list or type(obj) == tuple or type(obj) == set;
+
+    ### Initialization methods
+    def __init__(self, template, arguments, escape="_"):
+        '''
+            Initialization of a DinamicString. This method checks that all the arguments are correct.
+            
+            INPUT:
+                - template: a string (type(template) == str) with the basic text of the DinamicString.
+                - arguments: the arguments for this DinamicString. They will be replace in the positions of 'template' with 'escape' characters.
+                    'arguments' can be a single string or DinamicString or a list of them.
+                - escape: main escape character. This character followed by the number i will be use to replace in 'template' the string given by 'arguments[i]'.
+                
+            OUTPUT:
+                - Can raise a TypeError if 'template' is not a string.
+                - Can raise a TypeError if 'escape' is not a string or a character.
+                - Can raise a TypeError if the elements in 'arguments' are not strings.
+        '''
+        ## Assign template and escape
+        if(not (type(template) == str)):
+            raise TypeError("The template argument must be of type 'str'. Received: %s" %(type(tempalte)));
+        self.__template = template;
+        if(not (type(escape) == str or type(escape) == chr)):
+            raise TypeError("The escape character must be of type 'str' or 'chr'. Received %s" %(type(escape)));
+        self.__escape = escape;
+        
+        ## Checking the arguments for the string
+        self.__arguments = None;
+        if(DinamicString.is_string(arguments)):
+            self.__arguments = [arguments];
+        elif(DinamicString.is_list(arguments)):
+            self.__arguments = list(arguments);
+        if(any(not DinamicString.is_string(el) for el in self.__arguments)):
+            raise TypeError("No valid arguments for a DinamicString: only strings and DinamicStrings are valid.");
+        
+        ## Computing the first representation of the DinamicString
+        self.__real = None;
+        self.real();
+        
+    ### Getters and setters
+    def real(self):
+        '''
+            Get the current representation of the DinamicString. This method replace simultaneously the patters "'escape'i" for the string in 'arguments[i]'.
+            
+            This is a cached method: calling it several times in row returns the same string directly. To reset the string, call the method 'change_argument'.
+        '''
+        if(self.__real is None):
+            self.__real = din_m_replace(self.__template, {"%s%d" %(self.__escape, i+1) : str(self.__arguments[i]) for i in range(len(self.__arguments))});
+        
+        return self.__real;
+        
+    def change_argument(self, new_argument, index=0):
+        '''
+            Method to change the arguments of the DinamicString. This method has two different syntaxes:
+                - 'self.change-argument(self, list)' will change completely the arguments of 'self' to the new list.
+                - 'self.change_argument(self, element, index)' will change only the ith argument to element.
+            
+            In any case, this method reset the variable self.__real, so the method self.real() will recompute the final string.
+            
+            INPUT:
+                - new_argument: a list or a string or a DinamicString. Depending of the type of the parameter, this method will perform different actions (see above).
+                - index: in case 'new_argument' is not a list, this will be the index of the argument to change.
+                
+            OUTPUT:
+                - Can raise a TypeError exception if 'new_argument' is not a list nor a string.
+                - Can raise a IndexError exception if 'index' is not valid for 'self.__arguments'.
+        '''
+        if(DinamicString.is_list(new_argument)):
+            self.__arguments = list(new_argument);
+        elif(DinamicString.is_string(new_argument)):
+            self.__arguments[index] = new_argument;
+        else:
+            raise TypeError("The argument 'new_argument' must be a list or a 'str' o a DinamicString");
+            
+        self.__real = None;
+        
+    ### Implementation of 'str' methods
+    def replace(self, pattern, out, deep=False):
+        '''
+            Method equivalent to the replace method of the 'str' class but using DinamicString 
+            in order to have an improved representation of the strings.
+            
+            See documentation 'str.replace?'.
+            
+            INPUT:
+                - pattern: the string we want to change in 'self'.
+                - out: the new string to put where 'pattern' is found.
+                - deep: deep=True implies only replacements in the arguments of 'self' will be performed. Otherwise, we will also change the template.
+                
+            OUTPUT:
+                - A new DinamicString such that 'result.real() == self.real().replace(pattern, str(out))'.
+        '''
+        new_arguments = [din_replace(el, pattern,out, deep) for el in self.__arguments];
+        new_template = self.__template;
+        if(not deep):
+            new_template = new_template.replace(pattern, out);
+            
+        return DinamicString(new_template, new_arguments, self.__escape);
+    
+    ### Extra methods
+    def m_replace(self, to_replace, deep=False):
+        '''
+            Method to perform several replacements simultaneously. This method do exactly the same as 'self.replace(key, value) for all (key,value) in to_replace but all at once.
+            
+            INPUT:
+                - to_replace: dictionary with the pairs (pattern, out) to replace in 'self'.
+                - deep: deep=True implies only replacements in the arguments of 'self' will be performed. Otherwise, we will also change the template.
+                
+            OUTPUT:
+                - A new DinamicString where all the appearances of the keys of 'to_replace' have been changed to their values.
+        '''
+        new_arguments = [din_m_replace(el, to_replace, deep) for el in self.__arguments];
+        new_template = self.__template;
+        if(not deep):
+            new_template = din_m_replace(new_template, to_replace, False);
+            
+        return DinamicString(new_template, new_arguments, self.__escape);
+        
+    ### Magic Python methods
+    def __repr__(self):
+        '''Magic Python 'repr' implementation'''
+        return self.real();
+        
+    def __str__(self):
+        '''Magic Python 'str' implementation'''
+        return self.real();
+        
+    def __getitem__(self, key):
+        '''Magic Python implementation for 'self[key]' ''' 
+        return self.real()[key];
+        
+    ## Implementing concatenation
+    def __add__(self, other):
+        '''Magic Python implementation for '+' (i.e. concatenation)'''
+        if(type(other) == str):
+            return str(self) + other;
+        elif(isinstance(other, DinamicString)):
+            ## Changing arguments of other
+            other_template = din_m_replace(other.__template, {"%s%d" %(other.__escape, i) : "_%d" %(self.__escape, i+len(self.__arguments)) for i in range(len(other.__arguments))});
+            
+            return DinamicString(self.__template + other_template, self.__arguments + other.__arguments);
+            
+        return NotImplemented;
+    
+    def __radd__(self, other):
+        '''Magic Python implementation for '+' (i.e. concatenation)'''
+        if(type(other) == str):
+            return other + str(self);
+        elif(isinstance(other, DinamicString)):
+            return other.__add__(self);
+            
+        return NotImplemented;
+
+################################################################################
+################################################################################
+################################################################################
+### Utility methods combining 'str' and DinamicString
+    
+def din_replace(element, pattern, out, deep=False):
+    '''
+        Enhaced 'replace' method. This function is equivalent to the 'replace' method of
+        the 'str' class when both element and out are strings.
+        
+        This method combines the string functionality with the new DinamicString class.
+        
+        INPUT:
+            - element: 'str' or DinamicString where we want to replace 'pattern'.
+            - pattern: 'str' with the pattern to replace in 'element'.
+            - out: 'str' or DinamicString that will be put in the places where 'pattern' appears in 'element'.
+            - deep: in case 'element' is a DinamicString, deep=True implies the replacement is only performed in the arguments. Otherwise this argument is irrelevant.
+            
+        OUTPUT:
+            If 'element' and 'out' are of type 'str', this method return a new 'str' equivalent
+            to 'element.replace(pattern, out)'.
+            
+            Otherwise, this method returns a new DinamicString where 'pattern' has been replaced by 
+            'out'.
+            
+            - Can raise an error if 'element' is not a 'str' neither a DinamicString.
+    '''
+    if(isinstance(element, DinamicString)):
+        return element.replace(pattern, out, deep);
+    elif(type(element) == str):
+        if(isinsance(out, DinamicString)):
+            return DinamicString(element.replace(pattern, "_1"), out);
+        return element.replace(pattern, out);
+    
+    raise TypeError("No string given. Impossible to replace string");
+
+def din_m_replace(element, to_replace, deep=False):
+    '''
+        Method that replace simultaneously in 'element' the strings in 'to_replace.keys()' with the corresponding string.
+        
+        In case element and the values of 'to_replace' are all of type 'str', this method 
+        will return a new 'str'. Otherwise a new DinamicString will be returned.
+        
+        In case 'element' is a DinamicString, 'deep' marks wether the replacement must be done only
+        in the arguments or in the whole string.
+        
+        INPUT:
+            - element: 'str' or DinamicStrign where perform the replacement.
+            - to_replace: dictionary with the structure 'pattern : out'. This method will wubstitute the patterns in 'to_replace' by their corresponding 'out'.
+            - deep: in case 'element' is a DinamicString, deep=True implies the replacement is only performed in the arguments. Otherwise this argument is irrelevant.
+            
+        OUTPUT:
+            If 'element' and 'out' are of type 'str', this method return a new 'str' where all the patterns in 'to_replace' have been replaced simultaneously.
+            Otherwise, this method returns a new DinamicString.
+            
+            - Can raise an error if 'element' is not a 'str' neither a DinamicString.
+    '''
+    if(isinstance(element, DinamicString)):
+        return element.m_replace(to_replace, deep);
+    elif(type(element) == str):
+        real_replace = {};
+        to_dinam = [];
+        for k,v in to_replace.items():
+            if(isinstance(v, DinamicString)):
+                to_dinam += [v];
+                real_replace[k] = "_%d" %(len(to_dinam));
+            else:
+                real_replace[k] = v;
+        all_index = _din_m_indices(element, *real_replace.keys());
+        res = ""; current_index = 0;
+        for el in all_index:
+            res += element[current_index:el[0]] + real_replace[el[1]];
+            current_index = el[0]+len(el[1]);
+        res += element[current_index:];
+        
+        if(len(to_dinam) > 0):
+            return DinamicString(res, to_dinam);
+        else:
+            return res;
+    
+    raise TypeError("No string given. Impossible to replace multiple strings");
+
+def _din_indices(string, sep):
+    '''
+        Private method for this module. Return a list of indices where the string 'sep' appears on 'string'.
+        
+        This method relies in the fact that both 'string' and 'sep' are of type 'str'.
+    '''
+    try:
+        index = string.index(sep);
+        return [index] + [el+index+len(sep) for el in _din_indices(string[index+len(sep):],sep)];
+    except ValueError:
+        return [];
+
+def _din_m_indices(string, *seps):
+    '''
+        Private method for this module. Return a sorted list of pairs of (index, element) such
+        that the string 'element' starts in 'string[index]'. This method list all the apparitions 
+        of the strings in 'seps' within 'string'.
+        
+        
+        This method relies in the fact that both 'string' and the elements of 'seps' are of type 'str'.
+    '''
+    ## We assume no possible overlapping can occur between elements in seps
+    all_index = [];
+    for sep in seps:
+        all_index += [(el,sep) for el in _din_indices(string,sep)];
+    all_index.sort();
+    
+    return all_index;    
+    
+################################################################################
+################################################################################
+################################################################################
+### Module variables
+    
+__all__ = ["DinamicString", "din_replace", "din_m_replace"];
diff --git a/ajpastor/misc/euclidean.py b/ajpastor/misc/euclidean.py
new file mode 100644 (file)
index 0000000..2789dc5
--- /dev/null
@@ -0,0 +1,262 @@
+
+# This file was *autogenerated* from the file ./euclidean.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+
+####################################################################################################
+####################################################################################################
+###
+### Euclidean module
+###
+### ------------------------------------------------------------------------------------------------
+###
+### This file contains the code for some euclidean operations over for polynomials.
+### 
+### ------------------------------------------------------------------------------------------------
+###
+### Version: 0.0
+### Date of begining: 29-03-2017
+####################################################################################################
+####################################################################################################
+
+## Basic polynomial -- Private functions
+def __coefficients(PolR, p, var):
+    p = PolR(p);
+    if(not var in PolR.gens()):
+        raise ValueError("The variable given (%s) is not in the ring %s"%(var, PolR));
+        
+    var = PolR(var);
+    
+    try:
+        return [p.coefficient({var:i}) for i in range(p.degree(var)+_sage_const_1 )];
+    except TypeError:
+        return p.coefficients(sparse = False);
+        
+def __lc(PolR, p, var):
+    return __coefficients(PolR, p, var)[-_sage_const_1 ];
+    
+def __degree(PolR, p, var):
+    p = PolR(p);
+    if(not var in PolR.gens()):
+        raise ValueError("The variable given (%s) is not in the ring %s"%(var, PolR));
+        
+    var = PolR(var);
+    try:
+        return p.degree(var);
+    except TypeError:
+        return p.degree();
+        
+## Polynomial Division
+def PolyDivide(PolR, A,B):
+    A = PolR(A); B = PolR(B);
+    x = PolR.gens()[-_sage_const_1 ];
+    
+    Q = PolR.zero(); R = A;
+    d = __degree(PolR, R, x) - __degree(PolR, B, x);
+    while(R != PolR.zero() and d >= _sage_const_0 ):
+        T = (__lc(PolR, R, x)/__lc(PolR, B, x))*x**d;
+        Q += T;
+        R -= B*T;
+        d = __degree(PolR, R, x) - __degree(PolR, B, x);
+        
+    return (Q,R);
+    
+def PolyPseudoDivide(PolR, A, B):
+    A = PolR(A); B = PolR(B);
+    x = PolR.gens()[-_sage_const_1 ];
+    
+    b = __lc(PolR, B, x); N = __degree(PolR, A, x) - __degree(PolR, B, x)+_sage_const_1 ;
+    Q = PolR.zero(); R = A;
+    d = __degree(PolR, R, x) - __degree(PolR, B, x);
+    while(R != PolR.zero() and d >= _sage_const_0 ):
+        T = __lc(PolR, R, x)*x**d;
+        N -= _sage_const_1 ;
+        Q = b*Q + T;
+        R = b*R - T*B;
+        d = __degree(PolR, R, x) - __degree(PolR, B, x);
+        
+    return (b**N*Q, b**N*R);
+    
+## Euclidean Algorithms
+def Euclidean(PolR, a, b):
+    a = PolR(a); b = PolR(b);
+    while(b != PolR.zero()):
+        (q,r) = PolyDivide(PolR,a,b);
+        a = b;
+        b = r;
+    return a;
+    
+def ExtendedEuclidean_slow(PolR, a, b):
+    a = PolR(a); b = PolR(b);
+    a1 = PolR.one(); a2 = PolR.zero(); b1 = PolR.zero(); b2 = PolR.one();
+    
+    while(b != PolR.zero()):
+        (q,r) = PolyDivide(PolR,a,b);
+        a = b; b = r;
+        r1 = a1-q*b1; r2 = a2-q*b2;
+        a1 = b1; a2 = b2; b1 = r1; b2 = r2;
+        
+    return (a1,a2,a);
+    
+def HalfExtendedEuclidean(PolR, a, b):
+    a = PolR(a); b = PolR(b);
+    a1 = PolR.one(); b1 = PolR.zero();
+    
+    while(b != PolR.zero()):
+        (q,r) = PolyDivide(PolR,a,b);
+        a = b; b = r;
+        r1 = a1-q*b1;
+        a1 = b1; b1 = r1;
+        
+    return (a1,a);
+    
+def ExtendedEuclidean(PolR, a, b):
+    a = PolR(a); b = PolR(b);
+    (s,g) = HalfExtendedEuclidean(PolR, a,b);
+    (t,r) = PolyDivide(PolR, g-s*a,b);
+    
+    return (s,t,g);
+    
+def ExtendedEuclidean_diophantine_slow(PolR, a,b,c):
+    a = PolR(a); b = PolR(b); c = PolR(c);
+    x = PolR.gens()[-_sage_const_1 ];
+    
+    (s,t,g) = ExtendedEuclidean(PolR, a,b);
+    (q,r) = PolyDivide(PolR, c,g);
+    if(r != PolR.zero()):
+        raise ValueError("(%s) is not in the ideal generated by (%s) and (%s)" %(c,a,b));
+    s = q*s; t = t*q;
+    
+    if(s != PolR.zero() and (__degree(PolR, s, x) >= __degree(PolR, b, x))):
+        (q,r) = PolyDivide(PolR, s,b);
+        s = r;
+        t = t+q*a;
+    return (s,t);
+    
+def HalfExtendedEuclidean_diophantine(PolR, a,b,c):
+    a = PolR(a); b = PolR(b); c = PolR(c);
+    x = PolR.gens()[-_sage_const_1 ];
+    
+    (s,g) = HalfExtendedEuclidean(PolR, a,b);
+    (q,r) = PolyDivide(PolR, c,g);
+    if(r != PolR.zero()):
+        raise ValueError("(%s) is not in the ideal generated by (%s) and (%s)" %(c,a,b));
+    s = q*s;
+    
+    if(s != PolR.zero() and (__degree(PolR, s, x) >= __degree(PolR, b, x))):
+        (q,r) = PolyDivide(PolR, s,b);
+        s = r;
+    return s;
+    
+def ExtendedEuclidean_diophantine(PolR, a,b,c):
+    a = PolR(a); b = PolR(b); c = PolR(c);
+    s = HalfExtendedEuclidean_diophantine(PolR, a,b,c);
+    (t,r) = PolyDivide(PolR, c-s*a, b);
+    return (s,t);
+  
+## Partial Fractions  
+def PartialFraction_simple(PolR, a, d):
+    a = PolR(a); d = [PolR(el) for el in d];
+    if(len(d) == _sage_const_0 ):
+        return [a];
+    
+    d1 = prod(d); d2 = d1/d[_sage_const_0 ];
+    
+    (q,r) = PolyDivide(PolR, a,d1);
+    if(len(d) == _sage_const_1 ):
+        return [q,r];
+        
+    (a1,t) = ExtendedEuclidean_diophantine(PolR, d2, d[_sage_const_0 ],r);
+    aux = PartialFraction_simple(PolR, t,d[_sage_const_1 :])
+    
+    return [aux[_sage_const_0 ]+q, a1] + aux[_sage_const_1 :];
+    
+def PartialFraction(PolR, a, d, e = None):
+    a = PolR(a); d = [PolR(el) for el in d];
+    if(e is None):
+        e = [_sage_const_1  for i in range(len(d))];
+    else:
+        for i in range(len(e)):
+            e[i] = int(e[i]);
+            if(e[i] < _sage_const_0 ):
+                raise ValueError("No negative exponents allowed in the PartialFraction method. The %d-th exponent is negative." %i);
+                
+    if(len(d) != len(e)):
+        raise TypeError("PartialFraction need the same amount of exponents as denominators");
+        
+    dens = [d[i]**e[i] for i in range(len(d))];
+    pf = PartialFraction_simple(PolR, a, dens);
+    
+    res = [pf[_sage_const_0 ]];
+    for i in range(len(e)):
+        part_res = [];
+        for j in range(e[i],_sage_const_0 ,-_sage_const_1 ):
+            (q,aux) = PolyDivide(PolR, pf[i+_sage_const_1 ], d[i]);
+            pf[i+_sage_const_1 ] = q;
+            part_res += [aux];
+        part_res.reverse();
+        res[_sage_const_0 ] = res[_sage_const_0 ] + pf[i+_sage_const_1 ];
+        res += part_res;
+        
+    return res;
+    
+## Primitive part and content of polynomials
+def content(PolR, p):
+    p = PolR(p);
+    x = PolR.gens()[-_sage_const_1 ];
+    
+    coeffs = __coefficients(PolR, p, x);
+    return gcd(coeffs);
+    
+def pp_only(PolR, p):
+    p = PolR(p);
+    return p/content(PolR, p);
+    
+def pp(PolR, p):
+    c = content(PolR, p);
+    return (c, p/c);
+    
+## Squarefree factorization
+def Squarefree_Musser(PolR, A):
+    A = PolR(A);
+    x = PolR.gens()[-_sage_const_1 ];
+    
+    (c,S) = pp(PolR, A);
+    
+    S_minus = gcd(S, S.derivative(x));
+    S_star = PolR(S/S_minus);
+    res = []
+    while(__degree(PolR, S_minus, x) > _sage_const_0 ):
+        Y = gcd(S_star, S_minus);
+        res += [PolR(S_star/Y)];
+        S_star = Y;
+        S_minus = PolR(S_minus/Y);
+    res += [S_star];
+    
+    res[_sage_const_0 ] = res[_sage_const_0 ]*(c*S_minus);
+    return res;
+    
+def Squarefree_Yun(PolR, A):
+    A = PolR(A);
+    x = PolR.gens()[-_sage_const_1 ];
+    
+    (c,S) = pp(PolR, A);
+    S_prime = S.derivative(x);
+    S_minus = gcd(S,S_prime);
+    S_star = PolR(S/S_minus);
+    Y = PolR(S_prime/S_minus);
+    res = [];
+    Z = Y - S_star.derivative(x);
+    while(Z != _sage_const_0 ):
+        res += [gcd(S_star, Z)];
+        S_star = PolR(S_star/res[-_sage_const_1 ]);
+        Y = PolR(Z/res[-_sage_const_1 ]);
+        Z = Y - S_star.derivative(x);
+        
+    res += [S_star];
+    res[_sage_const_0 ] = res[_sage_const_0 ]*c;
+    
+    return res;
+    
+
diff --git a/ajpastor/misc/hermite.py b/ajpastor/misc/hermite.py
new file mode 100644 (file)
index 0000000..d1504b9
--- /dev/null
@@ -0,0 +1,261 @@
+
+# This file was *autogenerated* from the file ./hermite.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+from ajpastor.misc.euclidean import *;
+from ajpastor.misc.verbose import *;
+from ajpastor.misc.matrix import swap_cols as swap;
+
+####################################################################################
+###
+### Two Stages for Hermite Normal Form
+###
+####################################################################################
+@wverbose
+def stage1(A,i):
+    parent = A.parent().base();
+    sverbose("Initiating the loop...");
+    sverbose.start_iteration(i,False,False);
+    sverbose.increase_depth();
+    for j in range(i):
+        (p,q,r) = ExtendedEuclidean(parent, A[j][j],A[j][i]);
+        
+        sverbose("Computed the EEA: (%s)(%s) + (%s)(%s) = %s" %(p,A[j][j],q,A[j][i],r));
+        
+        new_rows = [[el for el in row] for row in A];
+        p2 = parent(-A[j][i]/r); q2 = parent(A[j][j]/r);
+        
+        sverbose("Column operation matrix:\n%s" %(Matrix(parent,[[p,p2],[q,q2]])));
+        for k in range(A.nrows()):
+            new_rows[k][j] = p*A[k][j] + q*A[k][i];
+            new_rows[k][i] = p2*A[k][j] + q2*A[k][i];
+        coeff = new_rows[j][j].leading_coefficient();
+        coeff2 = new_rows[j+_sage_const_1 ][i].leading_coefficient();
+        for k in range(A.nrows()):
+            new_rows[k][j] = new_rows[k][j]/coeff;
+            new_rows[k][i] = new_rows[k][i]/coeff2;
+        A = Matrix(parent,new_rows);
+        sverbose("New matrix computed:\n%s" %A);
+        sverbose.next_iteration();
+    sverbose.decrease_depth();
+    sverbose("Finished the loop");
+    return A;
+    
+@wverbose
+def stage2(Ao, A, i):
+    parent = Ao.parent().base();
+    m = i+_sage_const_1 ;
+    S = Matrix(parent,[[Ao[i][j] for j in range(m)] for i in range(m)]);
+    D = Matrix(parent,[([_sage_const_0  for j in range(l)] + [A[l][l]] + [_sage_const_0  for j in range(l+_sage_const_1 ,m)]) for l in range(m)]);
+    K = S * BackslashOperator() * D;
+    new_rows = [[el for el in row] + [_sage_const_0  for j in range(m,Ao.ncols())] for row in K];
+    for j in range(m,Ao.ncols()):
+        new_rows += [[_sage_const_0  for k in range(j)] + [_sage_const_1 ] + [_sage_const_0  for k in range(j+_sage_const_1 ,Ao.ncols())]];
+    K = Matrix(K.parent().base(), new_rows);
+    return Ao*K;
+    
+####################################################################################
+###
+### Rearrangement methods
+###
+####################################################################################
+@wverbose_w_data
+def index_n_from_m(n,m,subset):
+    '''
+    Let S(n,m) be the subset of the parts of {1,...,m} with n elements. It is well known that |S(n,m)| = binomial(m,n). Let A(n,m) = {1,2,...,binomial(m,m)}. Then there are bijections between S(n,m) and A(n,m).
+    
+    Consider f a bijection that orders the elements of S(n,m) in a lexicographic order. This method implements such map.
+    
+    INPUT:
+        - n: size fo the subsets we are considering
+        - m: number of elements we can take elements
+        - subset: an element of S(n,m).
+    '''
+    if(not (isinstance(subset,list) or not isinstance(subset,set))):
+        raise TypeError("The argument subset must be a set or a list of elements");
+        
+    subset = list(set(subset)); subset.sort();
+    
+    if(len(subset) != n):
+        raise TypeError("The argument subset must have %d elements, but it has %d" %(n,len(subset)));
+    
+    for el in subset:
+        if((not (isinstance(el,Integer) or type(el)==int)) or (el <= _sage_const_0 ) or (el > m)):
+            raise ValueError("The elements of subset must be integers from 1 to %d" %m);
+         
+    if(n == _sage_const_0 ):
+        return _sage_const_0 ;   
+    elif(n == _sage_const_1 ):
+        return subset[_sage_const_0 ];
+            
+    return sum(binomial(m-j,n-_sage_const_1 ) for j in range(_sage_const_1 ,subset[_sage_const_0 ])) + index_n_from_m(n-_sage_const_1 ,m-subset[_sage_const_0 ],[subset[i]-subset[_sage_const_0 ] for i in range(_sage_const_1 ,len(subset))]);
+    
+
+@wverbose_w_data
+def subset_n_from_m(n,m,i):
+    '''
+    Let S(n,m) be the subset of the parts of {1,...,m} with n elements. It is well known that |S(n,m)| = binomial(m,n). Let A(n,m) = {1,2,...,binomial(m,m)}. Then there are bijections between S(n,m) and A(n,m).
+    
+    Consider f a bijection that orders the elements of S(n,m) in a lexicographic order (see method index_n_from_m). This method inverts such method.
+    
+    INPUT:
+        - n: size fo the subsets we are considering
+        - m: number of elements we can take elements
+        - i: index of the subset we want to rebuild.
+    '''
+    if(n < _sage_const_1  or m < n):
+        raise TypeError("Imposible compute this: not valid configuration");
+    if(n == _sage_const_1 ):
+        sverbose("Base case (n = 1). Returning [%d]" %i);
+        return [i];
+    bin = binomial(m-_sage_const_1 ,n-_sage_const_1 )
+    current_sum = _sage_const_0 ;
+    index = _sage_const_1 ;
+    while(current_sum + bin < i):
+        index += _sage_const_1 ;
+        current_sum += bin;
+        bin = binomial(m-index,n-_sage_const_1 );
+        
+    sverbose("Index found: %d" %index);
+    
+    output = [index] + [el+index for el in subset_n_from_m(n-_sage_const_1 ,m-index,i-current_sum)];
+    sverbose("Output: %s" %output);
+    
+    return [index] + [el+index for el in subset_n_from_m(n-_sage_const_1 ,m-index,i-current_sum)];
+
+@wverbose_w_data
+def permutation_from_m(subset,m):
+    '''
+        Given a subset of {1,2,...,m} on the form [a_1<...<a_n], this method computes a permutation p in S_m such that for all i = 1,...,n
+            - p(a_i) = i.
+    '''
+    perm = [el for el in subset];
+    for j in range(_sage_const_1 ,m+_sage_const_1 ):
+        if(not j in perm):
+            perm += [j];
+    return Permutation(perm).inverse();
+    
+@wverbose_w_data
+def get_rearrange_from_minor(nrows,ncols,size,i):
+    '''
+        Given the size of a matrix, a size of a minor that is not zero and the index of such minor we compute the permutations that move rows and columns in order to have that minor in the left top corner of the matrix.
+    '''
+    bin = binomial(ncols,size);
+    for_rows = subset_n_from_m(size,nrows,ceil(i/bin));
+    
+    to_cols = i%bin;
+    if(to_cols == _sage_const_0 ):
+        to_cols = bin;
+    for_cols = subset_n_from_m(size,ncols,to_cols);
+    
+    return (permutation_from_m(for_rows,nrows),permutation_from_m(for_cols,ncols));
+    
+@wverbose
+def get_rearrange(M):
+    '''
+        Given a matrix M, this method computes the maximal non-zero minor (so, the rank of the matrix) and return the permutations needed to put that minor in the left top corner of the matrix.
+    '''
+    if(M.nrows() > M.ncols()):
+        (o,j) = get_rearrange(M.transpose());
+        return ((o[_sage_const_1 ],o[_sage_const_0 ]),j);
+    
+    done = False;
+    j = M.nrows()+_sage_const_1 ;
+    while((not done) and (j > _sage_const_1 )):
+        j -= _sage_const_1 ;
+        i = _sage_const_1 ;
+        minors = M.minors(j);
+        for el in minors:
+            if(el != _sage_const_0 ):
+                done = True;
+                break;
+            i += _sage_const_1 ;
+    
+    return (get_rearrange_from_minor(M.nrows(),M.ncols(),j,i), j);
+
+@wverbose
+def rearrange(M):
+    '''
+        Method that returns the same matrix M after changing rows and columns in order to have the rank defining minor in the top left corner of the matrix.
+    '''
+    (orders, size) = get_rearrange(M);
+    
+    return Matrix(M.parent().base(), [[M[orders[_sage_const_0 ](i+_sage_const_1 )-_sage_const_1 ][orders[_sage_const_1 ](j+_sage_const_1 )-_sage_const_1 ] for j in range(M.ncols())] for i in range(M.nrows())]), size;
+    
+    
+@wverbose
+def deep_rearrange(M):
+    '''
+        Method that returns the same matrix M after changing rows and columns in order to have all the possible principal minors non zero.
+    '''
+    M,rank = rearrange(M);
+    
+    nrows = M.nrows(); ncols = M.ncols();
+    for i in range(nrows):
+        minors = M.minors(i+_sage_const_1 );
+        j = i;
+        for j in range(i,ncols):
+            if(minors[j-i] != _sage_const_0 ):
+                break;
+        if(j != i):
+            M = swap(M,i+_sage_const_1 ,j+_sage_const_1 );
+        
+    return M;
+    
+####################################################################################
+###
+### Hermite Normal Form Method
+###
+####################################################################################  
+@wverbose 
+def HNF_square(A, check_argument = True):
+    '''
+    Method that computes the Hermite Normal Form of a non-singular square matrix.
+    
+    We can disable the checking for non-singularity changing the argument 'check_argument'.
+    '''
+    if(A.ncols() != A.nrows()):
+        raise TypeError("This method requires a square matrix");
+    
+    if(check_argument and A.determinant() == _sage_const_0 ):
+        raise ValueError("This method requires a non-singular matrix");
+        
+    sverbose("Matrix before rearrange:\n%s" %A);
+    A = deep_rearrange(A);
+    sverbose("Matrix after rearrange:\n%s" %A);
+        
+    current = Matrix(A.parent().base(), [[el for el in row] for row in A]);
+    sverbose.start_iteration(A.nrows()-_sage_const_1 , False, False);
+    for i in range(_sage_const_1 ,A.nrows()):
+        current = stage2(A,stage1(current,i),i);
+        sverbose.next_iteration();
+    
+    return current;
+        
+@wverbose
+def HNF(A):
+    if(A.ncols() < A.nrows()):
+        sverbose("Case with more rows than columns. Using the transpose case");
+        return HNF(A.transpose()).transpose();
+        
+    parent = A.parent().base();
+        
+    A,rank = rearrange(A);
+    sverbose("Matrix rearrange. The rank computed is %d" %rank);
+    new_rows = [[el for el in A[i]] for i in range(rank)];
+    for i in range(rank,A.ncols()):
+        new_rows += [[kronecker_delta(j,i) for j in range(A.ncols())]];
+        
+    B = HNF_square(Matrix(parent,new_rows));
+    sverbose("Computed the square Hermite Normal Form, we return the first %d rows" %rank);
+        
+    return Matrix(parent,[[el for el in B[i]] for i in range(rank)] + [[_sage_const_0  for j in range(B.ncols())] for i in range(rank,A.nrows())]);
+
+####################################################################################
+###
+### Package exports
+###
+#################################################################################### 
+#__all__ = ["HNF"];
+
diff --git a/ajpastor/misc/matrix.py b/ajpastor/misc/matrix.py
new file mode 100644 (file)
index 0000000..2c505d3
--- /dev/null
@@ -0,0 +1,371 @@
+
+# This file was *autogenerated* from the file ./matrix.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+
+from sage.matrix.matrix import Matrix as MatrixClass;
+
+####################################################################################
+###
+### MATRICES BUILDERS
+###
+### In this section we include some special constructor for matrices in order to 
+###     be able to build matrices with more flexibility than before.
+### The main functions here will be the diagonal_matrix and the block_matrix.
+###
+####################################################################################
+def block_matrix(parent, rows, constant_or_identity = True):
+    '''
+        Method that build a matrix using as blocks the elements of rows. The first step is check that dimensions matches for each row and column assuming that non-matrices elements can be extended to a matrix.
+        The way to extend that simple elements is marked by argument `constant_or_identity`: if it is False, then we put a constant matrix fullfilled with such element. Otherwise, we build a diagonal matrix with that element in the diagonal (i.e. it is the identity matrix times the element).
+    '''
+    ## we check that rows is a list of lists of the same size
+    d = len(rows[_sage_const_0 ]);
+    for i in range(_sage_const_1 , len(rows)):
+        if(d != len(rows[i])):
+            raise ValueError("Te rows provided can not be seen as a matrix");
+    
+    ## We check the sizes
+    rows_hights = [__check_row(row, parent) for row in rows];
+    cols_widths = [__check_col([rows[i][j] for i in range(len(rows))], parent) for j in range(len(rows[_sage_const_0 ]))];
+    
+    rows_with_matrix = [];
+    for i in range(len(rows)):
+        row_with_matrix = [];
+        for j in range(len(rows[_sage_const_0 ])):
+            if(rows[i][j] in parent):
+                if(constant_or_identity):
+                    row_with_matrix += [Matrix(parent, [[rows[i][j]*kronecker_delta(k,l) for l in range(cols_widths[j])] for k in range(rows_hights[i])])];
+                else:
+                    row_with_matrix += [Matrix(parent, [[rows[i][j] for l in range(cols_widths[j])] for k in range(rows_hights[i])])];
+            else:
+                row_with_matrix += [Matrix(parent, rows[i][j])];
+        rows_with_matrix += [row_with_matrix];
+    final_rows = [];
+    for i in range(len(rows_with_matrix)):
+        for j in range(rows_hights[i]):
+            final_rows += [reduce(lambda p,q : p+q, [list(el[j]) for el in rows_with_matrix[i]])];
+    return Matrix(parent, final_rows);
+    
+def diagonal_matrix(parent, *args, **kwds):
+    '''
+        Method that build a diagonal matrix in parent using the elements of args.
+        
+            - If args has more than 1 element, we take consider the list as input
+            - If args has one element and it it a list, we build a diagonal matrix using such elements
+            - If args has one element and it is not a list, we build a diagonal matrix using such element repeated `size` times.
+    '''
+    if(len(args) > _sage_const_1 ):
+        return diagonal_matrix(parent, args);
+    
+    if(len(args) == _sage_const_1  and (not (type(args[_sage_const_0 ]) == list))):
+        return diagonal_matrix(parent, [args[_sage_const_0 ] for i in range(kwds.get("size", _sage_const_1 ))]);
+        
+    list_of_elements = args[_sage_const_0 ];
+    d = len(list_of_elements);
+    rows = [];
+    for i in range(d):
+        rows += [[_sage_const_0  for j in range(i)] + [list_of_elements[i]] + [_sage_const_0  for j in range(i+_sage_const_1 ,d)]];
+        
+    return block_matrix(parent, rows);
+    
+def vandermonde_matrix(parent, *args, **kwds):
+    if(len(args) > _sage_const_1 ):
+        return vandermonde_matrix(parent, args, **kwds);
+    else:
+        casted = [parent(el) for el in args[_sage_const_0 ]];
+        
+    try:
+        ncols = kwds["size"];
+    except KeyError:
+        try:
+            ncols = kwds["ncols"];
+        except KeyError:
+            ncols = len(casted);
+        
+    rows = [[pow(casted[i], j) for j in range(ncols)] for i in range(len(casted))];
+    return Matrix(parent, rows);
+    
+def random_matrix(parent, *args, **kwds):
+    if(len(args) > _sage_const_1 ):
+        return random_matrix(parent, args, **kwds);
+    else:
+        casted = [int(el) for el in args[_sage_const_0 ]];
+        
+    try:
+        ncols = kwds["size"];
+        nrows = ncols;
+    except KeyError:
+        try:
+            ncols = kwds["ncols"];
+            nrows = kwds["nrows"];
+        except KeyError:
+            if(len(casted) > _sage_const_1 ):
+                nrows = casted[_sage_const_0 ];
+                ncols = casted[_sage_const_1 ];
+            else:
+                nrows = casted[_sage_const_0 ];
+                ncols = casted[_sage_const_1 ];
+            
+    try:    
+        min = kwds["min"];
+    except KeyError:
+        min = -sys.maxint-_sage_const_1 ;
+    try:
+        max = kwds["max"];
+    except KeyError:
+        max = sys.maxint;
+    
+    if(min > max):
+        raise ValueError("Can not create random numbers between %d and %d.\n\tReason: the minimal value is higher than the maximal value" %(min,max));
+    
+    return Matrix(parent, [[__rand(min, max) for i in range(ncols)] for j in range(nrows)]);
+    
+    
+### Auxiliary (and private) methods
+def __check_input(X, parent):
+    if(isinstance(X, MatrixClass) and parent.has_coerce_map_from(X.parent().base())):
+        return True;
+    elif(isinstance(X, list)):
+        try:
+            is_matrix = True;
+            d = len(X[_sage_const_0 ]);
+            i = _sage_const_1 ;
+            while(is_matrix and i < len(X)):
+                is_matrix = (is_matrix and d == len(X[i]));
+                i += _sage_const_1 ;
+                
+            if(is_matrix):
+                return True;
+        except AttributeError:
+            pass;
+    
+    return False;
+
+def __cast_input(X, parent):
+    if(__check_input(X,parent)):
+        return Matrix(parent, X);
+    
+    raise TypeError("The input %s can not be seen as a matrix of %s" %(X,parent));
+
+def __check_row(row,parent):
+    hight = None;
+    for el in row:
+        if(__check_input(el, parent)):
+            if(hight is None):
+                hight = __nrows(el);
+            else:
+                if(not (hight == __nrows(el))):
+                    raise ValueError("The row has not the proper format -- different size");
+        elif(not (el in parent)):
+            raise ValueError("The row has not the proper format -- non-castable element");
+    if(hight is None):
+        return _sage_const_1 ; 
+    return hight;
+
+def __check_col(col,parent):
+    width = None;
+    for el in col:
+        if(__check_input(el, parent)):
+            if(width is None):
+                width = __ncols(el);
+            else:
+                if(not (width == __ncols(el))):
+                    raise ValueError("The col has not the proper format -- different size");
+        elif(not (el in parent)):
+            raise ValueError("The col has not the proper format -- non-castable element");
+                               
+    if(width is None):
+        return _sage_const_1 ; 
+    return width;
+
+def __ncols(X):
+    try:
+        try:
+            return X.ncols();
+        except AttributeError:
+            return len(X[_sage_const_0 ]);
+    except Exception:
+        raise TypeError("Impossible to compute the number of columns for element %s" %X);
+        
+def __nrows(X):
+    try:
+        try:
+            return X.ncols();
+        except AttributeError:
+            return len(X);
+    except Exception:
+        raise TypeError("Impossible to compute the number of rows for element %s" %X);
+        
+def __rand(min, max):
+    return Integer(floor((random()*(max-min))+min))
+
+####################################################################################
+###
+### MATRICES OPERATIONS
+###
+### In this section we include some functionality about matrices as the row/column
+###     operations and the scalar product of some row/column.
+### These operations are essentially those that are used to perform a Gauss-Jordan
+###     algorithm over a Matrix. Such algorithm may be implemented in further 
+###     versions of this file.
+### We also include a simplify method that tries to simplify a matrix and, if some 
+###     element can not be simplified, then returns the original matrix.
+###
+####################################################################################
+def op_rows(M, s_r, d_r, el):
+    try:
+        try:
+            ## We make a copy of the matrix
+            new_rows = [];
+            for i in range(M.nrows()):
+                new_rows += [[M[i][j] for j in range(M.ncols())]];
+                
+            ## We make the current operations
+            for i in range(M.ncols()):
+                new_rows[d_r][i] = M[d_r][i]-el*M[s_r][i];
+                
+            return Matrix(M.parent().base(), new_rows);
+        except IndexError:
+            raise IndexError("Impossible access the rows %s or %s at matrix a matrix of dimension %dx%d" %(s_r, d_r, M.nrows(), M.ncols()));
+    except AttributeError:
+        raise TypeError("First argument must be a matrix. Given %s" %M);
+        
+def op_cols(M, s_c, d_c, el):
+    try:
+        try:
+            ## We make a copy of the matrix
+            new_rows = [];
+            for i in range(M.nrows()):
+                new_rows += [[M[i][j] for j in range(M.ncols())]];
+                
+            ## We make the current operations
+            for i in range(M.nrows()):
+                new_rows[i][d_c] = M[i][d_c]-el*M[i][s_c];
+                
+            return Matrix(M.parent().base(), new_rows);
+        except IndexError:
+            raise IndexError("Impossible access to position (%s,%s) at matrix" %(s_c, d_c));
+    except AttributeError:
+        raise TypeError("First argument must be a matrix. Given %s" %M);
+        
+def scal_row(M, d_r, el):
+    try:
+        try:
+            ## We make a copy of the matrix
+            new_rows = [];
+            for i in range(M.nrows()):
+                new_rows += [[M[i][j] for j in range(M.ncols())]];
+                
+            ## We make the current operations
+            for i in range(M.ncols()):
+                new_rows[d_r][i] = el*M[d_r][i];
+                
+            return Matrix(M.parent().base(), new_rows);
+        except IndexError:
+            raise IndexError("Impossible access to position (%s,%s) at matrix" %(s_c, d_c));
+    except AttributeError:
+        raise TypeError("First argument must be a matrix. Given %s" %M);
+        
+def scal_col(M, d_c, el):
+    try:
+        try:
+            ## We make a copy of the matrix
+            new_rows = [];
+            for i in range(M.nrows()):
+                new_rows += [[M[i][j] for j in range(M.ncols())]];
+                
+            ## We make the current operations
+            for i in range(M.nrows()):
+                new_rows[i][d_c] = el*M[i][d_c];
+                
+            return Matrix(M.parent().base(), new_rows);
+        except IndexError:
+            raise IndexError("Impossible access to position (%s,%s) at matrix" %(s_c, d_c));
+    except AttributeError:
+        raise TypeError("First argument must be a matrix. Given %s" %M);
+        
+def swap_rows(M, r1,r2):
+    try:
+        try:
+            ## We make a copy of the matrix
+            new_rows = [];
+            for i in range(M.nrows()):
+                new_rows += [[M[i][j] for j in range(M.ncols())]];
+                
+            ## We make the current operations
+            for i in range(M.ncols()):
+                new_rows[r1][i] = M[r2][i];
+                new_rows[r2][i] = M[r1][i];
+                
+            return Matrix(M.parent().base(), new_rows);
+        except IndexError:
+            raise IndexError("Impossible access to rows %s or %s at matrix of dimension %dx%d" %(r1, r2, M.nrows(),M.ncols()));
+    except AttributeError:
+        raise TypeError("First argument must be a matrix. Given %s" %M);
+        
+def swap_cols(M, c1,c2):
+    try:
+        try:
+            ## We make a copy of the matrix
+            new_rows = [];
+            for i in range(M.nrows()):
+                new_rows += [[M[i][j] for j in range(M.ncols())]];
+                
+            ## We make the current operation
+            for i in range(M.nrows()):
+                new_rows[i][c1] = M[i][c2];
+                new_rows[i][c2] = M[i][c1];
+                
+            return Matrix(M.parent().base(), new_rows);
+        except IndexError:
+            raise IndexError("Impossible access to rows %s or %s at matrix of dimension %dx%d" %(r1, r2, M.nrows(),M.ncols()));
+    except AttributeError:
+        raise TypeError("First argument must be a matrix. Given %s" %M);
+        
+def turn_matrix(M, vertical=False):
+    if(vertical):
+        new_rows = [[M[i][j] for j in range(M.ncols())] for i in range(-_sage_const_1 ,-M.nrows()-_sage_const_1 ,-_sage_const_1 )];
+    else:
+        new_rows = [[M[i][j] for j in range(-_sage_const_1 ,-M.ncols()-_sage_const_1 ,-_sage_const_1 )] for i in range(M.nrows())];
+        
+    return Matrix(M.parent().base(), new_rows);
+        
+def simplify_matrix(M):
+    try:
+        new_rows = [[M[i][j].simplify() for j in range(M.ncols())] for i in range(M.nrows())];
+        return Matrix(M.parent().base(), new_rows);
+    except AttributeError:
+        pass;
+    return M;
+    
+####################################################################################
+###
+### MATRICIAL D-MOVE
+###
+### In this section we include functionality to compute a differential movement 
+###     using a matrix.
+### A differential movement of v via M is the vector dm(v,M) = Mv + D(v) where D is
+###     a derivative over the domain where the coefficients of v lives.
+###
+####################################################################################
+def derivative_vector(v, D):
+    parent = v.parent().base();
+    
+    return vector(parent, [D(a) for a in v]);
+    
+def differential_movement(M, v, D):
+    w = M*v+derivative_vector(v,D);
+    return w;
+    
+def __dm(M,v,D):
+    return differential_movement(M,v,D);
+    
+def matrix_of_dMovement(M,v,D, cols):
+    res = [v];
+    for j in range(_sage_const_1 ,cols):
+        res += [__dm(M,res[-_sage_const_1 ],D)];
+    return Matrix(M.parent().base(), res).transpose();
+    
+
diff --git a/ajpastor/misc/nullspace_wrapper.py b/ajpastor/misc/nullspace_wrapper.py
new file mode 100644 (file)
index 0000000..7e57962
--- /dev/null
@@ -0,0 +1,158 @@
+
+# This file was *autogenerated* from the file ./nullspace_wrapper.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+from .matrix import swap_rows;
+from .matrix import swap_cols;
+
+from .cached_property import derived_property;
+
+from .verbose import *;
+
+from ore_algebra.nullspace import *;
+
+from sage.rings.polynomial.polynomial_ring import is_PolynomialRing as isUniPolynomial;
+from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing as isMPolynomial;
+
+class ElementInIdealFound(ValueError):
+    # Used for returning an exception with an element that is zero but is not the zero polynomial
+    def __init__(self, element):
+        self.__element = element;
+        print "ElementInIdealFound raised"
+    
+    def __str__(self):
+        return "A zero found: %s" %self.__element;
+        
+    def element(self):
+        return self.__element;
+
+class NullspaceAlgorithm(object):
+    ### Initialization method
+    def __init__(self, parent, M,  method=None, relations = []):
+        '''
+            This class represent the application of the Bareiss Algorithm over a matrix with polynomial coefficients.
+            This class receives several parameters:
+                - `parent`: the polynomial ring where the operations will be done. A fraction field is allowed if the base domain is a polynomial ring.
+                - `M`: the matrix we want to treat with the Algorithm
+                - `method`: a Boolean function
+                
+            This algorithm also works in a quotient of the polynomial ring (K[X]/I) and the method provided must be a method to check if some polynomial is in the ideal I.
+        '''
+        ## Checking the parent parameter
+        if(parent.is_field()):
+            parent = parent.base();
+        if(not (isUniPolynomial(parent) or isMPolynomial(parent))):
+            raise TypeError("The parent for this algorithm must be a polynomial ring.\n\t Got: %s" %parent);
+        if((not (parent.base() is QQ)) and (not(parent.base() is ZZ))):
+            raise TypeError("The parent for these algorithms must be based on rational numbers\n\t Got: %s" %parent);
+        self.__parent = PolynomialRing(ZZ,parent.gens());
+            
+        ## Checking the matrix input
+        self.base_matrix = Matrix(parent, M);
+        ## If the parent are the Rational numbers we clean denominators
+        ## Otherwise, the parent are the Integers, so we can go on.
+        if(parent.base() is QQ):
+            ## Cleaning rational denominators
+            new_rows = [];
+            for row in M:
+                aux_list = [];
+                for el in row:
+                    aux_list += [coeff.denominator() for coeff in el.coefficients()];
+                den_lcm = lcm(aux_list);
+                new_rows += [den_lcm*row];
+            M = new_rows;
+            self.__matrix = Matrix(self.parent(), M);
+        else:
+            self.__matrix = self.base_matrix;
+        
+        ## Storing the checking method
+        self.__in_ideal = method;
+        
+        ## Cached elements
+        self.__gb = ideal(self.parent(), relations).groebner_basis();        
+                
+    ### Getter methods
+    def parent(self):
+        '''
+            Method to have the same interface that the majority of objects of SAGE. Returns the polynomial ring where the matrix have its coefficients.
+        '''
+        return self.__parent;
+        
+    #################################################
+    ### Linear algebra methods
+    #################################################        
+    @derived_property
+    def rank(self):
+        return self.base_matrix.nrows()-self.right_kernel_matrix().ncols();
+                
+    def relations(self):
+        self.right_kernel_matrix();
+        return self.__gb;
+                
+    @cached_method
+    def right_kernel_matrix(self):
+        solver = self.__decide_solver();
+        
+        finished = False;
+        output = None;
+        while(not finished):
+            try:
+                output = solver(self.__matrix);
+                finished = True;
+            except ElementInIdealFound as e:
+                if(e.element().reduce(self.__gb) == _sage_const_0 ):
+                    raise RuntimeError("Same zero detected twice. Probable infinite loop");
+                self.__gb = ideal(self.parent(), self.__gb+[e.element()]).groebner_basis();
+                self.__matrix = self.__simplify_matrix(self.__matrix);
+        ## Putting the output in matrix form
+        rows = [[_sage_const_0  for i in range(len(output))] for j in range(self.__matrix.ncols())];
+        for i in range(len(output)):
+            for j in range(self.__matrix.ncols()):
+                rows[j][i] = output[i][j];
+        return Matrix(self.parent(), rows);
+        
+    #################################################
+    ### Other cached methods
+    #################################################
+    @cached_method
+    def is_in_ideal(self, p):
+        p = self.parent()(p);
+        
+        try:
+            return self.__in_ideal(p) is True;
+        except Exception:
+            return False;
+        
+    #################################################
+    ### Private methods for the Wrapper 
+    #################################################
+    @wverbose
+    def __pivot(self,M, r, n, c, m, zero):
+        # We delegate to the method _pivot of ore_algebra.nullspace
+        from ore_algebra.nullspace import _pivot;
+        (i,j) = _pivot(M,r,n,c,m,zero);
+        sverbose("A pivot found at position (%d,%d): %s" %(i,j,repr(M[i][j])));
+        if(self.is_in_ideal(M[i][j])):
+            sverbose("The pivot is zero");
+            raise ElementInIdealFound(M[i][j]);
+        
+        return (i,j);
+            
+    def __decide_solver(self):
+        return kronecker(gauss(lambda M,r,n,c,m,zero : self.__pivot(M,r,n,c,m,zero)));
+        
+    def __have_ideal(self, basis):
+        if(len(basis) == _sage_const_1 ):
+            return basis[_sage_const_0 ] != _sage_const_0 ;
+        return len(basis) > _sage_const_0 ;
+        
+    def __simplify_matrix(self, M):
+        rows = [[el for el in row] for row in M];
+        if(self.__have_ideal(self.__gb)):
+            rows = [[el.reduce(self.__gb) for el in row] for row in rows];
+        
+        return Matrix(self.parent(), rows);
+        
+        
+
diff --git a/ajpastor/misc/restore.py b/ajpastor/misc/restore.py
new file mode 100644 (file)
index 0000000..4cdd5b4
--- /dev/null
@@ -0,0 +1,128 @@
+##########################################
+###
+### RESTORING SESSION METHODS
+###
+##########################################
+from ajpastor.misc.verbose import *;
+
+## Data structure
+class __SystemState(object):
+    def __init__(self,global_dict, local_dict):
+        self.__globals = global_dict;
+        self.__locals = local_dict;
+        self.__exceptions = [];
+        
+    @wverbose
+    def add_exceptions(self,*input):
+        if(len(input) == 1 and isinstance(input[0],list)):
+            sverbose("Only a list gives as argument. Using it as parameter...");
+            input = input[0];
+        for element in input:
+            if(isinstance(element, str)):
+                if(element not in self.__exceptions):
+                    self.__exceptions += [element];
+                else:
+                    sverbose("A repeated element given as exception");
+            else:
+                sverbose("A non-string element given as exception. Skipping it...");
+    
+    @wverbose
+    def remove_exceptions(self,*input):
+        if(len(input) == 1 and isinstance(input[0],list)):
+            sverbose("Only a list gives as argument. Using it as parameter...");
+            input = input[0];
+        for element in input:
+            if(isinstance(element, str)):
+                if(element in self.__exceptions):
+                    self.__exceptions.remove(element);
+                else:
+                    sverbose("A non-existing element given as exception");
+            else:
+                sverbose("A non-string element given as non-exception. Skipping it...");
+                
+    def _restore_from_local(self,other_dict):
+        if(self.__locals is not None):
+            self.__restore_dictionary(other_dict, self.__locals);
+    
+    def _restore_from_global(self,other_dict):
+        self.__restore_dictionary(other_dict, self.__globals);
+        
+    def __restore_dictionary(self, target, source):
+        if(target is not None and source is not None):
+            if(isinstance(target, dict), isinstance(source, dict)):
+                ## Deleting the new variables (except those that are exceptions)
+                keys = [el for el in target];
+                for key in keys:
+                    if(not key in self.__exceptions):
+                        if(not key in source):
+                            del target[key];
+                ## Restoring the old variables (except those that are exceptions)
+                keys = [el for el in target];
+                for key in keys:
+                    if(not key in self.__exceptions):
+                        target[key] = source[key];
+                        
+    def __repr__(self):
+        n = 1;
+        if(self.__locals is not None):
+            n = 2;
+        return "Dictionary state with %d dictionaries" %n;
+        
+    def __str__(self):
+        res = repr(self);
+        if(len(self.__exceptions) > 0):
+            res += "\n  - Exceptions: %s" %self.__exceptions;
+        
+        return res;
+                
+                
+## Main functions
+def get_state(global_dict, local_dict = None):
+    '''
+        Method that save the current state of the program. It gets two dictionaries that are 
+        considered the global and the local variables.
+        
+        There are two recommended usages of this function:
+            - get_state(globals(),locals()): save the state of the global and the local variables
+            - get_state(globals()): only consider the global variables.
+            
+        Also other dictionaries can be given, and then they state will be saved.    
+    '''
+    ## Checking arguments
+    if(not isinstance(global_dict, dict)):
+        raise TypeError("The global argument must be a dictionary");
+    if((not (local_dict is None)) and (not isinstance(local_dict, dict))):
+        raise TypeError("The local argument must be None or a dictionary");
+    
+    ## Returning the saved state
+    local_copy = None;
+    if(local_dict is not None):
+        local_copy = local_dict.copy();
+    return __SystemState(global_dict.copy(), local_copy);
+    
+def restore_state(state, global_dict, local_dict=None):
+    '''
+        Restore a saved state to the dictionaries given as arguments. It considers the first 
+        (required) as global variables and the second (optional) as the local variables.
+        
+        There are two recommended usages of this function:
+            - restore_state(state,globals(),locals()): restore the state of the global and the local variables
+            - restore_state(state,globals()): only restore the global variables.
+            
+        Also other dictionaries can be given, and they will be restore to the state saved.
+    '''
+    ## Checking arguments
+    if(not isinstance(global_dict, dict)):
+        raise TypeError("The global argument must be a dictionary");
+    if((not (local_dict is None)) and (not isinstance(local_dict, dict))):
+        raise TypeError("The local argument must be None or a dictionary");
+    if(not isinstance(state, __SystemState)):
+        raise TypeError("The state argument must be a value obtained by 'get_state' method");
+    
+    if(local_dict is not None):
+        state._restore_from_local(local_dict);
+    state._restore_from_global(global_dict);
+        
+    
+## Package variables
+__all__ = ["get_state", "restore_state"];
diff --git a/ajpastor/misc/ring_w_sequence.py b/ajpastor/misc/ring_w_sequence.py
new file mode 100644 (file)
index 0000000..ebcdba4
--- /dev/null
@@ -0,0 +1,104 @@
+
+# This file was *autogenerated* from the file ./ring_w_sequence.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+
+########################################################
+### 
+
+from sage.rings.ring import Ring;
+
+class Ring_w_Sequence (UniqueRepresentation, Ring):
+    def __init__(self, base, method = None, category = None):
+        Ring.__init__(self, base, category=category);
+        self.__method = method;
+        
+    def sequence(self, el, n):
+        return self.getSequenceElement(el,n);
+        
+    def getSequenceElement(self, el, n):
+        if(el in self):
+            return self.__method(el,n);
+        raise TypeError("Can not compute a sequence for an element which is not in the ring");    
+
+class Wrap_w_Sequence_Ring (Ring_w_Sequence):
+    def __init__(self, base, method = None):
+        super(Wrap_w_Sequence_Ring, self).__init__(base, method=method, category=base.category());
+
+    def _coerce_map_from_(self, S):
+        return self.base()._coerce_map_from_(S);
+
+    def _has_coerce_map_from(self, S):
+        return self.base()._has_coerce_map_from(S);
+
+    def __call__(self, *args, **kwds):
+        return self.base()(*args, **kwds);
+    
+    def __getattribute__(self, name):
+        ## Avoiding infinite recursion
+        if(name == "base"):
+            return super(Ring_w_Sequence,self).__getattribute__(name);
+        
+        ## Now that base is not the attribute
+        try:
+            ## Trying to have the attribute f the base ring
+            attr = self.base().__getattribute__(name);
+            if(attr is None):
+                raise AttributeError;
+            return attr;
+        except AttributeError:
+            ## No attribute for the ring, searching for this ring
+            attr = super(Ring_w_Sequence, self).__getattribute__(name);
+            if(attr is None):
+                raise AttributeError;
+            return attr;
+            
+        raise AttributeError("'%s' object has no attribute '%'" %(self.__class__, name));
+        
+    def __repr__(self):
+        return "(SR)" + repr(self.base());
+
+    def __str__(self):
+        return "(SR)" + str(self.base());
+
+    def getSequenceElement(self, el, n):
+        self_gen = 'x';
+        try:
+            self_gen = str(self.base().gens()[-_sage_const_1 ]);
+        except:
+            pass;
+            
+        if(self_gen == '1'):
+            if(n > _sage_const_0 ):
+                return _sage_const_0 ;
+            elif(n==_sage_const_0 ):
+                return el;
+            else:
+                raise ValueError("Impossible get negative element of the sequence");
+        if(el in self):
+            res = el;
+            for i in range(n):
+                try:
+                    res = derivative(res,self(var(self_gen)));
+                except AttributeError:
+                    ## Not derivative possible. Considering a constant
+                    if(n == _sage_const_0 ):
+                        return res;
+                    return _sage_const_0 ;
+            try:
+                return res(**{self_gen:_sage_const_0 })/factorial(n);
+            except TypeError:
+                ## Not callable element. Returning the element without evaluation
+                return res/factorial(n);
+        else:
+            raise TypeError("Element not in `self` to compute the sequence.");
+#####################################################################
+
+def sequence(el, n):
+    R = el.parent();
+    if(not isinstance(R, Ring_w_Sequence)):
+        R = Wrap_w_Sequence_Ring(R);
+        
+    return R.getSequenceElement(el,n);
+
diff --git a/ajpastor/misc/storj_villard.py b/ajpastor/misc/storj_villard.py
new file mode 100644 (file)
index 0000000..1e6dd09
--- /dev/null
@@ -0,0 +1,250 @@
+
+# This file was *autogenerated* from the file ./storj_villard.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_100 = Integer(100); _sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+from .cached_property import derived_property;
+
+from sage.rings.polynomial.polynomial_ring import is_PolynomialRing as isUniPolynomial;
+
+class StorjVillardAlgorithm(object):
+    ### Initialization method
+    def __init__(self, parent, M):
+        '''
+            This class represent the application of the Storjohann-Villard algorithm (ISSAC 2005 paper) over a matrix with polynomial coefficients.
+            This class receives several parameters:
+                - `parent`: the polynomial ring where the operations will be done. A fraction field is allowed if the base domain is a polynomial ring.
+                - `M`: the matrix we want to treat with the Algorithm
+        '''
+        ## Checking the parent parameter
+        if(parent.is_field()):
+            parent = parent.base();
+        if(not (isUniPolynomial(parent))):
+            raise TypeError("The parent for this algorithm must be a univariate polynomial ring.\n\t Got: %s" %parent);
+        self.__parent = parent;
+        
+        self.__c_rank = None;
+        self.__c_nulls = None;
+            
+        ## Checking the matrix input
+        self.base_matrix = Matrix(self.parent(), M);
+        
+    ### Getter methods
+    def parent(self):
+        '''
+            Method to have the same interface that the majority of objects of SAGE. Returns the polynomial ring where the matrix have its coefficients.
+        '''
+        return self.__parent;
+        
+    #################################################
+    ### Linear algebra methods
+    ################################################# 
+    @derived_property
+    def rank(self):
+        if(self.__c_rank is None):
+            return self.__nullspace()[_sage_const_0 ];
+        return self.__c_rank;
+                
+    @cached_method
+    def right_kernel_matrix(self):
+        return self.symmetric_problem.left_kernel_matrix();
+        
+    @cached_method
+    def left_kernel_matrix(self):
+        if(self.__c_nulls is None):
+            return self.__nullspace()[_sage_const_1 ];
+        return self.__c_nulls;
+        
+    #################################################
+    ### Other cached methods
+    #################################################
+    @derived_property
+    def symmetric_problem(self):
+        return StorjVillardAlgorithm(self.parent(), M.transpose());
+    
+    #################################################
+    ### Private methods for Storjohann-Villard Algorithm 
+    #################################################
+    def __null_min_vector(self, M, thres):
+        '''
+            This method receives a matrix M and a degree threshold and computes some vectors in the 
+            left null-space of M.
+            
+            This requires that M has more rows than columns and a maximal rank.
+            
+            This operation use some random matrices.
+        '''
+        if(M.nrows() <= M.ncols()):
+            raise TypeError("Invalid matrix: more columns than rows");
+        n = M.ncols();
+        p = M.nrows()-n;
+        d = max([max([el.degree() for el in row]) for row in M]);
+          
+        ## M = QM for a random matrix Q  
+        Q = random_matrix(self.parent(), n+p, min=-_sage_const_100 ,max=_sage_const_100 );
+        M = Q*M;
+        ## M = M(x+x0) for a random x0
+        x0 = self.__rand_pol(_sage_const_0 );
+        M = Matrix(self.parent(), [[el(x=x+x0) for el in row] for row in M]); 
+        
+        ## Splitting the matrix into a square matrix and remaining rows
+        A = Matrix(self.parent(), [[M[i][j] for j in range(n)] for i in range(n)]);
+        B = Matrix(self.parent(), [[M[i][j] for j in range(n)] for i in range(n, n+p)]);
+        
+        ## Checking rank of M
+        if(A.determinant() == _sage_const_0 ):
+            raise ValueError("Failure in the algorithm. Probably rank(M) lower than require");
+            
+        ## Computing some degree bound
+        nu = thres+d+int(ceil(n*d/p));
+        
+        ## Computing a expansion of BA^-1 mod x^nu
+        H = raise NotImplementedError("Do not know how to implement this");
+        
+        ## Compressing the matrix H
+        Hp = H*Matrix(self.parent(), [[__rand_pol(d) for i in range(p)] for j in range(n)]);
+        t = [d-_sage_const_1  for i in range(p)] + [_sage_const_0  for i in range(p)];
+        
+        ## Compute a sigma-basis with respect to t for [-Ip Hp^T]^T of order nu
+        L = raise NotImplementedError("Do not know how to implement this");
+        
+        ## Computing kappa = #{rows of L such t-degree is at most thres}
+        ordered_rows = {};
+        kappa = _sage_const_0 
+        for row in L:
+            t_degree = max([row[i] - t[i] for i in range(len(t))]);
+            if(t_degree <= thres):
+                kappa += _sage_const_1 ;
+            
+            if(ordered_rows.get(t_degree) is None):
+                ordered_rows[t_degree] = [];
+            ordered_rows[t_degree] = ordered_rows[t_degree] + [row];
+        S = [];
+        for i in range(thres+_sage_const_1 ):
+            S += ordered_rows[i];
+        
+        ## Computing the partial reconstructions Ni
+        N = [el*block_matrix(self.parent(),H,_sage_const_1 ) for i in el];
+        N = [Matrix(self.parent(), [[el%((self.parent().gens()[_sage_const_0 ])**(thres+_sage_const_1 )) for el in row] for row in Ni]) for Ni in N];
+        N = [Matrix(self.parent(), [[el(x=x-x0) for el in row] for row in Ni]) for Ni in N];
+        N = [Ni*Q for Ni in N];
+        l = [Ni*M].count(_sage_const_0 );
+        
+        if(l != kappa):
+            raise ValueError("Failure in the algorithm. Error certifiating kappa");
+            
+        N = Matrix(self.parent(), N);
+        
+        lc_N = Matrix(self.parent().base(), [[el.leading_coefficient() for el in row] for row in N]);
+        if(lc_N.rank() != max(lc_N.nrows(), lc_N.ncols())):
+            raise ValueError("Failure in the algorithm. No minimality got");
+        
+        return kappa, N;
+        
+    def __nullspace2n(self, M):
+        '''
+            This method compute some `small` linearly independent polynomial vectors in the nullspace of a matrix M.
+            
+            Such matrix M must have more rows than columns, but no more than twice.
+            
+            Moreover, M must have maximal rank.
+        '''
+        if(M.nrows() <= M.ncols() or M.nrows() > _sage_const_2 *M.ncols()):
+            raise TypeError("Invalid matrix: invalid amount of rows with respect to columns");
+            
+        n = M.ncols();
+        q = M.nrows()-n;
+        d = max([max([el.degree() for el in row]) for row in M]);
+        
+        ## M = QM for a random matrix Q  
+        Q = random_matrix(self.parent(), n+q, min=-_sage_const_100 ,max=_sage_const_100 );
+        M = Q*M;
+        
+        ## Splitting the matrix into a square matrix and remaining rows
+        A = Matrix(self.parent(), [[M[i][j](x=self.__rand_pol(_sage_const_0 )) for j in range(n)] for i in range(n)]);
+        
+        if(A.determinant() == _sage_const_0 ):
+            raise ValueError("Failure in the algorithm. Non singular upper part of matrix");
+            
+        I = set();
+        p = q;
+        
+        res = [];
+        
+        while(len(I) < q):
+            ## Getting indices left
+            index_to_check = set([n+i for i in range(_sage_const_1 ,q+_sage_const_1 )])-I;
+            
+            ## Computing a degree bound
+            delta = _sage_const_2 *n*d/p;
+            
+            ## Building a matrix C
+            rows = [[_sage_const_0  for j in range(n+q)] for i in range(n+p)];
+            for i in range(n):
+                rows[i][i] = _sage_const_1 ;
+            for j in range(p):
+                rows[n+j][index_to_check[j]] = _sage_const_1 ;
+            C = Matrix(self.parent().base(), rows);
+            
+            ## Using C and M to use the null_min_vector method
+            M1 = C*M;
+            kappa, D = self.__null_min_vector(M1, delta);
+            N = Matrix(self.parent(),[Di*C for Di in D]);
+            
+            ## Getting new indices
+            J = raise NotImplementedError("Do not know how to implement this");
+            
+            ## Updating loop variables
+            I = I + set(J);
+            p = p - kappa;
+            
+            ## Nullspace update
+            res = block_matrix(self.parent(), res.transpose(), N.transpose()).transpose();
+            
+        return res*Q;
+        
+    def __nullspace(self):
+        '''
+        This method privately compute the left kernel of the matrix and its rank and stores the result in cached variables of the structure.
+        
+        This method returns both the rank and the minimal basis.
+        '''
+        m = self.base_matrix.nrows();
+        n = self.base_matrix.ncols();
+        d = max([max([el.degree() for el in row]) for row in M]);
+        
+        ## Computing a guess r0 for rank
+        r0 = raise NotImplementedError("Do not know how to implement this");
+        M1 = raise NotImplementedError("Do not know how to implement this");
+        
+        ## If the guess is too big, we can return an empty nullspace
+        if(m == r0):
+            self.__c_rank = m;
+            self.__c_nulls = [];
+            
+            return self.__c_rank, self.__c_nulls;
+            
+        ## Randomly ensure that the top r0xr0 submatrix of M1 is non-singular
+        raise NotImplementedError("Do not know how to implement this");
+        
+        s = int(ceil((m-_sage_const_2 *r0)/r0));
+        
+        ## Getting the 
+        N = [];
+        for k in range(s):
+            N += [row for row in self.__null_min_vector(raise NotImplementedError("Do not know how to implement this"), d)[_sage_const_1 ]];
+        N += [row for row in self.__nullspace2n(raise NotImplementedError("Do not know how to implement this"))];
+        
+        N = Matrix(self.parent(), N);
+        if(N*M != _sage_const_0 ):
+            raise ValueError("Failure in the algorithm. No kernel computed");
+            
+        self.__c_rank = r0;
+        self.__c_nulls = N;
+        
+        return self.__c_rank, self.__c_nulls;
+        
+        
+    def __rand_pol(self, degree):
+        return self.parent()([randint(-_sage_const_100 ,_sage_const_100 ) for i in range(randint(_sage_const_0 ,degree)+_sage_const_1 )]);
+
diff --git a/ajpastor/misc/timing.py b/ajpastor/misc/timing.py
new file mode 100644 (file)
index 0000000..f08df0b
--- /dev/null
@@ -0,0 +1,118 @@
+
+# This file was *autogenerated* from the file ./timing.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_3 = Integer(3); _sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0); _sage_const_60 = Integer(60); _sage_const_24 = Integer(24)
+
+    
+##########################################
+###
+### TIME OPERATIONS
+###
+##########################################
+from timeit import default_timer as timer;
+
+__GLOBAL_TIMER = None;
+__GENERAL_TIMER = None;
+def start_timer(reset = False):
+    global __GLOBAL_TIMER;
+    global __GENERAL_TIMER;
+    
+    if(reset):
+        __GENERAL_TIMER = timer();
+    
+    __GLOBAL_TIMER = timer();
+    
+def finish_timer(text = "an action"):    
+    global __GLOBAL_TIMER;
+    t = timer() - __GLOBAL_TIMER;
+    __print_timer(t, "Perfomed %s: it took" %(text));
+    
+def get_general_timing():    
+    global __GENERAL_TIMER;
+    t = timer() - __GENERAL_TIMER;
+    __print_timer(t, "General timing:");
+    
+def __print_timer(T, text):
+    END = "secs.";
+    if(T > _sage_const_60 ):
+        END = "mins.";
+        T = float(T/_sage_const_60 );
+        
+        if(T > _sage_const_60 ):
+            END = "h.";
+            T = float(T/_sage_const_60 );
+            
+            if(T > _sage_const_24 ):
+                END = "days"
+                T = float(T/_sage_const_24 );
+    print "%s %s %s" %(text, T, END);
+
+##########################################
+###
+### COMPARISON BETWEEN METHODS
+###
+##########################################  
+def __compare_methods(arguments, *args):
+    if(len(args) == _sage_const_1  and (type(args[_sage_const_0 ]) == list)):
+        args = args[_sage_const_0 ];
+        
+    res = [arguments];
+    for method in args:
+        try:
+            init = timer();
+            method(*arguments);
+            time = timer()-init;
+            res += [time];
+        except Exception as e:
+            print e
+            res += [-_sage_const_1 ];
+    return res;
+    
+def __compare_for_entries(entries, *args):
+    if(len(args) == _sage_const_0  and type(args[_sage_const_0 ]) == list):
+        args = args[_sage_const_0 ];
+    try:
+        res = [["Entry\Alg"] + [el.__name__ for el in args]];
+        for arguments in entries:
+            res += [__compare_methods(arguments, *args)];
+            
+        return res;
+    except AttributeError:
+        raise ValueError("The args must be a list of methods");
+             
+def __compute_global_stats(results):
+    try:
+        res = [["Stat\Alg"] + [el for el in results[_sage_const_0 ][_sage_const_1 :]]];
+        reverse = [[results[i][j] for i in range(len(results))] for j in range(len(results[_sage_const_0 ]))];
+        stats = [];
+        for i in range(_sage_const_1 , len(results[_sage_const_0 ])):
+            stats += [__compute_stats(reverse[i][_sage_const_1 :])];
+            
+        res += [["Mean"] + [stats[j][_sage_const_0 ] for j in range(len(stats))]];
+        res += [["Min"] + [stats[j][_sage_const_1 ] for j in range(len(stats))]];
+        res += [["Max"] + [stats[j][_sage_const_2 ] for j in range(len(stats))]];
+        res += [["N. Err"] + [stats[j][_sage_const_3 ] for j in range(len(stats))]];
+        
+        return res;
+    except (TypeError, IndexError):
+        raise TypeError("The input for compute stats must be a list of lists with at least one row");
+        
+def __compute_stats(results):
+    cMin = float('inf');
+    cMax = float('-inf');
+    cSum = _sage_const_0 ;
+    
+    for el in results:
+        cSum += el;
+        cMin = min(cMin, el);
+        cMax = max(cMax, el);
+    
+    return [cSum/len(results), cMin, cMax, results.count(-_sage_const_1 )];
+    
+def full_comparison(entries, *args):
+    if(len(args) == _sage_const_1  and (type(args[_sage_const_0 ]) == list)):
+        args = args[_sage_const_0 ];
+    
+    return __compute_global_stats(__compare_for_entries(entries, *args));
+
diff --git a/ajpastor/misc/verbose.py b/ajpastor/misc/verbose.py
new file mode 100644 (file)
index 0000000..35b214c
--- /dev/null
@@ -0,0 +1,416 @@
+
+# This file was *autogenerated* from the file ./verbose.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_100 = Integer(100); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+####################################################################
+###
+### verbose.sage
+###
+### Code for perform an easy conditional printing.
+###
+### Version 0.0 (12-07-2017)
+###
+### Version 1.0 (06-10-2017)
+###     - Changed the main system of verbose
+###     - Private class __verbose_session created
+###     - Visible instance sverbose created
+###     - To print with the sverbose session, use the call method ``sverbose(text)``
+###     - To change the configuration of the sverbose, call the public methods of the class
+###     - Added a decorator to easily set up a function to verbose
+###
+####################################################################
+
+import sys;
+from functools import wraps;
+
+class __verbose_session(object):
+    def __init__(self):
+        self.__time = True;
+        self.__current_depth = _sage_const_0 ;
+        self.__level = _sage_const_1 ;
+        self.__default_beg = "";
+        self.__begs = ["** ","## ","-- ","%% ","&& ","@@ "];
+        self.__end = "\n";
+        self.__deep_wrapper = _sage_const_0 ;
+        
+        self.__n_iterations = _sage_const_0 ;
+        self.__perc = False;
+        self.__inline = True;
+        self.__current_iteration = _sage_const_0 ;
+        
+    ## Basic call method
+    def __call__(self,string, end=None):
+        if(self.__level+self.__current_depth <= get_verbose()):
+            if(self.__time):
+                from datetime import datetime as time;
+                print "|",str(time.now()), "|",
+            for i in range(self.__current_depth):
+                print "\t",
+            if(self.__current_depth > _sage_const_0 ):
+                print self.__begs[(self.__current_depth-_sage_const_1 )%len(self.__begs)],
+            else:
+                print self.__default_beg,
+            
+            print string,
+            if(not(end is None)):
+                print end,
+            else:
+                print self.__end,
+                
+            sys.stdout.flush();
+        return;
+        
+    ## Changing level of verbose for the session
+    def get_level(self):
+        return self.__level;
+        
+    def set_level(self, level):
+        self.__level = level;
+        
+    ## Changing the time printing
+    def set_time(self, time):
+        self.__time = time;
+        
+    def get_time(self, time):
+        return self.__time;
+            
+    ## Changing depth methods
+    def reset_depth(self):
+        self.__current_depth = _sage_const_0 ;
+    
+    def change_depth(self,amount):
+        self.__current_depth = max(_sage_const_0 , self.__current_depth+amount);
+        
+    def increase_depth(self,amount = _sage_const_1 ):
+        if(amount < _sage_const_0 ):
+            raise ValueError("To increase the tab depth it is needed a positive number, not %d" %amount);
+        self.change_depth(amount);
+        
+    def decrease_depth(self,amount = _sage_const_1 ):
+        if(amount < _sage_const_0 ):
+            raise ValueError("To decrease the tab depth it is needed a positive number, not %d" %amount);
+        self.change_depth(-amount);
+        
+    ## Changing the wrapper behavior
+    def get_deep_wrapper(self):
+        return self.__deep_wrapper;
+        
+    def set_deep_wrapper(self, new_deep):
+        self.__deep_wrapper = new_deep;
+        
+    ## Changing default beginning
+    def get_default_beginning(self):
+        return self.__default_beginning;
+        
+    def set_default_beginning(self, beg):
+        self.__default_beginning = beg;
+        
+    ## Iteration helping system variables
+    def get_n_iterations(self):
+        return self.__n_iterations;
+    def set_n_iterations(self, n_iterations):
+        self.__n_iterations = n_iterations;
+        
+    def get_perc(self):
+        return self.__perc;
+    def set_perc(self, perc):
+        self.__perc = perc;
+        
+    def get_inline(self):
+        return self.__inline;
+    def set_inline(self, inline):
+        self.__inline = inline;
+        
+    def get_current_iteration(self):
+        return self.__current_iteration;
+    def set_current_iteration(self, current_iteration):
+        self.__current_iteration = current_iteration;
+        
+    def get_iteration_state(self):
+        return {'n': self.__n_iterations, 'perc' : self.__perc, 'inline' : self.__inline, 'current' : self.__current_iteration};
+        
+    def set_iteration_state(self, state):
+        try:
+            self.__n_iterations = state['n'];
+            self.__perc = state['perc'];
+            self.__inline = state['inline'];
+            self.__current_iteration = state['current'];
+        except KeyError:
+            pass;
+            
+    ## Iteration helping system
+    def start_iteration(self,iterations, percentage = False, in_line = True):
+        self.__n_iterations = iterations;
+        self.__perc = percentage;
+        self.__inline = in_line;
+        self.__current_iteration = _sage_const_1 ;
+        
+    def next_iteration(self):
+        msg = "Performed %d-th iteration " %self.__current_iteration;
+         
+        if(self.__perc and self.__n_iterations > _sage_const_0 ):
+            msg += "(%.2f %%)" %(self.__current_iteration*_sage_const_100 /self.__n_iterations);
+        else:
+            msg += "(%d/%d)" %(self.__current_iteration, self.__n_iterations);
+            
+        if(self.__inline and self.__current_iteration < self.__n_iterations):
+            end = "\r";
+        else:
+            end = None;
+            
+        self(msg, end);
+        self.__current_iteration += _sage_const_1 ;
+    
+## Conditional definition of the verbose variables
+sverbose = __verbose_session();
+
+class VerboseError(RuntimeError):
+    def __init__(self):
+        pass;
+
+#@wraps
+def wverbose(func):
+    ## Creating the wrapped function
+    def wrapped_function(*args, **kwds):
+        try:
+            sverbose.increase_depth(sverbose.get_deep_wrapper());
+            sverbose("Calling method %s" %(func.__name__));
+            state = sverbose.get_iteration_state();
+            sverbose.increase_depth();
+        except Exception:
+            raise VerboseError();
+            
+        e = None;
+        try:
+            output = func(*args,**kwds);
+        except Exception as e:
+            pass;
+        except KeyboardInterrupt as e:
+            pass;
+        sverbose.decrease_depth();
+        sverbose.set_iteration_state(state);
+        
+        if(not e is None):
+            sverbose("Exception raised in method %s" %(func.__name__));
+            sverbose.decrease_depth(sverbose.get_deep_wrapper());
+            raise e;
+        else:
+            sverbose("Finished method %s" %(func.__name__));
+            sverbose.decrease_depth(sverbose.get_deep_wrapper());
+            return output;
+        
+    ## Setting up the other attributes for the wrapped function
+    wrapped_function.__doc__ = getattr(func, '__doc__');
+    
+    return wrapped_function;
+    
+#@wraps
+def wverbose_w_data(func):
+    ## Creating the wrapped function
+    def wrapped_function(*args, **kwds):
+        try:
+            sverbose.increase_depth(sverbose.get_deep_wrapper());
+            sverbose("Calling method %s" %(func.__name__));
+            sverbose("Arguments:");
+            if(len(args) > _sage_const_0 ):
+                sverbose("%s" %(str(args)));
+            if(len(kwds)> _sage_const_0 ):
+                sverbose("%s" %(str(kwds)));
+            state = sverbose.get_iteration_state();
+            sverbose.increase_depth();
+        except Exception:
+            raise VerboseError();
+        
+        e = None;
+        try:
+            output = func(*args,**kwds);
+        except Exception as e:
+            pass;
+        except KeyboardInterrupt as e:
+            pass;
+        sverbose.decrease_depth();
+        sverbose.set_iteration_state(state);
+        
+        if(not e is None):
+            sverbose("Exception raised in method %s" %(func.__name__));
+            sverbose.decrease_depth(sverbose.get_deep_wrapper());
+            raise e;
+        else:
+            sverbose("Finished method %s" %(func.__name__));
+            sverbose("Output: %s" %(str(output)));
+            sverbose.decrease_depth(sverbose.get_deep_wrapper());
+            return output;
+        
+    ## Setting up the other attributes for the wrapped function
+    wrapped_function.__doc__ = getattr(func, '__doc__');
+    
+    return wrapped_function;
+
+__all__ = ["sverbose", "wverbose", "wverbose_w_data"];
+
+__VERBOSE_LEVEL = None;
+try:
+    set_verbose(get_verbose());
+except Exception:
+    __VERBOSE_LEVEL = _sage_const_0 ;
+    def set_verbose(level):
+        global __VERBOSE_LEVEL;
+        __VERBOSE_LEVEL = max(_sage_const_0 , level);
+        if(__VERBOSE_LEVEL >= _sage_const_1 ):
+            print "-- Changed verbose level to %d" %(__VERBOSE_LEVEL);
+    
+    def get_verbose():
+        global __VERBOSE_LEVEL;
+        return __VERBOSE_LEVEL;
+        
+    __all__ += ["set_verbose", "get_verbose"];
+        
+
+## Getters and setters of the verbose level
+#def set_verbose(level):
+#    global __VERBOSE_LEVEL;
+#    
+#    __VERBOSE_LEVEL = max(0, level);
+#    verbose("-- Changed verbose level to %d" %(__VERBOSE_LEVEL), 1);
+#    
+#def get_verbose():
+#    global __VERBOSE_LEVEL;
+#    return __VERBOSE_LEVEL;
+    
+## Main printing function
+#def verbose(text, level = 0, end = '\n', depth = 0, beg = '', time = False):
+#    global __VERBOSE_LEVEL;
+#    
+#    if(level <= __VERBOSE_LEVEL):
+#        if(time):
+#            from datetime import datetime as time;
+#            print str(time.now()), " --",
+#        for i in range(depth):
+#            print "\t",
+#        if(len(beg) > 0):
+#            print beg, " ",
+#        print text,
+#        print end,
+#        sys.stdout.flush();
+        
+## Other useful methods
+#__sv_active = False;
+#__sv_time = False;
+#__sv_current_depth = 0;
+#__sv_current_level = 0;
+#__sv_beg = "";
+#__sv_end = "\n";
+
+## Making smart verbose
+#def sverbose(text, end=None):
+#    global __sv_active;
+#    global __sv_current_depth;
+#    global __sv_current_level;
+#    global __sv_beg;
+#    global __sv_time;
+#    global __sv_end;
+#    if(end is None):
+#        tend = __sv_end;
+#    else:
+#        tend = end;
+#        
+#    if(__sv_active):
+#        verbose(text, __sv_current_level, end=tend, depth=__sv_current_depth, beg=__sv_beg, time=__sv_time);
+
+## Starting and finishing sessions
+#def start_smart_verbose(level, time=True):
+#    global __sv_active;
+#    global __sv_current_depth;
+#    global __sv_current_level;
+#    global __sv_time;
+#    
+#    if(__sv_active):
+#        verbose("A smart verbose session was already initiated. Reseting session...", 1, beg = "--");
+    
+#    __sv_active = True;
+#    __sv_time = time;
+#    __sv_current_depth = 0;
+#    __sv_current_level = level;
+#    
+#def end_smart_verbose():
+#    global __sv_active;
+#    
+#    __sv_active = False;
+    
+## Changing depth in smart session
+#def change_depth(amount):
+#    global __sv_active;
+#    global __sv_current_depth;
+#    
+#    if(__sv_active):
+#        __sv_current_depth = max(0, __sv_current_depth+amount);
+#
+#def increase_depth(amount = 1):
+#    if(amount <= 0):
+#        raise ValueError("To increase the tab depth it is needed a positive number, not %d" %amount);
+#    change_depth(amount);
+#    
+#def decrease_depth(amount = 1):
+#    if(amount <= 0):
+#        raise ValueError("To decrease the tab depth it is needed a positive number, not %d" %amount);
+#    change_depth(-amount);
+#    
+#def change_beginning(beg = ""):
+#    global __sv_beg;
+#    __sv_beg = beg;
+    
+## Printing iterations in smart session
+#__sv_iteration = False;
+#__sv_n_iterations = 0;
+#__sv_perc = False;
+#__sv_inline = True;
+#__sv_current_iteration = 0;
+#def start_iteration(iterations, percentage = False, in_line = True):
+#    global __sv_iteration;
+#    global __sv_n_iterations;
+#    global __sv_perc;
+#    global __sv_inline;
+#    global __sv_current_iteration;
+#    
+#    __sv_iteration = True;
+#    __sv_n_iterations = iterations;
+#    __sv_perc = percentage;
+#    __sv_inline = in_line;
+#    __sv_current_iteration = 1;
+#    
+#def next_iteration():
+#    global __sv_iteration;
+#    global __sv_n_iterations;
+#    global __sv_perc;
+#    global __sv_inline;
+#    global __sv_current_iteration;
+#    
+#    if(__sv_iteration):
+#        msg = "Performed %d-th iteration " %__sv_current_iteration;
+#        
+#        if(__sv_perc and __sv_n_iterations > 0):
+#            msg += "(%.2f %%)" %(__sv_current_iteration*100/__sv_n_iterations);
+#        else:
+#            msg += "(%d/%d)" %(__sv_current_iteration, __sv_n_iterations);
+#            
+#        if(__sv_inline):
+#            end = "\r";
+#        else:
+#            end = None;
+#            
+#        sverbose(msg, end);
+#        __sv_current_iteration += 1;
+#        
+#def end_iteration():
+#    global __sv_iteration;
+#    global __sv_inline;
+#    global __sv_current_level;
+#    global __VERBOSE_LEVEL;
+#    
+#    if(__sv_iteration):
+#        __sv_iteration = False;
+#        if(__sv_inline and __sv_current_level <= __VERBOSE_LEVEL):
+#            print "";
+   
+
diff --git a/ajpastor/operator/__init__.py b/ajpastor/operator/__init__.py
new file mode 100644 (file)
index 0000000..00cbfa0
--- /dev/null
@@ -0,0 +1,7 @@
+from .operator import Operator;
+from .oreOperator import w_OreOperator as OreOperator;
+from .directStepOperator import DirectStepOperator;
+from .polynomialLazyOperator import PolynomialLazyOperator;
+
+from pkgutil import extend_path;
+__path__ = extend_path(__path__, __name__);
diff --git a/ajpastor/operator/directStepOperator.py b/ajpastor/operator/directStepOperator.py
new file mode 100644 (file)
index 0000000..c99f50b
--- /dev/null
@@ -0,0 +1,90 @@
+
+# This file was *autogenerated* from the file ./directStepOperator.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+
+####################################################################################################
+####################################################################################################
+###
+### DirectStepOperator module
+###
+### ------------------------------------------------------------------------------------------------
+###
+### This file contains an extension of a TwoStepsOperator that computes the kernell of the appropiate matrix using standard SAGE algorithms.
+### ------------------------------------------------------------------------------------------------
+###
+### Version: 0.0
+### Date of begining: 05-05-2017
+###
+### Updated (21-08-2017)
+###     - Changed name parent to base
+###
+###
+### ------------------------------------------------------------------------------------------------
+### Dependencies:
+###     - TwoStepsOperator class
+####################################################################################################
+####################################################################################################
+
+# Local imports
+from .twoStepsOperator import TwoStepsOperator;
+from .operator import foo_derivative;
+
+class DirectStepOperator(TwoStepsOperator):
+
+    #######################################################
+    ### INIT METHOD AND GETTERS
+    #######################################################
+    def __init__(self, base, input, derivate = foo_derivative):
+        super(DirectStepOperator, self).__init__(base, input, derivate);
+            
+    ####################################################### 
+        
+    ####################################################### 
+    ### SOLVING MATRICES METHOD
+    ####################################################### 
+    def _get_element_nullspace(self, M):
+        from ajpastor.misc.bareiss import BareissAlgorithm;
+        ## We take the domain where our elements will lie
+        parent = M.parent().base().base();
+        
+        ## Computing the kernell of the matrix
+        try:
+            lcms = [lcm([el.denominator() for el in row]) for row in M];
+            N = Matrix(parent, [[el*lcms[i] for el in M[i]] for i in range(M.nrows())]);
+            ba = BareissAlgorithm(parent, N, lambda p : False);
+            
+            ker = ba.right_kernel_matrix();
+        except Exception as e:
+            print e
+            ker = M.right_kernel_matrix();
+        #ker = M.right_kernel_matrix();
+        ## If the nullspace has hight dimension, we try to reduce the final vector computing zeros at the end
+        aux = [row for row in ker];
+        i = _sage_const_1 ;
+        
+        while(len(aux) > _sage_const_1 ):
+            new = [];
+            current = None;
+            for j in range(len(aux)):
+                if(aux[j][-(i)] == _sage_const_0 ):
+                    new += [aux[j]];
+                elif(current is None):
+                    current = j;
+                else:
+                    new += [aux[current]*aux[j][-(i)] - aux[j]*aux[current][-(i)]];
+                    current = j;
+            aux = [el/gcd(el) for el in new];
+            i = i+_sage_const_1 ;
+
+            
+        ## When exiting the loop, aux has just one vector
+        sol = aux[_sage_const_0 ];
+        
+        ## Our solution has denominators. We clean them all
+        p = prod([el.denominator() for el in sol]);
+        return vector(parent, [el*p for el in sol]);
+    ####################################################### 
+    
+
diff --git a/ajpastor/operator/fullLazyOperator.py b/ajpastor/operator/fullLazyOperator.py
new file mode 100644 (file)
index 0000000..63c15ab
--- /dev/null
@@ -0,0 +1,232 @@
+
+# This file was *autogenerated* from the file ./fullLazyOperator.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0); _sage_const_5 = Integer(5)
+
+####################################################################################################
+####################################################################################################
+###
+### FullLazyOperator module
+###
+### ------------------------------------------------------------------------------------------------
+###
+### This file contains an extension of a TwoStepsOperator that computes the kernell of the appropiate matrix trnaslating the elements into polynomials and reversing the change once obtained the final vector.
+###
+### If some relations between the variables are known, Groebner basis reduction will be used.
+### ------------------------------------------------------------------------------------------------
+###
+### Version: 0.0
+### Date of begining: 07-02-2018
+###
+### ------------------------------------------------------------------------------------------------
+### Dependencies:
+###     - TwoStepsOperator class
+####################################################################################################
+####################################################################################################
+
+# Local imports
+from .twoStepsOperator import TwoStepsOperator;
+from .operator import foo_derivative;
+
+from ajpastor.lazy.lazyRing import LazyRing;
+
+from ajpastor.misc.bareiss import BareissAlgorithm;
+
+class FullLazyOperator(TwoStepsOperator):
+    ### Static parameters
+    _op_preference = _sage_const_5 ;
+
+    #######################################################
+    ### INIT METHOD AND GETTERS
+    #######################################################
+    def __init__(self, base, input, derivate = foo_derivative):
+        super(FullLazyOperator, self).__init__(base, input, derivate);
+        
+        self.__conversion = LazyRing(self.base(), self.base().base_ring());
+        self.__version = self.__conversion.version();
+        self.__companion = {};
+            
+    ####################################################### 
+    def companion(self, element=None):
+        R = self.__conversion;
+        if((not (element in self.__companion)) or (self.__version != R.version())):
+            self.__version = R.version();
+            if(element is None):
+                coefficients = [R(el) for el in self.getCoefficients()];    
+            else:
+                try:
+                    if(not element in self.base().original_ring()):
+                        raise TypeError("Composition with no polynomials is not allowed");
+                except AttributeError:
+                    if(not element in self.base().base_ring()):
+                        raise TypeError("Composition with no polynomials is not allowed");
+                ## TODO Fix this step to work for compositions
+                coefficients = [R(el(x=element)) for el in self.getCoefficients()];
+                                    
+            ## We divide by the leading coefficient
+            coefficients = [-(coefficients[i]/coefficients[-_sage_const_1 ]) for i in range(len(coefficients)-_sage_const_1 )];
+            ## Trying to reduce the elements
+            try:
+                for i in range(len(coefficients)):
+                    coefficients[i].reduce();
+            except AttributeError:
+                pass;
+            d = len(coefficients);
+            
+            ## Building the rows of our matrix
+            rows = [[_sage_const_0  for i in range(d-_sage_const_1 )] + [coefficients[_sage_const_0 ]]];
+            for i in range(d-_sage_const_1 ):
+                rows += [[kronecker_delta(i,j) for j in range(d-_sage_const_1 )] + [coefficients[i+_sage_const_1 ]]];
+                
+            ## Returning the matrix
+            self.__companion[element] = Matrix(R, rows);
+            
+        return self.__companion[element];
+        
+    ####################################################### 
+    ### GETTING MATRICES METHODS
+    ####################################################### 
+    def _get_matrix_add(self, other):
+        '''
+            Method to obtain a matrix such any element of the nullspace can be interpreted as a new operator that annihilates the sum (f+g) where f is solution to 'self=0' and g is solution to 'other=0'
+        '''        
+        from ajpastor.misc.matrix import diagonal_matrix as diagonal;
+        from ajpastor.misc.matrix import matrix_of_dMovement as move;
+        
+        f = self;
+        g = other;
+        
+        R = self.__conversion;
+                
+        ## Computing the companion matrices
+        Mf = f.companion();
+        Mg = g.companion();
+        
+        full_companion = diagonal(R, [Mf,Mg]);
+        init_vector = vector(R,([_sage_const_1 ] + [_sage_const_0  for i in range(self.getOrder()-_sage_const_1 )])+([_sage_const_1 ] + [_sage_const_0  for i in range(other.getOrder()-_sage_const_1 )]));
+        
+        return move(full_companion, init_vector, lambda p : p.derivative(), full_companion.ncols()+_sage_const_1 );
+        
+    def _get_matrix_mult(self, other):
+        '''
+            Method to obtain a matrix such any element of the nullspace can be interpreted as a new operator that annihilates the product (fg) where f is solution to 'self=0' and g is solution to 'other=0'
+        '''        
+        from ajpastor.misc.matrix import matrix_of_dMovement as move;
+        
+        f = self;
+        g = other;
+        
+        R = self.__conversion;
+                
+        ## Computing the companion matrices
+        Mf = f.companion();
+        Mg = g.companion();
+        
+        ## Computing Kroenecker sum of the matrices
+        full_companion = Mf.tensor_product(Matrix.identity(R, Mg.nrows())) + Matrix.identity(R, Mf.nrows()).tensor_product(Mg);
+        
+        init_vector = vector(R,([_sage_const_1 ] + [_sage_const_0  for i in range(f.getOrder()*g.getOrder()-_sage_const_1 )]));
+                
+        return move(full_companion, init_vector, lambda p : p.derivative(), full_companion.ncols()+_sage_const_1 );
+        
+    def _get_matrix_composition(self, other):
+        #raise TypeError("Composition not allowed *'FullLazyOperator level'*");
+        from ajpastor.misc.matrix import matrix_of_dMovement as move;
+    
+        g = other;
+        
+        Mf = self.companion(g);
+        
+        Mf,dg, parent = self._compose_companion(Mf,g);
+        
+        full_companion = dg*Mf;
+        init_vector = vector(parent, [_sage_const_1 ] + [_sage_const_0  for i in range(_sage_const_1 ,self.getOrder())]);
+        
+        return move(full_companion, init_vector, lambda p : p.derivative(), full_companion.ncols()+_sage_const_1 );
+    ####################################################### 
+    
+    ####################################################### 
+    ### SOLVING MATRICES METHOD
+    ####################################################### 
+    def _get_element_nullspace(self, M):
+        ## We take the Conversion System
+        R = self.__conversion;
+        
+        ## We assume the matrix is in the correct space
+        M = Matrix(R.poly_field(), [[R.simplify(el.poly()) for el in row] for row in M]);
+        
+        ## We clean denominators to lie in the polynomial ring
+        lcms = [self.__get_lcm([el.denominator() for el in row]) for row in M];
+        M = Matrix(R.poly_ring(), [[el*lcms[i] for el in M[i]] for i in range(M.nrows())]);
+        f = lambda p: R(p).is_zero();
+        try:
+            assert(f(_sage_const_1 ) == False);
+        except Exception:
+            raise ValueError("The method to check membership is not correct");
+
+        ## Computing the kernell of the matrix
+        if(len(R.map_of_vars()) > _sage_const_0 ):
+            bareiss_algorithm = BareissAlgorithm(R.poly_ring(),M,f,R._ConversionSystem__relations);
+            ker = bareiss_algorithm.right_kernel_matrix();
+            ## If some relations are found during this process, we add it to the conversion system
+            R.add_relations(bareiss_algorithm.relations());
+        else:
+            ker = [v for v in M.right_kernel_matrix()];
+                
+        ## If the nullspace has high dimension, we try to reduce the final vector computing zeros at the end
+        aux = [row for row in ker];
+        i = _sage_const_1 ;
+        
+        while(len(aux) > _sage_const_1 ):
+            new = [];
+            current = None;
+            for j in range(len(aux)):
+                if(aux[j][-(i)] == _sage_const_0 ):
+                    new += [aux[j]];
+                elif(current is None):
+                    current = j;
+                else:
+                    new += [aux[current]*aux[j][-(i)] - aux[j]*aux[current][-(i)]];
+                    current = j;
+            aux = [el/gcd(el) for el in new];
+            i = i+_sage_const_1 ;
+
+            
+        ## When exiting the loop, aux has just one vector
+        sol = aux[_sage_const_0 ];
+        sol = [R.simplify(a) for a in sol];
+        
+        ## Just to be sure everything is as simple as possible, we divide again by the gcd of all our
+        ## coefficients and simplify them using the relations known.
+        fin_gcd = gcd(sol);
+        finalSolution = [a/fin_gcd for a in sol];
+        finalSolution = [R.simplify(a) for a in finalSolution];
+        
+        ## We transform our polynomial into elements of our destiny domain
+        realSolution = [R.to_real(a) for a in finalSolution];
+        for i in range(len(realSolution)):
+            try:
+                realSolution[i].change_built("polynomial", (finalSolution[i], {str(key): R.map_of_vars()[str(key)] for key in finalSolution[i].variables()}));
+            except AttributeError:
+                pass;
+        
+        return vector(R.base(), realSolution);
+        
+    def __get_lcm(self, input):
+        try:
+            return lcm(input);
+        except AttributeError:
+            ## No lcm for this class, implementing a general lcm
+            try:
+                ## Relying on gcd
+                p = self.__conversion.poly_ring();
+                res = p(_sage_const_1 );
+                for el in input:
+                    res = p((res*el)/gcd(res,el));
+                return res;
+            except AttributeError:
+                ## Returning the product of everything
+                return prod(input);
+    ####################################################### 
+
diff --git a/ajpastor/operator/lazyStepOperator.py b/ajpastor/operator/lazyStepOperator.py
new file mode 100644 (file)
index 0000000..02c923a
--- /dev/null
@@ -0,0 +1,74 @@
+
+# This file was *autogenerated* from the file ./lazyStepOperator.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_3 = Integer(3); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+
+####################################################################################################
+####################################################################################################
+###
+### LazyStepOperator module
+###
+### ------------------------------------------------------------------------------------------------
+###
+### This file contains an extension of a TwoStepsOperator that computes the companion matrix in a lazy field, so the computations of the matrix are not directly done.
+### ------------------------------------------------------------------------------------------------
+###
+### Version: 0.0
+### Date of begining: 05-05-2017
+###
+### Updated (21-08-2017)
+###     - Changed name parent to base
+###
+###
+### ------------------------------------------------------------------------------------------------
+### Dependencies:
+###     - TwoStepsOperator class
+####################################################################################################
+####################################################################################################
+
+# Imports
+from .twoStepsOperator import TwoStepsOperator;
+from .operator import foo_derivative;
+
+from ajpastor.lazy.lazyIDElements import *;
+
+class LazyStepOperator(TwoStepsOperator):
+    ### Static parameters
+    _op_preference = _sage_const_3 ;
+
+    #######################################################
+    ### INIT METHOD AND GETTERS
+    #######################################################
+    def __init__(self, base, input, derivate = foo_derivative):
+        super(LazyStepOperator, self).__init__(base, input, derivate);
+            
+    ####################################################### 
+        
+    @cached_method
+    def companion(self):
+        field = LazyIntegralDomain(self._original_base).fraction_field();
+        
+        coefficients = [field(el) for el in self.getCoefficients()];
+            
+        ## We divide by the leading coefficient
+        coefficients = [-(coefficients[i]/coefficients[-_sage_const_1 ]) for i in range(len(coefficients)-_sage_const_1 )];
+        ## Trying to reduce the elements
+        try:
+            for i in range(len(coefficients)):
+                coefficients[i].reduce();
+        except AttributeError:
+            pass;
+        except ArithmeticError:
+            pass;
+        d = len(coefficients);
+        
+        ## Building the rows of our matrix
+        rows = [[_sage_const_0  for i in range(d-_sage_const_1 )] + [coefficients[_sage_const_0 ]]];
+        for i in range(d-_sage_const_1 ):
+            rows += [[kronecker_delta(i,j) for j in range(d-_sage_const_1 )] + [coefficients[i+_sage_const_1 ]]];
+            
+        ## Returning the matrix
+        return Matrix(field, rows);
+    
+
diff --git a/ajpastor/operator/listOperator.py b/ajpastor/operator/listOperator.py
new file mode 100644 (file)
index 0000000..78b0cb4
--- /dev/null
@@ -0,0 +1,235 @@
+
+# This file was *autogenerated* from the file ./listOperator.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_3 = Integer(3); _sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+
+####################################################################################################
+####################################################################################################
+###
+### ListOperator module
+###
+### ------------------------------------------------------------------------------------------------
+###
+### This file contains a simple implementation of the abstract class Operator needed to the some 
+### of the calculations for DD-Rings and DD-Functions
+### ------------------------------------------------------------------------------------------------
+###
+### Version: 2.0
+### Date of begining: 21-11-2016
+###
+### Update (05-05-2017):
+###     - Deleted method getCoefficientParent (now the Operator method parent() do the same)
+###     - Deleted attributes "parent" and "__derivate" (the Operator methods parent() and derivate() do the same)
+###     - Added the option of initialize a FooOperator from another operator.
+###     - Maded the imports more precise
+###     - Rearrange file: more readable structure
+###     - Added the methods __compute_add_solution, __compute_mult_solution, __compute_derivative_solution and __compute_integral_solution.
+###     - Changed name: method "isZero" to "is_zero"
+###     - Changed name of class FooOperator to ListOperator
+###
+### Updated (21-08-2017)
+###     - Changed name parent to base
+###
+### ------------------------------------------------------------------------------------------------
+### Dependencies:
+###     - Operator class
+####################################################################################################
+####################################################################################################
+
+# Local imports
+from .operator import Operator;
+from .operator import foo_derivative;
+from ajpastor.misc.ring_w_sequence import Wrap_w_Sequence_Ring;
+
+from sage.rings.polynomial.polynomial_ring import is_PolynomialRing as isPolynomial;
+
+class ListOperator(Operator):
+    ### Static parameters
+    _op_preference = _sage_const_2 ;
+
+    #######################################################
+    ### INIT METHOD AND GETTERS
+    #######################################################
+    def __init__(self, base, input, derivate = foo_derivative):
+        '''
+        This method allows the user to instantiate a new object of type Operator. 
+        
+        This method must be extended in each child-class of Operator.
+        
+        INPUT:
+            - base: the Structure where the coefficients of the operator will be
+            - input: the input data for the operator. The format must be checked in each class.
+        '''
+        ## Initializing the minimal structure
+        super(ListOperator, self).__init__(base, input, derivate);
+                
+        if (isinstance(input, list)):
+            if(len(input) == _sage_const_0 ):
+                self.__coefficients = [_sage_const_1 ];
+            else:
+                # Getting the las non-zero position
+                i = len(input);
+                while(input[i-_sage_const_1 ] == _sage_const_0  and i > _sage_const_0 ):
+                    i -= _sage_const_1 ; 
+                try:
+                    if(i == _sage_const_0 ):
+                        self.__coefficients = [self.base().one()];
+                    else:
+                        self.__coefficients = [self.base()(input[j]) for j in range(i)];
+                except Exception as e:
+                    raise TypeError('The input (%s) is not valid for a ListOperator with coefficients in (%s)' %(input,format(base)));
+        elif(isinstance(input, Operator)):
+            self.__coefficients = [self.base()(coeff) for coeff in input.getCoefficients()];
+        else:
+            try:
+                self.__coefficients = [self.base()(input)];
+            except Exception:
+                raise TypeError('The input is not valid for an operator with coefficients in (%s)' %(format(base)));
+        #coeff_gcd = gcd(self.__coefficients);
+        #if(coeff_gcd != 0):
+        #    self.__coefficients = [self.base()(el/coeff_gcd) for el in self.__coefficients];
+        #if(isinstance(self.base(), Wrap_w_Sequence_Ring) and isPolynomial(self.base().base())):
+        #    l = []
+        #    for el in self.__coefficients:
+        #        l += el.coefficients(x);
+        #    
+        #    coeff_gcd = gcd(l);
+        #    if(coeff_gcd != 0):
+        #        self.__coefficients = [el/coeff_gcd for el in self.__coefficients];
+            
+    ### Getter methods
+    def getOrder(self):
+        return len(self.__coefficients)-_sage_const_1 ;
+        
+    def getCoefficients(self):
+        return self.__coefficients;
+    
+    def getCoefficient(self, i):
+        if (i < _sage_const_0 ):
+            raise IndexError('The argument must be a number greater or equal to zero');
+        elif (i < len(self.__coefficients)):
+            return self.__coefficients[i];
+        
+        return _sage_const_0 ;
+    #######################################################
+        
+    #######################################################
+    ### OPERATOR ARITHMETIC METHODS (ABSTRACT)
+    ####################################################### 
+    ### Arithmetic
+    def add(self, other):
+        if(isinstance(other, ListOperator)):
+            if(self.is_zero()):
+                return other;
+            if(other.is_zero()):
+                return self;
+            return self.__class__(self.base(), 
+                [self.getCoefficient(i)+self.base()(other.getCoefficient(i)) for i in range(max(self.getOrder(), other.getOrder())+_sage_const_1 )], 
+                self.derivate());
+        elif(isinstance(other, Operator)):
+            return other.__radd__(self);
+        else:
+            return self.add(self.__class__(self.base(),other, self.derivate()));
+        
+    def scalar(self, other):
+        try:
+            r = self.base()(other);
+            if(r == _sage_const_0 ):
+                return self.__class__(self.base(), _sage_const_0 , self.derivate());
+            return self.__class__(self.base(), [r*coeff for coeff in self.getCoefficients()], self.derivate());
+        except TypeError:
+            raise TypeError("The argument for this method must be an element of the current domain");
+        
+    def mult(self, other):
+        try:
+            if(isinstance(other, ListOperator)):
+                if(self.is_zero() or other.is_zero()):
+                    self.__class__(self.base(), _sage_const_0 , self.derivate()); 
+                res = self.__class__(self.base(), _sage_const_0 , self.derivate());
+                aux = None;
+                for coeff in self.getCoefficients():
+                    if(aux is None):
+                        aux = other;
+                    else:
+                        aux = aux.derivative();
+                    res = res.add(aux.scalar(coeff));
+                return res;
+            elif(isinstance(other, Operator)):
+                return other.__rmul__(self);
+            else:
+                return self.mult(self.__class__(self.base(),other, self.derivate()));
+        except Exception as e:
+            print "Got an exception: %s"%(e);
+            raise e;
+    
+    ### Equality
+    def is_zero(self):
+        for coeff in self.getCoefficients():
+            if not (coeff == _sage_const_0 ):
+                return False;
+        return True;
+        
+    def __eq__(self, other):
+        if(isinstance(other, ListOperator)):
+            selfCoeffs = self.getCoefficients();
+            otherCoeffs = other.getCoefficients();
+            
+            if(len(selfCoeffs) == len(otherCoeffs)):
+                for i in range(len(selfCoeffs)):
+                    if(not (selfCoeffs[i] == otherCoeffs[i])):
+                        return False;
+                return True;
+        return False;
+        
+    ### Differential
+    def derivative(self):
+        newCoeffs = [self.derivate()(self.getCoefficient(_sage_const_0 ))];
+        for j in range(_sage_const_1 ,self.getOrder()+_sage_const_1 ):
+            newCoeffs += [self.derivate()(self.getCoefficient(j)) + self.getCoefficient(j-_sage_const_1 )];
+        newCoeffs += [self.getCoefficient(self.getOrder())];
+        
+        return self.__class__(self.base(), newCoeffs, self.derivate());
+    ####################################################### 
+    
+    ####################################################### 
+    ### SOLUTION ARITHMETHIC METHODS (ABSTRACT)
+    ####################################################### 
+    def _compute_derivative_solution(self):
+        r = self.getCoefficients();
+        ### The operation depends on the first coefficient of the equation
+        if(r[_sage_const_0 ] == _sage_const_0 ):
+            ### If is zero, then the next derivative has the same coefficients shifted 1 position to the left.
+            newCoeffs = [r[i] for i in range(_sage_const_1 ,len(r))];
+        else:
+            ### Otherwise, we compute another operator
+            der0 = self.derivate()(r[_sage_const_0 ]);
+            newCoeffs = [r[i]*r[_sage_const_0 ] + self.derivate()(r[i+_sage_const_1 ])*r[_sage_const_0 ]-der0*r[i+_sage_const_1 ] for i in range(self.getOrder())];
+            newCoeffs += [r[_sage_const_0 ]*r[-_sage_const_1 ]];
+            
+        return self.__class__(self.base(), newCoeffs, self.derivate());
+            
+    def _compute_integral_solution(self):
+        return self.__class__(self.base(), [_sage_const_0 ] + self.getCoefficients(), self.derivate());
+    ####################################################### 
+    
+    ####################################################### 
+    ### MAGIC PYTHON METHODS
+    ####################################################### 
+    def __call__(self, obj):
+        try:
+            obj = self.base()(obj);
+        except Exception:
+            verbose("The object %s can not be casted into an element of the coefficient ring" %(obj), level=_sage_const_3 );
+        res = _sage_const_0 ;
+        for coeff in self.getCoefficients():
+            res += coeff*obj;
+            obj = self.derivate()(obj);
+        return res;
+        
+    def __repr__(self):
+        return ("%s(%s)"%(self.__class__.__name__,self.__coefficients.__repr__()));
+        
+    #######################################################         
+    
+
diff --git a/ajpastor/operator/operator.py b/ajpastor/operator/operator.py
new file mode 100644 (file)
index 0000000..73ac3b6
--- /dev/null
@@ -0,0 +1,516 @@
+
+# This file was *autogenerated* from the file ./operator.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+
+####################################################################################################
+####################################################################################################
+###
+### Operator module
+###
+### ------------------------------------------------------------------------------------------------
+###
+### This file contains the code for the ABSTRACT class operator, that will be a helpful tool to the 
+### implementation of DD-Rings and DD-Functions.
+### 
+### ------------------------------------------------------------------------------------------------
+###
+### Version: 2.0
+### Date of begining: 21-11-2016
+###
+### Updated (28-04-2017)
+###     - Added forward and backward polynomial
+###     - Added the computation of the jp_value and jp_matrix
+###     - Added a cache functionality to high cost elements
+###
+### Updated (05-04-2017)
+###     - Added the Ring_w_Sequence to this file
+###     - Deleted the method _get_sequence_element
+###     - Added cached methods "parent" and "derivate"
+###     - Deleted the method getCoefficientsParent (now self.parent() do the same)
+###     - Rearrange file: more readable structure
+###     - Added the methods add_solution, mult_solution, derivative_solution and integral_solution.
+###     - Added the methods __compute_add_solution, __compute_mult_solution, __compute_derivative_solution and __compute_integral_solution.
+###     - Added a preference system for casting operators (see methods *_solution and get_preference 
+###     - Changed name: method "isZero" to "is_zero"
+###
+### Updated (21-08-2017)
+###     - Changed name parent to base
+###
+####################################################################################################
+####################################################################################################
+
+from ajpastor.misc.cached_property import derived_property;
+from ajpastor.misc.ring_w_sequence import Ring_w_Sequence;
+from ajpastor.misc.ring_w_sequence import Wrap_w_Sequence_Ring;
+
+## GENERIC UTILITIES METHODS
+def foo_derivative(p):
+    try:
+        return p.derivative(x);
+    except AttributeError:
+        return _sage_const_0 ;
+
+## Operator class
+class Operator(object):
+    ### Static parameters
+    _op_preference = _sage_const_0 ;
+
+    #######################################################
+    ### INIT METHOD AND GETTERS
+    #######################################################
+    def __init__(self, base, input, derivate = foo_derivative):
+        '''
+        This method allows the user to instantiate a new object of type Operator. 
+        
+        This method must be extended in each child-class of Operator.
+        
+        INPUT:
+            - base: the Structure where the coefficients of the operator will be
+            - input: the input data for the operator. The format must be checked in each class.
+        '''
+        
+        ## Computing the polynomial associated ring
+        try:
+            self.__pol_var = 'n';
+            i = -_sage_const_1 ;
+            names = [str(gen) for gen in base.base_ring().gens()];
+            while(self.__pol_var in names):
+                i += _sage_const_1 ;
+                self.__pol_var = 'n' + str(i);
+            
+            self.__polynomialRing = PolynomialRing(base.base_ring(), self.__pol_var);
+        except RuntimeError:
+            self.__pol_var = 'n';
+            self.__polynomialRing = PolynomialRing(base.base_ring(),self.__pol_var);
+            
+        ## Computing the Ring_w_Sequence associated to base
+        if(isinstance(base, Ring_w_Sequence)):
+            self.__base = base;
+        else:
+            self.__base = Wrap_w_Sequence_Ring(base);
+        self._original_base = base;    
+                
+        ## Declaring private fields
+        self.__derivate = derivate;
+        
+    def base(self):
+        return self.__base;
+        
+    def derivate(self):
+        return self.__derivate;
+        
+    def _get_preference(self):
+        return self._op_preference;
+    
+    ### Getter methods
+    def getOrder(self):
+        '''
+        This method allows the user to get the order of the operator. 
+        
+        This method must be extended in each child-class of Operator.
+        '''
+        raise NotImplementedError('Method not implemented -- Abstract class asked');
+        
+    def order(self):
+        return self.getOrder();
+        
+    def getCoefficients(self):
+        '''
+        This method allows the user to get the coefficients of the operator. 
+        
+        This method must be extended in each child-class of Operator.
+        '''
+        raise NotImplementedError('Method not implemented -- Abstract class asked');
+    
+    def coefficients(self):
+        return self.getCoefficients();
+    
+    def getCoefficient(self, i):
+        '''
+        This method allows the user to get a coefficient of the operator. 
+        
+        This method must be extended in each child-class of Operator.
+        '''
+        raise NotImplementedError('Method not implemented -- Abstract class asked');
+        
+    def coefficient(self, i):
+        return self.getCoefficient(i);
+        
+    @cached_method
+    def companion(self, element=None):
+        field = self.base().fraction_field();
+        
+        if(element is None):
+            coefficients = [field(el) for el in self.getCoefficients()];    
+        else:
+            coefficients = [field(el(x=element)) for el in self.getCoefficients()];
+            
+        ## We divide by the leading coefficient
+        coefficients = [-(coefficients[i]/coefficients[-_sage_const_1 ]) for i in range(len(coefficients)-_sage_const_1 )];
+        ## Trying to reduce the elements
+        try:
+            for i in range(len(coefficients)):
+                coefficients[i].reduce();
+        except (AttributeError, ArithmeticError):
+            pass;
+        d = len(coefficients);
+        
+        ## Building the rows of our matrix
+        rows = [[_sage_const_0  for i in range(d-_sage_const_1 )] + [coefficients[_sage_const_0 ]]];
+        for i in range(d-_sage_const_1 ):
+            rows += [[kronecker_delta(i,j) for j in range(d-_sage_const_1 )] + [coefficients[i+_sage_const_1 ]]];
+            
+        ## Returning the matrix
+        return Matrix(field, rows);
+    #######################################################
+    
+    #######################################################
+    ### RECURSION POLYNOMIALS METHODS
+    #######################################################        
+    def get_recursion_polynomial(self, n):
+        '''
+            Method to get a recursion polynomial associated with this operator.
+            
+            If the requested polynomial is greater than zero, then we will return a `forward` polynomial, but if the request is lesser than zero, we will return a backward polynomial, computing it if neccessary.
+            
+            INPUT:
+                - n: index of the recursion polynomial requested.
+                
+            OUTPUT:
+                A polynomial on self.base()[nx] where x will be an integer such the variable nx is not in self.base().gens()
+        '''
+        if(n >= _sage_const_0 ):
+            try:
+                return self.forward(n);
+            except IndexError:
+                return _sage_const_0 ;
+        else:
+            return self.backward(-n);
+            
+    @cached_method
+    def forward(self, n):
+        if(n < _sage_const_0 ):
+            raise IndexError("Forward polynomials have only positive index");
+        elif(n > self.getOrder()):
+            return _sage_const_0 ;
+        elif(self.is_zero()):
+            return _sage_const_0 ;
+        else: 
+            var = self.__polynomialRing.gens()[-_sage_const_1 ];
+            
+            return sum([self.base().sequence(self.getCoefficient(l),l-n)*falling_factorial(var+n,l) for l in range(n, self.getOrder()+_sage_const_1 )]);
+          
+    @cached_method  
+    def backward(self, n):
+        if(n < _sage_const_0 ):
+            raise IndexError("Backward polynomials have only positive index");
+        elif(self.is_zero()):
+            return _sage_const_0 ;
+        
+        var = self.__polynomialRing.gens()[-_sage_const_1 ];
+        return sum([self.base().sequence(self.getCoefficient(l), l+n)*falling_factorial(var-n, l) for l in range(_sage_const_0 ,self.getOrder()+_sage_const_1 )]);
+        
+    def get_recursion_row(self,i):
+        r = self.getCoefficients();
+        d = self.getOrder();
+        row = [];
+        
+        #var = self.__polynomialRing.gens()[-1];
+        ### First summation part
+        #row += [sum([falling_factorial(k,l)*self.base().sequence(r[l],i-k+l) for l in range(0,k+1)]) for k in range(0,min(i,d))];
+        #if(i<d):
+        #    ## Second summation part
+        #    row += [sum([falling_factorial(k+i,l)*self.base().sequence(r[l], l-k) for l in range(k,i+k+1)]) for k in range(d-i)];
+        #    ## Third summation part
+        #    row += [self.forward(k)(**{str(var):i}) for k in range(d-i,d+1)];
+        #else:
+        #    ## Second summation part
+        #    row += [self.backward(i-d-k)(**{str(var):i}) for k in range(i-d)];
+        #    ## Third summation part
+        #    row += [self.forward(k)(**{str(var):i}) for k in range(0,d+1)];
+        
+        ## First summation part
+        row += [sum([falling_factorial(k,l)*self.base().sequence(r[l],i-k+l) for l in range(_sage_const_0 ,k+_sage_const_1 )]) for k in range(_sage_const_0 ,min(i,d))];
+        if(i<d):
+            ## Second summation part
+            row += [sum([falling_factorial(k+i,l)*self.base().sequence(r[l], l-k) for l in range(k,i+k+_sage_const_1 )]) for k in range(d-i)];
+            ## Third summation part
+            row += [self.__eval_pol(self.forward(k),i) for k in range(d-i,d+_sage_const_1 )];
+        else:
+            ## Second summation part
+            row += [self.__eval_pol(self.backward(i-d-k),i) for k in range(i-d)];
+            ## Third summation part
+            row += [self.__eval_pol(self.forward(k),i) for k in range(_sage_const_0 ,d+_sage_const_1 )];
+            
+        return row;
+        
+    def get_recursion_matrix(self, n):
+        nrows = n+_sage_const_1 ;
+        ncols = n+self.forward_order+_sage_const_1 ;
+        rows = [];
+        
+        for i in range(nrows):
+            row = self.get_recursion_row(i);
+            
+            rows += [[row[i] for i in range(min(len(row),ncols))] + [_sage_const_0  for i in range(ncols-len(row))]];
+            
+        return Matrix(self.__polynomialRing.base(), rows); 
+    #######################################################
+        
+    #######################################################
+    ### SOLUTION SPACE METHODS
+    #######################################################
+    @derived_property
+    def dimension(self):
+        return self.jp_matrix.right_kernel_matrix().nrows();
+    
+    @derived_property
+    def forward_order(self):
+        if(self.is_zero()):
+            raise ValueError("The zero operator has not forward order");
+        
+        n = self.getOrder();
+        while(self.get_recursion_polynomial(n) == _sage_const_0 ):
+            n -= _sage_const_1 ;
+        
+        return n;
+    
+    @derived_property
+    def jp_value(self):
+        ## TODO Be careful with this computation:oinly valid is the base field are the rational
+        jp_pol = self.get_recursion_polynomial(self.forward_order);
+        return max([self.getOrder()-self.forward_order] + [root[_sage_const_0 ] for root in jp_pol.roots() if (root[_sage_const_0 ] in ZZ)]);
+       
+    @derived_property 
+    def jp_matrix(self):
+        return self.get_recursion_matrix(self.jp_value);
+        
+    def get_jp_fo(self):
+        return self.jp_value+self.forward_order;      
+    #######################################################
+    
+    #######################################################
+    ### OPERATOR ARITHMETIC METHODS (ABSTRACT)
+    ####################################################### 
+    def add(self, other):
+        '''
+        This method allows the user to add two operators. 
+        
+        This method must be extended in each child-class of Operator.
+        '''
+        raise NotImplementedError('Method not implemented -- Abstract class asked');
+        
+    def scalar(self, other):
+        '''
+        This method allows the user to do a (left)scalar multiplication. 
+        
+        This method must be extended in each child-class of Operator.
+        '''
+        raise NotImplementedError('Method not implemented -- Abstract class asked');
+        
+    def mult(self, other):
+        '''
+        This method allows the user to multiply two operators. 
+        
+        This method must be extended in each child-class of Operator.
+        '''
+        raise NotImplementedError('Method not implemented -- Abstract class asked');
+        
+    def is_zero(self):
+        '''
+        This method allows the user to know if this operator is the zero operator. 
+        
+        This method must be extended in each child-class of Operator.
+        '''
+        raise NotImplementedError('Method not implemented -- Abstract class asked');
+        
+    def derivative(self):
+        '''
+        This method allows the user to derivate the operator (if possible). If not, it will raise a
+        NotImplementedError. 
+        
+        This method must be extended in each child-class of Operator.
+        '''
+        raise NotImplementedError('This operator can not be derivated');
+    ####################################################### 
+    
+    ####################################################### 
+    ### SOLUTION ARITHMETHIC METHODS
+    ####################################################### 
+    def add_solution(self, other):
+        '''
+        This method computes a new operator such any solution of 'self == 0' plus any solution of 'other == 0' must satisfy.
+        '''
+        ## If the input is not an operator, trying the casting
+        if(not isinstance(other, Operator)):
+            other = self.__class__(self.base(), other, self.derivate());
+            
+        ## If the input is an Operator we guess the hightest preference type
+        if(not isinstance(other, self.__class__)):
+            if(other._get_preference() > self._get_preference()):
+                return other.add_solution(self);
+            other = self.__class__(self.base(), other, self.derivate());
+            
+        return self._compute_add_solution(other);
+                
+    def mult_solution(self, other):
+        '''
+        This method computes a new operator such any solution of 'self == 0' multiplied by any solution of 'other == 0' must satisfy.
+        '''
+        ## If the input is not an operator, trying the casting
+        if(not isinstance(other, Operator)):
+            other = self.__class__(self.base(), other, self.derivate());
+            
+        ## If the input is an Operator we guess the hightest preference type
+        if(not isinstance(other, self.__class__)):
+            if(other._get_preference() > self._get_preference()):
+                return other.mult_solution(self);
+            other = self.__class__(self.base(), other, self.derivate());
+            
+        return self._compute_mult_solution(other);
+        
+    def derivative_solution(self):
+        '''
+        This method computes a new operator such the derivative of any solution of 'self == 0' must satisfy.
+        '''
+        return self._compute_derivative_solution();
+        
+    def integral_solution(self):
+        '''
+        This method computes a new operator such any anti-derivative of any solution of 'self == 0' must satisfy.
+        '''
+        return self._compute_integral_solution();
+        
+    def compose_solution(self, other):
+        '''
+        This method computes a new operator that annihilates any solution of 'self' compose with 'other' (an element that must be in the base ring).
+        '''
+        ## If the input is not an operator, trying the casting
+        if(not other in self.base()):
+            raise TypeError("Element (%s) is not valid for compose with a solution of %s" %(other, str(self)));
+        
+        return self._compute_compose_solution(other);
+    
+    def _compute_add_solution(self, other):
+        '''
+        This method computes a new operator such any solution of 'self == 0' plus any solution of 'other == 0' must satisfy.
+        It assumes that other and self are exactly the same type.
+        '''
+        raise NotImplementedError('Method not implemented. Class: %s' %self.__class__);
+        
+    def _compute_mult_solution(self, other):
+        '''
+        This method computes a new operator such any solution of 'self == 0' multiplied by any solution of 'other == 0' must satisfy.
+        It assumes that other and self are exactly the same type.
+        '''
+        raise NotImplementedError('Method not implemented. Class: %s' %self.__class__);
+        
+    def _compute_derivative_solution(self):
+        '''
+        This method computes a new operator such the derivative of any solution of 'self == 0' must satisfy.
+        '''
+        raise NotImplementedError('Method not implemented. Class: %s' %self.__class__);
+        
+    def _compute_integral_solution(self):
+        '''
+        This method computes a new operator such any anti-derivative of any solution of 'self == 0' must satisfy.
+        '''
+        raise NotImplementedError('Method not implemented. Class: %s' %self.__class__);
+        
+    def _compute_compose_solution(self, other):
+        '''
+        This method computes a new operator that annihilates any solution of 'self' compose with any solution of 'other'.
+        '''
+        raise NotImplementedError('Method not implemented. Class: %s' %self.__class__);
+    ####################################################### 
+    
+    ####################################################### 
+    ### MAGIC PYTHON METHODS
+    ####################################################### 
+    def __getitem__(self, key):
+        if(key > self.getOrder() or key < _sage_const_0 ):
+            raise IndexError("No coefficient can be get bigger than the order or with negative index");
+        return self.getCoefficient(key);
+    
+    def __call__(self, obj):
+        '''
+        This method allows the user to apply the operator to an object. 
+        
+        This method must be extended in each child-class of Operator.
+        '''
+        try:
+            res = obj.parent().zero();
+            to_mult = obj;
+            for i in range(self.getOrder()+_sage_const_1 ):
+                res += self.getCoefficient(i)*to_mult;
+                to_mult = to_mult.derivative();
+            return res;
+        except Exception:
+            raise NotImplementedError('Method not implemented -- Abstract class asked');
+        
+    ### Addition
+    def __add__(self, other):
+        try:
+            return self.add(other);
+        except Exception:
+            return NotImplemented;
+            
+    ### Substraction
+    def __sub__(self, other):
+        try:
+            #return self.add(other.scalar(-1));
+            return self.scalar(-_sage_const_1 ).add(other).scalar(-_sage_const_1 );
+        except Exception:
+            return NotImplemented;
+            
+    ### Multiplication
+    def __mul__(self, other):
+        try:                
+            return self.mult(other);
+        except Exception:
+            return NotImplemented;
+  
+    ### Reverse addition
+    def __radd__(self, other):
+        return self.__add__(other);
+            
+    ### Reverse substraction
+    def __rsub__(self, other):
+        return self.scalar(-_sage_const_1 ).__add__(other);
+            
+    ### Reverse multiplication
+    def __rmul__(self, other):
+        try:
+            if(not isinstance(other, Operator)):
+                return self.scalar(other);
+                
+            return other.mult(self);
+        except Exception:
+            return NotImplemented;
+        
+    ### Implicit addition
+    def __iadd__(self, other):
+        return self.__add__(other);
+            
+    ### Implicit substraction
+    def __isub__(self, other):
+        return self.__sub__(other);
+            
+    ### Implicit multiplication
+    def __imul__(self, other):
+        return self.__mul__(other);
+            
+    ### Unary '-'
+    def __neg__(self):
+        return self.scalar(-_sage_const_1 );
+    ####################################################### 
+    
+    def __eval_pol(self,pol, val):
+        try:
+            return pol(**{self.__pol_var:val});
+        except RuntimeError:
+            coeffs = pol.coefficients(False);
+            return sum(coeffs[i]*val**i for i in range(len(coeffs)));
+
diff --git a/ajpastor/operator/oreOperator.py b/ajpastor/operator/oreOperator.py
new file mode 100644 (file)
index 0000000..f65051d
--- /dev/null
@@ -0,0 +1,210 @@
+
+# This file was *autogenerated* from the file ./oreOperator.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+
+####################################################################################################
+####################################################################################################
+###
+### Ore-Operator module
+###
+### ------------------------------------------------------------------------------------------------
+###
+### This file contains a wrapper that adapts the OreOperator class from Manuel Kauer's package (see
+### ore_algebra.OreOperator) so we can use them together with other classes of Operators
+### 
+### ------------------------------------------------------------------------------------------------
+###
+### Version: 2.0
+### Date of begining: 21-11-2016
+###
+### Update (05-05-2017)
+###     - Deleted the method getCoefficientParent (now the Operator method parent() do the same)
+###     - Deleted the field "parent" (it is stored in the Operator class)
+###     - Made the imports more precise
+###     - Created a new attribute "__oa" where the OreAlgebra of the element is stored
+###     - Rearrange file: more readable structure
+###     - Added the methods __compute_add_solution, __compute_mult_solution, __compute_derivative_solution and __compute_integral_solution.
+###     - Changed name: method "isZero" to "is_zero"
+###
+### Update (10-05-2017)
+###     - Updated the code for the new version of ore_algebra package (
+###
+### Updated (21-08-2017)
+###     - Changed name parent to base
+###
+### ------------------------------------------------------------------------------------------------
+### Dependencies:
+###     - Operator class
+###     - ore_algebra package 
+####################################################################################################
+####################################################################################################
+
+# General imports
+from ore_algebra import OreAlgebra;
+from ore_algebra.ore_operator import OreOperator;
+
+# Local imports
+from .operator import Operator;
+from .directStepOperator import DirectStepOperator;
+from .operator import foo_derivative;
+
+class w_OreOperator(Operator):
+    ### Static parameters
+    map_to_algebras = {};
+    _op_preference = _sage_const_1 ;
+
+    #######################################################
+    ### INIT METHOD AND GETTERS
+    #######################################################
+    def __init__(self, base, input, derivate = foo_derivative):
+        '''
+        This method allows the user to instantiate a new object of type Operator. 
+        
+        This method must be extended in each child-class of Operator.
+        
+        INPUT:
+            - base: the Structure where the coefficients of the operator will be
+            - input: the input data for the operator. It can be an element of the associated 
+            OreAlgebra for `base` or a list of elements of `base`
+        '''
+        ## Initializing the minimal structure
+        super(w_OreOperator, self).__init__(base, input, derivate);
+        
+        ## Computing the Ore_Algebra associated with base
+        if(not w_OreOperator.map_to_algebras.has_key(base)):
+            newOreAlgebra = OreAlgebra(base, ('D', lambda p : p, derivate));
+            w_OreOperator.map_to_algebras[self.base()] = newOreAlgebra;
+            
+        self.__oa = w_OreOperator.map_to_algebras[self.base()];
+        d = self.__oa.gen();
+        
+        ## Computing the operator
+        try:
+            if isinstance(input, w_OreOperator):
+                self.operator = input.operator;
+            else:
+                if isinstance(input, Operator):
+                    input = input.getCoefficients();
+                if isinstance(input, list):
+                    res = self.__oa(_sage_const_0 );
+                    for i in range(len(input)):
+                        res += self.base()(input[i])*d**i;
+                    self.operator = res;
+                else:
+                    self.operator = self.__oa(input);
+                    
+            if(self.operator == _sage_const_0 ):
+                self.operator = d;
+                
+        except TypeError as e:
+            raise e;
+        except Exception as e:
+            raise TypeError('The input (%s) is not valid for an oreOperator with coefficients in (%s)\nCaused by: %s - %s' %(input,format(base),type(e),e));
+            
+            
+    ### Getter methods
+    def getOrder(self):
+        return self.operator.order();
+        
+    def getCoefficients(self):
+        return self.operator.coefficients(sparse=False);
+    
+    def getCoefficient(self, i):
+        if (i < _sage_const_0 ):
+            raise IndexError('The argument must be a number greater or equal to zero');
+        elif (i < self.getOrder()+_sage_const_1 ):
+            return self.getCoefficients()[i];
+        
+        return _sage_const_0 ;
+    #######################################################
+        
+    #######################################################
+    ### OPERATOR ARITHMETIC METHODS (ABSTRACT)
+    ####################################################### 
+    ### Arithmetic
+    def add(self, other):
+        if(isinstance(other, w_OreOperator)):
+            return w_OreOperator(self.base(), self.operator+self.__oa(other.operator));
+        elif(isinstance(other, Operator)):
+            return self+w_OreOperator(self.base(),other.getCoefficients());
+        else:
+            return w_OreOperator(self.base(), self.operator+self.__oa(other));
+        
+    def scalar(self, other):
+        return w_OreOperator(self.base(), self.__oa(other)*self.operator);
+        
+    def mult(self, other):
+        if(isinstance(other, w_OreOperator)):
+            return w_OreOperator(self.base(), self.operator*self.__oa(other.operator));
+        elif(isinstance(other, Operator)):
+            try:
+                return self*w_OreOperator(self.base(),other.getCoefficients());
+            except Exception:
+                return other.__class__(self.base(),self).mult(other);
+        else:
+            return w_OreOperator(self.base(), self.operator*self.__oa(other));
+        
+    ### Equality
+    def is_zero(self):
+        return self.operator == _sage_const_0 ;
+        
+    def __eq__(self, other):
+        try:
+            if(not isinstance(other, OreOperator)):
+                other = self.__oa(other);
+            return self.operator == other.operator;
+        except Exception:
+            pass;
+        return False;
+        
+    ### Differential
+    def derivative(self):
+        d = self.__oa.gen();
+        return w_OreOperator(self.base(), d*self.operator);
+    ####################################################### 
+        
+    ####################################################### 
+    ### SOLUTION ARITHMETHIC METHODS (ABSTRACT)
+    ####################################################### 
+    def _compute_add_solution(self, other):
+        try:
+            return w_OreOperator(self.base(),self.operator.lclm(other.operator, algorithm="linalg"));
+        except TypeError:
+            return w_OreOperator(self.base(),self.operator.lclm(other.operator, algorithm="euclid"));
+        
+    def _compute_mult_solution(self, other):
+        return w_OreOperator(self.base(),self.operator.symmetric_product(other.operator));
+        
+    def _compute_derivative_solution(self):
+        d = self.__oa.gen();
+        try:
+            return w_OreOperator(self.base(), self.operator/d);
+        except ValueError:
+            c = self.getCoefficient(_sage_const_0 );
+            dc = self.derivate()(c);
+            
+            return w_OreOperator(self.base(), ((c*d - dc)*self.operator)/d);
+        
+    def _compute_integral_solution(self):
+        return w_OreOperator(self.base(),self.operator.annihilator_of_integral());
+        
+    def _compute_compose_solution(self, other):
+        op1 = DirectStepOperator(self.base(), self, self.derivate());
+        
+        return w_OreOperator(self.base(), op1._compute_compose_solution(other), self.derivate());
+    ####################################################### 
+    
+    ####################################################### 
+    ### MAGIC PYTHON METHODS
+    ####################################################### 
+    def __call__(self, obj):
+        return self.operator(obj);
+        
+    def __repr__(self):
+        return ("Wrapped_OreOperator(%s)"%(self.operator.__repr__()));
+        
+    ####################################################### 
+    
+
diff --git a/ajpastor/operator/polynomialLazyOperator.py b/ajpastor/operator/polynomialLazyOperator.py
new file mode 100644 (file)
index 0000000..f0c5560
--- /dev/null
@@ -0,0 +1,201 @@
+
+# This file was *autogenerated* from the file ./polynomialLazyOperator.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0); _sage_const_4 = Integer(4)
+
+####################################################################################################
+####################################################################################################
+###
+### PolynomialLazyOperator module
+###
+### ------------------------------------------------------------------------------------------------
+###
+### This file contains an extension of a LazyStepOperator that computes the kernell of the appropiate matrix trnaslating the elements into polynomials and reversing the change once obtained the final vector.
+###
+### If some relations between the variables are known, Groebner basis reduction will be used.
+### ------------------------------------------------------------------------------------------------
+###
+### Version: 0.0
+### Date of begining: 05-05-2017
+###
+### Updated (21-08-2017)
+###     - Changed name parent to base
+###
+###
+### ------------------------------------------------------------------------------------------------
+### Dependencies:
+###     - TwoStepsOperator class
+####################################################################################################
+####################################################################################################
+
+# Local imports
+from .twoStepsOperator import TwoStepsOperator;
+from .operator import foo_derivative;
+
+from ajpastor.lazy.lazyIDElements import LazyIntegralDomain;
+from ajpastor.lazy.lazyToPoly import LazyToPoly;
+
+from ajpastor.misc.bareiss import BareissAlgorithm;
+
+class PolynomialLazyOperator(TwoStepsOperator):
+    ### Static parameters
+    _op_preference = _sage_const_4 ;
+
+    #######################################################
+    ### INIT METHOD AND GETTERS
+    #######################################################
+    def __init__(self, base, input, derivate = foo_derivative):
+        super(PolynomialLazyOperator, self).__init__(base, input, derivate);
+        
+        self.__conversion = None;
+            
+    ####################################################### 
+    
+    @cached_method
+    def companion(self, element=None):
+        field = LazyIntegralDomain(self._original_base).fraction_field();
+        
+        if(element is None):
+            coefficients = [field(el) for el in self.getCoefficients()];    
+        else:
+            coefficients = [field(el(x=element)) for el in self.getCoefficients()];
+        
+        if(self.__conversion is None):  
+            self.__conversion = LazyToPoly(self.base(), coefficients);
+                    
+        coefficients = self.__conversion.to_poly(coefficients);
+            
+        ## We divide by the leading coefficient
+        coefficients = [-(coefficients[i]/coefficients[-_sage_const_1 ]) for i in range(len(coefficients)-_sage_const_1 )];
+        ## Trying to reduce the elements
+        try:
+            for i in range(len(coefficients)):
+                coefficients[i].reduce();
+        except AttributeError:
+            pass;
+        d = len(coefficients);
+        
+        ## Building the rows of our matrix
+        rows = [[_sage_const_0  for i in range(d-_sage_const_1 )] + [coefficients[_sage_const_0 ]]];
+        for i in range(d-_sage_const_1 ):
+            rows += [[kronecker_delta(i,j) for j in range(d-_sage_const_1 )] + [coefficients[i+_sage_const_1 ]]];
+            
+        ## Returning the matrix
+        return Matrix(self.__conversion.poly_field(), rows);
+        
+    def _mix_matrices(self, other, Mf, Mg):
+        new_conversion = self.__conversion.mix_conversion(other.__conversion);
+        self.__conversion = new_conversion; 
+        self.__dict__['companion'].cache.clear();
+        other.__conversion = new_conversion; 
+        other.__dict__['companion'].cache.clear();
+        
+        
+        return self.companion(), other.companion(), self.companion().parent().base();
+        
+    def _compose_companion(self, Mf, g):
+        ## Creating the new conversion system adding `dg`
+        dg = self.derivate()(self.base()(g));
+        new_conversion = self.__conversion.mix_conversion(dg);
+        
+        ## Changing the conversion system of `self`
+        self.__conversion = new_conversion; 
+        self.__dict__['companion'].cache.clear();
+        
+        ## Returning the new companion matrix and the conversed version of `dg`
+        return self.companion(g), new_conversion.to_poly(dg), self.companion(g).parent().base();
+        
+    def _pre_proc(self, M):
+        return self.__conversion.to_real(M);
+        
+    def _post_proc(self, M):
+        try:
+            return self.__conversion.to_poly(M);
+        except Exception:
+            new_conversion = self.__conversion.mix_conversion(LazyToPoly(self.base(), M));
+            self.__conversion = new_conversion;
+            self.__dict__['companion'].cache.clear();
+            
+            return self.__conversion.to_poly(M);
+            
+        
+    ####################################################### 
+    ### SOLVING MATRICES METHOD
+    ####################################################### 
+    def _get_element_nullspace(self, M):
+        ## We take the domain where our elements will lie
+        ## conversion->lazy domain->domain
+        parent = self.__conversion.base().base();
+        
+        ## We assume the matrix is in the correct space
+        M = self.__conversion.simplify(M);
+        
+        ## We clean denominators to lie in the polynomial ring
+        lcms = [lcm([el.denominator() for el in row]) for row in M];
+        R = M.parent().base().base();
+        M = Matrix(R, [[el*lcms[i] for el in M[i]] for i in range(M.nrows())]);
+        f = lambda p: self.__smart_is_null(p);
+        try:
+            assert(f(_sage_const_1 ) == False);
+        except Exception:
+            raise ValueError("The method to check membership is not correct");
+            
+        from sage.rings.polynomial.polynomial_ring import is_PolynomialRing as isUniPolynomial;
+        from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing as isMPolynomial;
+        ## Computing the kernell of the matrix
+        if(isUniPolynomial(R) or isMPolynomial(R)):
+            bareiss_algorithm = BareissAlgorithm(R,M,f);
+            ker = bareiss_algorithm.right_kernel_matrix();
+            ## If some relations are found during this process, we add it to the conversion system
+            self.__conversion.add_relations(bareiss_algorithm.relations());
+        else:
+            ker = [v for v in M.right_kernel_matrix()];
+                
+        ## If the nullspace has high dimension, we try to reduce the final vector computing zeros at the end
+        aux = [row for row in ker];
+        i = _sage_const_1 ;
+        
+        while(len(aux) > _sage_const_1 ):
+            new = [];
+            current = None;
+            for j in range(len(aux)):
+                if(aux[j][-(i)] == _sage_const_0 ):
+                    new += [aux[j]];
+                elif(current is None):
+                    current = j;
+                else:
+                    new += [aux[current]*aux[j][-(i)] - aux[j]*aux[current][-(i)]];
+                    current = j;
+            aux = [el/gcd(el) for el in new];
+            i = i+_sage_const_1 ;
+
+            
+        ## When exiting the loop, aux has just one vector
+        sol = aux[_sage_const_0 ];
+        sol = [self.__conversion.simplify(a) for a in sol];
+        
+        ## This steps for clearing denominator are no longer needed
+        #lazyLcm = lcm([a.denominator() for a in sol]);
+        #finalLazySolution = [a.numerator()*(lazyLcm/(a.denominator())) for a in sol];
+        
+        ## Just to be sure everything is as simple as possible, we divide again by the gcd of all our
+        ## coefficients and simplify them using the relations known.
+        fin_gcd = gcd(sol);
+        finalSolution = [a/fin_gcd for a in sol];
+        finalSolution = [self.__conversion.simplify(a) for a in finalSolution];
+        
+        ## We transform our polynomial into elements of our destiny domain
+        finalSolution = [self.__conversion.to_real(a).raw() for a in finalSolution];
+        
+        return vector(parent, finalSolution);
+    ####################################################### 
+    
+    def __smart_is_null(self, p):
+        try:
+            return any(self.__conversion.to_real(factor[_sage_const_0 ]).raw().is_null for factor in p.factor());
+        except AttributeError:
+            return self.__conversion.to_real(p).raw().is_null;
+            
+
+
diff --git a/ajpastor/operator/twoStepsOperator.py b/ajpastor/operator/twoStepsOperator.py
new file mode 100644 (file)
index 0000000..487ba95
--- /dev/null
@@ -0,0 +1,148 @@
+
+# This file was *autogenerated* from the file ./twoStepsOperator.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
+
+####################################################################################################
+####################################################################################################
+###
+### TwoStepsOperator module
+###
+### ------------------------------------------------------------------------------------------------
+###
+### This file contains an abstract extension of a ListOperator that add the structure for different algorithms fro computing the solution methods of operators
+###
+### ------------------------------------------------------------------------------------------------
+###
+### Version: 0.0
+### Date of begining: 05-05-2017
+###
+### Updated (21-08-2017)
+###     - Changed name parent to base
+###
+###
+### ------------------------------------------------------------------------------------------------
+### Dependencies:
+###     - ListOperator class
+####################################################################################################
+####################################################################################################
+
+# Local imports
+from .listOperator import ListOperator;
+from .operator import foo_derivative;
+
+class TwoStepsOperator(ListOperator):
+
+    #######################################################
+    ### INIT METHOD AND GETTERS
+    #######################################################
+    def __init__(self, base, input, derivate = foo_derivative):
+        super(TwoStepsOperator, self).__init__(base, input, derivate);
+            
+    ####################################################### 
+    
+    ####################################################### 
+    ### SOLUTION ARITHMETHIC METHODS (ABSTRACT)
+    ####################################################### 
+    def _compute_add_solution(self, other):
+        M = self._get_matrix_add(other);
+        v = self._get_element_nullspace(M);
+        
+        return self.__class__(self.base(), [el for el in v], self.derivate());
+        
+    def _compute_mult_solution(self, other):
+        M = self._get_matrix_mult(other);
+        v = self._get_element_nullspace(M);
+        
+        return self.__class__(self.base(), [el for el in v], self.derivate());
+        
+    def _compute_compose_solution(self, other):
+        M = self._get_matrix_composition(other);
+        v = self._get_element_nullspace(M);
+        
+        return self.__class__(self.base(), [el for el in v], self.derivate());
+    ####################################################### 
+    
+    ####################################################### 
+    ### GETTING MATRICES METHODS
+    ####################################################### 
+    def _get_matrix_add(self, other):
+        '''
+            Method to obtain a matrix such any element of the nullspace can be interpreted as a new operator that annihilates the sum (f+g) where f is solution to 'self=0' and g is solution to 'other=0'
+        '''        
+        from ajpastor.misc.matrix import diagonal_matrix as diagonal;
+        from ajpastor.misc.matrix import matrix_of_dMovement as move;
+        
+        Mf = self.companion();
+        Mg = other.companion();
+        
+        Mf, Mg, parent = self._mix_matrices(other, Mf, Mg);
+        
+        full_companion = diagonal(parent, [Mf,Mg]);
+        init_vector = vector(parent,([_sage_const_1 ] + [_sage_const_0  for i in range(self.getOrder()-_sage_const_1 )])+([_sage_const_1 ] + [_sage_const_0  for i in range(other.getOrder()-_sage_const_1 )]));
+        
+        return self._post_proc(move(self._pre_proc(full_companion), self._pre_proc(init_vector), self.derivate(), full_companion.ncols()+_sage_const_1 ));
+        
+    def _get_matrix_mult(self, other):
+        '''
+            Method to obtain a matrix such any element of the nullspace can be interpreted as a new operator that annihilates the product (fg) where f is solution to 'self=0' and g is solution to 'other=0'
+        '''        
+        from ajpastor.misc.matrix import block_matrix as block;
+        from ajpastor.misc.matrix import diagonal_matrix as diagonal;
+        from ajpastor.misc.matrix import matrix_of_dMovement as move;
+        
+        f = self;
+        g = other;
+        
+        Mf = f.companion();
+        Mg = g.companion();
+        
+        Mf, Mg, parent = self._mix_matrices(other, Mf, Mg);
+        
+        ## Using tensor product
+        full_companion = Mf.tensor_product(Matrix.identity(parent.base(), Mg.nrows())) + Matrix.identity(parent.base(), Mf.nrows()).tensor_product(Mg);
+        
+        init_vector = vector(parent,([_sage_const_1 ] + [_sage_const_0  for i in range(f.getOrder()*g.getOrder()-_sage_const_1 )]));
+                
+        return self._post_proc(move(self._pre_proc(full_companion), self._pre_proc(init_vector), self.derivate(), full_companion.ncols()+_sage_const_1 ));
+        
+    def _get_matrix_composition(self, other):
+        
+        from ajpastor.misc.matrix import matrix_of_dMovement as move;
+    
+        g = other;
+        
+        Mf = self.companion(g);
+        
+        Mf,dg, parent = self._compose_companion(Mf,g);
+        
+        full_companion = dg*Mf;
+        init_vector = vector(parent, [_sage_const_1 ] + [_sage_const_0  for i in range(_sage_const_1 ,self.getOrder())]);
+        
+        return self._post_proc(move(self._pre_proc(full_companion), self._pre_proc(init_vector), self.derivate(), full_companion.ncols()+_sage_const_1 ));
+        
+    def _mix_matrices(self, other, Mf, Mg):
+        return Mf, Mg, Mf.parent().base();
+        
+    def _compose_companion(self,Mf,g):
+        return Mf, self.derivate()(self.base()(g)), Mf.parent().base();
+        
+    def _pre_proc(self, M):
+        return M;
+        
+    def _post_proc(self, M):
+        return M;
+    ####################################################### 
+    
+    ####################################################### 
+    ### SOLVING MATRICES METHOD
+    ####################################################### 
+    def _get_element_nullspace(self, M):
+        '''
+            Method that computes an element in the nullspace of M.
+        '''        
+        raise NotImplementedError('Method not implemented. Class: %s' %self.__class__);
+    ####################################################### 
+    
+
diff --git a/ajpastor/tests/__init__.py b/ajpastor/tests/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ajpastor/tests/dd_functions/__init__.py b/ajpastor/tests/dd_functions/__init__.py
new file mode 100644 (file)
index 0000000..2daee45
--- /dev/null
@@ -0,0 +1,10 @@
+def run():
+    __LIST_OF_FILES = ["ddFunction", "ddFunction2", "examples", "identities", "hypergeometric", "chebyshev", "bessel"];
+    
+    import importlib;
+    for file_name in __LIST_OF_FILES:
+        module = importlib.import_module(__package__+"."+file_name);
+        print '###############################################################################'
+        print "### Testing file %s" %file_name;
+        module.run();
+        print '###############################################################################'    
diff --git a/ajpastor/tests/dd_functions/bessel.py b/ajpastor/tests/dd_functions/bessel.py
new file mode 100644 (file)
index 0000000..d1113fb
--- /dev/null
@@ -0,0 +1,92 @@
+
+# This file was *autogenerated* from the file ./dd_functions/bessel.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_20 = Integer(20); _sage_const_4 = Integer(4)########################################################################
+###
+### File for testing the definition of Chebyshev polynomials
+###
+### Here we will check several identities of the Chebyshev polynomials
+### for different values of the parameter $n$.
+###
+########################################################################
+
+from timeit import default_timer as timer;
+
+from ajpastor.dd_functions import *;
+from ajpastor.misc.verbose import *;
+
+
+def run():
+    __MIN_N = _sage_const_1 ;
+    __MAX_N = _sage_const_20 ;
+
+    __LEVEL = -_sage_const_2 ; ## Verbose level (-2 print loops, -1 print test, 0 print global data, greater print nothing
+    __PREV_LEVEL = sverbose.get_level();
+
+    sverbose.set_level(__LEVEL);
+    __deep_wrap = sverbose.get_deep_wrapper();
+    sverbose.set_deep_wrapper(-__LEVEL);
+    sverbose("Running tests over Bessel Functions using the package dd_functions");
+    sverbose("");
+    sverbose("Author: Antonio Jimenez");
+    sverbose("Date: 12-02-2018");
+    sverbose("");
+    sverbose("The values of the parameters will run from %d to %d" %(__MIN_N, __MAX_N));
+    sverbose.increase_depth();
+
+    ## Starting execution
+    t = timer();
+
+    try:
+        sverbose("Checking 2nxJ_n(x) = J_{n-1}(x)+J_{n+1}(x)");
+        sverbose.increase_depth();
+        sverbose.start_iteration(__MAX_N-__MIN_N, True, True);
+        for n in range(__MIN_N, __MAX_N):
+            f = BesselD(n); fp = BesselD(n+_sage_const_1 ); fm = BesselD(n-_sage_const_1 );
+            if(not _sage_const_2 *n*f == (fm+fp)*x):
+                raise ValueError("Error with the equality of 0F0(;;z) with initial value %d" %n);
+            sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+        sverbose("Finished test");
+        
+        sverbose("Checking 2xJ_n'(x) = J_{n-1}(x)-J_{n+1}(x)");
+        sverbose.increase_depth();
+        sverbose.start_iteration(__MAX_N-__MIN_N, True, True);
+        for n in range(__MIN_N, __MAX_N):
+            f = BesselD(n); fp = BesselD(n+_sage_const_1 ); fm = BesselD(n-_sage_const_1 );
+            if(not _sage_const_2 *f.derivative() == (fm-fp)):
+                raise ValueError("Error with the equality of 0F0(;;z) with initial value %d" %n);
+            sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+        sverbose("Finished test");
+        
+        sverbose("Checking J_n(x) = ((x/2)^n/(n!))*0F1(;n+1;-x^2/4)");
+        sverbose.increase_depth();
+        sverbose.start_iteration(__MAX_N-__MIN_N, True, True);
+        for n in range(__MIN_N, __MAX_N):
+            sverbose.increase_depth();
+            f = BesselD(n);
+            g = GenericHypergeometricFunction((),(n+_sage_const_1 ),_sage_const_1 )(-x**_sage_const_2 /_sage_const_4 );
+            if(not f == ((x/_sage_const_2 )**n/factorial(n))*g):
+                raise ValueError("Error with the equality of 0F0(;;z) with initial value %d" %n);
+            sverbose.decrease_depth();
+            sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+        sverbose("Finished test");
+    except (Exception, KeyboardInterrupt) as e:
+        sverbose.reset_depth();
+        sverbose.set_deep_wrapper(__deep_wrap);
+        sverbose.set_level(__PREV_LEVEL);
+        raise e;
+
+    sverbose.decrease_depth();
+    sverbose("Finished all tests over the Generic Hypergeometric functions");
+    t = timer() - t;
+    sverbose("Parametric tests finished successfully --> %s" %(t));
+    sverbose.set_deep_wrapper(__deep_wrap);
+    sverbose.set_level(__PREV_LEVEL);
+
diff --git a/ajpastor/tests/dd_functions/chebyshev.py b/ajpastor/tests/dd_functions/chebyshev.py
new file mode 100644 (file)
index 0000000..db2c435
--- /dev/null
@@ -0,0 +1,99 @@
+
+# This file was *autogenerated* from the file ./dd_functions/chebyshev.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_2 = Integer(2); _sage_const_20 = Integer(20); _sage_const_0 = Integer(0); _sage_const_1 = Integer(1)########################################################################
+###
+### File for testing the definition of Chebyshev polynomials
+###
+### Here we will check several identities of the Chebyshev polynomials
+### for different values of the parameter $n$.
+###
+########################################################################
+
+from timeit import default_timer as timer;
+
+from ajpastor.dd_functions import *;
+from ajpastor.misc.verbose import *;
+
+def run():
+    __MIN_N = _sage_const_2 ;
+    __MAX_N = _sage_const_20 ;
+
+    __LEVEL = -_sage_const_2 ; ## Verbose level (-2 print loops, -1 print test, 0 print global data, greater print nothing
+
+    __PREV_LEVEL = sverbose.get_level();
+
+    sverbose.set_level(__LEVEL);
+    __deep_wrap = sverbose.get_deep_wrapper();
+    sverbose.set_deep_wrapper(-__LEVEL);
+    sverbose("Running test over the Chebyshev polynomials using dd_functions package");
+    sverbose("");
+    sverbose("Author: Antonio Jimenez");
+    sverbose("Date: 12-07-2017");
+    sverbose("");
+    sverbose("All values of the parameter will run from %d to %d" %(__MIN_N, __MAX_N));
+    sverbose.increase_depth();
+
+    ## Starting execution
+    t = timer();
+
+    try:
+        ### Checking the recursive equality
+        sverbose("Recursion formula: p_{n+1} = 2xp_n - p_{n-1}");
+        sverbose.increase_depth();
+        sverbose.start_iteration(__MAX_N-__MIN_N, True, True);
+        for n in range(__MIN_N, __MAX_N):
+            ch1 = [ChebyshevD(n+_sage_const_1 ),ChebyshevD(n),ChebyshevD(n-_sage_const_1 )];
+            ch2 = [ChebyshevD(n+_sage_const_1 ,_sage_const_2 ),ChebyshevD(n,_sage_const_2 ),ChebyshevD(n-_sage_const_1 ,_sage_const_2 )];
+            is_true = [(ch1[_sage_const_0 ] == _sage_const_2 *x*ch1[_sage_const_1 ] - ch1[_sage_const_2 ]),(ch2[_sage_const_0 ] == _sage_const_2 *x*ch2[_sage_const_1 ] - ch2[_sage_const_2 ])];
+            if(not is_true[_sage_const_0 ]):
+                raise ValueError("Error with the recurrence equality for Chebyshev of first kind (%d)" %n);
+            if(not is_true[_sage_const_1 ]):
+                raise ValueError("Error with the recurrence equality for Chebyshev of second kind (%d)" %n);
+            sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+
+        sverbose("Finished test");
+
+        ### Checking Pell's equation
+        sverbose("Pell's equation: T_n^2 - (x^2-1)U_{n-1}^2 = 1");
+        sverbose.increase_depth();
+        sverbose.start_iteration(__MAX_N-__MIN_N, True, True);
+        for n in range(__MIN_N, __MAX_N):
+            T = ChebyshevD(n,_sage_const_1 ); U = ChebyshevD(n-_sage_const_1 ,_sage_const_2 );
+            if(not (T**_sage_const_2 -(x**_sage_const_2 -_sage_const_1 )*U**_sage_const_2  == _sage_const_1 )):
+                raise ValueError("Error with the Pell's equation for n=%d" %n);
+            sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+
+        ### Derivation properties
+        sverbose("Derivative equations");
+        sverbose.increase_depth();
+        sverbose.start_iteration(__MAX_N-__MIN_N, True, True);
+        for n in range(__MIN_N, __MAX_N):
+            T = [ChebyshevD(n,_sage_const_1 ),ChebyshevD(n+_sage_const_1 ,_sage_const_1 )]; U = [ChebyshevD(n,_sage_const_2 ),ChebyshevD(n-_sage_const_1 ,_sage_const_2 )];
+            if(not (T[_sage_const_0 ].derivative() == n*U[-_sage_const_1 ])):
+                raise ValueError("Error with the derivate of $T_%d$" %n);
+            if(not (U[_sage_const_0 ].derivative() == ((n+_sage_const_1 )*T[_sage_const_1 ] - x*U[_sage_const_0 ]).divide(DFinite(x**_sage_const_2 -_sage_const_1 )))):
+                raise ValueError("Error with the derivate of $U_%d$" %n);
+            sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+
+        sverbose("Finished test");      
+    except (Exception, KeyboardInterrupt) as e:
+        sverbose.reset_depth();
+        sverbose.set_deep_wrapper(__deep_wrap);
+        sverbose.set_level(__PREV_LEVEL);
+        raise e;
+
+    sverbose.decrease_depth();
+    sverbose("Finished all tests over the Chebyshev polynomials");
+    t = timer() - t;
+    sverbose("Chebyshev polynomials tests finished successfully --> %s" %(t));
+    sverbose.set_deep_wrapper(__deep_wrap);
+    sverbose.set_level(__PREV_LEVEL);
+
diff --git a/ajpastor/tests/dd_functions/ddFunction.py b/ajpastor/tests/dd_functions/ddFunction.py
new file mode 100644 (file)
index 0000000..129d5c8
--- /dev/null
@@ -0,0 +1,218 @@
+
+# This file was *autogenerated* from the file ./dd_functions/ddFunction.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_3 = Integer(3); _sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0); _sage_const_6 = Integer(6); _sage_const_4 = Integer(4); _sage_const_8 = Integer(8); _sage_const_10 = Integer(10); _sage_const_16 = Integer(16); _sage_const_32 = Integer(32)
+import sys, traceback;
+from timeit import default_timer as timer;
+
+from ajpastor.misc.verbose import *;
+from ajpastor.dd_functions import *;
+
+
+def run():
+    __LEVEL = -_sage_const_2 ; ## Verbose level (-2 print loops, -1 print test, 0 print global data, greater print nothing
+    __PREV_LEVEL = sverbose.get_level();
+
+    ##########################################
+    ### AUXILIARY FUNCTIONS
+    ##########################################
+    def assert_initialValues(func, values, name):
+        for i in range(len(values)):
+            aux = func.getInitialValue(i);
+            assert (aux == values[i]), "Error in the %d initial value of the function %s: expected %s but got %s"%(i,name,values[i],aux);
+
+    def assert_coefficients(func, values, name):
+        aux = func.getOperator().getCoefficients()
+        assert (aux == values), "Error in the coefficients of the function %s: expected %s but got %s"%(name,values,aux);  
+
+    ####################################################################################
+
+    sverbose.set_level(__LEVEL);
+    __deep_wrap = sverbose.get_deep_wrapper();
+    sverbose.set_deep_wrapper(-__LEVEL);
+    sverbose("Running basic tests over the implementation of DDRings and their functions");
+    sverbose("");
+    sverbose("Author: Antonio Jimenez");
+    sverbose("Date: 12-02-2018");
+    sverbose("");
+    sverbose.increase_depth();
+              
+
+    ##Starting execution
+    t = timer();
+    #########################################
+    ### TESTS FOR STRUCTURES DDRing and DDFunction
+    #########################################
+    try:
+        
+        _aux = None;    # Auxiliar variable for useless assigments
+        
+        ## Creation and Initial Values tests
+        sverbose("Test for creation and initial values computation");
+        sin = DFinite_example('sin');
+        assert_coefficients(sin, [_sage_const_1 ,_sage_const_0 ,_sage_const_1 ], 'sin');
+        assert_initialValues(sin, [_sage_const_0 , _sage_const_1 , _sage_const_0 , -_sage_const_1 , _sage_const_0 , _sage_const_1 ], 'sin');
+
+        # Inhomogeneous tests
+        sverbose("Test for inhomogeneous creation of elements");
+        b = DFinite.element([_sage_const_1 ], inhomogeneous = x**_sage_const_3 +_sage_const_1 )
+        assert_coefficients(b, [-_sage_const_3 *x**_sage_const_2 ,(x**_sage_const_3 +_sage_const_1 )], '3x^2 + 1');
+
+        # Differential tests
+        sverbose("Test for constant checking");
+        a=DFinite.element([_sage_const_0 ,_sage_const_1 ], [_sage_const_1 ]);
+        assert_initialValues(a, [_sage_const_1 ,_sage_const_0 ,_sage_const_0 ,_sage_const_0 ,_sage_const_0 ,_sage_const_0 ], 'constant=1');
+
+        #Derivation
+        sverbose("Test for derivation of elements");
+        _aux = a.derivative();
+        assert_coefficients(_aux, [_sage_const_0 ,_sage_const_1 ], 'constant=0');
+        assert_initialValues(_aux, [_sage_const_0 ,_sage_const_0 ,_sage_const_0 ,_sage_const_0 ,_sage_const_0 ,_sage_const_0 ], 'constant=0');
+
+        cos = sin.derivative();
+        assert_coefficients(cos, [_sage_const_1 ,_sage_const_0 ,_sage_const_1 ], 'cos');
+        assert_initialValues(cos, [_sage_const_1 , _sage_const_0 , -_sage_const_1 , _sage_const_0 , _sage_const_1 , _sage_const_0 ], 'cos');
+
+        #Integration
+        sverbose("Test for integration of elements");
+        _aux = a.integrate();
+        assert_coefficients(_aux, [_sage_const_0 ,_sage_const_0 ,_sage_const_1 ], 'x');
+        assert_initialValues(_aux, [_sage_const_0 ,_sage_const_1 ,_sage_const_0 ,_sage_const_0 ,_sage_const_0 ,_sage_const_0 ], 'x');
+
+        _aux = sin.integrate(-_sage_const_1 );
+        assert_coefficients(_aux, [_sage_const_0 ,_sage_const_1 ,_sage_const_0 ,_sage_const_1 ], '-cos');
+        assert_initialValues(_aux, [-_sage_const_1 , _sage_const_0 , _sage_const_1 , _sage_const_0 , -_sage_const_1 , _sage_const_0 ], '-cos');
+
+        # Summation, multiplication and scalar tests
+        # Summation
+        sverbose("Test for addition of elements (exp(x) + exp(x))");
+        e = DFinite_example('e');
+        _aux = e.add(e)
+        assert_coefficients(_aux, [-_sage_const_1 ,_sage_const_1 ], '2exp(x)');
+        assert_initialValues(_aux, [_sage_const_2 ,_sage_const_2 ,_sage_const_2 ,_sage_const_2 ,_sage_const_2 ,_sage_const_2 ], '2exp(x)');
+
+        #Multiplication
+        sverbose("Test for product of elements (exp(x) * exp(x))");
+        _aux = e.mult(e)
+        assert_coefficients(_aux, [-_sage_const_2 ,_sage_const_1 ], 'exp(2x)');
+        assert_initialValues(_aux, [_sage_const_1 ,_sage_const_2 ,_sage_const_4 ,_sage_const_8 ,_sage_const_16 ,_sage_const_32 ], 'exp(2x)');
+
+        #Scalar
+        sverbose("Test for scalar product of elements (6*exp(x))");
+        _aux = e.scalar(_sage_const_6 )
+        assert_coefficients(_aux, [-_sage_const_1 ,_sage_const_1 ], '6exp(x)');
+        assert_initialValues(_aux, [_sage_const_6 ,_sage_const_6 ,_sage_const_6 ,_sage_const_6 ,_sage_const_6 ,_sage_const_6 ], '6exp(x)');
+
+        # Zero and Equality methods
+        # Simple equalities (f == f)
+            # f == f
+        sverbose("Test for equality 1 (reflexiveness)");
+        assert e.equals(e), "Error with self-equality";
+
+            # 2*f == f+f
+        sverbose("Test for equality 2 (exp(x)+exp(x) == 2*exp(x))");
+        assert e.add(e).equals(e.scalar(_sage_const_2 )), "Error with the equality 2e == e+e";
+
+            # Different representations
+        sverbose("Test for equality 3 (same function with different representations)");
+        _aux = DFinite.element([_sage_const_0 ,_sage_const_0 ,_sage_const_1 ,-_sage_const_1 ], [_sage_const_1 ,_sage_const_1 ,_sage_const_1 ]);
+        assert e.equals(_aux), 'Error with the equality from different representations for same function';
+
+            #sin^2+cos^2 = 1
+        sverbose("Test for equality 4 (cos^2(x)+sin^2(x) == 1)");
+        unity = DFinite(_sage_const_1 );
+        assert unity.equals(sin.mult(sin).add(cos.mult(cos))), 'Error with sin^2 + cos^2 == 1 using basic operations';
+
+            #sin(2x) = 2sin(x)cos(x)
+        sverbose("Test for equality 5 (sin(2x) == 2sin(x)cos(x))");
+        sin2 = DFinite.element([_sage_const_4 ,_sage_const_0 ,_sage_const_1 ], [_sage_const_0 ,_sage_const_2 ]);
+        assert sin2.equals(sin.mult(cos).scalar(_sage_const_2 )), 'Error with sin(2x) == 2sin(x)cos(x) using basic operations';
+
+            #cos(2x) = cos^2(x)-sin^2(x)
+        sverbose("Test for equality 6 (cos(2x) = cos^2(x)-sin^2(x))");
+        cos2 = DFinite.element([_sage_const_4 ,_sage_const_0 ,_sage_const_1 ], [_sage_const_1 ,_sage_const_0 ]);
+        assert cos2.equals(cos.mult(cos).sub(sin.mult(sin))), 'Error with cos(2x) == cos^2(x)-sin^2(x) using basic operations';
+
+        # Python user friendly methods tests
+        sverbose("Test for magic Python methods");
+        sverbose.increase_depth();
+        # Object methods
+        try:
+            b[-_sage_const_1 ];
+            assert False, "Error getting position (-1). Previously, this meant inhomogeneous term. Now is nothing."
+        except IndexError:
+            pass;
+        # Python user friendly methods tests
+        sverbose("Direct arithmetic");
+        _aux = DFinite.element([x,-_sage_const_1 ],[_sage_const_1 ]);
+        assert (-sin).equals(sin.scalar(-_sage_const_1 )), 'Error checking unary -';
+        assert (sin+_aux).equals(sin.add(_aux)), 'Error checking + for DFinite';
+        assert (cos-e).equals(cos.sub(e)), 'Error checking - for DFinite';
+        assert (sin*sin).equals(sin.mult(sin)), 'Error checking * for DFinite';
+        assert (sin**_sage_const_2 ).equals(sin.mult(sin)), 'Error checking ** with value 2 for DFinite';
+        assert DFinite(_sage_const_1 ).equals(sin**_sage_const_0 ), 'Error checking ** with value 0 for DFinite';
+        assert (cos**_sage_const_3 ).equals(cos.mult(cos.mult(cos))), 'Error checking ** with value 3 for DFinite';
+        try:
+            aux = sin**cos;
+            assert False, "Error catching a NotImplementedError for method __pow__";
+        except (NotImplementedError):
+            pass;
+        try:
+            aux = pow(sin, -_sage_const_3 );
+            assert False, "Error catching a ZeroDivisionError for method __pow__";
+        except ZeroDivisionError:
+            pass;
+            
+        sverbose("In-place arithmetic");
+        inPlace = sin;
+        inPlace += e;
+        assert inPlace.equals(sin+e), 'Error checking inplace + for DFinite';
+        inPlace = sin;
+        inPlace -= cos;
+        assert inPlace.equals(sin-cos), 'Error checking inplace - for DFinite';
+        inPlace = cos;
+        inPlace *= sin;
+        assert inPlace.equals(sin.mult(cos)), 'Error checking inplace * for DFinite';
+        inPlace = sin;
+        inPlace **= _sage_const_2 ;
+        inPlace += cos**_sage_const_2 ;
+        assert inPlace.equals(DFinite(_sage_const_1 )), 'Error checking implace ** for DFinite';
+        
+        sverbose("Python equality (==)");
+        assert (sin**_sage_const_2 +cos**_sage_const_2  == _sage_const_1 ), 'Error checking equality sin^2+cos^2 == 1';
+        assert (not (cos2 == (sin*cos).scalar(_sage_const_2 ))), 'Error checking the inequality cos(2x) != 2sin(x)cos(x)';
+        assert (sin2 == (sin*cos).scalar(_sage_const_2 )), 'Error checking equality sin(2x) = 2sin(x)cos(x)';
+        assert (cos2 == cos**_sage_const_2 -sin**_sage_const_2 ), 'Error checking equality cos(2x) == cos^2(x)+sin^2(x)';
+        _aux=DFinite(_sage_const_3 *x**_sage_const_2 +x+_sage_const_1 );
+        assert (_aux.derivative() == _sage_const_6 *x+_sage_const_1 ), 'Error checking equality D(3x^2+x+1) == 6x+1';
+        sverbose.decrease_depth();
+        
+        sverbose("Test over initial values required");
+        # Impossible get initial values -- Giving more than expected
+        try:
+            f1 = DFinite.element([-_sage_const_4 *x**_sage_const_3 ,_sage_const_1 ,-x], [_sage_const_0 ,_sage_const_0 ,_sage_const_2 ]);
+            f2 = DFinite.element([-_sage_const_1 -x,x], [_sage_const_0 ,_sage_const_1 ]);
+            f1*f2;
+        except Exception:
+            assert False, "Error with DD-Function with more initial values than needed";
+
+        # Impossible get initial values -- Giving less than expected
+        _aux = DFinite.element([x,-_sage_const_1 ]);
+        _aux = e*_aux;
+        assert_coefficients(_aux, [-(x+_sage_const_1 ),_sage_const_1 ], 'e^x*e^(-x^2/2)');
+        assert (_aux.getInitialValueList(_sage_const_10 ) == []), "Error getting initial values: expected [] but got %s"%(_aux.getInitialValueList(_sage_const_10 ));
+    except (Exception, KeyboardInterrupt) as e:
+        sverbose.reset_depth();
+        sverbose.set_deep_wrapper(__deep_wrap);
+        sverbose.set_level(__PREV_LEVEL);
+        print "Error found during tests: %s" %(e);
+        raise e;
+        
+    sverbose.decrease_depth();
+    sverbose("Finished all the basic tests");
+    t = timer() - t;
+    sverbose("Basic tests finished successfully --> %s" %(t));
+    sverbose.set_deep_wrapper(__deep_wrap);
+    sverbose.set_level(__PREV_LEVEL);
+
diff --git a/ajpastor/tests/dd_functions/ddFunction2.py b/ajpastor/tests/dd_functions/ddFunction2.py
new file mode 100644 (file)
index 0000000..ad24b27
--- /dev/null
@@ -0,0 +1,123 @@
+
+# This file was *autogenerated* from the file ./dd_functions/ddFunction2.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_3 = Integer(3); _sage_const_2 = Integer(2); _sage_const_9 = Integer(9); _sage_const_0 = Integer(0)
+import sys, traceback, warnings;
+from timeit import default_timer as timer;
+
+from ajpastor.misc.verbose import *;
+from ajpastor.dd_functions import *;
+from ajpastor.dd_functions.ddFunction import DDFunctionWarning;
+
+def run():
+    __LEVEL = -_sage_const_2 ; ## Verbose level (-2 print loops, -1 print test, 0 print global data, greater print nothing
+    __PREV_LEVEL = sverbose.get_level();
+
+    ##########################################
+    ### AUXILIARY FUNCTIONS
+    ##########################################
+    def assert_initial(func, symbolic, size, name):
+        for i in range(size):
+            func_val = func.getInitialValue(i);
+            real_val = symbolic.derivative(i)(x=_sage_const_0 );
+            assert (func_val == real_val), "Error in the %d initial value of the function %s: expected %s but got %s"%(i,name,real_val,func_val);
+
+    ####################################################################################
+
+    sverbose.set_level(__LEVEL);
+    __deep_wrap = sverbose.get_deep_wrapper();
+    sverbose.set_deep_wrapper(-__LEVEL);
+    sverbose("Running advanced tests over the implementation of DDRings and their functions");
+    sverbose("");
+    sverbose("Author: Antonio Jimenez");
+    sverbose("Date: 14-02-2018");
+    sverbose("");
+    sverbose.increase_depth();
+              
+
+    ##Starting execution
+    warnings.simplefilter('ignore',DDFunctionWarning);
+    t = timer();
+    #########################################
+    ### TESTS FOR STRUCTURES DDRing and DDFunction
+    #########################################
+    try:
+        
+        ## Creation for the DDRing get the same object
+        sverbose("Test for checking the uniqueness of DDRings");
+        sverbose.increase_depth();
+        sverbose("Iteration vs. Direct");
+        assert DDRing(DDFinite) is DDRing(PolynomialRing(QQ,x), depth=_sage_const_3 ), "Error building DDRing. Not uniqueness detected from iteration against the direct step";
+        assert DDRing(DFinite, depth=_sage_const_2 ) is DDRing(PolynomialRing(QQ,x), depth=_sage_const_3 ), "Error building DDRing. Not uniqueness detected from a semi-direct step against the direct step";
+        
+        sverbose("P. DDRings with Symbols vs. P. DDRings with strings");
+        assert ParametrizedDDRing(DFinite, 'a') is ParametrizedDDRing(DFinite, var('a')), "Error building parameters. Not uniqueness from string or symbol";
+        
+        sverbose("Order on the parameters");
+        assert ParametrizedDDRing(DFinite, ['a','q']) is ParametrizedDDRing(DFinite, ['q', 'a']), "Error building parameters. Not uniqueness with the order of parameters";
+        
+        sverbose("Adding parameters vs. Create parameters");
+        assert ParametrizedDDRing(DFinite, ['a','q']) is ParametrizedDDRing(ParametrizedDDRing(DFinite, 'a'), 'q'), "Error building parameters. Not uniqueness adding variables instead of creating them";
+        
+        sverbose("Checking repeated parameters");
+        assert ParametrizedDDRing(ParametrizedDDRing(DFinite, 'a'), 'a') is ParametrizedDDRing(DFinite, 'a'), "Error building parameters. Not uniqueness when adding the same parameter";
+        assert ParametrizedDDRing(DFinite, ['a', 'q']) is ParametrizedDDRing(ParametrizedDDRing(DFinite, 'a'), ['a', 'q']), "Error building parameters. Not uniqueness when adding some repeated parameters";
+        
+        sverbose("Checking depth with parameters");
+        assert ParametrizedDDRing(DDFinite, ['a','q']).base() is ParametrizedDDRing(DFinite, ['a','q']), "Error building parameters. Not uniqueness in P(DD,var).base() and P(D,var)";
+        assert ParametrizedDDRing(DDFinite, 'P').base() is DFiniteP, "Error building parameters. Not uniqueness in P(DD,var).base() and P(D,var)";
+        sverbose.decrease_depth();
+        
+        sverbose("Testing pushouts");
+        sverbose.increase_depth();
+        sverbose.start_iteration(_sage_const_9 , True, True);
+        
+        from sage.categories.pushout import pushout;
+        DDFiniteP = ParametrizedDDRing(DDFinite, 'P');
+        DDFiniteA = ParametrizedDDRing(DDFinite, 'a');
+        DFiniteA = DDFiniteA.base();
+        DDFiniteQ = ParametrizedDDRing(DDFinite, 'q');
+        DFiniteQ = DDFiniteQ.base();
+        DDFiniteAQ = ParametrizedDDRing(DDFinite, ['a','q']);
+        DFiniteAQ = DDFiniteAQ.base();
+        DDFiniteAP = ParametrizedDDRing(DDFinite, ['a','P']);
+        DFiniteAP = DDFiniteAP.base();
+        DDFiniteAQP = ParametrizedDDRing(DDFinite, ['a','q','P']);
+        DFiniteAQP = DDFiniteAQP.base();
+        
+        assert DFiniteP is pushout(DFinite, DFiniteP), "Error in pushouts: D - D(P) -- D(P)";
+        sverbose.next_iteration();
+        assert DDFiniteP is pushout(DFinite, DDFiniteP), "Error in pushouts: D - DD(P) -- DD(P)";
+        sverbose.next_iteration();
+        assert DDFiniteP is pushout(DDFinite, DFiniteP), "Error in pushouts: DD - D(P) -- DD(P)";
+        sverbose.next_iteration();
+        assert DDFiniteAQ is pushout(DDFiniteA, DDFiniteQ), "Error in pushouts: DD(a) - DD(q) -- DD(a,q)";
+        sverbose.next_iteration();
+        assert DDFiniteAQ is pushout(DFiniteA, DDFiniteQ), "Error in pushouts: D(a) - DD(q) -- DD(a,q)";
+        sverbose.next_iteration();
+        assert DDFiniteAQ is pushout(DFiniteA, DDFiniteAQ), "Error in pushouts: D(a) - DD(a,q) -- DD(a,q)";
+        sverbose.next_iteration();
+        assert DFiniteAQ is pushout(DFiniteA, DFiniteQ), "Error in pushouts: D(a) - D(q) -- D(a,q)";
+        sverbose.next_iteration();
+        assert DDFiniteAQP is pushout(DFiniteP, DDFiniteAQ), "Error in pushouts: D(p) - DD(a,q) -- DD(a,q,P)";
+        sverbose.next_iteration();
+        assert DDFiniteAQP is pushout(DFiniteAP, DDFiniteAQ), "Error in pushouts: D(a,P) - D(a,q) -- D(a,q,P)";
+        sverbose.next_iteration();
+        sverbose.decrease_depth();
+        
+    except (Exception, KeyboardInterrupt) as e:
+        sverbose.reset_depth();
+        sverbose.set_deep_wrapper(__deep_wrap);
+        sverbose.set_level(__PREV_LEVEL);
+        print "\nError found during tests: %s" %(e);
+        raise e;
+        
+    sverbose.decrease_depth();
+    sverbose("Finished all the advanced tests");
+    t = timer() - t;
+    sverbose("Advanced tests finished successfully --> %s" %(t));
+    warnings.simplefilter('always',DDFunctionWarning);
+    sverbose.set_deep_wrapper(__deep_wrap);
+    sverbose.set_level(__PREV_LEVEL);
+
diff --git a/ajpastor/tests/dd_functions/examples.py b/ajpastor/tests/dd_functions/examples.py
new file mode 100644 (file)
index 0000000..a7b65c7
--- /dev/null
@@ -0,0 +1,216 @@
+
+# This file was *autogenerated* from the file ./dd_functions/examples.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_3 = Integer(3); _sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0); _sage_const_4 = Integer(4); _sage_const_8 = Integer(8); _sage_const_10 = Integer(10)
+import sys, traceback;
+from timeit import default_timer as timer;
+
+from ajpastor.misc.verbose import *;
+from ajpastor.dd_functions import *;
+
+
+def run():
+    __MIN_N = _sage_const_2 ;
+    __MAX_N = _sage_const_10 ;
+    __DIFF = __MAX_N - __MIN_N;
+
+    __LEVEL = -_sage_const_2 ; ## Verbose level (-2 print loops, -1 print test, 0 print global data, greater print nothing
+    __PREV_LEVEL = sverbose.get_level();
+
+    def assert_initial(func, symbolic, size, name):
+        for i in range(size):
+            func_val = func.getInitialValue(i);
+            real_val = symbolic.derivative(i)(x=_sage_const_0 );
+            assert (func_val == real_val), "Error in the %d initial value of the function %s: expected %s but got %s"%(i,name,real_val,func_val);
+            
+    def random_polynomial(min=-_sage_const_10 ,max=_sage_const_10 ,degree=_sage_const_3 ):
+        R = PolynomialRing(QQ,x);
+        p = R(_sage_const_1 );
+        while(p.degree() <= _sage_const_0 ):
+            p = R([randint(min,max) for i in range(degree+_sage_const_1 )])
+        
+        return p;
+
+
+    ####################################################################################
+
+    sverbose.set_level(__LEVEL);
+    __deep_wrap = sverbose.get_deep_wrapper();
+    sverbose.set_deep_wrapper(-__LEVEL);
+    sverbose("Running tests over the example functions of DDRings");
+    sverbose("");
+    sverbose("Author: Antonio Jimenez");
+    sverbose("Date: 13-02-2018");
+    sverbose("");
+    sverbose.increase_depth();
+              
+
+    ##Starting execution
+    t = timer();
+    #########################################
+    ### TESTS FOR STRUCTURES DDRing and DDFunction
+    #########################################
+    try:
+        
+        sverbose("Checking built-in functions");
+        sverbose.increase_depth();
+        sverbose.start_iteration(_sage_const_8 +_sage_const_4 *__DIFF, True, True);
+        sverbose.increase_depth();
+        assert_initial(Exp(x), exp(x), _sage_const_10 , "exp(x)");
+        sverbose.decrease_depth();    
+        sverbose.next_iteration();
+        sverbose.increase_depth();
+        assert_initial(Sin(x), sin(x), _sage_const_10 , "sin(x)");
+        sverbose.decrease_depth();    
+        sverbose.next_iteration();
+        sverbose.increase_depth();
+        assert_initial(Cos(x), cos(x), _sage_const_10 , "cos(x)");
+        sverbose.decrease_depth();    
+        sverbose.next_iteration();
+        sverbose.increase_depth();
+        assert_initial(Sinh(x), sinh(x), _sage_const_10 , "sinh(x)");
+        sverbose.decrease_depth();    
+        sverbose.next_iteration();
+        sverbose.increase_depth();
+        assert_initial(Cosh(x), cosh(x), _sage_const_10 , "cosh(x)");
+        sverbose.decrease_depth();    
+        sverbose.next_iteration();
+        sverbose.increase_depth();
+        assert_initial(Tan(x), tan(x), _sage_const_10 , "Tan(x)");
+        sverbose.decrease_depth();    
+        sverbose.next_iteration();
+        sverbose.increase_depth();
+        assert_initial(Log(x+_sage_const_1 ), log(x+_sage_const_1 ), _sage_const_10 , "log(x+1)");
+        sverbose.decrease_depth();    
+        sverbose.next_iteration();
+        sverbose.increase_depth();
+        assert_initial(Log1(x), log(x+_sage_const_1 ), _sage_const_10 , "log(x+1)(v2)");
+        sverbose.decrease_depth();    
+        sverbose.next_iteration();
+        sverbose.increase_depth();
+        for n in range(__MIN_N, __MAX_N):
+            assert_initial(BesselD(n), bessel_J(n,x), _sage_const_10 , "bessel_J(%d,x)" %n);
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+        for n in range(__MIN_N, __MAX_N):
+            assert_initial(LegendreD(n), legendre_P(n,x), _sage_const_10 , "legendre_P(%d,x)" %n);
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+        for n in range(__MIN_N, __MAX_N):
+            assert_initial(ChebyshevD(n,_sage_const_1 ), chebyshev_T(n,x), _sage_const_10 , "chebyshev_T(%d,x)" %n);
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+        for n in range(__MIN_N, __MAX_N):
+            assert_initial(ChebyshevD(n,_sage_const_2 ), chebyshev_U(n,x), _sage_const_10 , "chebyshev_U(%d,x)" %n);
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            
+        sverbose.decrease_depth();    
+        sverbose.decrease_depth();
+        sverbose("Finished tests");
+        
+        sverbose("Checking built-in functions with polynomial input");
+        sverbose.increase_depth();
+        sverbose.start_iteration(_sage_const_8 *__DIFF, True, True);
+        sverbose.increase_depth();
+        for i in range(__MIN_N, __MAX_N):
+            p = x*random_polynomial();
+            assert_initial(Exp(p), exp(p), _sage_const_10 , "exp(%s)" %p);
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert_initial(Sin(p), sin(p), _sage_const_10 , "sin(%s)" %p);
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert_initial(Cos(p), cos(p), _sage_const_10 , "cos(%s)" %p);
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert_initial(Sinh(p), sinh(p), _sage_const_10 , "sinh(%s)" %p);
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert_initial(Cosh(p), cosh(p), _sage_const_10 , "cosh(%s)" %p);
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert_initial(Tan(p), tan(p), _sage_const_10 , "Tan(%s)" %p);
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert_initial(Log(p+_sage_const_1 ), log(p+_sage_const_1 ), _sage_const_10 , "log(%s)" %(p+_sage_const_1 ));
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert_initial(Log1(p), log(p+_sage_const_1 ), _sage_const_10 , "log(%s)(v2)" %(p+_sage_const_1 ));
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            
+        sverbose.decrease_depth();    
+        sverbose.decrease_depth();
+        sverbose("Finished tests");
+        
+        sverbose("Checking composition of functions with polynomials");
+        sverbose.increase_depth();
+        sverbose.start_iteration(_sage_const_8 *__DIFF, True, True);
+        sverbose.increase_depth();
+        for i in range(__MIN_N, __MAX_N):
+            p = x*random_polynomial();
+            assert Exp(p) == Exp(x)(p), "Error checking the composition of 'Exp'";
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert Sin(p) == Sin(x)(p), "Error checking the composition of 'Sin'";
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert Cos(p) == Cos(x)(p), "Error checking the composition of 'Cos'";
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert Sinh(p) == Sinh(x)(p), "Error checking the composition of 'Sinh'";
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert Cosh(p) == Cosh(x)(p), "Error checking the composition of 'Cosh'";
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert Tan(p) == Tan(x)(p), "Error checking the composition of 'Tan'";
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert Log(p+_sage_const_1 ) == Log(x+_sage_const_1 )(p), "Error checking the composition of 'Log'";
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            assert Log1(p) == Log1(x)(p), "Error checking the composition of 'Log'(v2)";
+            sverbose.decrease_depth();    
+            sverbose.next_iteration();
+            sverbose.increase_depth();
+            
+        sverbose.decrease_depth();    
+        sverbose.decrease_depth();
+        sverbose("Finished tests");
+        
+    except (Exception, KeyboardInterrupt) as e:
+        sverbose.reset_depth();
+        sverbose.set_deep_wrapper(__deep_wrap);
+        sverbose.set_level(__PREV_LEVEL);
+        print "\nError found during tests: %s" %(e);
+        raise e;
+        
+    sverbose.decrease_depth();
+    sverbose("Finished all the tests on the examples");
+    t = timer() - t;
+    sverbose("Example tests finished successfully --> %s" %(t));
+    sverbose.set_deep_wrapper(__deep_wrap);
+    sverbose.set_level(__PREV_LEVEL);
+
diff --git a/ajpastor/tests/dd_functions/hypergeometric.py b/ajpastor/tests/dd_functions/hypergeometric.py
new file mode 100644 (file)
index 0000000..840138f
--- /dev/null
@@ -0,0 +1,147 @@
+
+# This file was *autogenerated* from the file ./dd_functions/hypergeometric.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_3 = Integer(3); _sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_10 = Integer(10); _sage_const_4 = Integer(4)########################################################################
+###
+### File for testing the definition of Chebyshev polynomials
+###
+### Here we will check several identities of the Chebyshev polynomials
+### for different values of the parameter $n$.
+###
+########################################################################
+
+from timeit import default_timer as timer;
+
+from ajpastor.dd_functions import *;
+from ajpastor.misc.verbose import *;
+
+
+def run():
+    __MIN_N = _sage_const_2 ;
+    __MAX_N = _sage_const_10 ;
+
+    __LEVEL = -_sage_const_2 ; ## Verbose level (-2 print loops, -1 print test, 0 print global data, greater print nothing
+    __PREV_LEVEL = sverbose.get_level();
+
+    sverbose.set_level(__LEVEL);
+    __deep_wrap = sverbose.get_deep_wrapper();
+    sverbose.set_deep_wrapper(-__LEVEL);
+    sverbose("Running test over the Generic Hypergeometric Functions using dd_functions package");
+    sverbose("");
+    sverbose("Author: Antonio Jimenez");
+    sverbose("Date: 01-08-2017");
+    sverbose("");
+    sverbose("All values of the parameters will run from %d to %d" %(__MIN_N, __MAX_N));
+    sverbose.increase_depth();
+
+    ## Starting execution
+    t = timer();
+
+    try:
+        sverbose("Identity for 0F0");
+        sverbose.increase_depth();
+        sverbose.start_iteration(__MAX_N-__MIN_N, True, True);
+        for n in range(__MIN_N, __MAX_N):
+            f = GenericHypergeometricFunction((),(),n);
+            g = n*Exp(x);
+            if(not f == g):
+                raise ValueError("Error with the equality of 0F0(;;z) with initial value %d" %n);
+            sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+
+        sverbose("Finished test");
+
+        sverbose("Identity for 1F0");
+        sverbose.increase_depth();
+
+        sverbose.start_iteration((__MAX_N-__MIN_N)**_sage_const_2 , True, True);
+        for a in range(__MIN_N, __MAX_N):
+            for n in range(__MIN_N, __MAX_N):
+                f = GenericHypergeometricFunction((a),(),n);
+                g = DFinite.element([-a,_sage_const_1 -x],[n]);
+                if(not f == g):
+                    raise ValueError("Error with the equality of 1F0(%d;;z) with initial value %d" %(a,n));
+                sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+
+        sverbose("Finished test");
+
+        sverbose("Identity for 0F1");
+        sverbose.increase_depth();
+        sverbose.start_iteration((__MAX_N-__MIN_N)**_sage_const_2 , True, True);
+        for a in range(__MIN_N, __MAX_N):
+            for n in range(__MIN_N, __MAX_N):
+                f = GenericHypergeometricFunction((),(a),n);
+                g = DFinite.element([-_sage_const_1 ,a,x],[n]);
+                if(not f == g):
+                    raise ValueError("Error with the equality of 1F0(;%d;z) with initial value %d" %(a,n));
+                sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+
+        sverbose("Finished test");
+
+        sverbose("Identity for 1F1");
+        sverbose.increase_depth();
+        sverbose.start_iteration((__MAX_N-__MIN_N)**_sage_const_3 , True, True);
+        for a in range(__MIN_N, __MAX_N):
+            for b in range(__MIN_N, __MAX_N):
+                for n in range(__MIN_N, __MAX_N):
+                    f = GenericHypergeometricFunction((a),(b),n);
+                    g = DFinite.element([-a,(b-x),x],[n]);
+                    if(not f == g):
+                        raise ValueError("Error with the equality of 1F1(%d;%d;z) with initial value %d" %(a,b,n));
+                    sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+
+        sverbose("Finished test");
+
+        sverbose("Identity for 2F1 (Usual Hypergeometric function)");
+        sverbose.increase_depth();
+        sverbose.start_iteration((__MAX_N-__MIN_N)**_sage_const_4 , True, True);
+        for a in range(__MIN_N, __MAX_N):
+            for b in range(__MIN_N, __MAX_N):
+                for c in range(__MIN_N, __MAX_N):
+                    for n in range(__MIN_N, __MAX_N):
+                        f = HypergeometricFunction(a,b,c,n);
+                        g = DFinite.element([-a*b,c-(a+b+_sage_const_1 )*x, x*(_sage_const_1 -x)],[n]);
+                        if(not f == g):
+                            raise ValueError("Error with the equality of 2F1(%d,%d;%d;z) with initial value %d" %(a,b,c,n));
+                        sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+
+        sverbose("Finished test");
+
+        ### Clausen's formula
+        sverbose("Clausen's formula");
+        sverbose.increase_depth();
+        sverbose.start_iteration((__MAX_N-__MIN_N)**_sage_const_2 , True, True);
+        for c in range(__MIN_N, __MAX_N):
+            for s in range(__MIN_N, __MAX_N):
+                        f = GenericHypergeometricFunction((_sage_const_2 *c-_sage_const_2 *s-_sage_const_1 ,_sage_const_2 *s,c-_sage_const_1 /_sage_const_2 ),(_sage_const_2 *c-_sage_const_1 ,c));
+                        g = GenericHypergeometricFunction((c-s-_sage_const_1 /_sage_const_2 ,s),(c))
+                        if(not f == g**_sage_const_2 ):
+                            raise ValueError("Error with the Clausen's formula for parameters c=%d and s=%d" %(c,s));
+                        sverbose.next_iteration();
+            
+        sverbose.decrease_depth();
+
+        sverbose("Finished test");
+    except (Exception, KeyboardInterrupt) as e:
+        sverbose.reset_depth();
+        sverbose.set_level(__PREV_LEVEL);
+        raise e;
+
+    sverbose.decrease_depth();
+    sverbose("Finished all tests over the Generic Hypergeometric functions");
+    t = timer() - t;
+    sverbose("Hypergeometric function tests finished successfully --> %s" %(t));
+    sverbose.set_deep_wrapper(__deep_wrap);
+    sverbose.set_level(__PREV_LEVEL);
+
+
diff --git a/ajpastor/tests/dd_functions/identities.py b/ajpastor/tests/dd_functions/identities.py
new file mode 100644 (file)
index 0000000..527557f
--- /dev/null
@@ -0,0 +1,100 @@
+
+# This file was *autogenerated* from the file ./dd_functions/identities.sage
+from sage.all_cmdline import *   # import sage library
+
+_sage_const_3 = Integer(3); _sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_10 = Integer(10); _sage_const_0 = Integer(0)
+import sys, traceback;
+from timeit import default_timer as timer;
+
+from ajpastor.misc.verbose import *;
+from ajpastor.dd_functions import *;
+
+
+def run():
+    __MIN_N = _sage_const_2 ;
+    __MAX_N = _sage_const_10 ;
+    __DIFF = __MAX_N - __MIN_N;
+
+    __LEVEL = -_sage_const_2 ; ## Verbose level (-2 print loops, -1 print test, 0 print global data, greater print nothing
+    __PREV_LEVEL = sverbose.get_level();
+
+    def assert_initial(func, symbolic, size, name):
+        for i in range(size):
+            func_val = func.getInitialValue(i);
+            real_val = symbolic.derivative(i)(x=_sage_const_0 );
+            assert (func_val == real_val), "Error in the %d initial value of the function %s: expected %s but got %s"%(i,name,real_val,func_val);
+            
+    def random_polynomial(min=-_sage_const_10 ,max=_sage_const_10 ,degree=_sage_const_3 ):
+        R = PolynomialRing(QQ,x);
+        p = R(_sage_const_1 );
+        while(p.degree() == _sage_const_0 ):
+            p = R([randint(min,max) for i in range(degree+_sage_const_1 )])
+        
+        return p;
+
+
+    ####################################################################################
+
+    sverbose.set_level(__LEVEL);
+    __deep_wrap = sverbose.get_deep_wrapper();
+    sverbose.set_deep_wrapper(-__LEVEL);
+    sverbose("Running tests over identities of DDFunctions");
+    sverbose("");
+    sverbose("Author: Antonio Jimenez");
+    sverbose("Date: 13-02-2018");
+    sverbose("");
+    sverbose.increase_depth();
+              
+
+    ##Starting execution
+    t = timer();
+    #########################################
+    ### TESTS FOR STRUCTURES DDRing and DDFunction
+    #########################################
+    try:
+        
+        sverbose("Checking trigonometric equalities");
+        sverbose.increase_depth();
+        sverbose("sin^2(x)+cos^2(x) = 1");
+        assert Sin(x)**_sage_const_2 +Cos(x)**_sage_const_2  == _sage_const_1 , "Error with sin^2(x)+cos^2(x) = 1";
+        sverbose("sin(2x) = 2sin(x)cos(x)");
+        assert Sin(_sage_const_2 *x) == _sage_const_2 *Sin(x)*Cos(x) , "Error with sin(2x) = 2sin(x)cos(x)";
+        sverbose("cos(2x) = cos^2(x) - sin^2(x)");
+        assert Cos(_sage_const_2 *x) == Cos(x)**_sage_const_2  - Sin(x)**_sage_const_2 , "Error with cos(2x) = cos^2(x) - sin^2(x)";
+        sverbose("tan(x) = sin(x)/cos(x)");
+        sverbose.increase_depth();
+        assert Tan(x) == Sin(x)/Cos(x), "Error with tan(x) = sin(x)/cos(x)";
+        sverbose.decrease_depth();
+        sverbose("tan'(x) = 1 + tan^2(x)");
+        ## Including the sine and cosine in the system of DFinite
+        R = Tan(x).equation._FullLazyOperator__conversion;
+        #assert R(Sin(x))^2+R(Cos(x))^2 == 1, "Error with sin^2(x)+cos^2(x) = 1 in the Lazy Ring";
+        sverbose.increase_depth();
+        assert Tan(x).derivative() ==  Tan(x)**_sage_const_2 + _sage_const_1  , "Error with tan'(x) = 1 + tan^2(x)";
+        sverbose.decrease_depth();
+        
+        sverbose.decrease_depth();
+        sverbose("Checking some parametric equalities");
+        sverbose.increase_depth();
+        sverbose("Matheiu Wronskian");
+        f = MathieuCos(); g = MathieuSin();
+        sverbose.increase_depth();
+        assert f*g.derivative()-g*f.derivative() == _sage_const_1 , "Error with the Mathieu's Wronskian";
+        sverbose.decrease_depth();
+        sverbose.decrease_depth();
+        sverbose("Finished tests");
+        
+    except (Exception, KeyboardInterrupt) as e:
+        sverbose.reset_depth();
+        sverbose.set_level(__PREV_LEVEL);
+        print "Error found during tests: %s" %(e);
+        raise e;
+        
+    sverbose.decrease_depth();
+    sverbose("Finished all the tests on the examples");
+    t = timer() - t;
+    sverbose("Example tests finished successfully --> %s" %(t));
+    sverbose.set_deep_wrapper(__deep_wrap);
+    sverbose.set_level(__PREV_LEVEL);
+
+
diff --git a/dependencies b/dependencies
new file mode 100644 (file)
index 0000000..e1acdc8
--- /dev/null
@@ -0,0 +1,5 @@
+# no dependencies
+
+----------
+All lines of this file are ignored except the first.
+It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile.
\ No newline at end of file
diff --git a/package-version.txt b/package-version.txt
new file mode 100644 (file)
index 0000000..ba66466
--- /dev/null
@@ -0,0 +1 @@
+0.0
diff --git a/releases/diff_defined_functions__0.0.zip b/releases/diff_defined_functions__0.0.zip
new file mode 100644 (file)
index 0000000..5b00e0f
Binary files /dev/null and b/releases/diff_defined_functions__0.0.zip differ
diff --git a/releases/old/diff_defined_functions__0.0__18.03.08_11:36:53.zip b/releases/old/diff_defined_functions__0.0__18.03.08_11:36:53.zip
new file mode 100644 (file)
index 0000000..5b00e0f
Binary files /dev/null and b/releases/old/diff_defined_functions__0.0__18.03.08_11:36:53.zip differ
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..148a4ef
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,12 @@
+
+from distutils.core import setup
+
+setup(
+    name = "ajpastor",
+    version = "0.0",
+    author = "Antonio Jimenez-Pastor",
+    author_email = "antonio.jimenez-pastor@dk-compmath.jku.at",
+    licence = "GPL",
+    packages = ["ajpastor", "ajpastor.dd_functions", "ajpastor.operator", "ajpastor.lazy", "ajpastor.misc", "ajpastor.tests"]
+    )
+    
diff --git a/type b/type
new file mode 100644 (file)
index 0000000..134d9bc
--- /dev/null
+++ b/type
@@ -0,0 +1 @@
+optional