#############################################################################
##
##  LocalizeRing.gi                            LocalizeRingForHomalg package
##
##  Copyright 2009-2011, Mohamed Barakat, University of Kaiserslautern
##                       Markus Lange-Hegermann, RWTH-Aachen University
##
##  Implementations of procedures for localized rings.
##
#############################################################################

##  <#GAPDoc Label="IsHomalgLocalRingRep">
##  <ManSection>
##    <Filt Type="Representation" Arg="R" Name="IsHomalgLocalRingRep"/>
##    <Returns>true or false</Returns>
##    <Description>
##      The representation of &homalg; local rings. <P/>
##      (It is a subrepresentation of the &GAP; representation <Br/>
##      <C>IsHomalgRingOrFinitelyPresentedModuleRep</C>.)
##    <Listing Type="Code"><![CDATA[
DeclareRepresentation( "IsHomalgLocalRingRep",
        IsHomalgRing
        and IsHomalgRingOrFinitelyPresentedModuleRep,
        [ "ring" ] );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>


##  <#GAPDoc Label="IsHomalgLocalRingElementRep">
##  <ManSection>
##    <Filt Type="Representation" Arg="r" Name="IsHomalgLocalRingElementRep"/>
##    <Returns>true or false</Returns>
##    <Description>
##      The representation of elements of &homalg; local rings. <P/>
##      (It is a representation of the &GAP; category <C>IsHomalgRingElement</C>.)
##    <Listing Type="Code"><![CDATA[
DeclareRepresentation( "IsHomalgLocalRingElementRep",
        IsHomalgRingElement,
        [ "pointer" ] );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

##  <#GAPDoc Label="IsHomalgLocalMatrixRep">
##  <ManSection>
##    <Filt Type="Representation" Arg="A" Name="IsHomalgLocalMatrixRep"/>
##    <Returns>true or false</Returns>
##    <Description>
##      The representation of &homalg; matrices with entries in a &homalg; local ring. <P/>
##      (It is a representation of the &GAP; category <C>IsHomalgMatrix</C>.)
##    <Listing Type="Code"><![CDATA[
DeclareRepresentation( "IsHomalgLocalMatrixRep",
        IsHomalgMatrix,
        [ ] );
##  ]]></Listing>
##    </Description>
##  </ManSection>
##  <#/GAPDoc>

## three new types:
BindGlobal( "TheTypeHomalgLocalRing",
        NewType( TheFamilyOfHomalgRings,
                IsHomalgLocalRingRep ) );

BindGlobal( "TheTypeHomalgLocalRingElement",
        NewType( TheFamilyOfHomalgRingElements,
                IsHomalgLocalRingElementRep ) );

BindGlobal( "TheTypeHomalgLocalMatrix",
        NewType( TheFamilyOfHomalgMatrices,
                IsHomalgLocalMatrixRep ) );

####################################
#
# methods for operations:
#
####################################

##  <#GAPDoc Label="AssociatedComputationRing:ring">
##  <ManSection>
##    <Oper Arg="R" Name="AssociatedComputationRing" Label="for homalg local rings"/>
##    <Returns>a &homalg; ring</Returns>
##    <Description>
##      Internally there is a ring, in which computations take place. This is either the global ring or a (not fully working) external pre ring in &Singular; with Mora's algorithm.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( AssociatedComputationRing,
        "for homalg local rings",
        [ IsHomalgLocalRingRep ],
        
  function( R )
    
    if IsBound( R!.AssociatedComputationRing ) then
    
      return R!.AssociatedComputationRing;
    
    else
    
      return R!.ring;
    
    fi;
    
  end
  
);

##  <#GAPDoc Label="AssociatedComputationRing:element">
##  <ManSection>
##    <Oper Arg="r" Name="AssociatedComputationRing" Label="for homalg local ring elements"/>
##    <Returns>a &homalg; ring</Returns>
##    <Description>
##      Internally there is a ring, in which computations take place. This is either the global ring or a (not fully working) external pre ring in &Singular; with Mora's algorithm.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( AssociatedComputationRing,
        "for homalg local ring elements",
        [ IsHomalgLocalRingElementRep ],
        
  function( r )
    
    return AssociatedComputationRing( HomalgRing( r ) );
    
  end
  
);

##  <#GAPDoc Label="AssociatedComputationRing:matrix">
##  <ManSection>
##    <Oper Arg="mat" Name="AssociatedComputationRing" Label="for homalg local matrices"/>
##    <Returns>a &homalg; ring</Returns>
##    <Description>
##      Internally there is a ring, in which computations take place. This is either the global ring or a (not fully working) external pre ring in &Singular; with Mora's algorithm.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( AssociatedComputationRing,
        "for homalg local matrices",
        [ IsHomalgLocalMatrixRep ],
        
  function( A )
    
    return AssociatedComputationRing( HomalgRing(A) );
    
  end

);

