from sympy import Matrix, eye
from sympy.combinatorics import Permutation
from sympy.core import S, Rational, Symbol, Basic
from sympy.core.containers import Tuple
from sympy.core.symbol import symbols
from sympy.external import import_module
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.printing.pretty.pretty import pretty
from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorSymmetry, \
    get_symmetric_group_sgs, TensorType, TensorIndex, tensor_mul, TensAdd, \
    riemann_cyclic_replace, riemann_cyclic, TensMul, \
    tensorsymmetry, tensorhead, TensorManager, TensExpr, TIDS
from sympy.utilities.pytest import raises, skip


#################### Tests from tensor_can.py #######################
def test_canonicalize_no_slot_sym():
    # A_d0 * B^d0; T_c = A^d0*B_d0
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    a, b, d0, d1 = tensor_indices('a,b,d0,d1', Lorentz)
    sym1 = tensorsymmetry([1])
    S1 = TensorType([Lorentz], sym1)
    A, B = S1('A,B')
    t = A(-d0)*B(d0)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0)*B(-L_0)'

    # A^a * B^b;  T_c = T
    t = A(a)*B(b)
    tc = t.canon_bp()
    assert tc == t
    # B^b * A^a
    t1 = B(b)*A(a)
    tc = t1.canon_bp()
    assert str(tc) == 'A(a)*B(b)'

    # A symmetric
    # A^{b}_{d0}*A^{d0, a}; T_c = A^{a d0}*A{b}_{d0}
    sym2 = tensorsymmetry([1]*2)
    S2 = TensorType([Lorentz]*2, sym2)
    A = S2('A')
    t = A(b, -d0)*A(d0, a)
    tc = t.canon_bp()
    assert str(tc) == 'A(a, L_0)*A(b, -L_0)'

    # A^{d1}_{d0}*B^d0*C_d1
    # T_c = A^{d0 d1}*B_d0*C_d1
    B, C = S1('B,C')
    t = A(d1, -d0)*B(d0)*C(-d1)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0, L_1)*B(-L_0)*C(-L_1)'

    # A without symmetry
    # A^{d1}_{d0}*B^d0*C_d1 ord=[d0,-d0,d1,-d1]; g = [2,1,0,3,4,5]
    # T_c = A^{d0 d1}*B_d1*C_d0; can = [0,2,3,1,4,5]
    nsym2 = tensorsymmetry([1],[1])
    NS2 = TensorType([Lorentz]*2, nsym2)
    A = NS2('A')
    B, C = S1('B, C')
    t = A(d1, -d0)*B(d0)*C(-d1)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0, L_1)*B(-L_1)*C(-L_0)'

    # A, B without symmetry
    # A^{d1}_{d0}*B_{d1}^{d0}
    # T_c = A^{d0 d1}*B_{d0 d1}
    B = NS2('B')
    t = A(d1, -d0)*B(-d1, d0)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0, L_1)*B(-L_0, -L_1)'
    # A_{d0}^{d1}*B_{d1}^{d0}
    # T_c = A^{d0 d1}*B_{d1 d0}
    t = A(-d0, d1)*B(-d1, d0)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0, L_1)*B(-L_1, -L_0)'

    # A, B, C without symmetry
    # A^{d1 d0}*B_{a d0}*C_{d1 b}
    # T_c=A^{d0 d1}*B_{a d1}*C_{d0 b}
    C = NS2('C')
    t = A(d1, d0)*B(-a, -d0)*C(-d1, -b)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0, L_1)*B(-a, -L_1)*C(-L_0, -b)'

    # A symmetric, B and C without symmetry
    # A^{d1 d0}*B_{a d0}*C_{d1 b}
    # T_c = A^{d0 d1}*B_{a d0}*C_{d1 b}
    A = S2('A')
    t = A(d1, d0)*B(-a, -d0)*C(-d1, -b)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0, L_1)*B(-a, -L_0)*C(-L_1, -b)'

    # A and C symmetric, B without symmetry
    # A^{d1 d0}*B_{a d0}*C_{d1 b} ord=[a,b,d0,-d0,d1,-d1]
    # T_c = A^{d0 d1}*B_{a d0}*C_{b d1}
    C = S2('C')
    t = A(d1, d0)*B(-a, -d0)*C(-d1, -b)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0, L_1)*B(-a, -L_0)*C(-b, -L_1)'

def test_canonicalize_no_dummies():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    a, b, c, d = tensor_indices('a, b, c, d', Lorentz)
    sym1 = tensorsymmetry([1])
    sym2 = tensorsymmetry([1]*2)
    sym2a = tensorsymmetry([2])

    # A commuting
    # A^c A^b A^a
    # T_c = A^a A^b A^c
    S1 = TensorType([Lorentz], sym1)
    A = S1('A')
    t = A(c)*A(b)*A(a)
    tc = t.canon_bp()
    assert str(tc) == 'A(a)*A(b)*A(c)'

    # A anticommuting
    # A^c A^b A^a
    # T_c = -A^a A^b A^c
    A = S1('A', 1)
    t = A(c)*A(b)*A(a)
    tc = t.canon_bp()
    assert str(tc) == '-A(a)*A(b)*A(c)'

    # A commuting and symmetric
    # A^{b,d}*A^{c,a}
    # T_c = A^{a c}*A^{b d}
    S2 = TensorType([Lorentz]*2, sym2)
    A = S2('A')
    t = A(b, d)*A(c, a)
    tc = t.canon_bp()
    assert str(tc) == 'A(a, c)*A(b, d)'

    # A anticommuting and symmetric
    # A^{b,d}*A^{c,a}
    # T_c = -A^{a c}*A^{b d}
    A = S2('A', 1)
    t = A(b, d)*A(c, a)
    tc = t.canon_bp()
    assert str(tc) == '-A(a, c)*A(b, d)'

    # A^{c,a}*A^{b,d}
    # T_c = A^{a c}*A^{b d}
    t = A(c, a)*A(b, d)
    tc = t.canon_bp()
    assert str(tc) == 'A(a, c)*A(b, d)'

def test_no_metric_symmetry():
    # no metric symmetry; A no symmetry
    # A^d1_d0 * A^d0_d1
    # T_c = A^d0_d1 * A^d1_d0
    Lorentz = TensorIndexType('Lorentz', metric=None, dummy_fmt='L')
    d0, d1, d2, d3 = tensor_indices('d:4', Lorentz)
    A = tensorhead('A', [Lorentz]*2, [[1], [1]])
    t = A(d1, -d0)*A(d0, -d1)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0, -L_1)*A(L_1, -L_0)'

    # A^d1_d2 * A^d0_d3 * A^d2_d1 * A^d3_d0
    # T_c = A^d0_d1 * A^d1_d0 * A^d2_d3 * A^d3_d2
    t = A(d1, -d2)*A(d0, -d3)*A(d2,-d1)*A(d3,-d0)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0, -L_1)*A(L_1, -L_0)*A(L_2, -L_3)*A(L_3, -L_2)'

    # A^d0_d2 * A^d1_d3 * A^d3_d0 * A^d2_d1
    # T_c = A^d0_d1 * A^d1_d2 * A^d2_d3 * A^d3_d0
    t = A(d0, -d1)*A(d1, -d2)*A(d2, -d3)*A(d3,-d0)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0, -L_1)*A(L_1, -L_2)*A(L_2, -L_3)*A(L_3, -L_0)'

