### skew braces
BindGlobal("SkewbraceFamily", NewFamily("SkewbraceFamily"));
#InstallValue(SkewbraceType, NewType(SkewbraceFamily, IsSkewbrace));

InstallMethod(Skewbrace, "for a list of pairs of elements in a group", [IsList], 
function(p)
  local add, mul, per, fam, obj, gens;

  ### <add> is (a permutation representation of) the additive group of the brace
  ### <mul> is the multiplicative group of the brace, 

  add := List(p, x->x[1]); 
  per := Sortex(add);
  mul := Permuted(List(p, x->x[2]), per);
 
  fam := NewFamily("SkewbraceElmFamily", IsSkewbraceElm, IsMultiplicativeElementWithInverse and IsAdditiveElementWithInverse);
  fam!.DefaultType := NewType(fam, IsSkewbraceElmRep);

  obj := Objectify(NewType(CollectionsFamily(fam), IsSkewbrace and IsAttributeStoringRep), rec());
  fam!.Skewbrace := obj;

  SetSize(obj, Size(add)); 
  SetSkewbraceAList(obj, add);
  SetSkewbraceMList(obj, mul);

  gens := SmallGeneratingSet(Group(add));
  if gens = [] then
    SetUnderlyingAdditiveGroup(obj, Group(()));
  else
    SetUnderlyingAdditiveGroup(obj, Group(gens));
  fi;

  gens := SmallGeneratingSet(Group(mul));
  if gens = [] then
    SetUnderlyingMultiplicativeGroup(obj, Group(()));
  else
    SetUnderlyingMultiplicativeGroup(obj, Group(gens));
  fi;

  return obj;

end);

# If <obj> is a classical brace, this function returns the id of the skew brace <obj>
InstallMethod(IdBrace, "for a skew brace", [IsSkewbrace], function(obj)
  if not IsClassical(obj) then
    Error("the skew brace is not classical\n");
  fi;
  return [Size(obj), First([1..NrSmallBraces(Size(obj))], k->IsomorphismSkewbraces(obj, SmallBrace(Size(obj),k)) <> fail)];
end);

# This function returns the id of the skew brace <obj>
InstallMethod(IdSkewbrace, "for a skew brace", [IsSkewbrace], function(obj)
  return [Size(obj), First([1..NrSmallSkewbraces(Size(obj))], k->IsomorphismSkewbraces(obj, SmallSkewbrace(Size(obj),k)) <> fail)];
end);

InstallMethod(AutomorphismGroup, "for a skew brace", [IsSkewbrace], function(obj)
  local f,s,lst,aut,add,is_homomorphism;

  is_homomorphism := function(f, dom, codom)
    local x,y,fx,fy,fxy;
    for x in dom do
      for y in dom do
        fx := Image(f, FromSkewbrace2Add(dom, [x])[1]);
        fy := Image(f, FromSkewbrace2Add(dom, [y])[1]);
        fxy := Image(f, FromSkewbrace2Add(dom, [x*y])[1]);
        if not FromAdd2Skewbrace(codom, [fx])[1]*FromAdd2Skewbrace(codom, [fy])[1] = FromAdd2Skewbrace(codom, [fxy])[1] then
          return false;
        fi;
      od;
    od;
    return true;
  end;

  lst := [];
  add := UnderlyingAdditiveGroup(obj);
  aut := AutomorphismGroup(add);

  for f in aut do
    if is_homomorphism(f, obj, obj) then
      Add(lst, f);
    fi;
  od;

  s := Subgroup(aut, lst); 
  SetIsAutomorphismGroupOfSkewbrace(s, true);
  SetIsAutomorphismGroup(s, true);
	SetAutomorphismDomain(s, obj);

  return s;
end);

InstallMethod(ImageElm, "for an automorphism and a skew brace element", [ IsGroupHomomorphism, IsSkewbraceElm ], function(f, x)
	local obj;
	obj := FamilyObj(x)!.Skewbrace;
	if Source(f) = UnderlyingAdditiveGroup(obj) then 
		return FromAdd2Skewbrace(obj, [Image(f, x![1])])[1];
  else
		Error(x, " is not in ", obj);
  fi; 
end);