##  <#GAPDoc Label="AssociatedGlobalRing:ring">
##  <ManSection>
##    <Oper Arg="R" Name="AssociatedGlobalRing" Label="for homalg local rings"/>
##    <Returns>a (global) &homalg; ring</Returns>
##    <Description>
##      The global &homalg; ring, from which the local ring <A>R</A> was created.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( AssociatedGlobalRing,
        "for homalg local rings",
        [ IsHomalgLocalRingRep ],
        
  function( R )
    
    if IsBound( R!.AssociatedGlobalRing ) then
    
      return R!.AssociatedGlobalRing;
    
    else
    
      return R!.ring;
    
    fi;
    
  end

);

##  <#GAPDoc Label="AssociatedGlobalRing:element">
##  <ManSection>
##    <Oper Arg="r" Name="AssociatedGlobalRing" Label="for homalg local ring elements"/>
##    <Returns>a (global) &homalg; ring</Returns>
##    <Description>
##      The global &homalg; ring, from which the local ring element <A>r</A> was created.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( AssociatedGlobalRing,
        "for homalg local ring elements",
        [ IsHomalgLocalRingElementRep ],
        
  function( r )
    
    return AssociatedGlobalRing( HomalgRing( r ) );
    
  end

);

##  <#GAPDoc Label="AssociatedGlobalRing:matrix">
##  <ManSection>
##    <Oper Arg="mat" Name="AssociatedGlobalRing" Label="for homalg local matrices"/>
##    <Returns>a (global) &homalg; ring</Returns>
##    <Description>
##      The global &homalg; ring, from which the local matrix <A>mat</A> was created.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( AssociatedGlobalRing,
        "for homalg local matrices",
        [ IsHomalgLocalMatrixRep ],
        
  function( A )
    
    return AssociatedGlobalRing( HomalgRing(A) );
    
  end

);

##  <#GAPDoc Label="Numerator:element">
##  <ManSection>
##    <Oper Arg="r" Name="Numerator" Label="for homalg local ring elements"/>
##    <Returns>a (global) &homalg; ring element</Returns>
##    <Description>
##      The numerator from a local ring element <A>r</A>, which is a &homalg; ring element from the computation ring.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( Numerator,
        "for homalg local ring elements",
        [ IsHomalgLocalRingElementRep ],
        
  function( r )
    
    return r!.numer;
    
  end

);

##  <#GAPDoc Label="Denominator:element">
##  <ManSection>
##    <Oper Arg="r" Name="Denominator" Label="for homalg local ring elements"/>
##    <Returns>a (global) &homalg; ring element</Returns>
##    <Description>
##      The denominator from a local ring element <A>r</A>, which is a &homalg; ring element from the computation ring.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( Denominator,
        "for homalg local ring elements",
        [ IsHomalgLocalRingElementRep ],
        
  function( r )
    
    return r!.denom;
    
  end

);

##  <#GAPDoc Label="Numerator:matrix">
##  <ManSection>
##    <Oper Arg="mat" Name="Numerator" Label="for homalg local matrices"/>
##    <Returns>a (global) &homalg; matrix</Returns>
##    <Description>
##      The numerator from a local matrix <A>mat</A>, which is a &homalg; matrix from the computation ring.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( Numerator,
        "for homalg local matrices",
        [ IsHomalgLocalMatrixRep ],
        
  function( M )
    
    return Eval( M )[1];
    
  end

);

##  <#GAPDoc Label="Denominator:matrix">
##  <ManSection>
##    <Oper Arg="mat" Name="Denominator" Label="for homalg local matrices"/>
##    <Returns>a (global) &homalg; ring element</Returns>
##    <Description>
##      The denominator from a local matrix <A>mat</A>, which is a &homalg; matrix from the computation ring.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( Denominator,
        "for homalg local matrices",
        [ IsHomalgLocalMatrixRep ],
        
  function( M )
    
    return Eval( M )[2];
    
  end

);