def test_canonicalize1():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    a, a0, a1, a2, a3, b, d0, d1, d2, d3 = \
      tensor_indices('a,a0,a1,a2,a3,b,d0,d1,d2,d3', Lorentz)
    sym1 = tensorsymmetry([1])
    base3, gens3 = get_symmetric_group_sgs(3)
    sym2 = tensorsymmetry([1]*2)
    sym2a = tensorsymmetry([2])
    sym3 = tensorsymmetry([1]*3)
    sym3a = tensorsymmetry([3])

    # A_d0*A^d0; ord = [d0,-d0]
    # T_c = A^d0*A_d0
    S1 = TensorType([Lorentz], sym1)
    A = S1('A')
    t = A(-d0)*A(d0)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0)*A(-L_0)'

    # A commuting
    # A_d0*A_d1*A_d2*A^d2*A^d1*A^d0
    # T_c = A^d0*A_d0*A^d1*A_d1*A^d2*A_d2
    t = A(-d0)*A(-d1)*A(-d2)*A(d2)*A(d1)*A(d0)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0)*A(-L_0)*A(L_1)*A(-L_1)*A(L_2)*A(-L_2)'

    # A anticommuting
    # A_d0*A_d1*A_d2*A^d2*A^d1*A^d0
    # T_c 0
    A = S1('A', 1)
    t = A(-d0)*A(-d1)*A(-d2)*A(d2)*A(d1)*A(d0)
    tc = t.canon_bp()
    assert tc == 0

    # A commuting symmetric
    # A^{d0 b}*A^a_d1*A^d1_d0
    # T_c = A^{a d0}*A^{b d1}*A_{d0 d1}
    S2 = TensorType([Lorentz]*2, sym2)
    A = S2('A')
    t = A(d0, b)*A(a, -d1)*A(d1, -d0)
    tc = t.canon_bp()
    assert str(tc) == 'A(a, L_0)*A(b, L_1)*A(-L_0, -L_1)'

    # A, B commuting symmetric
    # A^{d0 b}*A^d1_d0*B^a_d1
    # T_c = A^{b d0}*A_d0^d1*B^a_d1
    B = S2('B')
    t = A(d0, b)*A(d1, -d0)*B(a, -d1)
    tc = t.canon_bp()
    assert str(tc) == 'A(b, L_0)*A(-L_0, L_1)*B(a, -L_1)'

    # A commuting symmetric
    # A^{d1 d0 b}*A^{a}_{d1 d0}; ord=[a,b, d0,-d0,d1,-d1]
    # T_c = A^{a d0 d1}*A^{b}_{d0 d1}
    S3 = TensorType([Lorentz]*3, sym3)
    A = S3('A')
    t = A(d1, d0, b)*A(a, -d1, -d0)
    tc = t.canon_bp()
    assert str(tc) == 'A(a, L_0, L_1)*A(b, -L_0, -L_1)'

    # A^{d3 d0 d2}*A^a0_{d1 d2}*A^d1_d3^a1*A^{a2 a3}_d0
    # T_c = A^{a0 d0 d1}*A^a1_d0^d2*A^{a2 a3 d3}*A_{d1 d2 d3}
    t = A(d3, d0, d2)*A(a0, -d1, -d2)*A(d1, -d3, a1)*A(a2, a3, -d0)
    tc = t.canon_bp()
    assert str(tc) == 'A(a0, L_0, L_1)*A(a1, -L_0, L_2)*A(a2, a3, L_3)*A(-L_1, -L_2, -L_3)'

    # A commuting symmetric, B antisymmetric
    # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3
    # in this esxample and in the next three,
    # renaming dummy indices and using symmetry of A,
    # T = A^{d0 d1 d2} * A_{d0 d1 d3} * B_d2^d3
    # can = 0
    S2a = TensorType([Lorentz]*2, sym2a)
    A = S3('A')
    B = S2a('B')
    t = A(d0, d1, d2)*A(-d2, -d3, -d1)*B(-d0, d3)
    tc = t.canon_bp()
    assert tc == 0

    # A anticommuting symmetric, B anticommuting
    # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3
    # T_c = A^{d0 d1 d2} * A_{d0 d1}^d3 * B_{d2 d3}
    A = S3('A', 1)
    B = S2a('B')
    t = A(d0, d1, d2)*A(-d2, -d3, -d1)*B(-d0, d3)
    tc = t.canon_bp()
    assert str(tc) == 'A(L_0, L_1, L_2)*A(-L_0, -L_1, L_3)*B(-L_2, -L_3)'

    # A anticommuting symmetric, B antisymmetric commuting, antisymmetric metric
    # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3
    # T_c = -A^{d0 d1 d2} * A_{d0 d1}^d3 * B_{d2 d3}
    Spinor = TensorIndexType('Spinor', metric=1, dummy_fmt='S')
    a, a0, a1, a2, a3, b, d0, d1, d2, d3 = \
      tensor_indices('a,a0,a1,a2,a3,b,d0,d1,d2,d3', Spinor)
    S3 = TensorType([Spinor]*3, sym3)
    S2a = TensorType([Spinor]*2, sym2a)
    A = S3('A', 1)
    B = S2a('B')
    t = A(d0, d1, d2)*A(-d2, -d3, -d1)*B(-d0, d3)
    tc = t.canon_bp()
    assert str(tc) == '-A(S_0, S_1, S_2)*A(-S_0, -S_1, S_3)*B(-S_2, -S_3)'

    # A anticommuting symmetric, B antisymmetric anticommuting,
    # no metric symmetry
    # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3
    # T_c = A^{d0 d1 d2} * A_{d0 d1 d3} * B_d2^d3
    Mat = TensorIndexType('Mat', metric=None, dummy_fmt='M')
    a, a0, a1, a2, a3, b, d0, d1, d2, d3 = \
      tensor_indices('a,a0,a1,a2,a3,b,d0,d1,d2,d3', Mat)
    S3 = TensorType([Mat]*3, sym3)
    S2a = TensorType([Mat]*2, sym2a)
    A = S3('A', 1)
    B = S2a('B')
    t = A(d0, d1, d2)*A(-d2, -d3, -d1)*B(-d0, d3)
    tc = t.canon_bp()
    assert str(tc) == 'A(M_0, M_1, M_2)*A(-M_0, -M_1, -M_3)*B(-M_2, M_3)'

    # Gamma anticommuting
    # Gamma_{mu nu} * gamma^rho * Gamma^{nu mu alpha}
    # T_c = -Gamma^{mu nu} * gamma^rho * Gamma_{alpha mu nu}
    S1 = TensorType([Lorentz], sym1)
    S2a = TensorType([Lorentz]*2, sym2a)
    S3a = TensorType([Lorentz]*3, sym3a)
    alpha, beta, gamma, mu, nu, rho = \
      tensor_indices('alpha,beta,gamma,mu,nu,rho', Lorentz)
    Gamma = S1('Gamma', 2)
    Gamma2 = S2a('Gamma', 2)
    Gamma3 = S3a('Gamma', 2)
    t = Gamma2(-mu,-nu)*Gamma(rho)*Gamma3(nu, mu, alpha)
    tc = t.canon_bp()
    assert str(tc) == '-Gamma(L_0, L_1)*Gamma(rho)*Gamma(alpha, -L_0, -L_1)'

    # Gamma_{mu nu} * Gamma^{gamma beta} * gamma_rho * Gamma^{nu mu alpha}
    # T_c = Gamma^{mu nu} * Gamma^{beta gamma} * gamma_rho * Gamma^alpha_{mu nu}
    t = Gamma2(mu, nu)*Gamma2(beta, gamma)*Gamma(-rho)*Gamma3(alpha, -mu, -nu)
    tc = t.canon_bp()
    assert str(tc) == 'Gamma(L_0, L_1)*Gamma(beta, gamma)*Gamma(-rho)*Gamma(alpha, -L_0, -L_1)'

    # f^a_{b,c} antisymmetric in b,c; A_mu^a no symmetry
    # f^c_{d a} * f_{c e b} * A_mu^d * A_nu^a * A^{nu e} * A^{mu b}
    # g = [8,11,5, 9,13,7, 1,10, 3,4, 2,12, 0,6, 14,15]
    # T_c = -f^{a b c} * f_a^{d e} * A^mu_b * A_{mu d} * A^nu_c * A_{nu e}
    Flavor = TensorIndexType('Flavor', dummy_fmt='F')
    a, b, c, d, e, ff = tensor_indices('a,b,c,d,e,f', Flavor)
    mu, nu = tensor_indices('mu,nu', Lorentz)
    sym_f = tensorsymmetry([1], [2])
    S_f = TensorType([Flavor]*3, sym_f)
    sym_A = tensorsymmetry([1], [1])
    S_A = TensorType([Lorentz, Flavor], sym_A)
    f = S_f('f')
    A = S_A('A')
    t = f(c, -d, -a)*f(-c, -e, -b)*A(-mu, d)*A(-nu, a)*A(nu, e)*A(mu, b)
    tc = t.canon_bp()
    assert str(tc) == '-f(F_0, F_1, F_2)*f(-F_0, F_3, F_4)*A(L_0, -F_1)*A(-L_0, -F_3)*A(L_1, -F_2)*A(-L_1, -F_4)'

def test_bug_correction_tensor_indices():
    # to make sure that tensor_indices does not return a list if creating
    # only one index:
    from sympy.tensor.tensor import tensor_indices, TensorIndexType, TensorIndex
    A = TensorIndexType("A")
    i = tensor_indices('i', A)
    assert not isinstance(i, (tuple, list))
    assert isinstance(i, TensorIndex)

def test_riemann_invariants():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11 = \
            tensor_indices('d0:12', Lorentz)
    # R^{d0 d1}_{d1 d0}; ord = [d0,-d0,d1,-d1]
    # T_c = -R^{d0 d1}_{d0 d1}
    R = tensorhead('R', [Lorentz]*4, [[2, 2]])
    t = R(d0, d1, -d1, -d0)
    tc = t.canon_bp()
    assert str(tc) == '-R(L_0, L_1, -L_0, -L_1)'

    # R_d11^d1_d0^d5 * R^{d6 d4 d0}_d5 * R_{d7 d2 d8 d9} *
    # R_{d10 d3 d6 d4} * R^{d2 d7 d11}_d1 * R^{d8 d9 d3 d10}
    # can = [0,2,4,6, 1,3,8,10, 5,7,12,14, 9,11,16,18, 13,15,20,22,
    #        17,19,21<F10,23, 24,25]
    # T_c = R^{d0 d1 d2 d3} * R_{d0 d1}^{d4 d5} * R_{d2 d3}^{d6 d7} *
    # R_{d4 d5}^{d8 d9} * R_{d6 d7}^{d10 d11} * R_{d8 d9 d10 d11}


    t = R(-d11,d1,-d0,d5)*R(d6,d4,d0,-d5)*R(-d7,-d2,-d8,-d9)* \
        R(-d10,-d3,-d6,-d4)*R(d2,d7,d11,-d1)*R(d8,d9,d3,d10)
    tc = t.canon_bp()
    assert str(tc) == 'R(L_0, L_1, L_2, L_3)*R(-L_0, -L_1, L_4, L_5)*R(-L_2, -L_3, L_6, L_7)*R(-L_4, -L_5, L_8, L_9)*R(-L_6, -L_7, L_10, L_11)*R(-L_8, -L_9, -L_10, -L_11)'