InstallGlobalFunction(SkewbraceElm, function(obj, x)
  if x in SkewbraceAList(obj) then
    return SkewbraceElmConstructor(obj, x);
  else
    Error("the permutation does not belong to the additive group of the skew brace\n");
  fi;
end);

InstallGlobalFunction(IsSkewbraceHomomorphism, function(f, obj1, obj2)
  local a, b, c, x, y, z;
  for x in obj1 do
    a := x![1];
    for y in obj1 do
      b := y![1]; 
      z := x*y;
      c := z![1];
      if SkewbraceElm(obj2, Image(f, c)) <> SkewbraceElm(obj2, Image(f, a))*SkewbraceElm(obj2, Image(f, b)) then
        return false;
      fi;
    od;
  od;
  return true;
end);

# This function returns an isomorphism between the finite skew braces
# <obj1> and <obj2> 
# If <obj1> and <obj2> are not isomorphic the function returns fail
InstallGlobalFunction(IsomorphismSkewbraces, function(obj1, obj2)
  local f, ab1, ab2;

  if Size(obj1) <> Size(obj2) then
    return fail;
  fi;

  ab1 :=  UnderlyingAdditiveGroup(obj1);
  ab2 :=  UnderlyingAdditiveGroup(obj2);

  for f in AllInjectiveHomomorphisms(ab1, ab2) do
    if IsSkewbraceHomomorphism(f, obj1, obj2) then
      return f;
    fi;
  od;
  return fail;
end);



InstallMethod(SkewbraceElmConstructor, "for a skew brace and an underlying permutation", [ IsSkewbrace, IsPerm ], 
function( obj, perm )
  return Objectify(ElementsFamily(FamilyObj(obj))!.DefaultType, [ perm ]);
end);

InstallMethod( \<,
    "for two elements of a skew brace",
    IsIdenticalObj, [ IsSkewbraceElm, IsSkewbraceElm ],
    function( x, y )
      return x![1] < y![1];
end);

InstallMethod( \+,
    "for two elements of a skew brace",
    IsIdenticalObj, [ IsSkewbraceElm, IsSkewbraceElm ],
    function( x, y )
      local fam;
      fam := FamilyObj(x);
      return SkewbraceElmConstructor(fam!.Skewbrace, x![1] * y![1]);
end);

InstallMethod( \=,
    "for two elements of a skew brace",
    IsIdenticalObj, [ IsSkewbraceElm, IsSkewbraceElm ],
    function( x, y )
      return x![1] = y![1];
end);

InstallMethod( \*,
    "for two elements of a skew brace",
    IsIdenticalObj, [ IsSkewbraceElm, IsSkewbraceElm ],
    function( x, y )
      local i, j, obj, mul, add;

      obj := FamilyObj(x)!.Skewbrace;
      add := SkewbraceAList(obj);
      mul := SkewbraceMList(obj);

      i := Position(add, x![1]);
      j := Position(add, y![1]);
      return SkewbraceElmConstructor(obj, add[Position(mul, mul[i]*mul[j])]);
end);

InstallMethod(AdditiveInverseOp, 
    "for an element of a skew brace",
    [ IsSkewbraceElm ],
    function( x )
      return SkewbraceElmConstructor(FamilyObj(x)!.Skewbrace, Inverse(x![1]));
end);

InstallMethod(InverseOp, 
    "for an element of a skew brace",
    [ IsSkewbraceElm ],
    function( x )
      local i, obj, mul, add;

      obj := FamilyObj(x)!.Skewbrace;
      add := SkewbraceAList(obj);
      mul := SkewbraceMList(obj);

      i := Position(add, x![1]);
      return SkewbraceElmConstructor(obj, add[Position(mul, mul[i]^-1)]);
end);

InstallMethod(ZeroOp, 
    "for an element of a skew brace",
    [ IsSkewbraceElm ],
    function( x )
      return SkewbraceElmConstructor(FamilyObj(x)!.Skewbrace, () );
end);

InstallMethod(OneOp, 
    "for an element of a skew brace",
    [ IsSkewbraceElm ],
    function( x )
      return SkewbraceElmConstructor(FamilyObj(x)!.Skewbrace, () );
end);