##  <#GAPDoc Label="Name">
##  <ManSection>
##    <Oper Arg="r" Name="Name" Label="for homalg local ring elements"/>
##    <Returns>a string</Returns>
##    <Description>
##      The name of the local ring element <A>r</A>.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( Name,
        "for homalg local ring elements",
        [ IsHomalgLocalRingElementRep ],

  function( o )
    local name, nnumer, ndenom, s1, s2, s3, s4;
    
    if IsHomalgInternalRingRep( AssociatedComputationRing( o ) ) then
      name := String;
    else
      name := Name;
    fi;

    nnumer := name( Numerator( o ) );
    ndenom := name( Denominator( o ) );

    if Length( nnumer ) <= 2 or not ( '+' in nnumer or '-' in nnumer ) then 
      s1 := "";
      s2 := "";
    else
      s1 := "(";
      s2 := ")";
    fi;
    if Length( ndenom ) <= 2 or not ( '+' in ndenom or '-' in ndenom ) then 
      s3 := "";
      s4 := "";
    else
      s3 := "(";
      s4 := ")";
    fi;
    return Flat( [ s1, nnumer, s2, "/", s3, ndenom, s4 ] );
    
  end

);

InstallMethod( String,
        "for homalg local ring elements",
        [ IsHomalgLocalRingElementRep ],

  function( o )
    
    return Name( o );
    
  end

);

##
InstallMethod( String,
        "for homalg local ring elements",
        [ IsHomalgLocalRingElementRep ],
        
  function( r )
    
    if IsOne( Denominator( r ) ) then
        return String( Numerator( r ) );
    fi;
    
    TryNextMethod( );
    
end );

##
InstallMethod( BlindlyCopyMatrixPropertiesToLocalMatrix,	## under construction
        "for homalg matrices",
        [ IsHomalgMatrix, IsHomalgLocalMatrixRep ],
        
  function( S, T )
    local c;
    
    for c in [ NrRows, NrColumns ] do
        if Tester( c )( S ) then
            Setter( c )( T, c( S ) );
        fi;
    od;
    
    for c in [ IsZero, IsOne, IsDiagonalMatrix ] do
        if Tester( c )( S ) and c( S ) then
            Setter( c )( T, c( S ) );
        fi;
    od;
    
  end

);

##  <#GAPDoc Label="SetMatElm">
##  <ManSection>
##    <Oper Arg="mat, i, j, r, R" Name="SetMatElm" Label="for homalg local matrices"/>
##    <Description>
##      Changes the entry (<A>i,j</A>) of the local matrix <A>mat</A> to the value <A>r</A>. Here <A>R</A> is the (local) &homalg; ring involved in these computations.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( SetMatElm,
        "for homalg local matrices",
        [ IsHomalgLocalMatrixRep and IsMutable, IsPosInt, IsPosInt, IsHomalgLocalRingElementRep, IsHomalgLocalRingRep ],
        
  function( M, r, c, s, R )
    local m, cR, N, M2, e;
    
    cR := AssociatedComputationRing( R );
    
    #Create a new matrix with only the one entry
    N := HomalgInitialMatrix( NrRows( M ), NrColumns( M ), cR );
    SetMatElm( N, r, c, Numerator( s ) );
    ResetFilterObj( N, IsInitialMatrix );
    N := HomalgLocalMatrix( N, Denominator( s ), R );
    
    #set the corresponding entry to zero in the other matrix
    m := Eval( M );
    M2 := m[1];
    SetMatElm( M2, r, c, Zero( cR ) );
    
    #add these matrices
    e := Eval( HomalgLocalMatrix( M2, m[2], R ) + N );
    SetIsMutableMatrix( e[1], true );
    M!.Eval := e;
    
  end

);

##  <#GAPDoc Label="AddToMatElm">
##  <ManSection>
##    <Oper Arg="mat, i, j, r, R" Name="AddToMatElm" Label="for homalg local matrices"/>
##    <Description>
##      Changes the entry (<A>i,j</A>) of the local matrix <A>mat</A> by adding the value <A>r</A> to it. Here <A>R</A> is the (local) &homalg; ring involved in these computations.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( AddToMatElm,
        "for homalg local matrices",
        [ IsHomalgLocalMatrixRep and IsMutable, IsPosInt, IsPosInt, IsHomalgLocalRingElementRep, IsHomalgLocalRingRep ],
        
  function( M, r, c, s, R )
    local N, e;
    
    #create a matrix with just one entry (i,j), which is s
    N := HomalgInitialMatrix( NrRows( M ), NrColumns( M ), AssociatedComputationRing( R ) );
    SetMatElm( N, r, c, Numerator( s ) );
    ResetFilterObj( N, IsInitialIdentityMatrix );
    N := HomalgLocalMatrix( N, Denominator( s ), R );
    
    #and add this matrix to M
    e := Eval( M + N );
    SetIsMutableMatrix( e[1], true );
    M!.Eval := e;
    
  end

);

