#############################################################################
##
#A  codecr.gi               GUAVA library                       Reinald Baart
#A                                                         Jasper Cramwinckel
#A                                                            Erik Roijackers
#A                                                                Eric Minkes
##
##  This file contains functions for calculating with code covering radii
##
## changes 10-2004:
## 1. CalculateLinearCodeCoveringRadius changed to a slightly faster
##    algorithm.
## 2. minor bug fix for ExhaustiveSearchCoveringRadius
## 2. minor bug fix for IncreaseCoveringRadiusLowerBound
##

########################################################################
##
#F  CoveringRadius( <code> )
##
##  Return the covering radius of <code>
##  In case a special algorithm for this code exist, call
##  it first.
##
##  Not useful for large codes.
##
##  That's why I changed it, see the manual for more details
##  -- eric minkes.
##


## Calculation is done in this function, instead of in the method, so that
## users can override the redundancy restriction if desired.  Note that
## this does not check the trivial cases checked in the method,

CalculateLinearCodeCoveringRadius := function( code )
  local H, wts,CLs,i,rho;
  if Redundancy(code) = 0 then
    return 0;
  else
#
#return Maximum( List( SyndromeTable( code ), i -> Weight( i[ 1 ] ) ) );
# (old version had line above in place of next 5)
    H:=CheckMat(code);
    CLs:=GuavaCosetLeadersMatFFE(H,LeftActingDomain(code));
    wts:=List([1..Length(CLs)],i->WeightVecFFE(CLs[i]));
    rho:=Maximum(wts);
    return rho;
  fi;
end;

InstallMethod(CoveringRadius, "method for linear code", true,
    [IsLinearCode], 0,
function( code )
    # call the special algorithm for this code, if it exists
    if HasSpecialCoveringRadius( code ) then
        code!.boundsCoveringRadius :=
          SpecialCoveringRadius( code ) ( code );
    fi;

    if Length( BoundsCoveringRadius( code ) ) = 1 then
        return code!.boundsCoveringRadius[ 1 ];
    else
        if Redundancy( code ) < 20 then
            code!.boundsCoveringRadius :=
              [ CalculateLinearCodeCoveringRadius( code ) ];
        else
            ##LR - this sets CR to an interval
            InfoCoveringRadius(
              "CoveringRadius: warning, the covering radius of \n",
              "this code cannot be computed straightforward. \n",
              "Try to use IncreaseCoveringRadiusLowerBound( <code> ).\n",
              "(see the manual for more details).\n",
              "The covering radius of <code> lies in the interval:\n" );
            return BoundsCoveringRadius( code );
        fi;
    fi;
    return code!.boundsCoveringRadius[ 1 ];
end);

# For small codes in large spaces, this may take a very long time
InstallMethod(CoveringRadius, "method for unrestricted code", true,
    [IsCode], 0,
function( code )
    local Code, vector, d, curmax, n, q, one, count, size, t, i, j,
            zero, large, gen;
    if IsLinearCode( code ) then
        return CoveringRadius( code );
    elif Length(code!.boundsCoveringRadius) = 1 then
        return code!.boundsCoveringRadius[1];
    elif HasSpecialCoveringRadius( code ) then
        code!.boundsCoveringRadius := SpecialCoveringRadius( code ) ( code );
    else
        q := Size(LeftActingDomain(code));
        n := WordLength(code);
        size := Size(code);
        one := One(LeftActingDomain(code));
        zero := Zero(LeftActingDomain(code));
        Code := VectorCodeword(AsSSortedList(code));
        vector := List([1..n], i-> zero);
        large := one;
        gen := Z(q);

        curmax := n;
        for t in Code do
            d := DistanceVecFFE(t, vector);
            if d < curmax then
                curmax := d;
            fi;
        od;
        for count in [2..q^n] do
            t := n;
            while vector[t] = large do
                vector[t] := zero;
                t := t - 1;
            od;
            if vector[t] = zero then
                vector[t] := gen;
            else
                vector[t] := vector[t] * gen;
            fi;
            t := 1;
            repeat
                d := DistanceVecFFE(Code[t], vector);
                t := t + 1;
            until d <= curmax or t > size;
            if d > curmax then
                curmax := n;
                for t in Code do
                    d := DistanceVecFFE(t, vector);
                    if d < curmax then
                        curmax := d;
                    fi;
                od;
            fi;
        od;
        code!.boundsCoveringRadius := [curmax];
    fi;
    return code!.boundsCoveringRadius[1];
end);


########################################################################
##
#F  BoundsCoveringRadius( <code> )
##
##  Find a lower and an upper bound for the covering radius of code.
##

InstallMethod(BoundsCoveringRadius, "method for unrestricted code", true,
    [IsCode], 0,
function(code)
    local bcr;
    if HasCoveringRadius(code) then
        code!.boundsCoveringRadius := [CoveringRadius(code)];
    elif not IsBound(code!.boundsCoveringRadius) then
        bcr := [GeneralLowerBoundCoveringRadius(code) ..
                GeneralUpperBoundCoveringRadius(code)];
        if Length(bcr) = 0 then
            code!.boundsCoveringRadius := [0,WordLength(code)];
        else
            code!.boundsCoveringRadius := bcr;
        fi;
    fi;
    return code!.boundsCoveringRadius;
end);


########################################################################
##
#F  SetBoundsCoveringRadius( <code>, <cr> )
##  SetBoundsCoveringRadius( <code>, <interval> )
##
##  Enable the user to set the covering radius (or bounds) him/herself.
##  Used to be SetCoveringRadius, before GAP4.

InstallOtherMethod(SetBoundsCoveringRadius,
    "method for unrestricted code, integer",
    true, [IsCode, IsInt], 0,
function(code, cr)
    SetCoveringRadius(code, cr);
    code!.boundsCoveringRadius := [cr];
end);

InstallMethod(SetBoundsCoveringRadius,
    "method for unrestricted code, interval",
    true, [IsCode, IsVector], 0,
function(code, cr)
    code!.boundsCoveringRadius := IntersectionSet(
            BoundsCoveringRadius( code ), cr );
    if Length( code!.boundsCoveringRadius ) = 0 then
        code!.boundsCoveringRadius := cr;
    fi;
    IsRange( code!.boundsCoveringRadius );
    if Length(code!.boundsCoveringRadius) = 1 then
        SetCoveringRadius(code, code!.boundsCoveringRadius[1]);
    fi;
end);


