1#############################################################################
2##
3#W  good-semigroups.gd           Manuel Delgado <mdelgado@fc.up.pt>
4#W                          Pedro A. Garcia-Sanchez <pedro@ugr.es>
5##
6#Y  Copyright 2016-- Centro de Matemática da Universidade do Porto, Portugal and IEMath-GR, Universidad de Granada, Spain
7#############################################################################
8
9####################################################
10##
11#F NumericalDuplication(S,E,b)
12## returns 2S\cup(2E+b)
13####################################################
14InstallGlobalFunction(NumericalDuplication, function(S,E,b)
15    local smallS, doubS, smallE, f, small, mgsE, mgsS;
16
17    if not(IsNumericalSemigroup(S)) or not(IsIdealOfNumericalSemigroup(E)) then
18      Error("The first argument must be a numerical semigroup, and the second an ideal");
19    fi;
20
21    if not(IsInt(b)) then
22      Error("The third argument must be an integer");
23    fi;
24
25    if not(b mod 2=1) then
26      Error("The third argument must be an odd integer");
27    fi;
28
29    # if not(b in S) then
30    #   Error("The third argument must belong to the first argument");
31    # fi;
32
33    mgsE:=MinimalGeneratingSystem(E);
34    # if not(ForAll(mgsE, x -> x in S)) then
35    #   Error("The second argument must be a integral ideal of the first");
36    # fi;
37
38    mgsS:=MinimalGenerators(S);
39
40    if not(ForAll(Cartesian(mgsE,mgsE), p->Sum(p)+b in S)) then
41      Error("The arguments do not define a semigroup (E+E+b is not included in S)");
42    fi;
43
44    return NumericalSemigroup(Union(2*mgsS, 2*mgsE+b));
45end);
46
47
48
49###################################################
50##
51#F NumericalSemigroupDuplication(S,E)
52## returns S\bowtie E
53###################################################
54InstallGlobalFunction(NumericalSemigroupDuplication,function(S,E)
55    local M, mgsE;
56
57
58    if not(IsNumericalSemigroup(S)) or not(IsIdealOfNumericalSemigroup(E)) then
59      Error("The first argument must be a numerical semigroup, and the second an ideal");
60    fi;
61
62    mgsE:=MinimalGeneratingSystem(E);
63    if not(ForAll(mgsE, x -> x in S)) then
64      Error("The second argument must be an integral ideal of the first");
65    fi;
66
67    M:=Objectify(GoodSemigroupsType, rec());
68    SetNumericalSemigroupGS(M,S);
69    SetIdealGS(M,E);
70    SetDefinedByDuplication(M,true);
71
72    return M;
73end);
74
75
76###################################################
77##
78#F AmalgamationOfNumericalSemigroups(S,E,c)
79## returns S\bowtie^f E, f multiplication by c
80###################################################
81InstallGlobalFunction(AmalgamationOfNumericalSemigroups, function(S,E,c)
82    local M, T, msg;
83
84    if not(IsNumericalSemigroup(S)) or not(IsIdealOfNumericalSemigroup(E)) then
85      Error("The first argument must be a numerical semigroup, and the second an ideal");
86    fi;
87
88    if not(IsPosInt(c)) then
89      Error("The third argument must be a positive integer");
90    fi;
91
92    T:=AmbientNumericalSemigroupOfIdeal(E);
93    msg:=MinimalGeneratingSystem(S);
94    if not(ForAll(msg, x-> c*x in T)) then
95      Error("Multiplication by the third argument must be a morphism from the first argument to the ambient semigroup of the second");
96    fi;
97
98    M:=Objectify(GoodSemigroupsType, rec());
99    SetNumericalSemigroupGS(M,S);
100    SetIdealGS(M,E);
101    SetMorphismGS(M,c);
102    SetDefinedByAmalgamation(M,true);
103
104    return M;
105end);
106
107###################################################
108##
109#F CartesianProductOfNumericalSemigroups(S1,S2)
110## Computes the cartesian product of S1 and S2, which
111## is a good semigroup
112###################################################
113InstallGlobalFunction(CartesianProductOfNumericalSemigroups, function(S1,S2)
114    local M;
115
116    if not(IsNumericalSemigroup(S1)) or not(IsNumericalSemigroup(S2)) then
117      Error("The arguments must be numerical semigroups");
118    fi;
119
120    M:=Objectify(GoodSemigroupsType, rec());
121    SetNumericalSemigroupListGS(M,[S1,S2]);
122    SetDefinedByCartesianProduct(M,true);
123
124    return M;
125end);
126
127
128
129###################################################
130##
131#F RepresentsSmallElementsOfGoodSemigroup(X)
132## detects if X is a good semiring
133###################################################
134InstallGlobalFunction(RepresentsSmallElementsOfGoodSemigroup, function(X)
135    local c, inf, conG2, C;
136
137    inf:=function(x,y)
138        return [Minimum(x[1],y[1]), Minimum(x[2],y[2])];
139    end;
140
141    if not(IsRectangularTable(X)) and ForAll(X, x->Length(x)=2) then
142      Error("The argument must be a list of pairs of positive integers");
143    fi;
144
145    if not(ForAll(X, x->IsInt(x[1]) and IsInt(x[2]) and x[1]>=0 and x[2]>=0)) then
146      Error("The argument must be a list of pairs of positive integers");
147    fi;
148
149    C:=[0,0];
150    if not(C in X) then
151        Info(InfoNumSgps,2,"Zero is not in the set.");
152        return false;
153    fi;
154
155    C[1]:=Maximum(List(X,x->x[1]));
156    C[2]:=Maximum(List(X,x->x[2]));
157    if not(C in X) then
158        Info(InfoNumSgps,2,"The maximum is not in the set.");
159        return false;
160    fi;
161
162    c:=Cartesian(X,X);
163    if ForAny(c, p->not(inf(p[1]+p[2],C) in X)) then
164        Info(InfoNumSgps,2,"The set is not closed under addition modulo inf the maximum.");
165        return false;
166    fi;
167
168    if ForAny(c, p->not(inf(p[1], p[2]) in X)) then
169        Info(InfoNumSgps,2,"The set is not closed under infimums.");
170        return false;
171    fi;
172    conG2:=First(X, x->x[1]<C[1] and ForAny(X,y-> x[1]=y[1] and x[2]<y[2] and not(ForAny(X, z->x[2]=z[2] and z[1]>x[1]))));
173    if conG2<>fail then
174        Info(InfoNumSgps,2,"The set is not a good semiring: ",conG2);
175        return false;
176    fi;
177    conG2:=First(X, x->x[2]<C[2] and ForAny(X,y-> x[2]=y[2] and x[1]<y[1] and not(ForAny(X, z->x[1]=z[1] and z[2]>x[2]))));
178    if conG2<>fail then
179        Info(InfoNumSgps,2,"The set is not a good semiring: ",conG2);
180        return false;
181    fi;
182    return true;
183end);
184
185
186###################################################
187##
188#F GoodSemigroup(X,C)
189## define the good semigroup from the set of points G
190## with conductor C
191###################################################
192InstallGlobalFunction(GoodSemigroup, function(Gn,C)
193    local M, p1, p2, c, CC, sm, SemiRing_NS, gen, inf, G;
194
195    if not(IsRectangularTable(Gn)) and ForAll(Gn, x->IsList(x) and Length(x)=2) then
196      Error("The first argument must be a list of pairs of positive integers");
197    fi;
198
199    if not(ForAll(Gn, x->IsInt(x[1]) and IsInt(x[2]) and x[1]>=0 and x[2]>=0)) then
200      Error("The first argument must be a list of pairs of positive integers");
201    fi;
202
203    if not(IsList(C)) and Length(C)=2 then
204      Error("The second argument must be a list (pair) of nonnegative integers");
205    fi;
206
207    if not(IsInt(C[1]) and IsInt(C[2]) and C[1]>=0 and C[2]>=0) then
208      Error("The second argument must be a list (pair) of nonnegative integers");
209    fi;
210
211    inf:=function(u,v)
212        return [Minimum(u[1],v[1]),Minimum(u[2],v[2])];
213    end;
214
215      ###############################################################
216      ##
217      #F SemiRing_NS(G,C)
218      ## G is a set of points;
219      ## computes the saturation wrt sum (cutted by C) and inf
220      ################################################################
221      SemiRing_NS:=function(G,C)
222          local O, OO, new,i,j, o;
223
224
225          O:=G;
226          # sum saturation
227          repeat
228              new:=[];
229              for i in [1..Length(O)] do
230                  for j in [i.. Length(O)] do
231                      o:=inf(C,O[i]+O[j]);
232
233                      if not(o in O) then
234                          Add(new, o);
235                      fi;
236                  od;
237              od;
238              O:=Union(O,new);
239          until new=[];
240
241          O:=Union(O,[[0,0]]);
242          #inf saturation
243          repeat
244              new:=[];
245              for i in [1..Length(O)] do
246                  for j in [i.. Length(O)] do
247                      o:=inf(O[i],O[j]);
248                      if not(o in O) then
249                          Add(new, o);
250                      fi;
251                  od;
252              od;
253              O:=Union(O,new);
254          until new=[];
255
256          # #good saturation x coordinate
257          # repeat
258          #     new:=First(O, x->x[1]<C[1] and ForAny(O,y-> x[1]=y[1] and x[2]<y[2] and not(ForAny(O, z->x[2]=z[2] and z[1]>x[1]))));
259          #     if new<>fail then
260          #         Add(O,[C[1],new[2]]);
261          #     fi;
262
263          # until new=fail;
264          # #good saturation y coordinate
265          # repeat
266          #     new:=First(O, x->x[2]<C[2] and ForAny(O,y-> x[2]=y[2] and x[1]<y[1] and not(ForAny(O, z->x[1]=z[1] and z[2]>x[2]))));
267          #     if new<>fail then
268          #         Add(O,[new[1],C[2]]);
269          #     fi;
270
271          # until new=fail;
272          return O;
273      end; # of SemiRing_NS
274
275    G:=ShallowCopy(Gn);
276    p1:=List(G,x->x[1]);
277    p2:=List(G,x->x[2]);
278
279    CC:=[Maximum(p1), Maximum(p2)];
280    if not(C[1]>=CC[1] and C[2]>=CC[2]) then
281        Info(InfoNumSgps, 2, "The conductor is not larger than the maximum of the given set");
282        G:=List(G, x->inf(x,C));
283    fi;
284
285
286    sm:=Union(SemiRing_NS(G,C),[C]);
287    if not(RepresentsSmallElementsOfGoodSemigroup(sm)) then
288      Error("The given set does not generate a good semigroup");
289    fi;
290
291    c:=C;
292    while ((c-[0,1]) in sm) or ((c-[1,0]) in sm) do
293      if ((c-[0,1]) in sm) and ((c-[1,0]) in sm) then #c-[1,1] also in sm
294        c:=c-[1,1];
295      elif c-[0,1] in sm then
296        c:=c-[0,1];
297      else
298        c:=c-[1,0];
299      fi;
300    od;
301    if C<>c then #sm must be redefined, and gens
302      Info(InfoNumSgps,2,"Conductor redefined");
303      sm:=Filtered(sm, x->x[1]<=c[1] and x[2]<=c[2]);
304      gen:=List(G, x->inf(x,c));#x[1]<=c[1] and x[2]<=c[2]);
305    else
306      gen:=ShallowCopy(G);
307    fi;
308
309    M:=Objectify(GoodSemigroupsType, rec());
310    SetGenerators(M,gen);
311    SetConductor(M,c);
312    SetSmallElements(M,sm);
313    return M;
314end);
315
316###################################################
317##
318#M ConductorOfGoodSemigroup(M)
319#M Conductor(M)
320## returns the conductor of M
321##################################################
322InstallMethod(ConductorOfGoodSemigroup,
323        "Calculates the conductor of the semigroup",
324        [IsGoodSemigroup ],50,
325        function(M)
326    local e,s,c,ce,cs,c1,c2;
327
328    if HasConductor(M) then
329        return(Conductor(M));
330    fi;
331
332    if HasSmallElements(M) then
333      s:=SmallElements(M);
334      c:=s[Length(s)];
335      SetConductor(M,c);
336      return c;
337    fi;
338
339    if IsGoodSemigroupByDuplication(M) then
340        Info(InfoNumSgps,2,"Using semigroup duplication formula");
341        e:=IdealGS(M);
342        ce:=Conductor(e);
343        SetConductor(M,[ce,ce]);
344        return [ce,ce];
345    fi;
346
347    if IsGoodSemigroupByAmalgamation(M) then
348        Info(InfoNumSgps,2,"Using amalgamation formula");
349        e:=IdealGS(M);
350        ce:=Conductor(e);
351        s:=NumericalSemigroupGS(M);
352        c:=MorphismGS(M);
353        cs:=CeilingOfRational(ce/c);
354        while true do
355            cs:=cs-1;
356            if (cs in s) and not(2*cs in e) then
357                SetConductor(M,[cs+1,ce]);
358                return [cs+1,ce];
359            fi;
360            if cs<0 then
361                SetConductor(M,[0,ce]);
362                return [0,ce];
363            fi;
364        od;
365    fi;
366
367    if IsGoodSemigroupByCartesianProduct(M) then
368      Info(InfoNumSgps,2,"This is a cartesian product, so the two conductors in a list");
369      return([Conductor(NumericalSemigroupListGS(M)[1]),
370              Conductor(NumericalSemigroupListGS(M)[2])]);
371    fi;
372
373    return fail;
374
375end);
376
377###################################################
378##
379#A SmallElements(M)
380#A SmallElementsOfGoodSemigroup(M)
381## returns de small elements of M, that is,
382## the elements below the conductor
383##################################################
384InstallMethod(SmallElementsOfGoodSemigroup,
385        "Calculates the small elements of the semigroup",
386        [IsGoodSemigroup ],50,
387        function(M)
388    local C,box, sm;
389
390    if HasSmallElements(M) then
391        return SmallElements(M);
392    fi;
393
394    if IsGoodSemigroupByCartesianProduct(M) then
395      sm:=Cartesian(SmallElements(NumericalSemigroupListGS(M)[1]),
396                    SmallElements(NumericalSemigroupListGS(M)[2]));
397      SetSmallElements(M,sm);
398      return sm;
399    fi;
400    C:=Conductor(M);
401    box:=Cartesian([0..C[1]],[0..C[2]]);
402    sm:=Intersection(box,M);
403    SetSmallElements(M,sm);
404    return sm;
405end);
406
407
408###############################################################
409##
410#A IsSymmetric(M)
411## Determines if M is symmetric
412###############################################################
413InstallMethod(IsSymmetricGoodSemigroup,
414"Determines if the good semigroup is symmetric",
415[IsGoodSemigroup], function(M)
416  local sm, w1, w2, b1,b2, c;
417  c:=Conductor(M);
418  sm:=SmallElementsOfGoodSemigroup(M);
419  w1:=Length(Set(sm,x->x[1]));
420  w2:=Length(Set(sm,x->x[2]));
421  b1:=Length(Filtered(sm, x-> x[1]=c[1] and x[2]<c[2]));
422  b2:=Length(Filtered(sm, x-> x[2]=c[2] and x[1]<c[1]));
423  return Sum(c)=w1+w2+b1+b2-2;
424end);
425
426InstallMethod(IsSymmetric,
427"Tests wheter the semigroup is symmetric",
428[IsGoodSemigroup], IsSymmetricGoodSemigroup
429);
430
431
432###################################################
433##
434#A MinimalGenerators(M)
435#A MinimalGoodGeneratingSystemOfGoodSemigroup(M)
436## returns the unique minimal good generating of the
437## good semigroup M
438###################################################
439InstallMethod(MinimalGoodGeneratingSystemOfGoodSemigroup,
440       "Calculates the minimal generating system of the semigroup",
441        [IsGoodSemigroup ],50,
442        function(M)
443  local filter,mingen,C;
444
445  ## G is a given set of small elements
446  ## filter outputs a subset that generates all
447  filter:=function(G,C)
448
449      local member1, member2,  gen, g, gg, visited, left;
450
451
452      member1:=function(X,x)
453          if x[1]*x[2]=0 then
454              return x[2]<=0 and x[1]=0;
455          fi;
456          if x[1]<0 or x[2]< 0 then
457              return false;
458          fi;
459
460          return ForAny(X, y->member1(X,x-y));
461
462      end;
463
464      member2:=function(X,x)
465          if x[1]*x[2]=0 then
466              return x[1]<=0 and x[2]=0;
467          fi;
468          if x[1]<0 or x[2]< 0 then
469              return false;
470          fi;
471
472          return ForAny(X, y->member2(X,x-y));
473
474      end;
475
476      gen:=Set(ShallowCopy(G));
477      RemoveSet(gen,[0,0]);
478
479      # removing those that can be infimums of two others to make faster
480      # the next test
481      for g in G do
482          if g[1]<C[1] then
483              if First(gen, x->x[1]=g[1] and x[2]>g[2])<>fail then
484                  RemoveSet(gen,g);
485                  continue;
486              fi;
487          fi;
488
489          if g[2]<C[2] then
490              if First(gen, x->x[2]=g[2] and x[2]>g[2])<>fail then
491                  RemoveSet(gen,g);
492                  continue;
493              fi;
494          fi;
495      od;
496
497      if gen=[] then
498        return [];
499      fi;
500
501      visited:=[];
502      left:=gen;
503      while left<>[] and gen<>[] do
504          g:=left[1];
505          AddSet(visited,g);
506          left:=Difference(gen,visited);
507          gg:=Difference(gen,[g]);
508          if g[1]=C[1] then
509              if member2(gg,g) then
510                  RemoveSet(gen,g);
511              fi;
512          elif g[2]=C[2] then
513              if member1(gg,g) then
514                  RemoveSet(gen,g);
515              fi;
516          else
517              if member1(gg,g) and member2(gg,g) then
518                  RemoveSet(gen,g);
519              fi;
520          fi;
521      od;
522      return gen;
523  end;
524
525  if IsGoodSemigroupByCartesianProduct(M) then
526    Print("ToDo\n");
527    return fail;
528  fi;
529
530  C:=Conductor(M);
531  if HasGenerators(M) then
532    mingen:=filter(Generators(M),C);
533    #SetGenerators(M,mingen);
534    return mingen;
535  fi;
536
537  mingen:=filter(SmallElementsOfGoodSemigroup(M),C);
538  SetGenerators(M,mingen);
539  return mingen;
540end);
541
542###############################################################
543##
544#F GoodSemigroupBySmallElements(M)
545## Constructs good semigroup from a set of small elements
546###############################################################
547InstallGlobalFunction(GoodSemigroupBySmallElements, function(X)
548  local M, C, c, sm;
549  if not(IsRectangularTable(X)) and ForAll(X, x->Length(x)=2) then
550    Error("The argument must be a list of pairs of positive integers");
551  fi;
552
553  if not(ForAll(X, x->IsInt(x[1]) and IsInt(x[2]) and x[1]>=0 and x[2]>=0)) then
554    Error("The argument must be a list of pairs of positive integers");
555  fi;
556
557  if not(RepresentsSmallElementsOfGoodSemigroup(X)) then
558    Error("This set is not the set of small elements of a good semigroup");
559  fi;
560
561  C:=[Maximum(List(X,x->x[1])),Maximum(List(X,x->x[2]))];
562
563  #now we see if C is the minimum conductor
564  c:=C;
565  sm:=ShallowCopy(X);
566  while ((c-[0,1]) in sm) or ((c-[1,0]) in sm) do
567    if ((c-[0,1]) in sm) and ((c-[1,0]) in sm) then #c-[1,1] also in sm
568      c:=c-[1,1];
569    elif c-[0,1] in sm then
570      c:=c-[0,1];
571    else
572      c:=c-[1,0];
573    fi;
574  od;
575  if C<>c then #small elements must be redefined
576    Info(InfoNumSgps,2,"Conductor redefined");
577    sm:=Filtered(X, x->x[1]<=c[1] and x[2]<=c[2]);
578  fi;
579
580  M:=Objectify(GoodSemigroupsType, rec());
581  #SetGenerators(M,gen);
582  SetConductor(M,c);
583  SetSmallElements(M,sm);
584  return M;
585end);
586
587###############################################################
588##
589#F ArfGoodSemigroupClosure(M)
590## Constructs Arf good semigroup closure of M
591###############################################################
592InstallGlobalFunction(ArfGoodSemigroupClosure,function(s)
593   local CompatibilityLevelOfMultiplicitySequences, s1, s2, t1, t2, sm, sma, c, included, i, cand, ca, c1, c2, k, seq1, seq2, tail1, tail2, car;
594
595  CompatibilityLevelOfMultiplicitySequences:=function(M)
596            local ismultseq, k, s, max, D, i,j, inarf;
597            # tests whether x is in the Arf semigroup with multiplicity
598            # sequence j
599            inarf:=function(x,j)
600                local l;
601                if x>Sum(j) then
602                return true;
603                fi;
604                if x=0 then
605                return true;
606                fi;
607                if x<j[1] then
608                return false;
609                fi;
610                l:=List([1..Length(j)], i-> Sum(j{[1..i]}));
611                return x in l;
612            end;
613
614            # tests if m is a multiplicity sequence
615            ismultseq := function(m)
616                local n;
617                n:=Length(m);
618                return ForAll([1..n-1], i-> inarf(m[i], m{[i+1..n]}));
619            end;
620
621            if not(IsTable(M)) then
622                Error("The first argument must be a list of multiplicity sequences");
623            fi;
624
625            if Length(M)<>2 then
626                Error("We are so far only considering Arf good semigroups in N^2");
627            fi;
628
629            if not(ForAll(Union(M), IsPosInt)) then
630                Error("The first argument must be a list of multiplicity sequences");
631            fi;
632
633            if not(ForAll(M, ismultseq)) then
634                Error("The first argument must be a list of multiplicity sequences");
635            fi;
636
637            s:=[];
638            max:= Maximum(List(M, Length));
639
640            for i in [1..2] do
641                s[i]:=[];
642                for j in [1..Length(M[i])] do
643                s[i][j]:=First([j+1..Length(M[i])], k-> M[i][j]=Sum(M[i]{[j+1..k]}));
644                if s[i][j]=fail then
645                    s[i][j]:=M[i][j]-Sum(M[i]{[j+1..Length(M[i])]})+Length(M[i])-j;
646                else
647                    s[i][j]:=s[i][j]-j;
648                fi;
649                od;
650            od;
651            for i in [1..2] do
652                s[i]:=Concatenation(s[i],List([Length(s[i])+1..max],_->1));
653            od;
654
655            D:=Filtered([1..max], j->s[1][j]<>s[2][j]);
656            k:=[];
657            if D=[] then
658                k:=infinity;
659            else
660                k:=Minimum(Set(D, j->j+Minimum(s[1][j],s[2][j])));
661            fi;
662            return k;
663  end;
664
665  if not(IsGoodSemigroup(s)) then
666    Error("The argument must be a good semigroup");
667  fi;
668
669
670
671  sm := SmallElementsOfGoodSemigroup(s);
672  c:= Conductor(s);
673  s1:=Set(sm, x->x[1]);
674  s2:=Set(sm, x->x[2]);
675  s1:=NumericalSemigroupBySmallElements(s1);
676  s2:=NumericalSemigroupBySmallElements(s2);
677  t1:=ArfNumericalSemigroupClosure(s1);
678  t2:=ArfNumericalSemigroupClosure(s2);
679  c1:=Conductor(t1);
680  c2:=Conductor(t2);
681  seq1:=MultiplicitySequenceOfNumericalSemigroup(t1);
682  seq2:=MultiplicitySequenceOfNumericalSemigroup(t2);
683  k:=CompatibilityLevelOfMultiplicitySequences([seq1,seq2]);
684
685  t1:=Intersection([0..c[1]],t1);
686  t2:=Intersection([0..c[2]],t2);
687  ca:=[c1,c2];
688  i:=1;
689  included:=true;
690  while included do
691    if i>Length(t1) or i>Length(t2) then
692      i:=i-1;
693      sma:=List([1..i], i->[t1[i],t2[i]]);
694      return GoodSemigroupBySmallElements(sma);
695    fi;
696    if i>k+1 then
697      i:=i-1;
698      sma:=List([1..i], i->[t1[i],t2[i]]);
699      return GoodSemigroupBySmallElements(sma);
700    fi;
701    sma:=Union(List([1..i], i->[t1[i],t2[i]]), Cartesian(t1{[i+1..Length(t1)]}, t2{[i+1..Length(t2)]}));
702    if First(sm, x->not(x in sma))<> fail then
703      included:=false;
704    else
705        i:=i+1;
706    fi;
707  od;
708  i:=i-1;
709  tail1:=Intersection(t1{[i+1..Length(t1)]},[0..c[1]]);
710  tail2:=Intersection(t2{[i+1..Length(t2)]},[0..c[2]]);
711  car:=Cartesian(tail1,tail2);
712  sma:=Union(List([1..i], i->[t1[i],t2[i]]), car);
713
714  return GoodSemigroupBySmallElements(sma);
715end);
716
717InstallMethod(ArfClosure,
718"Computes the Arf closure of a good semigroup",
719[IsGoodSemigroup],
720  ArfGoodSemigroupClosure
721);
722
723###############################################################
724##
725#F MaximalElementsOfGoodSemigroup(M)
726## returns the set of maximal elements of M
727###############################################################
728InstallGlobalFunction(MaximalElementsOfGoodSemigroup,function(g)
729  local sm;
730  if not(IsGoodSemigroup(g)) then
731    Error("The argument must be a good semigroup");
732  fi;
733
734  sm:=SmallElements(g);
735  return Filtered(Difference(sm,[Conductor(g)]), x->not(ForAny(sm,
736      y->((y[1]=x[1] and y[2]>x[2]) or (y[1]>x[1] and y[2]=x[2])))));
737end);
738
739###############################################################
740##
741#F IrreducibleMaximalElementsOfGoodSemigroup(M)
742## returns the set of irreducible maximal elements of M
743###############################################################
744InstallGlobalFunction(IrreducibleMaximalElementsOfGoodSemigroup,
745function(g)
746  local mx;
747  mx:=MaximalElementsOfGoodSemigroup(g);
748  if Length(mx)=1 then
749    return mx;
750  fi;
751  return Filtered(Difference(mx,[[0,0]]), x->not(ForAny(Difference(mx,[[0,0]]), y->y<>x and (y[1]<=x[1]) and (y[2]<=x[2]) and ((x-y) in mx))));
752end);
753
754###############################################################
755##
756#F GoodSemigroupByMaximalElements(S1,S2,mx,c)
757## returns the good semigroup determined by removing from
758## S1 x S2 the set of points "above" a maximal element; c is
759## the conductor
760###############################################################
761InstallGlobalFunction(GoodSemigroupByMaximalElements,
762function(s1,s2,mx,c)
763  local l1,l2, m1,m2,c1,c2,q, v, cc, g1,g2;
764
765  if not(IsNumericalSemigroup(s1)) then
766    Error("The first argument must be a numerical semigroup");
767  fi;
768  if not(IsNumericalSemigroup(s2)) then
769    Error("The second argument must be a numerical semigroup");
770  fi;
771
772  l1:=List(mx,x->x[1]);
773  l2:=List(mx,x->x[2]);
774  q:=c;
775  # removed because this is true only for curves
776  #if ForAny(mx, x-> not(q-x in mx)) then
777  #  Error("There is no symmetry in the third argument");
778  #fi;
779  g1:=Intersection([0..q[1]+1],s1);
780  g2:=Intersection([0..q[2]+1],s2);
781  cc:=Cartesian(g1,g2);
782  return GoodSemigroupBySmallElements(Difference(cc,
783    Filtered(cc, x->ForAny(mx,
784      y->((y[1]=x[1] and x[2]>y[2]) or (x[1]>y[1] and y[2]=x[2]))))
785    ));
786end);
787
788
789###################################################
790##
791#M BelongsToGoodSemigroup
792## decides if a vector is in the semigroup
793##################################################
794InstallMethod(BelongsToGoodSemigroup,
795         "Tests if the vector is in the semigroup",
796         [IsHomogeneousList, IsGoodSemigroup], 50,
797  function(v, a)
798    local S,T,E,c,s,t,sprime, X, saturation, C,edge1,edge2, sm, edge;
799
800
801    # G is a set of points;
802    # computes the saturation wrt sum (cutted by the edges) and inf
803    saturation:=function(G)
804        local inf, O, OO, new,i,j, o;
805
806        inf:=function(u,v)
807            return [Minimum(u[1],v[1]),Minimum(u[2],v[2])];
808        end;
809
810        O:=G;
811        # sum saturation
812        repeat
813            new:=[];
814            for i in [1..Length(O)] do
815                for j in [i.. Length(O)] do
816                    o:=inf(C,O[i]+O[j]);
817
818                    if not(o in O) then
819                        Add(new, o);
820                    fi;
821                od;
822            od;
823            O:=Union(O,new);
824        until new=[];
825
826        O:=Union(O,[[0,0]]);
827        #inf saturation
828        repeat
829            new:=[];
830            for i in [1..Length(O)] do
831                for j in [i.. Length(O)] do
832                    o:=inf(O[i],O[j]);
833                    if not(o in O) then
834                        Add(new, o);
835                    fi;
836                od;
837            od;
838            O:=Union(O,new);
839        until new=[];
840
841        # #good saturation x coordinate
842        # repeat
843        #     new:=First(O, x->x[1]<C[1] and ForAny(O,y-> x[1]=y[1] and x[2]<y[2] and not(ForAny(O, z->x[2]=z[2] and z[1]>x[1]))));
844        #     if new<>fail then
845        #         Add(O,[C[1],new[2]]);
846        #     fi;
847
848        # until new=fail;
849        # #good saturation y coordinate
850        # repeat
851        #     new:=First(O, x->x[2]<C[2] and ForAny(O,y-> x[2]=y[2] and x[1]<y[1] and not(ForAny(O, z->x[1]=z[1] and z[2]>x[2]))));
852        #     if new<>fail then
853        #         Add(O,[new[1],C[2]]);
854        #     fi;
855
856        # until new=fail;
857        return O;
858    end;
859
860    if Length(v)<>2 then
861      Error("The first argument must be a list with two integers (a pair)");
862    fi;
863    if not(ForAll(v, IsInt)) then
864      Error("The first argument must be a list with two integers (a pair)");
865    fi;
866    if IsGoodSemigroupByDuplication(a) then
867        S:=NumericalSemigroupGS(a);
868        E:=IdealGS(a);
869        if v[1]=v[2] then
870            return v[1] in S;
871        fi;
872
873        if v[1]<v[2] then
874            return (v[1] in E) and (v[2] in S);
875        fi;
876
877        if v[2]<v[1] then
878            return (v[2] in E) and (v[1] in S);
879        fi;
880    fi;
881    if IsGoodSemigroupByAmalgamation(a) then
882        S:=NumericalSemigroupGS(a);
883        E:=IdealGS(a);
884        c:=MorphismGS(a);
885        T:=UnderlyingNSIdeal(E);
886        s:=v[1];
887        t:=v[2];
888        if not(s in S) then
889            return false;
890        fi;
891        if not(t in T) then
892            return false;
893        fi;
894
895        if t=c*s then
896            return true;
897        fi;
898
899        if (c*s in E) and (t in E) then
900            return true;
901        fi;
902
903        if t< c*s then
904            return t in E;
905        fi;
906        if t>c*s then
907            if not(c*s in E) then
908                return false;
909            fi;
910            if t in E then
911                return true;
912            fi;
913            if not(IsInt(t/c)) then
914                return false;
915            fi;
916            return t/c in S;
917        fi;
918
919    fi;
920
921    if IsGoodSemigroupByCartesianProduct(a) then
922      return v[1] in NumericalSemigroupListGS(a)[1] and
923              v[2] in NumericalSemigroupListGS(a)[2];
924    fi;
925
926    if HasSmallElements(a) then
927        C:=Conductor(a);
928
929        if v[1]>=C[1] and v[2]>=C[2] then
930            return true;
931        fi;
932
933        sm:=SmallElements(a);
934
935        if v[1]>C[1] then
936            edge:=Filtered(sm,x->x[1]=C[1]);
937            return ForAny(edge, x->x[2]=v[2]);
938        fi;
939
940        if v[2]>C[2] then
941            edge:=Filtered(sm,x->x[2]=C[2]);
942            return ForAny(edge, x->x[1]=v[1]);
943        fi;
944
945        return v in sm;
946
947    fi;
948
949
950    if HasGenerators(a) then
951        X:=Generators(a);
952        C:=Conductor(a);
953
954        if v[1]>=C[1] and v[2]>=C[2] then
955            return true;
956        fi;
957
958        if not(HasSmallElements(a)) then
959            SetSmallElements(a,saturation(X));
960        fi;
961        sm:=SmallElements(a);
962
963        if v[1]>C[1] then
964            edge:=Filtered(sm,x->x[1]=C[1]);
965            return ForAny(edge, x->x[2]=v[2]);
966        fi;
967
968        if v[2]>C[2] then
969            edge:=Filtered(sm,x->x[2]=C[2]);
970            return ForAny(edge, x->x[1]=v[1]);
971        fi;
972
973        return v in sm;
974    fi;
975
976    return false;
977
978end);
979
980
981###################################################
982##
983#M BelongsToGoodSemigroup
984## decides if a vector is in the semigroup
985##################################################
986InstallMethod( \in,
987        "for good semigroups",
988        [ IsHomogeneousList, IsGoodSemigroup],
989        function( v, a )
990    return BelongsToGoodSemigroup(v,a);
991end);
992
993###################################################
994##
995#M Equality of good semigroups
996## decides if the two good semigroups are equal
997##################################################
998InstallMethod( \=,
999        "for good semigroups",
1000        [ IsGoodSemigroup, IsGoodSemigroup],
1001        function( a, b )
1002    return Conductor(a)=Conductor(b) and SmallElements(a)=SmallElements(b);
1003end);
1004
1005
1006 #############################################################################
1007 ##
1008 #M  ViewObj(S)
1009 ##
1010 ##  This method for good semigroups.
1011 ##
1012 #############################################################################
1013 InstallMethod( ViewObj,
1014         "Displays an Affine Semigroup",
1015         [IsGoodSemigroup],
1016         function( S )
1017         Print("<Good semigroup>");
1018
1019 end);
1020
1021 #############################################################################
1022 ##
1023 #M  ViewString(S)
1024 ##
1025 ##  This method for good semigroups.
1026 ##
1027 #############################################################################
1028 InstallMethod( ViewString,
1029         "String of an Affine Semigroup",
1030         [IsGoodSemigroup],
1031         function( S )
1032         return ("Good semigroup");
1033
1034 end);
1035
1036
1037 #############################################################################
1038 ##
1039 #M  Display(S)
1040 ##
1041 ##  This method for good  semigroups. ## under construction... (= View)
1042 ##
1043 #############################################################################
1044InstallMethod( Display,
1045         "Displays an Affine Semigroup",
1046         [IsGoodSemigroup],
1047         function( S )
1048         Print("<Good semigroup>");
1049 end);
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061#####################################################
1062##
1063#F ProjectionOfGoodSemigroup:=function(S,num)
1064## Given a good semigroup S it returns the num-th numerical semigroup projection
1065#####################################################
1066
1067InstallGlobalFunction(ProjectionOfGoodSemigroup,
1068function(S,num)
1069  local small,S1,S2;
1070    if not(IsGoodSemigroup(S)) then
1071        Error("The first argument must be a good semigroup");
1072    fi;
1073
1074    if not(num=1 or num=2)  then
1075        Error("The second argument must be 1 or 2");
1076    fi;
1077    small:=SmallElements(S);
1078
1079    if num=1 then
1080    return NumericalSemigroupBySmallElements(Set([1..Length(small)],i->small[i][1]));
1081    fi;
1082    if num=2 then
1083    return NumericalSemigroupBySmallElements(Set([1..Length(small)],i->small[i][2]));
1084    fi;
1085end);
1086
1087
1088#####################################################
1089##
1090#F GenusOfGoodSemigroup:=function(S)
1091## Given a good semigroup S it returns its genus
1092#####################################################
1093InstallGlobalFunction(GenusOfGoodSemigroup,
1094function(S)
1095    if not(IsGoodSemigroup(S)) then
1096        Error("The argument must be a good semigroup");
1097    fi;
1098    return Length(MaximalElementsOfGoodSemigroup(S))+Genus(ProjectionOfGoodSemigroup(S,1))+Genus(ProjectionOfGoodSemigroup(S,2));
1099end);
1100
1101
1102InstallMethod(Genus,"Genus for a good semigroup",[IsGoodSemigroup], GenusOfGoodSemigroup );
1103
1104#####################################################
1105##
1106#F LengthOfGoodSemigroup:=function(S)
1107## Given a good semigroup S it returns its length
1108#####################################################
1109InstallGlobalFunction(LengthOfGoodSemigroup,
1110function(S)
1111    local c;
1112    if not(IsGoodSemigroup(S)) then
1113        Error("The argument must be a good semigroup");
1114    fi;
1115    c:=Conductor(S);
1116    return c[1]+c[2]-GenusOfGoodSemigroup(S);
1117end);
1118
1119InstallMethod(Length,"for a good semigroup",[IsGoodSemigroup], LengthOfGoodSemigroup );
1120#####################################################
1121#F AperySetOfGoodSemigroup:=function(S)
1122## Given a good semigroup S it returns a list with the elements of the Apery Set
1123#####################################################
1124InstallGlobalFunction(AperySetOfGoodSemigroup,
1125function(S)
1126    local c,small,i,j,AddElementsOverTheConductor;
1127
1128    AddElementsOverTheConductor:=function(v,w)
1129        local ags,i,j;
1130
1131        if Length(v)=1 then
1132            ags:=[];
1133            for i in [v[1]..w[1]] do
1134                ags:=Union(ags,[[i]]);
1135            od;
1136
1137        else
1138            ags:=[];
1139            for i in AddElementsOverTheConductor(v{[1..Length(v)-1]},w{[1..Length(v)-1]}) do
1140                for j in [v[Length(v)]..w[Length(v)]] do
1141                    ags:=Union(ags,[Concatenation(i,[j])]);
1142                od;
1143            od;
1144        fi;
1145
1146        return ags;
1147    end;
1148
1149    if not(IsGoodSemigroup(S)) then
1150        Error("The argument must be a good semigroup");
1151    fi;
1152
1153    c:=Conductor(S);
1154    small:=SmallElements(S);
1155
1156    #First we add to the small elements the elements on the infinite lines that can to become elements of the AperySet.
1157
1158    for i in Filtered(small,j->j[1]=c[1]) do
1159        for j in [c[1]+1..c[1]+small[2][1]] do
1160            small:=Union(small,[[j,i[2]]]);
1161        od;
1162    od;
1163
1164    for i in Filtered(small,j->j[2]=c[2]) do
1165        for j in [c[2]+1..c[2]+small[2][2]] do
1166            small:=Union(small,[[i[1],j]]);
1167        od;
1168    od;
1169
1170    return  Filtered(Union(small,AddElementsOverTheConductor(c,c+small[2])),i-> not i-small[2] in small);
1171end);
1172
1173#####################################################
1174#F StratifiedAperySetOfGoodSemigroup:=function(S)
1175## Given a good semigroup S, it returns a list
1176#  where the elements are the levels of the AperySet
1177#####################################################
1178InstallGlobalFunction(StratifiedAperySetOfGoodSemigroup,
1179function(S)
1180local Dominance,ce,small,A,ags,temp,temp2;
1181
1182    Dominance:=function(v,w,cond)
1183        return v=w or ForAll([1..Length(v)], i->v[i]<w[i] or w[i]=cond[i]);
1184    end;
1185
1186    if not(IsGoodSemigroup(S)) then
1187        Error("The argument must be a good semigroup");
1188    fi;
1189
1190    small:=SmallElements(S);
1191    ce:=Conductor(S)+small[2];
1192    A:=AperySetOfGoodSemigroup(S);
1193    ags:=[];
1194    while A<>[] do
1195        temp:=Filtered(A,i->Length(Filtered(A,j->Dominance(i,j,ce)))=1);
1196        temp2:=Filtered(temp,i->Filtered(temp,j->j[1]=i[1] and j[2]>i[2])=[] or Filtered(temp,j->j[2]=i[2] and j[1]>i[1])=[]);
1197        ags:=Union(ags,[temp2]);
1198        A:=Filtered(A,i->not i in temp2);
1199    od;
1200    Info(InfoNumSgps,2,"Number of levels ", Length(ags));
1201    return ags;
1202end);
1203
1204
1205#####################################################
1206#F AbsoluteIrreduciblesOfGoodSemigroup:=function(S)
1207## Given a good semigroup S, the function returns the irreducible absolutes of S.
1208#  These are the elements that generates S as semiring.
1209#####################################################
1210InstallGlobalFunction(AbsoluteIrreduciblesOfGoodSemigroup,
1211function(S)
1212local ElementsOnTheEdge,TransformToInf,c,small,irrabsf,irrabsi,infi,edge,i;
1213
1214  if not(IsGoodSemigroup(S)) then
1215    Error("The argument must be a good semigroup");
1216  fi;
1217
1218  #This function check if an elements has a coordinate equal to the conductor.
1219  ElementsOnTheEdge:=function(vs,c)
1220    return vs[1]=c[1] or vs[2]=c[2];
1221  end;
1222
1223  #This function transforms the elements with a coordinate equal to the conductor in infinities.
1224  TransformToInf:=function(vs,c)
1225    local a,i;
1226    a:=ShallowCopy(vs);
1227      for i in Filtered([1..2],j->vs[j]=c[j]) do
1228        a[i]:=infinity;
1229      od;
1230    return a;
1231  end;
1232
1233  c:=Conductor(S);
1234  small:=Difference(SmallElements(S),[[0,0]]);
1235  #Computation of finite irreducible absolutes.
1236  irrabsf:=IrreducibleMaximalElementsOfGoodSemigroup(S);
1237
1238  #I take the elements of S different from the conductor but with a coordinate equal to this one.
1239  edge:=Filtered(small,k->k<>c and ElementsOnTheEdge(k,c));
1240
1241  infi:=[];
1242
1243  #Here I add to Infi the infinities in the square over the conductor (built considering the multiplicity)
1244  for i in [0..small[1][1]-1] do
1245    infi:=Union(infi,[[c[1]+i,infinity]]);
1246  od;
1247
1248  for i in [0..small[1][2]-1] do
1249    infi:=Union(infi,[[infinity,c[2]+i]]);
1250  od;
1251
1252  #Here I add the infinities under the conductor
1253  for i in edge do
1254    infi:=Union(infi,[TransformToInf(i,c)]);
1255  od;
1256
1257  #Computation of infinite irreducible absolutes
1258  irrabsi:=Filtered(infi,i->Filtered(small,j->i-j in infi)=[]);
1259
1260  return  Union(irrabsi,irrabsf);
1261end);
1262
1263
1264#####################################################
1265#F TracksOfGoodSemigroup:=function(S)
1266## Given a good semigroup S, the function returns the tracks of S.
1267#####################################################
1268InstallGlobalFunction(TracksOfGoodSemigroup,
1269function(S)
1270local CompareGS,MinimumGS,I,RemoveLabels,GluePieceOfTrack,ComputePieceOfTrack,T,temp;
1271
1272  if not(IsGoodSemigroup(S)) then
1273    Error("The argument must be a good semigroup");
1274  fi;
1275
1276  CompareGS:=function(v,w)
1277
1278    return ForAll([1..Length(v)], i->v[i]<=w[i]);
1279  end;
1280
1281  MinimumGS:=function(v,w)
1282    return List([1..Length(v)],i->Minimum(v[i],w[i]));
1283  end;
1284
1285  #It glues a new piece of track to an existing track. T is the list of all piece of track. V is a list of two elements,
1286  # V[1] represents not complete tracks and V[2] the complete tracks.
1287  #The function add a new piece to the incomplete tracks and returns the updated V.
1288  GluePieceOfTrack:=function(T,V)
1289    local ags,temp,i,j;
1290    ags:=[[],[]];
1291    ags[2]:=ShallowCopy(V[2]);
1292    for i in V[1] do
1293      temp:=i[Length(i)];
1294      if temp="last" then
1295        ags[2]:=Union(ags[2],[i]);
1296      else
1297        for j in Filtered(T,k->k[1]=temp) do
1298          ags[1]:=Union(ags[1],[Concatenation(i,[j[2]])]);
1299        od;
1300      fi;
1301    od;
1302    return ags;
1303  end;
1304
1305  #This funcion compute all possibles piece of track of a good semigroups having irreducible absolutes I
1306  ComputePieceOfTrack:=function(I)
1307    local IsAPOT,MaximalRed,ags,first,last,i;
1308
1309    #It check if between two irreducible absolutes there is a piece of track. It check if  their minimum overcome
1310    #the maximum  in both direction or is equal to this one in entrambe le direzioni o coincide
1311    IsAPOT:=function(a,b)
1312        local min;
1313        if CompareGS(a[1],b[1]) or CompareGS(b[1],a[1]) then return false; else if a[1][1]>b[1][1] then return false; else
1314
1315        min:=MinimumGS(a[1],b[1]); return min[2]>=a[3] and min[1]>=b[2];
1316        fi; fi;
1317    end;
1318
1319    #If (x,y) is an irreducible absolute it returns (x1,y1), where (x,y1) is the greatest irr. abs. under (x,y)
1320    # and (x1,y) is the greatest irr. abs. on the left of (x,y).
1321    MaximalRed:=function(v,I)
1322      local Factorize,ind,temp,temp2,temp3;
1323
1324      Factorize:=function(n,l)
1325          local a,b,ags,i,c,j,a1;
1326
1327          if l=[] then
1328            return [];
1329
1330          else
1331            a1:=Filtered([1..Length(l)],i->l[i]<>infinity);
1332            a:=List(a1,k->l[k]);
1333            b:=FactorizationsIntegerWRTList(n,a);
1334            ags:=[];
1335              for i in b do
1336                c:=List([1..Length(l)],o->0);
1337                j:=1;
1338                while j<=Length(a1) do
1339                  c[a1[j]]:=i[j]; j:=j+1;
1340                od;
1341                ags:=Union(ags,[c]);
1342              od;
1343            return ags;
1344          fi;
1345
1346      end;
1347
1348      if not v in I then
1349      return [0,0];
1350
1351      else
1352        if infinity in v then
1353        temp3:=[0,0];
1354        ind:=First([1,2],i->v[i]<>infinity);
1355        temp3[3-ind]:=0;
1356        temp:=Filtered(I,i->i[ind]<v[ind]);
1357        temp2:=List(List(Factorize(v[ind],List(temp,i->i[ind])),j->Sum(List([1..Length(j)],k->j[k]*temp[k]))),k1->k1[3-ind]);
1358
1359          if temp2=[] then
1360          temp3[ind]:=0;
1361          return Reversed(temp3);
1362
1363          else
1364          temp3[ind]:=Maximum(temp2);
1365          return Reversed(temp3);
1366          fi;
1367        else
1368        temp3:=[0,0];
1369
1370          for ind in [1,2] do
1371          temp:=Filtered(I,i->i[ind]<v[ind]);
1372          temp2:=List(List(Factorize(v[ind],List(temp,i->i[ind])),j->Sum(List([1..Length(j)],k->j[k]*temp[k]))),k1->k1[3-ind]);
1373
1374            if temp2=[] then
1375            temp3[ind]:=0;
1376            else
1377            temp3[ind]:=Maximum(temp2);
1378            fi;
1379          od;
1380        return Reversed(temp3);
1381        fi;
1382      fi;
1383    end;
1384
1385
1386    #If (x,y) is an irreducible absolute it returns ((x,y),x1,y1), where (x,y1) is the greatest irr. abs. under (x,y)
1387    # and (x1,y) is the greatest irr. abs. on the left of (x,y).
1388    I:=List(I,i->Concatenation([i],MaximalRed(i,I)));
1389
1390    #I add all the Piece Of Track
1391    ags:=List(Filtered(Cartesian(I,I),i->IsAPOT(i[1],i[2])),k->[k[1][1],k[2][1]]);
1392
1393    #I add the point of start and the point of end
1394    first:=Filtered(I,i->i[2]=0);
1395    last:=Filtered(I,i->i[3]=0);
1396
1397    for i in first do
1398    ags:=Union(ags,[["first",i[1]]]);
1399    od;
1400
1401    for i in last do
1402    ags:=Union(ags,[[i[1],"last"]]);
1403    od;
1404
1405    return ags;
1406  end;
1407
1408  #This function removes the label "first" and "last" in the tracks.
1409  RemoveLabels:=function(T)
1410    return List(T,i->Filtered(i,j->j<>"last" and j<>"first"));
1411  end;
1412
1413  I:=AbsoluteIrreduciblesOfGoodSemigroup(S);
1414  T:=ComputePieceOfTrack(I);
1415
1416  #The idea is to create the list of all tracks, adding one by one the piece of tracks in all possible way, reccalling
1417  #GluePieceOfTrack until all possible track are completed (V[1]=[])
1418  temp:=[[["first"]],[]];
1419  temp:=GluePieceOfTrack(T,temp);
1420
1421  while temp[1]<>[] do
1422  temp:=GluePieceOfTrack(T,temp);
1423  od;
1424
1425  return RemoveLabels(temp[2]);
1426
1427end);
1428
1429###############################################################
1430##
1431#P IsLocal(S)
1432## Determines if S is local
1433###############################################################
1434
1435InstallMethod(IsLocal,
1436"Determines if the good semigroup is local",
1437[IsGoodSemigroup], function(S)
1438local small;
1439small:=Difference(SmallElements(S),[[0,0]]);
1440return ForAll([1..Length(small)],i->small[i][1]<>0);
1441end);
1442
1443###############################################################
1444##
1445#A Multiplicity(S)
1446## Determines the multiplicity of S
1447###############################################################
1448
1449InstallMethod(Multiplicity,
1450"Returns the multiplicity of a local good semigroup",
1451[IsGoodSemigroup], function(S)
1452local small;
1453if not(IsLocal(S)) then
1454  Error("The good semigroup must be local");
1455fi;
1456
1457small:=SmallElements(S);
1458return small[2];
1459end);
1460
1461
1462