def test_riemann_products():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    d0, d1, d2, d3, d4, d5, d6 = tensor_indices('d0:7', Lorentz)
    a0, a1, a2, a3, a4, a5 = tensor_indices('a0:6', Lorentz)
    a, b = tensor_indices('a,b', Lorentz)
    R = tensorhead('R', [Lorentz]*4, [[2, 2]])
    # R^{a b d0}_d0 = 0
    t = R(a, b, d0, -d0)
    tc = t.canon_bp()
    assert tc == 0

    # R^{d0 b a}_d0
    # T_c = -R^{a d0 b}_d0
    t = R(d0, b, a, -d0)
    tc = t.canon_bp()
    assert str(tc) == '-R(a, L_0, b, -L_0)'

    # R^d1_d2^b_d0 * R^{d0 a}_d1^d2; ord=[a,b,d0,-d0,d1,-d1,d2,-d2]
    # T_c = -R^{a d0 d1 d2}* R^b_{d0 d1 d2}
    t = R(d1, -d2, b, -d0)*R(d0, a, -d1, d2)
    tc = t.canon_bp()
    assert str(tc) == '-R(a, L_0, L_1, L_2)*R(b, -L_0, -L_1, -L_2)'

    # A symmetric commuting
    # R^{d6 d5}_d2^d1 * R^{d4 d0 d2 d3} * A_{d6 d0} A_{d3 d1} * A_{d4 d5}
    # g = [12,10,5,2, 8,0,4,6, 13,1, 7,3, 9,11,14,15]
    # T_c = -R^{d0 d1 d2 d3} * R_d0^{d4 d5 d6} * A_{d1 d4}*A_{d2 d5}*A_{d3 d6}
    V = tensorhead('V', [Lorentz]*2, [[1]*2])
    t = R(d6, d5, -d2, d1)*R(d4, d0, d2, d3)*V(-d6, -d0)*V(-d3, -d1)*V(-d4, -d5)
    tc = t.canon_bp()
    assert str(tc) == '-R(L_0, L_1, L_2, L_3)*R(-L_0, L_4, L_5, L_6)*V(-L_1, -L_4)*V(-L_2, -L_5)*V(-L_3, -L_6)'

    # R^{d2 a0 a2 d0} * R^d1_d2^{a1 a3} * R^{a4 a5}_{d0 d1}
    # T_c = R^{a0 d0 a2 d1}*R^{a1 a3}_d0^d2*R^{a4 a5}_{d1 d2}
    t = R(d2, a0, a2, d0)*R(d1, -d2, a1, a3)*R(a4, a5, -d0, -d1)
    tc = t.canon_bp()
    assert str(tc) == 'R(a0, L_0, a2, L_1)*R(a1, a3, -L_0, L_2)*R(a4, a5, -L_1, -L_2)'
######################################################################

def test_canonicalize2():
    D = Symbol('D')
    Eucl = TensorIndexType('Eucl', metric=0, dim=D, dummy_fmt='E')
    i0,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14 = \
            tensor_indices('i0:15', Eucl)
    A = tensorhead('A', [Eucl]*3, [[3]])

    # two examples from Cvitanovic, Group Theory page 59
    # of identities for antisymmetric tensors of rank 3
    # contracted according to the Kuratowski graph  eq.(6.59)
    t = A(i0,i1,i2)*A(-i1,i3,i4)*A(-i3,i7,i5)*A(-i2,-i5,i6)*A(-i4,-i6,i8)
    t1 = t.canon_bp()
    assert t1 == 0

    # eq.(6.60)
    #t = A(i0,i1,i2)*A(-i1,i3,i4)*A(-i2,i5,i6)*A(-i3,i7,i8)*A(-i6,-i7,i9)*
    #    A(-i8,i10,i13)*A(-i5,-i10,i11)*A(-i4,-i11,i12)*A(-i3,-i12,i14)
    t = A(i0,i1,i2)*A(-i1,i3,i4)*A(-i2,i5,i6)*A(-i3,i7,i8)*A(-i6,-i7,i9)*\
        A(-i8,i10,i13)*A(-i5,-i10,i11)*A(-i4,-i11,i12)*A(-i9,-i12,i14)
    t1 = t.canon_bp()
    assert t1 == 0

def test_canonicalize3():
    D = Symbol('D')
    Spinor = TensorIndexType('Spinor', dim=D, metric=True, dummy_fmt='S')
    a0,a1,a2,a3,a4 = tensor_indices('a0:5', Spinor)
    C = Spinor.metric
    chi, psi = tensorhead('chi,psi', [Spinor], [[1]], 1)

    t = chi(a1)*psi(a0)
    t1 = t.canon_bp()
    assert t1 == t

    t = psi(a1)*chi(a0)
    t1 = t.canon_bp()
    assert t1 == -chi(a0)*psi(a1)

class Metric(Basic):
    def __new__(cls, name, antisym, **kwargs):
        obj = Basic.__new__(cls, name, antisym, **kwargs)
        obj.name = name
        obj.antisym = antisym
        return obj

def test_TensorIndexType():
    D = Symbol('D')
    G = Metric('g', False)
    Lorentz = TensorIndexType('Lorentz', metric=G, dim=D, dummy_fmt='L')
    m0, m1, m2, m3, m4 = tensor_indices('m0:5', Lorentz)
    sym2 = tensorsymmetry([1]*2)
    sym2n = tensorsymmetry(*get_symmetric_group_sgs(2))
    assert sym2 == sym2n
    g = Lorentz.metric
    assert str(g) == 'g(Lorentz,Lorentz)'
    assert Lorentz.eps_dim == Lorentz.dim

    TSpace = TensorIndexType('TSpace')
    i0, i1 = tensor_indices('i0 i1', TSpace)
    g = TSpace.metric
    A = tensorhead('A', [TSpace]*2, [[1]*2])
    assert  str(A(i0,-i0).canon_bp()) == 'A(TSpace_0, -TSpace_0)'

def test_indices():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    a, b, c, d = tensor_indices('a,b,c,d', Lorentz)
    assert a.tensortype == Lorentz
    assert a != -a
    A, B = tensorhead('A B', [Lorentz]*2, [[1]*2])
    t = A(a,b)*B(-b,c)
    indices = t.get_indices()
    L_0 = TensorIndex('L_0', Lorentz)
    assert indices == [a, L_0, -L_0, c]
    raises(ValueError, lambda: tensor_indices(3, Lorentz))
    raises(ValueError, lambda: A(a,b,c))

def test_tensorsymmetry():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    sym = tensorsymmetry([1]*2)
    sym1 = TensorSymmetry(get_symmetric_group_sgs(2))
    assert sym == sym1
    sym = tensorsymmetry([2])
    sym1 = TensorSymmetry(get_symmetric_group_sgs(2, 1))
    assert sym == sym1
    sym2 = tensorsymmetry()
    assert sym2.base == Tuple() and sym2.generators == Tuple(Permutation(1))
    raises(NotImplementedError, lambda: tensorsymmetry([2, 1]))

def test_TensorType():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    sym = tensorsymmetry([1]*2)
    A = tensorhead('A', [Lorentz]*2, [[1]*2])
    assert A.typ == TensorType([Lorentz]*2, sym)
    assert A.types == [Lorentz]
    typ = TensorType([Lorentz]*2, sym)
    assert str(typ) == "TensorType(['Lorentz', 'Lorentz'])"
    raises(ValueError, lambda: typ(2))

def test_TensExpr():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    a, b, c, d = tensor_indices('a,b,c,d', Lorentz)
    g = Lorentz.metric
    A, B = tensorhead('A B', [Lorentz]*2, [[1]*2])
    raises(ValueError, lambda: g(c, d)/g(a, b))
    raises(ValueError, lambda: S.One/g(a, b))
    raises(ValueError, lambda: (A(c, d) + g(c, d))/g(a, b))
    raises(ValueError, lambda: S.One/(A(c, d) + g(c, d)))
    raises(ValueError, lambda: A(a, b) + A(a, c))
    t = A(a, b) + B(a, b)
    raises(NotImplementedError, lambda: TensExpr.__mul__(t, 'a'))
    raises(NotImplementedError, lambda: TensExpr.__add__(t, 'a'))
    raises(NotImplementedError, lambda: TensExpr.__radd__(t, 'a'))
    raises(NotImplementedError, lambda: TensExpr.__sub__(t, 'a'))
    raises(NotImplementedError, lambda: TensExpr.__rsub__(t, 'a'))
    raises(NotImplementedError, lambda: TensExpr.__div__(t, 'a'))
    raises(NotImplementedError, lambda: TensExpr.__rdiv__(t, 'a'))
    raises(ValueError, lambda: A(a, b)**2)
    raises(NotImplementedError, lambda: 2**A(a, b))
    raises(NotImplementedError, lambda: abs(A(a, b)))

