############################################################################
##
#W  field.gi            Alnuth - Kant interface                 Bettina Eick
#W       					               Bjoern Assmann
##

#############################################################################
## 
#M MaximalOrderBasis( F )
##  
InstallMethod(MaximalOrderBasis, "for numberfield", true,[IsNumberField], 0, 
function( F ) 
    local e, T, b, B;
  
    if DegreeOverPrimeField(F)=1 then
        return EquationOrderBasis(F);
    fi;
    e := EquationOrderBasis(F);
    T := MaximalOrderDescriptionKant(F);
    b := List( T, x -> LinearCombination( e, x ) );
    B := Objectify(NewType(FamilyObj(F), IsBasisOfNumberField), rec());
    SetUnderlyingLeftModule( B, F );
    SetBasisVectors( B, b );
    SetUnderlyingBasis( B, e );
    SetTranslationMat( B, T^-1 );
    return B;
end );

#############################################################################
##
#F IsIntegerOfNumberField( F, k )
##
IsIntegerOfNumberField := function( F, k )
    local c;
    c := Coefficients( MaximalOrderBasis(F), k );
    return ForAll( c, IsInt );
end;

#############################################################################
##
#M UnitGroup( F )
##
AddNaturalHomomorphismOfUnitGroup := function( G )
    local gens, rels, H, nat;

    # the generators of G are independent and the first one is torsion
    gens := GeneratorsOfGroup(G);
    rels := List( gens, x -> 0 ); rels[1] := Order( gens[1] );
    H := AbelianPcpGroup( Length(rels), rels );
    nat := GroupHomomorphismByImagesNC( G, H, gens, AsList(Pcp(H)) );

    # add infos
    SetIsBijective( nat, true );
    SetIsMapping( nat, true );
    SetKernelOfMultiplicativeGeneralMapping( nat, true );
    SetIsUnitGroupIsomorphism( nat, true );
    G!.nathom := nat;
end;

AddUnitGroupOfNumberField := function( F, units )
    local gens, G;

    # check if units are known
    if HasUnitGroup(F) then
        gens := GeneratorsOfGroup(UnitGroup(F));
        if not gens = units then Error("wrong units"); fi;
    fi;

    # otherwise add them
    G := GroupByGenerators( units );
    SetIsUnitGroup( G, true );
    SetFieldOfUnitGroup( G, F );
    AddNaturalHomomorphismOfUnitGroup( G );
    SetUnitGroup( F, G );
end;

UnitGroupOfNumberField := function( F )
    local eqn, uni, gen, G, r, H, nat;

    # determine generators
    eqn := EquationOrderBasis(F);
    uni := UnitGroupDescriptionKant(F);
    if uni=[-1] then
        G:=GroupByGenerators([-1*eqn[1]]);
    else
        gen := List( uni, x -> LinearCombination( eqn, x ) );
        G := GroupByGenerators(gen);
    fi;
    # add info
    SetIsUnitGroup( G, true );
    SetFieldOfUnitGroup( G, F );
    AddNaturalHomomorphismOfUnitGroup( G );

    # return
    return G;
end;

InstallMethod( UnitGroup, "for number field", true,
[IsNumberField], 0, function( F ) return
UnitGroupOfNumberField( F ); end);

#############################################################################
##
#F IsUnitOfNumberField( F, k )
##
InstallGlobalFunction( IsUnitOfNumberField, function( F, k )
    if not IsIntegerOfNumberField( F, k ) then return false; fi;
    return AbsInt(Norm( F, k )) = 1;
end );

#############################################################################
##
#F ExponentsOfUnitsOfNumberField( F, elms )
#M ExponentsOfUnits( F, elms )
##
ExponentsOfUnitsOfNumberField := function( F, elms )
    local base, flat, coef, exps, gens;

    # catch a trivial case
    if IsPrimeField(F) then return List( elms, x -> Norm( F, x ) ); fi;

    # determine exponents
    base := EquationOrderBasis( F );
    coef := List( elms, x -> Coefficients( base, x ) );
    exps := ExponentsOfUnitsDescriptionKant( F, coef );

    # add unit group if this is not yet known
    gens := List( exps.units, x -> LinearCombination( base, x ) );
    AddUnitGroupOfNumberField( F, gens );

    # return exponents
    return exps.expns;
end;

InstallMethod( ExponentsOfUnits, "for number fields", true,
[IsNumberField, IsCollection], 0, function( F, elms )
return ExponentsOfUnitsOfNumberField( F, elms ); end);

#############################################################################
##
#F ExponentsOfUnitsWithRank( F, elms )
##
ExponentsOfUnitsWithRank := function( F, elms )
    local base, flat, coef, exps, gens;

    # determine exponents
    base := EquationOrderBasis( F );
    coef := List( elms, x -> Coefficients( base, x ) );
    exps := ExponentsOfUnitsDescriptionWithRankKant( F, coef );

    # add unit group if this is not yet known
    gens := List( exps.units, x -> LinearCombination( base, x ) );
    AddUnitGroupOfNumberField( F, gens );

    # return exponents
    return rec(exps:=exps.expns, rank:=exps.rank);
end;

#############################################################################
##
#F NormCosetsOfNumberField( F, norm )
##
InstallGlobalFunction( NormCosetsOfNumberField, function( F, norm )
    local base, reps, gens;

    # catch a trivial case
    if IsPrimeField(F) then return [norm*One(F)]; fi;

    # get representatives mod unit group
    base := EquationOrderBasis( F );
    reps := NormCosetsDescriptionKant( F, norm );

    # add unit group if this is not yet known
    gens := List( reps.units, x -> LinearCombination( base, x ) );
    AddUnitGroupOfNumberField( F, gens );

    # translate coset reps
    return List( reps.creps, x -> LinearCombination( base, x ) );
end );

#############################################################################
##
#F IsCyclotomicField( F )
##
InstallMethod( IsCyclotomicField, "for number fields", true,
[IsNumberField], 0, function( F )
    local U, t, o;
    U := UnitGroup(F);
    t := GeneratorsOfGroup(U)[1];
    o := Order(t);
    return Phi(o) = DegreeOverPrimeField(F);
end);