########################################################################
##
#F  IncreaseCoveringRadiusLowerBound(
##      <code> [, <stopdistance> ] [, <startword> ] )
##
## small bug fix 10-2004
##
InstallMethod(IncreaseCoveringRadiusLowerBound,
    "method for unrestricted code, stop distance, start vector",
    true, [IsCode, IsInt, IsVector], 0,
function ( code, stopdistance, startvector )

    local
          n,                 # length of the code
          k,                 # dimension of the code
          genmat,            # generator matrix of the code
          field,             # field of the code
          q,                 # the size of field
          fieldels,          # elements of field
          fieldzero,         # zero element of field
          nonzeroels,        # non-zero elements of field
          boundscr,          # current bounds on the covering radius
          lb,                # current achieved lower bound
          current,           # the element of field^n that we are
                             # currently checking
          cwcurrent,         # current in Codeword form
          distcurrenttocode, # the distance of current to the code
          currentchanged,    # did we change current during the loop ?
          counterchanged,    # number of changes we have made so far
          countertotal,      # number of iterations we have made so far
          h, i, j,           # indexes to make the first slice
          slice,             # the number of the current slice
          numberofslices,    # the elements of the code are handled in
                             # slices of 2^10 elements
          slicedim,          # the dimension of a slice
          slicesize,         # the size of a slice ( q^slicedim )
          index,             # to enumerate the codewords, the
                             # correct generator must be added
                             # index contains the generator number
          satisfied,         # are we satisfied with the results ?
          words,             # a list of codewords in the current slice
          wordslist0,        # a list of codewords with distance
                             # distcurrentocode + 0 from current
          wordslist1,        # a list of codewords with distance
                             # distcurrentocode + 1 from current
          word,              # a index for wordslist*
          coord,             # the coordinate where current will
                             # be changed
          staychance,        # chance that we stay at the same distance
          downchance,        # chance that we move closer to the code
          bestdist,          # best distance reached so far
          bestword,          # the corresponding word
          newelement,        # the new element for current[ coord ]
          newdist;           # the new distance of the changed current
                             # to the code

    # extract some code parameters
    n := WordLength( code );
    if IsLinearCode( code ) then
        k := Dimension( code );
    fi;

    field := LeftActingDomain( code );
    q := Size( field );

    # if we cannot compute the minimum distance of the code,
    # this algorithm will not be of much help either.
    # <lb> is a safe place to start searching
    lb := IntFloor( MinimumDistance( code ) / 2 );

    boundscr := BoundsCoveringRadius( code );
    if lb > boundscr[ 1 ] then
        code!.boundsCoveringRadius := Filtered( boundscr, n -> lb <= n );
        IsRange( code!.boundsCoveringRadius );
        boundscr := code!.boundsCoveringRadius;
    fi;

    if Length( boundscr ) = 1 then
        # if there is nothing to compute,
        # then just return the covering radius
        return boundscr[ 1 ];
    fi;

    # starting vector
#
    current := ShallowCopy(VectorCodeword( Codeword( startvector ) ));
# (old version has above in place of below)
#   current := Codeword( startvector );
    distcurrenttocode := MinimumDistance( code, Codeword( current) );
    bestdist := distcurrenttocode;
    bestword := ShallowCopy( current );

    # initialise some parameters and useful variables
    fieldels := AsSSortedList( field );
    fieldzero := Zero(field);
    nonzeroels := Difference( fieldels, [ fieldzero ] );

    if IsLinearCode( code ) then
        genmat := GeneratorMat( code );
        # try to make the size of a group codewords to be about
        # CRMemSize
        slicedim := LogInt( CRMemSize, q );
        numberofslices := Maximum( 1, q^( k-slicedim ) );
        slicesize := q^slicedim;
    fi;

    satisfied := false;
    currentchanged := true;
    counterchanged := 0;
    countertotal := 0;
    staychance := 10; # maybe make arguments of these
    downchance := 1;

    # the words array contains all codewords generated by
    # genmat[ 1 ] ... genmat[ slicedim ] ( or genmat[ k ], if
    # slicedim > k )

    if IsLinearCode( code ) then
        words := [ NullVector( n, field ) ];
        for i in [ 1 .. Minimum( slicedim, k ) ] do
            for j in [ 1 .. q^( i-1 ) ] do
                for h in [ 1 .. q-1 ] do
                    Add( words, words[ j ] + genmat[ i ] * nonzeroels[ h ] );
                od;
            od;
        od;
    else
        # if the code is non-linear, the
        # elements field is obligatory,
        # so it fits in memory.
        # no need for all the hassle as in the linear case,
        # but the disadvantage is that we can't handle
        # large non-linear codes.
        # but then again, GUAVA can't handle these anyway.
        words := VectorCodeword( AsSSortedList( code ) );
    fi;

    # start algorithm, stop when we are satisfied with the results
    # (either we have a coset leader of weight <stopweigth>,
    # or lb = ub)
    while not satisfied do
        countertotal := countertotal + 1;
        if countertotal mod 1000 = 0 then
            InfoCoveringRadius( "Number of runs: ",
                    countertotal,
                    "  best distance so far: ", bestdist, "\n" );
        fi;
        if currentchanged then
            # current word has changed, generate three lists of
            # codewords that have distance distcurrenttocode,
            # distcurrenttocode + 1 and distcurrenttocode + 2

            cwcurrent := Codeword( current );

            if distcurrenttocode > bestdist then
                bestdist := distcurrenttocode;
                bestword := ShallowCopy( current );
                InfoCoveringRadius(
                        "New best distance: ", bestdist, "\n" );
            fi;

            wordslist0 := [];
            wordslist1 := [];

            if IsLinearCode( code ) then
                for slice in [ 0 .. numberofslices-1 ] do
                    if slice > 0 then
                        i := k - slicedim - 1;
                        while EuclideanRemainder( slice, q^i ) <> 0 do
                            i := i - 1;
                        od;
                        index := slicedim + i + 1;
                        words := List( words, x -> x + genmat[ index ] );
                    fi;

                    Append( wordslist0,
                            Filtered( words, x ->
                                    DistanceVecFFE( x, current )
                                    = distcurrenttocode ) );
                    Append( wordslist1,
                            Filtered( words, x ->
                                    DistanceVecFFE( x, current )
                                    = distcurrenttocode + 1 ) );

                od;
            else
                wordslist0 := Filtered( words, x->
                                      DistanceVecFFE( x, current )
                                      = distcurrenttocode );
                wordslist1 := Filtered( words, x->
                                      DistanceVecFFE( x, current )
                                      = distcurrenttocode + 1 );
            fi;

            currentchanged := false;
            counterchanged := counterchanged + 1;
            if EuclideanRemainder( counterchanged, 100 ) = 0 then
                InfoCoveringRadius( "Number of changes: ",
                        counterchanged, "\n" );
            fi;
        fi;

        # pick a coordinate
        # the algorithm will look what happens if we change this coordinate
        coord := Random( [ 1 .. n ] );

        # the possible new element for this coordinate
        # is picked at random from the field elements
        newelement := Random( Difference( fieldels,
                              [ current[ coord ] ] ) );

        # check the new word against the codewords that are
        # at distance distcurrenttocode + 0 from current
        # the result can be:
        #   1) the distance to all words in wordslist0 is
        #      one more than distcurrenttocode
        #      (this is the situation that we hope for)
        #   2) there is at least one word in wordslist0 that
        #      has distance distcurrenttocode - 1 to the
        #      new current
        #   3) if the field is not GF(2):
        #      there is a word in wordslist0 that stays
        #      at the same distance
        newdist := distcurrenttocode + 1;
        for word in wordslist0 do
            if word[ coord ] <> current[ coord ] then
                if word[ coord ] = newelement then
                    newdist := distcurrenttocode - 1;
                else
                    newdist := distcurrenttocode;
                fi;
            fi;
        od;

        # only check against other words if the previous tests
        # did not fail
        if newdist > distcurrenttocode then

            # check the new word against the codewords that are at
            # distance distcurrenttocode + 1 from current
            # again, two results are possible
            #   1) all words in wordslist1 are at distance
            #      distcurrenttocode + 1 or + 2 (this is good)
            #   2) there is a word in wordslist1 that now has
            #      distance distcurrenttocode to current
            #      this means we did not find an improvement

            for word in wordslist1 do
                if word[ coord ] = newelement then
                    newdist := distcurrenttocode;
                fi;
            od;
        fi;

        if newdist > distcurrenttocode then
            # we found a new coset leader with larger weight

            # now change current
            current[ coord ] := newelement;
            currentchanged := true;
            distcurrenttocode := newdist;

            # also check whether the covering radius lower bound
            # can be increased, this is what the whole
            # algorithm is about !

            if distcurrenttocode > boundscr[ 1 ] then

                # write directly to the code to make the change
                # permanent, even if the user interrupted us
                code!.boundsCoveringRadius :=
                  Filtered( boundscr, x -> x >= distcurrenttocode );
                # make it a range together if possible
                IsRange( code!.boundsCoveringRadius );
                boundscr := code!.boundsCoveringRadius;

                # maybe we have reached the upper bound
                # then we can stop altogether !
                if Length( boundscr ) = 1 then
                    satisfied := true;
                fi;
            fi;
        elif newdist = distcurrenttocode then
            # the change to the word did not change the
            # distance to the code
            if Random( [ 1 .. 100 ] ) <= staychance then
                current[ coord ] := newelement;
                currentchanged := true;
            fi;
        else
            # make it a 1 in 100 chance to get closer anyway
            # because we do not want to get stuck in a
            # suboptimal coset
            if Random( [ 1 .. 100 ] ) <= downchance then
                current[ coord ] := newelement;
                currentchanged := true;
                distcurrenttocode := newdist;
            fi;
        fi;

        # maybe the distance of current to the code
        # is high enough for the user
        # then we should stop
        if distcurrenttocode = stopdistance then
            satisfied := true;
        fi;
    od;

    # return the new covering radius bounds, and a coset leader
    # that has weight equal to the lower bound
    return rec( boundsCoveringRadius := code!.boundsCoveringRadius,
                cosetLeader := Codeword( current ) );
end);

InstallOtherMethod(IncreaseCoveringRadiusLowerBound,
    "method for unrestricted code, starting vector",
    true, [IsCode, IsVector], 0,
function( code, startvector )
    # stopdistance = -1 is the default: never stop, unless the lower
    # bound meets the upper bound
    return IncreaseCoveringRadiusLowerBound( code, -1, startvector );
end);

InstallOtherMethod(IncreaseCoveringRadiusLowerBound,
    "method for unrestricted code, stopdistance",
    true, [IsCode, IsInt], 0,
function( code, stopdistance )
    local lb, current, distcurrenttocode;

    lb := IntFloor( MinimumDistance( code ) / 2 );
    current := RandomVector( WordLength( code ),
                             Random( [0..WordLength( code )] ),
                             LeftActingDomain( code ));
    distcurrenttocode := MinimumDistance( code,  Codeword(current) );
    while distcurrenttocode < lb do
        current := RandomVector( WordLength( code ),
                                 Random( [0..WordLength( code )] ),
                                 LeftActingDomain( code ));
        distcurrenttocode := MinimumDistance( code,  Codeword(current) );
    od;

    return IncreaseCoveringRadiusLowerBound( code, -1, current);
end);


InstallOtherMethod(IncreaseCoveringRadiusLowerBound,
    "method for unrestricted code", true, [IsCode], 0,
function( code )
    local lb, current, distcurrenttocode;

    lb := IntFloor( MinimumDistance( code ) / 2 );
    current := RandomVector( WordLength( code ),
                             Random( [0..WordLength( code )] ),
                             LeftActingDomain( code ));
#   distcurrenttocode := MinimumDistance( code, current );
#  (old version had above line)
    distcurrenttocode := MinimumDistance( code, Codeword(current) );
    while distcurrenttocode < lb do
        current := RandomVector( WordLength( code ),
                                 Random( [0..WordLength( code )] ),
                                 LeftActingDomain( code ));
        distcurrenttocode := MinimumDistance( code, Codeword(current) );
    od;

    return IncreaseCoveringRadiusLowerBound( code, -1, current);
end);


