Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/cap/gap/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 22.8.2025 mit Größe 99 kB image not shown  

Quelle  MethodRecordTools.gi   Sprache: unbekannt

 
# SPDX-License-Identifier: GPL-2.0-or-later
# CAP: Categories, Algorithms, Programming
#
# Implementations
#
BindGlobal( "CAP_INTERNAL_VALID_RETURN_TYPES",
#! @BeginCode CAP_INTERNAL_VALID_RETURN_TYPES
    [
        "object",
        "morphism",
        "twocell",
        "object_in_range_category_of_homomorphism_structure",
        "morphism_in_range_category_of_homomorphism_structure",
        "bool",
        "list_of_objects",
        "list_of_morphisms",
        "list_of_lists_of_morphisms",
        "object_datum",
        "morphism_datum",
        "nonneg_integer_or_infinity",
        "list_of_elements_of_commutative_ring_of_linear_structure",
    ]
#! @EndCode
);

BindGlobal( "CAP_INTERNAL_VALID_METHOD_NAME_RECORD_COMPONENTS",
#! @BeginCode CAP_INTERNAL_VALID_METHOD_NAME_RECORD_COMPONENTS
    [
        "filter_list",
        "input_arguments_names",
        "return_type",
        "output_source_getter_string",
        "output_source_getter_preconditions",
        "output_range_getter_string",
        "output_range_getter_preconditions",
        "with_given_object_position",
        "dual_operation",
        "dual_arguments_reversed",
        "dual_with_given_objects_reversed",
        "dual_preprocessor_func",
        "dual_preprocessor_func_string",
        "dual_postprocessor_func",
        "dual_postprocessor_func_string",
        "functorial",
        "compatible_with_congruence_of_morphisms",
        "redirect_function",
        "pre_function",
        "pre_function_full",
        "post_function",
    ]
#! @EndCode
);

# additional components which are deprecated or undocumented
BindGlobal( "CAP_INTERNAL_LEGACY_METHOD_NAME_RECORD_COMPONENTS",
    [
        "is_merely_set_theoretic",
        "is_reflected_by_faithful_functor",
    ]
);

BindGlobal( "CAP_INTERNAL_METHOD_NAME_RECORD", rec( ) );

InstallGlobalFunction( "CAP_INTERNAL_ENHANCE_NAME_RECORD_LIMITS",
  function ( limits )
    local object_specification, morphism_specification, source_position, type, range_position, unbound_morphism_positions, number_of_unbound_morphisms, unbound_objects, morphism, unbound_object_positions, number_of_unbound_objects, targets, target_positions, nontarget_positions, number_of_targets, number_of_nontargets, diagram_filter_list, diagram_arguments_names, limit, position;
    
    for limit in limits do
        object_specification := limit.object_specification;
        morphism_specification := limit.morphism_specification;
        
        #### check that given diagram is well-defined
        if not (IsDenseList( object_specification ) and IsDenseList( morphism_specification )) then
            Error( "the given diagram is not well-defined" );
        fi;

        if Length( object_specification ) = 0 and Length( morphism_specification ) > 0 then
            Error( "the given diagram is not well-defined" );
        fi;
        
        if not (ForAll( object_specification, object -> object in [ "fixedobject", "varobject" ] )) then
            Error( "the given diagram is not well-defined" );
        fi;

        for morphism in morphism_specification do
            if not (IsList( morphism ) and Length( morphism ) = 3) then
                Error( "the given diagram is not well-defined" );
            fi;
            source_position := morphism[1];
            type := morphism[2];
            range_position := morphism[3];

            if not (IsInt( source_position ) and source_position >= 1 and source_position <= Length( object_specification )) then
                Error( "the given diagram is not well-defined" );
            fi;

            if not (IsInt( range_position ) and range_position >= 1 and range_position <= Length( object_specification )) then
                Error( "the given diagram is not well-defined" );
            fi;

            if not type in [ "fixedmorphism", "varmorphism", "zeromorphism" ] then
                Error( "the given diagram is not well-defined" );
            fi;

            if type = "fixedmorphism" and (object_specification[source_position] = "varobject" or object_specification[range_position] = "varobject") then
                Error( "the given diagram is not well-defined" );
            fi;
        od;

        #### get number of variables
        # morphisms
        unbound_morphism_positions := PositionsProperty( morphism_specification, x -> x[2] = "varmorphism" or x[2] = "fixedmorphism" );
        if Length( unbound_morphism_positions ) = 0 then
            number_of_unbound_morphisms := 0;
        elif Length( unbound_morphism_positions ) = 1 and morphism_specification[unbound_morphism_positions[1]][2] = "fixedmorphism" then
            number_of_unbound_morphisms := 1;
        else
            number_of_unbound_morphisms := 2;
        fi;

        limit.unbound_morphism_positions := unbound_morphism_positions;
        limit.number_of_unbound_morphisms := number_of_unbound_morphisms;

        if not ForAll( unbound_morphism_positions, i -> morphism_specification[i][2] = "fixedmorphism" or i = Length( unbound_morphism_positions ) ) then
            Error( "diagrams of the given type are not supported" );
        fi;

        # objects
        unbound_objects := StructuralCopy( object_specification );
        for position in unbound_morphism_positions do
            morphism := morphism_specification[position];
            source_position := morphism[1];
            range_position := morphism[3];

            unbound_objects[source_position] := "";
            unbound_objects[range_position] := "";
        od;
        unbound_object_positions := PositionsProperty( unbound_objects, x -> x <> "" );
        if Length( unbound_object_positions ) = 0 then
            number_of_unbound_objects := 0;
        elif Length( unbound_object_positions ) = 1 and object_specification[unbound_object_positions[1]] = "fixedobject" then
            number_of_unbound_objects := 1;
        else
            number_of_unbound_objects := 2;
        fi;

        limit.unbound_object_positions := unbound_object_positions;
        limit.number_of_unbound_objects := number_of_unbound_objects;

        if not ForAll( unbound_object_positions, i -> object_specification[i] = "fixedobject" or i = Length( unbound_object_positions ) ) then
            Error( "diagrams of the given type are not supported" );
        fi;

        # (non-)targets
        targets := StructuralCopy( object_specification );
        for morphism in morphism_specification do
            range_position := morphism[3];
            
            targets[range_position] := "";
        od;
        target_positions := PositionsProperty( targets, x -> x <> "" );
        nontarget_positions := PositionsProperty( targets, x -> x = "" );
        if Length( target_positions ) = 0 then
            number_of_targets := 0;
        elif Length( target_positions ) = 1 and object_specification[target_positions[1]] = "fixedobject" then
            number_of_targets := 1;
        else
            number_of_targets := 2;
        fi;
        if Length( nontarget_positions ) = 0 then
            number_of_nontargets := 0;
        elif Length( nontarget_positions ) = 1 and object_specification[nontarget_positions[1]] = "fixedobject" then
            number_of_nontargets := 1;
        else
            number_of_nontargets := 2;
        fi;

        limit.target_positions := target_positions;
        limit.number_of_targets := number_of_targets;
        limit.nontarget_positions := nontarget_positions;
        limit.number_of_nontargets := number_of_nontargets;

        #### get filter list and names of input arguments of the diagram
        diagram_filter_list := [ ];
        diagram_arguments_names := [ ];
        if number_of_unbound_objects = 1 then
            Add( diagram_filter_list, "object" );
            Add( diagram_arguments_names, "X" );
        elif number_of_unbound_objects > 1 then
            Add( diagram_filter_list, "list_of_objects" );
            Add( diagram_arguments_names, "objects" );
        fi;
        if number_of_unbound_morphisms = 1 then
            Add( diagram_filter_list, "morphism" );
            Add( diagram_arguments_names, "alpha" );
        elif number_of_unbound_morphisms > 1 then
            if number_of_targets = 1 then
                Add( diagram_filter_list, "object" );
                Add( diagram_arguments_names, "Y" );
            fi;
            Add( diagram_filter_list, "list_of_morphisms" );
            Add( diagram_arguments_names, "morphisms" );
        fi;

        limit.diagram_filter_list := diagram_filter_list;
        limit.diagram_arguments_names := diagram_arguments_names;
        
        #### set default projection/injection/universal morphism names
        if number_of_targets > 0 and not IsBound( limit.limit_projection_name ) then
            limit.limit_projection_name := Concatenation( "ProjectionInFactorOf", limit.limit_object_name );
        fi;
        if not IsBound( limit.limit_universal_morphism_name ) then
            limit.limit_universal_morphism_name := Concatenation( "UniversalMorphismInto", limit.limit_object_name );
        fi;

        if number_of_targets > 0 and not IsBound( limit.colimit_injection_name ) then
            limit.colimit_injection_name := Concatenation( "InjectionOfCofactorOf", limit.colimit_object_name );
        fi;
        if not IsBound( limit.colimit_universal_morphism_name ) then
            limit.colimit_universal_morphism_name := Concatenation( "UniversalMorphismFrom", limit.colimit_object_name );
        fi;
        
        if number_of_targets > 0 then
            limit.limit_projection_with_given_name := Concatenation( limit.limit_projection_name, "WithGiven", limit.limit_object_name );
            limit.colimit_injection_with_given_name := Concatenation( limit.colimit_injection_name, "WithGiven", limit.colimit_object_name );
        fi;
        
        limit.limit_universal_morphism_with_given_name := Concatenation( limit.limit_universal_morphism_name, "WithGiven", limit.limit_object_name );
        limit.colimit_universal_morphism_with_given_name := Concatenation( limit.colimit_universal_morphism_name, "WithGiven", limit.colimit_object_name );
        
        limit.limit_functorial_name := Concatenation( limit.limit_object_name, "Functorial" );
        limit.colimit_functorial_name := Concatenation( limit.colimit_object_name, "Functorial" );

        limit.limit_functorial_with_given_name := Concatenation( limit.limit_functorial_name, "WithGiven", limit.limit_object_name, "s" );
        limit.colimit_functorial_with_given_name := Concatenation( limit.colimit_functorial_name, "WithGiven", limit.colimit_object_name, "s" );

        if limit.number_of_nontargets = 1 then
            limit.limit_morphism_to_sink_name := Concatenation( "MorphismFrom", limit.limit_object_name, "ToSink" );
            limit.colimit_morphism_from_source_name := Concatenation( "MorphismFromSourceTo", limit.colimit_object_name );
        fi;

        if Length( diagram_filter_list ) > 0 then
            if limit.number_of_targets = 1 then
                limit.diagram_morphism_filter_list := [ "morphism" ];
                limit.diagram_morphism_arguments_names := [ "mu" ];
            else
                limit.diagram_morphism_filter_list := [ "list_of_morphisms" ];
                limit.diagram_morphism_arguments_names := [ "L" ];
            fi;
        else
            limit.diagram_morphism_filter_list := [ ];
            limit.diagram_morphism_arguments_names := [ ];
        fi;
        
        limit.functorial_source_diagram_arguments_names := limit.diagram_arguments_names;
        limit.functorial_range_diagram_arguments_names := List( limit.diagram_arguments_names, x -> Concatenation( x, "p" ) );
        
    od;
end );

