# HG changeset patch
# User michielkosters
# Date 1324485447 18000
# Node ID a7759d44eac330ce5ea4fbaf0449ec1133a447a0
# Parent  e0c2d588aa083c63c85e2c69a011c84d5c28a92d
[mq]: finite_algebras

diff --git a/sage/algebras/finite_algebras/finite_algebra.py b/sage/algebras/finite_algebras/finite_algebra.py
--- a/sage/algebras/finite_algebras/finite_algebra.py
+++ b/sage/algebras/finite_algebras/finite_algebra.py
@@ -29,7 +29,7 @@
     - ``table`` - a list of matrices
     
     The list ``table`` must have the following form: there exists a
-    finite commutative ``k``-algebra of degree ``n`` with basis
+    finite dimensional ``k``-algebra of degree ``n`` with basis
     ``(b_1,...,b_n)`` such that the ``i``-th element of ``table`` is
     the matrix of right multiplication by ``b_i`` with respect to the basis
     ``(b_1,...,b_n)``.
@@ -55,25 +55,17 @@
         """
         self._table = [b.base_extend(k) for b in table]
         n = len(table)
+        self._degree = n
         for b in table:
             if b.nrows() != n or b.ncols() != n:
                 raise ValueError, "Input is not a multiplication table"
-        self._degree = n
-        B = reduce(lambda x, y: x.augment(y), self._table, Matrix(k, n, 0))
-        # This is the vector obtained by concatenating the rows of the
-        # n times n identity matrix:
-        if n == 0:
-            self._one = vector(k, [])
-        else:
-            v = vector(k, (n - 1) * ([1] + n * [0]) + [1])
-            self._one = B.solve_left(v)
         Algebra.__init__(self, base=k, category=category)
     
     Element = FiniteAlgebraElement
         
     def _repr_(self):
         """
-        Return string representation of self.
+        Return string representation of ``self``.
         
         EXAMPLE::
     
@@ -135,7 +127,7 @@
         
     def is_commutative(self):
         """
-        Return True if `self` is commutative.
+        Return True if ``self`` is commutative.
 
         EXAMPLES:: 
             
@@ -147,21 +139,117 @@
             sage: C.is_commutative()
             False
         """
-        try:
+        try: 
             return self._is_commutative
         except AttributeError:
-            B = self.basis()
-            for i in xrange(len(B)):
+            B = self.table()
+            C = self.left_table()
+            for i in xrange(self._degree):
                 for j in xrange(i):
-                    if B[i]*B[j] != B[j]*B[i]:
+                    if B[j][i] != B[i][j]:
                         self._is_commutative = False
                         return self._is_commutative
             self._is_commutative = True
-            return self._is_commutative
+            return self._is_commutative         
+
+    def left_table(self):
+        """
+        Return the left multiplication of the basis
+
+        EXAMPLES:
+
+            sage: B = FiniteAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1],[-1,0]])])
+            sage: B.left_table()
+            [
+            [1 0]  [ 0  1]
+            [0 1], [-1  0]
+            ]
+
+        """        
+        try:
+            return self._left_table
+        except AttributeError:
+            n = len(self._table)
+            self._left_table=[Matrix([self._table[j][i] for j in xrange(n)]) for i in xrange(n)] 
+            return self._left_table
+
+    def is_associative(self):
+        """
+        Return True if ``self`` is associative.
+
+        EXAMPLES::
+
+            sage: B = FiniteAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1],[-1,0]])])
+            sage: B.is_associative()
+            True
+            sage: B = FiniteAlgebra(QQ, [Matrix([[1,0,0], [0,1,0], [0,0,1]]), Matrix([[0,1,0], [0,0,0], [0,0,0]]), Matrix([[0,0,1], [0,0,0], [1,0,0]])])
+            sage: B.is_associative()
+            False
+            sage: C = B.basis()
+            sage: (C[1]*C[2])*C[2]==C[1]*(C[2]*C[2])
+            False
+        """
+        try:
+            return self._is_associative
+        except AttributeError:
+            B = self._table
+            n = len(B)
+            for i in xrange(n):
+                for j in xrange(n):
+                    for k in xrange(n):
+                        if sum([B[k][j][r]*B[r] for r in xrange(n)])[i] != B[j][i]*B[k]:
+                            self._is_associative = False
+                            return self._is_associative
+            self._is_associative = True
+            return self._is_associative 
+
+    def is_unitary(self):
+        """
+        Return True if ``self`` is unitary.
+
+        EXAMPLES::
+
+            sage: B = FiniteAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1],[-1,0]])])
+            sage: B.is_unitary()
+            True
+            sage: C = FiniteAlgebra(QQ, [Matrix([[0,0], [0,0]]), Matrix([[0,0],[0,0]])]) 
+            sage: C.is_unitary()
+            False
+        """
+        try:
+            return self._is_unitary
+        except AttributeError:
+            n = self._degree
+            k = self.base_ring()
+            B1 = reduce(lambda x, y: x.augment(y), self._table, Matrix(k, n, 0))
+            B2 = reduce(lambda x, y: x.augment(y), self.left_table(), Matrix(k, n, 0))
+            # This is the vector obtained by concatenating the rows of the
+            # n times n identity matrix:
+            if n == 0:
+                self._one = vector(k, [])
+            else:
+                v = vector(k, (n - 1) * ([1] + n * [0]) + [1])
+                try:
+                    sol1 = B1.solve_left(v)
+                except ValueError:
+                    self._is_unitary = False
+                    return self._is_unitary
+                try:
+                    sol2 = B2.solve_left(v) #  Do we solve the right equation??
+                except ValueError:
+                    self._is_unitary = False
+                    return self._is_unitary
+            if sol1 == sol2:
+                self._one = sol1
+                self._is_unitary = True
+                return self._is_unitary
+            else:
+                self._is_unitary = False
+                return self._is_unitary
 
     def table(self):
         """
-        Return the multiplication table of self.
+        Return the multiplication table of ``self``.
 
         EXAMPLE::
             
@@ -176,7 +264,7 @@
 
     def degree(self):
         """
