-# class DirectSumEJA(ConcreteEJA):
-# r"""
-# The external (orthogonal) direct sum of two other Euclidean Jordan
-# algebras. Essentially the Cartesian product of its two factors.
-# Every Euclidean Jordan algebra decomposes into an orthogonal
-# direct sum of simple Euclidean Jordan algebras, so no generality
-# is lost by providing only this construction.
-
-# SETUP::
-
-# sage: from mjo.eja.eja_algebra import (random_eja,
-# ....: HadamardEJA,
-# ....: RealSymmetricEJA,
-# ....: DirectSumEJA)
-
-# EXAMPLES::
-
-# sage: J1 = HadamardEJA(2)
-# sage: J2 = RealSymmetricEJA(3)
-# sage: J = DirectSumEJA(J1,J2)
-# sage: J.dimension()
-# 8
-# sage: J.rank()
-# 5
-
-# TESTS:
-
-# The external direct sum construction is only valid when the two factors
-# have the same base ring; an error is raised otherwise::
-
-# sage: set_random_seed()
-# sage: J1 = random_eja(field=AA)
-# sage: J2 = random_eja(field=QQ,orthonormalize=False)
-# sage: J = DirectSumEJA(J1,J2)
-# Traceback (most recent call last):
-# ...
-# ValueError: algebras must share the same base field
-
-# """
-# def __init__(self, J1, J2, **kwargs):
-# if J1.base_ring() != J2.base_ring():
-# raise ValueError("algebras must share the same base field")
-# field = J1.base_ring()
-
-# self._factors = (J1, J2)
-# n1 = J1.dimension()
-# n2 = J2.dimension()
-# n = n1+n2
-# V = VectorSpace(field, n)
-# mult_table = [ [ V.zero() for j in range(i+1) ]
-# for i in range(n) ]
-# for i in range(n1):
-# for j in range(i+1):
-# p = (J1.monomial(i)*J1.monomial(j)).to_vector()
-# mult_table[i][j] = V(p.list() + [field.zero()]*n2)
-
-# for i in range(n2):
-# for j in range(i+1):
-# p = (J2.monomial(i)*J2.monomial(j)).to_vector()
-# mult_table[n1+i][n1+j] = V([field.zero()]*n1 + p.list())
-
-# # TODO: build the IP table here from the two constituent IP
-# # matrices (it'll be block diagonal, I think).
-# ip_table = [ [ field.zero() for j in range(i+1) ]
-# for i in range(n) ]
-# super(DirectSumEJA, self).__init__(field,
-# mult_table,
-# ip_table,
-# check_axioms=False,
-# **kwargs)
-# self.rank.set_cache(J1.rank() + J2.rank())
-
-
-# def factors(self):
-# r"""
-# Return the pair of this algebra's factors.
-
-# SETUP::
-
-# sage: from mjo.eja.eja_algebra import (HadamardEJA,
-# ....: JordanSpinEJA,
-# ....: DirectSumEJA)
-
-# EXAMPLES::
-
-# sage: J1 = HadamardEJA(2, field=QQ)
-# sage: J2 = JordanSpinEJA(3, field=QQ)
-# sage: J = DirectSumEJA(J1,J2)
-# sage: J.factors()
-# (Euclidean Jordan algebra of dimension 2 over Rational Field,
-# Euclidean Jordan algebra of dimension 3 over Rational Field)
-
-# """
-# return self._factors
-
-# def projections(self):
-# r"""
-# Return a pair of projections onto this algebra's factors.
-
-# SETUP::
-
-# sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
-# ....: ComplexHermitianEJA,
-# ....: DirectSumEJA)
-
-# EXAMPLES::
-
-# sage: J1 = JordanSpinEJA(2)
-# sage: J2 = ComplexHermitianEJA(2)
-# sage: J = DirectSumEJA(J1,J2)
-# sage: (pi_left, pi_right) = J.projections()
-# sage: J.one().to_vector()
-# (1, 0, 1, 0, 0, 1)
-# sage: pi_left(J.one()).to_vector()
-# (1, 0)
-# sage: pi_right(J.one()).to_vector()
-# (1, 0, 0, 1)
-
-# """
-# (J1,J2) = self.factors()
-# m = J1.dimension()
-# n = J2.dimension()
-# V_basis = self.vector_space().basis()
-# # Need to specify the dimensions explicitly so that we don't
-# # wind up with a zero-by-zero matrix when we want e.g. a
-# # zero-by-two matrix (important for composing things).
-# P1 = matrix(self.base_ring(), m, m+n, V_basis[:m])
-# P2 = matrix(self.base_ring(), n, m+n, V_basis[m:])
-# pi_left = FiniteDimensionalEJAOperator(self,J1,P1)
-# pi_right = FiniteDimensionalEJAOperator(self,J2,P2)
-# return (pi_left, pi_right)
-
-# def inclusions(self):
-# r"""
-# Return the pair of inclusion maps from our factors into us.
-
-# SETUP::
-
-# sage: from mjo.eja.eja_algebra import (random_eja,
-# ....: JordanSpinEJA,
-# ....: RealSymmetricEJA,
-# ....: DirectSumEJA)
-
-# EXAMPLES::
-
-# sage: J1 = JordanSpinEJA(3)
-# sage: J2 = RealSymmetricEJA(2)
-# sage: J = DirectSumEJA(J1,J2)
-# sage: (iota_left, iota_right) = J.inclusions()
-# sage: iota_left(J1.zero()) == J.zero()
-# True
-# sage: iota_right(J2.zero()) == J.zero()
-# True
-# sage: J1.one().to_vector()
-# (1, 0, 0)
-# sage: iota_left(J1.one()).to_vector()
-# (1, 0, 0, 0, 0, 0)
-# sage: J2.one().to_vector()
-# (1, 0, 1)
-# sage: iota_right(J2.one()).to_vector()
-# (0, 0, 0, 1, 0, 1)
-# sage: J.one().to_vector()
-# (1, 0, 0, 1, 0, 1)
-
-# TESTS:
-
-# Composing a projection with the corresponding inclusion should
-# produce the identity map, and mismatching them should produce
-# the zero map::
-
-# sage: set_random_seed()
-# sage: J1 = random_eja()
-# sage: J2 = random_eja()
-# sage: J = DirectSumEJA(J1,J2)
-# sage: (iota_left, iota_right) = J.inclusions()
-# sage: (pi_left, pi_right) = J.projections()
-# sage: pi_left*iota_left == J1.one().operator()
-# True
-# sage: pi_right*iota_right == J2.one().operator()
-# True
-# sage: (pi_left*iota_right).is_zero()
-# True
-# sage: (pi_right*iota_left).is_zero()
-# True
-
-# """
-# (J1,J2) = self.factors()
-# m = J1.dimension()
-# n = J2.dimension()
-# V_basis = self.vector_space().basis()
-# # Need to specify the dimensions explicitly so that we don't
-# # wind up with a zero-by-zero matrix when we want e.g. a
-# # two-by-zero matrix (important for composing things).
-# I1 = matrix.column(self.base_ring(), m, m+n, V_basis[:m])
-# I2 = matrix.column(self.base_ring(), n, m+n, V_basis[m:])
-# iota_left = FiniteDimensionalEJAOperator(J1,self,I1)
-# iota_right = FiniteDimensionalEJAOperator(J2,self,I2)
-# return (iota_left, iota_right)
-
-# def inner_product(self, x, y):
-# r"""
-# The standard Cartesian inner-product.
-
-# We project ``x`` and ``y`` onto our factors, and add up the
-# inner-products from the subalgebras.
-
-# SETUP::
-
-
-# sage: from mjo.eja.eja_algebra import (HadamardEJA,
-# ....: QuaternionHermitianEJA,
-# ....: DirectSumEJA)
-
-# EXAMPLE::
-
-# sage: J1 = HadamardEJA(3,field=QQ)
-# sage: J2 = QuaternionHermitianEJA(2,field=QQ,orthonormalize=False)
-# sage: J = DirectSumEJA(J1,J2)
-# sage: x1 = J1.one()
-# sage: x2 = x1
-# sage: y1 = J2.one()
-# sage: y2 = y1
-# sage: x1.inner_product(x2)
-# 3
-# sage: y1.inner_product(y2)
-# 2
-# sage: J.one().inner_product(J.one())
-# 5
-
-# """
-# (pi_left, pi_right) = self.projections()
-# x1 = pi_left(x)
-# x2 = pi_right(x)
-# y1 = pi_left(y)
-# y2 = pi_right(y)
-
-# return (x1.inner_product(y1) + x2.inner_product(y2))