BindGlobal( "CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES", FunctionWithNamedArguments(
  [
    [ "subset_only", false ],
  ],
  function ( CAP_NAMED_ARGUMENTS, method_record, entry_name, generated_entry )
  local excluded_names, method_record_entry, name;
    
    excluded_names := [ "function_name", "pre_function", "pre_function_full", "post_function" ];
    
    if not IsBound( method_record.(entry_name) ) then
        Display( Concatenation( "WARNING: The method record is missing a component named \"", entry_name, "\" which is expected by the validator.\n" ) );
        return;
    fi;
    
    method_record_entry := method_record.(entry_name);
    
    for name in RecNames( method_record_entry ) do
        if name in excluded_names then
            continue;
        fi;
        if not IsBound( generated_entry.(name) ) then
            if CAP_NAMED_ARGUMENTS.subset_only then
                continue;
            else
                Display( Concatenation( "WARNING: The entry \"", entry_name, "\" in the method record has a component named \"", name, "\" which is not expected by the validator.\n" ) );
            fi;
        elif method_record_entry.(name) <> generated_entry.(name) then
            Display( Concatenation( "WARNING: The entry \"", entry_name, "\" in the method record has a component named \"", name, "\" with value \"", String( method_record_entry.(name) ), "\". The value expected by the validator is \"", String( generated_entry.(name) ), "\".\n" ) );
        fi;
    od;
    for name in RecNames( generated_entry ) do
        if name in excluded_names then
            continue;
        fi;
        if not IsBound( method_record_entry.(name) ) then
            Display( Concatenation( "WARNING: The entry \"", entry_name, "\" in the method record is missing a component named \"", name, "\" which is expected by the validator.\n" ) );
        fi;
    od;
end ) );

