+ @cached_method
+ def _charpoly_coeff(self, i):
+ """
+ Return the coefficient polynomial "a_{i}" of this algebra's
+ general characteristic polynomial.
+
+ Having this be a separate cached method lets us compute and
+ store the trace/determinant (a_{r-1} and a_{0} respectively)
+ separate from the entire characteristic polynomial.
+ """
+ (A_of_x, x) = self._charpoly_matrix()
+ R = A_of_x.base_ring()
+ A_cols = A_of_x.columns()
+ A_cols[i] = (x**self.rank()).vector()
+ numerator = column_matrix(A_of_x.base_ring(), A_cols).det()
+ denominator = A_of_x.det()
+
+ # We're relying on the theory here to ensure that each a_i is
+ # indeed back in R, and the added negative signs are to make
+ # the whole charpoly expression sum to zero.
+ return R(-numerator/denominator)
+
+
+ @cached_method
+ def _charpoly_matrix(self):
+ """
+ Compute the matrix whose entries A_ij are polynomials in
+ X1,...,XN. This same matrix is used in more than one method and
+ it's not so fast to construct.
+ """
+ r = self.rank()
+ n = self.dimension()
+
+ # Construct a new algebra over a multivariate polynomial ring...
+ names = ['X' + str(i) for i in range(1,n+1)]
+ R = PolynomialRing(self.base_ring(), names)
+ J = FiniteDimensionalEuclideanJordanAlgebra(R,
+ self._multiplication_table,
+ rank=r)
+
+ idmat = identity_matrix(J.base_ring(), n)
+
+ x = J(vector(R, R.gens()))
+ l1 = [column_matrix((x**k).vector()) for k in range(r)]
+ l2 = [idmat.column(k-1).column() for k in range(r+1, n+1)]
+ A_of_x = block_matrix(R, 1, n, (l1 + l2))
+ return (A_of_x, x)
+
+