InstallMethod(ZeroOp, 
    "for an element of a skew brace",
    [ IsSkewbraceElm ],
    function( x )
      return SkewbraceElmConstructor(FamilyObj(x)!.Skewbrace, () );
end);

InstallMethod(ZeroImmutable, 
    "for a skew brace",
    [ IsSkewbrace ],
    function( obj )
      return SkewbraceElmConstructor( obj, () );
end);

InstallMethod(Representative, 
    "for a skew brace",
    [ IsSkewbrace ],
    function( obj )
      return ZeroImmutable( obj );
end);

InstallMethod(Random,
    "for a skew brace",
    [ IsSkewbrace ],
    function( obj )
      return SkewbraceElmConstructor(obj, Random(SkewbraceAList(obj)));
end);

InstallMethod(Enumerator,
    "for a skew brace",
    [ IsSkewbrace ],
    AsList);

InstallMethod(AsList, 
    "for a skew brace",
    [ IsSkewbrace ],
    function( obj )
      return List( SkewbraceAList(obj), x->SkewbraceElmConstructor( obj, x ) );
end);

InstallMethod(ViewObj, "for skew braces", [ IsSkewbrace ],
function(obj)
  if HasIdBrace(obj) or IsClassical(obj) then
    Print("<brace of size ", Size(obj), ">");
  else
    Print("<skew brace of size ", Size(obj), ">");
  fi;
end);

InstallMethod(ViewObj, "for skew brace elements", [ IsSkewbraceElm ],
function(x)
  Print("<", x![1], ">");
end);

InstallMethod(PrintObj, "for skew braces", [ IsSkewbrace ],
function(obj)
  if HasIdBrace(obj) then
    Print( "SmallBrace(", IdBrace(obj)[1], ",", IdBrace(obj)[2], ")");
  elif HasIdSkewbrace(obj) then
    Print( "SmallSkewbrace(", IdSkewbrace(obj)[1], ",", IdSkewbrace(obj)[2], ")");
  else
    Print("<skew brace of size ", Size(obj), ">");
  fi;
end);

#### This function returns true is the <brace> is a two-sided brace
#### This means: (a+b)c+c=ac+bc
#### Equivalent condition: l(a+b)(c)+c=l(a)(c)+l(b)(c), where l(a)(b)=ab-a
InstallMethod(IsTwoSided, "for a skew brace", [IsSkewbrace], 
function(obj)
  local a, b, c;
  for a in AsList(obj) do 
    for b in AsList(obj) do
      for c in AsList(obj) do 
        if (a+b)*c <> a*c-c+b*c then
          return false;
        fi;
      od;
    od;
  od;
  return true;
end);

InstallMethod(SmallSkewbrace, "for a list of integers", [IsInt, IsInt],
function(size, number)
  local obj, known, implemented, dir, filename, add, mul, l, n;

	# If <size> is a prime number, the brace is trivial
  if Gcd(size, Phi(size))=1 then
    if number=1 then
      return TrivialBrace(CyclicGroup(IsPermGroup, size));
    else
      Error("there is only one brace of size ", size, "\n");
    fi;
  fi;

 	# If <size> is the square of a prime, braces are known
	if IsPrime(Sqrt(size)) then
		return BraceP2(size, number);
	fi;

  known := IsBound(NCBRACES[size]);
  if not known then
    dir := DirectoriesPackageLibrary("YangBaxter", "data")[1];
    filename := Filename(dir, Concatenation("SBsize", String(size), ".g"));
    if IsReadableFile(filename) then
      Read(filename);
    else
      Error("Skew braces of size ", size, " are not implemented");
    fi;
  fi;
  if number <= Size(NCBRACES[size]) then

    add := List(Group(NCBRACES[size][number].gadd));
    mul := List(Group(NCBRACES[size][number].gmul)); 

    Sortex(add);
    Sortex(mul);

    add := Permuted(add, Inverse(NCBRACES[size][number].p));
    mul := Permuted(mul, Inverse(NCBRACES[size][number].q));

    obj := Skewbrace(List([1..Size(add)], k->[add[k], mul[k]]));
    SetIdSkewbrace( obj, [ size, number ] );
    if size > 15 then
      Unbind(NCBRACES[size]);
    fi;
    return obj;
  else
    Error("there are just ", NrSmallSkewbraces(size), " skew braces of size ", size);
  fi;
end);