def test_TensorHead():
    assert TensAdd() == 0
    # simple example of algebraic expression
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    a,b = tensor_indices('a,b', Lorentz)
    # A, B symmetric
    A = tensorhead('A', [Lorentz]*2, [[1]*2])
    assert A.rank == 2
    assert A.symmetry == tensorsymmetry([1]*2)

def test_add1():
    assert TensAdd() == 0
    # simple example of algebraic expression
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    a,b,d0,d1,i,j,k = tensor_indices('a,b,d0,d1,i,j,k', Lorentz)
    # A, B symmetric
    A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2])
    t1 = A(b,-d0)*B(d0,a)
    assert TensAdd(t1).equals(t1)
    t2a = B(d0,a) + A(d0, a)
    t2 = A(b,-d0)*t2a
    assert str(t2) == 'A(a, L_0)*A(b, -L_0) + A(b, L_0)*B(a, -L_0)'
    t2b = t2 + t1
    assert str(t2b) == '2*A(b, L_0)*B(a, -L_0) + A(a, L_0)*A(b, -L_0)'
    p, q, r = tensorhead('p,q,r', [Lorentz], [[1]])
    t = q(d0)*2
    assert str(t) == '2*q(d0)'
    t = 2*q(d0)
    assert str(t) == '2*q(d0)'
    t1 = p(d0) + 2*q(d0)
    assert str(t1) == '2*q(d0) + p(d0)'
    t2 = p(-d0) + 2*q(-d0)
    assert str(t2) == '2*q(-d0) + p(-d0)'
    t1 = p(d0)
    t3 = t1*t2
    assert str(t3) == '2*p(L_0)*q(-L_0) + p(L_0)*p(-L_0)'
    t3 = t2*t1
    assert str(t3) == '2*p(L_0)*q(-L_0) + p(L_0)*p(-L_0)'
    t1 = p(d0) + 2*q(d0)
    t3 = t1*t2
    assert str(t3) == '4*p(L_0)*q(-L_0) + 4*q(L_0)*q(-L_0) + p(L_0)*p(-L_0)'
    t1 = p(d0) - 2*q(d0)
    assert str(t1) == '-2*q(d0) + p(d0)'
    t2 = p(-d0) + 2*q(-d0)
    t3 = t1*t2
    assert t3 == p(d0)*p(-d0) - 4*q(d0)*q(-d0)
    t = p(i)*p(j)*(p(k) + q(k)) + p(i)*(p(j) + q(j))*(p(k) - 3*q(k))
    assert t == 2*p(i)*p(j)*p(k) - 2*p(i)*p(j)*q(k) + p(i)*p(k)*q(j) - 3*p(i)*q(j)*q(k)
    t1 = (p(i) + q(i) + 2*r(i))*(p(j) - q(j))
    t2 = (p(j) + q(j) + 2*r(j))*(p(i) - q(i))
    t = t1 + t2
    assert t == 2*p(i)*p(j) + 2*p(i)*r(j) + 2*p(j)*r(i) - 2*q(i)*q(j) - 2*q(i)*r(j) - 2*q(j)*r(i)
    t = p(i)*q(j)/2
    assert 2*t == p(i)*q(j)
    t = (p(i) + q(i))/2
    assert 2*t == p(i) + q(i)

    t = S.One - p(i)*p(-i)
    assert (t + p(-j)*p(j)).equals(1)
    t = S.One + p(i)*p(-i)
    assert (t - p(-j)*p(j)).equals(1)

    t = A(a, b) + B(a, b)
    assert t.rank == 2
    t1 = t - A(a, b) - B(a, b)
    assert t1 == 0
    t = 1 - (A(a, -a) + B(a, -a))
    t1 = 1 + (A(a, -a) + B(a, -a))
    assert (t + t1).equals(2)
    t2 = 1 + A(a, -a)
    assert t1 != t2
    assert t2 != TensMul.from_data(0, [], [], [])
    t = p(i) + q(i)
    raises(ValueError, lambda: t(i, j))


def test_special_eq_ne():
    # test special equality cases:
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    a,b,d0,d1,i,j,k = tensor_indices('a,b,d0,d1,i,j,k', Lorentz)
    # A, B symmetric
    A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2])
    p, q, r = tensorhead('p,q,r', [Lorentz], [[1]])

    t = 0*A(a, b)
    assert t == 0
    assert t == S.Zero

    assert p(i) != A(a, b)
    assert A(a, -a) != A(a, b)
    assert 0*(A(a, b) + B(a, b)) == 0
    assert 0*(A(a, b) + B(a, b)) == S.Zero

    assert 3*(A(a, b) - A(a, b)) == S.Zero

    assert p(i) + q(i) != A(a, b)
    assert p(i) + q(i) != A(a, b) + B(a, b)

    assert p(i) - p(i) == 0
    assert p(i) - p(i) == S.Zero

    assert A(a, b) == A(b, a)

def test_add2():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    m, n, p, q = tensor_indices('m,n,p,q', Lorentz)
    R = tensorhead('R', [Lorentz]*4, [[2, 2]])
    A = tensorhead('A', [Lorentz]*3, [[3]])
    t1 = 2*R(m, n, p, q) - R(m, q, n, p) + R(m, p, n, q)
    t2 = t1*A(-n, -p, -q)
    assert t2 == 0
    t1 = S(2)/3*R(m,n,p,q) - S(1)/3*R(m,q,n,p) + S(1)/3*R(m,p,n,q)
    t2 = t1*A(-n, -p, -q)
    assert t2 == 0
    t = A(m, -m, n) + A(n, p, -p)
    assert t == 0

def test_mul():
    from sympy.abc import x
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    a, b, c, d = tensor_indices('a,b,c,d', Lorentz)
    sym = tensorsymmetry([1]*2)
    t = TensMul.from_data(S.One, [], [], [])
    assert str(t) == '1'
    A, B = tensorhead('A B', [Lorentz]*2, [[1]*2])
    t = (1 + x)*A(a, b)
    assert str(t) == '(x + 1)*A(a, b)'
    assert t.types == [Lorentz]
    assert t.rank == 2
    assert t.dum == []
    assert t.coeff == 1 + x
    assert sorted(t.free) == [(a, 0, 0), (b, 1, 0)]
    assert t.components == [A]

    t = A(-b, a)*B(-a, c)*A(-c, d)
    t1 = tensor_mul(*t.split())
    assert t == t(-b, d)
    assert t == t1
    assert tensor_mul(*[]) == TensMul.from_data(S.One, [], [], [])

    t = TensMul.from_data(1, [], [], [])
    zsym = tensorsymmetry()
    typ = TensorType([], zsym)
    C = typ('C')
    assert str(C()) == 'C'
    assert str(t) == '1'
    assert t.split()[0] == t
    raises(ValueError, lambda: TIDS.free_dum_from_indices(a, a))
    raises(ValueError, lambda: TIDS.free_dum_from_indices(-a, -a))
    raises(ValueError, lambda: A(a, b)*A(a, c))
    t = A(a, b)*A(-a, c)
    raises(ValueError, lambda: t(a, b, c))

def test_substitute_indices():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    i, j, k, l, m, n, p, q = tensor_indices('i,j,k,l,m,n,p,q', Lorentz)
    A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2])
    t = A(i, k)*B(-k, -j)
    t1 = t.substitute_indices((i, j), (j, k))
    t1a = A(j, l)*B(-l, -k)
    assert t1 == t1a

    p = tensorhead('p', [Lorentz], [[1]])
    t = p(i)
    t1 = t.substitute_indices((j, k))
    assert t1 == t
    t1 = t.substitute_indices((i, j))
    assert t1 == p(j)
    t1 = t.substitute_indices((i, -j))
    assert t1 == p(-j)
    t1 = t.substitute_indices((-i, j))
    assert t1 == p(-j)
    t1 = t.substitute_indices((-i, -j))
    assert t1 == p(j)

def test_riemann_cyclic_replace():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    m0, m1, m2, m3 = tensor_indices('m:4', Lorentz)
    symr = tensorsymmetry([2, 2])
    R = tensorhead('R', [Lorentz]*4, [[2, 2]])
    t = R(m0, m2, m1, m3)
    t1 = riemann_cyclic_replace(t)
    t1a = -S.One/3*R(m0, m3, m2, m1) + S.One/3*R(m0, m1, m2, m3) + Rational(2, 3)*R(m0, m2, m1, m3)
    assert t1 == t1a