########################################################################
##
#F  ExhaustiveSearchCoveringRadius( <code> )
##
##  Try to compute the covering radius. Don't compute all coset
##  leaders, but increment the lower bound as soon as a coset leader
##  is found.
##

InstallMethod(ExhaustiveSearchCoveringRadius,
    "unrestricted code, boolean stopsoon",
    true, [IsCode,IsBool], 0,
function(C, stopsoon)
    if IsLinearCode(C) then
        return ExhaustiveSearchCoveringRadius(C, stopsoon);
    else
        Error("ExhaustiveSearchCoveringRadius: <code> must be a linear code");
    fi;
end);

InstallMethod(ExhaustiveSearchCoveringRadius, "linear code, boolean stopsoon",
    true, [IsLinearCode, IsBool], 0,
function(code, stopsoon)

    local k, n, i, j, lastone, zerofound, IsCosetLeader,
          lb, we, wd, vc, cont, codewords,
          leaderfound, allexamined, supp, elmsC, elms, len, one, zero,
          boundscr;

    IsCosetLeader := function( codewords, len, word, wt, one )
        local i, check, cw, wcw, j;
        check := true;
        i := 1;
        while i <= len and check do
            cw := codewords[ i ] + word;
            wcw := 0;
            for j in [ 1 .. Length( cw ) ] do
                if cw[ j ] = one then
                    wcw := wcw + 1;
                fi;
            od;
            if wcw < wt then
                check := false;
            fi;
            i := i + 1;
        od;
        return check;
    end;

    if Size( LeftActingDomain( code ) ) <> 2 then
        Error( "CoveringRadiusSearch: <code> must be a binary code" );
    fi;

    boundscr := BoundsCoveringRadius( code );
    if Length( boundscr ) = 1 then
        return boundscr[ 1 ];
    fi;

    lb := boundscr[ 1 ];
    n := WordLength( code );
    wd := WeightDistribution( code );
    elms := [];
    for i in [ 0 .. n ] do
        if wd[ i + 1 ] > 0 then
            elms[ i + 1 ] := ShallowCopy(AsSSortedList(
                                     ConstantWeightSubcode( code, i ) ));
        fi;
    od;

    for i in [ 1 .. n+1 ] do
        if IsBound( elms[ i ] ) then
            for j in [ 1 .. Length( elms[ i ] ) ] do
                elms[ i ][ j ] := VectorCodeword( elms[ i ][ j ] );
#mutable error on:
# C:=RandomLinearCode(10,5,GF(2));
# ExhaustiveSearchCoveringRadius(C,true);
            od;
        fi;
    od;

    # try to find a coset leader with weight > lb
    # if found, increase lb
    one := One(GF(2));
    zero := Zero(GF(2));
    cont := true;
    while cont do
        k := BoundsCoveringRadius(code)[ 1 ] + 1;
        InfoCoveringRadius( "Trying ", k, " ...\n" );
        codewords := [ NullVector(n, GF(2) ) ];
        for i in [ 1 .. Minimum( n, 2 * k - 1) ] do
            if wd[ i + 1 ] <> 0 then
                Append( codewords, elms[ i + 1 ] );
            fi;
        od;
        len := Length( codewords );

        vc := NullVector( n, GF(2) );
        for i in [ 1 .. k ] do
            vc[ i ] := one;
        od;
        lastone := k;
        allexamined := false;
        leaderfound := false;

        while not leaderfound and not allexamined do
            if not IsCosetLeader( codewords, len, vc, k, one ) then
                if lastone = n then
                    zerofound := false;
                    i := lastone - 1;
                    while i > n - k and vc[ i ] = one do
                        i := i - 1;
                    od;
                    if i = n - k then
                        allexamined := true;
                    else
                        j := i;
                        i := i + 1;
                        while vc[ j ] = zero do
                            j := j - 1;
                        od;
                        vc[ j ] := zero;
                        vc[ j + 1 ] := one;
                        j := j + 2;
                        if i <> j then
                            while i <= lastone do
                                vc[ j ] := one;
                                vc[ i ] := zero;
                                i := i + 1;
                                j := j + 1;
                            od;
                            lastone := j - 1;
                        else
                            lastone := n;
                        fi;
                    fi;
                else
                    vc[ lastone ] := zero;
                    lastone := lastone + 1;
                    vc[ lastone ] := one;
                fi;
            else
                leaderfound := true;
            fi;
        od;

        if leaderfound then
            code!.boundsCoveringRadius :=
              Filtered( code!.boundsCoveringRadius, x -> x >= k );
            if stopsoon then
                cont := false;
            fi;
        else
            code!.boundsCoveringRadius :=
              [ code!.boundsCoveringRadius[ 1 ] ];
            cont := false;
        fi;
    od;

    IsRange( code!.boundsCoveringRadius );
    return( code!.boundsCoveringRadius );
end);

InstallOtherMethod(ExhaustiveSearchCoveringRadius, "unrestricted code", true,
    [IsCode], 0,
function(C)
    return ExhaustiveSearchCoveringRadius(C, true);
end);


########################################################################
##
#F  CoveringRadiusLowerBoundTable
##