InstallMethod(SmallBrace, "for a list of integers", [IsInt, IsInt],
function(size, number)
  local obj, known, implemented, dir, filename, add, mul, l;

  # Braces of size p are known
  if IsPrime(size) then
    if number=1 then
        l := AsList(CyclicGroup(IsPermGroup, size));
        return Skewbrace(List([1..size], k->[l[k],l[k]]));
    else
        Error("there is only one brace of size ", size);
    fi;
  fi;

	# If <size> is a prime number, the brace is trivial
  # The same if <size> Burnside number
  if Gcd(size, Phi(size))=1 then
    if number=1 then
      return TrivialBrace(CyclicGroup(IsPermGroup, size));
    else
      Error("there is only one brace of size ", size, "\n");
    fi;
  fi;

 	# If <size> is the square of a prime, braces are known
	if IsPrime(Sqrt(size)) then
		return BraceP2(size, number);
	fi;

  known := IsBound(BRACES[size]);
  if not known then
    dir := DirectoriesPackageLibrary("YangBaxter", "data")[1];
    filename := Filename(dir, Concatenation("Bsize", String(size), ".g"));
    if IsReadableFile(filename) then
      Read(filename);
    else
      Error("Braces of size ", size, " are not implemented");
    fi;
  fi;
  if number <= Size(BRACES[size]) then

    add := List(Group(BRACES[size][number].gadd));
    mul := List(Group(BRACES[size][number].gmul)); 

    Sortex(add);
    Sortex(mul);

    add := Permuted(add, Inverse(BRACES[size][number].p));
    mul := Permuted(mul, Inverse(BRACES[size][number].q));

    obj := Skewbrace(List([1..Size(add)], k->[add[k], mul[k]]));
    SetIdBrace( obj, [ size, number ] );
    SetIsClassical(obj, true);
    if size > 15 then
      Unbind(BRACES[size]);
    fi;
    return obj;
  else
    Error("there are just ", NrSmallBraces(size), " braces of size ", size);
  fi;
end);

InstallMethod(TrivialSkewbrace, "for a group", [IsGroup],
function(group)
  local l, obj;
 
  if IsPermGroup(group) then
    l := AsList(group);
  else
    l := AsList(Image(IsomorphismPermGroup(group)));
  fi;
  obj := Skewbrace(List([1..Size(group)], k->[l[k],l[k]]));
  SetIsTrivialSkewbrace(obj, true);
  return obj;
end);

InstallMethod(TrivialBrace, "for a group", [IsGroup],
function(group)
  local l;
  if not IsAbelian(group) then
    Error("The group should be abelian");
  else
    return TrivialSkewbrace(group);
  fi;
end);

InstallMethod(IsSkewbraceImplemented, "for an integer", [IsInt],
function(size)
  local known, dir, filename;

  if Gcd(size, Phi(size))=1 or IsPrime(Sqrt(size)) then
    return true;
  fi;

  known := IsBound(NCBRACES[size]);
  if not known then
    dir := DirectoriesPackageLibrary("YangBaxter", "data")[1];
    filename := Filename(dir, Concatenation("SBsize", String(size), ".g"));
    if IsReadableFile(filename) then
      return true;
    else
      return false;
    fi;
  fi;
  return true;
end);

InstallMethod(IsBraceImplemented, "for an integer", [IsInt],
function(size)
  local known, implemented, dir, filename;

  if Gcd(size, Phi(size))=1 or IsPrime(Sqrt(size)) then
    return true;
  fi;

  known := IsBound(BRACES[size]);
  if not known then
    dir := DirectoriesPackageLibrary("YangBaxter", "data")[1];
    filename := Filename(dir, Concatenation("Bsize", String(size), ".g"));
    if IsReadableFile(filename) then
      return true;
    else
      return false;
    fi;
  fi;
  return true;
end);

#### This function returns the number of braces of size <n>
InstallGlobalFunction(NrSmallSkewbraces, 
function(size)
  local dir, filename;

  if Gcd(size, Phi(size))=1 then
    return 1;
  fi;

	if IsPrime(Sqrt(size)) then
		return 4;
	fi;

  if size <= 15 then
    return Size(NCBRACES[size]);
  else
    dir := DirectoriesPackageLibrary("YangBaxter", "data")[1];
    filename := Filename(dir, Concatenation("SBsize", String(size), ".g"));
    if IsReadableFile(filename) then
      Read(filename);
      return Size(NCBRACES[size]);
    else
      Error("Skew braces of size ", size, " are not implemented");
    fi;
  fi;
end);

InstallGlobalFunction(NrSmallBraces, 
function(size)
  local dir, filename;

  if Gcd(size, Phi(size))=1 then
    return 1;
  fi;

	if IsPrime(Sqrt(size)) then
		return 4;
	fi;

  if size <= 15 then
    return Size(BRACES[size]);
  else
    dir := DirectoriesPackageLibrary("YangBaxter", "data")[1];
    filename := Filename(dir, Concatenation("Bsize", String(size), ".g"));
    if IsReadableFile(filename) then
      Read(filename);
      return Size(BRACES[size]);
    else
      Error("Braces of size ", size, " are not implemented");
    fi;
  fi;
end);

InstallMethod(IsClassical, "for a skew brace", [ IsSkewbrace ], 
function(obj)
  return IsAbelian(Group(SkewbraceAList(obj)));
end);

InstallMethod(IsOfNilpotentType, "for a skew brace", [ IsSkewbrace ], 
function(obj)
  return IsNilpotent(UnderlyingAdditiveGroup(obj));
end);

InstallMethod(IsTrivialSkewbrace, "for a skew brace", [ IsSkewbrace ],
function(obj)
local a,b;
  for a in obj do
    for b in obj do
      if a*b <> a+b then
        return false;
      fi;
    od;
  od;
  return true;
end);

InstallMethod(Lambda, 
    "for two elements of a skew brace",
    IsIdenticalObj, [ IsSkewbraceElm, IsSkewbraceElm ],
    function( x, y )
      return -x+x*y;
end);

InstallMethod(InverseLambda, 
    "for two elements of a skew brace",
    IsIdenticalObj, [ IsSkewbraceElm, IsSkewbraceElm ],
    function( x, y )
      return Inverse(x)*(x+y);
end);

InstallMethod(Star, 
  "for two elements of a skew brace",
  IsIdenticalObj, [ IsSkewbraceElm, IsSkewbraceElm ],
  function( x, y )
    return -x+x*y-y;
end);

InstallMethod(Star,
  "for two subsets of elements of the same skew brace",
  [IsCollection, IsCollection], function(subset1, subset2)
  local elm, gens, group, obj; 
  elm := AsList(subset1)[1];
  obj := FamilyObj(elm)!.Skewbrace;
  gens := List(Cartesian(AsList(subset1), AsList(subset2)), x->Star(x[1], x[2])); 
  group := Group(List(gens, x->x![1]));
  return List(AsList(group), x->SkewbraceElmConstructor(obj, x));
end);

InstallMethod(Lambda2Permutation, "for an element of a skew brace", [ IsSkewbraceElm ], function(x)
  local obj, lst, p, k;
  obj := FamilyObj(x)!.Skewbrace;
  lst := AsList(obj);

  p := [1..Size(obj)];

  for k in [1..Size(obj)] do
    p[k] := Position(lst, Lambda(x, lst[k]));
  od;

  return PermList(p);
end);

InstallMethod(InverseBijective1Cocycle,
    "for a skew brace", 
    [ IsSkewbrace ],
    function(obj)
      local add, mul;
      add := SkewbraceAList(obj);
      mul := SkewbraceMList(obj);
      return MappingByFunction(Domain(AsList(obj)), Domain(mul), x->mul[Position(add, x![1])]);
end);

InstallMethod(Bijective1Cocycle,
    "for a skew brace", 
    [ IsSkewbrace ],
    function(obj)
       return InverseGeneralMapping(InverseBijective1Cocycle(obj));  
end);