##  <#GAPDoc Label="MatElmAsString">
##  <ManSection>
##    <Oper Arg="mat, i, j, R" Name="MatElmAsString" Label="for homalg local matrices"/>
##    <Returns>a string</Returns>
##    <Description>
##      Returns the entry (<A>i,j</A>) of the local matrix <A>mat</A> as a string. Here <A>R</A> is the (local) &homalg; ring involved in these computations.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( MatElmAsString,
        "for homalg local matrices",
        [ IsHomalgLocalMatrixRep, IsPosInt, IsPosInt, IsHomalgLocalRingRep ],
        
  function( M, r, c, R )
    local m;
    
    m := Eval( M );
    return Concatenation( [ "(", MatElmAsString( m[1], r, c, AssociatedComputationRing( R ) ), ")/(", Name( m[2] ), ")" ] );
    
  end

);

##  <#GAPDoc Label="MatElm">
##  <ManSection>
##    <Oper Arg="mat, i, j, R" Name="MatElm" Label="for homalg local matrices"/>
##    <Returns>a local ring element</Returns>
##    <Description>
##      Returns the entry (<A>i,j</A>) of the local matrix <A>mat</A>. Here <A>R</A> is the (local) &homalg; ring involved in these computations.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( MatElm,
        "for homalg local matrices",
        [ IsHomalgLocalMatrixRep, IsPosInt, IsPosInt, IsHomalgLocalRingRep ],
        
  function( M, r, c, R )
    local m;
    
    m :=Eval( M );
    return HomalgLocalRingElement( MatElm( m[1], r, c, AssociatedComputationRing( R ) ), m[2], R );
    
  end

);

##  <#GAPDoc Label="Cancel">
##  <ManSection>
##    <Oper Arg="a, b" Name="Cancel" Label="for pairs of ring elements"/>
##    <Returns>two ring elements</Returns>
##    <Description>
##      For <M><A>a</A>=a'*c</M> and <M><A>b</A>=b'*c</M> return <M>a'</M> and <M>b'</M>. The exact form of <M>c</M> depends on whether a procedure for gcd computation is included in the ring package.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( Cancel,
        "for pairs of ring elements from the computation ring",
        [ IsRingElement, IsRingElement ],
        
  function( a, b )
    local za, zb, z, ma, mb, o, g;
    
    za := IsZero( a );
    zb := IsZero( b );
    
    if za and zb then
        
        z := Zero( a );
        
        return [ z, z ];
        
    elif za then
        
        return [ Zero( a ), One( a ) ];
        
    elif zb then
        
        return [ One( a ), Zero( a ) ];
        
    fi;
    
    if IsOne( a ) then
        
        return [ One( a ), b ];
        
    elif IsOne( b ) then
        
        return [ a, One( a ) ];
        
    fi;
    
    ma := a = -One( a );
    mb := b = -One( b );
    
    if ma and mb then
        
        o := One( a );
        
        return [ o, o ];
        
    elif ma then
        
        return [ One( a ), -b ];
        
    elif mb then
        
        return [ -a, One( b ) ];
        
    fi;
    
    g := Gcd( a, b );
    
    return [ a / g, b / g ];
    
end );

InstallMethod( Cancel,
        "for pairs of ring elements from the computation ring",
        [ IsHomalgRingElement, IsHomalgRingElement ],
        
  function( a, b )
    local R, za, zb, z, ma, mb, o, RP, result;
    
    R := HomalgRing( a );
    
    if R = fail then
        TryNextMethod( );
    elif not HasRingElementConstructor( R ) then
        Error( "no ring element constructor found in the ring\n" );
    fi;
    
    za := IsZero( a );
    zb := IsZero( b );
    
    if za and zb then
        
        z := Zero( R );
        
        return [ z, z ];
        
    elif za then
        
        return [ Zero( R ), One( R ) ];
        
    elif zb then
        
        return [ One( R ), Zero( R ) ];
        
    fi;
    
    if IsOne( a ) then
        
        return [ One( R ), b ];
        
    elif IsOne( b ) then
        
        return [ a, One( R ) ];
        
    fi;
    
    ma := IsMinusOne( a );
    mb := IsMinusOne( b );
    
    if ma and mb then
        
        o := One( R );
        
        return [ o, o ];
        
    elif ma then
        
        return [ One( R ), -b ];
        
    elif mb then
        
        return [ -a, One( R ) ];
        
    fi;
    
    RP := homalgTable( R );
    
    if IsBound(RP!.CancelGcd) then
        
        result := RP!.CancelGcd( a, b );
        
        result := List( result, x -> HomalgRingElement( x, R ) );
        
        Assert( 6, result[1] * b = result[2] * a );
        
        return result;
        
    else	#fallback: no cancelation
        
        return [ a, b ];
        
    fi;
    
end );