InstallGlobalFunction( CAP_INTERNAL_VALIDATE_LIMITS_IN_NAME_RECORD,
  function ( method_name_record, limits )
    local make_record_with_given, make_colimit, object_filter_list, object_input_arguments_names, projection_filter_list, projection_input_arguments_names, projection_range_getter_string, morphism_to_sink_filter_list, morphism_to_sink_input_arguments_names, morphism_to_sink_range_getter_string, universal_morphism_filter_list, universal_morphism_input_arguments_names, object_record, projection_record, morphism_to_sink_record, universal_morphism_record, functorial_record, functorial_with_given_record, limit;
    
    #### helper functions
    make_record_with_given := function ( record, object_name, coobject_name )
        record := StructuralCopy( record );
        
        record.function_name := Concatenation( record.function_name, "WithGiven", object_name );
        Add( record.filter_list, "object" );
        Add( record.input_arguments_names, "P" );
        if record.with_given_object_position = "Source" then
            record.output_source_getter_string := "P";
            record.output_source_getter_preconditions := [ ];
        else
            record.output_range_getter_string := "P";
            record.output_range_getter_preconditions := [ ];
        fi;
        record.dual_operation := Concatenation( record.dual_operation, "WithGiven", coobject_name );
        Unbind( record.with_given_object_position );

        return record;
    end;

    make_colimit := function ( limit, record )
      local orig_function_name, orig_output_source_getter_string, orig_output_source_getter_preconditions;
        
        record := StructuralCopy( record );
        
        orig_function_name := record.function_name;
        record.function_name := record.dual_operation;
        record.dual_operation := orig_function_name;
        
        if IsBound( record.functorial ) then
            
            Assert( 0, record.functorial = limit.limit_functorial_name );
            
            record.functorial := limit.colimit_functorial_name;
            
        fi;
        
        if IsBound( record.with_given_object_position ) then
            if record.with_given_object_position = "Source" then
                record.with_given_object_position := "Range";
            elif record.with_given_object_position = "Range" then
                record.with_given_object_position := "Source";
            fi;
        fi;
        
        # reverse output getters, except if the input is reversed
        if not (IsBound( record.dual_arguments_reversed ) and record.dual_arguments_reversed) then
            
            orig_output_source_getter_string := fail;
            
            if IsBound( record.output_source_getter_string ) then
                
                orig_output_source_getter_string := record.output_source_getter_string;
                orig_output_source_getter_preconditions := record.output_source_getter_preconditions;
                
            fi;
            
            if IsBound( record.output_range_getter_string ) then
                
                record.output_source_getter_string := record.output_range_getter_string;
                record.output_source_getter_preconditions := record.output_range_getter_preconditions;
                
            else
                
                Unbind( record.output_source_getter_string );
                Unbind( record.output_source_getter_preconditions );
                
            fi;
            
            if orig_output_source_getter_string <> fail then
                
                record.output_range_getter_string := orig_output_source_getter_string;
                record.output_range_getter_preconditions := orig_output_source_getter_preconditions;
                
            else
                
                Unbind( record.output_range_getter_string );
                Unbind( record.output_range_getter_preconditions );
                
            fi;
            
        fi;
        
        if IsBound( record.output_source_getter_string ) then
            
            record.output_source_getter_string := ReplacedString( record.output_source_getter_string, "Source", "tmp" );
            record.output_source_getter_string := ReplacedString( record.output_source_getter_string, "Range", "Source" );
            record.output_source_getter_string := ReplacedString( record.output_source_getter_string, "tmp", "Range" );
            
            if IsEmpty( record.output_source_getter_preconditions ) then
                # do nothing
            elif record.output_source_getter_preconditions = [ [ limit.limit_object_name, 1 ] ] then
                record.output_source_getter_string := ReplacedString( record.output_source_getter_string, limit.limit_object_name, limit.colimit_object_name );
                record.output_source_getter_preconditions := [ [ limit.colimit_object_name, 1 ] ];
            else
                Error( "this case is not supported yet" );
            fi;
            
        fi;
        
        if IsBound( record.output_range_getter_string ) then
            
            record.output_range_getter_string := ReplacedString( record.output_range_getter_string, "Source", "tmp" );
            record.output_range_getter_string := ReplacedString( record.output_range_getter_string, "Range", "Source" );
            record.output_range_getter_string := ReplacedString( record.output_range_getter_string, "tmp", "Range" );
            
            if IsEmpty( record.output_range_getter_preconditions ) then
                # do nothing
            elif record.output_range_getter_preconditions = [ [ limit.limit_object_name, 1 ] ] then
                record.output_range_getter_string := ReplacedString( record.output_range_getter_string, limit.limit_object_name, limit.colimit_object_name );
                record.output_range_getter_preconditions := [ [ limit.colimit_object_name, 1 ] ];
            else
                Error( "this case is not supported yet" );
            fi;
            
        fi;
        
        return record;
    end;

    for limit in limits do
        
        #### get filter lists and io types
        object_filter_list := Concatenation( [ "category" ], StructuralCopy( limit.diagram_filter_list ) );
        object_input_arguments_names := Concatenation( [ "cat" ], limit.diagram_arguments_names );
        
        # only used if limit.number_of_targets > 0
        projection_filter_list := Concatenation( [ "category" ], StructuralCopy( limit.diagram_filter_list ) );
        projection_input_arguments_names := Concatenation( [ "cat" ], limit.diagram_arguments_names );
        if limit.number_of_targets > 1 then
            Add( projection_filter_list, "integer" );
            Add( projection_input_arguments_names, "k" );
        fi;
        if limit.target_positions = limit.unbound_object_positions then
            # range can be derived from the objects
            if limit.number_of_targets = 1 then
                projection_range_getter_string := "X";
            else
                projection_range_getter_string := "objects[k]";
            fi;
        elif limit.target_positions = List( limit.unbound_morphism_positions, i -> limit.morphism_specification[i][1] ) then
            # range can be derived from the morphisms as sources
            if limit.number_of_unbound_morphisms = 1 then
                projection_range_getter_string := "Source( alpha )";
            elif limit.number_of_targets = 1 then
                projection_range_getter_string := "Y";
            else
                projection_range_getter_string := "Source( morphisms[k] )";
            fi;
        elif limit.target_positions = List( limit.unbound_morphism_positions, i -> limit.morphism_specification[i][3] ) then
            # range can be derived from the morphisms as ranges
            if limit.number_of_unbound_morphisms = 1 then
                projection_range_getter_string := "Range( alpha )";
            elif limit.number_of_targets = 1 then
                projection_range_getter_string := "Y";
            else
                projection_range_getter_string := "Range( morphisms[k] )";
            fi;
        else
            Error( "Warning: cannot express range getter" );
        fi;

        # only used if limit.number_of_nontargets = 1
        morphism_to_sink_filter_list := Concatenation( [ "category" ], StructuralCopy( limit.diagram_filter_list ) );
        morphism_to_sink_input_arguments_names := Concatenation( [ "cat" ], limit.diagram_arguments_names );
        morphism_to_sink_range_getter_string := [ StructuralCopy( limit.diagram_arguments_names ), [ ] ];
        if limit.number_of_unbound_morphisms = 1 then
            morphism_to_sink_range_getter_string := "Range( alpha )";
        elif limit.number_of_unbound_morphisms > 1 then
            morphism_to_sink_range_getter_string := "Range( morphisms[1] )";
        fi;

        universal_morphism_filter_list := Concatenation( [ "category" ], StructuralCopy( limit.diagram_filter_list ), [ "object" ] );
        universal_morphism_input_arguments_names := Concatenation( [ "cat" ], limit.diagram_arguments_names, [ "T" ] );
        if limit.number_of_targets = 1 then
            Add( universal_morphism_filter_list, "morphism" );
            Add( universal_morphism_input_arguments_names, "tau" );
        elif limit.number_of_targets > 1 then
            Add( universal_morphism_filter_list, "list_of_morphisms" );
            Add( universal_morphism_input_arguments_names, "tau" );
        fi;

        
        #### get base records
        object_record :=  rec(
            function_name := limit.limit_object_name,
            filter_list := object_filter_list,
            input_arguments_names := object_input_arguments_names,
            return_type := "object",
            dual_operation := limit.colimit_object_name,
            functorial := limit.limit_functorial_name,
        );

        if limit.number_of_targets > 0 then
            projection_record := rec(
                function_name := limit.limit_projection_name,
                filter_list := projection_filter_list,
                input_arguments_names := projection_input_arguments_names,
                return_type := "morphism",
                output_range_getter_string := projection_range_getter_string,
                output_range_getter_preconditions := [ ],
                with_given_object_position := "Source",
                dual_operation := limit.colimit_injection_name,
            );
        fi;

        if limit.number_of_nontargets = 1 then
            morphism_to_sink_record := rec(
                function_name := Concatenation( "MorphismFrom", limit.limit_object_name, "ToSink" ),
                filter_list := morphism_to_sink_filter_list,
                input_arguments_names := morphism_to_sink_input_arguments_names,
                return_type := "morphism",
                output_range_getter_string := morphism_to_sink_range_getter_string,
                output_range_getter_preconditions := [ ],
                with_given_object_position := "Source",
                dual_operation := limit.colimit_morphism_from_source_name,
            );
        fi;

        universal_morphism_record := rec(
            function_name := limit.limit_universal_morphism_name,
            filter_list := universal_morphism_filter_list,
            input_arguments_names := universal_morphism_input_arguments_names,
            return_type := "morphism",
            output_source_getter_string := "T",
            output_source_getter_preconditions := [ ],
            with_given_object_position := "Range",
            dual_operation := limit.colimit_universal_morphism_name,
        );
        
        functorial_record := rec(
            function_name := limit.limit_functorial_name,
            filter_list := Concatenation( [ "category" ], limit.diagram_filter_list, limit.diagram_morphism_filter_list, limit.diagram_filter_list ),
            input_arguments_names := Concatenation( [ "cat" ], limit.functorial_source_diagram_arguments_names, limit.diagram_morphism_arguments_names, limit.functorial_range_diagram_arguments_names ),
            return_type := "morphism",
            # object_name
            output_source_getter_string := ReplacedStringViaRecord(
                "object_name( arguments... )",
                rec( object_name := limit.limit_object_name, arguments := Concatenation( [ "cat" ], limit.functorial_source_diagram_arguments_names ) )
            ),
            output_source_getter_preconditions := [ [ limit.limit_object_name, 1 ] ],
            output_range_getter_string := ReplacedStringViaRecord(
                "object_name( arguments... )",
                rec( object_name := limit.limit_object_name, arguments := Concatenation( [ "cat" ], limit.functorial_range_diagram_arguments_names ) )
            ),
            output_range_getter_preconditions := [ [ limit.limit_object_name, 1 ] ],
            with_given_object_position := "both",
            dual_operation := limit.colimit_functorial_name,
            dual_arguments_reversed := true,
        );
        
        functorial_with_given_record := rec(
            function_name := limit.limit_functorial_with_given_name,
            filter_list := Concatenation( [ "category", "object" ], limit.diagram_filter_list, limit.diagram_morphism_filter_list, limit.diagram_filter_list, [ "object" ] ),
            input_arguments_names := Concatenation( [ "cat", "P" ], limit.functorial_source_diagram_arguments_names, limit.diagram_morphism_arguments_names, limit.functorial_range_diagram_arguments_names, [ "Pp" ] ),
            return_type := "morphism",
            output_source_getter_string := "P",
            output_source_getter_preconditions := [ ],
            output_range_getter_string := "Pp",
            output_range_getter_preconditions := [ ],
            dual_operation := limit.colimit_functorial_with_given_name,
            dual_arguments_reversed := true,
        );
        
        if limit.number_of_unbound_morphisms = 0 then
            
            # The diagram has only objects as input -> all operations are compatible with the congruence of morphisms:
            # For the universal morphisms and functorials, this follows from the universal property.
            # All other operations are automatically compatible because they do not have morphisms as input.
            
            # if limit.number_of_targets = 0, the universal morphism has no test morphism as input anyway
            if limit.number_of_targets > 0 then
                
                universal_morphism_record.compatible_with_congruence_of_morphisms := true;
                functorial_record.compatible_with_congruence_of_morphisms := true;
                functorial_with_given_record.compatible_with_congruence_of_morphisms := true;
                
            fi;
            
        else
            
            # The universal object might depend on the morphism datum.
            # Thus, the operations are in general not compatible with the congruence of morphisms.
            
            object_record.compatible_with_congruence_of_morphisms := false;
            projection_record.compatible_with_congruence_of_morphisms := false;
            morphism_to_sink_record.compatible_with_congruence_of_morphisms := false;
            universal_morphism_record.compatible_with_congruence_of_morphisms := false;
            functorial_record.compatible_with_congruence_of_morphisms := false;
            functorial_with_given_record.compatible_with_congruence_of_morphisms := false;
            
        fi;
        
        #### validate limit records
        CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.limit_object_name, object_record );
        
        if limit.number_of_targets > 0 then
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.limit_projection_name, projection_record );
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.limit_projection_with_given_name, make_record_with_given( projection_record, limit.limit_object_name, limit.colimit_object_name ) );
        fi;
        
        if limit.number_of_nontargets = 1 then
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.limit_morphism_to_sink_name, morphism_to_sink_record );
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, Concatenation( limit.limit_morphism_to_sink_name, "WithGiven", limit.limit_object_name ), make_record_with_given( morphism_to_sink_record, limit.limit_object_name, limit.colimit_object_name ) );
        fi;
        
        CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.limit_universal_morphism_name, universal_morphism_record );
        CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.limit_universal_morphism_with_given_name, make_record_with_given( universal_morphism_record, limit.limit_object_name, limit.colimit_object_name ) );
        
        # GAP has a limit of 6 arguments per operation -> operations which would have more than 6 arguments have to work around this
        if Length( functorial_with_given_record.filter_list ) <= 6 then
            
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, functorial_record.function_name, functorial_record );
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, functorial_with_given_record.function_name, functorial_with_given_record );
            
        fi;
        
        #### validate colimit records
        CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.colimit_object_name, make_colimit( limit, object_record ) );
        
        if limit.number_of_targets > 0 then
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.colimit_injection_name, make_colimit( limit, projection_record ) );
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.colimit_injection_with_given_name, make_record_with_given( make_colimit( limit, projection_record ), limit.colimit_object_name, limit.limit_object_name ) );
        fi;
        
        if limit.number_of_nontargets = 1 then
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.colimit_morphism_from_source_name, make_colimit( limit, morphism_to_sink_record ) );
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, Concatenation( limit.colimit_morphism_from_source_name, "WithGiven", limit.colimit_object_name ), make_record_with_given( make_colimit( limit, morphism_to_sink_record ), limit.colimit_object_name, limit.limit_object_name ) );
        fi;
        
        CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.colimit_universal_morphism_name, make_colimit( limit, universal_morphism_record ) );
        CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, limit.colimit_universal_morphism_with_given_name, make_record_with_given( make_colimit( limit, universal_morphism_record ), limit.colimit_object_name, limit.limit_object_name ) );
        
        # GAP has a limit of 6 arguments per operation -> operations which would have more than 6 arguments have to work around this
        if Length( functorial_with_given_record.filter_list ) <= 6 then
            
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, functorial_record.dual_operation, make_colimit( limit, functorial_record ) );
            CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( method_name_record, functorial_with_given_record.dual_operation, make_colimit( limit, functorial_with_given_record ) );
            
        fi;
        
    od;
    
