############################################################################
##
#W  drawdclasses.gi                     Manuel Delgado <mdelgado@fc.up.pt>
#W                                      Jose Morais    <josejoao@fc.up.pt>
##
#H  @(#)$Id: drawdclasses.gi,v 0.996 $
##
#Y  Copyright (C)  2005,  CMUP, Universidade do Porto, Portugal
##
##
##--------------------------------------------------------------------------------
InstallGlobalFunction(DrawDClassOfElement, function(arg)
    local S,                          # The semigroup
          El,                         # The element whose D-class we'll draw
          dc,                         # one d-class
          cl, cl2, cl3,               # index of one certain d-class in dclasses
          el, el2,                    # one element of an H-class
          dclasses,
          len_dclasses,               # number of d-classes
          dc_number,                  # the number of the dot node of a D-class
          eggbox, eggbox2,
          idegg, idegg2,
          line, col,
          ident,
          existsPathFromAtoB,         # matrix with true in [i][j] iff
# there is there is a path of arrows
# from dclasses[i] to dclasses[j]
          phi,                        # phi[i][j] = [i', j'] where
# mat_input[i'][j'] = mat[i][j]
# where mat_input is the input matrix to
# GrahamBlocks (it will
# reference entries in eggbox!
# and mat is graham_eggbox
          box4,                       # Defined where used
          box5,                       # Defined where used
          lenb,                       # Length of box5 which is the number of
# levels in the drawing
          visited, list,              # auxiliary arrays
          greens_less_than,           # matrix with true in [i][j] iff
# dclasses[i] <= dclasses[j]
          file,                       # name of the file where dot code
# will be written
          bag, bag2,                  # matrix such that bag[i][j] = L
# where L is the list of elements of the
# H-class currently being processed
# ready for being written according to
# whether we're writing as transformations
# or words
          zero,                       # The "zero" on the transformations if partial
          is_partial,                 # whether the transformations are partial
          trans_list,				  # The list of lists of elements to be coloured
          len_trans_list,
          generators, generatorsx,
          genslen,
          colors, color,
          fich,                       # the name of the dot file given as argument
          path,
          gv, dot, tdir,
          alphabet,
          idempots,                   # Idempotents of the semigroup
          idempots2,                  # ImageListOfTransformation of idempots
          T,                          # whether we're displaying as transformations
          str, str1, str2,
          tlen,
          rows, cols, val,
          retels,
          i, j, k, k2, p, m, c, ret, px,
          powerizeWord;

# ==========================================

# if we're displaying as words this function
# replaces 2 or more followd occurrences of
# letter a by a^...
    powerizeWord := function(w)
        local w2, c, j, a;

        w2 := [];
        c := 0;
        for j in [1..Length(w)] do
            if c = 0 then
                a := w[j];
                c := 1;
            elif w[j] = a then
                c := c + 1;
            else
                if c = 1 then
                    Add(w2, a);
                else
                    w2 := Concatenation(w2, [a], "^", String(c));
                fi;
                c := 1;
                a := w[j];
            fi;
        od;
        if c = 1 then
            Add(w2, a);
        else
            w2 := Concatenation(w2, [a], "^", String(c));
        fi;
        return(w2);
    end;
##  End of powerizeWord()  --


    if not IsBound(arg[1]) or not IsSemigroup(arg[1]) then
        Error("The first argument must be a semigroup");
    else
        S := arg[1];
    fi;

    if not IsBound(arg[2]) or not IsTransformation(arg[2]) then
        Error("The second argument must be an element of semigroup");
    else
        El := arg[2];
    fi;

# alphabet for displaying as words
    alphabet := "abcdefghijklmnopqrstuvwxyz";

	colors := [ "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
  "crimson", "cyan", "darkgoldenrod", "darkkhaki", "darkorange", "darkorchid",
  "darksalmon", "darkseagreen", "darkturquoise",
  "darkviolet", "deeppink", "deepskyblue", "dodgerblue", "firebrick",
  "forestgreen", "gold", "goldenrod", "green", "greenyellow", "grey",
  "hotpink", "indianred", "khaki", "lawngreen",
  "lightblue", "lightcoral",
  "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", "lightslateblue",
  "lightslategrey", "limegreen", "magenta",
  "maroon", "mediumaquamarine", "mediumorchid", "mediumpurple", "mediumseagreen",
  "mediumspringgreen", "mediumturquoise", "mediumvioletred",
  "moccasin", "navajowhite", "olivedrab2", "orange", "orangered",
  "orchid", "palegreen", "paleturquoise", "palevioletred", "peachpuff", "peru",
  "pink", "plum", "powderblue", "purple", "red", "rosybrown", "royalblue1", "saddlebrown", "salmon",
  "sandybrown", "seagreen", "skyblue", "slateblue", "slategrey",
  "springgreen", "steelblue", "tan", "thistle", "tomato", "turquoise", "violet", "violetred",
  "wheat", "yellow", "yellowgreen" ];

# Search for a program to display .ps
    path := DirectoriesSystemPrograms();
    gv := Filename( path, "evince" );
    if gv = fail then
        gv := Filename( path, "ggv" );
    fi;
    if gv = fail then
        gv := Filename( path, "gsview32" );
    fi;
    if gv = fail then
        gv := Filename( path, "gsview64" );
    fi;
    if gv = fail then
        gv := Filename( path, "gv" );
    fi;
    if gv = fail then
        Error("Please install evince, ggv, gsview or gv");
    fi;

    dot := Filename( path, "dot" );
    if dot = fail then
        Error("Please install GraphViz (http://www.graphviz.org )");
    fi;


    T := false;                          # Display as transformations
    trans_list := [];                    # the list of lists of elements
# to draw in colors given by
# the user.
    fich := "semigroup";                 # the name of the dot file
    for i in [3..Length(arg)] do
        if IsString(arg[i]) then
            if arg[i] = "" then
                fich := "semigroup";
            else
                fich := arg[i];
            fi;
        elif IsList(arg[i]) then
            if not ForAll(arg[i], e -> IsTransformation(e)) then
                Error("The list of elements must be a list of Transformations ", arg[i]);
            fi;
            Add(trans_list, Set(List(arg[i], trans -> ImageListOfTransformation(trans))));
        elif i = Length(arg) and arg[i] = 1 then
            T := true;
        else
            Error("The arguments must be: <semigroup>, <element>, [, list of elements]* [, file name]");
        fi;
    od;
    len_trans_list := Length(trans_list);

    if not IsTransformation(AsList(S)[1]) then
        Print("I will work with an isomorphic transformation semigroup instead\n");
        S:= Range(IsomorphismTransformationSemigroup(S));
        S := Semigroup(ReduceNumberOfGenerators(GeneratorsOfSemigroup(S)));
    fi;
    tdir := DirectoryTemporary();
    if tdir = fail then
        tdir := Directory(GAPInfo.UserHome);
    fi;
    file := Filename(tdir, Concatenation(fich, ".dot"));

    idempots := Idempotents(S);
    idempots2 := List(idempots, x -> ImageListOfTransformation(x));

##  We will check if the transformations are partial or total
    generators := GeneratorsOfSemigroup(S);
    genslen := Length(generators);
    tlen := DegreeOfTransformation(generators[1]);
    generatorsx := [];
    for el in generators do
        if not el = IdentityTransformation(tlen) then
            Add(generatorsx, el);
        fi;
    od;

# Determine if transitions are partial
# and determine the "zero" digit
    is_partial := ForAll(List(generators, g -> ImageListOfTransformation(g)), lx -> lx[tlen] = tlen);
    zero := tlen;

# ============= Main Code ==================

    dclasses := [GreensDClassOfElement(S, El)];
    len_dclasses := Length(dclasses);
    dc_number := 1;

    if not T then
        retels := [Elements(dclasses[1]), SemigroupFactorization(S, Elements(dclasses[1])),[]];
    fi;

# write the preambule of the dot file
    PrintTo(file, "digraph  sgp {\ngraph [center=yes,ordering=out];\nnode [shape=box];\nedge [color=cornflowerblue,arrowhead=none];\n");

# For each d-class write the dot record node
    for dc in dclasses do
        eggbox := EggBoxOfDClass(dc);
        rows := Length(eggbox);
        cols := Length(eggbox[1]);

        idegg := List(eggbox, r->List(r,
                                   function(h)
                if IsGroupHClass(h) then
                    return 1;
                else
                    return 0;
                fi;
            end));


##  Order the columns lexicographically
        line := List([1..cols], x -> [Representative(eggbox[1][x]), x]);
        Sort(line);
        eggbox2 := NullMat(rows, cols);
        idegg2 := NullMat(rows, cols);

        j := 1;
        for el in line do
            for i in [1..rows] do
                eggbox2[i][j] := ShallowCopy(eggbox[i][el[2]]);
                idegg2[i][j] := ShallowCopy(idegg[i][el[2]]);
            od;
            j := j + 1;
        od;

##  Order the rows lexicographically
        col := List([1..rows], x -> [Representative(eggbox[x][1]), x]);
        Sort(col);
        eggbox := NullMat(rows, cols);
        idegg := NullMat(rows, cols);
        i := 1;
        for el in col do
            for j in [1..cols] do
                eggbox[i][j] := ShallowCopy(eggbox2[el[2]][j]);
                idegg[i][j] := ShallowCopy(idegg2[el[2]][j]);
            od;
            i := i + 1;
        od;


        if IsRegularDClass(dc) then
            ret := GrahamBlocks(idegg);
#            graham_eggbox := ret[1];
            phi := ret[2];
        fi;

        bag := [];
        bag2 := [];
        for k in [1..rows] do
            bag[k] := [];
            bag2[k] := [];
            for p in [1..cols] do
                bag2[k][p] := [];
            od;
        od;


# Write the dot node definition of the current
# D-class
        AppendTo(file, String(dc_number), " [label=<\n<TABLE BORDER=\"0\" CELLBORDER=\"0\">\n");  # opens the D-class and
# first column for writing

# Visit left column first, then second,... because of dot
        for i in [1..rows] do
        	AppendTo(file, "<TR>");
            for j in [1..cols] do
# Write H-class [i][j] from current eggbox
# With the list of
# elements of an H-class writes the
# corresponding cell of the dot record node
# in the current D-class node
                AppendTo(file, "<TD BORDER=\"0\">");  # opens h-class

# bag2[i][j] is the list of elements
# of eggbox[i'][j'] where i' = phi[i][j][1]
# and j' = phi[i][j][2], formatted for being written
# phi is needed because GrahamBlocks was called.
                if IsRegularDClass(dc) then
# If we're displaying as transformations
                    if T then
                        bag[i][j] := List(Elements(eggbox[phi[i][j][1]][phi[i][j][2]]), x -> ImageListOfTransformation(x));
# if the transformations are partial
                        if is_partial then
                            for el in bag[i][j] do
# replace the "zero" by "_"
                                list := [];
                                for p in [1..zero-1] do
                                    if el[p] = zero then
                                        Add(list, "_");
                                    elif el[p] > zero then
                                        Add(list, el[p]-1);
                                    else
                                        Add(list, el[p]);
                                    fi;
                                od;
                                for p in [zero+1..tlen] do
                                    if el[p] = zero then
                                        Add(list, "_");
                                    elif el[p] > zero then
                                        Add(list, el[p]-1);
                                    else
                                        Add(list, el[p]);
                                    fi;
                                od;
                                if list = [] then
                                    Add(list, "_");
                                fi;
# End of replace the "zero" by "_"

# str2 will be the written string
                                str := String(list);
# if it's an idempot put the "*"
                                if el in idempots2 then
                                    str2 := ['*'];
                                else
                                    str2 := [];
                                fi;
# Remove '"'
                                for c in str do
                                    if not c = '"' then
                                       Add(str2, c);
                                    fi;
                                od;
# add element for write
                                if str2[6] = '.' then
                                    ident := List([1..tlen], x -> x);
                                    if str2[1] = '*' then
                                        str2 := Concatenation("*", String(ident));
                                    else
                                        str2 := String(ident);
                                    fi;
                                fi;
                                Add(bag2[i][j], str2);
                            od;
# If the transformations are total
                        else
# Add '*' if it's an idempotent
                            for el in bag[i][j] do
                                if el in idempots2 then
                                    if String(el)[6] = '.' then
                                        Add(bag2[i][j], Concatenation("*", String(List([1..tlen], x -> x))));
                                    else
                                        Add(bag2[i][j], Concatenation("*", String(el)));
                                    fi;
                                else
                                    Add(bag2[i][j], String(el));
                                fi;
                            od;
                        fi;
# If we're displaying as words
                    else
                        bag[i][j] := Elements(eggbox[phi[i][j][1]][phi[i][j][2]]);
                        for el in bag[i][j] do
                            if el = MultiplicativeNeutralElement(S) then
                                str1 := "1";
                                retels[3][Position(retels[1], el)] := "1";
                            elif el = MultiplicativeZero(S) then
                                str1 := "0";
                                retels[3][Position(retels[1], el)] := "0";
                            else
                                px := Position(retels[1], el);
                                ret := retels[2][px];
                                str1 := [];
                                if genslen > 26 then
                                    for el2 in ret do
                                        str1 := Concatenation(str1, "a", String(Position(generatorsx, el2)));
                                    od;
                                else
                                    for el2 in ret do
                                        Add(str1, alphabet[Position(generatorsx, el2)]);
                                    od;
                                fi;
                                str1 := powerizeWord(str1);
                                retels[3][px] := str1;
                            fi;
                            if el in idempots then
                                Add(bag2[i][j], Concatenation("*", str1));
                            else
                                Add(bag2[i][j], str1);
                            fi;
                        od;
                    	bag[i][j] := List(bag[i][j], x -> ImageListOfTransformation(x));
					fi;
# if it is not a regular d-class
                else
# If we're displaying as transformations
                    if T then
# if it is not a regular d-class it has not been sorted by GrahamBlocks,
# so pick the elements from eggbox in same (not mapped by phi) order
                        bag[i][j] := List(Elements(eggbox[i][j]), x -> ImageListOfTransformation(x));
                        if is_partial then
                            for el in bag[i][j] do
                                list := [];
                                for p in [1..zero-1] do
                                    if el[p] = zero then
                                        Add(list, "_");
                                    elif el[p] > zero then
                                        Add(list, el[p]-1);
                                    else
                                        Add(list, el[p]);
                                    fi;
                                od;
                                for p in [zero+1..tlen] do
                                    if el[p] = zero then
                                        Add(list, "_");
                                    elif el[p] > zero then
                                        Add(list, el[p]-1);
                                    else
                                        Add(list, el[p]);
                                    fi;
                                od;
                                str := String(list);
                                if el in idempots2 then
                                    str2 := ['*'];
                                else
                                    str2 := [];
                                fi;
                                for c in str do
                                    if not c = '"' then
                                       Add(str2, c);
                                    fi;
                                od;
                                Add(bag2[i][j], str2);
                            od;
# If the transformations are total
                        else
# Add '*' if it's an idempotent
                            for el in bag[i][j] do
                                if el in idempots2 then
                                    Add(bag2[i][j], Concatenation("*", String(el)));
                                else
                                    Add(bag2[i][j], String(el));
                                fi;
                            od;
                        fi;
# If we're displaying as words
                    else
                        bag[i][j] := Elements(eggbox[i][j]);
                        for el in bag[i][j] do
                            if el = MultiplicativeNeutralElement(S) then
                                str1 := "1";
                                retels[3][Position(retels[1], el)] := "1";
                            elif el = MultiplicativeZero(S) then
                                str1 := "0";
                                retels[3][Position(retels[1], el)] := "0";
                            else
                                px := Position(retels[1], el);
                                ret := retels[2][px];
                                str1 := [];
                                if genslen > 26 then
                                    for el2 in ret do
                                        str1 := Concatenation(str1, "a", String(Position(generatorsx, el2)));
                                    od;
                                else
                                    for el2 in ret do
                                        Add(str1, alphabet[Position(generatorsx, el2)]);
                                    od;
                                fi;
                                str1 := powerizeWord(str1);
                                retels[3][px] := str1;
                            fi;
                            if el in idempots then
                                Add(bag2[i][j], Concatenation("*", str1));
                            else
                                Add(bag2[i][j], str1);
                            fi;
                        od;
                    	bag[i][j] := List(bag[i][j], x -> ImageListOfTransformation(x));
                    fi;
                fi;

# write the elements of current H-class
				AppendTo(file, "<TABLE>");
				for k in [1..Length(bag2[i][j])] do
					color := "white";
					el := bag[i][j][k];
					k2 := len_trans_list;
					while k2 > 0 do
						if el in trans_list[k2] then
							color := colors[k2];
							break;
						fi;
						k2 := k2 - 1;
					od;
                	AppendTo(file, "<TR><TD BGCOLOR=\"", color, "\" BORDER=\"0\">");
                    AppendTo(file, bag2[i][j][k]);
                    AppendTo(file, "</TD></TR>\n");
                od;
                AppendTo(file, "</TABLE>");

                AppendTo(file, "</TD>");	# close H-class

            od;
            AppendTo(file, "</TR>\n");  # closes current row for writing
# Current H-class written
        od;
# Current D-class written

# close current node (D-class)
        AppendTo(file, "</TABLE>>];\n");

# Next D-class will have next number
        dc_number := dc_number + 1;
    od;

# ===== write the arrows =====

    greens_less_than := [];
    for i in [1..len_dclasses] do
        greens_less_than[i] := [];
        greens_less_than[i][i] := false;
    od;

    box4 := [];  # This will be a list of lists
# for poi3 will be
# [ [ 3 ], [ 1, 3 ], [  ], [ 1, 2, 3 ] ],
# meaning that dclasses[1] is only below dclasses[3],
# dclasses[2] is only below dclasses[1] and dclasses[3],...
    for i in [1..len_dclasses] do
        box4[i] := [];
        list := [1..len_dclasses];
        RemoveSet(list, i);
        for j in list do
            if IsGreensLessThanOrEqual(dclasses[i], dclasses[j]) then
                Add(box4[i], j);
                greens_less_than[i][j] := true;
            else
                greens_less_than[i][j] := false;
            fi;
        od;
    od;
    visited := [];
    for i in [1..len_dclasses] do
        visited[i] := false;
    od;
    val := [];
    box5 := [];  # This variable will be a list of lists
# of integers (indexes into dclasses) where
# box5 = [ [ 1 ], [ 2, 3 ], [ 4, 5 ], [ 6 ] ]
# means (from top to bottom) first row has dclasses[1],
# second row has dclasses[2] and dclasses[3],...
    j := 1;
    while ForAny(visited, v -> v = false) do
        box5[j] := [];
        for i in [1..len_dclasses] do
# If for all d-classes above dclasses[i]
#
            if ForAll(box4[i], a -> a in val) then
                Add(box5[j], i);
                visited[i] := true;
            fi;
        od;
        val := box5[j];
        j := j + 1;
    od;
    lenb := j - 1;  # Length of box5
    list := ShallowCopy(box5[1]);
    for i in [2..lenb] do
        SubtractSet(box5[i], list);
        UniteSet(list, ShallowCopy(box5[i]));
    od;
# box5 has been computed.


# Write arrows
    existsPathFromAtoB := List([1..len_dclasses], x -> List([1..len_dclasses], y -> false));
    for i in [2..lenb] do
        for cl in box5[i] do
            j := i - 1;
            while j > 0 do
                for cl2 in box5[j] do
                    if greens_less_than[cl][cl2] and
                       not greens_less_than[cl2][cl] and
                       not existsPathFromAtoB[cl2][cl] then
# Draw arrow from node of dclasses[cl2] to node of
# dclasses[cl] where map[i] is the number of dot's node
# of d-class dclasses[i]
                        AppendTo(file, cl2, " -> ", cl, ";\n");

                        existsPathFromAtoB[cl2][cl] := true;
                        m := j - 1;
                        while m > 0 do
                            for cl3 in box5[m] do
                                if existsPathFromAtoB[cl3][cl2] then
                                    existsPathFromAtoB[cl3][cl] := true;
                                fi;
                            od;
                            m := m - 1;
                        od;
                    fi;
                od;
                j := j - 1;
            od;
        od;
    od;
# Arrows written

# Close the dot file
    AppendTo(file, "}\n");

# ================= Exec dot and display file =============
    Process(tdir, dot, InputTextUser(), OutputTextUser(), ["-Tps", "-o", Concatenation(fich, ".dot.ps"), Concatenation(fich, ".dot")]);
    Print(Concatenation("Displaying file: ", file, ".ps\n"));
    if ARCH_IS_UNIX( ) then
        Exec(Concatenation("cd ", Filename(tdir, ""), "; ", gv, Concatenation(" ", Concatenation(fich, ".dot"), ".ps 2>/dev/null 1>/dev/null &")));
    elif ARCH_IS_WINDOWS( ) then
        Process(tdir, gv, InputTextUser(), OutputTextUser(), [Concatenation(Concatenation(fich, ".dot"), ".ps")]);
    else

    fi;
end);