##
InstallMethod( Cancel,
        "for pairs of global ring elements",
        [ IsRingElement and IsMinusOne, IsRingElement ],
        
  function( a, b )
    
    return [ One( b ), -b ];
    
end );

##
InstallMethod( Cancel,
        "for pairs of global ring elements",
        [ IsRingElement, IsRingElement and IsMinusOne ],
        
  function( a, b )
    
    return [ -a, One( a ) ];
    
end );

##
InstallMethod( Cancel,
        "for pairs of global ring elements",
        [ IsRingElement and IsMinusOne, IsRingElement and IsMinusOne ],
        
  function( a, b )
    local o;
    
    o := One( a );
    
    return [ o, o ];
    
end );

##
InstallMethod( Cancel,
        "for pairs of global ring elements",
        [ IsRingElement and IsOne, IsRingElement ],
        
  function( a, b )
    
    return [ One( a ), b ];
    
end );

##
InstallMethod( Cancel,
        "for pairs of global ring elements",
        [ IsRingElement, IsRingElement and IsOne ],
        
  function( a, b )
    
    return [ a, One( b ) ];
    
end );

##
InstallMethod( Cancel,
        "for pairs of global ring elements",
        [ IsRingElement and IsZero, IsRingElement ],
        
  function( a, b )
    
    return [ Zero( a ), One( a ) ];
    
end );

##
InstallMethod( Cancel,
        "for pairs of global ring elements",
        [ IsRingElement, IsRingElement and IsZero ],
        
  function( a, b )
    
    return [ One( b ), Zero( b ) ];
    
end );

##
InstallMethod( Cancel,
        "for pairs of global ring elements",
        [ IsRingElement and IsZero, IsRingElement and IsZero ],
        
  function( a, b )
    local z;
    
    z := Zero( a );
    
    return [ z, z ];
    
end );

##
InstallMethod( SaveHomalgMatrixToFile,
        "for local rings",
        [ IsString, IsHomalgMatrix, IsHomalgLocalRingRep ],
        
  function( filename, M, R )
  local ComputationRing, NumerString, DenomString;
    
    if LoadPackage( "HomalgToCAS" ) <> true then
       Error( "the package HomalgToCAS failed to load\n" );
    fi;
    
    NumerString := Concatenation( filename, "_numerator" );
    DenomString := Concatenation( filename, "_denominator" );
    
    ComputationRing := AssociatedComputationRing( M );
    SaveHomalgMatrixToFile( NumerString, Numerator( M ), ComputationRing );
    SaveHomalgMatrixToFile( DenomString, HomalgMatrix( [ Denominator( M ) ], 1, 1, ComputationRing ), ComputationRing );
    
    return [ NumerString, DenomString ];
    
end );

##
InstallMethod( LoadHomalgMatrixFromFile,
        "for local rings",
        [ IsString, IsInt, IsInt, IsHomalgLocalRingRep ],
        
  function( filename, r, c, R )
    local ComputationRing, numer, denom, homalgIO;
    
    ComputationRing := AssociatedComputationRing( R );
    
    if IsExistingFile( Concatenation( filename, "_numerator" ) ) and IsExistingFile( Concatenation( filename, "_denominator" ) ) then
    
      numer := LoadHomalgMatrixFromFile( Concatenation( filename, "_numerator" ), r, c, ComputationRing );
      denom := LoadHomalgMatrixFromFile( Concatenation( filename, "_denominator" ), r, c, ComputationRing );
      denom := MatElm( denom, 1, 1 );
    
    elif IsExistingFile( filename ) then
    
      numer := LoadHomalgMatrixFromFile( filename, r, c, ComputationRing );
      denom := One( ComputationRing );
    
    else
    
      Error( "file does not exist" );
    
    fi;
    
    return HomalgLocalMatrix( numer, denom, R );
    
end );

##
InstallMethod( CreateHomalgMatrixFromString,
        "constructor for homalg matrices over local rings",
        [ IsString, IsInt, IsInt, IsHomalgLocalRingRep ],
        
  function( s, r, c, R )
    local mat;
    
    mat := CreateHomalgMatrixFromString( s, r, c, AssociatedComputationRing( R ) );
    
    return R * mat;
    
end );