def test_riemann_cyclic():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    i, j, k, l, m, n, p, q = tensor_indices('i,j,k,l,m,n,p,q', Lorentz)
    R = tensorhead('R', [Lorentz]*4, [[2, 2]])
    t = R(i,j,k,l) + R(i,l,j,k) + R(i,k,l,j) - \
        R(i,j,l,k) - R(i,l,k,j) - R(i,k,j,l)
    t2 = t*R(-i,-j,-k,-l)
    t3 = riemann_cyclic(t2)
    assert t3 == 0
    t = R(i,j,k,l)*(R(-i,-j,-k,-l) - 2*R(-i,-k,-j,-l))
    t1 = riemann_cyclic(t)
    assert t1 == 0
    t = R(i,j,k,l)
    t1 = riemann_cyclic(t)
    assert t1 == -S(1)/3*R(i, l, j, k) + S(1)/3*R(i, k, j, l) + S(2)/3*R(i, j, k, l)

    t = R(i,j,k,l)*R(-k,-l,m,n)*(R(-m,-n,-i,-j) + 2*R(-m,-j,-n,-i))
    t1 = riemann_cyclic(t)
    assert t1 == 0

def test_div():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    m0,m1,m2,m3 = tensor_indices('m0:4', Lorentz)
    R = tensorhead('R', [Lorentz]*4, [[2, 2]])
    t = R(m0,m1,-m1,m3)
    t1 = t/S(4)
    assert str(t1) == '1/4*R(m0, L_0, -L_0, m3)'
    t = t.canon_bp()
    assert not t1._is_canon_bp
    t1 = t*4
    assert t1._is_canon_bp
    t1 = t1/4
    assert t1._is_canon_bp

def test_contract_metric1():
    D = Symbol('D')
    Lorentz = TensorIndexType('Lorentz', dim=D, dummy_fmt='L')
    a, b, c, d, e = tensor_indices('a,b,c,d,e', Lorentz)
    g = Lorentz.metric
    p = tensorhead('p', [Lorentz], [[1]])
    t = g(a, b)*p(-b)
    t1 = t.contract_metric(g)
    assert t1 == p(a)
    A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2])

    # case with g with all free indices
    t1 = A(a,b)*B(-b,c)*g(d, e)
    t2 = t1.contract_metric(g)
    assert t1 == t2

    # case of g(d, -d)
    t1 = A(a,b)*B(-b,c)*g(-d, d)
    t2 = t1.contract_metric(g)
    assert t2 == D*A(a, d)*B(-d, c)

    # g with one free index
    t1 = A(a,b)*B(-b,-c)*g(c, d)
    t2 = t1.contract_metric(g)
    assert t2 == A(a, c)*B(-c, d)

    # g with both indices contracted with another tensor
    t1 = A(a,b)*B(-b,-c)*g(c, -a)
    t2 = t1.contract_metric(g)
    assert t2 == A(a, b)*B(-b, -a)

    t1 = A(a,b)*B(-b,-c)*g(c, d)*g(-a, -d)
    t2 = t1.contract_metric(g)
    assert t2 == A(a,b)*B(-b,-a)

    t1 = A(a,b)*g(-a,-b)
    t2 = t1.contract_metric(g)
    assert t2 == A(a, -a)
    assert not t2.free
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    a, b = tensor_indices('a,b', Lorentz)
    g = Lorentz.metric
    raises(ValueError, lambda: g(a, -a).contract_metric(g)) # no dim

def test_contract_metric2():
    D = Symbol('D')
    Lorentz = TensorIndexType('Lorentz', dim=D, dummy_fmt='L')
    a, b, c, d, e, L_0 = tensor_indices('a,b,c,d,e,L_0', Lorentz)
    g = Lorentz.metric
    p, q = tensorhead('p,q', [Lorentz], [[1]])

    t1 = g(a,b)*p(c)*p(-c)
    t2 = 3*g(-a,-b)*q(c)*q(-c)
    t = t1*t2
    t = t.contract_metric(g)
    assert t == 3*D*p(a)*p(-a)*q(b)*q(-b)
    t1 = g(a,b)*p(c)*p(-c)
    t2 = 3*q(-a)*q(-b)
    t = t1*t2
    t = t.contract_metric(g)
    t = t.canon_bp()
    assert t == 3*p(a)*p(-a)*q(b)*q(-b)

    t1 = 2*g(a,b)*p(c)*p(-c)
    t2 = - 3*g(-a,-b)*q(c)*q(-c)
    t = t1*t2
    t = t.contract_metric(g)
    t = 6*g(a,b)*g(-a,-b)*p(c)*p(-c)*q(d)*q(-d)
    t = t.contract_metric(g)

    t1 = 2*g(a,b)*p(c)*p(-c)
    t2 = q(-a)*q(-b) + 3*g(-a,-b)*q(c)*q(-c)
    t = t1*t2
    t = t.contract_metric(g)
    assert t == (2 + 6*D)*p(a)*p(-a)*q(b)*q(-b)

    t1 = p(a)*p(b) + p(a)*q(b) + 2*g(a,b)*p(c)*p(-c)
    t2 = q(-a)*q(-b) - g(-a,-b)*q(c)*q(-c)
    t = t1*t2
    t = t.contract_metric(g)
    t1 = (1 - 2*D)*p(a)*p(-a)*q(b)*q(-b) + p(a)*q(-a)*p(b)*q(-b)
    assert t == t1

    t = g(a,b)*g(c,d)*g(-b,-c)
    t1 = t.contract_metric(g)
    assert t1 == g(a, d)

    t1 = g(a,b)*g(c,d) + g(a,c)*g(b,d) + g(a,d)*g(b,c)
    t2 = t1.substitute_indices((a,-a),(b,-b),(c,-c),(d,-d))
    t = t1*t2
    t = t.contract_metric(g)
    assert t.equals(3*D**2 + 6*D)

    t = 2*p(a)*g(b,-b)
    t1 = t.contract_metric(g)
    assert t1.equals(2*D*p(a))

    t = 2*p(a)*g(b,-a)
    t1 = t.contract_metric(g)
    assert t1 == 2*p(b)

    M = Symbol('M')
    t = (p(a)*p(b) + g(a, b)*M**2)*g(-a, -b) - D*M**2
    t1 = t.contract_metric(g)
    assert t1 == p(a)*p(-a)

    A = tensorhead('A', [Lorentz]*2, [[1]*2])
    t = A(a, b)*p(L_0)*g(-a, -b)
    t1 = t.contract_metric(g)
    assert str(t1) == 'A(L_1, -L_1)*p(L_0)' or str(t1) == 'A(-L_1, L_1)*p(L_0)'

def test_metric_contract3():
    D = Symbol('D')
    Spinor = TensorIndexType('Spinor', dim=D, metric=True, dummy_fmt='S')
    a0,a1,a2,a3,a4 = tensor_indices('a0:5', Spinor)
    C = Spinor.metric
    chi, psi = tensorhead('chi,psi', [Spinor], [[1]], 1)
    B = tensorhead('B', [Spinor]*2, [[1],[1]])

    t = C(a0, -a0)
    t1 = t.contract_metric(C)
    assert t1.equals(-D)

    t = C(-a0, a0)
    t1 = t.contract_metric(C)
    assert t1.equals(D)

    t = C(a0,a1)*C(-a0,-a1)
    t1 = t.contract_metric(C)
    assert t1.equals(D)

    t = C(a1,a0)*C(-a0,-a1)
    t1 = t.contract_metric(C)
    assert t1.equals(-D)

    t = C(-a0,a1)*C(a0,-a1)
    t1 = t.contract_metric(C)
    assert t1.equals(-D)

    t = C(a1,-a0)*C(a0,-a1)
    t1 = t.contract_metric(C)
    assert t1.equals(D)

    t = C(a0,a1)*B(-a1,-a0)
    t1 = t.contract_metric(C)
    assert t1 == B(a0, -a0)

    t = C(a1,a0)*B(-a1,-a0)
    t1 = t.contract_metric(C)
    assert t1 == -B(a0, -a0)

    t = C(a0,-a1)*B(a1,-a0)
    t1 = t.contract_metric(C)
    assert t1 == -B(a0, -a0)

    t = C(-a0,a1)*B(-a1,a0)
    t1 = t.contract_metric(C)
    assert t1 == -B(a0, -a0)

    t = C(-a0,-a1)*B(a1,a0)
    t1 = t.contract_metric(C)
    assert t1 == B(a0, -a0)

    t = C(-a1, a0)*B(a1,-a0)
    t1 = t.contract_metric(C)
    assert t1 == B(a0, -a0)

    t = C(a0,a1)*psi(-a1)
    t1 = t.contract_metric(C)
    assert t1 == psi(a0)

    t = C(a1,a0)*psi(-a1)
    t1 = t.contract_metric(C)
    assert t1 == -psi(a0)

    t = C(a0,a1)*chi(-a0)*psi(-a1)
    t1 = t.contract_metric(C)
    assert t1 == -chi(a1)*psi(-a1)

    t = C(a1,a0)*chi(-a0)*psi(-a1)
    t1 = t.contract_metric(C)
    assert t1 == chi(a1)*psi(-a1)

    t = C(-a1,a0)*chi(-a0)*psi(a1)
    t1 = t.contract_metric(C)
    assert t1 == chi(-a1)*psi(a1)

    t = C(a0, -a1)*chi(-a0)*psi(a1)
    t1 = t.contract_metric(C)
    assert t1 == -chi(-a1)*psi(a1)

    t = C(-a0,-a1)*chi(a0)*psi(a1)
    t1 = t.contract_metric(C)
    assert t1 == chi(-a1)*psi(a1)

    t = C(-a1,-a0)*chi(a0)*psi(a1)
    t1 = t.contract_metric(C)
    assert t1 == -chi(-a1)*psi(a1)

    t = C(-a1,-a0)*B(a0,a2)*psi(a1)
    t1 = t.contract_metric(C)
    assert t1 == -B(-a1,a2)*psi(a1)

    t = C(a1,a0)*B(-a2,-a0)*psi(-a1)
    t1 = t.contract_metric(C)
    assert t1 == B(-a2,a1)*psi(-a1)

