# 
# Test 1:= keep_terms

# obj1:= a1 + a2 + a3 + a4 + a5 + a6 + a7;
# @keep_terms(%){2}{4};
# tst1:= a3 + a4 + a5 - @(obj1);
# @collect_terms!(%);
# @assert(tst1);
# 
# obj2:= a1 + a2 + a3 + a4 + a5 + a6 + a7;
# @keep_terms(%){6}{6};
# tst2:= a7 - @(obj2);
# @collect_terms!(%);
# @assert(tst2);
# 
# obj3:= a1 + a2 + a3 + a4 + a5 + a6 + a7;
# @keep_terms(%){3};
# tst3:= a4 + a5 + a6 + a7 - @(obj3);
# @collect_terms!(%);
# @assert(tst3);

# Test 4:= dummy index relabelling upon @ substitution:
#
# def test04():
# {m1,m2,m3,m4,m5}::Indices(vector).
# tmp1:= A_{m1 m2} + B_{m1 m2};
# tmp2:= C_{m3 m4} + D_{m3 m4};
# obj4:= @(tmp1) @(tmp2) g^{m1 m3} g^{m2 m4};

# Test 6 - 11:= prodsort
#
def test06():
    __cdbkernel__=create_scope()
    {X,G,Y,A,B}::SortOrder.
    {X,A}::AntiCommuting.
    obj6:= A B G X A X;
    sort_product(obj6)
    tst6:= X X G A A B + @(obj6);
    assert(tst6==0)
    print("Test 06 passed")

test06()

def test07():
    __cdbkernel__=create_scope()
    {X,G,Y,A,B}::SortOrder.
    {X,A}::NonCommuting.
    obj7:= A*B*G*X*A*X;
    sort_product(_)
    tst7:= G A X A X B - @(obj7);
    assert(tst7==0)
    print("Test 07 passed")
    
test07()

def test08():
    __cdbkernel__=create_scope()
    { W_{m n}, W_{m} }::SortOrder.
    { W_{m n}, W_{m} }::AntiCommuting.
    obj8:= W_{m n} W_{p} W_{q r} W_{s} W_{t};
    sort_product(_)
    tst8:= - W_{m n} W_{q r} W_{p} W_{s} W_{t} - @(obj8);
    assert(tst8==0)
    print("Test 08 passed")
    
test08()

def test09():
    __cdbkernel__=create_scope()
    { \theta_a, \theta_b }::SelfAntiCommuting.
    obj9:= \theta_c \theta_b \theta_a;
    sort_product(_)
    tst9:= \theta_a \theta_b \theta_c + @(obj9);
    assert(tst9==0)
    print("Test 09 passed")

test09()

def test10():
    __cdbkernel__=create_scope()
    {a,b,c,d}::Indices.
    { \theta_a, \chi_a }::SortOrder.
    \theta_{a}::SelfAntiCommuting.
    \chi_{a}::SelfAntiCommuting.
    { \theta_a, \chi_a }::Commuting.
    obj10:= \theta_c \theta_b \theta_d \chi_d \theta_a \chi_a;
    sort_product(_)
    tst10:= \theta_a \theta_b \theta_c \theta_d \chi_a \chi_d + @(obj10);
    assert(tst10==0)
    print("Test 10 passed")
    
test10()

