#############################################################################
##
#W  numsgp-def.gi           Manuel Delgado <mdelgado@fc.up.pt>
#W                          Pedro A. Garcia-Sanchez <pedro@ugr.es>
#W                          Jose Morais <josejoao@fc.up.pt>
##
##
#H  @(#)$Id: numsgp-def.gi,v 0.95 $
##
#Y  Copyright 2005 by Manuel Delgado,
#Y  Pedro Garcia-Sanchez and Jose Joao Morais
#Y  We adopt the copyright regulations of GAP as detailed in the
#Y  copyright notice in the GAP manual.
##
#############################################################################


#############################################################################
##
#F  NumericalSemigroupByGenerators(arg)
##
##  Returns the numerical semigroup generated by arg.
##
#############################################################################
InstallGlobalFunction(NumericalSemigroupByGenerators, function(arg)
    local L, S, M;

    if IsList(arg[1]) then
        L := Difference(Set(arg[1]),[0]);
    else
        L := Difference(Set(arg),[0]);
    fi;
    if not ForAll(L, x -> IsPosInt(x)) then
        Error("The arguments should be positive integers");
    fi;
    if Gcd(L) <> 1 then
        Error("The greatest common divisor is not 1");
    fi;

    M:= Objectify( NewType( FamilyObj( L ),
                IsAttributeStoringRep and IsNumericalSemigroup),
                rec(generators := L) );
    if 1 in L then
        M!.minimalgenerators := [1];
    elif Length(L) = 2 then # a numerical semigroup has at least 2 generators
        M!.minimalgenerators := L;
    fi;
    Setter(IsNumericalSemigroupByGenerators)(M,true);
    return M;

end);


#############################################################################
##
#F  XNumericalSemigroupByGenerators(arg)
##
##  The same as above but to be called by XNumericalSemigroup (the difference
##	is that errors are printed instead of entering the Break loop).
##
#############################################################################
InstallGlobalFunction(XNumericalSemigroupByGenerators, function(arg)
    local L, S, M;

    if IsList(arg[1]) then
        L := Difference(Set(arg[1]),[0]);
    else
        L := Difference(Set(arg),[0]);
    fi;
    if not ForAll(L, x -> IsPosInt(x)) then
        Print("The arguments should be positive integers\n");
        return fail;
    fi;
    if Gcd(L) <> 1 then
        Print("The greatest common divisor is not 1\n");
        return fail;
    fi;

    M:= Objectify( NewType( FamilyObj( L ),
                IsAttributeStoringRep and IsNumericalSemigroup),
                rec(generators := L) );
    if 1 in L then
        M!.minimalgenerators := [1];
    elif Length(L) = 2 then # a numerical semigroup has at least 2 generators
        M!.minimalgenerators := L;
    fi;
    Setter(IsNumericalSemigroupByGenerators)(M,true);
    return M;

end);


