/*
Test input for finitely presented semigroups and monoids.

This file should include a test of every function listed in the manual.

No claims are made for completeness in the testing!  in particular, not
all cases will be tested, and certain complex interactions are not tested.
*/


/*
----------------------------------------
    Construction of a free semigroup
----------------------------------------
*/

f2 := FreeSemigroup(2);
f3 := FreeSemigroup(3);
m2 := FreeMonoid(2);
m3 := FreeMonoid(3);

f<x, y> := FreeSemigroup(2);
m<w, z> := FreeMonoid(2);
delete x, y, f, w, z, m;

/*
----------------------------------------
    Specification of a word
----------------------------------------
*/

errors := 0;

if (f2.1) ne f2.1 or (m2.1) ne m2.1 then
    print "ERROR: Parenthesised generator not equal to non-parenthesised generator";
    errors := errors + 1;
end if;

if f3.2^3 ne f3.2 * f3.2 * f3.2 or m3.2^3 ne m3.2 * m3.2 * m3.2 then
    print "ERROR: Power of generator not correct";
    errors := errors + 1;
end if;

if f2![1, 2, 1, 2, 2] ne f2.1 * f2.2 * f2.1 * f2.2^2 then
    print "ERROR: Sequence not equal to word";
    errors := errors + 1;
end if;

if m2![1, 2, 1, 2, 2] ne m2.1 * m2.2 * m2.1 * m2.2^2 then
    print "ERROR: Sequence not equal to word";
    errors := errors + 1;
end if;

if m2!1 ne Id(m2) then
    print "ERROR: m2!1 not equal to Id(m2)";
    errors := errors + 1;
end if;

if errors eq 0 then
    print "Correctness in specification of words";
else
    print errors, " error(s) in specification of words";
end if;

/*
----------------------------------------
    Elementary operators for words
----------------------------------------
*/

errors := 0;

u := f2.1 * f2.2^3 * f2.1^2;
v := f2.1^4 * f2.2 * f2.1;

w := m2.1 * m2.2^3 * m2.1^2;
z := m2.1^4 * m2.2 * m2.1;

if u * v ne f2.1 * f2.2^3 * f2.1^6 * f2.2 * f2.1 then
    print "ERROR: Word product not correct";
    errors := errors + 1;
end if;

if w * z ne m2.1 * m2.2^3 * m2.1^6 * m2.2 * m2.1 then
    print "ERROR: Word product not correct";
    errors := errors + 1;
end if;

if v^3 ne f2.1^4 * f2.2 * f2.1^5 * f2.2 * f2.1^5 * f2.2 * f2.1 then
    print "ERROR: Power of word incorrect";
    errors := errors + 1;
end if;

if z^3 ne m2.1^4 * m2.2 * m2.1^5 * m2.2 * m2.1^5 * m2.2 * m2.1 then
    print "ERROR: Power of word incorrect";
    errors := errors + 1;
end if;

delete u, v, w, z;

if errors eq 0 then
    print "Correctness in elementary operators for words";
else
    print errors, " error(s) in elementary operators for words";
end if;

/*
----------------------------------------
    Boolean operators for words
----------------------------------------
*/

errors := 0;

u1 := f3.3^3 * f3.1 * f3.2 * f3.1^2 * f3.3;
u2 := f3.3^3 * f3.1 * f3.2;

v1 := f3.1 * f3.2;
v2 := f3.1^2;
v3 := f3.2 * f3.1;

w1 := m3.3^3 * m3.1 * m3.2 * m3.1^2 * m3.3;
w2 := m3.3^3 * m3.1 * m3.2;

z1 := m3.1 * m3.2;
z2 := m3.1^2;
z3 := m3.2 * m3.1;

if u1 eq u2 or w1 eq w2 then
    print "ERROR: Failure for eq between two words";
    errors := errors + 1;
end if;

if u2 ne u2 or w2 ne w2 then
    print "ERROR: Failure for ne between two words";
    errors := errors + 1;
end if;

if v1 lt v2 or z1 lt z2 then
    print "ERROR: Failure for lt between two words";
    errors := errors + 1;
