def test01():
    __cdbkernel__=create_scope()
    ex:= a*b+a*c+d;
    factor_in(_, $b,c$)
    tst:=(b+c)*a + d - @(ex);
    assert(tst==0)
    print('Test 01 passed')

test01()

def test01b():
    __cdbkernel__=create_scope()
    ex:= a*b*c+a*c+d;
    factor_in(_, $b,c$)
    tst:=(b*c+c)*a + d - @(ex);
    assert(tst==0)
    print('Test 01b passed')

test01b()

def test02():
    __cdbkernel__=create_scope()
    ex:=2*a+b*a;
    factor_in(_, $b$)
    tst:=(2+b)*a - @(ex);
    assert(tst==0)
    print('Test 02 passed')

test02()
    
def test03():
    __cdbkernel__=create_scope()
    ex:= 3*a*b + 5*c*b;
    factor_in(_, $a,c$)
    tst:=(3 a + 5 c) b - @(ex);
    assert(tst==0)
    print('Test 03 passed')

test03()

def test04():
    __cdbkernel__=create_scope()
    ex:= a b + a c e - 4 q - 2 g a d + a d - 3 p s - 9/2 a;
    factor_out(_, $a$)
    tst4:= -4 q - 3 p s + a*(b + c e - 2 g d + d - 9/2) - @(ex);
    assert(tst4==0)
    print('Test 04 passed')

test04()

def test05():
    __cdbkernel__=create_scope()
    ex:= c - a;
    factor_out(_, $a$)
    tst:= c - a - @(ex);
    assert(tst==0)
    print('Test 05 passed')

test05()
    
def test06():
    __cdbkernel__=create_scope()
    ex:= 3 a b + 2 a c e + 5 a c + 7 e f - 2 c e + c d - a d + a - c;
    factor_out(ex, $a,c$)
    tst5:=  a * c * (2 * e + 5) + c * ( - 2 * e + d - 1) + a * (3 * b - d + 1) + 7 * e * f - @(ex);
    assert(tst5==0)
    print('Test 06 passed')

test06()
    
# Non-commuting objects

# Basic behaviour
def test07():
    __cdbkernel__=create_scope()
    {A, B, C, D, E}::NonCommuting;
    ex := A B + A C + D A + E A:
    factor_out(_, $A$, right=True)
    factor_out(_, $A$, right=False)
    tst := A*(B + C) + (D + E)*A - @(ex);
    assert(tst==0)
    print('Test 07 passed')

test07()

def test08():
    __cdbkernel__=create_scope()
    {A, B, C, D}::AntiCommuting.
    obj := A B + A C + D A:
    factor_out(_, $A$)
    tst := A*(B + C - D) - @(obj);
    assert(tst==0)
    print('Test 08 passed')

test08()

# Ordering of pulled out factors
def test09():
    __cdbkernel__=create_scope()
    {A, B}::AntiCommuting.
    obj := A B c X + c B A Y:
    factor_out(_, $A, B, c$)
    tst := A B c * (X - Y) - @(obj);
    assert(tst==0)
    print('Test 09 passed')

test09()

def test10():
    __cdbkernel__=create_scope()
    {A, B}::AntiCommuting.
    {A, X, Y}::NonCommuting.
    {B, X, Y}::NonCommuting.
    obj := A B c X + c B A Y + X A B c + Y c B A:
    factor_out(_, $c, A, B$)
    factor_out(_, $c, A, B$, right=True)
    tst := (A B (X-Y) + (-X+Y) A B) c - @(obj);
    assert(tst==0)
    print('Test 10 passed')

test10()

# Ordering past a non commuting term
def test11():
    __cdbkernel__=create_scope()
    {A, B, C, D, E}::AntiCommuting.
    {A, X}::NonCommuting.
    {B, X}::NonCommuting.
    obj := X A B Y + X B A Z:
    factor_out(_, $A, B, X$)
    tst := X A B (Y - Z) - @(obj);
    assert(tst==0)
    print('Test 11 passed')

test11()

def test12():
    __cdbkernel__=create_scope()
    {A, B, C, D, E}::AntiCommuting.
    {A, X}::NonCommuting.
    {B, X}::NonCommuting.
    obj := A B X A B Y + B A X B A Z:
    factor_out(_, $A, B, X$)
    tst := A B X A B (Y + Z) - @(obj);
    assert(tst==0)
    print('Test 12 passed')

test12()