def test_epsilon():
    Lorentz = TensorIndexType('Lorentz', dim=4, dummy_fmt='L')
    a, b, c, d, e = tensor_indices('a,b,c,d,e', Lorentz)
    g = Lorentz.metric
    epsilon = Lorentz.epsilon
    p, q, r, s = tensorhead('p,q,r,s', [Lorentz], [[1]])

    t = epsilon(b,a,c,d)
    t1 = t.canon_bp()
    assert t1 == -epsilon(a,b,c,d)

    t = epsilon(c,b,d,a)
    t1 = t.canon_bp()
    assert t1 == epsilon(a,b,c,d)

    t = epsilon(c,a,d,b)
    t1 = t.canon_bp()
    assert t1 == -epsilon(a,b,c,d)

    t = epsilon(a,b,c,d)*p(-a)*q(-b)
    t1 = t.canon_bp()
    assert t1 == epsilon(c, d, a, b)*p(-a)*q(-b)

    t = epsilon(c,b,d,a)*p(-a)*q(-b)
    t1 = t.canon_bp()
    assert t1 == epsilon(c, d, a, b)*p(-a)*q(-b)

    t = epsilon(c,a,d,b)*p(-a)*q(-b)
    t1 = t.canon_bp()
    assert t1 == -epsilon(c, d, a, b)*p(-a)*q(-b)

    t = epsilon(c,a,d,b)*p(-a)*p(-b)
    t1 = t.canon_bp()
    assert t1 == 0

    t = epsilon(c,a,d,b)*p(-a)*q(-b) + epsilon(a,b,c,d)*p(-b)*q(-a)
    t1 = t.canon_bp()
    assert t1 == -2*epsilon(c, d, a, b)*p(-a)*q(-b)

def test_contract_delta1():
    # see Group Theory by Cvitanovic page 9
    n = Symbol('n')
    Color = TensorIndexType('Color', metric=None, dim=n, dummy_fmt='C')
    a, b, c, d, e, f = tensor_indices('a,b,c,d,e,f', Color)
    delta = Color.delta

    def idn(a, b, d, c):
        assert a.is_up and d.is_up
        assert not (b.is_up or c.is_up)
        return delta(a, c)*delta(d, b)

    def T(a, b, d, c):
        assert a.is_up and d.is_up
        assert not (b.is_up or c.is_up)
        return delta(a, b)*delta(d, c)

    def P1(a, b, c, d):
        return idn(a,b,c,d) - 1/n*T(a,b,c,d)

    def P2(a, b, c, d):
        return 1/n*T(a,b,c,d)

    t = P1(a, -b, e, -f)*P1(f, -e, d, -c)
    t1 = t.contract_delta(delta)
    assert t1 == P1(a, -b, d, -c)

    t = P2(a, -b, e, -f)*P2(f, -e, d, -c)
    t1 = t.contract_delta(delta)
    assert t1 == P2(a, -b, d, -c)

    t = P1(a, -b, e, -f)*P2(f, -e, d, -c)
    t1 = t.contract_delta(delta)
    assert t1 == 0

    t = P1(a, -b, b, -a)
    t1 = t.contract_delta(delta)
    assert t1.equals(n**2 - 1)

def test_fun():
    D = Symbol('D')
    Lorentz = TensorIndexType('Lorentz', dim=D, dummy_fmt='L')
    a,b,c,d,e = tensor_indices('a,b,c,d,e', Lorentz)
    g = Lorentz.metric

    p, q = tensorhead('p q', [Lorentz], [[1]])
    t = q(c)*p(a)*q(b) + g(a,b)*g(c,d)*q(-d)
    assert t(a,b,c) == t
    assert t - t(b,a,c) == q(c)*p(a)*q(b) - q(c)*p(b)*q(a)
    assert t(b,c,d) == q(d)*p(b)*q(c) + g(b,c)*g(d,e)*q(-e)
    t1 = t.fun_eval((a,b),(b,a))
    assert t1 == q(c)*p(b)*q(a) + g(a,b)*g(c,d)*q(-d)

    # check that g_{a b; c} = 0
    # example taken from  L. Brewin
    # "A brief introduction to Cadabra" arxiv:0903.2085
    # dg_{a b c} = \partial_{a} g_{b c} is symmetric in b, c
    dg = tensorhead('dg', [Lorentz]*3, [[1], [1]*2])
    # gamma^a_{b c} is the Christoffel symbol
    gamma = S.Half*g(a,d)*(dg(-b,-d,-c) + dg(-c,-b,-d) - dg(-d,-b,-c))
    # t = g_{a b; c}
    t = dg(-c,-a,-b) - g(-a,-d)*gamma(d,-b,-c) - g(-b,-d)*gamma(d,-a,-c)
    t = t.contract_metric(g)
    assert t == 0
    t = q(c)*p(a)*q(b)
    assert t(b,c,d) == q(d)*p(b)*q(c)

def test_TensorManager():
    Lorentz = TensorIndexType('Lorentz', dummy_fmt='L')
    LorentzH = TensorIndexType('LorentzH', dummy_fmt='LH')
    i, j = tensor_indices('i,j', Lorentz)
    ih, jh = tensor_indices('ih,jh', LorentzH)
    p, q = tensorhead('p q', [Lorentz], [[1]])
    ph, qh = tensorhead('ph qh', [LorentzH], [[1]])

    Gsymbol = Symbol('Gsymbol')
    GHsymbol = Symbol('GHsymbol')
    TensorManager.set_comm(Gsymbol, GHsymbol, 0)
    G = tensorhead('G', [Lorentz], [[1]], Gsymbol)
    assert TensorManager._comm_i2symbol[G.comm] == Gsymbol
    GH = tensorhead('GH', [LorentzH], [[1]], GHsymbol)
    ps = G(i)*p(-i)
    psh = GH(ih)*ph(-ih)
    t = ps + psh
    t1 = t*t
    assert t1 == ps*ps + 2*ps*psh + psh*psh
    qs = G(i)*q(-i)
    qsh = GH(ih)*qh(-ih)
    assert ps*qsh == qsh*ps
    assert ps*qs != qs*ps
    n = TensorManager.comm_symbols2i(Gsymbol)
    assert TensorManager.comm_i2symbol(n) == Gsymbol

    assert GHsymbol in TensorManager._comm_symbols2i
    raises(ValueError, lambda: TensorManager.set_comm(GHsymbol, 1, 2))
    TensorManager.set_comms((Gsymbol,GHsymbol,0),(Gsymbol,1,1))
    assert TensorManager.get_comm(n, 1) == TensorManager.get_comm(1, n) == 1
    TensorManager.clear()
    assert TensorManager.comm == [{0:0, 1:0, 2:0}, {0:0, 1:1, 2:None}, {0:0, 1:None}]
    assert GHsymbol not in TensorManager._comm_symbols2i
    nh = TensorManager.comm_symbols2i(GHsymbol)
    assert GHsymbol in TensorManager._comm_symbols2i


def test_hash():
    D = Symbol('D')
    Lorentz = TensorIndexType('Lorentz', dim=D, dummy_fmt='L')
    a,b,c,d,e = tensor_indices('a,b,c,d,e', Lorentz)
    g = Lorentz.metric

    p, q = tensorhead('p q', [Lorentz], [[1]])
    p_type = p.args[1]
    t1 = p(a)*q(b)
    t2 = p(a)*p(b)
    assert hash(t1) != hash(t2)
    t3 = p(a)*p(b) + g(a,b)
    t4 = p(a)*p(b) - g(a,b)
    assert hash(t3) != hash(t4)

    assert a.func(*a.args) == a
    assert Lorentz.func(*Lorentz.args) == Lorentz
    assert g.func(*g.args) == g
    assert p.func(*p.args) == p
    assert p_type.func(*p_type.args) == p_type
    assert p(a).func(*(p(a)).args) == p(a)
    assert t1.func(*t1.args) == t1
    assert t2.func(*t2.args) == t2
    assert t3.func(*t3.args) == t3
    assert t4.func(*t4.args) == t4

    assert hash(a.func(*a.args)) == hash(a)
    assert hash(Lorentz.func(*Lorentz.args)) == hash(Lorentz)
    assert hash(g.func(*g.args)) == hash(g)
    assert hash(p.func(*p.args)) == hash(p)
    assert hash(p_type.func(*p_type.args)) == hash(p_type)
    assert hash(p(a).func(*(p(a)).args)) == hash(p(a))
    assert hash(t1.func(*t1.args)) == hash(t1)
    assert hash(t2.func(*t2.args)) == hash(t2)
    assert hash(t3.func(*t3.args)) == hash(t3)
    assert hash(t4.func(*t4.args)) == hash(t4)

    def check_all(obj):
        return all([isinstance(_, Basic) for _ in obj.args])

    assert check_all(a)
    assert check_all(Lorentz)
    assert check_all(g)
    assert check_all(p)
    assert check_all(p_type)
    assert check_all(p(a))
    assert check_all(t1)
    assert check_all(t2)
    assert check_all(t3)
    assert check_all(t4)

    tsymmetry = tensorsymmetry([2], [1], [1, 1, 1])

    assert tsymmetry.func(*tsymmetry.args) == tsymmetry
    assert hash(tsymmetry.func(*tsymmetry.args)) == hash(tsymmetry)
    assert check_all(tsymmetry)


def test_hidden_indices_for_matrix_multiplication():
    L = TensorIndexType('Lorentz')
    S = TensorIndexType('Matind')

    m0, m1, m2, m3, m4, m5 = tensor_indices('m0:6', L)
    s0, s1, s2 = tensor_indices('s0:3', S)

    A = tensorhead('A', [L, S, S], [[1], [1], [1]], matrix_behavior=True)
    B = tensorhead('B', [L, S], [[1], [1]], matrix_behavior=True)
    D = tensorhead('D', [L, L, S, S], [[1, 1], [1, 1]], matrix_behavior=True)
    E = tensorhead('E', [L, L, L, L], [[1], [1], [1], [1]], matrix_behavior=True)
    F = tensorhead('F', [L], [[1]], matrix_behavior=True)

    assert (A(m0)) == A(m0, S.auto_left, -S.auto_right)
    assert (B(-m1)) == B(-m1, S.auto_left)

    A0 = A(m0)
    B0 = B(-m0)
    B1 = B(m1)

    assert (B1*A0*B0) == B(m1, s0)*A(m0, -s0, s1)*B(-m0, -s1)
    assert (B0*A0) == B(-m0, s0)*A(m0, -s0, -S.auto_right)
    assert (A0*B0) == A(m0, S.auto_left, s0)*B(-m0, -s0)

    C = tensorhead('C', [L, L], [[1]*2])

    assert (C(True, True)) == C(L.auto_left, -L.auto_right)

    assert (A(m0)*C(m1, -m0)) == A(m2, S.auto_left, -S.auto_right)*C(m1, -m2)

    assert (C(True, True)*C(True, True)) == C(L.auto_left, m0)*C(-m0, -L.auto_right)

    assert A(m0) == A(m0)
    assert B(-m1) == B(-m1)

    assert A(m0) - A(m0) == 0
    ts1 = A(m0)*A(m1) + A(m1)*A(m0)
    ts2 = A(m1)*A(m0) + A(m0)*A(m1)
    assert ts1 == ts2
    assert A(m0)*A(m1) + A(m1)*A(m0) == A(m1)*A(m0) + A(m0)*A(m1)

    assert A(m0) == (2*A(m0))/2
    assert A(m0) == -(-A(m0))
    assert 2*A(m0) - 3*A(m0) == -A(m0)
    assert 2*D(m0, m1) - 5*D(m1, m0) == -3*D(m0, m1)

    D0 = D(True, True, True, True)
    Aa = A(True, True, True)

    assert D0 * Aa == D(L.auto_left, m0, S.auto_left, s0)*A(-m0, -s0, -S.auto_right)
    assert D(m0, m1) == D(m0, m1, S.auto_left, -S.auto_right)

    raises(ValueError, lambda: C(True))
    raises(ValueError, lambda: C())

    raises(ValueError, lambda: E(True, True, True, True))

    # test that a delta is automatically added on missing auto-matrix indices in TensAdd
    assert F(m2)*F(m3)*F(m4)*A(m1) + E(m1, m2, m3, m4) == \
        E(m1, m2, m3, m4)*S.delta(S.auto_left, -S.auto_right) +\
        F(m2)*F(m3)*F(m4)*A(m1, S.auto_left, -S.auto_right)
    assert E(m1, m2) + F(m1)*F(m2) == E(m1, m2) + F(m1)*F(m2)*L.delta(L.auto_left, -L.auto_right)
    assert E(m1, m2)*A(m3) + F(m1)*F(m2)*F(m3) == \
        E(m1, m2, L.auto_left, -L.auto_right)*A(m3, S.auto_left, -S.auto_right) +\
        F(m1)*F(m2)*F(m3)*L.delta(L.auto_left, -L.auto_right)*S.delta(S.auto_left, -S.auto_right)

    assert L.delta() == L.delta(L.auto_left, -L.auto_right)
    assert S.delta() == S.delta(S.auto_left, -S.auto_right)

    assert L.metric() == L.metric(L.auto_left, -L.auto_right)
    assert S.metric() == S.metric(S.auto_left, -S.auto_right)


### TEST VALUED TENSORS ###

numpy = import_module('numpy')

if numpy:
    minkowski = Matrix((
        (1, 0, 0, 0),
        (0, -1, 0, 0),
        (0, 0, -1, 0),
        (0, 0, 0, -1),
    ))
    Lorentz = TensorIndexType('Lorentz', dim=4)
    Lorentz.data = minkowski

    i0, i1, i2, i3, i4 = tensor_indices('i0:5', Lorentz)

    E, px, py, pz = symbols('E px py pz')
    A = tensorhead('A', [Lorentz], [[1]])
    A.data = [E, px, py, pz]
    B = tensorhead('B', [Lorentz], [[1]], 'Gcomm')
    B.data = range(4)
    AB = tensorhead("AB", [Lorentz] * 2, [[1]]*2)
    AB.data = minkowski

    ba_matrix = Matrix((
        (1, 2, 3, 4),
        (5, 6, 7, 8),
        (9, 0, -1, -2),
        (-3, -4, -5, -6),
    ))

    BA = tensorhead("BA", [Lorentz] * 2, [[1]]*2)
    BA.data = ba_matrix

    BA(i0, i1)*A(-i0)*B(-i1)

    # Let's test the diagonal metric, with inverted Minkowski metric:
    LorentzD = TensorIndexType('LorentzD')
    LorentzD.data = [-1, 1, 1, 1]
    mu0, mu1, mu2 = tensor_indices('mu0:3', LorentzD)
    C = tensorhead('C', [LorentzD], [[1]])
    C.data = [E, px, py, pz]

    ### non-diagonal metric ###
    ndm_matrix = (
        (0, 1, 0,),
        (1, 0, 1),
        (0, 1, 0,),
    )
    ndm = TensorIndexType("ndm")
    ndm.data = ndm_matrix
    n0, n1, n2 = tensor_indices('n0:3', ndm)
    NA = tensorhead('NA', [ndm], [[1]])
    NA.data = range(10, 13)
    NB = tensorhead('NB', [ndm]*2, [[1]]*2)
    NB.data = [[i+j for j in range(10, 13)] for i in range(10, 13)]
    NC = tensorhead('NC', [ndm]*3, [[1]]*3)
    NC.data = [[[i+j+k for k in range(4, 7)] for j in range(1, 4)] for i in range(2, 5)]

def test_valued_tensor_iter():
    numpy = import_module("numpy")
    if numpy is None:
        skip("numpy not installed.")
    # iteration on VTensorHead
    assert list(A) == [E, px, py, pz]
    assert list(ba_matrix) == list(BA)

    # iteration on VTensMul
    assert list(A(i1)) == [E, px, py, pz]
    assert list(BA(i1, i2)) == list(ba_matrix)
    assert list(3 * BA(i1, i2)) == [3 * i for i in list(ba_matrix)]
    assert list(-5 * BA(i1, i2)) == [-5 * i for i in list(ba_matrix)]

    # iteration on VTensAdd
    # A(i1) + A(i1)
    assert list(A(i1) + A(i1)) == [2*E, 2*px, 2*py, 2*pz]
    assert BA(i1, i2) - BA(i1, i2) == 0
    assert list(BA(i1, i2) - 2 * BA(i1, i2)) == [-i for i in list(ba_matrix)]


def test_valued_tensor_covariant_contravariant_elements():
    numpy = import_module("numpy")
    if numpy is None:
        skip("numpy not installed.")

    assert A(-i0)[0] == A(i0)[0]
    assert A(-i0)[1] == -A(i0)[1]

    assert AB(i0, i1)[1, 1] == -1
    assert AB(i0, -i1)[1, 1] == 1
    assert AB(-i0, -i1)[1, 1] == -1
    assert AB(-i0, i1)[1, 1] == 1


