############################################################################
##
#W  basics.gi                       Manuel Delgado <mdelgado@fc.up.pt>
#W                                    Jose Morais    <josejoao@fc.up.pt>
##
#H  @(#)$Id: basics.gi,v 0.996 $
##
#Y  Copyright (C)  2005,  CMUP, Universidade do Porto, Portugal
##
##
###########################################################################
##
##
###########################################################################
##
#M HasCommutingIdempotents(M)
##
## Tests whether the idempotents of a semigroup <M> commute
##
InstallMethod(HasCommutingIdempotents, "for finite semigroups", true,
                 [IsSemigroup], 0, 
    function(M)
    local e1, e2, E;
    E := Idempotents(M);
    for e1 in E do
        for e2 in E do
            if e1*e2 <> e2*e1 then
                Info(InfoSgpViz,2, e1, " and ", e2, " don't commute");
                return false;
            fi;
        od;
    od;
    return true;
end);


#####################################33###################################
##
#M IsInverseSemigroup(S)
##
## Tests whether a finite semigroup is inverse
##
InstallMethod(IsInverseSemigroup, "for finite semigroups", true,
              [IsSemigroup], 0,
function(S)
    return HasCommutingIdempotents(S) and IsRegularSemigroup(S);
end);

#############################################################################
##
#F PartialTransformation(L)
##
## A partial transformation is a partial function of a set of integers of 
## the form  {1,..., n}. It is given by means of the list of images. When 
## an element has no image, we write 0. 
##
## Returns a transformation on a set with one more element that acts like 
## a zero
##
InstallGlobalFunction(PartialTransformation, function(L)    
    local i, n, K;
    n := Length(L);
    #check that it represents a partial transformation.
       
    if ForAny([1..n], i-> (IsBound(L[i]) and not L[i] in [0..n])) then
        Error ("<L> does not describe a partial transformation");
    fi;
    
    K := ShallowCopy(L);
    for i in [1..n] do
        if K[i] = 0 then
            K[i] := n+1;
        fi;
    od;
    K[n+1] := n+1;
    return TransformationNC(K);
end);

############################################################################
##
#F RightCayleyGraphAsAutomaton
##
## Computes the right Cayley graph of a finite monoid or semigroup. It uses the GAP 
## buit-in function CayleyGraphSemigroup to compute the Cayley Graph 
## and returns it as an automaton without initial and final states.
##
##
InstallGlobalFunction(RightCayleyGraphAsAutomaton, function(M)
    local i, size, n, cg, one, table, tr, p;
    
    if not IsSemigroup(M) then
        Error("<M> must be a semigroup");
    fi;
    if IsFpSemigroup(M) or IsFpMonoid(M) then
        cg := CayleyGraphSemigroup(Range(IsomorphismTransformationSemigroup(M)));
    else
        cg := CayleyGraphSemigroup(M);
    fi;
    
    tr := ShallowCopy(TransposedMat(cg));
    
    size := Size(M);
    
    if First(tr, i-> i = [1..size]) <> fail then
         Unbind(tr[Position(tr,First(tr, i-> i = [1..size]))]);
         table := Compacted(tr);
    else
        table := tr;
    fi;
    
    if MultiplicativeNeutralElement(M) in GeneratorsOfSemigroup(M) then
        n := Length(GeneratorsOfSemigroup(M))-1;
    else
        n := Length(GeneratorsOfSemigroup(M));
    fi;
  
        
    return Automaton("det", Size(M), n, table, [], []);
end);

###########################################################################
##
#F DrawRightCayleyGraph
##
## Drawss the right Cayley graph of a finite monoid or semigroup.
##
InstallGlobalFunction(DrawRightCayleyGraph, function(M)
    DrawAutomaton(RightCayleyGraphAsAutomaton(M));
end);


InstallMethod(SmallGeneratingSetForSemigroups,"generators subset",true,[IsSemigroup],
        function (S)
    local Dif, High, i, n, rk, SGS, U, V, US, T;

    SGS := ShallowCopy(GeneratorsOfSemigroup(S));
    n := Length(SGS);
    #
    # For some transformation semigroups the following coincides with the Jorder
    if IsTransformationSemigroup(S) then
        Sort(SGS, function(u,v) return
          RankOfTransformation(u)>RankOfTransformation(v);end);
      else
          Sort(SGS, function(u,v) return
            IsGreensLessThanOrEqual(GreensJClassOfElement(S,v),GreensJClassOfElement(S,u));end);
        fi;

        rk := Length(SGS);
        if 5*LogInt(rk,2) < rk/2 then
            High := SGS{[1..5*LogInt(rk,2)+1]};
            US := Semigroup(High);
            i := 1;
            while i < Length(High)  do
                U:=Subsemigroup(US,High{Difference([1..Length(High)],[i])});
                if Size(U)<Size(US) then
                    i:=i+1;
                else
                    High:=GeneratorsOfSemigroup(U);
                fi;
            od;
            Dif := Difference(SGS,Elements(Subsemigroup(US,High)));
            SGS := Union(Dif, High);
        fi;
        
        rk := Length(SGS);
        if rk > n/10 then
            High := SGS{[1..Int(rk/3)+1]};
            US := Semigroup(High);
            i := 1;
            while i < Length(High)  do
                U:=Subsemigroup(US,High{Difference([1..Length(High)],[Length(High)-i])});
                if Size(U)<Size(US) then
                    i:=i+1;
                else
                    High:=GeneratorsOfSemigroup(U);
                fi;
            od;
            Dif := Difference(SGS,Elements(Subsemigroup(US,High)));
            SGS := Union(Dif, High);
        fi;

        rk := Length(SGS);
        if rk > n/10 then

        High := SGS{[1..Int(rk/2)+1]};
        US := Semigroup(High);
        i := 1;
        while i < Length(High)  do
            U:=Subsemigroup(US,High{Difference([1..Length(High)],[Length(High)-i])});
            if Size(U)<Size(US) then
                i:=i+1;
            else
                High:=GeneratorsOfSemigroup(U);
            fi;
        od;
        Dif := Difference(SGS,Elements(Subsemigroup(US,High)));
        SGS := Union(Dif, High);
    fi;

    # A test...
    T := Semigroup(SGS);
    if not ForAll(GeneratorsOfSemigroup(S), i -> i in T) then
        Error("Problems in SmallGeneratingSetForSemigroups");
        #otherwise S=T
    fi;

    i := 1;
    while i < Length(SGS)  do
        U:=Subsemigroup(T,SGS{Difference([1..Length(SGS)],[Length(SGS)-i])});
        if Size(U)<Size(T) then
            i:=i+1;
        else
            SGS:=GeneratorsOfSemigroup(U);
        fi;
    od;
    
    return SGS;
end);
   
############################################################################
##
#F ReduceNumberOfGenerators(L)
## Imposing that L is a subset of the set of generators, produces a new
## set of generators.
#
InstallGlobalFunction(ReduceNumberOfGenerators, function(arg)
    return SmallGeneratingSetForSemigroups(Semigroup(Flat(arg)));
end);
