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


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.38 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