#############################################################################
##
##  LICPX.gi                                                LICPX subpackage
##
##         LICPX = Logical Implications for homalg ComPleXes
##
##  Copyright 2007-2010, Mohamed Barakat, RWTH-Aachen
##
##  Implementation stuff for the LICPX subpackage.
##
#############################################################################

####################################
#
# global variables:
#
####################################

####################################
#
# logical implications methods:
#
####################################

####################################
#
# immediate methods for properties:
#
####################################

####################################
#
# methods for properties:
#
####################################

####################################
#
# methods for attributes:
#
####################################

InstallMethod( BettiTableOverCoefficientsRing,
        "LICPX: for homalg complexes",
        [ IsHomalgComplex ],
        
  function( C )
    local S, k, weights, min, max, left, z, i, N, deg, M, j, CC;
    
    S := HomalgRing( C );
    
    k := CoefficientsRing( S );
    
    weights := WeightsOfIndeterminates( S );
    
    weights := List( weights, HomalgElementToInteger );
    
    min := 0;
    max := 0;
    for i in weights do
        if i < 0 then
            min := min + i;
        else
            max := max + i;
        fi;
    od;
    
    left := IsHomalgLeftObjectOrMorphismOfLeftObjects( C );
    
    if left then
        z := 0 * S;
    else
        z := S * 0;
    fi;
    
    for i in ObjectDegreesOfComplex( C ) do
        
        N := CertainObject( C, i );
        deg := DegreesOfGenerators( N );
        deg := List( deg, HomalgElementToInteger );
        M := z;
        
        if deg <> [] then
            
            for j in [ min + Minimum( deg ).. max + Maximum( deg ) ] do
                
                if left then
                    M := M + HomogeneousPartOverCoefficientsRing( j, N ) * S;
                else
                    M := M + S * HomogeneousPartOverCoefficientsRing( j, N );
                fi;
                
            od;
            
        fi;
        
        if IsBound( CC ) then
            
            Add( CC, M );
            
        else
            
            if IsCocomplexOfFinitelyPresentedObjectsRep( C ) then
                CC := HomalgCocomplex( M, i );
            else
                CC := HomalgComplex( M, i );
            fi;
            
        fi;
        
    od;
    
    return BettiTable( CC );
        
end );

##
InstallMethod( BettiTable,
        "LICPX: for homalg complexes",
        [ IsHomalgComplex ],
        
  function( C )
    local S, weights, positive, higher_degrees, lower_degrees, factor, cocomplex, degrees,
          min, C_degrees, l, ll, CM, r, beta, ar;
    
    S := HomalgRing( C );
    
    weights := WeightsOfIndeterminates( S );
    
    weights := List( weights, HomalgElementToInteger );
    
    if weights = [ ] then
        Error( "the set of weights is empty" );
    elif IsHomalgElement( weights[ 1 ] ) then
        weights := List( weights, HomalgElementToInteger );
        if Length( weights[ 1 ] ) > 1 then
            Error( "not yet implemented" );
        fi;
        positive := weights[1] > 0;
    elif not IsInt( weights[1] ) then
        Error( "not yet implemented" );
    else
        positive := weights[1] > 0;
    fi;
    
    if positive then
        higher_degrees := MaximumList;
        lower_degrees := MinimumList;
        factor := 1;
    else
        higher_degrees := MinimumList;
        lower_degrees := MaximumList;
        factor := -1;
    fi;
    
    cocomplex := IsCocomplexOfFinitelyPresentedObjectsRep( C );
    
    if cocomplex then
        if not IsHomalgGradedModule( HighestDegreeObject( C ) ) then
            Error( "the highest module was not created as a graded module\n" );
        fi;
    else
        if not IsHomalgGradedModule( LowestDegreeObject( C ) ) then
            Error( "the lowest module was not created as a graded module\n" );
        fi;
    fi;
    
    ## the list of generators degrees of the objects of the complex C
    degrees := List( ObjectsOfComplex( C ), DegreesOfGenerators );
    
    degrees := List( degrees, i -> List( i, HomalgElementToInteger ) );
    
    ## take care of cocomplexes
    if cocomplex then
        degrees := Reversed( degrees );
    fi;
    
    ## the (co)homological degrees of the (co)complex
    C_degrees := ObjectDegreesOfComplex( C );
    
    ## a counting list
    l := [ 1 .. Length( C_degrees ) ];
    
    ## the non-empty list
    ll := Filtered( l, j -> degrees[j] <> [ ] );
    
    ## the graded Castelnuovo-Mumford regularity of the resolved module
    if ll <> [ ] then
        CM := higher_degrees( List( ll, j -> higher_degrees( degrees[j] ) - factor * ( j - 1 ) ) );
    else
        CM := 0;
    fi;
    
    if IsHomalgElement( CM ) then
        
        CM := HomalgElementToInteger( CM );
        
    fi;
    
    ## the lowest generator degree of the lowest object in C
    if ll <> [ ] then
        min := lower_degrees( List( ll, j -> lower_degrees( degrees[j] ) - factor * ( j - 1 ) ) );
    else
        min := CM;
    fi;
    
    if IsHomalgElement( min ) then
        
        min := HomalgElementToInteger( min );
        
    fi;
    
    ## the row range of the Betti diagram
    if positive then
        r := [ min .. CM ];
    else
        r := [ CM .. min ];
    fi;
    
    ## take care of cocomplexes
    if cocomplex then
        if positive then
            r := Reversed( r );
        fi;
        l := Reversed( l );
    fi;
    
    ## the Betti table
    beta := List( r, i -> List( l, j -> Length( Filtered( degrees[j], a -> HomalgElementToInteger( a ) = i + factor * ( j - 1 ) ) ) ) );
    
    ## take care of cocomplexes
    if cocomplex then
        if ll <> [ ] then
            if positive then
                r := [ min .. CM ] + C_degrees[Length( C_degrees )];
            else
                r := Reversed( -[ CM .. min ] + C_degrees[Length( C_degrees )] );
            fi;
            ConvertToRangeRep( r );
        else
            r := [ 0 ];
        fi;
    fi;
    
    if HasIsExteriorRing( S ) and IsExteriorRing( S ) then
        r := r + Length( Indeterminates( S ) );
    fi;
    
    ar := [ beta, r, C_degrees, C ];
    
    if IsBound( C!.display_twist ) and C!.display_twist = true then
        Append( ar, [ [ "twist", Length( Indeterminates( S ) ) - 1 ] ] );
    fi;
    
    if IsBound( C!.higher_vanish ) and IsInt( C!.higher_vanish ) then
        Append( ar, [ [ "higher_vanish", C!.higher_vanish ] ] );
    fi;
    
    if IsBound( C!.markers ) and IsList( C!.markers ) then
        Append( ar, [ [ "markers", C!.markers ] ] );
    fi;
    
    if IsBound( C!.EulerCharacteristic ) and IsUnivariatePolynomial( C!.EulerCharacteristic ) then
        Append( ar, [ [ "EulerCharacteristic", C!.EulerCharacteristic ] ] );
    fi;
    
    ## take care of cocomplexes
    if cocomplex then
        Append( ar, [ "reverse" ] );	## read the row range upside down
    fi;
    
    return CallFuncList( HomalgBettiTable, ar  );
    
end );