CoveringRadiusLowerBoundTable := [
    [ 3, 2, , , , , , , , ,                     # n = 13
       ,  , , , , , , , , ,
       ,  , , , , , , , , ,
       ,  , , , , , , , , ,
       ,  , , , , , , , ],
    [ 3, 3, , , , , , , , ,                     # n = 14
       ,  , , , , , , , , ,
       ,  , , , , , , , , ,
       ,  , , , , , , , , ,
       ,  , , , , , , , ],
    [ 4, 3, 3, , , , , , , ,                    # n = 15
       ,  ,  , , , , , , , ,
       ,  ,  , , , , , , , ,
       ,  ,  , , , , , , , ,
       ,  ,  , , , , , , ],
    [ 4, 4, 3, 3, , , , , , ,                   # n = 16
       ,  ,  ,  , , , , , , ,
       ,  ,  ,  , , , , , , ,
       ,  ,  ,  , , , , , , ,
       ,  ,  ,  , , , , , ],
    [ 4, 4, 3, 3, 3, , , , , ,                  # n = 17
       ,  ,  ,  ,  , , , , , ,
       ,  ,  ,  ,  , , , , , ,
       ,  ,  ,  ,  , , , , , ,
       ,  ,  ,  ,  , , , , ],
    [ 5, 4, 4, 3, 3, 3, , , , ,                 # n = 18
       ,  ,  ,  ,  ,  , , , , ,
       ,  ,  ,  ,  ,  , , , , ,
       ,  ,  ,  ,  ,  , , , , ,
       ,  ,  ,  ,  ,  , , , ],
    [ 5, 4, 4, 4, 3, 3, 2, , , ,                # n = 19
       ,  ,  ,  ,  ,  ,  , , , ,
       ,  ,  ,  ,  ,  ,  , , , ,
       ,  ,  ,  ,  ,  ,  , , , ,
       ,  ,  ,  ,  ,  ,  , , ],
    [ 6, 5, 4, 4, 4, 3, 3, 2, , ,               # n = 20
       ,  ,  ,  ,  ,  ,  ,  , , ,
       ,  ,  ,  ,  ,  ,  ,  , , ,
       ,  ,  ,  ,  ,  ,  ,  , , ,
       ,  ,  ,  ,  ,  ,  ,  , ],
    [ 6, 5, 5, 4, 4, 3, 3, 3, , ,               # n = 21
       ,  ,  ,  ,  ,  ,  ,  , , ,
       ,  ,  ,  ,  ,  ,  ,  , , ,
       ,  ,  ,  ,  ,  ,  ,  , , ,
       ,  ,  ,  ,  ,  ,  ,  , ],
    [ 6, 6, 5, 5, 4, 4, 3, 3, 3, ,              # n = 22
       ,  ,  ,  ,  ,  ,  ,  ,  , ,
       ,  ,  ,  ,  ,  ,  ,  ,  , ,
       ,  ,  ,  ,  ,  ,  ,  ,  , ,
       ,  ,  ,  ,  ,  ,  ,  ,  ],
    [ 7, 6, 5, 5, 4, 4, 3, 3, 3, 3,             # n = 23
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ],
    [ 7, 6, 6, 5, 5, 4, 4, 3, 3, 3,             # n = 24
      3,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ],
    [ 7, 7, 6, 6, 5, 5, 4, 4, 3, 3,             # n = 25
      3, 2,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ],
    [ 8, 7, 7, 6, 6, 5, 5, 4, 4, 3,             # n = 26
      3, 3, 2,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ],
    [ 8, 8, 7, 6, 6, 5, 5, 4, 4, 4,             # n = 27
      3, 3, 3, 2,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ],
    [ 9, 8, 7, 7, 6, 6, 5, 5, 4, 4,             # n = 28
      4, 3, 3, 3, 2,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ],
    [ 9, 8, 8, 7, 7, 6, 6, 5, 5, 4,             # n = 29
      4, 4, 3, 3, 3, 2,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ],
    [ 9, 9, 8, 7, 7, 6, 6, 5, 5, 5,             # n = 30
      4, 4, 4, 3, 3, 3,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
       ,  ,  ,  ,  ,  ,  ,  ,  ],
    [ 10, 9, 8, 8, 7, 7, 6, 6, 5, 5,            # n = 31
       5, 4, 4, 3, 3, 3, 3,  ,  ,  ,
        ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
        ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
        ,  ,  ,  ,  ,  ,  ,  ,  ],
    [ 10, 9, 9, 8, 8, 7, 7, 6, 6, 5,            # n = 32
       5, 4, 4, 4, 3, 3, 3, 3,  ,  ,
        ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
        ,  ,  ,  ,  ,  ,  ,  ,  ,  ,
        ,  ,  ,  ,  ,  ,  ,  ,  ],
    [ 11, 10, 9, 9, 8, 7, 7, 6, 6, 6,           # n = 33
       5,  5, 4, 4, 4, 3, 3, 3, 3,  ,
        ,   ,  ,  ,  ,  ,  ,  ,  ,  ,
        ,   ,  ,  ,  ,  ,  ,  ,  ,  ,
        ,   ,  ,  ,  ,  ,  ,  ,  ],
    [ 11, 10, 10, 9, 8, 8, 7, 7, 6, 6,          # n = 34
       5,  5,  5, 4, 4, 4, 3, 3, 3, 2,
        ,   ,   ,  ,  ,  ,  ,  ,  ,  ,
        ,   ,   ,  ,  ,  ,  ,  ,  ,  ,
        ,   ,   ,  ,  ,  ,  ,  ,  ],
    [ 11, 11, 10, 9, 9, 8, 8, 7, 7, 6,          # n = 35
       6,  5,  5, 5, 4, 4, 4, 3, 3, 3,
       2,   ,   ,  ,  ,  ,  ,  ,  ,  ,
        ,   ,   ,  ,  ,  ,  ,  ,  ,  ,
        ,   ,   ,  ,  ,  ,  ,  ,  ],
    [ 12, 11, 10, 10, 9, 9, 8, 8, 7, 7,         # n = 36
       6,  6,  5,  5, 5, 4, 4, 4, 3, 3,
       3,  2,   ,   ,  ,  ,  ,  ,  ,  ,
        ,   ,   ,   ,  ,  ,  ,  ,  ,  ,
        ,   ,   ,   ,  ,  ,  ,  ,  ],
    [ 12, 11, 11, 10, 9, 9, 8, 8, 7, 7,         # n = 37
       6,  6,  6,  5, 5, 4, 4, 4, 4, 3,
       3,  3,  2,   ,  ,  ,  ,  ,  ,  ,
        ,   ,   ,   ,  ,  ,  ,  ,  ,  ,
        ,   ,   ,   ,  ,  ,  ,  ,  ],
    [ 13, 12, 11, 10, 10, 9, 9, 8, 8, 7,        # n = 38
       7,  6,  6,  6,  5, 5, 4, 4, 4, 3,
       3,  3,  3,  2,   ,  ,  ,  ,  ,  ,
        ,   ,   ,   ,   ,  ,  ,  ,  ,  ,
        ,   ,   ,   ,   ,  ,  ,  ,  ],
    [ 13, 12, 12, 11, 10, 10, 9, 9, 8, 8,       # n = 39
       7,  7,  6,  6,  5,  5, 5, 4, 4, 4,
       3,  3,  3,  3,  2,   ,  ,  ,  ,  ,
        ,   ,   ,   ,   ,   ,  ,  ,  ,  ,
        ,   ,   ,   ,   ,   ,  ,  ,  ],
    [ 14, 13, 12, 11, 11, 10, 9, 9, 8, 8,       # n = 40
       7,  7,  7,  6,  6,  5, 5, 5, 4, 4,
       4,  3,  3,  3,  3,  2,  ,  ,  ,  ,
        ,   ,   ,   ,   ,   ,  ,  ,  ,  ,
        ,   ,   ,   ,   ,   ,  ,  ,  ],
    [ 14, 13, 12, 12, 11, 10, 10, 9, 9, 8,      # n = 41
       8,  7,  7,  6,  6,  6,  5, 5, 5, 4,
       4,  4,  3,  3,  3,  3,  2,  ,  ,  ,
        ,   ,   ,   ,   ,   ,   ,  ,  ,  ,
        ,   ,   ,   ,   ,   ,   ,  ,  ],
    [ 14, 14, 13, 12, 11, 11, 10, 10, 9, 9,     # n = 42
       8,  8,  7,  7,  6,  6,  6,  5, 5, 5,
       4,  4,  4,  3,  3,  3,  3,  2,  ,  ,
        ,   ,   ,   ,   ,   ,   ,   ,  ,  ,
        ,   ,   ,   ,   ,   ,   ,   ,  ],
    [ 15, 14, 13, 12, 12, 11, 10, 10, 9, 9,     # n = 43
       8,  8,  7,  7,  7,  6,  6,  6, 5, 5,
       5,  4,  4,  4,  3,  3,  3,  3, 2,  ,
        ,   ,   ,   ,   ,   ,   ,   ,  ,  ,
        ,   ,   ,   ,   ,   ,   ,   ,  ],
    [ 15, 14, 14, 13, 12, 11, 11, 10, 10, 9,    # n = 44
       9,  8,  8,  7,  7,  7,  6,  6,  5, 5,
       5,  4,  4,  4,  4,  3,  3,  3,  3,  ,
        ,   ,   ,   ,   ,   ,   ,   ,   ,  ,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 16, 15, 14, 13, 12, 12, 11, 11, 10, 10,   # n = 45
       9,  9,  8,  8,  7,  7,  7,  6,  6,  5,
       5,  5,  4,  4,  4,  4,  3,  3,  3,  3,
        ,   ,   ,   ,   ,   ,   ,   ,   ,   ,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 16, 15, 14, 14, 13, 12, 12, 11, 11, 10,   # n = 46
       9,  9,  8,  8,  8,  7,  7,  6,  6,  6,
       5,  5,  5,  4,  4,  4,  4,  3,  3,  3,
       3,   ,   ,   ,   ,   ,   ,   ,   ,   ,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 16, 16, 15, 14, 13, 13, 12, 11, 11, 10,   # n = 47
      10,  9,  9,  8,  8,  8,  7,  7,  6,  6,
       6,  5,  5,  5,  4,  4,  4,  3,  3,  3,
       3,  2,   ,   ,   ,   ,   ,   ,   ,   ,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 17, 16, 15, 14, 14, 13, 12, 12, 11, 11,   # n = 48
      10, 10,  9,  9,  8,  8,  7,  7,  7,  6,
       6,  6,  5,  5,  5,  4,  4,  4,  3,  3,
       3,  3,  2,   ,   ,   ,   ,   ,   ,   ,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 17, 16, 16, 15, 14, 13, 13, 12, 12, 11,   # n = 49
      11, 10,  9,  9,  9,  8,  8,  7,  7,  7,
       6,  6,  6,  5,  5,  5,  4,  4,  4,  3,
       3,  3,  3,  2,   ,   ,   ,   ,   ,   ,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 18, 17, 16, 15, 14, 14, 13, 13, 12, 11,   # n = 50
      11, 10, 10,  9,  9,  8,  8,  8,  7,  7,
       7,  6,  6,  6,  5,  5,  5,  4,  4,  4,
       3,  3,  3,  3,  2,   ,   ,   ,   ,   ,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 18, 17, 16, 16, 15, 14, 13, 13, 12, 12,   # n = 51
      11, 11, 10, 10,  9,  9,  8,  8,  8,  7,
       7,  6,  6,  6,  5,  5,  5,  5,  4,  4,
       4,  3,  3,  3,  3,  2,   ,   ,   ,   ,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 19, 18, 17, 16, 15, 15, 14, 13, 13, 12,   # n = 52
      12, 11, 11, 10, 10,  9,  9,  8,  8,  8,
       7,  7,  6,  6,  6,  5,  5,  5,  5,  4,
       4,  4,  3,  3,  3,  3,  2,   ,   ,   ,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 19, 18, 17, 16, 16, 15, 14, 14, 13, 12,   # n = 53
      12, 11, 11, 10, 10,  9,  9,  9,  8,  8,
       7,  7,  7,  6,  6,  6,  5,  5,  5,  4,
       4,  4,  4,  3,  3,  3,  3,  2,   ,   ,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 19, 18, 18, 17, 16, 15, 15, 14, 13, 13,   # n = 54
      12, 12, 11, 11, 10, 10,  9,  9,  9,  8,
       8,  7,  7,  7,  6,  6,  6,  5,  5,  5,
       4,  4,  4,  4,  3,  3,  3,  3,  2,   ,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 20, 19, 18, 17, 16, 16, 15, 14, 14, 13,   # n = 55
      13, 12, 12, 11, 11, 10, 10,  9,  9,  8,
       8,  8,  7,  7,  7,  6,  6,  6,  5,  5,
       5,  4,  4,  4,  4,  3,  3,  3,  3,  2,
        ,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 20, 19, 18, 18, 17, 16, 15, 15, 14, 14,   # n = 56
      13, 12, 12, 11, 11, 10, 10, 10,  9,  9,
       8,  8,  8,  7,  7,  7,  6,  6,  6,  5,
       5,  5,  4,  4,  4,  4,  3,  3,  3,  3,
       2,   ,   ,   ,   ,   ,   ,   ,   ],
    [ 21, 20, 19, 18, 17, 16, 16, 15, 14, 14,   # n = 57
      13, 13, 12, 12, 11, 11, 10, 10,  9,  9,
       9,  8,  8,  7,  7,  7,  6,  6,  6,  5,
       5,  5,  5,  4,  4,  4,  4,  3,  3,  3,
       3,  2,   ,   ,   ,   ,   ,   ,   ],
    [ 21, 20, 19, 18, 18, 17, 16, 15, 15, 14,   # n = 58
      14, 13, 13, 12, 12, 11, 11, 10, 10,  9,
       9,  9,  8,  8,  7,  7,  7,  6,  6,  6,
       5,  5,  5,  5,  4,  4,  4,  4,  3,  3,
       3,  3,  2,   ,   ,   ,   ,   ,   ],
    [ 22, 21, 20, 19, 18, 17, 17, 16, 15, 15,   # n = 59
      14, 14, 13, 12, 12, 11, 11, 11, 10, 10,
       9,  9,  8,  8,  8,  7,  7,  7,  6,  6,
       6,  5,  5,  5,  5,  4,  4,  4,  4,  3,
       3,  3,  3,  2,   ,   ,   ,   ,   ],
    [ 22, 21, 20, 19, 18, 18, 17, 16, 16, 15,   # n = 60
      14, 14, 13, 13, 12, 12, 11, 11, 10, 10,
      10,  9,  9,  8,  8,  8,  7,  7,  7,  6,
       6,  6,  5,  5,  5,  5,  4,  4,  4,  3,
       3,  3,  3,  3,  2,   ,   ,   ,   ],
    [ 23, 21, 20, 20, 19, 18, 17, 17, 16, 15,   # n = 61
      15, 14, 14, 13, 13, 12, 12, 11, 11, 10,
      10, 10,  9,  9,  8,  8,  8,  7,  7,  7,
       6,  6,  6,  5,  5,  5,  5,  4,  4,  4,
       3,  3,  3,  3,  3,  2,   ,   ,   ],
    [ 23, 22, 21, 20, 19, 18, 18, 17, 16, 16,   # n = 62
      15, 15, 14, 14, 13, 12, 12, 12, 11, 11,
      10, 10,  9,  9,  9,  8,  8,  8,  7,  7,
       7,  6,  6,  6,  5,  5,  5,  4,  4,  4,
       4,  3,  3,  3,  3,  3,  2,   ,   ],
    [ 23, 22, 21, 20, 20, 19, 18, 17, 17, 16,   # n = 63
      16, 15, 14, 14, 13, 13, 12, 12, 11, 11,
      11, 10, 10,  9,  9,  9,  8,  8,  7,  7,
       7,  6,  6,  6,  6,  5,  5,  5,  4,  4,
       4,  4,  3,  3,  3,  3,  3,  2,   ],
    [ 24, 23, 22, 21, 20, 19, 18, 18, 17, 16,   # n = 64
      16, 15, 15, 14, 14, 13, 13, 12, 12, 11,
      11, 10, 10, 10,  9,  9,  8,  8,  8,  7,
       7,  7,  6,  6,  6,  6,  5,  5,  5,  4,
       4,  4,  4,  3,  3,  3,  3,  3,  2 ]
];