##--------------------------------------------------------------------------------
##--------------------------------------------------------------------------------

InstallGlobalFunction(DrawDClasses, function(arg)
	local S,                          # The semigroup
          dc,                         # one d-class
          cl, cl2, cl3,               # index of one certain d-class in dclasses
          el, el2,                    # one element of an H-class
          dclasses,
          len_dclasses,               # number of d-classes
          dc_number,                  # the number of the dot node of a D-class
          eggbox, eggbox2,
          idegg, idegg2,
          line, col,
          ident,
          existsPathFromAtoB,         # matrix with true in [i][j] iff
# there is there is a path of arrows
# from dclasses[i] to dclasses[j]
          phi,                        # phi[i][j] = [i', j'] where
# mat_input[i'][j'] = mat[i][j]
# where mat_input is the input matrix to
# GrahamBlocks (it will
# reference entries in eggbox!
# and mat is graham_eggbox
          box4,                       # Defined where used
          box5,                       # Defined where used
          lenb,                       # Length of box5 which is the number of
# levels in the drawing
          visited, list,              # auxiliary arrays
          greens_less_than,           # matrix with true in [i][j] iff
# dclasses[i] <= dclasses[j]
          file,                       # name of the file where dot code
# will be written
          bag, bag2,                  # matrix such that bag[i][j] = L
# where L is the list of elements of the
# H-class currently being processed
# ready for being written according to
# whether we're writing as transformations
# or words
          zero,                       # The "zero" on the transformations if partial
          is_partial,                 # whether the transformations are partial
          trans_list,				  # The list of lists of elements to be coloured
          len_trans_list,
          generators, generatorsx,
          genslen,
          colors, color,
          fich,                       # the name of the dot file given as argument
          path,
          gv, dot, tdir,
          alphabet,
          idempots,                   # Idempotents of the semigroup
          idempots2,                  # ImageListOfTransformation of idempots
          T,                          # whether we're displaying as transformations
          str, str1, str2,
          tlen,
          rows, cols, val,
          retels,
          i, j, k, k2, p, m, c, ret, px,
          powerizeWord;

# ==========================================

# if we're displaying as words this function
# replaces 2 or more followed occurrences of
# letter a by a^...
    powerizeWord := function(w)
        local w2, c, j, a;

        w2 := [];
        c := 0;
        for j in [1..Length(w)] do
            if c = 0 then
                a := w[j];
                c := 1;
            elif w[j] = a then
                c := c + 1;
            else
                if c = 1 then
                    Add(w2, a);
                else
                    w2 := Concatenation(w2, [a], "^", String(c));
                fi;
                c := 1;
                a := w[j];
            fi;
        od;
        if c = 1 then
            Add(w2, a);
        else
            w2 := Concatenation(w2, [a], "^", String(c));
        fi;
        return(w2);
    end;
##  End of powerizeWord()  --


    if not IsBound(arg[1]) or not IsSemigroup(arg[1]) then
        Error("The first argument must be a semigroup");
    else
        S := arg[1];
    fi;

# alphabet for displaying as words
    alphabet := "abcdefghijklmnopqrstuvwxyz";

	colors := [ "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
  "crimson", "cyan", "darkgoldenrod", "darkkhaki", "darkorange", "darkorchid",
  "darksalmon", "darkseagreen", "darkturquoise",
  "darkviolet", "deeppink", "deepskyblue", "dodgerblue", "firebrick",
  "forestgreen", "gold", "goldenrod", "green", "greenyellow", "grey",
  "hotpink", "indianred", "khaki", "lawngreen",
  "lightblue", "lightcoral",
  "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", "lightslateblue",
  "lightslategrey", "limegreen", "magenta",
  "maroon", "mediumaquamarine", "mediumorchid", "mediumpurple", "mediumseagreen",
  "mediumspringgreen", "mediumturquoise", "mediumvioletred",
  "moccasin", "navajowhite", "olivedrab2", "orange", "orangered",
  "orchid", "palegreen", "paleturquoise", "palevioletred", "peachpuff", "peru",
  "pink", "plum", "powderblue", "purple", "red", "rosybrown", "royalblue1", "saddlebrown", "salmon",
  "sandybrown", "seagreen", "skyblue", "slateblue", "slategrey",
  "springgreen", "steelblue", "tan", "thistle", "tomato", "turquoise", "violet", "violetred",
  "wheat", "yellow", "yellowgreen" ];

# Search for a program to display .ps
    path := DirectoriesSystemPrograms();
    gv := Filename( path, "evince" );
    if gv = fail then
        gv := Filename( path, "ggv" );
    fi;
    if gv = fail then
        gv := Filename( path, "gsview32" );
    fi;
    if gv = fail then
        gv := Filename( path, "gsview64" );
    fi;
    if gv = fail then
        gv := Filename( path, "gv" );
    fi;
    if gv = fail then
        Error("Please install evince, ggv, gsview or gv");
    fi;

    dot := Filename( path, "dot" );
    if dot = fail then
        Error("Please install GraphViz (http://www.graphviz.org )");
    fi;


    T := false;                          # Display as transformations
    trans_list := [];                    # the list of lists of elements
# to draw in colors given by
# the user.
    fich := "semigroup";                 # the name of the dot file
    for i in [2..Length(arg)] do
        if IsString(arg[i]) then
            if arg[i] = "" then
                fich := "semigroup";
            else
                fich := arg[i];
            fi;
        elif IsList(arg[i]) then
            if not ForAll(arg[i], e -> IsTransformation(e)) then
                Error("The list of elements must be a list of Transformations ", arg[i]);
            fi;
            Add(trans_list, Set(List(arg[i], trans -> ImageListOfTransformation(trans))));
        elif i = Length(arg) and arg[i] = 1 then
            T := true;
        else
            Error("The arguments must be: <semigroup> [, list of elements]* [, file name]");
        fi;
    od;
    len_trans_list := Length(trans_list);

    if not IsTransformation(AsList(S)[1]) then
        Print("I will work with an isomorphic transformation semigroup instead\n");
        S:= Range(IsomorphismTransformationSemigroup(S));
        S := Semigroup(ReduceNumberOfGenerators(GeneratorsOfSemigroup(S)));
    fi;
    tdir := DirectoryTemporary();
    if tdir = fail then
        tdir := Directory(GAPInfo.UserHome);
    fi;
    file := Filename(tdir, Concatenation(fich, ".dot"));

    idempots := Idempotents(S);
    idempots2 := List(idempots, x -> ImageListOfTransformation(x));

##  We will check if the transformations are partial or total
    generators := GeneratorsOfSemigroup(S);
    genslen := Length(generators);
    tlen := DegreeOfTransformation(generators[1]);
    generatorsx := [];
    for el in generators do
        if not el = IdentityTransformation(tlen) then
            Add(generatorsx, el);
        fi;
    od;

# Determine if transitions are partial
# and determine the "zero" digit
    is_partial := ForAll(List(generators, g -> ImageListOfTransformation(g)), lx -> lx[tlen] = tlen);
    zero := tlen;

# ============= Main Code ==================

    dclasses := GreensDClasses(S);
    len_dclasses := Length(dclasses);
    dc_number := 1;

    if not T then
        retels := [Elements(S), SemigroupFactorization(S, Elements(S)),[]];
    fi;

# write the preambule of the dot file
    PrintTo(file, "digraph  sgp {\ngraph [center=yes,ordering=out];\nnode [shape=box];\nedge [color=cornflowerblue,arrowhead=none];\n");

# For each d-class write the dot record node
    for dc in dclasses do
        eggbox := EggBoxOfDClass(dc);
        rows := Length(eggbox);
        cols := Length(eggbox[1]);

        idegg := List(eggbox, r->List(r,
                                   function(h)
                if IsGroupHClass(h) then
                    return 1;
                else
                    return 0;
                fi;
            end));


##  Order the columns lexicographically
        line := List([1..cols], x -> [Representative(eggbox[1][x]), x]);
        Sort(line);
        eggbox2 := NullMat(rows, cols);
        idegg2 := NullMat(rows, cols);

        j := 1;
        for el in line do
            for i in [1..rows] do
                eggbox2[i][j] := ShallowCopy(eggbox[i][el[2]]);
                idegg2[i][j] := ShallowCopy(idegg[i][el[2]]);
            od;
            j := j + 1;
        od;

##  Order the rows lexicographically
        col := List([1..rows], x -> [Representative(eggbox[x][1]), x]);
        Sort(col);
        eggbox := NullMat(rows, cols);
        idegg := NullMat(rows, cols);
        i := 1;
        for el in col do
            for j in [1..cols] do
                eggbox[i][j] := ShallowCopy(eggbox2[el[2]][j]);
                idegg[i][j] := ShallowCopy(idegg2[el[2]][j]);
            od;
            i := i + 1;
        od;


        if IsRegularDClass(dc) then
            ret := GrahamBlocks(idegg);
#            graham_eggbox := ret[1];
            phi := ret[2];
        fi;

        bag := [];
        bag2 := [];
        for k in [1..rows] do
            bag[k] := [];
            bag2[k] := [];
            for p in [1..cols] do
                bag2[k][p] := [];
            od;
        od;


# Write the dot node definition of the current
# D-class
        AppendTo(file, String(dc_number), " [label=<\n<TABLE BORDER=\"0\" CELLBORDER=\"0\">\n");  # opens the D-class and
# first column for writing

# Visit left column first, then second,... because of dot
        for i in [1..rows] do
        	AppendTo(file, "<TR>");
            for j in [1..cols] do
# Write H-class [i][j] from current eggbox
# With the list of
# elements of an H-class writes the
# corresponding cell of the dot record node
# in the current D-class node
                AppendTo(file, "<TD BORDER=\"0\">");  # opens h-class

# bag2[i][j] is the list of elements
# of eggbox[i'][j'] where i' = phi[i][j][1]
# and j' = phi[i][j][2], formatted for being written
# phi is needed because GrahamBlocks was called.
                if IsRegularDClass(dc) then
# If we're displaying as transformations
                    if T then
                        bag[i][j] := List(Elements(eggbox[phi[i][j][1]][phi[i][j][2]]), x -> ImageListOfTransformation(x));
# if the transformations are partial
                        if is_partial then
                            for el in bag[i][j] do
# replace the "zero" by "_"
                                list := [];
                                for p in [1..zero-1] do
                                    if el[p] = zero then
                                        Add(list, "_");
                                    elif el[p] > zero then
                                        Add(list, el[p]-1);
                                    else
                                        Add(list, el[p]);
                                    fi;
                                od;
                                for p in [zero+1..tlen] do
                                    if el[p] = zero then
                                        Add(list, "_");
                                    elif el[p] > zero then
                                        Add(list, el[p]-1);
                                    else
                                        Add(list, el[p]);
                                    fi;
                                od;
                                if list = [] then
                                    Add(list, "_");
                                fi;
# End of replace the "zero" by "_"

# str2 will be the written string
                                str := String(list);
# if it's an idempot put the "*"
                                if el in idempots2 then
                                    str2 := ['*'];
                                else
                                    str2 := [];
                                fi;
# Remove '"'
                                for c in str do
                                    if not c = '"' then
                                       Add(str2, c);
                                    fi;
                                od;
# add element for write
                                if str2[6] = '.' then
                                    ident := List([1..tlen], x -> x);
                                    if str2[1] = '*' then
                                        str2 := Concatenation("*", String(ident));
                                    else
                                        str2 := String(ident);
                                    fi;
                                fi;
                                Add(bag2[i][j], str2);
                            od;
# If the transformations are total
                        else
# Add '*' if it's an idempotent
                            for el in bag[i][j] do
                                if el in idempots2 then
                                    if String(el)[6] = '.' then
                                        Add(bag2[i][j], Concatenation("*", String(List([1..tlen], x -> x))));
                                    else
                                        Add(bag2[i][j], Concatenation("*", String(el)));
                                    fi;
                                else
                                    Add(bag2[i][j], String(el));
                                fi;
                            od;
                        fi;
# If we're displaying as words
                    else
                        bag[i][j] := Elements(eggbox[phi[i][j][1]][phi[i][j][2]]);
                        for el in bag[i][j] do
                            if el = MultiplicativeNeutralElement(S) then
                                str1 := "1";
                                retels[3][Position(retels[1], el)] := "1";
                            elif el = MultiplicativeZero(S) then
                                str1 := "0";
                                retels[3][Position(retels[1], el)] := "0";
                            else
                                px := Position(retels[1], el);
                                ret := retels[2][px];
                                str1 := [];
                                if genslen > 26 then
                                    for el2 in ret do
                                        str1 := Concatenation(str1, "a", String(Position(generatorsx, el2)));
                                    od;
                                else
                                    for el2 in ret do
                                        Add(str1, alphabet[Position(generatorsx, el2)]);
                                    od;
                                fi;
                                str1 := powerizeWord(str1);
                                retels[3][px] := str1;
                            fi;
                            if el in idempots then
                                Add(bag2[i][j], Concatenation("*", str1));
                            else
                                Add(bag2[i][j], str1);
                            fi;
                        od;
                    	bag[i][j] := List(bag[i][j], x -> ImageListOfTransformation(x));
					fi;
# if it is not a regular d-class
                else
# If we're displaying as transformations
                    if T then
# if it is not a regular d-class it has not been sorted by GrahamBlocks,
# so pick the elements from eggbox in same (not mapped by phi) order
                        bag[i][j] := List(Elements(eggbox[i][j]), x -> ImageListOfTransformation(x));
                        if is_partial then
                            for el in bag[i][j] do
                                list := [];
                                for p in [1..zero-1] do
                                    if el[p] = zero then
                                        Add(list, "_");
                                    elif el[p] > zero then
                                        Add(list, el[p]-1);
                                    else
                                        Add(list, el[p]);
                                    fi;
                                od;
                                for p in [zero+1..tlen] do
                                    if el[p] = zero then
                                        Add(list, "_");
                                    elif el[p] > zero then
                                        Add(list, el[p]-1);
                                    else
                                        Add(list, el[p]);
                                    fi;
                                od;
                                str := String(list);
                                if el in idempots2 then
                                    str2 := ['*'];
                                else
                                    str2 := [];
                                fi;
                                for c in str do
                                    if not c = '"' then
                                       Add(str2, c);
                                    fi;
                                od;
                                Add(bag2[i][j], str2);
                            od;
# If the transformations are total
                        else
# Add '*' if it's an idempotent
                            for el in bag[i][j] do
                                if el in idempots2 then
                                    Add(bag2[i][j], Concatenation("*", String(el)));
                                else
                                    Add(bag2[i][j], String(el));
                                fi;
                            od;
                        fi;
# If we're displaying as words
                    else
                        bag[i][j] := Elements(eggbox[i][j]);
                        for el in bag[i][j] do
                            if el = MultiplicativeNeutralElement(S) then
                                str1 := "1";
                                retels[3][Position(retels[1], el)] := "1";
                            elif el = MultiplicativeZero(S) then
                                str1 := "0";
                                retels[3][Position(retels[1], el)] := "0";
                            else
                                px := Position(retels[1], el);
                                ret := retels[2][px];
                                str1 := [];
                                if genslen > 26 then
                                    for el2 in ret do
                                        str1 := Concatenation(str1, "a", String(Position(generatorsx, el2)));
                                    od;
                                else
                                    for el2 in ret do
                                        Add(str1, alphabet[Position(generatorsx, el2)]);
                                    od;
                                fi;
                                str1 := powerizeWord(str1);
                                retels[3][px] := str1;
                            fi;
                            if el in idempots then
                                Add(bag2[i][j], Concatenation("*", str1));
                            else
                                Add(bag2[i][j], str1);
                            fi;
                        od;
                    	bag[i][j] := List(bag[i][j], x -> ImageListOfTransformation(x));
                    fi;
                fi;

# write the elements of current H-class
				AppendTo(file, "<TABLE>");
				for k in [1..Length(bag2[i][j])] do
					color := "white";
					el := bag[i][j][k];
					k2 := len_trans_list;
					while k2 > 0 do
						if el in trans_list[k2] then
							color := colors[k2];
							break;
						fi;
						k2 := k2 - 1;
					od;
                	AppendTo(file, "<TR><TD BGCOLOR=\"", color, "\" BORDER=\"0\">");
                    AppendTo(file, bag2[i][j][k]);
                    AppendTo(file, "</TD></TR>\n");
                od;
                AppendTo(file, "</TABLE>");

                AppendTo(file, "</TD>");	# close H-class

            od;
            AppendTo(file, "</TR>\n");  # closes current row for writing
# Current H-class written
        od;
# Current D-class written

# close current node (D-class)
        AppendTo(file, "</TABLE>>];\n");

# Next D-class will have next number
        dc_number := dc_number + 1;
    od;

# ===== write the arrows =====

    greens_less_than := [];
    for i in [1..len_dclasses] do
        greens_less_than[i] := [];
        greens_less_than[i][i] := false;
    od;

    box4 := [];  # This will be a list of lists
# for poi3 will be
# [ [ 3 ], [ 1, 3 ], [  ], [ 1, 2, 3 ] ],
# meaning that dclasses[1] is only below dclasses[3],
# dclasses[2] is only below dclasses[1] and dclasses[3],...
    for i in [1..len_dclasses] do
        box4[i] := [];
        list := [1..len_dclasses];
        RemoveSet(list, i);
        for j in list do
            if IsGreensLessThanOrEqual(dclasses[i], dclasses[j]) then
                Add(box4[i], j);
                greens_less_than[i][j] := true;
            else
                greens_less_than[i][j] := false;
            fi;
        od;
    od;
    visited := [];
    for i in [1..len_dclasses] do
        visited[i] := false;
    od;
    val := [];
    box5 := [];  # This variable will be a list of lists
# of integers (indexes into dclasses) where
# box5 = [ [ 1 ], [ 2, 3 ], [ 4, 5 ], [ 6 ] ]
# means (from top to bottom) first row has dclasses[1],
# second row has dclasses[2] and dclasses[3],...
    j := 1;
    while ForAny(visited, v -> v = false) do
        box5[j] := [];
        for i in [1..len_dclasses] do
# If for all d-classes above dclasses[i]
#
            if ForAll(box4[i], a -> a in val) then
                Add(box5[j], i);
                visited[i] := true;
            fi;
        od;
        val := box5[j];
        j := j + 1;
    od;
    lenb := j - 1;  # Length of box5
    list := ShallowCopy(box5[1]);
    for i in [2..lenb] do
        SubtractSet(box5[i], list);
        UniteSet(list, ShallowCopy(box5[i]));
    od;
# box5 has been computed.


# Write arrows
    existsPathFromAtoB := List([1..len_dclasses], x -> List([1..len_dclasses], y -> false));
    for i in [2..lenb] do
        for cl in box5[i] do
            j := i - 1;
            while j > 0 do
                for cl2 in box5[j] do
                    if greens_less_than[cl][cl2] and
                       not greens_less_than[cl2][cl] and
                       not existsPathFromAtoB[cl2][cl] then
# Draw arrow from node of dclasses[cl2] to node of
# dclasses[cl] where map[i] is the number of dot's node
# of d-class dclasses[i]
                        AppendTo(file, cl2, " -> ", cl, ";\n");

                        existsPathFromAtoB[cl2][cl] := true;
                        m := j - 1;
                        while m > 0 do
                            for cl3 in box5[m] do
                                if existsPathFromAtoB[cl3][cl2] then
                                    existsPathFromAtoB[cl3][cl] := true;
                                fi;
                            od;
                            m := m - 1;
                        od;
                    fi;
                od;
                j := j - 1;
            od;
        od;
    od;
# Arrows written

# Close the dot file
    AppendTo(file, "}\n");

# ================= Exec dot and display file =============
    Process(tdir, dot, InputTextUser(), OutputTextUser(), ["-Tps", "-o", Concatenation(fich, ".dot.ps"), Concatenation(fich, ".dot")]);
    Print(Concatenation("Displaying file: ", file, ".ps\n"));
    if ARCH_IS_UNIX( ) then
        Exec(Concatenation("cd ", Filename(tdir, ""), "; ", gv, Concatenation(" ", Concatenation(fich, ".dot"), ".ps 2>/dev/null 1>/dev/null &")));
    elif ARCH_IS_WINDOWS( ) then
        Process(tdir, gv, InputTextUser(), OutputTextUser(), [Concatenation(Concatenation(fich, ".dot"), ".ps")]);
    else

    fi;
end);