InstallMethod(\in, "for a skew brace and a skew brace element", [ IsSkewbraceElm, IsSkewbrace ], 
function(x, obj)
  return x in AsList(obj);
end);

InstallMethod(Brace2YB, "for a skew brace", [ IsSkewbrace ], function(obj)
  return Skewbrace2YB(obj);
end);

InstallMethod(Skewbrace2YB, "for a skew brace", [ IsSkewbrace ], function(obj)
  local a, b, x, y, u, v, add, set, tmp_r, tmp_l, lperms, rperms, yb;

  add := AsList(obj);
  set := [1..Size(obj)];

  lperms := [];
  rperms := [];

  for a in AsList(obj) do

    tmp_l := [];
    tmp_r := [];

    for b in AsList(obj) do
      Add(tmp_l, Position(AsList(obj), Lambda(a,b))); 
      Add(tmp_r, Position(AsList(obj), Lambda(a,b)^(-1)*a*b)); 
    od;
    Add(lperms, tmp_l);
    Add(rperms, tmp_r);
  od;

  lperms := List(lperms, PermList);
  rperms := List(TransposedMat(rperms), PermList);

  yb := Permutations2YB(lperms, rperms); 
  SetLabels(yb, AsList(obj));

  return yb;
end);

InstallMethod(SkewbraceSubset2YB, "for a skew brace and a subset of a skew brace", [ IsSkewbrace, IsCollection ], function(obj, subset)
  local a, b, x, y, u, v, add, set, tmp_r, tmp_l, lperms, rperms, yb;

  set := [1..Size(subset)];

  lperms := [];
  rperms := [];

  for a in AsList(subset) do

    tmp_l := [];
    tmp_r := [];

    for b in AsList(subset) do
      Add(tmp_l, Position(AsList(subset), Lambda(a,b))); 
      Add(tmp_r, Position(AsList(subset), Lambda(a,b)^(-1)*a*b)); 
    od;
    Add(lperms, tmp_l);
    Add(rperms, tmp_r);
  od;

  lperms := List(lperms, PermList);
  rperms := List(TransposedMat(rperms), PermList);

  yb := Permutations2YB(lperms, rperms); 
  SetLabels(yb, AsList(subset));

  return yb;

end);

InstallMethod(Brace2CycleSet, "for a brace", [ IsSkewbrace ], function(obj)
  if not IsClassical(obj) then
    Error("this is not a classical brace\n");
  fi;
  return YB2CycleSet(Skewbrace2YB(obj));
end);


################################################################################
#
# SelectSmallBraces
# AllSmallBraces
# AllSmallSkewbraces
#
SelectSmallBraces := function( argl, skew )
local sizes, sizepos, size, conditions, name, availabilitycheck, nrfunc, constructfunc, 
  funcs, vals, pos, f, res, i, j, br;

if Length(argl) = 0 then
  Error("You must specify at least one argument\n");
fi;
  
# determine expected position of size(s) in the list of arguments 
if argl[1] = Size then
  sizepos := 2;
else
  sizepos := 1;
fi;

if IsPosInt( argl[ sizepos ] ) then     # only one size is given
  sizes := [ argl[ sizepos ] ];
elif IsList (argl[ sizepos ]) then          # list of sizes is given
  if ForAll( argl[ sizepos ], IsPosInt ) then
    sizes := argl[ sizepos ];
  else
    Error("The ", Ordinal( sizepos ), " argument is not a list of positive integers\n");
  fi;
else
  Error("The ", Ordinal( sizepos ), " argument is not a positive integer or a list\n");
fi;

conditions := argl{[ sizepos+1 .. Length(argl)]};
 
if skew then
  name := "skew braces";
  availabilitycheck := IsSkewbraceImplemented;
  nrfunc := NrSmallSkewbraces;
  constructfunc := SmallSkewbrace;
else
  name := "braces";
  availabilitycheck := IsBraceImplemented;
  nrfunc := NrSmallBraces;
  constructfunc := SmallBrace;
fi;

if not ForAll( sizes, availabilitycheck ) then
  Error( name, " of sizes ", Filtered( sizes, i -> not availabilitycheck(i) ), " are not implemented\n");
fi;

if IsBound(conditions[1]) and not IsFunction( conditions[1] ) then
  Error( "Expected a function, but got ", conditions[1], "\n");
fi;

funcs:=[];
vals:=[];

pos:=1;
while pos <= Length(conditions) do
  if IsFunction( conditions[pos] ) then
    # we have a function
    Add( funcs, conditions[pos] );
    if not IsBound( conditions[pos+1] ) or IsFunction( conditions[pos+1] ) then
      # if next entry is bound and is a function too, default is '[true]'
      Add( vals, [ true ] );
      pos := pos+1;
    else
      # otherwise, use next entry for the list of values
      if IsList( conditions[pos+1] ) then
        Add( vals, conditions[pos+1] );
      else  
        Add( vals, [ conditions[pos+1] ] );
      fi;        
      pos := pos+2;
      # look ahead and check that the value is either 
      # the last or followed by a function
      if IsBound( conditions[pos] ) and not IsFunction( conditions[pos] ) then
        Error( "Expected a function, but got ", conditions[pos], "\n");
      fi;
    fi;
  fi;
od;

res := [];

for size in sizes do
  for i in [1..nrfunc(size)] do
    br := constructfunc(size,i);
    if ForAll( [1..Length(funcs)], j -> funcs[j](br) in vals[j] ) then
      Add( res, br );
    fi;
  od;
od;
  
return res;
end;


InstallGlobalFunction(AllSmallBraces, 
function( arg )
  return SelectSmallBraces( arg, false );
end);

InstallGlobalFunction(AllSmallSkewbraces, 
function( arg )
  return SelectSmallBraces( arg, true );
end);

# The function transform a list of element of the
# multiplicative group of a skew brace into the additiv
# group
InstallGlobalFunction(FromMul2Add, function(obj, subset)
  local p;
  p := Bijective1Cocycle(obj);
  return List(List(subset, x->x^p), y->y![1]);
end);

InstallGlobalFunction(FromAdd2Mul, function(obj, subset)
  local q;
  q := InverseBijective1Cocycle(obj);
  return List(List(subset, x->SkewbraceElmConstructor(obj, x)), y->y^q);
end);

InstallGlobalFunction(FromSkewbrace2Add, function(obj, subset)
  return List(subset, x->x![1]);
end);

InstallGlobalFunction(FromSkewbrace2Mul, function(obj, subset)
  return FromAdd2Mul(obj, FromSkewbrace2Add(obj, subset));
end);

InstallGlobalFunction(FromAdd2Skewbrace, function(obj, subset)
  return List(subset, x->SkewbraceElmConstructor(obj, x));
end);

InstallGlobalFunction(FromMul2Skewbrace, function(obj, subset)
  return FromAdd2Skewbrace(obj, FromMul2Add(obj, subset));
end);

InstallGlobalFunction(BraceP2, function(size, number)
  local l, i, j, p, q, x, y, u, v, add, mul, tmp;

  add := NullMat(size,size);
  mul := NullMat(size,size);

  if number = 1 then
    return TrivialBrace(CyclicGroup(IsPermGroup, size));
  elif number = 2 then
    l := AsList(ZmodnZ(size));
    for i in [1..size] do
      x := l[i];
      for j in [1..size] do
        y := l[j];
        add[i][j] := Position(l, x+y);
        mul[i][j] := Position(l, x+y+Sqrt(size)*x*y);
      od;
    od;

    p := List(add, PermList);
    q := List(mul, PermList);

    return Skewbrace(List([1..size], k->[p[k],q[k]]));
  elif number=3 then
    return TrivialBrace(ElementaryAbelianGroup(IsPermGroup, size));
  elif number=4 then

    tmp := AsList(ZmodnZ(Sqrt(size))); 
    l := AsList(Cartesian(tmp,tmp));

    for i in [1..size] do
      for j in [1..size] do

        x := l[i][1];
        y := l[i][2];
        u := l[j][1];
        v := l[j][2];

        add[Position(l,[x,y])][Position(l,[u,v])] := Position(l,[x+u,y+v]);
        mul[Position(l,[x,y])][Position(l,[u,v])] := Position(l,[x+u+y*v,y+v]);
      od;
    od;

    p := List(add, PermList);
    q := List(mul, PermList);

    return Skewbrace(List([1..size], k->[p[k],q[k]]));
  else
    Error("there are four braces of size ", size, "\n");
  fi;
end); 

InstallMethod(DirectProductSkewbraces, "for two skew braces", [IsSkewbrace, IsSkewbrace], function(obj1, obj2)
  local l,u,v,a,b,c,d,add,mul;

  l := List(Cartesian(obj1,obj2), u->[u]);

  add := AsList(DirectProduct(UnderlyingAdditiveGroup(obj1),UnderlyingAdditiveGroup(obj2)));
  mul := NullMat(Size(l),Size(l));

  for u in l do
  	a := u[1];
    for v in l do
    	b := v[1];
      c := [a[1]*b[1],a[2]*b[2]];
      mul[Position(l,v)][Position(l,u)] := Position(l,[c]);
    od;
  od;

  mul := List(mul, PermList);
  return Skewbrace(List([1..Size(l)], x->[add[x],mul[x]]));
end);

InstallMethod( DirectProductOp,
    "for a list of skew braces, and a skew brace",
    [ IsList, IsSkewbrace ], 0,
    function( braces, b )

    local new, old, i;

    # Check the arguments.
    if not ForAll( braces, IsSkewbrace ) then
      TryNextMethod();
    fi;

    old := braces[1];

    for i in [ 2 .. Length(braces) ] do
      new := DirectProductSkewbraces(old,braces[i]);
      old := new;
    od;

    return old;

end);

InstallMethod(\/, "for a semigroup and an ideal",
[IsSkewbrace, IsSkewbrace and IsIdealInParent],
function(obj, ideal)
  return Quotient(obj,ideal);
end);

InstallMethod(IsBiSkewbrace, "for a skew brace", [ IsSkewbrace ],
function(obj)
  local a,b,c;
  for a in obj do
    for b in obj do
      for c in obj do
        if not a+(b*c)=(a+b)*Inverse(a)*(a+c) then
          return false;
        fi;
      od;
    od;
  od;
  return true;
end);

# returns all group homomorphism from the multiplicative group
# of <obj2> to the skew brace automorphism group of <obj1>
InstallMethod(SkewbraceActions, "for two skew braces", [ IsSkewbrace, IsSkewbrace ], 
function(obj2, obj1)
  return AllHomomorphisms(UnderlyingMultiplicativeGroup(obj2), AsGroup(AutomorphismGroup(obj1)));
end);

InstallMethod(EvaluateSkewbraceAction, "for two skew braces elements and a map", [ IsSkewbraceElm, IsSkewbraceElm, IsGeneralMapping ], 
function(b, a, f)
  local x, A, B;

  A := FamilyObj(a)!.Skewbrace;
  B := FamilyObj(b)!.Skewbrace;

  # WARNING: The skew brace automorphism group acts on the right!
  b := FromSkewbrace2Mul(B, [b])[1];
  x := Image(Inverse(Image(f, b)), a![1]);

  return FromAdd2Skewbrace(A, [x])[1];
end);

InstallMethod(SemidirectProduct, "for two skew braces and a map", [ IsSkewbrace, IsSkewbrace, IsGeneralMapping ], 
function(obj1, obj2, sigma)
  local l,u,v,a,b,c,d,add,mul;

	l := List(Cartesian(obj1,obj2), u->[u]);

	mul := NullMat(Size(l),Size(l));
	add := NullMat(Size(l),Size(l));

  # WARNING: The skew brace automorphism group acts on the right!
	for u in l do
		a := u[1];
		for v in l do
			b := v[1];
     c := [a[1]*EvaluateSkewbraceAction(a[2],b[1],sigma),a[2]*b[2]];
			mul[Position(l,v)][Position(l,u)] := Position(l,[c]);
			add[Position(l,v)][Position(l,u)] := Position(l,[[a[1]+b[1],a[2]+b[2]]]);
		od;
	od;

	mul := List(mul, PermList);
	add := List(add, PermList);

	return Skewbrace(List([1..Size(l)], x->[add[x],mul[x]]));

end);