########################################################################
##
#F  GeneralLowerBoundCoveringRadius( <n>, <size> [, <F> ] )
##  GeneralLowerBoundCoveringRadius( <code> )
##

InstallMethod(GeneralLowerBoundCoveringRadius, "unrestricted code", true,
    [IsCode], 0,
function(code)
    local n, size, field, listofbounds, max;
    if IsLinearCode(code) then
        return GeneralLowerBoundCoveringRadius(code);
    fi;
    n := WordLength( code );
    size := Size( code );
    field := LeftActingDomain( code );
    listofbounds := [
      LowerBoundCoveringRadiusSphereCovering( n, size, field, false ),
    ];
    if field = GF(2) then
        Append( listofbounds, [
          LowerBoundCoveringRadiusVanWee1( n, size, field, false ),
          LowerBoundCoveringRadiusVanWee2( n, size, false ),
          LowerBoundCoveringRadiusCountingExcess( n, size, false )
        ] );
        if n <= 200 then
            Append( listofbounds, [
              LowerBoundCoveringRadiusEmbedded1( n, size, field, false ),
                  LowerBoundCoveringRadiusEmbedded2( n, size, field, false )
                ] );
        fi;
    fi;
    max := Maximum( listofbounds );

    if IsBound(code!.boundsCoveringRadius) then
        return Maximum ([ max, code!.boundsCoveringRadius[1] ]);
    else
        return max;
    fi;
end);


InstallMethod(GeneralLowerBoundCoveringRadius, "linear code", true,
    [IsLinearCode], 0,
function (code)
    local n, size, field, k, listofbounds, return_value;
    n := WordLength(code);
    size := Size(code);
    field := LeftActingDomain(code);
    listofbounds := [
      LowerBoundCoveringRadiusSphereCovering( n, size, field, false ),
    ];
    return_value := -99;  # Allows to check whether next set of if
                          # statements actually assign a value
    if field = GF(2) then
        k := Dimension( code );
        # small codimensions (n-k)
        if k = n then
            return_value :=  0;
        elif k = n - 1 then
            return_value :=  1;
        elif k = n - 2 then
            return_value :=  1;
        elif k = n - 3 and n >= 7 then
            return_value :=  1;
        elif k = n - 4 and n >= 5 then
            if n >= 15 then
                return_value :=  1;
            else
                return_value :=  2;
            fi;
        elif k = n - 5 and n >= 9 then
            if n >= 31 then
                return_value :=  1;
            else
                return_value :=  2;
            fi;
        elif k = n - 6 then
            if n >= 63 then
                return_value :=  1;
            elif n >= 14 then
                return_value :=  2;
            elif n = 12 then
                return_value :=  3;
            fi;
        elif k = n - 7 and n >= 21 and n <= 64 then
            return_value :=  2;
        elif k = n - 8 and n >= 30 and n <= 64 then
            return_value :=  2;
        elif k = n - 9 and n >= 44 and n <= 64 then
            return_value :=  2;
        elif k = 1 then
            return_value :=  IntFloor( n/2 );
        elif k = 2 then
            return_value :=  IntFloor( (n-1)/2 );
        elif k = 3 then
            return_value :=  IntFloor( (n-2)/2 );
        elif k = 4 then
            return_value :=  IntFloor( (n-4)/2 );
        elif k = 5 then
            return_value :=  IntFloor( (n-5)/2 );
        fi;


        # use the table of Cohen, Litsyn, Lobstein and Mattson
        if return_value < 0 and n >= 13 and n <= 64 and k>=6 and k<= 64 then
            if IsBound(
                 CoveringRadiusLowerBoundTable[ n - 12 ][ k - 5 ] ) then
                return_value :=
                            CoveringRadiusLowerBoundTable[ n - 12 ][ k - 5 ];
            fi;
        fi;

        if return_value < 0 then
            # not in the table. use the bounds
            listofbounds := [
              LowerBoundCoveringRadiusSphereCovering( n, size, field, false ),
              LowerBoundCoveringRadiusVanWee1( n, size, field, false ),
              LowerBoundCoveringRadiusVanWee2( n, size, false ),
              LowerBoundCoveringRadiusCountingExcess( n, size, false )
            ];
            if n <= 200 then
                Append( listofbounds, [
                  LowerBoundCoveringRadiusEmbedded1( n, size, field, false ),
                  LowerBoundCoveringRadiusEmbedded2( n, size, field, false ),
                ] );
            fi;
            return_value := Maximum( listofbounds );
        fi;
    else  # field is not GF(2)
        listofbounds := [
          LowerBoundCoveringRadiusSphereCovering( n, size, field, false ),
        ];
        return_value := Maximum( listofbounds );
    fi;

    if IsBound(code!.boundsCoveringRadius) then
        return Maximum([return_value, code!.boundsCoveringRadius[1]]);
    else
        return return_value;
    fi;

end);

InstallOtherMethod(GeneralLowerBoundCoveringRadius, "n, k, field", true,
    [IsInt, IsInt, IsField], 0,
function(n, size, field)
    local listofbounds;
    if n < 1 or size < 1 then
        Error("GeneralLBCR: <n> and <size> must be positive");
    fi;
    listofbounds := [
      LowerBoundCoveringRadiusSphereCovering( n, size, field, false ),
      LowerBoundCoveringRadiusVanWee1( n, size, field, false ),
      LowerBoundCoveringRadiusEmbedded1( n, size, field, false ),
      LowerBoundCoveringRadiusEmbedded2( n, size, field, false )
    ];
    if field = GF(2) then
        Append( listofbounds, [
          LowerBoundCoveringRadiusVanWee2( n, size, false ),
          LowerBoundCoveringRadiusCountingExcess( n, size, false )
        ] );
    fi;
    return Maximum( listofbounds );
end);