-        Return the degree of self over its base field.
+        Return the degree of ``self`` over its base field.
         
         EXAMPLE::
             
@@ -188,7 +276,7 @@
 
     def basis(self):
         """
-        Return a list of FiniteAlgebraElements forming a basis of self.
+        Return a list of FiniteAlgebraElements forming a basis of ``self``.
         
         EXAMPLE::
             
diff --git a/sage/algebras/finite_algebras/finite_algebra_element.py b/sage/algebras/finite_algebras/finite_algebra_element.py
--- a/sage/algebras/finite_algebras/finite_algebra_element.py
+++ b/sage/algebras/finite_algebras/finite_algebra_element.py
@@ -26,7 +26,7 @@
     INPUT:
     - ``A`` - FiniteAlgebra
     - ``elt`` (default: None) - vector or matrix
-
+    - ``check`` (default: True) - on false it assumes that 
     If ``elt`` is a vector, it is interpreted as a vector of
     coordinates with respect to the given basis of ``A``.  If ``elt``
     is a matrix, it is interpreted as a multiplication matrix with
@@ -43,7 +43,7 @@
         [0 1]
     """
 
-    def __init__(self, A, elt=None):
+    def __init__(self, A, elt=None, check=True):
         """
         TESTS::
         
@@ -52,6 +52,14 @@
             Traceback (most recent call last):
             ...
             TypeError: elt should be a matrix, a vector, or an element of the base field
+            sage: B = FiniteAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1],[-1,0]])])
+            sage: B(Matrix([[1,1],[-1,1]]))
+            [ 1  1]
+            [-1  1]
+            sage: B(Matrix([[0,1],[1,0]]))
+            Traceback (most recent call last):
+            ...
+            ValueError: matrix does not define an element of the algebra
         """
         AlgebraElement.__init__(self, A)
         n = A.degree()
@@ -64,12 +72,21 @@
             if isinstance(elt, list):
                 elt = vector(elt)
             if A.base_ring().has_coerce_map_from(elt.parent()):
-                e = A.base_ring()(elt)
-                self._vector = A._one * e
-                self._matrix = identity_matrix(A.base_ring(), n) * e
+                if A.is_unitary():               
+                    e = A.base_ring()(elt)
+                    self._vector = A._one * e
+                    self._matrix = identity_matrix(A.base_ring(), n) * e
+                else:
+                    raise ValueError, "algebra is not unitary"
             elif is_Matrix(elt):
-                self._vector = A._one * elt
-                self._matrix = elt
+                if A.is_unitary():
+                    self._vector = A._one * elt 
+                    if not check or sum([self._vector[i]*A.table()[i] for i in xrange(n)]) == elt: 
+                        self._matrix = elt
+                    else:
+                        raise ValueError, "matrix does not define an element of the algebra"
+                else:
+                    raise ValueError, "algebra is not unitary"
             elif is_Vector(elt):
                 self._vector = elt
                 self._matrix = sum([elt[i] * A.table()[i] for i in xrange(n)])
@@ -281,7 +298,9 @@
             [   0   64    0]
             [   0    0 4096]
         """
-        return FiniteAlgebraElement(self.parent(), self.matrix() ** n)
+        if not self.parent().is_associative():
+            raise TypeError, 'powers are not well-defined in a non-associative algebra'
+        return FiniteAlgebraElement(self.parent(), self.matrix() ** n, check=False)
 
     def vector(self):
         """