end if;

if v3 le v2 or z3 le z2 then
    print "ERROR: Failure for le between two words";
    errors := errors + 1;
end if;

if u2 gt u1 or w2 gt w1 then
    print "ERROR: Failure for gt between two words";
    errors := errors + 1;
end if;

delete u1, u2, v1, v2, v3, w1, w2, z1, z2, z3;

if errors eq 0 then
    print "Correctness in Boolean operators for words";
else
    print errors, " error(s) in Boolean operators for words";
end if;

/*
----------------------------------------
    Elementary functions for words
----------------------------------------
*/

errors := 0;

u1 := f3.3^3 * f3.1 * f3.2 * f3.1^2;
u2 := f3.3^3 * f3.2 * f3.3 * f3.2 * (f3.2 * f3.3)^2;

v1 := f3.1 * f3.2;
v2 := f3.2 * f3.3 * f3.2;

w1 := m3.3^3 * m3.1 * m3.2 * m3.1^2;
w2 := m3.3^3 * m3.2 * m3.3 * m3.2 * (m3.2 * m3.3)^2;

z1 := m3.1 * m3.2;
z2 := m3.2 * m3.3 * m3.2;

// # (length of a word)

if #u1 ne 7 or #w1 ne 7 then
    print "ERROR: Length of non-tfial word incorrect";
    errors := errors + 1;
end if;

if #Id(m2) ne 0 then
    print "ERROR: Length of tfial word incorrect";
    errors := errors + 1;
end if;

// G!sequence

if f3![v2] ne v2 or m3![z2] ne z2 then
    print "ERROR: Simple sequence to word failed";
    errors := errors + 1;
end if;

if f3![u1, v1, v1] ne u1 * v1^2 or m3![w1, z1, z1] ne w1 * z1^2 then
    print "ERROR: Non-tfial sequence to word failed";
    errors := errors + 1;
end if;

// Eliminate

if Eliminate(u1, f3.1, f3.2 * f3.3) ne u2 or Eliminate(w1, m3.1, m3.2 * m3.3) ne w2 then
    print "ERROR: Non-tfial Eliminate incorrect";
    errors := errors + 1;
end if;

if Eliminate(v1, f3.3, f3.1) ne v1 or Eliminate(z1, m3.3, m3.1) ne z1 then
    print "ERROR: Trivial Eliminate incorrect";
    errors := errors + 1;
end if;

if Eliminate({u1, v1}, f3.1, f3.2 * f3.3) ne {u2, v2} then
    print "ERROR: Non-tfial set Eliminate incorrect";
    errors := errors + 1;
end if;

if Eliminate({w1, z1}, m3.1, m3.2 * m3.3) ne {w2, z2} then
    print "ERROR: Non-tfial set Eliminate incorrect";
    errors := errors + 1;
end if;

// Match

m_t, m_s := Match(u2, v2, 1);
if not m_t or m_s ne 4 then
    print "ERROR: Non-tfial Match incorrect";
    errors := errors + 1;
end if;

m_t, m_s := Match(u2, u2, 1);
if not m_t or m_s ne 1 then
    print "ERROR: Matching identical words failed";
    errors := errors + 1;
end if;

m_t, m_s := Match(w2, z2, 1);
if not m_t or m_s ne 4 then
    print "ERROR: Non-tfial Match incorrect";
    errors := errors + 1;
end if;

m_t, m_s := Match(w2, w2, 1);
if not m_t or m_s ne 1 then
    print "ERROR: Matching identical words failed";
    errors := errors + 1;
end if;

if Match(u1, u2, 3) then
    print "ERROR: Match claimed for non-subword";
    errors := errors + 1;
end if;

if Match(w1, w2, 3) then
    print "ERROR: Match claimed for non-subword";
    errors := errors + 1;
end if;

if Match(Id(m3), w2, 1) then
    print "ERROR: Match in tfial word failed";
    errors := errors + 1;
end if;

m_t, m_s := Match(z1, Id(m3), 2);
if not m_t or m_s ne 2 then
    print "ERROR: Matching tfial word failed";
    errors := errors + 1;
end if;

delete m_t, m_s;

// Random

if Random(m3, 0, 0) ne Id(m3) then
    print "ERROR: Random word of length 0 not identity";
    errors := errors + 1;
end if;

lrf3 := #Random(f3, 5, 12);
if lrf3 lt 5 or lrf3 gt 12 then
    print "ERROR: Random word was length outside range";
    errors := errors + 1;
end if;
lrf3 := #Random(m3, 5, 12);
if lrf3 lt 5 or lrf3 gt 12 then
    print "ERROR: Random word was length outside range";
    errors := errors + 1;
end if;
delete lrf3;

// RotateWord

if RotateWord(u1, 0) ne u1 then
    print "ERROR: Rotation by zero does not return original word";
    errors := errors + 1;
end if;

if RotateWord(u2, #u2) ne u2 then
    print "ERROR: Rotation of word by its length does not return original word";
    errors := errors + 1;
end if;

if RotateWord(w1, 0) ne w1 then
    print "ERROR: Rotation by zero does not return original word";
    errors := errors + 1;
end if;

if RotateWord(w2, #w2) ne w2 then
    print "ERROR: Rotation of word by its length does not return original word";
    errors := errors + 1;
end if;

if RotateWord(Id(m2), 2) ne Id(m2) then
    print "ERROR: Rotation of identity does not return identity";
    errors := errors + 1;
end if;

if RotateWord(v2, 1) ne RotateWord(v2, #v2 + 1) then
    print "ERROR: Rotation does not reduce by length of word";
    errors := errors + 1;
end if;

if RotateWord(z2, 1) ne RotateWord(z2, #z2 + 1) then
    print "ERROR: Rotation does not reduce by length of word";
    errors := errors + 1;
end if;

if RotateWord(v2, 1) ne RotateWord(v2, 1 - #v2) then
    print "ERROR: Negative rotation not equal to equivalent positive rotation";
    errors := errors + 1;
end if;

if RotateWord(z2, 1) ne RotateWord(z2, 1 - #z2) then
    print "ERROR: Negative rotation not equal to equivalent positive rotation";
    errors := errors + 1;
end if;

if RotateWord(u1, 3) ne f3.2 * f3.1^2 * f3.3^3 * f3.1 then
    print "ERROR: Non-tfial rotation failed";
    errors := errors + 1;
end if;

if RotateWord(w1, 3) ne m3.2 * m3.1^2 * m3.3^3 * m3.1 then
    print "ERROR: Non-tfial rotation failed";
    errors := errors + 1;
end if;

// Substitute

if Substitute(u1, 1, #u1, u1) ne u1 then
    print "ERROR: Substitution of word for itself failed";
    errors := errors + 1;
end if;

if Substitute(w1, 1, #w1, w1) ne w1 then
    print "ERROR: Substitution of word for itself failed";
    errors := errors + 1;
end if;

if Substitute(u1, 1, 0, u1) ne u1^2 then
    print "ERROR: Substitution of tfial string failed";
    errors := errors + 1;
end if;

if Substitute(w1, 1, 0, w1) ne w1^2 then
    print "ERROR: Substitution of tfial string failed";
    errors := errors + 1;
end if;

if Substitute(u2, 1, #u2, v1) ne v1 then
    print "ERROR: Complete substitution failed";
    errors := errors + 1;
end if;

if Substitute(w2, 1, #w2, z1) ne z1 then
    print "ERROR: Complete substitution failed";
    errors := errors + 1;
end if;

if Substitute(v2, 2, 1, v1) ne f3.2 * f3.1 * f3.2^2 then
    print "ERROR: Simple substitution failed";
    errors := errors + 1;
end if;

if Substitute(z2, 2, 1, z1) ne m3.2 * m3.1 * m3.2^2 then
    print "ERROR: Simple substitution failed";
    errors := errors + 1;
end if;

// Subword

if Subword(u1, 1, #u1) ne u1 then
    print "ERROR: Complete subword incorrect";
    errors := errors + 1;
end if;

if Subword(u2, 7, 3) ne v2 then
    print "ERROR: Proper subword incorrect";
    errors := errors + 1;
end if;

if Subword(w1, 1, #w1) ne w1 then
    print "ERROR: Complete subword incorrect";
    errors := errors + 1;
end if;

if Subword(w2, 2, 0) ne Id(m3) then
    print "ERROR: Trivial subword incorrect";
    errors := errors + 1;
end if;

if Subword(w2, 7, 3) ne z2 then
    print "ERROR: Proper subword incorrect";
    errors := errors + 1;
end if;

delete u1, u2, v1, v2, w1, w2, z1, z2;

if errors eq 0 then
    print "Correctness in elementary functions for words";
else
    print errors, " error(s) in elementary functions for words";
end if;

/*
----------------------------------------
    Specification of Relations
----------------------------------------
*/

errors := 0;

u := f2.1 * f2.2^2 * f2.1;
v := f2.1 * f2.2 * f2.1 * f2.2;

r := u = v;

if LHS(r) ne u then
    print "ERROR: Left hand side of relation incorrect";
    errors := errors + 1;
end if;

if RHS(r) ne v then
    print "ERROR: Right hand side of relation incorrect";
    errors := errors + 1;
end if;

u := m2.1 * m2.2^2 * m2.1;
v := m2.1 * m2.2 * m2.1 * m2.2;

r := u = v;

if LHS(r) ne u then
    print "ERROR: Left hand side of relation incorrect";
    errors := errors + 1;
end if;

if RHS(r) ne v then
    print "ERROR: Right hand side of relation incorrect";
    errors := errors + 1;
end if;

delete u, v, r;

if errors eq 0 then
    print "Correctness in specification of relations";
else
    print errors, " error(s) in specification of relations";
end if;

/*
----------------------------------------
    Construction of a quotient
----------------------------------------
*/

tf := quo<f3 | f3.3*f3.1*f3.3*f3.1 = f3.1*f3.2*f3.1*f3.2 = f3.2*f3.3*f3.2*f3.3>;
tm := quo<m3 | m3.3*m3.1*m3.3*m3.1 = m3.1*m3.2*m3.1*m3.2 = m3.2*m3.3*m3.2*m3.3>;

af5 := quo<f2 | f2.1^2 = f2.2^3 = (f2.1 * f2.2)^5>;
am5 := quo<m2 | m2.1^2, m2.2^3, (m2.1 * m2.2)^5>;

tetf<rf, sf> := Semigroup<r, s | r^3 = s^3 = (r * s)^2>;
tetm<rm, sm> := Monoid<r, s | r^3, s^3, (r * s)^2>;

gf16 := quo<f3 | f3.1^2 = f3.2^2 = f3.3^2,
		f3.1*f3.2*f3.3 = f3.2*f3.3*f3.1 = f3.3*f3.1*f3.2>;
gm16 := quo<m3 | m3.1^2, m3.2^2, m3.3^2,
		m3.1*m3.2*m3.3 = m3.2*m3.3*m3.1 = m3.3*m3.1*m3.2>;

ftc := quo<f2 | f2.1^8 = f2.2^7 = (f2.1 * f2.2)^2 = (f2.2 * f2.1)^3>;
mtc := quo<m2 | m2.1^8, m2.2^7, (m2.1 * m2.2)^2, (m2.2 * m2.1)^3>;

inf := quo<f2 | f2.1^3 = f2.2^3 = (f2.1 * f2.2)^3>;
inm := quo<m2 | m2.1^3, m2.2^3, (m2.1 * m2.2)^3>;

/*
----------------------------------------
    Construction of a subgroup
----------------------------------------
*/

// sub<group | Generators>

errors := 0;

tf12 := sub<tf | tf.1, tf.2>;
tf13 := sub<tf | tf.1, tf.3>;
tf2 := sub<tf | tf.2>;
tm12 := sub<tm | tm.1, tm.2>;
tm13 := sub<tm | tm.1, tm.3>;
tm2 := sub<tm | tm.2>;
tm0 := sub<tm | Id($)>;

Hf := sub<af5 | af5.1, af5.1*af5.2>;
Kf := sub<af5 | [af5.1 * af5.2, af5.2 * af5.1]>;
Hm := sub<am5 | am5.1, am5.1*am5.2>;
Km := sub<am5 | [am5.1 * am5.2, am5.2 * am5.1]>;
af5a := sub<af5 | af5.1>;
af5b := sub<af5 | af5.2>;
am5a := sub<am5 | am5.1>;
am5b := sub<am5 | am5.2>;

t1f := sub<tetf | [1, 2]>;
t2f := sub<tetf | {rf, rf*sf}>;
t1m := sub<tetm | [1, 2]>;
t2m := sub<tetm | {rm, rm*sm}>;

gf16a := sub<gf16 | (gf16.1*gf16.2)>;
gf16b := sub<gf16 | gf16.1*gf16.3, gf16.2>;
gm16a := sub<gm16 | (gm16.1*gm16.2)>;
gm16b := sub<gm16 | gm16.1*gm16.3, gm16.2>;

if errors eq 0 then
    print "Correctness in construction of quotients and subgroups";
else
    print errors, " error(s) in construction of quotients and subgroups";
end if;

/*
----------------------------------------
    Editing defining Relations
----------------------------------------
*/

errors := 0;

gf16add := AddRelation(gf16, gf16!gf16a.1 = gf16!gf16a.1^2);
gf16add2 := AddRelation(gf16, gf16!gf16a.1 = gf16!gf16a.1^2, 2);
gf16iso1 := DeleteRelation(gf16add2, 3);
gf16iso2 := DeleteRelation(gf16add2, gf16!gf16a.1 = gf16!gf16a.1^2);
af4a := ReplaceRelation(inf, 2, (inf.1 * inf.2)^2 = inf.1);
af4b := ReplaceRelation(inf, inf.1^3 = (inf.1 * inf.2)^3, (inf.1 * inf.2)^2 = inf.1);
tetfP := AddGenerator(tetf);
tetfPM := DeleteGenerator(tetfP, tetfP.3);
af5iso := AddGenerator(af5, af5.1 * af5.2);

gm16add := AddRelation(gm16, gm16!gm16a.1 = Id(gm16));
gm16add2 := AddRelation(gm16, gm16!gm16a.1 = Id(gm16), 2);
gm16iso1 := DeleteRelation(gm16add2, 3);
gm16iso2 := DeleteRelation(gm16add2, gm16!gm16a.1 = Id(gm16));
am4a := ReplaceRelation(inm, 3, (inm.1 * inm.2)^2 = Id(inm));
am4b := ReplaceRelation(inm, (inm.1 * inm.2)^3 = Id(inm), (inm.1 * inm.2)^2 = Id(inm));
tetmP := AddGenerator(tetm);
tetmPM := DeleteGenerator(tetmP, tetmP.3);
am5iso := AddGenerator(am5, am5.1 * am5.2);

delete gf16add, gf16add2, gf16iso1, gf16iso2, af4a, af4b, tetfP, tetfPM, af5iso;
delete gm16add, gm16add2, gm16iso1, gm16iso2, am4a, am4b, tetmP, tetmPM, am5iso;

if errors eq 0 then
    print "Correctness in editing defining relations";
else
    print errors, " error(s) in editing defining relations";
end if;

/*
----------------------------------------
    Construction of extensions
----------------------------------------
*/

errors := 0;

af5xaf5 := DirectProduct(af5, af5);
am5xam5 := DirectProduct(am5, am5);

af5faf5 := FreeProduct(af5, af5);
am5xam5 := FreeProduct(am5, am5);

if errors eq 0 then
    print "Correctness in construction of extensions";
else
    print errors, " error(s) in construction of extensions";
end if;


/*
----------------------------------------
    Accessing defining Generators and Relations
----------------------------------------
*/

errors := 0;

if NumberOfGenerators(f2) ne 2 or Ngens(m2) ne 2 then
    print "ERROR: NumberOfGenerators wrong for free semigroup";
    errors := errors + 1;
end if;

if NumberOfGenerators(gf16) ne 3 or Ngens(gm16) ne 3 then
    print "ERROR: NumberOfGenerators wrong for fp semigroup";
    errors := errors + 1;
end if;

if NumberOfGenerators(f2) ne #Generators(f2) then
    print "ERROR: NumberOfGenerators not #Generators for free semigroup";
    errors := errors + 1;
end if;

if NumberOfGenerators(m2) ne #Generators(m2) then
    print "ERROR: NumberOfGenerators not #Generators for free monoid";
    errors := errors + 1;
end if;

if NumberOfGenerators(gf16) ne #Generators(gf16) then
    print "ERROR: NumberOfGenerators not #Generators for fp semigroup";
    errors := errors + 1;
end if;

if NumberOfGenerators(gm16) ne #Generators(gm16) then
    print "ERROR: NumberOfGenerators not #Generators for fp monoid";
    errors := errors + 1;
end if;

if Generators(f3) ne {f3.1, f3.2, f3.3} then
    print "ERROR: Generating set wrong for free group";
    errors := errors + 1;
end if;

if Generators(m3) ne {m3.1, m3.2, m3.3} then
    print "ERROR: Generating set wrong for free group";
    errors := errors + 1;
end if;

if Generators(ftc) ne {ftc.1, ftc.2} then
    print "ERROR: Generating set wrong for fp group";
    errors := errors + 1;
end if;

if Generators(mtc) ne {mtc.1, mtc.2} then
    print "ERROR: Generating set wrong for fp group";
    errors := errors + 1;
end if;

if #Relations(f2) ne 0 then
    print "ERROR: Number of relations wrong for free group";
    errors := errors + 1;
end if;

if Relations(f2) ne [] then
    print "ERROR: Relations wrong for free group";
    errors := errors + 1;
end if;

if #Relations(m2) ne 0 then
    print "ERROR: Number of relations wrong for free group";
    errors := errors + 1;
end if;

if Relations(m2) ne [] then
    print "ERROR: Relations wrong for free group";
    errors := errors + 1;
end if;

if #Relations(ftc) ne 3 then
    print "ERROR: Number of relations wrong for quotient";
    errors := errors + 1;
end if;

if #Relations(mtc) ne 4 then
    print "ERROR: Number of relations wrong for quotient";
    errors := errors + 1;
end if;

if Relations(af5) ne [af5.1^2 = (af5.1 * af5.2)^5, af5.2^3 = (af5.1 * af5.2)^5]
then
    print "ERROR: Wrong relations for quotient";
    errors := errors + 1;
end if;

if Relations(am5) ne [am5.1^2 = Id(am5), am5.2^3 = Id(am5),
		     (am5.1 * am5.2)^5 = Id(am5)] then
    print "ERROR: Wrong relations for quotient";
    errors := errors + 1;
end if;

if errors eq 0 then
    print "Correctness in accessing generators and relations";
else
    print errors, " error(s) in accessing generators and relations";
end if;

/*
---------------------------------------------------------------------------
    Clean up: Delete all remaining identifiers.  There should be nothing
    left after the deletions.
---------------------------------------------------------------------------
*/

delete Hf;
delete af5b;
delete am5xam5;
delete gf16a;
delete inm;
delete sf;
delete tetf;
delete tm;
delete Hm;
delete af5faf5;
delete errors;
delete gf16b;
delete m2;
delete sm;
delete tetm;
delete tm0;
delete Kf;
delete af5xaf5;
delete f2;
delete gm16;
delete m3;
delete t1f;
delete tf;
delete tm12;
delete Km;
delete am5;
delete f3;
delete gm16a;
delete mtc;
delete t1m;
delete tf12;
delete tm13;
delete af5;
delete am5a;
delete ftc;
delete gm16b;
delete rf;
delete t2f;
delete tf13;
delete tm2;
delete af5a;
delete am5b;
delete gf16;
delete inf;
delete rm;
delete t2m;
delete tf2;

//quit;