def test13():
    __cdbkernel__=create_scope()
    {A, B, C, D, E}::AntiCommuting.
    {A, X}::NonCommuting.
    {B, X}::NonCommuting.
    obj := A B C D Y + D A B C Z:
    factor_out(_, $A, B, C, D$)
    tst := A B C D (Y - Z) - @(obj);
    assert(tst==0)
    print('Test 13 passed')

test13()

# Indices.

def test14():
    __cdbkernel__=create_scope()
    obj:= A_{a} B_{b} + A_{a} C_{b} + A_{b} D_{a};
    factor_out(_, $A_{a}$)
    tst:= A_{a} (B_{b} + C_{b}) + A_{b} D_{a} - @(obj);
    assert(tst==0)
    print('Test 14 passed')

test14()

def test15():
    __cdbkernel__=create_scope()
    ex:=  3 A G B_{m} C D + 4 E_{m}  ;
    factor_out(_,  $  A $ )
    tst:= 3 A G B_{m} C D + 4 E_{m}  - @(ex);
    assert(tst==0)
    print('Test 15 passed')

test15()

def test16():
    __cdbkernel__=create_scope()
    ex:=  3 A G B_{m} C D + 4 E_{m}  ;
    factor_out(_,  $  A $, right=True )
    tst:= 4 E_{m} + 3 G B_{m} C D A - @(ex);
    assert(tst==0)
    print('Test 16 passed')

test16()

def test17():
    __cdbkernel__=create_scope()
    ex:= x*x + x*x*x;
    factor_in(ex, $x$)
    tst:= x*x + x*x*x - @(ex);
    assert(tst==0)
    print("Test 17 passed")

test17()
    
def test18():
    __cdbkernel__=create_scope()
    {a,b,c,d,e}::Indices;
    D{#}::Derivative;
    ex:= D_{a}{D_{b}{F}} - D_{b}{D_{a}{F}} - S A_{a b};
    factor_in(ex, $ S $)
    tst:= D_{a}{D_{b}{F}} - D_{b}{D_{a}{F}} - S A_{a b} - @(ex);
    assert(tst==0)
    print("Test 18 passed")

test18()


# @reset.
# {a,b,m,n,p,q}::Indices.
# obj6:= A_{m n} C_{m n} D_{p q} + A_{p m} C_{m n} D_{n q} + A_{m n} C_{m n} E_{p q} Q;
# @factor_out!(%){ A_{a b}, C_{m n} };
# 
# NOTE: see the email to James Allen on Thu, 14 Oct 2010 20:58:52 +0100. 
# Should be solved by modifying @rename_dummies. The text below is obsolete but
# kept until the bug is fixed.
#
# This is very subtle. Consider
#
#   A_{m n} C_{n p} D_{m p} + A_{q r} C_{r s} E_{q t} F_{t s}
#
# This can clearly factor_out A & C. Standard comparison of the two prefactors
# would give a match, since  A_{m n} C_{n p} and A_{q r} C_{r s}  are equivalent.
# But we would need to relabel indices along the way.
#
# However, @collect_terms does not do this type of collecting either: it requires
# you to rename dummies first. If we do that, our problem goes away here too.

#{m,n,p,q,r,s,t}::Indices.
#A_{m n} C_{n p} D_{m p} + A_{q r} C_{r s} E_{q t} F_{t s};
#@rename_dummies!(%);

# So the conclusion is that we would prefer to stick to a simpler matching, in which
# we only allow for dummy index names to differ. But what do we then do with

#{m,n,p,q,r,s,t}::Indices.
#A_{m n} D_{m p} Q_{n p} + A_{q r} E_{q t} F_{t s} Q_{r s} ;
#@rename_dummies!(%);

# -> A_{m n} * D_{m p} * Q_{n p} + A_{m n} * E_{m p} * F_{p q} * Q_{n q};

# This does not lead to equal A*Q factors. So it seems that we are forced
# to do a comparison in which we flag
#
#    A_{m n} Q_{m n}  and  A_{m n} Q_{n q} as un-equal 
#
#    A_{m n} Q_{n q}  and  A_{m n} Q_{n p} as equal
#
#
    
# {m,n,p,q,r,s,t}::Indices;
# ex:= A_{m n} D_{m p} Q_{n p} + A_{q r} E_{q t} F_{t s} Q_{r s} ;
# factor_out(_, $A_{m n}$);
# sort_product(_);
# rename_dummies(_);
