
# Derivatives are generic objects satisfying the product rule
# in one or more of their arguments. Commutators are examples of
# such objects.

def test01():
    __cdbkernel__=create_scope()
    D(#)::Derivative.
    obj1:= a*b*D(c*d*e)*f*g;
    product_rule(_)
    tst1:= a*b*(D(c)*d*e+c*D(d)*e+c*d*D(e))*f*g-@(obj1);
    assert(tst1==0)
    print("Test 01 passed")

test01()

def test02():
    __cdbkernel__=create_scope()
    D(a??)(b??)(c??)::Derivative.
    obj2:= a*b*D(c*d*e)(h1)(h2)*f*g;
    product_rule(_)
    tst2:= a*b*(D(c)(h1)(h2)*d*e+c*D(d)(h1)(h2)*e+c*d*D(e)(h1)(h2))*f*g-@(obj2);
    assert(tst2==0)
    print("Test 02 passed")
    
test02()

# Using the properties of \diff.

def test03():
    __cdbkernel__=create_scope()
    \diff{#}::PartialDerivative.
    obj5:= \diff{A*B}_\mu;
    product_rule(_)
    tst5:= \diff{A}_\mu * B + A * \diff{B}_\mu - @(obj5);
    assert(tst5==0)
    print("Test 03 passed")
    
test03()

def test04():
    __cdbkernel__=create_scope()
    \diff{#}::PartialDerivative;
    obj6:= \diff{g^{\mu \kappa} * \diff{g_{\kappa \nu}}_{\rho}}_{\sigma};
    product_rule(_)
    tst6:= \diff{g^{\mu \kappa}}_{\sigma} * \diff{g_{\kappa \nu}}_{\rho}
        +g^{\mu \kappa} * \diff{g_{\kappa \nu}}_{\rho \sigma} - @(obj6);
    assert(tst6==0)
    print("Test 04 passed")
    
test04()

def test07():
    __cdbkernel__=create_scope()
    \diff{#}::PartialDerivative;
    {\mu,\nu,\rho,\sigma,\kappa,\lambda,\eta}::Indices(spacetime);
    obj7:= g^{\eta \kappa} * \diff{\eta_{\nu \sigma}}_{\kappa} * g^{\mu \lambda} * \diff{eta_{\lambda \eta}}_{\rho};
    rl:=\diff{\eta_{\mu\nu}}_{\rho} -> 0;
    substitute(obj7, rl)
    assert(obj7==0)
    print("Test 07 passed")
    
test07()

# \delta in combination with \diff
#
def test08():
    __cdbkernel__=create_scope()
    \delta_{n q}::KroneckerDelta;
    \diff{#}::Derivative;
    obj8:= \diff{A_{m n}}_{p} \delta_{n q} \delta_{p t};
    eliminate_kronecker(_)
    tst8:= \diff{A_{m q}}_{t} - @(obj8);
    assert(tst8==0)
    print("Test 08 passed")
    
test08()

# Multiple derivatives and the product rule

def test09():
    __cdbkernel__=create_scope()
    \diff{#}::PartialDerivative.
    obj9:= 3 \diff_{m n}{4*A*B};
    product_rule(_)
    distribute(_)
    product_rule(_)
    tst9:= 12 \diff_{m n}{A} B + 12 \diff_{m}{A} \diff_{n}{B} + 12 \diff_{n}{A} \diff_{m}{B} + 12 A \diff_{m n}{B} - @(obj9);
    assert(tst9==0)
    print("Test 09 passed")
    
test09()

# Order of child nodes on PartialDerivative.

def test10():
    __cdbkernel__=create_scope()
    {a,b,c,d}::Indices(vector).
    \partial{#}::PartialDerivative.
    A_{a b}::AntiSymmetric.
    obj10:= \partial_{b a}{ A_{d c} };
    canonicalise(_)
    tst10:= \partial_{a b}{ A_{c d} } + @(obj10);
    collect_terms(_)
    assert(tst10==0)
    print("Test 10 passed")
    
test10()

def test10b():
    __cdbkernel__=create_scope()
    {a,b,c,d}::Indices(vector).
    \partial{#}::PartialDerivative.
    A_{a b}::AntiSymmetric.
    obj10b:= \partial{ A_{d c} }_{b a};
    canonicalise(_)
    tst10b:= \partial{ A_{c d} }_{a b} + @(obj10b);
    assert(tst10b==0)
    print("Test 10b passed")
    
test10b()

# Multiple derivatives again
#

# def test11():
#     __cdbkernel__=create_scope()
#     \partial{#}::PartialDerivative.
#     obj11a:= \partial(A B C)_{a b};
#     product_rule(_)
#     tst11a:= \partial{\partial(A)_{a} * B * C + A * \partial(B)_{a} * C + A * B * \partial(C)_{a}}_{b} - @(obj11a);
#     collect_terms(_)
#     assert(tst11a==0)
# 
# test11()

def test11b():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative.
    obj11b:= \partial_{a b}(A B C);
    product_rule(_)
    tst11b:= \partial_{a}{\partial_{b}(A) * B * C + A * \partial_{b}(B) * C + A * B * \partial_{b}(C)} - @(obj11b);
    collect_terms(_)
    assert(tst11b==0)
    print("Test 11b passed")
    
test11b()

# More tricky derivatives
def test12():
    __cdbkernel__=create_scope()
    \Omega{#}::Derivative.
    {A,B,C,D}::Depends(\Omega{#});
    obj12:= \Omega_{a}(A B)(C D);
    product_rule(_)
    product_rule(_)
    distribute(_)
    tst12:= \Omega_{a}(A)(C) * D * B + C * \Omega_{a}(A)(D) * B + A * \Omega_{a}(B)(C) * D + A * C * \Omega_{a}(B)(D)
        -@(obj12);
    collect_terms(_)
    assert(tst12==0)
    print("Test 12 passed")
    
test12()

# Test 13: Inheritance
#
# reset.
# \nabla{#}::Derivative.
# A_{m n}::Traceless.
# obj13:= \nabla_{m}{A_{p p}}*Q_{m};
# canonicalise(_);

# Product_rule on powers

def test14():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative.
    obj14a:= \partial_{m n}{A**2};
    product_rule(_)
    tst14a:= \partial_{m}(2*A*\partial_{n}{A}) - @(obj14a);
    collect_terms(_)
    assert(tst14a==0)
    print("Test 14 passed")
    
test14()

def test14b():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative.
    obj14b:= \partial_{m n}{A**3};
    product_rule(_)
    tst14b:= \partial_{m}(3*A**2*\partial_{n}{A}) - @(obj14b);
    collect_terms(_)
    assert(tst14b==0)
    print("Test 14b passed")
    
test14b()

# reset.
# \partial{#}::PartialDerivative.
# obj14c:= \partial_{m n}{A**(d+1)};
# product_rule(_);
# # FIXME

def test14d():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative.
    obj14d:= \partial_{m n}{A**d};
    product_rule(_)
    tst14d:= \partial_{m}(d * A**(d - 1) * \partial_{n}{A}) - @(obj14d);
    collect_terms(_)
    assert(tst14d==0)
    print("Test 14d passed")
    
test14d()

def test14f():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative.
    obj14f:= \partial{A*\partial{A}};
    product_rule(_)
    tst14f:= \partial{A} * \partial{A} + A * \partial{\partial{A}} - @(obj14f);
    collect_terms(_)
    assert(tst14f==0)
    print("Test 14f passed")
    
test14f()

def test15():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative.
    obj15:= \partial{-a**2};
    product_rule(_)
    tst15:= 2 a \partial{a} + @(obj15);
    collect_terms(_)
    assert(tst15==0)
    print("Test 15 passed")
    
test15()

def test16():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative.
    obj16:= \partial{-a**3};
    product_rule(_)
    tst16:= 3 a**2 \partial{a} + @(obj16);
    collect_terms(_)
    assert(tst16==0)
    print("Test 16 passed")
    
test16()

# Flattening
#
def test17():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative.
    obj17:= \partial_{a}{\partial_{b c}{F}};
    tst17:= \partial_{a b c}{F} - @(obj17);
    assert(tst17==0)
    print("Test 17 passed")
    
test17()

# # Test 18: more flattening
# #
# # FIXME: Because multi-derivatives get collected, we would now need
# # to have a notation like \nabla_{a??.. b}{A??} -> 
# \partial{#}::PartialDerivative.
# \nabla{#}::Derivative.
# obj18:= \nabla_{a}{\nabla_{b}{F}};
# substitute(_, "\nabla_{a}{A??} -> \partial_{a}{A??}" );
# tst18:= \partial_{a b}{F} - @(obj18);
# collect_terms(_);
# assert(tst18);
# 

# Test 19: Grassman derivatives
#
def test19():
    __cdbkernel__=create_scope()
    {\alpha,\beta,\gamma}::Indices(spinor).
    \theta^{\alpha}::Coordinate.
    \theta^{\alpha}::SelfAntiCommuting.
    D{#}::Derivative.
    obj19a:= D_{\theta^{\alpha}}{\theta^\beta \theta^\gamma};
    product_rule(_)
    tst19a:= D_{\theta^{\alpha}}{\theta^{\beta}} \theta^{\gamma}  
             - \theta^{\beta} D_{\theta^{\alpha}}{\theta^{\gamma}} - @(obj19a);
    assert(tst19a==0)
    print("Test 19 passed")
    
test19()

def test19b():
    __cdbkernel__=create_scope()
    {\alpha,\beta,\gamma}::Indices(spinor).
    {\alpha,\beta,\gamma}::AntiCommuting.
    \theta^{\alpha}::Coordinate.
    D{#}::Derivative.
    obj19b:= D_{\alpha}{\theta^\beta \theta^\gamma};
    product_rule(_);
    tst19b:= D_{\alpha}{\theta^{\beta}} \theta^{\gamma}  
             - \theta^{\beta} D_{\alpha}{\theta^{\gamma}} - @(obj19b);
    assert(tst19b==0)
    print("Test 19b passed")
    
def test19c():
    __cdbkernel__=create_scope()
    {\alpha,\beta,\gamma}::Indices(spinor, position=fixed).
    {\alpha,\beta,\gamma}::AntiCommuting.
    D{#}::Derivative.
    obj19c:= D^{\alpha}{ D_{\alpha}{T^{\beta} S^{\gamma}}};
    product_rule(_)
    distribute(_)
    product_rule(_)
    tst19c:= D^{\alpha}{D_{\alpha}{T^{\beta}}} * S^{\gamma} 
              + D_{\alpha}{T^{\beta}} * D^{\alpha}{S^{\gamma}} 
              - D^{\alpha}{T^{\beta}} * D_{\alpha}{S^{\gamma}}
              + T^{\beta} * D^{\alpha}{D_{\alpha}{S^{\gamma}}} - @(obj19c);
    assert(tst19c==0)
    print("Test 19c passed")
    
test19c()

# In the following test, the objects 'b' and 'c' carry an implicit
# spinor index, and the derivative 'D' carries an explicit spinor
# index. Moving a 'D_\alpha' through 'b' should pick up a minus sign.

# def test19d():
# {\alpha,\beta,\gamma}::Indices(spinor, position=fixed).
# {\alpha,\beta,\gamma}::AntiCommuting.
# D{#}::Derivative.
# {b, c}::SelfAntiCommuting.
# {b, c}::ImplicitIndex(spinor).
# obj19d:= D^{\alpha}{D_{\alpha}{b*c}};
# product_rule(_);
# distribute(_);
# product_rule(_);
# distribute(_);
# tst19d:= D^{\alpha}{D_{\alpha}{b}} c
# - D_{\alpha}{b} D^{\alpha}{c}
# + D^{\alpha}{b} D_{\alpha}{c}
# + b D^{\alpha}{D_{\alpha}{c}} - @(obj19d);
# collect_terms(_);
# assert(tst19d==0)
# 
# test19d()

# Anti-commuting indices.

def test20():
    __cdbkernel__=create_scope()
    \dot{#}::Accent.
    {a,b,c,d}::Indices(spinor).
    {\dot{a},\dot{b},\dot{c},\dot{d}}::Indices(dspinor).
    {a,b,c,d,\dot{a},\dot{b},\dot{c},\dot{d}}::AntiCommuting.
    D{#}::Derivative.
    obj20:= D_{a}{D_{\dot{a}}{f g}};
    product_rule(_)
    distribute(_)
    product_rule(_)
    tst20:= D_{a}{D_{\dot{a}}{f}} * g 
        - D_{\dot{a}}{f} * D_{a}{g} 
        + D_{a}{f} * D_{\dot{a}}{g} 
        + f * D_{a}{D_{\dot{a}}{g}} - @(obj20);
    collect_terms(_)
    assert(tst20==0)
    print("Test 20 passed")
    
test20()

# Two index sets, anti-commuting

def test21():
    __cdbkernel__=create_scope()
    {\alpha,\beta,\gamma}::Indices(Spinor).
    {\dalpha,\dbeta,\dgamma}::Indices(DottedSpinor).
    {\alpha,\beta,\gamma,\dalpha,\dbeta,\dgamma,b,c}::AntiCommuting.
    {\bD{#}, D{#}}::Derivative.
    obj21:= D_{\alpha}{\bD_{\dalpha}{b*c}};
    product_rule(_)
    distribute(_)
    product_rule(_)
    distribute(_)
    tst21:= D_{\alpha}{\bD_{\dalpha}{b}} * c 
        + \bD_{\dalpha}{b} * D_{\alpha}{c} 
        - D_{\alpha}{b} * \bD_{\dalpha}{c} 
        + b * D_{\alpha}{\bD_{\dalpha}{c}} - @(obj21);
    collect_terms(_)
    assert(tst21==0)
    print("Test 21 passed")
    
test21()


# Explicit and implicit indices

def test22():
    __cdbkernel__=create_scope()
    {\alpha,\beta,\gamma}::Indices().
    {\theta,\lambda}::ImplicitIndex().
    {\alpha,\beta,\gamma,\theta,\lambda}::AntiCommuting.
    D{#}::Derivative.
    obj22:= D_{\alpha}{\theta \lambda};
    product_rule(_)
    tst22:= D_{\alpha}{\theta} \lambda - \theta D_{\alpha}{\lambda} - @(obj22);
    collect_terms(_)
    assert(tst22==0)
    print("Test 22 passed")
    
test22()

# Multiple indices on tensors.

def test23():
    __cdbkernel__=create_scope()
    {a,b,c,d,e}::Indices.
    {a,b,c,d,e}::AntiCommuting.
    D{#}::Derivative.
    obj23:= D_{a}{A^{b} T^{c d} C^{e}}; 
    product_rule(_)
    tst23:= D_{a}{A^{b}} * T^{c d} * C^{e} - A^{b} * D_{a}{T^{c d}} * C^{e} - A^{b} * T^{c d} * D_{a}{C^{e}}
        - @(obj23);
    collect_terms(_)
    assert(tst23==0)
    print("Test 23 passed")
    
test23()

# Unwrapping.

def test24():
    __cdbkernel__=create_scope()
    \hat{#}::Accent;
    \hat{#}::Distributable;
    B::Depends(\hat{#});
    ex:=\hat{A+B+C};
    distribute(_)
    unwrap(_)
    tst:=\hat{B}-@(ex);
    assert(tst==0)
    print("Test 24 passed")

test24()

# Bug reported 
def test25():
    __cdbkernel__=create_scope()
    \del{#}::Derivative.
    X::Depends(\del{#}).
    ex1:= \del{X*Y*Z*\del{X}};
    product_rule(ex1)
    unwrap(ex1)
    tst:=\del{X} Y Z \del{X} + X Y Z \del{\del{X}} - @(ex1);
    assert(tst==0)
    print("Test 25a passed")
    ex2:= \del{X*Y*Z*\del{Y}};
    product_rule(ex2)
    unwrap(ex2)
    assert(ex2==0)
    print("Test 25b passed")

test25()

def test26():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative;
    ex:=\partial_{\mu}{A};
    substitute(_, $A -> 3$)
    assert(ex==0)
    print("Test 26 passed")

test26()

def test27():
    __cdbkernel__=create_scope()
    \partial{#}::Derivative;
    ex:=\partial_{\mu}{A};
    substitute(_, $A -> 3$)
    assert(ex==0)
    print("Test 27 passed")

test27()

def test28():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative;
    { A, B, C, D }::Depends(\partial{#});
    ex := \partial_{\alpha}{ A (B + r C) };
    unwrap(ex)
    tst:= \partial_{\alpha}{ A (B + r C) } - @(ex);
    assert(tst==0)
    print("Test 28 passed")

test28()

def test29():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative;
    { A, B, C, D }::Depends(\partial{#});
    ex := \partial_{\alpha}{ (A + r B) (C + r D) }.
    unwrap(ex)
    tst:= \partial_{\alpha}{ (A + r B) (C + r D) } - @(ex);
    assert(tst==0)
    print("Test 29 passed")

test29()

def test30():
    __cdbkernel__=create_scope()
    { \del{#}, \delbar{#} }::PartialDerivative;
    X_{\mu}::Depends(\del{#}, \delbar{#});
    ex:= \del{\delbar{X_{\mu}}};
    unwrap(_)
    tst:= \del{\delbar{X_{\mu}}} - @(ex);
    assert(tst==0)
    print("Test 30 passed")

test30()

def test31():
    __cdbkernel__=create_scope()
    \hat{#}::Accent;
    \partial{#}::PartialDerivative;
    A::Depends(\partial{#}, \hat{#});
    ex:=\partial{A \hat{A} B};
    product_rule(_)
    unwrap(_)
    tst:= \partial{A} \hat{A} B + A \partial{\hat{A}} B - @(ex);
    assert(tst==0)
    print("Test 31 passed")

test31()
        
def test32():
    __cdbkernel__=create_scope()
    \partial{#}::PartialDerivative;
    ex:= \partial_{\mu}{A_{\nu}} = B_{\mu\nu};
    ex2:= 4 \partial^{\mu}{ @(ex) };
    lhs2 = lhs(ex2)
    rhs2 = rhs(ex2)
    lhstst:= 4 \partial^{\mu}_{\mu}{A_{\nu}} - @(lhs2);
    rhstst:= 4 \partial^{\mu}{ B_{\mu\nu} } -  @(rhs2);
    assert(lhstst==0)
    assert(rhstst==0)
    print("Test 32 passed")

test32()

def test33():
    __cdbkernel__=create_scope()
    d{#}::ExteriorDerivative;
    A::DifferentialForm(degree=3);
    B::DifferentialForm(degree=3);    
    ex:= A = B;
    ex2:= d{ @(ex) };
    lhs2 = lhs(ex2)
    rhs2 = rhs(ex2)
    lhstst := d{A} - @(lhs2);
    rhstst := d{B} - @(rhs2);
    assert(lhstst==0)
    assert(rhstst==0)
    print("Test 33 passed")

test33()

def test34():
    __cdbkernel__=create_scope()
    {\mu,\nu,\rho}::Indices(position=fixed, values={0,1,2,3});
    \partial{#}::PartialDerivative;
    A^{\mu}::Depends(\partial{#});
    ex:= \partial_{\mu}{A^{2}};
    unwrap(ex)
    tst:= \partial_{\mu}{A^{2}} - @(ex);
    assert(tst==0)
    print("Test 34 passed")        

#test34()
        
def test35():
    __cdbkernel__=create_scope()        
    \partial{#}::PartialDerivative;
    {\mu,\nu,\rho}::Indices;    
    ex:= \partial_{\mu}{ (\partial_{\nu}{\phi} \partial^{\nu}{\phi})**3 };
    product_rule(ex)
    tst:= 3 (\partial_{\nu}{\phi} \partial^{\nu}{\phi})**2
        \partial_{\mu}{\partial_{\rho}{\phi} \partial^{\rho}{\phi} } - @(ex);
    assert(tst==0)
    print("Test 35 passed")

test35()

def test36():
    __cdbkernel__=create_scope()        
    # When there are more types of accents and derivatives, unwrap
    # needs to be guided about what to unwrap, or it will do all.
    {m, n, p, q}::Indices( position=independent);
    \partial{#}::PartialDerivative;
    X_{m}::Depends(\partial{#});
    \Gamma{#}::GammaMatrix;
    { \theta, \psi }::Spinor(dimension=10, type=MajoranaWeyl);
    \bar{\theta}::Depends(\partial{#});
    \bar{#}::DiracBar;
    ex:=\bar{Q \Gamma^{m} \theta}\partial_{\mu}{X_{m}} + \partial_{\mu}{\Gamma^{m}\bar{\theta}} X_{m};
    unwrap(_, $\partial{#}$)
    tst:=\bar{Q \Gamma^{m} \theta}\partial_{\mu}{X_{m}} + \Gamma^{m} \partial_{\mu}{\bar{\theta}} X_{m} - @(ex);
    assert(tst==0)
    print("Test 36a passed")    
    unwrap(ex)
    tst:=Q \bar{\Gamma^{m} \theta}\partial_{\mu}{X_{m}} + \Gamma^{m} \partial_{\mu}{\bar{\theta}} X_{m} - @(ex);
    assert(tst==0)
    print("Test 36b passed")

test36()

def test37():
    __cdbkernel__=create_scope()        
    A::DifferentialForm(degree=1).
    B::DifferentialForm(degree=2).
    C::DifferentialForm(degree=3).
    d{#}::ExteriorDerivative.
    ex:=C= d{B}+A ^ B;
    ge:=d{C}+A ^ C;
    substitute(ge, ex)
    distribute(ge)
    product_rule(ge)
    tst:= d{A} ^ B - @(ge);
    assert(tst==0)
    print("Test 37 passed")                

test37()

def test38():        
    __cdbkernel__=create_scope()        
    A::DifferentialForm(degree=1).
    B::DifferentialForm(degree=1).
    C::DifferentialForm(degree=2).        
    ex:= A ^ B + B ^ A + A ^ C + C ^ A;
    sort_product(_)
    tst:= 2 A ^ C - @(ex);
    assert(tst==0)
    print("Test 38 passed")        

test38()

def test39():        
    __cdbkernel__=create_scope()
    A0::DifferentialForm(degree=0).        
    A1::DifferentialForm(degree=1).
    A2::DifferentialForm(degree=2).
    A3::DifferentialForm(degree=3).        
    ex:= A3 ^ A1 - A1 ^ A3 + A2 ^ A2 ^ A0;
    sort_product(_)
    tst:= - 2 A1 ^ A3 + A0 ^ A2 ^ A2 - @(ex);
    assert(tst==0)
    print("Test 39 passed")        

test39()

# def test40():
#     __cdbkernel__=create_scope()
#     {m,n,p,q}::Indices();
#     \partial{#}::PartialDerivative;
#     \phi::Depends(m);
#     ex:=\partial_{m}{ c \phi };
#     unwrap(_)
#     tst:= c \partial_{m}{\phi} - @(ex);
#     assert(tst==0)
#     print("Test 40 passed")
# 
# test40()

# {dx,dp}::DifferentialForm(degree=1);
# ex:= ( x dp ) ^ ( p dx ) + ( p dx ) ^ ( x dp );
# sort_product(ex);

def test40a():
    __cdbkernel__=create_scope()        
    {x,y}::Coordinate.
    \partial{#}::PartialDerivative.
    f::Depends(x);
    g::Depends(y);
    ex:=\partial_{x}{A*f*g};
    unwrap(_)
    tst:= A g \partial_{x}{f} - @(ex);
    assert(tst==0)
    print("Test 40a passed")

def test40b():
    __cdbkernel__=create_scope()        
    {x,y}::Coordinate.
    \partial{#}::PartialDerivative.
    f::Depends(x);
    g::Depends(y);
    ex:=\partial_{y}{A*f*g};
    unwrap(_)
    tst:= A f \partial_{y}{g} - @(ex);
    assert(tst==0)
    print("Test 40b passed")

test40a()
test40b()

def test41():
    __cdbkernel__=create_scope()        
    {a,b}::Indices;
    F^{a b}::AntiSymmetric;
    \nabla{#}::Derivative;
    ex:=\nabla_{b}{F^{a b}};
    young_project_tensor(ex)
    tst:= \nabla_{b}{ 1/2 F^{a b} - 1/2 F^{b a} } - @(ex);
    assert(tst==0)
    print("Test 41 passed")

test41()


        
# {\mu,\nu,\rho,\sigma}::Indices;        
# \partial{#}::Derivative;
# \psi_{\mu}::SelfAntiCommuting;    
# ex:= \psi_{\nu} \psi_{\mu} + \psi_{\mu} \psi_{\nu};
# sort_product(_);
# ex:= \partial_{\mu}{\psi_{\nu}} \psi_{\rho} + \psi_{\rho} \partial_{\mu}{\psi_{\nu}};
# sort_product(_);
# 
# ex:= \partial{\psi_{\nu}} \partial{\psi_{\mu}} + \psi_{\nu} \psi_{\mu};
# canonicalise(_);
# 
# ex:= \partial{\psi_{\nu}} \partial{\psi_{\mu}} + \psi_{\nu} \psi_{\mu};
# sort_product(_);

def test42():
    __cdbkernel__=create_scope()        
    \nabla{#}::Derivative;
    A::ImplicitIndex;
    tr{#}::Trace;
    \nabla{#}::ImplicitIndex;
    ex:=tr{\nabla{A B}};
    product_rule(ex)
    tst:= tr{\nabla{A} B + A \nabla{B}} - @(ex);
    assert(tst==0)
    print("Test 42 passed")

test42()