InstallOtherMethod(GeneralLowerBoundCoveringRadius, "n, size", true,
    [IsInt, IsInt], 0,
function(n, size)
    return GeneralLowerBoundCoveringRadius(n, size, GF(2));
end);


########################################################################
##
#F  LowerBoundCoveringRadiusSphereCovering( <n>, <r> [, <F> ] [, true ] )
##

InstallMethod(LowerBoundCoveringRadiusSphereCovering,
    "n, r, fieldsize, usage",
    true, [IsInt, IsInt, IsInt, IsBool], 0,
function(n, r, q, usage)
    local m, lb, ub, tmpcr, tmpsize, t;
    if n <= 0 then
        Error("LBCRSphereCovering: <n> must be positive");
    fi;

    if usage = false then
        # last argument is false, try to find a lower bound for
        # the covering radius, given length and size of the code, and field

        m := r;
        if m <= 0 or m > q^n then
            Error( "LBCRSphereCovering: <m> must be > 0 and <= q^n" );
        fi;

        # everything is set up. now compute the bound

        lb := 0;
        ub := n;
        while lb <> ub do
            tmpcr := IntFloor( ( lb + ub ) / 2 );
            tmpsize := IntCeiling( q^n / SphereContent( n, tmpcr, q ) );
            if tmpsize > m then
                lb := tmpcr + 1;
            else
                ub := tmpcr;
            fi;
        od;
        return ub;
    else
        # the last argument is not false
        # now it is assumed that the first argument is the length
        # of the code and the second argument is the covering radius
        # of the code. a lower bound for the minimal size of the
        # code is returned

        t := r;
        if t < 0 or t > n then
            Error( "LBCRSphereCovering: <t> must be >= 0 and <= <n>" );
        fi;

        return IntCeiling( q^n / SphereContent( n, t, q ) );
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusSphereCovering,
    "n, r, field, usage", true,
    [IsInt, IsInt, IsField, IsBool], 0,
function(n, r, F, usage)
    if not IsFinite(F) then
        Error("LBCRSphereCovering: <F> must be a finite field");
    else
        return LowerBoundCoveringRadiusSphereCovering(n, r, Size(F), usage);
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusSphereCovering,
    "n, r, fieldsize", true, [IsInt, IsInt, IsInt], 0,
function(n, r, q)
    return LowerBoundCoveringRadiusSphereCovering(n, r, q, true);
end);

InstallOtherMethod(LowerBoundCoveringRadiusSphereCovering,
    "n, r, field", true, [IsInt, IsInt, IsField], 0,
function(n, r, F)
    if not IsFinite(F) then
        Error("LBCRSphereCovering: <F> must be a finite field");
    else
        return LowerBoundCoveringRadiusSphereCovering(n, r, Size(F), true);
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusSphereCovering,
    "n, r, usage", true, [IsInt, IsInt, IsBool], 0,
function(n, r, usage)
    return LowerBoundCoveringRadiusSphereCovering(n, r, 2, usage);
end);

InstallOtherMethod(LowerBoundCoveringRadiusSphereCovering, "n, r", true,
    [IsInt, IsInt], 0,
function(n, r)
    return LowerBoundCoveringRadiusSphereCovering(n, r, 2, true);
end);


########################################################################
##
#F  LowerBoundCoveringRadiusVanWee1( ... )
##

InstallMethod(LowerBoundCoveringRadiusVanWee1,
    "n, r, fieldsize, usage", true, [IsInt, IsInt, IsInt, IsBool], 0,
function(n, r, q, usage)
    local m, lb, ub, tmpcr, tmpsize, t, tmp;
    if n <= 0 then
        Error("LBCRVanWee1: <n> must be positive");
    fi;
    if usage = false then
        # last argument is false, try to find a lower bound for
        # the covering radius, given length and size of the code, and field

        m := r;
        if m <= 0 or m > q^n then
            Error( "LBCRVanWee1: <m> must be > 0 and <= q^n" );
        fi;

        # everything is set up. now compute the bound

        lb := 0;
        ub := n;
        while lb <> ub do
            tmpcr := IntFloor( (ub+lb) / 2 );
            tmpsize := LowerBoundCoveringRadiusVanWee1( n, tmpcr, q );
            if tmpsize > m then
                lb := tmpcr + 1;
            else
                ub := tmpcr;
            fi;
        od;
        return ub;
    else
        # the last argument is not false
        # now it is assumed that the first argument is the length
        # of the code and the second argument is the covering radius
        # of the code. a lower bound for the minimal size of the
        # code is returned

        t := r;
        if t < 0 or t > n then
            Error( "LBCRVanWee1: <t> must be >= 0 and <= <n>" );
        fi;
        if t = n then
            return 1;
        fi;

        tmp := (Binomial(n,t))/(IntCeiling((n-t)/(t+1)));
        tmp := tmp * (IntCeiling((t+1)/(t+1)) - (t+1)/(t+1));
        tmp := SphereContent( n, t, q ) - tmp;
        return IntCeiling( q^n / tmp );
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusVanWee1,
    "n, r, field, usage", true, [IsInt, IsInt, IsField, IsBool], 0,
function(n, r, F, usage)
    if not IsFinite(F) then
        Error("LBCRVanWee1: <F> must be a finite field");
    else
        return LowerBoundCoveringRadiusVanWee1(n, r, Size(F), usage);
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusVanWee1, "n,r,fieldsize",
    true, [IsInt, IsInt, IsInt], 0,
function(n, r, q)
    return LowerBoundCoveringRadiusVanWee1(n, r, q, true);
end);

InstallOtherMethod(LowerBoundCoveringRadiusVanWee1, "n, r, field",
    true, [IsInt, IsInt, IsField], 0,
function(n, r, F)
    if not IsFinite(F) then
        Error("LBCRVanWee1: <F> must be a finite field");
    else
        return LowerBoundCoveringRadiusVanWee1(n, r, Size(F), true);
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusVanWee1,
    "n, r, usage", true, [IsInt, IsInt, IsBool], 0,
function(n, r, usage)
    return LowerBoundCoveringRadiusVanWee1(n, r, 2, usage);
end);

InstallOtherMethod(LowerBoundCoveringRadiusVanWee1,
    "n, r", true, [IsInt, IsInt], 0,
function(n, r)
    return LowerBoundCoveringRadiusVanWee1(n, r, 2, true);
end);


#############################################################################
##
#F  LowerBoundCoveringRadiusVanWee2( <n>, <r> ) Counting Excess bound
##

InstallMethod(LowerBoundCoveringRadiusVanWee2,
    "code length, code size or covering radius, usage",
    true, [IsInt, IsInt, IsBool], 0,
function(n, r, usage)
    local m, lb, ub, tmpcr, eps, tmpsize, t, tmpb1, tmpb2;

    if n<=0 then
        Error( "LBCRVanWee2: <n> must be positive" );
    fi;

    if usage = false then
        m := r;
        if m <= 0 or m > 2^n then
            Error( "LBCRVanWee2: <m> must be > 0 and <= 2^n" );
        fi;
        lb := 0;
        ub := IntFloor( n/2 );
        while lb <> ub do
            tmpcr := IntFloor( ( ub + lb ) / 2 );
            tmpb1 := (tmpcr+2)*(tmpcr+1)/2; # Binomial(tmpcr+2,2)
            tmpb2 := (n-tmpcr+1)*(n-tmpcr)/2; # Binomial(n-tmpcr+1,2)
            eps := tmpb1 * IntCeiling( tmpb2/tmpb1 ) - tmpb2;
            tmpsize := 2^n * ( SphereContent( n, 2, 2 ) + eps
                               - 1/2*( tmpcr + 2 )*( tmpcr - 1 ) );
            tmpsize := tmpsize / ( SphereContent( n, tmpcr, 2 ) *
                               ( SphereContent( n, 2, 2 )
                                 - 1/2*( tmpcr + 2 )*( tmpcr - 1 ) )
                               + eps * SphereContent( n, tmpcr - 2 ) );
            if tmpsize > m then
                lb := tmpcr + 1;
            else
                ub := tmpcr;
            fi;
        od;
        return ub;
    else
        t := r;
        if t < 0 or t > n then
            Error( "LBCRVanWee2: <t> must be >= 0 and <= <n>" );
        fi;
        if 2 * t > n then
            return 0;
        fi;
        tmpb1 := (t+2)*(t+1)/2;
        tmpb2 := (n-t+1)*(n-t)/2;
        eps := tmpb1 * IntCeiling( tmpb2/tmpb1 ) - tmpb2;
        tmpsize := 2^n * ( SphereContent( n, 2, 2 ) + eps
                           - 1/2*( t + 2 )*( t - 1 ) );
        tmpsize := tmpsize / ( SphereContent( n, t, 2 )
                           * ( SphereContent( n, 2, 2 )
                               - 1/2*( t + 2 )*( t - 1 ) )
                           + eps * SphereContent( n, t-2, 2 ) );
        return IntCeiling(tmpsize);
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusVanWee2,
    "code length, code covering radius",
    true, [IsInt, IsInt], 0,
function(n, r)
    return LowerBoundCoveringRadiusVanWee2(n, r, true);
end);