def test_valued_tensor_get_matrix():
    numpy = import_module("numpy")
    if numpy is None:
        skip("numpy not installed.")

    matab = AB(i0, i1).get_matrix()
    assert matab == Matrix([
                            [1,  0,  0,  0],
                            [0, -1,  0,  0],
                            [0,  0, -1,  0],
                            [0,  0,  0, -1],
                            ])
    # when alternating contravariant/covariant with [1, -1, -1, -1] metric
    # it becomes the identity matrix:
    assert AB(i0, -i1).get_matrix() == eye(4)

    # covariant and contravariant forms:
    assert A(i0).get_matrix() == Matrix([E, px, py, pz])
    assert A(-i0).get_matrix() == Matrix([E, -px, -py, -pz])


def test_valued_tensor_contraction():
    numpy = import_module("numpy")
    if numpy is None:
        skip("numpy not installed.")

    assert A(i0) * A(-i0) == E ** 2 - px ** 2 - py ** 2 - pz ** 2
    assert A(i0) * A(-i0) == A ** 2
    assert A(i0) * A(-i0) == A(i0) ** 2
    assert (A(i0) * B(-i0)) == -px - 2 * py - 3 * pz

    for i in range(4):
        for j in range(4):
            assert (A(i0) * B(-i1))[i, j] == [E, px, py, pz][i] * [0, -1, -2, -3][j]

    # test contraction on the alternative Minkowski metric: [-1, 1, 1, 1]
    assert C(mu0) * C(-mu0) == -E ** 2 + px ** 2 + py ** 2 + pz ** 2

    contrexp = A(i0) * AB(i1, -i0)
    assert A(i0).rank == 1
    assert AB(i1, -i0).rank == 2
    assert contrexp.rank == 1
    for i in range(4):
        assert contrexp[i] == [E, px, py, pz][i]


def test_valued_tensor_self_contraction():
    numpy = import_module("numpy")
    if numpy is None:
        skip("numpy not installed.")

    assert AB(i0, -i0) == 4
    assert BA(i0, -i0) == 2


def test_valued_tensor_pow():
    numpy = import_module("numpy")
    if numpy is None:
        skip("numpy not installed.")

    assert C**2 == -E**2 + px**2 + py**2 + pz**2
    assert C**1 == sqrt(-E**2 + px**2 + py**2 + pz**2)
    assert C(mu0)**2 == C**2
    assert C(mu0)**1 == C**1


def test_valued_tensor_expressions():
    numpy = import_module("numpy")
    if numpy is None:
        skip("numpy not installed.")

    x1, x2, x3 = symbols('x1:4')

    # test coefficient in contraction:
    rank2coeff = x1 * A(i3) * B(i2)
    assert rank2coeff[1, 1] == x1 * px
    assert rank2coeff[3, 3] == 3 * pz * x1
    coeff_expr = (x1 * A(i4)) * (B(-i4) / x2)

    assert coeff_expr.expand() == -px*x1/x2 - 2*py*x1/x2 - 3*pz*x1/x2

    add_expr = A(i0) + B(i0)

    assert add_expr[0] == E
    assert add_expr[1] == px + 1
    assert add_expr[2] == py + 2
    assert add_expr[3] == pz + 3

    sub_expr = A(i0) - B(i0)

    assert sub_expr[0] == E
    assert sub_expr[1] == px - 1
    assert sub_expr[2] == py - 2
    assert sub_expr[3] == pz - 3

    assert add_expr * B(-i0) == -px - 2*py - 3*pz - 14

    expr1 = x1*A(i0) + x2*B(i0)
    expr2 = expr1 * B(i1) * (-4)
    expr3 = expr2 + 3*x3*AB(i0, i1)
    expr4 = expr3 / 2
    assert expr4 * 2 == expr3
    expr5 = expr4 * BA(-i1, -i0)

    assert expr5 == 28*E*x1 + 12*px*x1 + 20*py*x1 + 28*pz*x1 + 136*x2 + 3*x3


def test_noncommuting_components():
    numpy = import_module("numpy")
    if numpy is None:
        skip("numpy not installed.")

    euclid = TensorIndexType('Euclidean')
    euclid.data = [1, 1]
    i1, i2, i3 = tensor_indices('i1:4', euclid)

    a, b, c, d = symbols('a b c d', commutative=False)
    V1 = tensorhead('V1', [euclid] * 2, [[1]]*2)
    V1.data = [[a, b], (c, d)]
    V2 = tensorhead('V2', [euclid] * 2, [[1]]*2)
    V2.data = [[a, c], [b, d]]

    vtp = V1(i1, i2) * V2(-i2, -i1)

    assert vtp == a**2 + b**2 + c**2 + d**2
    assert vtp != a**2 + 2*b*c + d**2

    Vc = b * V1(i1, -i1)
    assert Vc.expand() == b * a + b * d


def test_valued_non_diagonal_metric():
    numpy = import_module("numpy")
    if numpy is None:
        skip("numpy not installed.")

    mmatrix = Matrix(ndm_matrix)
    assert NA(n0)*NA(-n0) == (NA(n0).get_matrix().T * mmatrix * NA(n0).get_matrix())[0, 0]


def test_valued_tensor_strip():
    numpy = import_module("numpy")
    if numpy is None:
        skip("numpy not installed.")

    sA = A(i0).strip()
    sB = B(-i0).strip()
    sAB = AB(i0, i1).strip()

    assert sA.data is None
    assert sB.data is None
    assert sAB.data is None

    sthA = A.strip()
    sthB = B.strip()
    sthAB = AB.strip()

    assert sA.data is None
    assert sB.data is None
    assert sAB.data is None


def test_valued_tensor_applyfunc():
    numpy = import_module("numpy")
    if numpy is None:
        skip("numpy not installed.")

    aA = A(i0).applyfunc(lambda x: x**2)
    aB = B(i0).applyfunc(lambda x: x**3)
    aB2 = B(-i0).applyfunc(lambda x: x**3)

    for i in range(4):
        assert aA[i] == A(i0)[i]**2
        assert aB[i] == B(i1)[i]**3
    assert aB*aB2 == -794

    tA = A.applyfunc(lambda x: x + 33)
    tB = B.applyfunc(lambda x: x + 33)
    tAB = AB.applyfunc(lambda x: x + 33)

    assert (tA(i0)*tA(-i0)).expand() == ((E + 33)**2 - (px + 33)**2 - (py + 33)**2 - (pz + 33)**2).expand()
    assert tB(i0).get_matrix() == Matrix([33, 34, 35, 36])
    assert tAB(i0, i1).get_matrix() == Matrix([
        [34, 33, 33, 33],
        [33, 32, 33, 33],
        [33, 33, 32, 33],
        [33, 33, 33, 32],
    ])

def test_valued_canon_bp_swapaxes():
    numpy = import_module("numpy")
    if numpy is None:
        return

    e1 = A(i1)*A(i0)
    e1.data[0, 1] = 44
    e2 = e1.canon_bp()
    assert e2 == A(i0)*A(i1)
    for i in range(4):
        for j in range(4):
            assert e1[i, j] == e2[j, i]
    o1 = B(i2)*A(i1)*B(i0)
    o2 = o1.canon_bp()
    for i in range(4):
        for j in range(4):
            for k in range(4):
                assert o1[i, j, k] == o2[j, i, k]


def test_pprint():
    Lorentz = TensorIndexType('Lorentz')
    i0, i1, i2, i3, i4 = tensor_indices('i0:5', Lorentz)
    A = tensorhead('A', [Lorentz], [[1]])

    assert pretty(A) == "A(Lorentz)"
    assert pretty(A(i0)) == "A(i0)"


def test_contract_automatrix_and_data():
    numpy = import_module('numpy')
    if numpy is None:
        return

    L = TensorIndexType('L')
    S = TensorIndexType('S')
    G = tensorhead('G', [L, S, S], [[1] * 3], matrix_behavior=True)

    def G_data():
        G.data = [[[1]]]
    raises(ValueError, G_data)
    L.data = [1, -1]
    raises(ValueError, G_data)
    S.data = [[1, 0], [0, 2]]
    G.data = [
        [[1, 2],
         [3, 4]],
        [[5, 6],
         [7, 8]]
    ]
    m0, m1, m2 = tensor_indices('m0:3', L)
    s0, s1, s2 = tensor_indices('s0:3', S)

    assert (G(-m0).data == numpy.array([
       [[1, 4],
        [3, 8]],
       [[-5, -12],
        [-7, -16]]
    ])).all()

    (G(m0) * G(-m0)).data
    G(m0, s0, -s1).data

    c1 = G(m0, s0, -s1)*G(-m0, s1, -s2)
    c2 = G(m0) * G(-m0)

    assert (c1.data == c2.data).all()

    del L.data
    del S.data
    del G.data
    assert L.data is None
    assert S.data is None
    assert G.data is None