#############################################################################
##
#F  NumericalSemigroupByMinimalGenerators(arg)
##
##  Returns the numerical semigroup minimally generated by arg.
##  If the generators given are not minimal, the minimal ones
##  are computed and used.
##
#############################################################################
InstallGlobalFunction(NumericalSemigroupByMinimalGenerators, function(arg)
    local L, S, M, l,
          minimalGSNS, sumNS;


    #####################################################
    # Computes the sum of subsets of numerical semigroups
    sumNS := function(S,T)
        local mm, s, t, R;
        R := [];
        mm := Minimum(Maximum(S),Maximum(T));
        for s in S do
            for t in T do
                if s+t > mm then
                    break;
                else
                    AddSet(R,s+t);
                fi;
            od;
        od;
        return R;
    end;
    ##  End of sumNS(S,T)  --


    minimalGSNS := function(L)
        local res, gen, ss, sss, ssss, i, ngen;

        if 1 in L then
            return [1];
        fi;
        gen := ShallowCopy(L);
        # a naive reduction of the number of generators
        ss := sumNS(gen,gen);
        gen := Difference(gen,ss);
        if ss <> [] then
            sss := sumNS(ss,gen);
            gen := Difference(gen,sss);
            if sss <> [] then
                ssss := sumNS(sss,gen);
                gen := Difference(gen,ssss);
            fi;
        fi;
        if Length(gen) = 2 then
            return gen;
        fi;
        i := Length(gen);
        while i >= 2 do
            ngen := Difference(gen, [gen[i]]);
            if Gcd(ngen) = 1 and
               BelongsToNumericalSemigroup(gen[i], NumericalSemigroup(ngen)) then
                gen := ngen;
                i := i - 1;
            else

                i := i - 1;
            fi;
        od;
        return gen;
    end;
    ##  End of minimalGSNS(L)  --


    if IsList(arg[1]) then
        L := Difference(Set(arg[1]),[0]);
    else
        L := Difference(Set(arg),[0]);
    fi;
    if not ForAll(L, x -> IsPosInt(x)) then
        Error("The arguments should be positive integers");
    fi;
    if Gcd(L) <> 1 then
        Error("The greatest common divisor is not 1");
    fi;


    l := minimalGSNS(L);
    if not Length(L) = Length(l) then
        Info(InfoNumSgps,1, "The given list was not the minimal generating set. The list ", l, " will be used instead.");
        L := l;
    fi;
    M:= Objectify( NewType( FamilyObj( L ),
                IsAttributeStoringRep and IsNumericalSemigroup),
                rec(minimalgenerators := L, generators := L) );
    Setter(GeneratorsOfNumericalSemigroup)( M, AsList( L ) );
    Setter(IsNumericalSemigroupByMinimalGenerators)(M,true);
    Setter(IsNumericalSemigroupByGenerators)(M,true);
    return M;

end);



#############################################################################
##
#F  NumericalSemigroupByMinimalGeneratorsNC(arg)
##
##  Returns the numerical semigroup minimally generated by arg.
##  No test is made about args' minimality.
##
#############################################################################
InstallGlobalFunction(NumericalSemigroupByMinimalGeneratorsNC, function(arg)
    local L, S, M;


    if IsList(arg[1]) then
        L := Difference(Set(arg[1]),[0]);
    else
        L := Difference(Set(arg),[0]);
    fi;
    if not ForAll(L, x -> IsPosInt(x)) then
        Error("The arguments should be positive integers");
    fi;
    if Gcd(L) <> 1 then
        Error("The greatest common divisor is not 1");
    fi;


    M:= Objectify( NewType( FamilyObj( L ),
                IsAttributeStoringRep and IsNumericalSemigroup),
                rec(minimalgenerators := L, generators := L) );
    Setter(GeneratorsOfNumericalSemigroup)( M, AsList( L ) );
    Setter(IsNumericalSemigroupByMinimalGenerators)(M,true);
    Setter(IsNumericalSemigroupByGenerators)(M,true);
    return M;

end);



#############################################################################
##
#F  ModularNumericalSemigroup(a,b)
##
##  Returns the modular numerical semigroup satisfying ax mod b <= x
##
#############################################################################
InstallGlobalFunction(ModularNumericalSemigroup, function(a,b)
    local M;
    if not IsPosInt(a) or not IsPosInt(b) then
        Error("ModularNumericalSemigroup has 2 positive integers as arguments");
    fi;
    M:= Objectify( NewType( FamilyObj( [a,b] ),
                 IsAttributeStoringRep and IsNumericalSemigroup),
                rec(modularcondition := [a,b]) );
    Setter(IsModularNumericalSemigroup)(M,true);
    return M;
end);




#############################################################################
##
#F  ProportionallyModularNumericalSemigroup(a,b,c)
##
##  Returns the proportionally modular numerical semigroup
##  satisfying ax mod b <= cx
##
#############################################################################
InstallGlobalFunction(ProportionallyModularNumericalSemigroup, function(a,b,c)
    local M;
#    if not IsInt(a) or not IsInt(b) or not IsInt(c) then
	if not IsPosInt(a) or not IsPosInt(b) or not IsPosInt(c) then
        Error("ProportionallyModularNumericalSemigroup has 3 positive integers as arguments");
    fi;
    M:= Objectify( NewType( FamilyObj( [a,b,c] ),
                 IsAttributeStoringRep and IsNumericalSemigroup),
                rec(proportionallymodularcondition := [a,b,c]) );
    Setter(IsProportionallyModularNumericalSemigroup)(M,true);
    return M;
end);




#############################################################################
##
#F  NumericalSemigroupByInterval(arg)
##
##  Returns the numerical semigroup
##
#############################################################################
InstallGlobalFunction(NumericalSemigroupByInterval, function(arg)
    local M, r, s;
    if IsList(arg[1]) and Length(arg[1])=2 then
        r := arg[1][1];
        s := arg[1][2];
    elif Length(arg)=2 then
        r := arg[1];
        s := arg[2];
    else
        Error("The argument of NumericalSemigroupByInterval must consist of a pair of rational numbers, <r> and <s> and with r < s");
    fi;
    if not (IsRat(r) and IsRat(s) and r < s) then
        Error("The argument of NumericalSemigroupByInterval must consist of a pair of rational numbers, <r> and <s> and with r < s");
    fi;

    M:= Objectify( NewType( FamilyObj( [r,s] ),
                 IsAttributeStoringRep and IsNumericalSemigroup),
                rec(closedinterval := [r,s]) );
    Setter(IsNumericalSemigroupByInterval)(M,true);
    return M;
end);



#############################################################################
##
#F  NumericalSemigroupByOpenInterval(arg)
##
##  Returns the numerical semigroup
##
#############################################################################
InstallGlobalFunction(NumericalSemigroupByOpenInterval, function(arg)
    local M, r, s;
    if IsList(arg[1]) and Length(arg[1])=2 then
        r := arg[1][1];
        s := arg[1][2];
    elif Length(arg)=2 then
        r := arg[1];
        s := arg[2];
    else
        Error("The argument of NumericalSemigroupByOpenInterval must consist of a pair of rational numbers");
    fi;
    if not (IsRat(r) and IsRat(s) and r < s) then
        Error("The argument of NumericalSemigroupByOpenInterval must consist of a pair of rational numbers, <r> and <s> and with r < s");
    fi;

    M:= Objectify( NewType( FamilyObj( [r,s] ),
                 IsAttributeStoringRep and IsNumericalSemigroup),
                rec(openinterval := [r,s]) );
    Setter(IsNumericalSemigroupByOpenInterval)(M,true);
    return M;
end);



#############################################################################
##
#F  NumericalSemigroupBySubAdditiveFunction(L)
##
##  Returns the numerical semigroup specified by the subadditive
##  function L.
##
#############################################################################
InstallGlobalFunction(NumericalSemigroupBySubAdditiveFunction, function(L)
    local M;

    if not RepresentsPeriodicSubAdditiveFunction(L) then
        Error("The argument of NumericalSemigroupBySubAdditiveFunction must be a subadditive function");
    fi;

    M:= Objectify( NewType( FamilyObj( L ),
                 IsAttributeStoringRep and IsNumericalSemigroup),
                rec(subadditive := L) );
    Setter(IsNumericalSemigroupBySubAdditiveFunction)(M,true);
    return M;
end);



#############################################################################
##
#F  NumericalSemigroupByAperyList(L)
##
##  Returns the numerical semigroup specified by the Apery list L.
##
#############################################################################
InstallGlobalFunction(NumericalSemigroupByAperyList, function(L)
    local i, M;

    if not IsAperyListOfNumericalSemigroup(L) then
        Error("The argument of NumericalSemigroupByAperyList must be The Apery List of some numerical semigroup");
    fi;

    M:= Objectify( NewType( FamilyObj( L ),
                 IsAttributeStoringRep and IsNumericalSemigroup),
                rec(aperylist := L) );
    Setter(IsNumericalSemigroupByAperyList)(M,true);
    return M;
end);




#############################################################################
##
#F  NumericalSemigroupBySmallElements(L)
##
##  Returns the numerical semigroup specified by L,
##  which must be the list of elements of a numerical semigroup,
##  not greater than the Frobenius number + 1.
##
#############################################################################
InstallGlobalFunction(NumericalSemigroupBySmallElements, function(L)
    local i, M, K, R;

    if not RepresentsSmallElementsOfNumericalSemigroup(L) then
        Error("The argument does not represent a numerical semigroup");
    fi;
    K := Difference([1..L[Length(L)]],L);
    R := Intersection([0..K[Length(K)]+1],L);

    M:= Objectify( NewType( FamilyObj( R ),
                 IsAttributeStoringRep and IsNumericalSemigroup),
                rec(elements := R, gaps := Difference([1..R[Length(R)]], R)) );
    Setter(IsNumericalSemigroupBySmallElements)(M,true);
    Setter(IsNumericalSemigroupByGaps)(M,true);
    return M;
end);




#############################################################################
##
#F  NumericalSemigroupByGaps(L)
##
##  Returns the numerical semigroup specified by L,
##  which must be the list of gaps of a numerical semigroup.
##
#############################################################################
InstallGlobalFunction(NumericalSemigroupByGaps, function(L)
    local i, M, K;
    K := Difference([0..L[Length(L)]+1],L);
    if not RepresentsSmallElementsOfNumericalSemigroup(K) then
        Error("The argument does not represent the gaps of  a numerical semigroup");
    fi;

    M:= Objectify( NewType( FamilyObj( L ),
                 IsAttributeStoringRep and IsNumericalSemigroup),
                rec(gaps := L, elements := K) );
    Setter(IsNumericalSemigroupByGaps)(M,true);
    Setter(IsNumericalSemigroupBySmallElements)(M,true);
    return M;
end);




#############################################################################
##
#F  NumericalSemigroupByFundamentalGaps(L)
##
##  Returns the numerical semigroup specified by L,
##  which must be the list of fundamental gaps of a numerical semigroup.
##
#############################################################################
InstallGlobalFunction(NumericalSemigroupByFundamentalGaps, function(L)
    local i, M, K, G;
    G := Set(Flat(List(L,i->DivisorsInt(i))));
    K := Difference([0..G[Length(G)]+1],G);
    if not RepresentsSmallElementsOfNumericalSemigroup(K) then
        Error("The argument does not represent the fundamental gaps of  a numerical semigroup");
    fi;

    M:= Objectify( NewType( FamilyObj( L ),
                 IsAttributeStoringRep and IsNumericalSemigroup),
                rec(fundamentalgaps := L) );
    M!.gaps := G;
    M!.elements := K;
    Setter(IsNumericalSemigroupByFundamentalGaps)(M,true);
    return M;
end);



#############################################################################
##
#F  NumericalSemigroup(arg)
##
##  This function's first argument may be one of:
##  "generators", "minimalgenerators", "modular",
##  "propmodular", "elements", "gaps",
##  "fundamentalgaps", "subadditive" or "apery" according to
##  how the semigroup is being defined.
##  The following arguments must conform to the arguments of
##  the corresponding function defined above.
##  By default, the option "generators" is used, so,
##  gap> NumericalSemigroup(3,7);
##  <Numerical semigroup with 2 generators>
##
#############################################################################
InstallGlobalFunction(NumericalSemigroup, function(arg)
    local L, S, M;

    if IsString(arg[1]) then
        if arg[1] = "modular" then
            if Length(arg) = 3 then
                return ModularNumericalSemigroup(arg[2],arg[3]);
            else
                Error("For the modular case NumericalSemigroup must have 3 arguments");
            fi;
        elif arg[1] = "propmodular" then
            if Length(arg) = 4 then
                return ProportionallyModularNumericalSemigroup(arg[2],arg[3],arg[4]);
            else
                Error("For the proportionally modular case NumericalSemigroup must have 4 arguments");
            fi;
        elif arg[1] = "interval" then
            if Length(arg) = 3 then
                return NumericalSemigroupByInterval(arg[2],arg[3]);
            elif Length(arg) = 2 then
                return NumericalSemigroupByInterval(arg[2]);
            else
                Error("For the interval case NumericalSemigroup must have 2 or 3 arguments");
            fi;
        elif arg[1] = "openinterval" then
            if Length(arg) = 3 then
                return NumericalSemigroupByOpenInterval(arg[2],arg[3]);
            elif Length(arg) = 2 then
                return NumericalSemigroupByOpenInterval(arg[2]);
            else
                Error("For the open interval case NumericalSemigroup must have 2 or 3 arguments");
            fi;
        elif arg[1] = "fundamentalgaps" then
            return NumericalSemigroupByFundamentalGaps(arg[2]);
        elif arg[1] = "gaps" then
            return NumericalSemigroupByGaps(arg[2]);
        elif arg[1] = "elements" then
            return NumericalSemigroupBySmallElements(arg[2]);
        elif arg[1] = "subadditive" then
            return NumericalSemigroupBySubAdditiveFunction(arg[2]);
        elif arg[1] = "apery" then
            return NumericalSemigroupByAperyList(arg[2]);
        elif arg[1] = "generators" then
            return NumericalSemigroupByGenerators(Flat(arg{[2..Length(arg)]}));
        elif arg[1] = "minimalgenerators" then
            return NumericalSemigroupByMinimalGenerators(Flat(arg{[2..Length(arg)]}));
        else
            Error("Invalid first argument, it should be one of: \"modular\", \"propmodular\", \"interval\", \"openinterval\", \"fundamentalgaps\", \"gaps\", \"elements\", \"subadditive\", \"apery\", \"generators\", \"minimalgenerators\"");
        fi;

    else
        if Length(arg) = 1 and IsList(arg[1]) then
            return NumericalSemigroupByGenerators(arg[1]);
        else
            return NumericalSemigroupByGenerators(arg{[1..Length(arg)]});
        fi;
    fi;
end);


########################################################################
########################################################################

#############################################################################
##
#M  PrintObj(S)
##
##  This method for numerical semigroups.
##
#############################################################################
InstallMethod( PrintObj,
    "prints a Numerical Semigroup",
    [ IsNumericalSemigroup],
        function( S )
    if HasIsModularNumericalSemigroup(S) then
        Print("ModularNumericalSemigroup( ", S!.modularcondition, " )\n");
    elif HasIsProportionallyModularNumericalSemigroup(S) then
        Print("ProportionallyModularNumericalSemigroup( ", S!.proportionallymodularcondition, " )\n");
    elif IsBound(S!.generators) then
        Print("NumericalSemigroup( ", S!.generators, " )\n");
    else
        Print("NumericalSemigroup( ", GeneratorsOfNumericalSemigroup(S), " )\n");
    fi;
end);



#############################################################################
##
#M  ViewObj(S)
##
##  This method for numerical semigroups.
##
#############################################################################
InstallMethod( ViewObj,
    "displays a Numerical Semigroup",
    [IsNumericalSemigroup],
        function( S )
    if IsBound(S!.modularcondition) then
        Print("<Modular numerical semigroup satisfying ", S!.modularcondition[1],"x mod ",S!.modularcondition[2], " <= x >");
    elif IsBound(S!.proportionallymodularcondition) then
        Print("<Proportionally modular numerical semigroup satisfying ", S!.proportionallymodularcondition[1],"x mod ",S!.proportionallymodularcondition[2], " <= ",S!.proportionallymodularcondition[3],"x >");
    elif IsBound(S!.generators) then
        Print("<Numerical semigroup with ", Length(S!.generators), " generators>");
    else
        Print("<Numerical semigroup>");
    fi;
end);



#############################################################################
##
#M  Display(S)
##
##  This method for numerical semigroups.
##
#############################################################################
InstallMethod( Display,
        "displays a Numerical Semigroup",
    [IsNumericalSemigroup],
        function( S )
    local M, u, L, condensed;

    condensed := function(L)
        local c, C,j, n, search, bool;
        C := [];
        bool := true;
        j := 0;
        c := L[1];
        search := function(n) # searches the greatest subinterval starting in n
            local i, k;
            k := 0;
            for i in [Position(L,n).. Length(L)-1] do
                if not (L[i]+1 = L[i+1]) then
                    c := L[i+1];
                    return [n..n+k];
                fi;
                k := k+1;
            od;
            bool := false;
            return [n..L[Length(L)]];
        end;
        while bool do
            Add(C,search(c));
        od;
        return C;
    end;
    ##  End of condensed()  --

    L := SmallElementsOfNumericalSemigroup(S);
    M := condensed(L);
    u := [M[Length(M)][1],"->"];
    M[Length(M)] := u;
    return M;
end);


####################################################
####################################################

#############################################################################
##
#A  IsNumericalSemigroupByGenerators(S)
##
##  Tests if a numerical semigroup was given by its generators.
##
#############################################################################
InstallMethod(IsNumericalSemigroupByGenerators,
        "Tests if a Numerical Semigroup was given by its generators",
    [IsNumericalSemigroup],
        function( S )
    return(HasIsNumericalSemigroupByGenerators( S ));
end);



#############################################################################
##
#A  IsNumericalSemigroupByMinimalGenerators(S)
##
##  Tests if a numerical semigroup was given by its minimal generators.
##
#############################################################################
InstallMethod(IsNumericalSemigroupByMinimalGenerators,
        "Tests if a Numerical Semigroup was given by its minimal generators",
    [IsNumericalSemigroup],
        function( S )
    return(HasIsNumericalSemigroupByMinimalGenerators( S ));
end);



#############################################################################
##
#A  IsNumericalSemigroupByInterval(S)
##
##  Tests if a numerical semigroup was given by an interval.
##
#############################################################################
InstallMethod(IsNumericalSemigroupByInterval,
        "Tests if a Numerical Semigroup was given by an interval",
    [IsNumericalSemigroup],
        function( S )
    return(HasIsNumericalSemigroupByInterval( S ));
end);



#############################################################################
##
#A  IsNumericalSemigroupByOpenInterval(S)
##
##  Tests if a numerical semigroup was given by an open interval.
##
#############################################################################
InstallMethod(IsNumericalSemigroupByOpenInterval,
        "Tests if a Numerical Semigroup was given by an open interval",
    [IsNumericalSemigroup],
        function( S )
    return(HasIsNumericalSemigroupByOpenInterval( S ));
end);




#############################################################################
##
#A  IsNumericalSemigroupBySubAdditiveFunction(S)
##
##  Tests if a numerical semigroup was given by a subadditive function.
##
#############################################################################
InstallMethod(IsNumericalSemigroupBySubAdditiveFunction,
        "Tests if a Numerical Semigroup was given by a subadditive function",
    [IsNumericalSemigroup],
        function( S )
    return(HasIsNumericalSemigroupBySubAdditiveFunction( S ));
end);




#############################################################################
##
#A  IsNumericalSemigroupByAperyList(S)
##
##  Tests if a numerical semigroup was given by its Apery list.
##
#############################################################################
InstallMethod(IsNumericalSemigroupByAperyList,
        "Tests if a Numerical Semigroup was given by its Apery list",
    [IsNumericalSemigroup],
        function( S )
    return(HasIsNumericalSemigroupByAperyList( S ));
end);



#############################################################################
##
#A  IsNumericalSemigroupBySmallElements(S)
##
##  Tests if a numerical semigroup was given by its small elements.
##
#############################################################################
InstallMethod(IsNumericalSemigroupBySmallElements,
        "Tests if a Numerical Semigroup was given by its small elements",
    [IsNumericalSemigroup],
        function( S )
    return(HasIsNumericalSemigroupBySmallElements( S ));
end);




#############################################################################
##
#A  IsNumericalSemigroupByGaps(S)
##
##  Tests if a numerical semigroup was given by its gaps.
##
#############################################################################
InstallMethod(IsNumericalSemigroupByGaps,
        "Tests if a Numerical Semigroup was given by gaps",
    [IsNumericalSemigroup],
        function( S )
    return(HasIsNumericalSemigroupByGaps( S ));
end);




#############################################################################
##
#A  IsNumericalSemigroupByFundamentalGaps(S)
##
##  Tests if a numerical semigroup was given by its fundamental gaps.
##
#############################################################################
InstallMethod(IsNumericalSemigroupByFundamentalGaps,
        "Tests if a Numerical Semigroup was given by fundamental gaps",
    [IsNumericalSemigroup],
        function( S )
    return(HasIsNumericalSemigroupByFundamentalGaps( S ));
end);




#############################################################################
##
#A  IsProportionallyModularNumericalSemigroup(S)
##
##  Tests if a numerical semigroup is proportionally modular.
##
#############################################################################
InstallMethod(IsProportionallyModularNumericalSemigroup,
        "Tests if a Numerical Semigroup is proportionally modular",
    [IsNumericalSemigroup],
        function( S )
    local fg, psfrob, EH, A, gens, a, ka, B, leng, i, j, q, qr, lenB,
          a1, b1, a2, b2,
          compare;


    compare := function(x, y)
        local a, ka, a2, ka2;

        a := x[1];
        ka := x[2];
        a2 := y[1];
        ka2 := y[2];

        return(a/ka < a2/ka2 or (a/ka = a2/ka2 and a < a2));
    end;
    ##  End of compare()  --


    fg := FundamentalGapsOfNumericalSemigroup(S);
    psfrob := PseudoFrobeniusOfNumericalSemigroup(S);
    EH := Intersection(fg, psfrob);
    gens := GeneratorsOfNumericalSemigroup(S);
    A := Union(Difference(EH, [1]), gens);
    B := [];
    for a in A do
        for ka in [1..a-1] do
            Add(B, [a,ka]);
        od;
    od;
    Sort(B, compare);
    leng := Length(gens);
    lenB := Length(B);
    q := 1;
    qr := q + leng - 1;
    while qr <= lenB do
        if q-1 > 0 then
            if qr+1 <= lenB then
                if B[q-1][1]/B[q-1][2] <> B[q][1]/B[q][2] and
                   B[qr][1]/B[qr][2] <> B[qr+1][1]/B[qr+1][2] and
                   Difference(List([q..qr], ind -> B[ind][1]), gens) = [] then
                    a1 := B[q][1];
                    b1 := B[q][2];
                    a2 := B[qr][1];
                    b2 := B[qr][2];
                    S!.proportionallymodularcondition := [a2*b1,a1*a2,a2*b1-a1*b2];
                    if a2*b1-a1*b2 = 1 then
                        S!.modularcondition := [a2*b1,a1*a2];
                        Setter(IsModularNumericalSemigroup)(S,true);
                    fi;
                    return(true);
                fi;
            else
                if B[q-1][1]/B[q-1][2] <> B[q][1]/B[q][2] and
                   Difference(List([q..qr], ind -> B[ind][1]), gens) = [] then
                    a1 := B[q][1];
                    b1 := B[q][2];
                    a2 := B[qr][1];
                    b2 := B[qr][2];
                    S!.proportionallymodularcondition := [a2*b1,a1*a2,a2*b1-a1*b2];
                    if a2*b1-a1*b2 = 1 then
                        S!.modularcondition := [a2*b1,a1*a2];
                        Setter(IsModularNumericalSemigroup)(S,true);
                    fi;
                    return(true);
                fi;
            fi;
        else
            if qr+1 <= lenB then
                if B[qr][1]/B[qr][2] <> B[qr+1][1]/B[qr+1][2] and
                   Difference(List([q..qr], ind -> B[ind][1]), gens) = [] then
                    a1 := B[q][1];
                    b1 := B[q][2];
                    a2 := B[qr][1];
                    b2 := B[qr][2];
                    S!.proportionallymodularcondition := [a2*b1,a1*a2,a2*b1-a1*b2];
                    if a2*b1-a1*b2 = 1 then
                        S!.modularcondition := [a2*b1,a1*a2];
                        Setter(IsModularNumericalSemigroup)(S,true);
                    fi;
                    return(true);
                fi;
            else
                if Difference(List([q..qr], ind -> B[ind][1]), gens) = [] then
                    a1 := B[q][1];
                    b1 := B[q][2];
                    a2 := B[qr][1];
                    b2 := B[qr][2];
                    S!.proportionallymodularcondition := [a2*b1,a1*a2,a2*b1-a1*b2];
                    if a2*b1-a1*b2 = 1 then
                        S!.modularcondition := [a2*b1,a1*a2];
                        Setter(IsModularNumericalSemigroup)(S,true);
                    fi;
                    return(true);
                fi;
            fi;
        fi;
        q := q + 1;
        qr := qr + 1;
    od;
    return(false);
end);



#############################################################################
##
#A  IsModularNumericalSemigroup(S)
##
##  Tests if a numerical semigroup is modular.
##
#############################################################################
InstallMethod(IsModularNumericalSemigroup,
        "Tests if a Numerical Semigroup is modular",
    [IsNumericalSemigroup],
        function( S )
    local A, a, Ac, C, c, lhs, gs, ms, b;


    lhs := Length(GapsOfNumericalSemigroup(S));
    gs := FrobeniusNumberOfNumericalSemigroup(S);
    ms := MultiplicityOfNumericalSemigroup(S);     #the least positive integer in S
    b := gs + ms;
    A := [];
    for a in [2..Int((b+1)/2)] do
        if b = 2*lhs + GcdInt(a,b) + GcdInt(a-1,b)-1 and
           ms < Minimum(b/GcdInt(a,b),b/GcdInt(a-1,b)) then
            Add(A,a);
        fi;
    od;
    for a in A do
        if Display(S) = Display(ModularNumericalSemigroup(a,b)) then
            S!.modularcondition := [a,b];
            S!.proportionallymodularcondition := [a,b,1];
            Setter(IsProportionallyModularNumericalSemigroup)(S,true);
            return(true);
        fi;
    od;
    C := [];
    for c in [2 * lhs +1 .. 12 * lhs -6] do
        if RemInt(c,ms) = 0 then
            Add(C,c);
        fi;
    od;
    for c in C do
        Ac := [];
        for a in [2..Int((c+1)/2)] do
            if c = 2*lhs + GcdInt(a,c) + GcdInt(a-1,c)-1 and
               ms = Minimum(c/GcdInt(a,c),c/GcdInt(a-1,c)) then
                Add(Ac,a);
            fi;
        od;
        for a in Ac do
            if Display(S)
               = Display(ModularNumericalSemigroup(a,c)) then
                S!.modularcondition := [a,c];
                S!.proportionallymodularcondition := [a,c,1];
                Setter(IsProportionallyModularNumericalSemigroup)(S,true);
                return(true);
            fi;
        od;
    od;
    return(false);
end);



InstallOtherMethod( One,
    "partial method for a Numerical Semigroup",
    true,
    [ IsMagmaWithOne and IsNumericalSemigroup ], 100,
#T high priority?
    function( M )
    return(0);
    end );




InstallMethod(MultiplicativeNeutralElement,
	"for a magma-with-one",
	true,
	[HasMultiplicativeNeutralElement and IsMagmaWithOne and IsNumericalSemigroup], GETTER_FLAGS+1,
	function(S)
    return(0);
end);




InstallMethod( Size,
        "for a collection",
        [ IsCollection and IsNumericalSemigroup ],
        function(S)
    return(infinity);
    return(Length(AsSSortedList(S)));
end);

############################################################################
##
#M Methods for the comparison of numerical semigroups.
##
InstallMethod( \=,
        "for two numerical semigroups",
        [IsNumericalSemigroup and IsNumericalSemigroupRep,
         IsNumericalSemigroup and IsNumericalSemigroupRep],
        function(x, y )

    if Set(GeneratorsOfNumericalSemigroup(x)) =
       Set(GeneratorsOfNumericalSemigroup(y)) then
        return(true);
    elif IsBound(x!.minimalgenerators) and IsBound(y!.minimalgenerators) then
        return  x!.minimalgenerators=y!.minimalgenerators;

    elif IsBound(x!.modularcondition) and IsBound(y!.modularcondition) and
      x!.modularcondition=y!.modularcondition then
        return  true;

    elif IsBound(x!.proportionallymodularcondition) and
      IsBound(y!.proportionallymodularcondition) and
      x!.proportionallymodularcondition=y!.proportionallymodularcondition then
        return true;

     elif IsBound(x!.aperylist) and IsBound(y!.aperylist) then
         return  x!.aperylist=y!.aperylist;

     elif IsBound(x!.gaps) and IsBound(y!.gaps) then
         return  x!.gaps=y!.gaps;

     elif IsBound(x!.fundamentalgaps) and IsBound(y!.fundamentalgaps) then
         return  x!.fundamentalgaps=y!.fundamentalgaps;

     elif IsBound(x!.elements) and IsBound(y!.elements) then
         return  x!.elements=y!.elements;

     else
         return SmallElementsOfNumericalSemigroup(x) = SmallElementsOfNumericalSemigroup(y);
    fi;
end);

InstallMethod( \<,
        "for two numerical semigroups",
        [IsNumericalSemigroup,IsNumericalSemigroup],
        function(x, y )
    return(SmallElementsOfNumericalSemigroup(x) < SmallElementsOfNumericalSemigroup(y));
end );