def test11():
    __cdbkernel__=create_scope()
    \Gamma_{#}::GammaMatrix.
    obj11:= \Gamma_{m n p} \Gamma_{r s};
    sort_product(_)
    tst11:= \Gamma_{m n p} \Gamma_{r s} - @(obj11);
    assert(tst11==0)
    print("Test 11 passed")

test11()

# Test 12:= Inherited sort order
#
def test12():
    __cdbkernel__=create_scope()
    \bar{#}::Accent.
    {D,K,B,A}::SortOrder.
    obj12:= A \bar{B} \bar{K} D;
    sort_product(_)
    tst12:= D \bar{K} \bar{B} A - @(obj12);
    assert(tst12==0)
    print("Test 12 passed")

test12()

def test13():
    __cdbkernel__=create_scope()
    {b,d,e,f}::AntiCommuting.
    obj13:= (a+b)*(d*e*f);
    sort_product(_)
    tst13:= (a+b)*(d*e*f) - @(obj13);
    assert(tst13==0)
    print("Test 13 passed")

test13()
        
#def test14():
#
# {a,b,f}::AntiCommuting.
# {d,e,f,a,b}::SortOrder.
# obj14:= (a+b)*(d*e*f);
# sort_product(_);
# tst14:= (d*e*f)*(a+b) + @(obj14);
# assert(tst14==0)

def test15():
    __cdbkernel__=create_scope()
    {b,f}::AntiCommuting.
    obj15:= (d*e*f)*(a*b);
    sort_product(_)
    tst15:= (a*b)*(d*e*f) + @(obj15);
    assert(tst15==0)
    print("Test 15 passed")

test15()


def test16():
    __cdbkernel__=create_scope()
    {A_\mu, B_\mu}::AntiCommuting.
    \diff{#}::Derivative.
    obj16:= \diff{B_\mu}_{\nu} * \diff{A_\rho}_{\sigma};
    sort_product(_)
    tst16:= \diff{A_\rho}_{\sigma} * \diff{B_\mu}_{\nu} + @(obj16);
    assert(tst16==0)
    print("Test 16 passed")

test16()


def test17():
    __cdbkernel__=create_scope()
    \Gamma{#}::GammaMatrix.
    obj17a:= (\Gamma_{m n} + A_{m} \Gamma_{n})\Gamma_{p q r};
    sort_product(_)
    tst17a:= (\Gamma_{m n} + A_{m} \Gamma_{n})\Gamma_{p q r} - @(obj17a);
    assert(tst17a==0)
    obj17b:= \Gamma_{p q r} (\Gamma_{m n} + A_{m} \Gamma_{n});
    sort_product(_)
    tst17b:= \Gamma_{p q r} (\Gamma_{m n} + A_{m} \Gamma_{n}) - @(obj17b);
    assert(tst17b==0)
    print("Test 17 passed")

test17()

# def test18():
# 
# {\psi, \chi}::Spinor.
# {\psi, \chi, \Gamma_{#}}::NonCommuting.
# \Gamma_{#}::GammaMatrix.
# {\chi,\psi}::SortOrder.
# \bar{#}::Accent.
# obj18:= \bar{\psi} \Gamma_{m n p} \chi;
# @prodsort!(%);
# tst18:= \bar{\psi} \Gamma_{m n p} \chi - @(obj18);
# @collect_terms!(%);
# @assert(tst18);
# 

# obj20:= (A+(B+C*(G+K))*D)*(E+F)*(M+(N+P*(H+K))*P);
# @distribute!3(%);
# tst20:= (A + B * D + C * (G + K) * D) * (E + F) * (M + N * P + P * (H + K) * P) - @(obj20);
# @collect_terms(%);
# @assert(tst20);
# 
# obj21:= { A*(B+C*(D+E)), F*(G+H*(I+J)) };
# @distribute!2(%);
# tst21:= { A*B + A*C*(D+E), F*G+F*H*(I+J) } - @(obj21);
# @collect_terms!(%);
# @assert(tst21);
# 

def test18():
    __cdbkernel__=create_scope()
    {\chi, \psi}::Weight(label=grassmann, value=1);
    ex:= \chi \psi \chi;
    drop_weight(_, $grassmann=2$)
    tst1:= \chi\psi\chi - @(ex);
    assert(tst1==0)
    drop_weight(ex, $grassmann=3$)
    assert(ex==0)
    print("Test 18 passed")
        
test18()

def test19():
    __cdbkernel__=create_scope()
    {\phi, B_{m}, B^{m}}::Weight(label=small, value=1).
    ex:= H B^{m} B_{n} + B_{n} C^{m};
    drop_weight(_, $small=2$)
    tst:= B_{n} C^{m} - @(ex);
    assert(tst==0)
    print("Test 19 passed")
        
test19()

def test20():
    __cdbkernel__=create_scope()
    {\phi, B_{m}, B^{m}}::Weight(label=small, value=1).
    ex:= H B^{m} B_{n} + B_{n} C^{m};
    keep_weight(_, $small=2$)
    tst:= H B^{m} B_{n} - @(ex);
    assert(tst==0)
    print("Test 20 passed")
        
test20()

def test21():
    __cdbkernel__=create_scope()
    {\chi, \psi}::Weight(label=grassmann, value=1).
    ex:= \chi \chi \chi + 2 \chi \chi + \chi;
    drop_weight(_, $grassmann=3$)
    tst:= 2 \chi \chi + \chi - @(ex);
    assert(tst==0)
    print("Test 21 passed")
        
test21()

def test21b():
    __cdbkernel__=create_scope()
    {\chi, \psi}::Weight(label=grassmann, value=1).
    ex:= \chi \chi \chi + \chi \chi * (\chi + \psi) + \chi + \chi \chi;
    keep_weight(_, $grassmann=3$)
    tst:= \chi \chi \chi + \chi \chi * (\chi + \psi) - @(ex);
    assert(tst==0)
    print("Test 21b passed")
        
test21b()

def test22():
    __cdbkernel__=create_scope()
    {\chi, \psi}::Weight(label=grassmann, value=1).
    \bar{#}::Accent.
    ex:= \chi \bar{\chi \chi};
    keep_weight(_, $grassmann=3$)
    tst:= \chi \bar{\chi \chi} - @(ex);
    assert(tst==0)
    print("Test 22 passed")
        
test22()

def test23():
    __cdbkernel__=create_scope()
    {a,b}::Weight(label=small, value=1);
    ex:= a**2 + a b + b**2 + a**2 b + a b c;
    keep_weight(_, $small=2$)
    tst:= a**2 + a b + b**2 + a b c - @(ex);
    assert(tst==0)
    print("Test 23 passed")
        
test23()

def test24():
    __cdbkernel__=create_scope()
    \theta::SelfAntiCommuting;
    ex:= \theta \chi \theta + \theta \chi \lambda;
    sort_product(_)
    tst:= \theta\chi - @(ex);
    assert(tst)
    print("Test 24 passed")
        
test24()

def test25():
    __cdbkernel__=create_scope()
    {i,j,k}::Indices;
    \theta_i::SelfAntiCommuting;
    ex:= \theta_{k} \theta_{j} \theta_{k} \theta_{j};
    sort_product(_)
    assert(ex==0)
    print("Test 25 passed")

test25()        

def test26():
    __cdbkernel__=create_scope()
    A = Ex(0)
    B = Ex(0)
    tst:= @(A) - @(B);
    assert(tst==0)
    A = Ex(0)
    B = Ex(0)
    tst:= @(A) + @(B);
    assert(tst==0)
    print("Test 26 passed")

test26()

def test27():
    __cdbkernel__=create_scope()
    \dagger::Symbol;
    ex:= A^{\dagger} A^{\dagger};
    collect_factors(_)
    tst:= (A^{\dagger})**2 - @(ex);
    assert(tst==0)
    print("Test 27a passed")
    ex:= B^{m} B^{m};
    collect_factors(_)
    tst:= B^{m} B^{m} - @(ex);
    assert(tst==0)
    print("Test 27b passed")

test27()
        
  
# \bar{#}::Accent.
# obj22e:= \chi \chi \bar{\chi};
# @keep_weight!(%){grassmann}{3};
# tst22e:= \chi \chi \bar{\chi} - @(obj22e);
# @collect_terms!(%);
# @assert(tst22e);
# 
# @reset.
# {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u#}::Indices.
# Q_{a b c}::Weight(label=myTerms,value=3).
# g_{a b c}::Weight(label=myTerms,value=1).
# obj22f:=Q_{f g h} Dx^{f} Dx^{g} Dx^{h};
# @keep_weight!(%){myTerms}{3};
# tst22f:=Q_{f g h} Dx^{f} Dx^{g} Dx^{h} - @(obj22f);
# @collect_terms!(%);
# @assert(tst22f);
# 
# @reset.
# {A,B}::Weight(label=field, value=1).
# obj22g:= C + A + A B;
# @keep_weight!(%){field}{2};
# tst22g:= A B - @(obj22g);
# @collect_terms!(%);
# @assert(tst22g);
# 
# @reset.
# A::Weight(label=field, value=1).
# B::Weight(label=field, value=-1).
# obj22h:= C + A + A B;
# @keep_weight!(%){field}{0};
# tst22h:= C + A B - @(obj22h);
# @collect_terms!(%);
# @assert(tst22h);
# 
# @reset.
# A::Weight(label=field, value=3/2).
# B::Weight(label=field, value=-1).
# C::Weight(label=field, value=1/2).
# obj22i:= C + A + A B;
# @keep_weight!(%){field}{1/2};
# tst22i:= C + A B - @(obj22i);
# @collect_terms!(%);
# @assert(tst22i);
# 
# 
# # Test 23: spinor sort
# #
# @reset;
# {\chi, \psi, \psi_{m}}::Spinor(dimension=10, type=MajoranaWeyl).
# {\chi, \psi, \psi_{m}}::AntiCommuting.
# \bar{#}::DiracBar.
# \Gamma{#}::GammaMatrix.
# {\psi_{m}, \psi, \chi}::SortOrder.
# obj23:= \bar{\chi} \Gamma_{m n p} \psi;
# @spinorsort!(%);
# tst23:= \bar{\psi} \Gamma_{m n p} \chi - @(obj23);
# @collect_terms!(%);
# @assert(tst23);
# 
# obj24:= \bar{\chi}\psi;
# @spinorsort!(%);
# tst24:= \bar{\psi}\chi - @(obj24);
# @collect_terms!(%);
# @assert(tst24);
# 
# obj25:= \chi\Gamma_{m n p}\psi;
# @spinorsort!(%);
# tst25:= \chi\Gamma_{m n p}\psi - @(obj25);
# @collect_terms!(%);
# @assert(tst25);
# 
# obj26:= \bar{\chi} \Gamma_{m n} \psi_{m};
# @spinorsort!(%);
# tst26:= - \bar{\psi_{m}} \Gamma_{m n} \chi - @(obj26);
# @collect_terms!(%);
# @assert(tst26);
# 
# # Test 27:= multiple implicitindex objects
# #
# @reset;
# {\chi,\psi, \lambda, \rho}::Spinor.
# \Gamma{#}::GammaMatrix.
# \bar{#}::DiracBar.
# obj27:= \bar{\chi}\Gamma_{m n p}\psi A_{m n} \bar{\lambda}\Gamma_{p}\rho;
# @prodsort!(%);
# @spinorsort!(%);
# A_{m n} \bar{\chi} \Gamma_{m n p} \psi \bar{\lambda} \Gamma_{p} \rho;
# 
# # Test 28: powers
# @reset.
# obj28:= 8 d**2 - d**3;
# @collect_terms!(%);
# @substitute!(%)( d**3 -> Q );
# 
# tst28:= 8 d**2 - Q - @(obj28);
# @collect_terms!(%);
# @assert(tst28);
# 
# # Test 28: sumsort
# #
# @reset.
# {D,A,C,B}::SortOrder;
# obj28:= A + B + C - D + B - A;
# @sumsort!(%);
# @rename!(%)("\sum")("\comma");
# tst28:= {- D,  A, -A, C, B, B} - @(obj28);
# @collect_terms!(%);
# @assert(tst28);
# 
# @reset.
# obj29:= A + B + C - D + B - A;
# @sumsort!(%);
# @rename!(%)("\sum")("\comma");
# tst29:= { A, -A, B, B, C, -D } - @(obj29);
# @collect_terms!(%);
# @assert(tst29);
# 
# @reset.
# obj30:= a**{-1+d}-a**{d-1};
# @sumsort!(%);
# @collect_terms!(%);
# @assert(obj30);
# 
# # Test 31: commutativity properties
# #
# @reset.
# {\sigma_{1},\sigma_{2},\sigma_{3}}::NonCommuting.
# obj31:= \sigma_{2}\sigma_{1};
# @prodsort!(%);
# tst31:= \sigma_{2}\sigma_{1} - @(obj31);
# @collect_terms!(%);
# @assert(tst31);
# 
# @reset.
# \sigma{#}::SelfNonCommuting.
# obj32:= \sigma_{2}\sigma_{1};
# @prodsort!(%);
# tst32:= \sigma_{2}\sigma_{1} - @(obj32);
# @collect_terms!(%);
# @assert(tst32);
# 

# Test 32: more drop & keep
def test32a():
    __cdbkernel__=create_scope()
    {\phi,\chi}::Weight(label=small, value=1).
    \partial{#}::PartialDerivative.
    \partial{#}::WeightInherit(label=all, type=multiplicative).
    obj:= \phi \partial_{0}{\phi} + \partial_{0}{\lambda};
    drop_weight(obj, $small=1$)
    tst:= \phi \partial_{0}{\phi} + \partial_{0}{\lambda} - @(obj);
    assert(tst==0)
    print("Test 32a passed")

test32a()

def test32b():
    __cdbkernel__=create_scope()
    {\phi,\chi}::Weight(label=small, value=1).
    \lambda::Weight(label=small, value=3).
    obj:= \phi \chi + \lambda;
    drop_weight(obj, $small=2$)
    tst:= \lambda - @(obj);
    assert(tst==0)
    print("Test 32b passed")

test32b()
        
def test32c():
    __cdbkernel__=create_scope()
    {\phi,\chi}::Weight(label=small, value=2).
    \lambda::Weight(label=small, value=2).
    obj:= Q ( \phi \chi + \lambda ) + ( \phi + \chi );
    #           4          2           2       2
    #             undefined                2
    drop_weight(obj, $small=2$)
    tst:= Q ( \phi \chi + \lambda ) - @(obj);
    assert(tst==0)
    print("Test 32c passed")

test32c()

# # Test34: canonicalorder
# #
# @reset.
# obj34:= A_{a b c} + A_{b a c};
# @acanonicalorder!(%)(_{a}, _{b});
# @collect_terms!(%);
# @assert(obj34);
# 
# # Test 35: keep current expression pointer consistent
# #
# @reset.
# obj35a:= A B + B Q;
# obj35b:= { A -> Z };
# vary(obj35a, $Q -> Z$)
# substitute(_, obj35b)
# tst35:= B Z - @(%);
# @collect_terms!(%);
# @assert(tst35);
# 

# Test 36: two sets of anti-commuting objects
def test36():
    __cdbkernel__=create_scope()
    {a,b}::AntiCommuting.
    {a,c}::AntiCommuting.
    obj36:= b*a;
    sort_product(_)
    tst36:= - a b - @(obj36);
    assert(tst36==0)
    print("Test 36 passed")

test36()

# # Test 37: weight inheritance
# #
# @reset.
# D{#}::Derivative.
# {a,b,c}::Symbol.
# {a,b,c}::Weight(label=grassmann, value=1).
# D_{a}( b c ) + D_{c}( d e );
# @keep_weight!(%){grassmann}{3};
# 

# Test 38: stupid nested expression bug

def test36():
    __cdbkernel__=create_scope()
    obj37:= A - ( B - ( C - ( D - E ) ) );
    tst38:= A - B + C - D + E - @(obj37);
    assert(tst38==0)
    print("Test 38 passed")

test36()

def test28():
    ex:=k^{a}n_{c a}+2**{1/2} k_{c} 2**{1/2};
    collect_factors(_)
    tst:=k^{a}n_{c a}+2**{1/2} k_{c} 2**{1/2} - @(ex);
    assert(tst==0)
    print("Test 28 passed")

test28()

def test29():
    __cdbkernel__=create_scope()
    {a,ad, ket{#}}::NonCommuting;
    ex:= ad ad ad ket(0);
    substitute(ex, $ad ket(n??) = \sqrt{n?? +1} ket(n??+1) $)
    tst:= ad ad ket(1) - @(ex);
    assert(tst==0)
    print("Test 29 passed")

test29()

def test30():
    __cdbkernel__=create_scope()
    t::Coordinate;
    a::Depends(t);
    \partial{#}::PartialDerivative;
    ex:= (\partial_{t}{a})**(-1)  (\partial_{t}{a})**3;
    collect_factors(ex)
    tst:= (\partial_{t}{a})**2 - @(ex);
    assert(tst==0)
    print("Test 30 passed")

test30()

def test31():
    __cdbkernel__=create_scope()
    {A,B,C}::AntiCommuting.
    \partial{#}::Derivative.
    {F,G,H}::Depends(\partial{#}).
    ex := \partial{(A F + G) C};
    unwrap(ex)
    tst:= \partial{(A F + G) C} - @(ex);
    assert(tst==0)
    print("Test 31a passed")
    ex := \partial{(A F + B G) C};
    unwrap(ex)
    tst:= -C \partial{A F + B G} - @(ex);
    assert(tst==0)
    print("Test 31b passed")

test31()

def test32():
    # issues/106
    __cdbkernel__=create_scope()  
    {A, B, C}::AntiCommuting.
    d{#}::Derivative.
    A::Depends(d{#}).
    B::Depends(d{#}).
    ex:= d{A B C};
    unwrap(ex)
    tst:= C d{A B} - @(ex);
    assert(tst==0)
    print("Test 32a passed")
    ex:= d{A B A C};
    unwrap(ex)
    tst:= - C d{A B A} - @(ex);
    assert(tst==0)
    print("Test 32b passed")

test32()
        
def test33():
    __cdbkernel__=create_scope()  
    {\phi,\chi}::Weight(label=small, value=2).
    ex:= \chi\chi + \phi = \phi + \chi;
    keep_weight(ex, $small=2$, deep=True)    
    tst:= (\phi = \phi + \chi) - @(ex);
    assert(tst==$0=0$)
    print("Test 33 passed")

test33()
        
# {A, B, C}::AntiCommuting.
# d{#}::Derivative.
# A::Depends(d{#}).
# B::Depends(d{#}).
# ex:= d_{B}{A B C};
# unwrap(ex)

# {A,B,C,D,E,F}::NonCommuting;
# ex:= A * (B + C) * D * (E + F);
# distribute(_);

def test34():        
    __cdbkernel__=create_scope()  
    ex:= [1,2,3,4];
    tst:= -3/2 @(ex);
    assert(tst==$[-3/2, -3, -9/2, -6]$)
    print("Test 34 passed")

test34()

def test35():
    __cdbkernel__=create_scope()  
    ex:= 3 a**b;
    substitute(ex, $b -> 1$)
    assert( ex == $3 a$ )
    print("Test 35a passed")
    ex:= 3 2**b;
    substitute(ex, $b -> 1$)
    assert( ex == $6$ )
    print("Test 35b passed")
        

test35()


def test36():
    __cdbkernel__=create_scope()  
    ex:= (2 a 3 b c)**(-1);
    expand_power(ex)
    tst:= 1/6 a**(-1) b**(-1) c**(-1)  - @(ex);
    assert(tst==0)
    print("Test 36a passed")
    ex:= (2 a 3 b c)**(-2);
    expand_power(ex)
    tst:= 1/36 a**(-1) b**(-1) c**(-1)  a**(-1) b**(-1) c**(-1) - @(ex);
    assert(tst==0)
    print("Test 36b passed")
    ex:= a**(-1);
    expand_power(_)
    tst:= a**(-1) - @(ex);
    assert(tst==0)        
    print("Test 36c passed")
    ex:= a**(-2);
    expand_power(_)
    tst:= a**(-1) a**(-1) - @(ex);
    assert(tst==0)        
    print("Test 36d passed")
    ex:= a b c (a b c)**(-1);
    expand_power(_)
    collect_factors(_)
    assert(ex==1)
    print("Test 36e passed")
    ex:= a**0;
    expand_power(_)
    assert(ex==1)
    print("Test 36f passed")

test36()
        
        