##
InstallMethod( SetRingProperties,
        "for homalg local rings",
        [ IsHomalgRing and IsLocal ],
        
  function( S )
    local R;
    
    R := S!.AssociatedGlobalRing;
    
#    SetCoefficientsRing( S, R );
#    SetCharacteristic( S, Characteristic( R ) );
    
    if HasIsCommutative( R ) and IsCommutative( R ) then
        SetIsCommutative( S, true );
    fi;
    
#    if HasGlobalDimension( R ) then
#        SetGlobalDimension( S, GlobalDimension( R ) );	## would be wrong
#    fi;
#    if HasKrullDimension( R ) then
#        SetKrullDimension( S, KrullDimension( R ) ); ## would be wrong
#    fi;
#    
#    SetIsIntegralDomain( S, true ); ## would be wrong, see Hartshorne
    
    if HasCoefficientsRing( R ) then
        SetCoefficientsRing( S, CoefficientsRing( R ) );
    fi;
    
    if HasIndeterminatesOfPolynomialRing( R ) then
        SetIndeterminatesOfPolynomialRing( S,
                List( IndeterminatesOfPolynomialRing( R ),
                      x -> x / S )
                );
    fi;
    
    if HasIsFreePolynomialRing( R ) and IsFreePolynomialRing( R ) then
        SetIsIntegralDomain( S, true );
    fi;
    
    if HasKrullDimension( R ) and HasIsIntegralDomain( R ) and IsIntegralDomain( R ) then
        SetKrullDimension( S, KrullDimension( R ) );
    fi;
    
#    SetBasisAlgorithmRespectsPrincipalIdeals( S, true ); ## ???
    
end );

####################################
#
# constructor functions and methods:
#
####################################

##  <#GAPDoc Label="LocalizeAt">
##  <ManSection>
##    <Oper Arg="R, l" Name="LocalizeAt" Label= "for a commutative ring and a maximal ideal"/>
##    <Returns>a local ring</Returns>
##    <Description>
##      If <A>l</A> is a list of elements of the global ring <A>R</A> generating a maximal ideal, the method creates the corresponding localization of <A>R</A> at the complement of the maximal ideal.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( LocalizeAt,
        "constructor for homalg localized rings",
        [ IsHomalgRing and IsCommutative, IsList ],
        
  function( globalR, ideal_gens )
    local RP, localR, n_gens, gens;
    
    if not IsString( ideal_gens ) then
        ideal_gens := List( ideal_gens,
                            function( x )
                              if not IsString( x ) then
                                  return String( x );
                              fi;
                              return x;
                          end );
                          
        ideal_gens := JoinStringsWithSeparator( ideal_gens );
    fi;
    
    ideal_gens := ParseListOfIndeterminates( SplitString( ideal_gens, "," ) );
    
    if ForAny( ideal_gens, IsString ) then
        ideal_gens :=
          List( ideal_gens,
                function( x )
                  if IsString( x ) then
                      return x / globalR;
                  fi;
                  return x;
              end );
              
    fi;
    
    RP := CreateHomalgTableForLocalizedRings( globalR );
    
    if LoadPackage( "RingsForHomalg" ) <> true then
        Error( "the package RingsForHomalg failed to load\n" );
    fi;
    
    if ValueGlobal( "IsHomalgExternalRingInSingularRep" )( globalR ) then
        
        UpdateMacrosOfLaunchedCAS( LocalizeRingMacrosForSingular, homalgStream( globalR ) );
        
        AppendToAhomalgTable( RP, HomalgTableForLocalizedRingsForSingularTools );
        
    fi;
    
    ## create the local ring
    localR := CreateHomalgRing( globalR, [ TheTypeHomalgLocalRing, TheTypeHomalgLocalMatrix ], HomalgLocalRingElement, RP );
    
    SetConstructorForHomalgMatrices( localR,
            function( arg )
              local R, r, ar, M;
              
              R := arg[Length( arg )];
              
              #at least be able to construct 1x1-matrices from lists of ring elements for the fallback IsUnit
              if IsList( arg[1] ) and Length( arg[1] ) = 1 and IsHomalgLocalRingElementRep( arg[1][1] ) then
              
                r := arg[1][1];
              
                return HomalgLocalMatrix( HomalgMatrix( [ Numerator( r ) ], 1, 1, AssociatedComputationRing( R ) ), Denominator( r ), R );
              
              fi;
              
              ar := List( arg,
                          function( i )
                            if IsHomalgLocalRingRep( i ) then
                                return AssociatedComputationRing( i );
                            else
                                return i;
                            fi;
                          end );
              
              M := CallFuncList( HomalgMatrix, ar );
              
              return HomalgLocalMatrix( M, R );
              
            end );
    
    ## for the view methods:
    ## <A homalg local ring>
    ## <A matrix over a local ring>
    localR!.description := " local";
    
    SetIsLocal( localR, true );
    
    #Set the ideal, at which we localize
    n_gens := Length( ideal_gens );
    gens := HomalgMatrix( ideal_gens, n_gens, 1, globalR );
    SetGeneratorsOfMaximalLeftIdeal( localR, gens );
    gens := HomalgMatrix( ideal_gens, 1, n_gens, globalR );
    SetGeneratorsOfMaximalRightIdeal( localR, gens );
    
    localR!.AssociatedGlobalRing := globalR;
    localR!.AssociatedComputationRing := globalR;
    
    SetRingProperties( localR );
    
    return localR;
    
end );

##  <#GAPDoc Label="LocalizeAtZero">
##  <ManSection>
##    <Oper Arg="R" Name="LocalizeAtZero" Label= "for a free polynomial ring"/>
##    <Returns>a local ring</Returns>
##    <Description>
##      This method creates the corresponding localization of <A>R</A> at the complement of the maximal ideal generated by the indeterminates ("at zero").
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
InstallMethod( LocalizeAtZero,
        "constructor for homalg localized rings",
        [ IsHomalgRing and IsFreePolynomialRing ],
        
  function( globalR )
    
    return LocalizeAt( globalR, IndeterminatesOfPolynomialRing( globalR ) );
    
end );

##  <#GAPDoc Label="HomalgLocalRingElement">
##  <ManSection>
##    <Func Arg="numer, denom, R" Name="HomalgLocalRingElement" Label="constructor for local ring elements using numerator and denominator"/>
##    <Func Arg="numer, R" Name="HomalgLocalRingElement" Label="constructor for local ring elements using a given numerator and one as denominator"/>
##    <Returns>a local ring element</Returns>
##    <Description>
##      Creates the local ring element <M><A>numer</A>/<A>denom</A></M> or in the second case <M><A>numer</A>/1</M> for the local ring <A>R</A>. Both <A>numer</A> and <A>denom</A> may either be a string describing a valid global ring element or from the global ring or computation ring.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallGlobalFunction( HomalgLocalRingElement,
  function( arg )
    local nargs, numer, ring, ar, properties, denom, computationring, c, r;
    
    nargs := Length( arg );
    
    if nargs = 0 then
        Error( "empty input\n" );
    fi;
    
    numer := arg[1];
    
    if IsHomalgLocalRingElementRep( numer ) then
        
        ##a local ring element as first argument will just be returned
        return numer;
        
    elif nargs = 2 then
        
        ## extract the properties of the global ring element
        if IsHomalgRing( arg[2] ) then
            ring := arg[2];
            ar := [ numer, One( AssociatedComputationRing( ring ) ), ring ];
            properties := KnownTruePropertiesOfObject( numer );	## FIXME: a huge potential for problems
            Add( ar, List( properties, ValueGlobal ) );  ## at least an empty list is inserted; avoids infinite loops
            return CallFuncList( HomalgLocalRingElement, ar );
        fi;
        
    fi;
    
    properties := [ ];
    
    for ar in arg{[ 2 .. nargs ]} do
        if not IsBound( ring ) and IsHomalgRing( ar ) then
            ring := ar;
        elif IsList( ar ) and ForAll( ar, IsFilter ) then
            Append( properties, ar );
        elif not IsBound( denom ) and ( IsRingElement( ar ) or IsString( ar ) ) then
            denom := ar;
        else
            Error( "this argument (now assigned to ar) should be in { IsHomalgRing, IsList( IsFilter ), IsRingElement, IsString }\n" );
        fi;
    od;
    
    computationring := AssociatedComputationRing( ring );
    
    if not IsBound( denom ) then
       denom := One( numer );
    fi;
    
    if not IsHomalgRingElement( numer ) then
      numer := HomalgRingElement( numer, computationring );
    fi;
    
    if not IsHomalgRingElement( denom ) then
      denom := HomalgRingElement( denom, computationring );
    fi;
    
    c := Cancel( numer, denom );
    numer := c[1] / computationring;
    denom := c[2] / computationring;
    
    if IsBound( ring ) then
        r := rec( numer := numer, denom := denom, ring := ring );
        ## Objectify:
        Objectify( TheTypeHomalgLocalRingElement, r );
    fi;
    
    if properties <> [ ] then
        for ar in properties do
            Setter( ar )( r, true );
        od;
    fi;
    
    return r;
    
end );

##  <#GAPDoc Label="HomalgLocalMatrix">
##  <ManSection>
##    <Func Arg="numer, denom, R" Name="HomalgLocalMatrix" Label="constructor for local matrices using numerator and denominator"/>
##    <Func Arg="numer, R" Name="HomalgLocalMatrix" Label="constructor for local matrices using a given numerator and one as denominator"/>
##    <Returns>a local matrix</Returns>
##    <Description>
##      Creates the local matrix <M><A>numer</A>/<A>denom</A></M> or in the second case <M><A>numer</A>/1</M> for the local ring <A>R</A>. Both <A>numer</A> and <A>denom</A> may either be from the global ring or the computation ring.
##    </Description>
##  </ManSection>
##  <#/GAPDoc>
##
InstallMethod( HomalgLocalMatrix,
        "constructor for matrices over localized rings",
        [ IsHomalgMatrix, IsRingElement, IsHomalgLocalRingRep ],
        
  function( A, r, R )
    local G, type, matrix, HookDenom, ComputationRing, rr, AA;
    
    G := HomalgRing( A );
    
    ComputationRing := AssociatedComputationRing( R );
    
    if IsBound( A!.Denominator ) then
      HookDenom := A!.Denominator;
      Unbind( A!.Denominator );
    else
      HookDenom := One( ComputationRing );
    fi;
    
    if not ( IsHomalgRingElement( r ) and IsIdenticalObj( ComputationRing , HomalgRing( r ) ) ) then
      rr := r / ComputationRing;
    else
      rr := r;
    fi;
    if not IsIdenticalObj( ComputationRing , HomalgRing( A ) ) then
      AA := ComputationRing * A;
    else
      AA := A;
    fi;
    
    matrix := rec( ring := R );
    
    ObjectifyWithAttributes(
        matrix, TheTypeHomalgLocalMatrix,
        Eval, [ AA, rr * HookDenom ]
    );
    
    BlindlyCopyMatrixPropertiesToLocalMatrix( A, matrix );
    
    return matrix;
    
end );

##
InstallMethod( HomalgLocalMatrix,
        "constructor for matrices over localized rings",
        [ IsHomalgMatrix, IsHomalgLocalRingRep ],
        
  function( A, R )
    
    return HomalgLocalMatrix( A, One( AssociatedComputationRing( R ) ), R );
    
end );

##
InstallMethod( \*,
        "for homalg matrices",
        [ IsHomalgLocalRingRep, IsHomalgMatrix ],
        
  function( R, m )
    
    if IsHomalgLocalMatrixRep( m ) then
        TryNextMethod( );
    fi;
    
    return HomalgLocalMatrix( AssociatedComputationRing( R ) * m, R );
    
end );

##
InstallMethod( \*,
        "for matrices over local rings",
        [ IsHomalgRing, IsHomalgLocalMatrixRep ],
        
  function( R, m )
    
    if not IsOne( Denominator( m ) ) then
        TryNextMethod( );
    fi;
    
    return R * Numerator( m );
    
end );

##
InstallMethod( PostMakeImmutable,
        "for homalg local matrices",
        [ IsHomalgLocalMatrixRep and HasEval ],
        
  function( A )
    
    MakeImmutable( Numerator( A ) );
    
end );

##
InstallMethod( SetIsMutableMatrix,
        "for homalg local matrices",
        [ IsHomalgLocalMatrixRep, IsBool ],
        
  function( A, b )
    
    if b = true then
      SetFilterObj( A, IsMutable );
    else
      ResetFilterObj( A, IsMutable );
    fi;
    
    SetIsMutableMatrix( Numerator( A ), b );
    
end );

##
InstallMethod( RingOfDerivations,
        "for local rings",
        [ IsHomalgLocalRingRep ],
        
  function( Rm )
    local R;
    
    R := AssociatedGlobalRing( Rm );
    
    return RingOfDerivations( R );
    
end );

####################################
#
# View, Print, and Display methods:
#
####################################

##
InstallMethod( Display,
        "for homalg local ring elements",
        [ IsHomalgLocalRingElementRep ],
        
  function( r )
    
    Print( Name( r ), "\n" );
    
end );

##
InstallMethod( Display,
        "for homalg matrices over a homalg local ring",
        [ IsHomalgLocalMatrixRep ],
        
  function( A )
    local a;
    
    a := Eval( A );
    
    Display( a[1] );
    
    if IsHomalgInternalRingRep( AssociatedComputationRing( A ) ) then
      Print( "/ ", a[2], "\n" );
    else
      if IsOne( a[2] ) or IsMinusOne( a[2] ) then
        Print( "/ ", Name( a[2] ), "\n" );
      else
        Print( "/(", Name( a[2] ), ")\n" );
      fi;
    fi;
    
end );