#############################################################################
##
#F  LowerBoundCoveringRadiusCountingExcess( <n>, <r> )
##

InstallMethod(LowerBoundCoveringRadiusCountingExcess,
    "code length, code size or covering radius, usage", true,
    [IsInt, IsInt, IsBool], 0,
function(n, r, usage)
    local m, lb, ub, tmpcr, tmpsize, t, rho, eps;

    if n<=0 then
        Error( "LBCRCountingExcess: <n> must be positive" );
    fi;

    if usage = false then
        m := r;
        if m<=0 or m > 2^n then
            Error( "LBCRCountingExcess: <m> must be > 0 and <= 2^n" );
        fi;

        lb := 0;
        ub := IntFloor( ( n-1 ) / 2 );
        while lb <> ub do
            tmpcr := IntFloor( ( ub + lb ) / 2 );
            tmpsize := LowerBoundCoveringRadiusCountingExcess(
                               n, tmpcr, true );
            if tmpsize > m then
                lb := tmpcr + 1;
            else
                ub := tmpcr;
            fi;
        od;
        return ub;
    else
        t := r;
        if t < 0 or t > n then
            Error( "LBCRCountingExcess: <t> must be >=0 and <= <n>" );
        fi;

        if t < 2 or 2 * t + 1 > n then
            return 0;
        fi;
        if t = 2 then
            rho := n - 3 + 2/n;
        else
            rho := n - t - 1;
        fi;
        eps := ( t+1 ) * IntCeiling( ( n+1 ) / ( t+1 ) ) - ( n+1 );
        if eps > t then
            return 0;
        fi;
        tmpsize := 2^n * ( rho + eps );
        tmpsize := tmpsize / ( rho * SphereContent( n, t )
                           + eps * SphereContent( n, t-1 ) );

        return IntCeiling( tmpsize );
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusCountingExcess,
    "code length, code covering radius", true,
    [IsInt, IsInt], 0,
function(n, r)
    return LowerBoundCoveringRadiusCountingExcess(n, r, true);
end);


########################################################################
##
#F  LowerBoundCoveringRadiusEmbedded1( <n>, <r> [, <givesize> ] )
##

InstallMethod(LowerBoundCoveringRadiusEmbedded1,
    "code length, code size or covering radius, field size, usage",
    true, [IsInt, IsInt, IsInt, IsBool], 0,
function(n, r, q, usage)
    local m, lb, ub, tmpcr, tmpsize, t, upperb;
    if n <= 0 then
        Error("LBCREmbedded1: <n> must be positive");
    fi;

    if usage = false then
        # last argument is false, try to find a lower bound for
        # the covering radius, given length and size of the code, and field

        m := r;
        if m <= 0 or m > q^n then
            Error( "LBCREmbedded1: <m> must be > 0 and <= q^n" );
        fi;

        # everything is set up. now compute the bound

        if m = q^n then
            return 0;
        fi;
        if n = 1 then
            return 0;
        fi;

        lb := 1;
        ub := n;
        while lb <> ub do
            tmpcr := IntFloor( (ub+lb) / 2 );
            tmpsize := SphereContent( n, tmpcr, q )
                       - Binomial( 2*tmpcr, tmpcr );
            if tmpsize = 0 then
                lb := lb + 1;
            elif tmpsize < 0 then
                ub := tmpcr;
            else
                if 2*tmpcr+1 > n then
                    upperb := 1;
                else
                    upperb := UpperBound( n, 2*tmpcr+1, q );
                fi;
                tmpsize := IntCeiling( ( q^n - upperb
                                   * Binomial( 2 * tmpcr, tmpcr ) )
                                   / tmpsize );
                if tmpsize > m then
                    lb := tmpcr + 1;
                else
                    ub := tmpcr;
                fi;
            fi;
        od;
        return ub;
    else
        # the last argument is not false
        # now it is assumed that the first argument is the length
        # of the code and the second argument is the covering radius
        # of the code. a lower bound for the minimal size of the
        # code is returned

        t := r;
        if t < 0 or t > n then
            Error( "LBCREmbedded1: <t> must be >= 0 and <= <n>" );
        fi;

        tmpsize := SphereContent( n, t, q ) - Binomial( 2*t, t );
        if tmpsize <= 0 then
            return 0;
        else
            if 2 * t + 1 > n then
                upperb := 1;
            else
                upperb := UpperBound( n, 2*t+1, q );
            fi;
            return IntCeiling( ( q^n - upperb
              * Binomial( 2*t, t ) ) / tmpsize );
        fi;
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusEmbedded1,
    "code length, code size or covering radius, field, usage",
    true, [IsInt, IsInt, IsField, IsBool], 0,
function(n, r, F, usage)
    if not IsFinite(F) then
        Error("LBCREmbedded1: <F> must be a finite field");
    else
        return LowerBoundCoveringRadiusEmbedded1(n, r, Size(F), usage);
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusEmbedded1,
    "code length, code size or covering radius, usage",
    true, [IsInt, IsInt, IsBool], 0,
function(n, r, usage)
    return LowerBoundCoveringRadiusEmbedded1(n, r, 2, usage);
end);

InstallOtherMethod(LowerBoundCoveringRadiusEmbedded1,
    "code length, code covering radius, field size", true,
    [IsInt, IsInt, IsInt], 0,
function(n, r, q)
    return LowerBoundCoveringRadiusEmbedded1(n, r, q, true);
end);

InstallOtherMethod(LowerBoundCoveringRadiusEmbedded1,
    "code length, code covering radius, field", true,
    [IsInt, IsInt, IsField], 0,
function(n, r, F)
    if not IsFinite(F) then
        Error("LBCREmbedded1: <F> must be a finite field");
    else
        return LowerBoundCoveringRadiusEmbedded1(n, r, Size(F), true);
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusEmbedded1,
    "code length, code covering radius", true, [IsInt, IsInt], 0,
function(n, r)
    return LowerBoundCoveringRadiusEmbedded1(n, r, 2, true);
end);


########################################################################
##
#F  LowerBoundCoveringRadiusEmbedded2( <n>, <r> [, <givesize> ] )
##

InstallMethod(LowerBoundCoveringRadiusEmbedded2,
    "code length, code size or covering radius, field size, usage",
    true, [IsInt, IsInt, IsInt, IsBool], 0,
function(n, r, q, usage)
    local m, lb, ub, tmpcr, tmpsize, t, upperb;
    if n <= 0 then
        Error("LBCREmbedded2: <n> must be positive");
    fi;

    if usage = false then
        # last argument is false, try to find a lower bound for
        # the covering radius, given length and size of the code, and field

        m := r;
        if m <= 0 or m > q^n then
            Error( "LBCREmbedded2: <m> must be > 0 and <= q^n" );
        fi;

        # everything is set up. now compute the bound

        if m = q^n then
            return 0;
        fi;
        if n = 1 or n = 2 then
            return 0;
        fi;

        lb := 1;
        ub := n;
        while lb <> ub do

            tmpcr := IntFloor( (ub+lb) / 2 );
            tmpsize := SphereContent( n, tmpcr, q )
                       - 3/2 * Binomial( 2*tmpcr, tmpcr );
            if tmpsize = -1/2 then
                lb := lb + 1;
            elif tmpsize <= 0 then
                ub := tmpcr;
            else
                if 2*tmpcr+1 > n then
                    upperb := 1;
                else
                    upperb := UpperBound( n, 2*tmpcr+1, q );
                fi;
                tmpsize := IntCeiling( ( q^n - 2 * upperb
                                   * Binomial( 2*tmpcr, tmpcr ) )
                                   / tmpsize );
                if tmpsize > m then
                    lb := tmpcr + 1;
                else
                    ub := tmpcr;
                fi;
            fi;
        od;
        return ub;
    else
        # the last argument is not false
        # now it is assumed that the first argument is the length
        # of the code and the second argument is the covering radius
        # of the code. a lower bound for the minimal size of the
        # code is returned

        t := r;
        if t < 0 or t > n then
            Error( "LBCREmbedded2: <t> must be >= 0 and <= <n>" );
        fi;

        tmpsize := SphereContent( n, t, q ) - 3/2*Binomial( 2*t, t );
        if tmpsize <= 0 then
            return 0;
        else
            if 2*t+1 > n then
                upperb := 1;
            else
                upperb := UpperBound( n, 2*t+1, q );
            fi;

            return IntCeiling( ( q^n - 2*upperb
                           * Binomial( 2*t, t ) ) / tmpsize );
        fi;
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusEmbedded2,
    "code length, code size or covering radius, field, usage",
    true, [IsInt, IsInt, IsField, IsBool], 0,
function(n, r, F, usage)
    if not IsFinite(F) then
        Error("LBCREmbedded2: <F> must be a finite field");
    else
        return LowerBoundCoveringRadiusEmbedded2(n, r, Size(F), usage);
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusEmbedded2,
    "code length, code size or covering radius, usage",
    true, [IsInt, IsInt, IsBool], 0,
function(n, r, usage)
    return LowerBoundCoveringRadiusEmbedded2(n, r, 2, usage);
end);

InstallOtherMethod(LowerBoundCoveringRadiusEmbedded2,
    "code length, code covering radius, field size",
    true, [IsInt, IsInt, IsInt], 0,
function(n, r, q)
    return LowerBoundCoveringRadiusEmbedded2(n, r, q, true);
end);

InstallOtherMethod(LowerBoundCoveringRadiusEmbedded2,
    "code length, code covering radius, field",
    true, [IsInt, IsInt, IsField], 0,
function(n, r, F)
    if not IsFinite(F) then
        Error("LBCREmbedded2: <F> must be a finite field");
    else
        return LowerBoundCoveringRadiusEmbedded2(n, r, Size(F), true);
    fi;
end);

InstallOtherMethod(LowerBoundCoveringRadiusEmbedded2,
    "code length, code covering radius",
    true, [IsInt, IsInt], 0,
function(n, r)
    return LowerBoundCoveringRadiusEmbedded2(n, r, 2, true);
end);


#############################################################################
##
#F  LowerBoundCoveringRadiusInduction( <n>, <r> ) Induction bound
##

InstallMethod(LowerBoundCoveringRadiusInduction,
    "method for two integers", true, [IsInt, IsInt], 0,
function(n, t)
    if n <= 0 then
        Error( "LBCRInduction: <n> must be positive" );
    fi;
    if t < 0 or t > n then
        Error( "LBCRInduction: <t> must be >= 0 and <= <n>" );
    fi;

    if n = 2 * t + 2 and t >= 1 then
        return 4;
    elif n = 2 * t + 3 and t >= 1 then
        return 7;
    elif n = 2 * t + 4 and t >= 4 then
        return 8;
    else
        return 0;
    fi;

end);


########################################################################
##
#F  GeneralUpperBoundCoveringRadius( <code> )
##
InstallMethod(GeneralUpperBoundCoveringRadius, "unrestricted code", true,
    [IsCode], 0,
function(code)
    local listofbounds;

    listofbounds := [ UpperBoundCoveringRadiusStrength( code ) ];
    if WordLength( code ) <= 100 then
        Append( listofbounds, [
          UpperBoundCoveringRadiusDelsarte( code )
                ] );
    fi;
    if IsLinearCode( code ) then
        Append( listofbounds, [
          UpperBoundCoveringRadiusRedundancy( code ),
        ] );
        if LeftActingDomain( code ) = GF(2) then
            if ( IsBound( code!.lowerBoundMinimumDistance ) and
                 IsBound( code!.upperBoundMinimumDistance ) ) and
               LowerBoundMinimumDistance( code ) =
                 UpperBoundMinimumDistance (code )
               then
                Append( listofbounds, [
                        UpperBoundCoveringRadiusGriesmerLike( code )
                        ] );
            fi;
        fi;
    fi;
    if IsCyclicCode( code ) and LeftActingDomain( code ) = GF(2) then
        Append( listofbounds, [
          UpperBoundCoveringRadiusCyclicCode( code )
        ] );
    fi;
    if IsBound( code!.boundsCoveringRadius ) then
        Add( listofbounds, Maximum( code!.boundsCoveringRadius ) );
    fi;
    return Minimum( listofbounds );
end);


########################################################################
##
#F  UpperBoundCoveringRadiusRedundancy( <code> )
##
##  Return the redundancy of the code as an upper bound for
##  the covering radius.
##
##  Only for linear codes.
##

InstallMethod(UpperBoundCoveringRadiusRedundancy, "unrestricted code", true,
    [IsCode], 0,
function( code )
    if IsLinearCode( code ) then
        return UpperBoundCoveringRadiusRedundancy( code );
    else
        Error("UBCRRedundancy: <code> must be a linear code");
    fi;
end);

InstallMethod(UpperBoundCoveringRadiusRedundancy, "linear code", true,
    [IsLinearCode], 0,
function( code)
    return Redundancy(code);
end);


########################################################################
##
#F  UpperBoundCoveringRadiusDelsarte( <code> )
##

InstallMethod(UpperBoundCoveringRadiusDelsarte, "method for unrestricted codes", true, [IsCode], 0,
function(code)
    local p;
    p := CodeMacWilliamsTransform(code);
    p := CoefficientsOfLaurentPolynomial(p);
    p := ShiftedCoeffs(p[1], p[2]);
    return WeightVector(p) - 1;
end);

InstallMethod(UpperBoundCoveringRadiusDelsarte, "method for linear codes",
    true, [IsLinearCode], 0,
function(code)
    local dual, wddual;
    if not IsBound(code!.boundsCoveringRadius) then
        # avoid recursion
        code!.boundsCoveringRadius := [ 0 .. WordLength( code ) ];
    fi;
    dual := DualCode( code );
    wddual := WeightDistribution( dual );
    return WeightVector( wddual ) - 1;
end);


########################################################################
##
#F  UpperBoundCoveringRadiusStrength( <code> )
##
##  Return (q-1)n/q as an upper bound for <code>, if it
##  has strength 1 (i.e. every coordinate contains each element
##  of the field the same number of times).
##

InstallMethod(UpperBoundCoveringRadiusStrength, "method for unrestricted codes",
    true, [IsCode], 0,
function(code)
    local q, n, stris1, i, j, els, fieldels, coordlist, number, zerocols;
    if IsLinearCode(code) then
        return UpperBoundCoveringRadiusStrength(code);
    fi;
    q := Size( LeftActingDomain( code ) );
    n := WordLength( code );
    stris1 := true;
    i := 1;
    els := VectorCodeword( AsSSortedList( code ) );
    if not ( Length( els ) in List( [ 1 .. n ], x -> q^x ) ) then
        stris1 := false;
    fi;
    fieldels := AsSSortedList(LeftActingDomain( code ) );
    zerocols := 0;
    while stris1 and i <= n do
        coordlist := List( els, x -> x[ i ] );
        for j in fieldels do
            number := Length( Filtered( coordlist, x -> x = j ) );
            if number = Length( els ) then
                zerocols := zerocols + 1;
            elif number <> Length( els ) / q then
                stris1 := false;
            fi;
        od;
        i := i + 1;
    od;
    if stris1 then
        return IntFloor((q-1)*(n-zerocols)/q)+zerocols;
    else
        return n;
    fi;
end);

InstallMethod(UpperBoundCoveringRadiusStrength, "method for linear code",
    true, [IsLinearCode], 0,
function(code)
    local q, zerocols, i, j, genmat, n, k, zero, onlyzeroes;
    if Dimension(code) = 0 then
        return WordLength(code);
    fi;
    q := Size(LeftActingDomain(code));
    genmat := GeneratorMat( code );
    n := WordLength( code );
    k := Dimension( code );
    zerocols := 0;
    zero := Zero(LeftActingDomain(code));
    for i in [ 1 .. n ] do
        onlyzeroes := true;
        j := 1;
        while onlyzeroes and j <= k do
            onlyzeroes := ( genmat[ j ][ i ] = zero );
            j := j + 1;
        od;
        if onlyzeroes then
            zerocols := zerocols + 1;
        fi;
    od;

    return IntFloor((q-1)*(n-zerocols)/q) + zerocols;
end);


########################################################################
##
#F  UpperBoundCoveringRadiusGriesmerLike( <code> )
##

InstallMethod(UpperBoundCoveringRadiusGriesmerLike, "unrestrictred code",
    true, [IsCode], 0,
function( code )
    if IsLinearCode( code ) then
        return UpperBoundCoveringRadiusGriesmerLike(code);
    else
        Error("UBCRGriesmerLike: <code> must be a linear code");
    fi;
end);

InstallMethod(UpperBoundCoveringRadiusGriesmerLike, "linear codes", true,
    [IsLinearCode], 0,
function(code)
    local q;
    q := Size( LeftActingDomain( code ) );
    return WordLength( code ) - Sum( [ 1 .. Dimension( code ) ],
                   x -> IntCeiling( MinimumDistance( code ) / q^x ) );
end);


########################################################################
##
#F  UpperBoundCoveringRadiusCyclicCode( <code> )
##

InstallMethod(UpperBoundCoveringRadiusCyclicCode, "unrestricted code",
    true, [IsCode], 0,
function( code )
    if IsCyclicCode( code ) then
        return UpperBoundCoveringRadiusCyclicCode( code );
    else
        Error("UBCRCyclicCode: <code> must be a cyclic code");
    fi;
end);

InstallMethod(UpperBoundCoveringRadiusCyclicCode, "cyclic codes", true,
    [IsCyclicCode], 0,
function(code)
    return WordLength( code ) - Dimension( code ) + 1 -
           IntCeiling( Weight( Codeword( GeneratorPol( code ) ) ) / 2 );
end);


