+ This works around the inability to scale certain elements of
+ Cartesian product spaces, as reported in
+
+ https://trac.sagemath.org/ticket/31435
+
+ ..WARNING:
+
+ This will do the wrong thing if you feed it a tuple or list.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_utils import _scale
+
+ EXAMPLES::
+
+ sage: v = vector(QQ, (1,2,3))
+ sage: _scale(v,2)
+ (2, 4, 6)
+ sage: m = matrix(QQ, [[1,2],[3,4]])
+ sage: M = cartesian_product([m.parent(), m.parent()])
+ sage: _scale(M((m,m)), 2)
+ ([2 4]
+ [6 8], [2 4]
+ [6 8])
+
+ """
+ if hasattr(x, 'cartesian_factors'):
+ P = x.parent()
+ return P(tuple( _scale(x_i, alpha)
+ for x_i in x.cartesian_factors() ))
+ else:
+ return x*alpha
+
+
+def _all2list(x):
+ r"""
+ Flatten a vector, matrix, or cartesian product of those things
+ into a long list.
+
+ If the entries of the matrix themselves belong to a real vector
+ space (such as the complex numbers which can be thought of as
+ pairs of real numbers), they will also be expanded in vector form
+ and flattened into the list.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_utils import _all2list
+ sage: from mjo.hurwitz import (QuaternionMatrixAlgebra,
+ ....: Octonions,
+ ....: OctonionMatrixAlgebra)
+
+ EXAMPLES::
+
+ sage: _all2list([[1]])
+ [1]
+
+ ::
+
+ sage: V1 = VectorSpace(QQ,2)
+ sage: V2 = MatrixSpace(QQ,2)
+ sage: x1 = V1([1,1])
+ sage: x2 = V1([1,-1])
+ sage: y1 = V2.one()
+ sage: y2 = V2([0,1,1,0])
+ sage: _all2list((x1,y1))
+ [1, 1, 1, 0, 0, 1]
+ sage: _all2list((x2,y2))
+ [1, -1, 0, 1, 1, 0]
+ sage: M = cartesian_product([V1,V2])
+ sage: _all2list(M((x1,y1)))
+ [1, 1, 1, 0, 0, 1]
+ sage: _all2list(M((x2,y2)))
+ [1, -1, 0, 1, 1, 0]
+
+ ::
+
+ sage: _all2list(Octonions().one())
+ [1, 0, 0, 0, 0, 0, 0, 0]
+ sage: _all2list(OctonionMatrixAlgebra(1).one())
+ [1, 0, 0, 0, 0, 0, 0, 0]
+
+ ::
+
+ sage: _all2list(QuaternionAlgebra(QQ, -1, -1).one())
+ [1, 0, 0, 0]
+ sage: _all2list(QuaternionMatrixAlgebra(1).one())
+ [1, 0, 0, 0]
+
+ ::
+
+ sage: V1 = VectorSpace(QQ,2)
+ sage: V2 = OctonionMatrixAlgebra(1,field=QQ)
+ sage: C = cartesian_product([V1,V2])
+ sage: x1 = V1([3,4])
+ sage: y1 = V2.one()
+ sage: _all2list(C( (x1,y1) ))
+ [3, 4, 1, 0, 0, 0, 0, 0, 0, 0]
+
+ """
+ if hasattr(x, 'to_vector'):
+ # This works on matrices of e.g. octonions directly, without
+ # first needing to convert them to a list of octonions and
+ # then recursing down into the list. It also avoids the wonky
+ # list(x) when x is an element of a CFM. I don't know what it
+ # returns but it aint the coordinates. We don't recurse
+ # because vectors can only contain ring elements as entries.
+ return x.to_vector().list()
+
+ if is_Matrix(x):
+ # This sucks, but for performance reasons we don't want to
+ # call _all2list recursively on the contents of a matrix
+ # when we don't have to (they only contain ring elements
+ # as entries)
+ return x.list()
+
+ try:
+ xl = list(x)
+ except TypeError: # x is not iterable
+ return [x]
+
+ if xl == [x]:
+ # Avoid the retardation of list(QQ(1)) == [1].
+ return [x]
+
+ return sum( map(_all2list, xl) , [])
+
+
+def gram_schmidt(v, inner_product=None):