end );

BindGlobal( "CAP_INTERNAL_METHOD_RECORD_REPLACEMENTS", rec( ) );

InstallGlobalFunction( CAP_INTERNAL_ADD_REPLACEMENTS_FOR_METHOD_RECORD,
  function( replacement_data )
    local current_name;

    for current_name in RecNames( replacement_data ) do
        if IsBound( CAP_INTERNAL_METHOD_RECORD_REPLACEMENTS.(current_name) ) then
            Error( Concatenation( current_name, " already has a replacement" ) );
        fi;
        CAP_INTERNAL_METHOD_RECORD_REPLACEMENTS.(current_name) := replacement_data.(current_name);
    od;
    
end );

BindGlobal( "CAP_INTERNAL_OPPOSITE_PROPERTY_PAIRS_FOR_OBJECTS", [ ] );

BindGlobal( "CAP_INTERNAL_OPPOSITE_PROPERTY_PAIRS_FOR_MORPHISMS", [ ] );

InstallGlobalFunction( CAP_INTERNAL_FIND_OPPOSITE_PROPERTY_PAIRS_IN_METHOD_NAME_RECORD,
  function( method_name_record )
    local recnames, current_recname, current_entry, current_rec, category_property_list, elem;
    
    recnames := RecNames( method_name_record );
    
    for current_recname in recnames do
        
        current_rec := method_name_record.( current_recname );
        
        if not (current_rec.return_type = "bool" and Length( current_rec.filter_list ) = 2) then
            continue;
        fi;
        
        if current_recname in [ "IsWellDefinedForObjects", "IsWellDefinedForMorphisms", "IsWellDefinedForTwoCells" ] then
            continue;
        fi;
        
        if not IsBound( current_rec.dual_operation ) or current_rec.dual_operation = current_recname then
            
            current_entry := NameFunction( current_rec.operation );
            
        else
            
            current_entry := [ NameFunction( current_rec.operation ), NameFunction( method_name_record.( current_rec.dual_operation ).operation ) ];
            current_entry := [ Concatenation( current_entry[ 1 ], " vs ", current_entry[ 2 ] ), current_entry ];
            
        fi;
        
        if current_rec.filter_list[2] = "object" then
            
            if not current_entry in CAP_INTERNAL_OPPOSITE_PROPERTY_PAIRS_FOR_OBJECTS then
                
                Add( CAP_INTERNAL_OPPOSITE_PROPERTY_PAIRS_FOR_OBJECTS, current_entry );
                
            fi;
            
        elif current_rec.filter_list[2] = "morphism" then
            
            if not current_entry in CAP_INTERNAL_OPPOSITE_PROPERTY_PAIRS_FOR_MORPHISMS then
                
                Add( CAP_INTERNAL_OPPOSITE_PROPERTY_PAIRS_FOR_MORPHISMS, current_entry );
                
            fi;
            
        fi;
        
    od;
    
end );

BindGlobal( "CAP_INTERNAL_PREPARE_INHERITED_PRE_FUNCTION",
  function( func, drop_both )
    
    if drop_both then
        
        return function( arg_list... )
            # drop second and last argument
            return CallFuncList( func, arg_list{Concatenation( [ 1 ], [ 3 .. Length( arg_list ) - 1 ] )} );
        end;
        
    else
        
        return function( arg_list... )
            # drop last argument
            return CallFuncList( func, arg_list{[ 1 .. Length( arg_list ) - 1 ]} );
        end;
        
    fi;
    
end );

BindGlobal( "CAP_INTERNAL_CREATE_REDIRECTION",
  
  function( without_given_name, with_given_name, object_function_name, object_filter_list, object_arguments_positions )
    local object_function, with_given_name_function, is_attribute, attribute_tester;
    
    object_function := ValueGlobal( object_function_name );
    
    with_given_name_function := ValueGlobal( with_given_name );
    
    # Check if `object_function` is declared as an attribute and can actually be used as one in our context.
    is_attribute := IsAttribute( object_function ) and Length( object_filter_list ) <= 2 and IsSpecializationOfFilter( IsAttributeStoringRep, CAP_INTERNAL_REPLACED_STRING_WITH_FILTER( Last( object_filter_list ) ) );
    
    if not is_attribute then
        
        return function( arg )
          local category, without_given_weight, with_given_weight, object_args, cache, cache_value;
            
            category := arg[ 1 ];
            
            without_given_weight := OperationWeight( category, without_given_name );
            with_given_weight := OperationWeight( category, with_given_name );
            
            # If the WithGiven version is more expensive than the WithoutGiven version, redirection makes no sense and
            # might even lead to inifite loops if the WithGiven version is derived from the WithoutGiven version.
            if with_given_weight > without_given_weight then
                
                return [ false ];
                
            fi;
            
            object_args := arg{ object_arguments_positions };
            
            cache := GET_METHOD_CACHE( category, object_function_name, Length( object_arguments_positions ) );
            
            cache_value := CallFuncList( CacheValue, [ cache, object_args ] );
            
            if cache_value = [ ] then
                
                return [ false ];
                
            fi;
            
            return [ true, CallFuncList( with_given_name_function, Concatenation( arg, [ cache_value[ 1 ] ] ) ) ];
            
        end;
        
    else
        
        if not Length( object_arguments_positions ) in [ 1, 2 ] then
            
            Error( "we can only handle attributes of the category or of a single object/morphism/twocell" );
            
        fi;
        
        attribute_tester := Tester( object_function );
        
        return function( arg )
          local category, without_given_weight, with_given_weight, object_args, cache_value, cache;
            
            category := arg[ 1 ];
            
            without_given_weight := OperationWeight( category, without_given_name );
            with_given_weight := OperationWeight( category, with_given_name );
            
            # If the WithGiven version is more expensive than the WithoutGiven version, redirection makes no sense and
            # might even lead to inifite loops if the WithGiven version is derived from the WithoutGiven version.
            if with_given_weight > without_given_weight then
                
                return [ false ];
                
            fi;
            
            object_args := arg{ object_arguments_positions };

            if attribute_tester( object_args[ Length( object_args ) ] ) then
                
                cache_value := [ object_function( object_args[ Length( object_args ) ] ) ];
                
            else
                
                cache := GET_METHOD_CACHE( category, object_function_name, Length( object_arguments_positions ) );
                
                cache_value := CallFuncList( CacheValue, [ cache, object_args ] );
                
                if cache_value = [ ] then
                    
                    return [ false ];
                    
                fi;
                
            fi;
            
            return [ true, CallFuncList( with_given_name_function, Concatenation( arg, [ cache_value[ 1 ] ] ) ) ];
            
        end;
        
    fi;
    
end );

BindGlobal( "CAP_INTERNAL_CREATE_POST_FUNCTION",
  
  function( source_range_object, object_function_name, object_filter_list, object_arguments_positions )
    local object_getter, object_function, cache_key_length, is_attribute, setter_function;
    
    if source_range_object = "Source" then
        object_getter := Source;
    elif source_range_object = "Range" then
        object_getter := Range;
    else
        Error( "the first argument of CAP_INTERNAL_CREATE_POST_FUNCTION must be 'Source' or 'Range'" );
    fi;
    
    object_function := ValueGlobal( object_function_name );
    
    cache_key_length := Length( object_arguments_positions );
    
    # Check if `object_function` is declared as an attribute and can actually be used as one in our context.
    is_attribute := IsAttribute( object_function ) and Length( object_filter_list ) <= 2 and IsSpecializationOfFilter( IsAttributeStoringRep, CAP_INTERNAL_REPLACED_STRING_WITH_FILTER( Last( object_filter_list ) ) );
    
    if not is_attribute then
    
        return function( arg )
          local category, object_args, result, object;
            
            category := arg[ 1 ];
            
            object_args := arg{ object_arguments_positions };
            
            result := arg[ Length( arg ) ];
            object := object_getter( result );
            
            SET_VALUE_OF_CATEGORY_CACHE( category, object_function_name, cache_key_length, object_args, object );
            
        end;
        
    else
        
        if not Length( object_arguments_positions ) in [ 1, 2 ] then
            
            Error( "we can only handle attributes of the category or of a single object/morphism/twocell" );
            
        fi;
        
        setter_function := Setter( object_function );
        
        return function( arg )
          local category, object_args, result, object;
            
            category := arg[ 1 ];

            object_args := arg{ object_arguments_positions };
            
            result := arg[ Length( arg ) ];
            object := object_getter( result );
            
            SET_VALUE_OF_CATEGORY_CACHE( category, object_function_name, cache_key_length, object_args, object );
            setter_function( object_args[ Length( object_args ) ], object );
            
        end;
        
    fi;
    
end );

InstallGlobalFunction( CAP_INTERNAL_ENHANCE_NAME_RECORD,
  function( record )
    local recnames, current_recname, current_rec, diff, number_of_arguments, func,
          without_given_name, with_given_prefix, with_given_names, with_given_name, without_given_rec, with_given_object_position, object_name,
          object_filter_list, with_given_object_filter, given_source_argument_name, given_range_argument_name, with_given_rec,
          collected_list, preconditions, can_always_compute_output_source_getter, can_always_compute_output_range_getter;
    
    recnames := RecNames( record );
    
    # loop before detecting With(out)Given pairs
    for current_recname in recnames do
        
        current_rec := record.(current_recname);
        
        diff := Difference( RecNames( current_rec ), CAP_INTERNAL_VALID_METHOD_NAME_RECORD_COMPONENTS );
        diff := Difference( diff, CAP_INTERNAL_LEGACY_METHOD_NAME_RECORD_COMPONENTS );
        
        if not IsEmpty( diff ) then
            
            Print( "WARNING: The following method name record components are not known: " );
            Display( diff );
            
        fi;
        
        # validity checks
        if not IsBound( current_rec.return_type ) then
            Error( "<current_rec> has no return_type" );
        fi;
        
        if current_rec.return_type in [ "other_object", "other_morphism" ] then
            Error( "The return types \"other_object\" and \"other_morphism\" are not supported anymore. If you need those, please report this using the CAP_projects's issue tracker." );
        fi;
        
        if not current_rec.return_type in CAP_INTERNAL_VALID_RETURN_TYPES then
            Error( "The return_type of <current_rec> does not appear in CAP_INTERNAL_VALID_RETURN_TYPES. Note that proper filters are not supported anymore." );
        fi;
        
        if current_rec.filter_list[1] <> "category" then
            
            Error( "The first entry of `filter_list` must be the string \"category\"." );
            
        fi;
        
        if ForAny( current_rec.filter_list, x -> x in [ "other_category", "other_object", "other_morphism", "other_twocell" ] ) then
            Error( "The filters \"other_category\", \"other_object\", \"other_morphism\", and \"other_twocell\" are not supported anymore. If you need those, please report this using the CAP_projects's issue tracker." );
        fi;
        
        if IsBound( current_rec.output_source_getter_preconditions ) and not IsBound( current_rec.output_source_getter_string ) then
            
            Error( "output_source_getter_preconditions may only be set if output_source_getter_string is set" );
            
        fi;
        
        if IsBound( current_rec.output_range_getter_preconditions ) and not IsBound( current_rec.output_range_getter_string ) then
            
            Error( "output_range_getter_preconditions may only be set if output_range_getter_string is set" );
            
        fi;
        
        current_rec.function_name := current_recname;
        
        if IsBound( current_rec.pre_function ) and IsString( current_rec.pre_function ) then
            
            if IsBound( record.(current_rec.pre_function) ) and IsBound( record.(current_rec.pre_function).pre_function ) and IsFunction( record.(current_rec.pre_function).pre_function ) then
                
                current_rec.pre_function := record.(current_rec.pre_function).pre_function;
                
            else
                
                Error( "Could not find pre function for ", current_recname, ". ", current_rec.pre_function, " is not the name of an operation in the record, has no pre function, or has itself a string as pre function." );
                
            fi;
            
        fi;
        
        if IsBound( current_rec.pre_function_full ) and IsString( current_rec.pre_function_full ) then
            
            if IsBound( record.(current_rec.pre_function_full) ) and IsBound( record.(current_rec.pre_function_full).pre_function_full ) and IsFunction( record.(current_rec.pre_function_full).pre_function_full ) then
                
                current_rec.pre_function_full := record.(current_rec.pre_function_full).pre_function_full;
                
            else
                
                Error( "Could not find full pre function for ", current_recname, ". ", current_rec.pre_function_full, " is not the name of an operation in the record, has no full pre function, or has itself a string as full pre function." );
                
            fi;
            
        fi;
        
        if IsBound( current_rec.redirect_function ) and IsString( current_rec.redirect_function ) then
            
            if IsBound( record.(current_rec.redirect_function) ) and IsBound( record.(current_rec.redirect_function).redirect_function ) and IsFunction( record.(current_rec.redirect_function).redirect_function ) then
                
                current_rec.redirect_function := record.(current_rec.redirect_function).redirect_function;
                
            else
                
                Error( "Could not find redirect function for ", current_recname, ". ", current_rec.redirect_function, " is not the name of an operation in the record, has no redirect function, or has itself a string as redirect function." );
                
            fi;
            
        fi;
        
        number_of_arguments := Length( current_rec.filter_list );
        
        if IsBound( current_rec.pre_function ) and NumberArgumentsFunction( current_rec.pre_function ) >= 0 and NumberArgumentsFunction( current_rec.pre_function ) <> number_of_arguments then
            Error( "the pre function of <current_rec> has the wrong number of arguments" );
        fi;
        
        if IsBound( current_rec.pre_function_full ) and NumberArgumentsFunction( current_rec.pre_function_full ) >= 0 and NumberArgumentsFunction( current_rec.pre_function_full ) <> number_of_arguments then
            Error( "the full pre function of <current_rec> has the wrong number of arguments" );
        fi;
        
        if IsBound( current_rec.redirect_function ) and NumberArgumentsFunction( current_rec.redirect_function ) >= 0 and NumberArgumentsFunction( current_rec.redirect_function ) <> number_of_arguments then
            Error( "the redirect function of <current_rec> has the wrong number of arguments" );
        fi;
        
        if IsBound( current_rec.post_function ) and NumberArgumentsFunction( current_rec.post_function ) >= 0 and NumberArgumentsFunction( current_rec.post_function ) <> number_of_arguments + 1 then
            Error( "the post function of <current_rec> has the wrong number of arguments" );
        fi;
        
        if IsBound( current_rec.dual_preprocessor_func ) and NumberArgumentsFunction( current_rec.dual_preprocessor_func ) >= 0 and NumberArgumentsFunction( current_rec.dual_preprocessor_func ) <> number_of_arguments then
            Error( "the dual preprocessor function of ", current_recname, " has the wrong number of arguments" );
        fi;
        
        if not ForAll( current_rec.filter_list, IsString ) then
            Error( "Not all entries of filter_list of ", current_recname, " are strings. This is not supported anymore." );
        fi;
        
        if not IsBound( current_rec.install_convenience_without_category ) then
            
            if ForAny( [ "object", "morphism", "twocell", "list_of_objects", "list_of_morphisms", "list_of_twocells" ], filter -> filter in current_rec.filter_list ) then
                
                current_rec.install_convenience_without_category := true;
                
            else
                
                current_rec.install_convenience_without_category := false;
                
            fi;
            
        fi;
        
        if IsBound( current_rec.universal_object_position ) then
            
            Display( "WARNING: universal_object_position was renamed to with_given_object_position" );
            
            current_rec.with_given_object_position := current_rec.universal_object_position;
            
        fi;
        
        if IsBound( current_rec.with_given_object_position ) and not current_rec.with_given_object_position in [ "Source", "Range", "both" ] then
            
            Error( "with_given_object_position must be one of the strings \"Source\", \"Range\", or \"both\", not ", current_rec.with_given_object_position );
            
        fi;
        
        if not IsBound( current_rec.is_with_given ) then
            
            current_rec.is_with_given := false;
            
        fi;
        
        if not IsBound( current_rec.with_given_without_given_name_pair ) then
            
            current_rec.with_given_without_given_name_pair := fail;
            
        fi;
        
        if IsBound( current_rec.dual_operation ) then
            
            # check that dual of the dual is the original operation
            
            if not IsBound( record.( current_rec.dual_operation ) ) then
                
                Error( "the dual operation must be added in the same call to `CAP_INTERNAL_ENHANCE_NAME_RECORD`" );
                
            fi;
            
            if not IsBound( record.( current_rec.dual_operation ).dual_operation ) then
                
                Error( "the dual operation of ", current_recname, ", i.e. ", current_rec.dual_operation, ", has no dual operation"  );
                
            fi;
            
            if record.( current_rec.dual_operation ).dual_operation <> current_recname then
                
                Error( "the dual operation of ", current_recname, ", i.e. ", current_rec.dual_operation, ", has the unexpected dual operation ", record.( current_rec.dual_operation ).dual_operation  );
                
            fi;
            
        fi;
        
        if not IsBound( current_rec.dual_arguments_reversed ) then
            
            current_rec.dual_arguments_reversed := false;
            
        fi;
        
        if Length( Filtered( [ "dual_preprocessor_func", "dual_arguments_reversed", "dual_with_given_objects_reversed" ],
                             name -> IsBound( current_rec.(name) ) and ( IsFunction( current_rec.(name) ) or current_rec.(name) = true )
                           ) ) >= 2 then
            
            Error( "dual_preprocessor_func, dual_arguments_reversed = true and dual_with_given_objects_reversed = true are mutually exclusive" );
            
        fi;
        
        if IsBound( current_rec.dual_preprocessor_func ) then
            
            if IsBound( current_rec.dual_preprocessor_func_string ) then
                
                Error( "dual_preprocessor_func and dual_preprocessor_func_string are mutually exclusive" );
                
            fi;
            
            if IsOperation( current_rec.dual_preprocessor_func ) or IsKernelFunction( current_rec.dual_preprocessor_func ) then
                
                current_rec.dual_preprocessor_func_string := NameFunction( current_rec.dual_preprocessor_func );
                
            else
                
                current_rec.dual_preprocessor_func_string := String( current_rec.dual_preprocessor_func );
                
            fi;
            
        fi;
        
        if IsBound( current_rec.dual_postprocessor_func ) then
            
            if IsBound( current_rec.dual_postprocessor_func_string ) then
                
                Error( "dual_postprocessor_func and dual_postprocessor_func_string are mutually exclusive" );
                
            fi;
            
            if IsOperation( current_rec.dual_postprocessor_func ) or IsKernelFunction( current_rec.dual_postprocessor_func ) then
                
                current_rec.dual_postprocessor_func_string := NameFunction( current_rec.dual_postprocessor_func );
                
            else
                
                current_rec.dual_postprocessor_func_string := String( current_rec.dual_postprocessor_func );
                
            fi;
            
        fi;
        
        func := ValueGlobal( current_recname );
        
        if IsOperation( func ) then
            
            current_rec.operation := func;
            
        elif IsFunction( func ) then
            
            current_rec.operation := ValueGlobal( Concatenation( current_recname, "Op" ) );
            
        else
            
            Error( "`ValueGlobal( current_recname )` is neither an operation nor a function" );
            
        fi;
        
        if not IsBound( current_rec.input_arguments_names ) then
            
            current_rec.input_arguments_names := Concatenation( [ "cat" ], List( [ 2 .. Length( current_rec.filter_list ) ], i -> Concatenation( "arg", String( i ) ) ) );
            
        fi;
        
        if current_rec.input_arguments_names[1] <> "cat" then
            
            Error( "the category argument must always be called \"cat\", please adjust the method record entry of ", current_recname );
            
        fi;
        
        if not ForAll( current_rec.input_arguments_names, x -> IsString( x ) ) then
            
            Error( "the entries of input_arguments_names must be strings, please adjust the method record entry of ", current_recname );
            
        fi;
        
        if not IsDuplicateFreeList( current_rec.input_arguments_names ) then
            
            Error( "input_arguments_names must be duplicate free, please adjust the method record entry of ", current_recname );
            
        fi;
        
        if ForAll( current_rec.filter_list, x -> x in [ "element_of_commutative_ring_of_linear_structure", "integer", "nonneg_integer_or_infinity", "category", "object", "object_in_range_category_of_homomorphism_structure", "list_of_objects" ] ) then
            
            if not IsBound( current_rec.compatible_with_congruence_of_morphisms ) then
                
                current_rec.compatible_with_congruence_of_morphisms := true;
                
            fi;
            
            if current_rec.compatible_with_congruence_of_morphisms <> true then
                
                Error( current_recname, " does not depend on morphisms but is still marked as not compatible with the congruence of morphisms" );
                
            fi;
            
        fi;
        
    od;
    
    # detect With(out)Given pairs
    for current_recname in recnames do
        
        current_rec := record.(current_recname);
        
        if IsBound( current_rec.with_given_object_position ) then
            
            if PositionSublist( current_recname, "WithGiven" ) <> fail then
                
                Error( "WithGiven operations must NOT have the component with_given_object_position set, please adjust the method record entry of ", current_recname );
                
            fi;
            
            without_given_name := current_recname;
            
            with_given_prefix := Concatenation( without_given_name, "WithGiven" );
            
            with_given_names := Filtered( recnames, x -> StartsWith( x, with_given_prefix ) );
            
            if Length( with_given_names ) <> 1 then
                
                Error( "Could not find unique WithGiven version for ", without_given_name );
                
            fi;
            
            with_given_name := with_given_names[1];
            
            without_given_rec := record.(without_given_name);
            
            with_given_object_position := without_given_rec.with_given_object_position;
            
            object_name := ReplacedString( with_given_name, with_given_prefix, "" );
            
            # generate output_source_getter_string resp. output_range_getter_string automatically if possible
            if object_name in recnames then
                
                object_filter_list := record.( object_name ).filter_list;
                
                if with_given_object_position = "Source" then
                    
                    if not IsBound( without_given_rec.output_source_getter_string ) then
                        
                        without_given_rec.output_source_getter_string := Concatenation( object_name, "( ", JoinStringsWithSeparator( without_given_rec.input_arguments_names{[ 1 .. Length( object_filter_list ) ]}, ", " ), " )" );
                        without_given_rec.output_source_getter_preconditions := [ [ object_name, 1 ] ];
                        
                    fi;
                    
                fi;
                
                if with_given_object_position = "Range" then
                    
                    if not IsBound( without_given_rec.output_range_getter_string ) then
                        
                        without_given_rec.output_range_getter_string := Concatenation( object_name, "( ", JoinStringsWithSeparator( without_given_rec.input_arguments_names{[ 1 .. Length( object_filter_list ) ]}, ", " ), " )" );
                        without_given_rec.output_range_getter_preconditions := [ [ object_name, 1 ] ];
                        
                    fi;
                    
                fi;
                
            fi;
            
            # plausibility checks for without_given_rec
            if with_given_object_position in [ "Source", "both" ] then
                
                if not IsBound( without_given_rec.output_source_getter_string ) then
                    
                    Error( "This is a WithoutGiven record, but output_source_getter_string is not set. This is not supported." );
                    
                fi;
                
            fi;
            
            if with_given_object_position in [ "Range", "both" ] then
                
                if not IsBound( without_given_rec.output_range_getter_string ) then
                    
                    Error( "This is a WithoutGiven record, but output_range_getter_string is not set. This is not supported." );
                    
                fi;
                
            fi;
            
            if not without_given_rec.return_type in [ "morphism", "morphism_in_range_category_of_homomorphism_structure" ] then
                
                Error( "This is a WithoutGiven record, but return_type is neither \"morphism\" nor \"morphism_in_range_category_of_homomorphism_structure\". This is not supported." );
                
            fi;
            
            # generate with_given_rec
            if without_given_rec.return_type = "morphism" then
                
                with_given_object_filter := "object";
                
            elif without_given_rec.return_type = "morphism_in_range_category_of_homomorphism_structure" then
                
                with_given_object_filter := "object_in_range_category_of_homomorphism_structure";
                
            else
                
                Error( "this should never happen" );
                
            fi;
            
            if with_given_object_position = "Source" then
                
                given_source_argument_name := Last( record.(with_given_name).input_arguments_names );
                
            elif with_given_object_position = "Range" then
                
                given_range_argument_name := Last( record.(with_given_name).input_arguments_names );
                
            else
                
                given_source_argument_name := record.(with_given_name).input_arguments_names[2];
                given_range_argument_name := Last( record.(with_given_name).input_arguments_names );
                
            fi;
            
            with_given_rec := rec(
                return_type := without_given_rec.return_type,
            );
            
            if with_given_object_position = "Source" then
                
                with_given_rec.filter_list := Concatenation( without_given_rec.filter_list, [ with_given_object_filter ] );
                with_given_rec.input_arguments_names := Concatenation( without_given_rec.input_arguments_names, [ given_source_argument_name ] );
                with_given_rec.output_source_getter_string := given_source_argument_name;
                
                if IsBound( without_given_rec.output_range_getter_string ) then
                    
                    with_given_rec.output_range_getter_string := without_given_rec.output_range_getter_string;
                    
                fi;
                
                if IsBound( without_given_rec.output_range_getter_preconditions ) then
                    
                    with_given_rec.output_range_getter_preconditions := without_given_rec.output_range_getter_preconditions;
                    
                fi;
                
            elif with_given_object_position = "Range" then
                
                with_given_rec.filter_list := Concatenation( without_given_rec.filter_list, [ with_given_object_filter ] );
                with_given_rec.input_arguments_names := Concatenation( without_given_rec.input_arguments_names, [ given_range_argument_name ] );
                with_given_rec.output_range_getter_string := given_range_argument_name;
                
                if IsBound( without_given_rec.output_source_getter_string ) then
                    
                    with_given_rec.output_source_getter_string := without_given_rec.output_source_getter_string;
                    
                fi;
                
                if IsBound( without_given_rec.output_source_getter_preconditions ) then
                    
                    with_given_rec.output_source_getter_preconditions := without_given_rec.output_source_getter_preconditions;
                    
                fi;
                
            elif with_given_object_position = "both" then
                
--> --------------------

--> maximum size reached

--> --------------------

[ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ]