Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  complex.gi   Sprache: unbekannt

 
#######################################################################
##
#F  Cat( <identity>, <properties> ) 
##
##  Constructs a category object from two pieces of data:
##  -- <identity>, a list with information identifying the category, 
##     f.ex. a name and some object the cat is constructed from.
##  -- <properties>, a record with the following entries:
##     name (will be used for printing a categeory), zeroObj (the zero
##     object of the cat), isZeroObj (a test function), zeroMap (an 
##     operation on two objects of the cat), isZeroMapping (a test
##     function), composeMaps (an operation on two maps of the cat), 
##     ker (an operation on the maps of the cat), im (an operation on
##     the maps of the cat), isExact (an operation on two maps of the 
##     cat) and objStr (a function printing the objects of the cat).
##  
InstallGlobalFunction( Cat,
function( identity, properties )
    return Objectify( NewType( NewFamily( "CatFamily", IsCat ),
                               IsCat and IsCatDefaultRep ),
                      rec( identity := identity,
                           properties := properties ) );
end );

#######################################################################
##
#M  \.( <cat>, <propName> ) 
##
##  Method for retrieving properties from a category, where <propName>
##  is one of the properties listed in the Cat() documentation above.
##  
InstallMethod( \.,
[ IsCat, IsPosInt ],
function( cat, propName )
    return cat!.properties.( NameRNam( propName ) );
end );

#######################################################################
##
#M  \=( <cat1>, <cat2> ) 
##
##  Equality test for two categories. Two categories are equal if
##  and only if their identity are the same.
##  
InstallMethod( \=,
[ IsCat, IsCat ],
function( cat1, cat2 )
    return cat1!.identity = cat2!.identity;
end );

#######################################################################
##
#M  PrintObj( <cat> ) 
##
##  Printing a category, using its name.
##  
InstallMethod( PrintObj,
[ IsCat ],
function( cat )
    Print( "<cat: ", cat.name, ">" );
end );

#######################################################################
##
#M  ViewObj( <cat> ) 
##
##  Viewing a cateory, adding no more information than the PrintObj.
##  
InstallMethod( ViewObj,
[ IsCat ],
function( cat )
    Print( cat );
end );

#######################################################################
##
#M  CatOfRightAlgebraModules( <A> ) 
##
##  Returns the category of right modules over some (quotient of a) 
##  path algebra <A>.
##  
InstallMethod( CatOfRightAlgebraModules,
[ IsAlgebra ],
function( A )
    local isZeroObj, composeMaps, isExact, objStr;
    isZeroObj := function( M )
        return Dimension( M ) = 0;
    end;
    composeMaps := function( g, f )
        return f * g;
    end;
    isExact := function( g, f )
        return Dimension( Kernel( g ) ) = Dimension( Image( f ) );
    end;
    objStr := function( M )
        local dims;
        dims := JoinStringsWithSeparator( DimensionVector( M ), "," );
        return Concatenation( "(", dims, ")" );
    end;
    return Cat( [ "right modules", A ],
                rec( name := "right modules over algebra",
                     zeroObj := ZeroModule( A ),
                     isZeroObj := isZeroObj,
                     zeroMap := ZeroMapping,
                     isZeroMapping := IsZero,
                     composeMaps := composeMaps,
                     ker := Kernel,
                     im := Image,
                     isExact := isExact,
                     objStr := objStr ) );
end );


#######################################################################
##
#F  FiniteComplex( <cat>, <basePosition>, <differentials> ) 
##
##  This function returns a finite complex with objects in <cat>.  The
##  differentials are given in the list <differentials> = [d1, ..., dN],
##  an <basePosition> is some integer i. The returned complex has the 
##  map d1 from degree (i) to degree (i-1).
##  
InstallGlobalFunction( FiniteComplex,
function( cat, basePosition, differentials )
    return Complex( cat, basePosition, differentials, "zero", "zero" );
end );

#######################################################################
##
#F  ZeroComplex( <cat> ) 
##
##  Returns the complex in which all objects are the zero object in
##  <cat>.
##  
InstallGlobalFunction( ZeroComplex,
function( cat )
    local fam, C;
    fam := NewFamily( "ComplexesFamily", IsQPAComplex );
    fam!.cat := cat;
    C := Objectify( NewType( fam, IsZeroComplex and IsQPAComplexDefaultRep ),
                    rec( ) );
    SetCatOfComplex( C, cat );
    SetDifferentialsOfComplex( C, ConstantInfList( cat.zeroMap( cat.zeroObj, cat.zeroObj ) ) );
    return C;
end );

#######################################################################
##
#F  StalkComplex( <cat>, <obj>, <degree> ) 
##
##  Returns the stalk complex with the object <obj> from <cat> in
##  degree <degree>.
##  
InstallGlobalFunction( StalkComplex,
function( cat, obj, degree )
    return FiniteComplex( cat, degree,
                          [ cat.zeroMap( obj, cat.zeroObj ),
                            cat.zeroMap( cat.zeroObj, obj ) ] );
end );

#######################################################################
##
#F  ShortExactSequence( <cat>, <f>, <g> ) 
##
##  Returns a complex with three non-zero consecutive objects, and zero
##  objects elsewhere, such that the complex is exact: The image of <f>
##  should be the kernel of <g>, and <f> should be injective, and <g>
##  should be surjective. The function checks that this is the case, 
##  and returns an error otherwise.
##  
InstallGlobalFunction( ShortExactSequence,
function( cat, f, g )
    local SES;
    SES := FiniteComplex( cat, 0, [ g, f ] );
    if not IsShortExactSequence( SES ) then
        Error( "not exact\n" );
    fi;
    return SES;
end );

#######################################################################
##
#F  Complex( <cat>, <basePosition>, <middle>, <positive>, <negative> ) 
##
##  Constructs a complex, not necessarily finite, from the given data.
##  See the QPA manual for detailed information on the input data.
##  
InstallGlobalFunction( Complex,
function( cat, basePosition, middle, positive, negative )
    local checkDifferentials, checkDifferentialList, checkDifferentialListWithRepeat,
          positiveRepeat, negativeRepeat,
          fam, C, basePositionL, middleL, positiveL, negativeL,
          firstMiddleObj, lastMiddleObj, checkNewDifferential,
          firstMap, next, secondMap;

    # check that all consecutive differentials compose to zero
    checkDifferentials := function( topDegree, indices, lists, listNames )
        local degrees, diffs, diffNames;
        degrees := [ topDegree, topDegree - 1 ];
        diffs := List( [1,2], i -> lists[ i ][ indices[ i ] ] );
        diffNames := List( [1,2], i -> Concatenation(
          "differential ", String( degrees[ i ] ),
          " (element ", String( indices[ i ] ), " in ", String( listNames[ i ] ), " list)" ) );
        if Range( diffs[ 1 ] ) <> Source( diffs[ 2 ] ) then
            Error( "range of ", diffNames[ 1 ], " is not the same as source of ",
                   diffNames[ 2 ], ".\n" );
        fi;
        if not cat.isZeroMapping( cat.composeMaps( diffs[ 2 ], diffs[ 1 ] ) ) then
            Error( "non-zero composition of ", diffNames[ 2 ],
                   " and ", diffNames[ 1 ], ".\n" );
        fi;
    end;

    checkDifferentialList := function( list, startDegree, listName )
        local i;
        for i in [ 2 .. Length( list ) ] do
            checkDifferentials( startDegree + i - 1,
                                [ i, i - 1 ],
                                [ list, list ],
                                [ listName, listName ] );
        od;
    end;

    checkDifferentialListWithRepeat := function( list, startDegree, listName,
                                                 repeatDirection )
                                       checkDifferentialList( list, startDegree, listName );
        checkDifferentials( startDegree + Length( list ) * repeatDirection,
                            [ 1, Length( list ) ],
                            [ list, list ], [ listName, listName ] );
    end;

    checkDifferentialList( middle, basePosition, "middle" );
    if positive[ 1 ] = "repeat" then
        positiveRepeat := positive[ 2 ];
        checkDifferentialListWithRepeat( positiveRepeat, basePosition + Length( middle ),
                                         "positive", 1 );
        if Length( middle ) > 0 then
            checkDifferentials( basePosition + Length( middle ),
                                [ 1, Length( middle ) ],
                                [ positiveRepeat, middle ],
                                [ "positive", "middle" ] );
        fi;
    fi;
    if negative[ 1 ] = "repeat" then
        negativeRepeat := Reversed( negative[ 2 ] );
        checkDifferentialListWithRepeat( negativeRepeat, basePosition + Length( middle ),
                                         "negative", 1 );
        if Length( middle ) > 0 then
            checkDifferentials( basePosition,
                                [ 1, Length( negativeRepeat ) ],
                                [ middle, negativeRepeat ],
                                [ "middle", "negative" ] );
        fi;
    fi;
    if positive[ 1 ] = "repeat" and negative[ 1 ] = "repeat" and Length( middle ) = 0 then
        checkDifferentials( basePosition,
                            [ 1, Length( negativeRepeat ) ],
                            [ positiveRepeat, negativeRepeat ],
                            [ "positive", "negative" ] );
    fi;

    # Create the complex object

    fam := NewFamily( "family of complexes", IsQPAComplex );
    fam!.cat := cat;

    C := Objectify( NewType( fam, IsQPAComplex and IsQPAComplexDefaultRep ),
                    rec( ) );
    SetCatOfComplex( C, cat );

    # Normalize middle list if positive or negative is zero

    basePositionL := basePosition;
    middleL := middle;
    if positive = "zero" then
        lastMiddleObj := Source( middle[ Length( middle ) ] );
        # add zero object at the end if necessary:
        if not cat.isZeroObj( lastMiddleObj ) then
            middleL := Concatenation( middleL,
                                      [ cat.zeroMap( cat.zeroObj, lastMiddleObj ) ] );
        fi;
        # cut away superfluous zero objects:
        while Length( middleL ) > 0 and cat.isZeroObj( Range( middleL[ Length( middleL ) ] ) ) do
            middleL := middleL{ [ 1 .. Length( middleL ) - 1 ] };
        od;
    fi;
    if negative = "zero" then
        firstMiddleObj := Range( middle[ 1 ] );
        # add zero object at the end if necessary:
        if not cat.isZeroObj( firstMiddleObj ) then
            middleL := Concatenation( [ cat.zeroMap( firstMiddleObj, cat.zeroObj ) ],
                                      middleL );
            basePositionL := basePositionL - 1;
        fi;
        # cut away superfluous zero objects:
        while Length( middleL ) > 0 and cat.isZeroObj( Source( middleL[ 1 ] ) ) do
            middleL := middleL{ [ 2 .. Length( middleL ) ] };
            basePositionL := basePositionL + 1;
        od;
    fi;

    if positive = "zero" and Length( middleL ) = 0 and negative[ 1 ] = "next/repeat" then
      firstMap := negative[ 3 ];
      next := negative[ 2 ];
      secondMap := next( firstMap );
      if cat.isZeroMapping( firstMap ) and cat.isZeroMapping( secondMap ) then
        return ZeroComplex( cat );
      fi;
    fi;
    if negative = "zero" and Length( middleL ) = 0 and positive[ 1 ] = "next/repeat" then
      firstMap := positive[ 3 ];
      next := positive[ 2 ];
      secondMap := next( firstMap );
      if cat.isZeroMapping( firstMap ) and cat.isZeroMapping( secondMap ) then
        return ZeroComplex( cat );
      fi;
    fi;

    if positive = "zero" then
        positiveL := [ "repeat", [ cat.zeroMap( cat.zeroObj, cat.zeroObj ) ] ];
    elif positive[ 1 ] = "pos" then
        positiveL := ShallowCopy( positive );
        positiveL[ 2 ] := function( i )
            return positive[ 2 ]( C, i );
        end;
    else
        positiveL := positive;
    fi;
    if negative = "zero" then
        negativeL := [ "repeat", [ cat.zeroMap( cat.zeroObj, cat.zeroObj ) ] ];
    elif negative[ 1 ] = "pos" then
        negativeL := ShallowCopy( negative );
        negativeL[ 2 ] := function( i )
            return negative[ 2 ]( C, i );
        end;
    else
        negativeL := negative;
    fi;
            
    checkNewDifferential := function( i, dir, type )
        local degrees, diffs;
        if dir = 1 then
            degrees := [ i - 1, i ];
        else
            degrees := [ i, i + 1 ];
        fi;
        diffs := [ DifferentialOfComplex( C, degrees[ 1 ] ),
                   DifferentialOfComplex( C, degrees[ 2 ] ) ];
        if Source( diffs[ 1 ] ) <> Range( diffs[ 2 ] ) then
            Error( "source of differential ", degrees[ 1 ],
                   " is not the same as range of differential ",
                   degrees[ 2 ], " in complex\n   ", C, "\n" );
        fi;
        if not cat.isZeroMapping( cat.composeMaps( diffs[ 1 ], diffs[ 2 ] ) ) then
            Error( "nonzero composition of differentials ", degrees[ 1 ],
                   " and ", degrees[ 2 ], " in complex\n   ", C, "\n" );
        fi;
    end;

    SetDifferentialsOfComplex( C,
                               MakeInfList( basePositionL, middleL, positiveL, negativeL,
                                            checkNewDifferential ) );

    return C;

end );

#######################################################################
##
#M  DifferentialOfComplex( <C>, <i> ) 
##
##  Returns the differential in degree <i> of the complex <C>.
##  
InstallMethod( DifferentialOfComplex,
[ IsQPAComplex, IsInt ],
function( C, i )
    return DifferentialsOfComplex( C )^i;
end );

#######################################################################
##
#M  ObjectOfComplex( <C>, <i> ) 
##
##  Returns the object in degree <i> of the complex <C>.
##  
InstallMethod( ObjectOfComplex,
[ IsQPAComplex, IsInt ],
function( C, i )
    return Source( DifferentialOfComplex( C, i ) );
end );

#######################################################################
##
#M  \^( <C>, <i> ) 
##
##  Is this in use??
##  
InstallMethod( \^,
[ IsQPAComplex, IsInt ],
function( C, i )
    return DifferentialOfComplex( C, i );
end );

#######################################################################
##
#M  CyclesOfComplex( <C>, <i> ) 
##
##  For a complex <C> and an integer <i>. Returns the i-cycle of the
##  complex, that is the subobject Ker(d_i) of the object in degree i.
##  
InstallMethod( CyclesOfComplex,
[ IsQPAComplex, IsInt ],
function( C, i )
    return Kernel( DifferentialOfComplex( C, i ) );
end );

#######################################################################
##
#M  BoundariesOfComplex( <C>, <i> ) 
##
##  For a complex <C> and an integer <i>. Returns the i-boundary of the
##  complex, that is the subobject Im(d_{i+1}) of the object in degree i.
##  
InstallMethod( BoundariesOfComplex,
[ IsQPAComplex, IsInt ],
function( C, i )
    return Image( DifferentialOfComplex( C, i + 1 ) );
end );

#######################################################################
##
#M  HomologyOfComplex( <C>, <i> ) 
##
##  For a complex <C> and an integer <i>. Returns the ith homology of 
##  the complex.
##
##  TODO: Does not currently work (see the documentation).
##  
InstallMethod( HomologyOfComplex, # TODO: this does not work
[ IsQPAComplex, IsInt ],
function( C, i )
    return CyclesOfComplex( C, i ) / BoundariesOfComplex( C, i );
end );

#######################################################################
##
#M  IsFiniteComplex( <C> ) 
##
##  Returns true if the complex <C> is a finite complex, false otherwise.
##  
InstallMethod( IsFiniteComplex,
[ IsQPAComplex ],
function( C )
    local upbound, lowbound;

    upbound := UpperBound( C );
    lowbound := LowerBound( C );

    if IsZeroComplex( C ) then
        return true;
    elif IsInt( upbound ) and IsInt( lowbound ) then
        return true;
      elif ( upbound in [ -infinity, infinity ] ) or ( lowbound in [ -infinity, infinity ] ) then
        return false;
    else
        return fail;
    fi;
end );

#######################################################################
##
#M  LengthOfComplex( <C> ) 
##
##  Returns the length of the complex <C>. If C is a zero complex, then
##  the length is zero. If C is a finite complex, the length is the
##  upper bound - the lower bound + 1. If C is an infinite complex, the
##  length is infinity.
##  
InstallMethod( LengthOfComplex,
[ IsQPAComplex ],
function( C )
    local finiteness;
    finiteness := IsFiniteComplex( C );
    if IsZeroComplex( C ) then
        return 0;
    elif finiteness = true then
        return UpperBound( C ) - LowerBound( C ) + 1;
    elif finiteness = false then
      return infinity;
    else
        return fail;
    fi;
end );

#######################################################################
##
#M  HighestKnownDegree( <C> ) 
##
##  Returns the greatest integer i such that the object at position i 
##  is known (or computed). For a finite complex, this will be infinity.
##  
InstallMethod( HighestKnownDegree,
[ IsQPAComplex ],
function( C )
    return HighestKnownPosition( DifferentialsOfComplex( C ) );
end );

#######################################################################
##
#M  LowestKnownDegree( <C> ) 
##
##  Returns the smallest integer i such that the object at position i 
##  is known (or computed). For a finite complex, this will be negative
##  infinity.
##  
InstallMethod( LowestKnownDegree,
[ IsQPAComplex ],
function( C )
    return LowestKnownPosition( DifferentialsOfComplex( C ) );
end );

#######################################################################
##
#M  IsExactSequence( <C> ) 
##
##  True if the complex <C> is exact in every degree. If the complex
##  is not finite and not repeating, the function fails.
##  
InstallMethod( IsExactSequence,
[ IsQPAComplex ],
function( C )
    return ForEveryDegree( C, CatOfComplex( C ).isExact );
end );

#######################################################################
##
#M  IsExactInDegree( <C>, <i> ) 
##
##  Returns true if the complex <C> is exact in degree <i>.
##  
InstallMethod( IsExactInDegree,
[ IsQPAComplex, IsInt ],
function( C, i )
    return CatOfComplex( C ).isExact( DifferentialOfComplex( C, i ),
                                      DifferentialOfComplex( C, i + 1 ) );
end );

#######################################################################
##
#M  IsShortExactSequence( <C> ) 
##
##  Returns true if the complex <C> is exact and has only three non-zero
##  objects, which are consecutive.
##  
InstallMethod( IsShortExactSequence,
[ IsQPAComplex ],
function( C )
    local length;
    length := LengthOfComplex( C );
    if length = fail then return fail; fi;
    if length = 3 then
        return IsExactSequence( C );
    else
        return false;
    fi;
end );

#######################################################################
##
#M  ForEveryDegree( <C>, <func> ) 
##
##  <C> is a complex, and <func> is a function operating on two conse-
##  cutive differentials, returning either true or false.
##
##  Returns true if func(d_i, d_{i+1}) is true for all i. Fails if this
##  is unknown, i.e. if the complex is infinite and not repeating.
##  
InstallMethod( ForEveryDegree, # TODO: misleading name?
[ IsQPAComplex, IsFunction ],
function( C, func )
    local diffs, pos, neg, i;
    diffs := DifferentialsOfComplex( C );
    pos := PositivePart( diffs );
    neg := NegativePart( diffs );
    if not (IsRepeating( PositivePart( diffs ) ) and
            IsRepeating( NegativePart( diffs ) ) ) then
        return fail;
    fi;
    for i in [ StartPosition( neg ) - Length( RepeatingList( neg ) )
               .. StartPosition( pos ) + Length( RepeatingList( pos ) ) ] do
        if not func( diffs^i, diffs^(i+1) ) then
            return false;
        fi;
    od;
    return true;
end );

#######################################################################
##
#M  UpperBound( <C> ) 
##
##  Returns: 
##  If it exists: The smallest integer i such that the object at 
##  position i is non-zero, but for all j > i the object at position j 
##  is zero. If C is not a finite complex, the operation will return 
##  fail or infinity, depending on how C was defined.
##  
InstallMethod( UpperBound,
[ IsQPAComplex ],
function( C )
    local cat, diffs, positive, i;

    cat := CatOfComplex( C );
    diffs := DifferentialsOfComplex( C );
    positive := PositivePart( diffs );

    if IsZeroComplex( C ) then
      return -infinity;
    elif IsRepeating( positive ) and Length( RepeatingList( positive ) ) = 1
      and cat.isZeroObj( ObjectOfComplex( C, StartPosition( positive ) ) ) then
        i := MiddleEnd( diffs ) - 1;
        while cat.isZeroObj( ObjectOfComplex( C, i ) ) do
            i := i - 1;
            if i < MiddleStart( diffs ) then
                return fail;
            fi;
        od;
        return i;
    elif IsRepeating( positive ) then
      return infinity;
    else
        return fail;
    fi;
end );

#######################################################################
##
#M  LowerBound( <C> ) 
##
##  Returns:
##  If it exists: The greatest integer i such that the object at 
##  position i is non-zero, but for all j < i the object at position j 
##  is zero. If C is not a finite complex, the operation will return 
##  fail or negative infinity, depending on how C was defined.
##
InstallMethod( LowerBound,
[ IsQPAComplex ],
function( C )
    local cat, diffs, negative, i;

    cat := CatOfComplex( C );
    diffs := DifferentialsOfComplex( C );
    negative := NegativePart( diffs );

    if IsZeroComplex( C ) then
      return infinity;
    elif IsRepeating( negative ) and Length( RepeatingList( negative ) ) = 1
      and cat.isZeroObj( ObjectOfComplex( C, StartPosition( negative ) ) ) then
        i := MiddleStart( diffs );
        while cat.isZeroObj( ObjectOfComplex( C, i ) ) do
            i := i + 1;
            if i > MiddleEnd( diffs ) then
                return fail;
            fi;
        od;
        return i;
    elif IsRepeating( negative ) then
      return -infinity;
    else
        return fail;
    fi;
end );

#######################################################################
##
#M  IsPositiveRepeating( <C> ) 
##
##  Returns true if the positive part of the differential list of the
##  complex <C> is repeating.
##
InstallMethod( IsPositiveRepeating,
[ IsQPAComplex ],
function( C )
    return IsRepeating( PositivePart( DifferentialsOfComplex( C ) ) );
end );

#######################################################################
##
#M  IsNegativeRepeating( <C> ) 
##
##  Returns true if the negative part of the differential list of the
##  complex <C> is repeating.
##
InstallMethod( IsNegativeRepeating,
[ IsQPAComplex ],
function( C )
    return IsRepeating( NegativePart( DifferentialsOfComplex( C ) ) );
end );

#######################################################################
##
#M  PositiveRepeatDegrees( <C> ) 
##
##  Returns a list describing the positions of the positive repeating
##  differentials.  The returned list is of the form [first .. last], 
##  where 'first' is the smallest degree such that the differential
##  ending there is in the positive repeating part. After the degree 
##  'last', the same differentials start repeating. Fails if
##  IsPositiveRepeating(C) is false.
##
InstallMethod( PositiveRepeatDegrees,
[ IsQPAComplex ],
function( C )
    local positive, first, last;
    positive := PositivePart( DifferentialsOfComplex( C ) );
    if not IsRepeating( positive ) then
        return fail;
    fi;
    first := StartPosition( positive );
    last := first + Length( RepeatingList( positive ) ) - 1;
    return [ first .. last ];
end );

#######################################################################
##
#M  NegativeRepeatDegrees( <C> ) 
##
##  Returns a list describing the positions of the negative repeating
##  differentials.  The returned list is of the form [last .. first], 
##  where 'last' is the greatest degree such that the differential
##  starting there is in the negative repeating part. After the degree 
##  'first', the same differentials start repeating. Fails if
##  IsNegativeRepeating(C) is false.
##
InstallMethod( NegativeRepeatDegrees,
[ IsQPAComplex ],
function( C )
    local negative, first, last;
    negative := NegativePart( DifferentialsOfComplex( C ) );
    if not IsRepeating( negative ) then
        return fail;
    fi;
    first := StartPosition( negative );
    last := first - Length( RepeatingList( negative ) ) + 1;
    return [ last .. first ];
end );

#######################################################################
##
#M  Shift( <C>, <i> ) 
##
##  Returns the complex C[i].  Note that the signs of the differentials
##  change if i is odd.
##
InstallMethod( Shift,
[ IsQPAComplex, IsInt ],
function( C, shift )
    local newDifferentials;

    newDifferentials := Shift( DifferentialsOfComplex( C ), shift );
    if shift mod 2 = 1 then
        newDifferentials := InfList( newDifferentials, d -> -d );
    fi;

    return ComplexByDifferentialList( CatOfComplex( C ), newDifferentials );

end );

#######################################################################
##
#M  ShiftUnsigned( <C>, <i> ) 
##
##  Returns a complex with the same objects as C[i], but with the 
##  differentials of C shifted without changing the signs.  A 'non-
##  algebraic' operation, but useful for manipulating complexes.
##
InstallMethod( ShiftUnsigned,
[ IsQPAComplex, IsInt ],
function( C, shift )
    local newDifferentials;
    
    newDifferentials := Shift( DifferentialsOfComplex( C ), shift );
    return ComplexByDifferentialList( CatOfComplex( C ), newDifferentials );

end );

#######################################################################
##
#M  YonedaProduct( <C1>, <C2> ) 
##
##  <C1> and <C2> are complexes such that the object in degree LowerBound(C)
##  equals the object in degree UpperBound(D).  The returned complex
##  is the Yoneda product of <C1> and <C2> (see the QPA documentation
##  for a more precise definition).
##  
InstallMethod( YonedaProduct,
[ IsQPAComplex, IsQPAComplex ],
function( C1, C2 )
    local cat, lowbound1, upbound2, diff1, diff2, connection, diffs;

    if IsZeroComplex( C1 ) then
      return C2;
    fi;

    cat := CatOfComplex( C1 );

    lowbound1 := LowerBound( C1 );
    upbound2 := UpperBound( C2 );

    if not IsInt( lowbound1 ) then
        Error( "first argument in Yoneda product must be bounded below" );
    fi;
    if not IsInt( upbound2 ) then
        Error( "second argument in Yoneda product must be bounded above" );
    fi;
    if ObjectOfComplex( C1, lowbound1 ) <> ObjectOfComplex( C2, upbound2 ) then
        Error( "non-compatible complexes for Yoneda product" );
    fi;

    diff1 := Shift( DifferentialsOfComplex( C1 ), lowbound1 - upbound2 + 1 );
    diff2 := DifferentialsOfComplex( C2 );
    connection := cat.composeMaps( C2^upbound2, C1^(lowbound1+1) );

    diffs := InfConcatenation( PositivePartFrom( diff1, upbound2 + 1 ),
                               FiniteInfList( 0, [ connection ] ),
                               NegativePartFrom( diff2, upbound2 - 1 ) );

    return ComplexByDifferentialList( cat, diffs );

end );

#######################################################################
##
#M  GoodTruncationBelow( <C>, <i> ) 
##
##  Not working at the moment.  Suppose that C is a complex
##    ... --> C_{i+1} --> C_i --> C_{i-1} --> ...
##
##  then the function should return the complex
##    ... --> C_{i+1} --> Z_i --> 0 --> 0 --> ...
##
##  where Z_i is the i-cycle of C.
##  
InstallMethod( GoodTruncationBelow,
[ IsQPAComplex, IsInt ],
function( C, i )
    local cat, difflist, truncpart, newpart, zeropart, newdifflist, kerinc;

    cat := CatOfComplex( C );
    difflist := DifferentialsOfComplex( C );
    truncpart := PositivePartFrom( difflist, i+2 );
    kerinc := KernelInclusion( DifferentialOfComplex( C, i ) );
    newpart := FiniteInfList( i, [ cat.zeroMap( Source(kerinc), cat.zeroObj ),
                                   LiftingInclusionMorphisms( kerinc, DifferentialOfComplex( C, i+1 ) ) ] );
    zeropart := NegativePartFrom( DifferentialsOfComplex( ZeroComplex( cat ) ),
                                  i-1 );
    newdifflist := InfConcatenation( truncpart, newpart, zeropart );
    
    return ComplexByDifferentialList( cat, newdifflist );

end );

#######################################################################
##
#M  GoodTruncationAbove( <C>, <i> ) 
##
##  Not working at the moment.  Suppose that C is a complex
##    ... --> C_{i+1} --> C_i --> C_{i-1} --> ...
##
##  then the function should return the complex
##    ... --> 0 --> C_i/Z_i --> C_{i-1} --> 0 --> ...
##
##  where Z_i is the i-cycle of C.
##  
InstallMethod( GoodTruncationAbove,
 [ IsQPAComplex, IsInt ],
function( C, i )
    local cat, difflist, truncpart, newpart, zeropart, newdifflist, factor, factorinclusion,
          kerinc, factorproj;

    cat := CatOfComplex( C );
    difflist := DifferentialsOfComplex( C );
    truncpart := NegativePartFrom( difflist, i-1 );
    kerinc := KernelInclusion( DifferentialOfComplex( C, i ) );
    factorproj := CoKernelProjection( kerinc );
    factor := Range( factorproj );
    factorinclusion := cat.zeroMap( factor, ObjectOfComplex( C, i-1 ) ); #TODO
    newpart := FiniteInfList( i, [ factorinclusion, cat.zeroMap( cat.zeroObj, factor ) ] );

    zeropart := NegativePartFrom( DifferentialsOfComplex( ZeroComplex( cat ) ),
                                  i+2 );
    newdifflist := InfConcatenation( zeropart, newpart, truncpart );
    
    return ComplexByDifferentialList( cat, newdifflist );

end );

# TODO!
# InstallMethod( GoodTruncation, [ IsQPAComplex, IsInt, IsInt ] );

#######################################################################
##
#M  BrutalTruncationBelow( <C>, <i> ) 
##
##  Suppose that C is a complex
##    ... --> C_{i+1} --> C_i --> C_{i-1} --> ...
##
##  then the function returns the complex
##    ... --> C_{i+1} --> C_i --> 0 --> 0 --> ...
##
InstallMethod( BrutalTruncationBelow,
[ IsQPAComplex, IsInt ],
function( C, i )
    local cat, difflist, truncpart, newpart, zeropart, newdifflist;
    
    cat := CatOfComplex( C );
    difflist := DifferentialsOfComplex( C );
    truncpart := PositivePartFrom( difflist, i+1 );
    newpart := FiniteInfList( i, [ cat.zeroMap( ObjectOfComplex( C, i), 
                                                cat.zeroObj) ] );
    zeropart := NegativePartFrom( DifferentialsOfComplex( ZeroComplex( cat ) ),
                                  i-1 );
    newdifflist := InfConcatenation( truncpart, newpart, zeropart );
    
    return ComplexByDifferentialList( cat, newdifflist );

end );

#######################################################################
##
#M  BrutalTruncationAbove( <C>, <i> ) 
##
##  Suppose that C is a complex
##    ... --> C_{i+1} --> C_i --> C_{i-1} --> ...
##
##  then the function returns the complex
##    ... --> 0 --> C_i --> C_{i-1} --> 
##
InstallMethod( BrutalTruncationAbove,
[ IsQPAComplex, IsInt ],
function( C, i )
    local cat, difflist, truncpart, newpart, zeropart, newdifflist;
    
    cat := CatOfComplex( C );
    difflist := DifferentialsOfComplex( C );
    truncpart := NegativePartFrom( difflist, i );
    newpart := FiniteInfList( i+1, [ cat.zeroMap( cat.zeroObj,
                                                ObjectOfComplex( C, i )) ] );
    zeropart := PositivePartFrom( DifferentialsOfComplex( ZeroComplex( cat ) ),
                                  i+2 );
    newdifflist := InfConcatenation( zeropart, newpart, truncpart );
    
    return ComplexByDifferentialList( cat, newdifflist );

end );

#######################################################################
##
#M  BrutalTruncation( <C>, <i>, <j> ) 
##
##  Suppose that C is a complex
##    ... --> C_{i+1} --> C_i --> C_{i-1} --> ...
##
##  then the function returns the complex
##    ... --> 0 --> C_i --> C_{i-1} --> ... --> C_j --> 0 --> ...
##
InstallMethod( BrutalTruncation, 
[ IsQPAComplex, IsInt, IsInt ],
function( C, i, j )
    local cat, difflist, middlediffs, truncpart, newpart1, zeropart1, 
          newpart2, zeropart2, newdifflist;
    
    if( j > i ) then
        Error( "First input integer must be greater than or equal to the second" );
    fi;
    
    return BrutalTruncationAbove( BrutalTruncationBelow( C, j ), i );
end );

#######################################################################
##
#O  SyzygyTruncation( <C>, <i> ) 
##
##  Suppose that C is a complex
##    ... --> C_{i+1} --> C_i --> C_{i-1} --> ...
##
##  then the function returns the complex
##    ... --> 0 --> ker(d_i) --> C_i --> C_{i-1} --> ...
##
InstallMethod( SyzygyTruncation, 
[ IsQPAComplex, IsInt ],
function( C, i )
    local cat, difflist, truncpart, kernelinc, newpart, kernel,
          zeropart, newdifflist;
    
    cat := CatOfComplex( C );
    difflist := DifferentialsOfComplex( C );
    truncpart := NegativePartFrom( difflist, i );

    kernelinc := KernelInclusion( DifferentialOfComplex( C, i ) );
    kernel := Source( kernelinc );
    newpart := FiniteInfList( i+1, [ kernelinc, 
                                     cat.zeroMap( cat.zeroObj, kernel ) ] );
    zeropart := PositivePartFrom( DifferentialsOfComplex( ZeroComplex( cat ) ),
                                  i+3 );

    newdifflist := InfConcatenation( zeropart, newpart, truncpart );

    return ComplexByDifferentialList( cat, newdifflist );

end );

#######################################################################
##
#O  CosyzygyTruncation( <C>, <i> ) 
##
##  Suppose that C is a complex
##    ... --> C_{i+1} --> C_i --> C_{i-1} --> ...
##
##  then the function returns the complex
##    ... --> C_i --> C_{i-1} --> cok(d_i) --> 0 --> ...
##
InstallMethod( CosyzygyTruncation, 
[ IsQPAComplex, IsInt ],
function( C, i )
    local cat, difflist, truncpart, newpart,
          zeropart, newdifflist, cokerproj, coker;
    
    cat := CatOfComplex( C );
    difflist := DifferentialsOfComplex( C );
    truncpart := PositivePartFrom( difflist, i );

    cokerproj := CoKernelProjection( DifferentialOfComplex( C, i ) );
    coker := Range( cokerproj );
    newpart := FiniteInfList( i-2, [ cat.zeroMap( coker, cat.zeroObj ),
                                     cokerproj ] );

    zeropart := NegativePartFrom( DifferentialsOfComplex( ZeroComplex( cat ) ),
                                  i-3 );

    newdifflist := InfConcatenation( truncpart, newpart, zeropart );

    return ComplexByDifferentialList( cat, newdifflist );

end );

#######################################################################
##
#O  SyzygyCosyzygyTruncation( <C>, <i>, <j> ) 
##
##  Suppose that C is a complex
##    ... --> C_{i+1} --> C_i --> C_{i-1} --> ...
##
##  then the function returns the complex
##    ... --> 0 --> ker(d_i) --> C_i --> ... --> C_{j+1} --> cok(d_j) --> 0 --> ...
##
InstallMethod( SyzygyCosyzygyTruncation, 
[ IsQPAComplex, IsInt, IsInt ],
function( C, i, j )
    local cat, difflist, truncpart, newdifflist, cokerproj, coker, 
          kernelinc, kernel, newpart1, zeropart1, newpart2, zeropart2, middlediffs;
    
    if( j > i ) then
        Error( "First input integer must be greater than or equal to the second" );
    fi;

    cat := CatOfComplex( C );

    difflist := DifferentialsOfComplex( C );
    middlediffs := FinitePartAsList( difflist, j, i );
    truncpart := FiniteInfList( j, middlediffs );

    kernelinc := KernelInclusion( DifferentialOfComplex( C, i ) );
    kernel := Source( kernelinc );
    newpart1 := FiniteInfList( i+1, [ kernelinc, 
                                     cat.zeroMap( cat.zeroObj, kernel ) ] );
    zeropart1 := PositivePartFrom( DifferentialsOfComplex( ZeroComplex( cat ) ),
                                  i+3 );


    cokerproj := CoKernelProjection( DifferentialOfComplex( C, j ) );
    coker := Range( cokerproj );
    newpart2 := FiniteInfList( j-2, [ cat.zeroMap( coker, cat.zeroObj ),
                                     cokerproj ] );

    zeropart2 := NegativePartFrom( DifferentialsOfComplex( ZeroComplex( cat ) ),
                                  j-3 );

    newdifflist := InfConcatenation( zeropart1, newpart1, truncpart, 
                                     newpart2, zeropart2 );

    return ComplexByDifferentialList( cat, newdifflist );
  
end );

#######################################################################
##
#O  CutComplexAbove( <C> ) 
##
##  For a bounded below complex C which is stored as an infinite complex,
##  but is known to be finite. Returns the same complex, but represented
##  as a finite complex.
##
InstallMethod( CutComplexAbove,
[ IsQPAComplex ],
function( C )
    local i, obj;
    if (IsInt(UpperBound(C))) then
        return C;
    else
       i := LowerBound(C);
       while true do
           obj := ObjectOfComplex(C, i);
           if (IsZero(DimensionVector(obj))) then
               return BrutalTruncationAbove(C, i-1);
           fi;
           i := i+1;
       od;
   fi;

end );               

#######################################################################
##
#O  CutComplexBelow( <C> ) 
##
##  For a bounded above complex C which is stored as an infinite complex,
##  but is known to be finite. Returns the same complex, but represented
##  as a finite complex.
##
InstallMethod( CutComplexBelow,
[ IsQPAComplex ],
function( C )
    local i, obj;
    if (IsInt(LowerBound(C))) then
        return C;
    else
       i := UpperBound(C);
       while true do
           obj := ObjectOfComplex(C, i);
           if (IsZero(DimensionVector(obj))) then
               return BrutalTruncationBelow(C, i+1);
           fi;
           i := i-1;
       od;
   fi;

end );               

#######################################################################
##
#O  ComplexByDifferentialList( <cat>, <differentials> ) 
##
##  <cat> is a category, and <differentials> is an InfList of
##  differentials.
##  
InstallMethod( ComplexByDifferentialList,
[ IsCat, IsInfList ],
function( cat, differentials )
    local C, fam;

    fam := NewFamily( "ComplexesFamily", IsQPAComplex );
    fam!.cat := cat;
    C := Objectify( NewType( fam, IsQPAComplex and IsQPAComplexDefaultRep ),
                    rec( ) );
    SetCatOfComplex( C, cat );
    SetDifferentialsOfComplex( C, differentials );
    return C;

end );

#######################################################################
##
#O  PrintObj( <C> ) 
##
##  Prints the zero complex
##  
InstallMethod( PrintObj,
[ IsZeroComplex ],
function( C )
    Print( "0 -> 0" );
end );

#######################################################################
##
#O  PrintObj( <C> ) 
##
##  Prints a non-zero complex
##  
InstallMethod( PrintObj,
[ IsQPAComplex ],
function( C )
    local cat, diffs, i, upbound, lowbound, top, bottom;

    cat := CatOfComplex( C );

    diffs := DifferentialsOfComplex( C );
    upbound := UpperBound( C );
    lowbound := LowerBound( C );
    if IsInt( upbound ) then
        top := MiddleEnd( diffs ) - 1;
    elif IsPositiveRepeating( C ) then
        top := MiddleEnd( diffs );
    else
        top := HighestKnownDegree( C );
    fi;
    if IsNegativeRepeating( C ) then
        bottom := MiddleStart( diffs );
    else
        bottom := LowestKnownDegree( C );
    fi;
    
    if IsInt( upbound ) then
        Print( "0 -> " );
    else
        Print( "--- -> " );
    fi;

    if IsPositiveRepeating( C ) and upbound = infinity then
        Print( "[ " );
        for i in Reversed( PositiveRepeatDegrees( C ) ) do
            Print( i, ":", cat.objStr( ObjectOfComplex( C, i ) ), " -> " );
        od;
        Print( "] " );
    fi;

    if IsInt( top ) and IsInt( bottom ) then
      for i in [ top, top - 1 .. bottom ] do
        Print( i, ":", cat.objStr( ObjectOfComplex( C, i ) ), " -> " );
      od;
    fi;

    if IsNegativeRepeating( C ) and lowbound = -infinity then
        Print( "[ " );
        for i in Reversed( NegativeRepeatDegrees( C ) ) do
            Print( i, ":", cat.objStr( ObjectOfComplex( C, i ) ), " -> " );
        od;
        Print( "] " );
    fi;

    if IsInt( lowbound ) then
        Print( "0" );
    else
        Print( "---" );
    fi;
end );

#######################################################################
##
#O  ProjectiveResolution( <M> ) 
##
##  Returns the projective resolution of M, including M in degree -1.
##  
InstallMethod( ProjectiveResolution,
[ IsPathAlgebraMatModule ],
function( M )
    local nextDifferential, cover;

    nextDifferential := function( d )
        return ProjectiveCover( Kernel( d ) ) * KernelInclusion( d );
    end;

    cover := ProjectiveCover( M );

    return Complex( CatOfRightAlgebraModules( ActingAlgebra( M ) ),
                    0,
                    [ cover ],
                    [ "next/repeat", nextDifferential, cover ],
                    "zero" );
end );

#######################################################################
##
#O  InjectiveResolution( < M > ) 
##
##  Returns the minimal injective resolution of M, including M in degree -1.
##  
InstallMethod( InjectiveResolution,
    [ IsPathAlgebraMatModule ],
    function( M )
    local nextDifferential, envelope;

    nextDifferential := function( d )
        return  CoKernelProjection( d )*InjectiveEnvelope( CoKernel( d ) );
    end;

    envelope := InjectiveEnvelope( M );

    return Complex( CatOfRightAlgebraModules( ActingAlgebra( M ) ),
                    0,
                    [ envelope ],
                    "zero",
                    [ "next/repeat", nextDifferential, envelope ]
                    );
end
);

#######################################################################
##
#F  ChainMap( <M>, <v> ) 
##
##  TODO: documentation for all chain map-functions
##  
InstallMethod( ChainMap,
               "for complexes, int and lists",
               [ IsQPAComplex, IsQPAComplex, IsInt, IsList, IsList, IsList ],
function( source, range, basePosition, middle, positive, negative )
    local cat, fam, map, positiveL, negativeL, numZeroMaps, i,
          correctDomainAt, correctCodomainAt, commutesAt,
          checkDomainAndCodomain, checkCommutes, checkNewMorphism,
          morphisms, firstCheckDegree, lastCheckDegree;

    cat := CatOfComplex( source );
    if cat <> CatOfComplex( range ) then
        Error( "source and range of chain map must be complexes over the same cat" );
    fi;

    fam := NewFamily( "ChainMapsFamily", IsChainMap );
    map := Objectify( NewType( fam, IsChainMap and IsChainMapDefaultRep ),
                      rec( ) );

    SetSource( map, source );
    SetRange( map, range );

    if positive = "zero" then
        positiveL := [ "pos",
                       i -> cat.zeroMap( ObjectOfComplex( source, i ),
                                         ObjectOfComplex( range, i ) ),
                       false ];
    elif positive[ 1 ] = "pos" then
        positiveL := ShallowCopy( positive );
        positiveL[ 2 ] := function( i )
            return positive[ 2 ]( map, i );
        end;
    else
        positiveL := positive;
    fi;
    if negative = "zero" then
        negativeL := [ "pos",
                       i -> cat.zeroMap( ObjectOfComplex( source, i ),
                                         ObjectOfComplex( range, i ) ),
                       false ];
    elif negative[ 1 ] = "pos" then
        negativeL := ShallowCopy( negative );
        negativeL[ 2 ] := function( i )
            return negative[ 2 ]( map, i );
        end;
    else
        negativeL := negative;
    fi;

    # if positive = "zero" then
    #     numZeroMaps := 0;
    #     for i in [ Length( middle ), Length( middle ) - 1 .. 1 ] do
    #         if cat.isZeroMapping( middle[ i ] ) then
    #             numZeroMaps := numZeroMaps + 1;
    #         else
    #             break;
    #         fi;
    #     od;
    #     SetUpperBound( map, basePosition + Length( middle ) - 1 - numZeroMaps );
    # fi;
    # if negative = "zero" then
    #     numZeroMaps := 0;
    #     for i in [ 1 .. Length( middle ) ] do
    #         if cat.isZeroMapping( middle[ i ] ) then
    #             numZeroMaps := numZeroMaps + 1;
    #         else
    #             break;
    #         fi;
    #     od;
    #     SetLowerBound( map, basePosition + numZeroMaps );
    # fi;

    correctDomainAt := function( i )
        return Source( map^i ) = ObjectOfComplex( source, i );
    end;
    correctCodomainAt := function( i )
        return Range( map^i ) = ObjectOfComplex( range, i );
    end;

    commutesAt := function( i )
        return cat.composeMaps( range^i, map^i ) = cat.composeMaps( map^(i-1), source^i );
    end;

    checkDomainAndCodomain := function( i )
        if not correctDomainAt( i ) then
            Error( "incorrect source of chain map morphism in degree ", i, "\n" );
        fi;
        if not correctCodomainAt( i ) then
            Error( "incorrect range of chain map morphism in degree ", i, "\n" );
        fi;
    end;

    checkCommutes := function( i )
        if not commutesAt( i ) then
            Error( "chain map morphisms at degrees (", i, ",", i-1, ") ",
                   "do not commute with complex differentials\n" );
        fi;
    end;

    checkNewMorphism := function( i, dir, type )
        local topDegreeOfNewSquare;
        if dir = 1 then
            topDegreeOfNewSquare := i;
        else
            topDegreeOfNewSquare := i + 1;
        fi;
        checkDomainAndCodomain( i );
        checkCommutes( topDegreeOfNewSquare );
    end;

    morphisms := MakeInfList( basePosition, middle, positiveL, negativeL,
                              checkNewMorphism );
    SetMorphismsOfChainMap( map, morphisms );

    # for i in [ LowestKnownDegree( map ) .. HighestKnownDegree( map ) ] do
    #     checkDomainAndCodomain( i );
    # od;
    # for i in [ LowestKnownDegree( map ) + 1 .. HighestKnownDegree( map ) ] do
    #     checkCommutes( i );
    # od;

    # TODO: hvor mye som må sjekkes (må se på kompleksene)
    if IsRepeating( NegativePart( morphisms ) ) then
        firstCheckDegree := StartPosition( NegativePart( morphisms ) )
                            - Length( RepeatingList( NegativePart( morphisms ) ) );
    else
        firstCheckDegree := MiddleStart( morphisms );
    fi;
    if IsRepeating( PositivePart( morphisms ) ) then
        lastCheckDegree := StartPosition( PositivePart( morphisms ) )
                           + Length( RepeatingList( PositivePart( morphisms ) ) );
    else
        lastCheckDegree := MiddleEnd( morphisms );
    fi;
    for i in [ firstCheckDegree .. lastCheckDegree ] do
        checkDomainAndCodomain( i );
    od;
    for i in [ firstCheckDegree + 1 .. lastCheckDegree ] do
        checkCommutes( i );
    od;

    return map;

end );

#######################################################################
##
#O  HighestKnownDegree( <map> ) 
##
##  Returns the highest degree that has been computed (or is otherwise
##  known) of the chain map <map>.  
##  
InstallMethod( HighestKnownDegree,
[ IsChainMap ],
function( map )
    return HighestKnownPosition( MorphismsOfChainMap( map ) );
end );

#######################################################################
##
#O  LowestKnownDegree( <map> ) 
##
##  Returns the lowest degree that has been computed (or is otherwise)
##  known) of the chain map <map>.
##  
InstallMethod( LowestKnownDegree,
[ IsChainMap ],
function( map )
    return LowestKnownPosition( MorphismsOfChainMap( map ) );
end );

#######################################################################
##
#O  MorphismOfChainMap( <map>, <i> ) 
##
##  Returns the morhpism in degree <i> of the map <map>.
##  
InstallMethod( MorphismOfChainMap,
[ IsChainMap, IsInt ],
function( map, i )
    return MorphismsOfChainMap( map )^i;
end );

#######################################################################
##
#O  \^( <map>, <i> ) 
##
##  Returns the morphism in degree <i> of the map <map>.
##  
InstallMethod( \^,
[ IsChainMap, IsInt ],
function( map, i )
    return MorphismsOfChainMap( map )^i;
end );

#######################################################################
##
#O  PrintObj( <map> ) 
##
##  Prints the chain map <map>.
##  
InstallMethod( PrintObj,
[ IsChainMap ],
function( map )
    Print( "<chain map>" );
end );

#######################################################################
##
#F  ComplexAndChainMAps( <sourceComplexes>, <rangeComplexes>,
##                       <basePosition>, <middle>, <positive>,
##                       <negative> ) 
##
##  Returns a list consisting of a newly created complex, togeher with
##  one or more newly created chain maps.  The new complex is either
##  source or range for the new chain map(s).
##
##  <sourceComplexes> is a list of the complexes to be sources of the
##  chain maps which will have the new complex as range.  Similarly,
##  <rangeComplexes> is a list of the complexes to be ranges of the new
##  chain maps which will have the new complex as source.
##  
InstallGlobalFunction( ComplexAndChainMaps,
function( sourceComplexes, rangeComplexes,
          basePosition, middle, positive, negative )

    local cat, C, inMaps, outMaps, numInMaps, numOutMaps,
          positiveL, negativeL, list,
          positiveDiffs, positiveInMaps, positiveOutMaps,
          negativeDiffs, negativeInMaps, negativeOutMaps,
          middleDiffs, middleInMaps, middleOutMaps;

    cat := CatOfComplex( Concatenation( sourceComplexes, rangeComplexes )[ 1 ] );

    if positive = "zero" then
        positiveL := [ "repeat", [ fail ] ];
    elif positive[ 1 ] = "pos" then
        positiveL := ShallowCopy( positive );
        positiveL[ 2 ] := function( i )
            return CallFuncList( positive[ 2 ],
                                 Concatenation( [ C ], inMaps, outMaps, [ i ] ) );
        end;
    else
        positiveL := positive;
    fi;
    if negative = "zero" then
        negativeL := [ "repeat", [ fail ] ];
    elif negative[ 1 ] = "pos" then
        negativeL := ShallowCopy( negative );
        negativeL[ 2 ] := function( i )
            return CallFuncList( negative[ 2 ],
                                 Concatenation( [ C ], inMaps, outMaps, [ i ] ) );
        end;
    else
        negativeL := negative;
    fi;

    list := MakeInfList( basePosition, middle, positiveL, negativeL, false );

    numInMaps := Length( sourceComplexes );
    numOutMaps := Length( rangeComplexes );

    if positive = "zero" then
        positiveDiffs := "zero";
        positiveInMaps := List( [ 1 .. numInMaps ], i -> "zero" );
        positiveOutMaps := List( [ 1 .. numOutMaps ], i -> "zero" );
    else
        positiveDiffs := [ "pos",
                           function( C, i ) return \^(list, i)[ 1 ]; end ];
        positiveInMaps :=
          List( [ 1 .. numInMaps ],
                i -> [ "pos", function( map, pos ) return \^(list, pos)[ 1 + i ]; end ] );
        positiveOutMaps :=
          List( [ 1 .. numOutMaps ],
                i -> [ "pos", function( map, pos )
                                 return  \^(list, pos)[ 1 + numInMaps + i ]; end ] );
    fi;
    if negative = "zero" then
        negativeDiffs := "zero";
        negativeInMaps := List( [ 1 .. numInMaps ], i -> "zero" );
        negativeOutMaps := List( [ 1 .. numOutMaps ], i -> "zero" );
    else
        negativeDiffs := [ "pos", function( C, i ) return \^(list, i)[ 1 ]; end ];
        negativeInMaps :=
          List( [ 1 .. numInMaps ],
                i -> [ "pos", function( map, pos ) return \^(list, pos)[ 1 + i ]; end ] );
        negativeOutMaps :=
          List( [ 1 .. numOutMaps ],
                i -> [ "pos", function( map, pos )
                                 return \^(list, pos)[ 1 + numInMaps + i ]; end ] );
    fi;

    middleDiffs := List( middle, L -> L[ 1 ] );
    middleInMaps := List( [ 1 .. numInMaps ],
                          i -> List( middle, L -> L[ 1 + i ] ) );
    middleOutMaps := List( [ 1 .. numOutMaps ],
                           i -> List( middle, L -> L[ 1 + numInMaps + i ] ) );

    C := Complex( cat, basePosition, middleDiffs, positiveDiffs, negativeDiffs );
    inMaps := List( [ 1 .. numInMaps ],
                    i -> ChainMap( sourceComplexes[ i ],
                                   C,
                                   basePosition,
                                   middleInMaps[ i ],
                                   positiveInMaps[ i ],
                                   negativeInMaps[ i ] ) );
    outMaps := List( [ 1 .. numOutMaps ],
                     i -> ChainMap( C,
                                    rangeComplexes[ i ],
                                    basePosition,
                                    middleOutMaps[ i ],
                                    positiveOutMaps[ i ],
                                    negativeOutMaps[ i ] ) );

    return Concatenation( [ C ], inMaps, outMaps );
                       
end );

#######################################################################
##
#F  FiniteChainMap( <source>, <range>, <baseDegree>, <morphisms> ) 
##
##  This function returns a finite chain map between two finite
##  complexes <source> and <range>.
##  
InstallMethod( FiniteChainMap,
               [ IsQPAComplex, IsQPAComplex, IsInt, IsList ],
function( source, range, baseDegree, morphisms )
    return ChainMap( source, range, baseDegree, morphisms, "zero", "zero" );
end );

#######################################################################
##
#F  ZeroChainMap( <source>, <range> )
##
##  This function returns the zero chain map between two
##  complexes <source> and <range>.
##  
InstallMethod( ZeroChainMap,
[ IsQPAComplex, IsQPAComplex ],
function( source, range )
    return ChainMap( source, range, 0, [], "zero", "zero" );
end );

#######################################################################
##
#O  ComparisonLifting( <f>, <PC>, <EC> )
##  
##  <f> is a morphism between two modules M and N.  <PC> is a complex
##  with M in degree i, zero in degrees j < i and projective modules
##  in degrees j > i.  <EC> is an exact complex with N in degree i
##  and zero in degrees j < i.  The function returns a chain map from
##  <PC> to <EC> which lifts the map <f> (it has <f> in degree i).
##  
InstallMethod( ComparisonLifting,
               [ IsPathAlgebraMatModuleHomomorphism, IsQPAComplex, IsQPAComplex ],
               function( f, PC, EC )
    local lbound, surjection, middle, nextLifting, nextLiftingFunction, i, j, chainmap;

    if ( LowerBound(PC) <> LowerBound(EC) ) then
       Error("The complexes must have the same lower bound");
    fi;

    lbound := LowerBound(PC);

    if (ObjectOfComplex(PC,lbound) <> Source( f ) or
        ObjectOfComplex(EC,lbound) <> Range( f ) ) then
       Error("The map has wrong source and/or range");
    fi;

    surjection := DifferentialOfComplex( PC , lbound + 1 )*f;

    middle := [ f, LiftingMorphismFromProjective( DifferentialOfComplex( EC, lbound + 1 ), surjection ) ];

    nextLifting := function( prev, i )
        local maps, kerinc, map, maps2, surj;

        if IsZero(DimensionVector( ObjectOfComplex( PC, i ))) then
            return ZeroMapping( ObjectOfComplex( PC, i ), ObjectOfComplex( EC, i ));
        fi;
        
        maps := ImageProjectionInclusion( DifferentialOfComplex( PC, i )*prev );
        kerinc := LiftingInclusionMorphisms( KernelInclusion( DifferentialOfComplex( EC, i-1 ) ), maps[2] );
        map := maps[1] * kerinc;
        maps2 := ImageProjectionInclusion( DifferentialOfComplex( EC, i ) );
        surj := maps2[1]*LiftingInclusionMorphisms( KernelInclusion( DifferentialOfComplex( EC, i-1 )), maps2[2]);
        
        return LiftingMorphismFromProjective(surj, map);
    end;

    if  IsInt( UpperBound( PC ) ) and IsInt( UpperBound ( EC )) then
        for i in [ lbound+2..UpperBound( PC ) ] do
            Append( middle, [ nextLifting( middle[i-lbound], i ) ] );
        od;
        for j in [ i+1..UpperBound( EC ) ] do
            Append( middle, [ ZeroMapping( ObjectOfComplex( PC, j ), ObjectOfComplex( EC, j ) ) ] );
        od;
        return FiniteChainMap( PC, EC, lbound, middle );
    else
        nextLiftingFunction := function( M, i ) return nextLifting( MorphismOfChainMap( M, i-1 ), i ); end; 
        return ChainMap( PC, EC, lbound, middle, [ "pos", nextLiftingFunction, true ], "zero" );
    fi;
end );


#######################################################################
##
#O  ComparisonLiftingToProjectiveResolution( <f> )
##  
##  <f> is a morphism between two modules M and N.  The function
##  returns the lifting of <f> to a chain map between the projective
##  resolutions of M and N.  The modules M and N are in degree -1 of
##  the resolutions, and the map in degree -1 is <f>.
##  
InstallMethod( ComparisonLiftingToProjectiveResolution,
[ IsPathAlgebraMatModuleHomomorphism ],
function( f )
    return ComparisonLifting( f, ProjectiveResolution( Source(f) ), ProjectiveResolution( Range(f) ) );
end );

#######################################################################
##
#O  MappingCone( <f> )
##  
##  <f> is a chain map between two complexes A and B.
##  This method returns a list [ C, i, p ] where C is the mapping cone
##  of <f>, i is the (chain map) inclusion of B into the cone and
##  p is the projection from the cone onto A[-1].
##  
InstallMethod( MappingCone,
[ IsChainMap ],
function( f )
    local C, i, A, B, dirsum, dirsum2, diff, middle, positiveFunction, negativeFunction;

    A := Source(f);
    B := Range(f);

#
#  Consider where to "start" the cone complex.
#
    if (IsInt(LowerBound(A))) then
        i := LowerBound(A);
    elif (IsInt(LowerBound(B))) then
        i := LowerBound(B);
    elif (IsInt(UpperBound(A))) then
        i := UpperBound(A);
    elif (IsInt(UpperBound(B))) then
        i := UpperBound(B);
    else
        i := 0;
    fi;

#
#  Construct the first differential of the cone, and the first projection/inclusion morphisms
#

    dirsum := DirectSumOfQPAModules( [ ObjectOfComplex(A,i-1), ObjectOfComplex(B,i) ] );
    dirsum2 := DirectSumOfQPAModules( [ ObjectOfComplex(A,i-2), ObjectOfComplex(B,i-1) ] );
    diff := MultiplyListsOfMaps( DirectSumProjections(dirsum),
                                 [[ -DifferentialOfComplex(A,i-1),
                                    ZeroMapping(ObjectOfComplex(B,i), ObjectOfComplex(A,i-2)) ],
                                  [ -MorphismOfChainMap(f,i-1), DifferentialOfComplex(B,i)]],
                                 DirectSumInclusions(dirsum2) );
    middle := [ [ diff, DirectSumInclusions(dirsum)[2], DirectSumProjections(dirsum)[1] ] ];

#
#  Positive degrees of the cone, the projection and the inclusion
#
    positiveFunction := function(C,inmap,outmap,i)
        local nextObj, prevObj, nextDiff;
        nextObj := DirectSumOfQPAModules( [ ObjectOfComplex(A,i-1), ObjectOfComplex(B,i) ] );
        prevObj := Source(DifferentialOfComplex(C,i-1));

        nextDiff :=  MultiplyListsOfMaps( DirectSumProjections(nextObj),
                                          [[ -DifferentialOfComplex(A,i-1),
                                             ZeroMapping(ObjectOfComplex(B,i), ObjectOfComplex(A,i-2)) ],
                                           [ -MorphismOfChainMap(f,i-1), DifferentialOfComplex(B,i)]],
                                          DirectSumInclusions(prevObj) );
        return [ nextDiff, DirectSumInclusions(nextObj)[2], DirectSumProjections(nextObj)[1] ];

    end ;

#
#  Negative degrees of the cone, the projection and the inclusion
#
    negativeFunction := function(C,inmap,outmap,i)
        local nextObj, prevObj, nextDiff;
        nextObj := DirectSumOfQPAModules( [ ObjectOfComplex(A,i-2), ObjectOfComplex(B,i-1) ] );
        prevObj := Range(DifferentialOfComplex(C,i+1));

        nextDiff := MultiplyListsOfMaps( DirectSumProjections(prevObj),
                                         [[ -DifferentialOfComplex(A,i-1),
                                            ZeroMapping(ObjectOfComplex(B,i), ObjectOfComplex(A,i-2)) ],
                                          [ -MorphismOfChainMap(f,i-1), DifferentialOfComplex(B,i)]],
                                         DirectSumInclusions(nextObj) );
        return [ nextDiff, DirectSumInclusions(prevObj)[2], DirectSumProjections(prevObj)[1] ];
    end ;

#
#  Returns the cone (as a complex) together with the projection and the inclusion
#
    C := ComplexAndChainMaps( [B], [Shift(A,-1)], i, middle,
                              ["pos", positiveFunction, true],
                              ["pos", negativeFunction, true] );
    return C;
end );


[ Dauer der Verarbeitung: 0.62 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge