Spracherkennung für: .gi vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
# 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
with_given_rec.filter_list := Concatenation(
[ without_given_rec.filter_list[1] ],
[ with_given_object_filter ],
without_given_rec.filter_list{[ 2 .. Length( without_given_rec.filter_list ) ]},
[ with_given_object_filter ]
);
with_given_rec.input_arguments_names := Concatenation(
[ without_given_rec.input_arguments_names[1] ],
[ given_source_argument_name ],
without_given_rec.input_arguments_names{[ 2 .. Length( without_given_rec.input_arguments_names ) ]},
[ given_range_argument_name ]
);
with_given_rec.output_source_getter_string := given_source_argument_name;
with_given_rec.output_range_getter_string := given_range_argument_name;
else
Error( "this should never happen" );
fi;
CAP_INTERNAL_IS_EQUAL_FOR_METHOD_RECORD_ENTRIES( record, with_given_name, with_given_rec : subset_only := true );
# now enhance the actual with_given_rec
with_given_rec := record.(with_given_name);
if IsBound( without_given_rec.pre_function ) and not IsBound( with_given_rec.pre_function ) then
with_given_rec.pre_function := CAP_INTERNAL_PREPARE_INHERITED_PRE_FUNCTION( record.(without_given_name).pre_function, with_given_object_position = "both" );
fi;
if IsBound( without_given_rec.pre_function_full ) and not IsBound( with_given_rec.pre_function_full ) then
with_given_rec.pre_function_full := CAP_INTERNAL_PREPARE_INHERITED_PRE_FUNCTION( record.(without_given_name).pre_function_full, with_given_object_position = "both" );
fi;
with_given_rec.is_with_given := true;
with_given_rec.with_given_without_given_name_pair := [ without_given_name, with_given_name ];
without_given_rec.with_given_without_given_name_pair := [ without_given_name, with_given_name ];
if object_name in recnames then
if with_given_object_position = "both" then
Error( "with_given_object_position is \"both\", but the WithGiven name suggests that only a single object of name ", object_name, " is given. This is not supported." );
fi;
with_given_rec.with_given_object_name := object_name;
object_filter_list := record.( object_name ).filter_list;
if with_given_object_position = "Source" then
if not StartsWith( without_given_rec.output_source_getter_string, object_name ) then
Error( "the output_source_getter_string of the WithoutGiven record does not call the detected object ", object_name );
fi;
fi;
if with_given_object_position = "Range" then
if not StartsWith( without_given_rec.output_range_getter_string, object_name ) then
Error( "the output_range_getter_string of the WithoutGiven record does not call the detected object ", object_name );
fi;
fi;
if not StartsWith( without_given_rec.filter_list, object_filter_list ) then
Error( "the object arguments must be the first arguments of the without given method, but the corresponding filters do not match" );
fi;
if not IsBound( without_given_rec.redirect_function ) then
if Length( record.( without_given_name ).filter_list ) + 1 <> Length( record.( with_given_name ).filter_list ) then
Display( Concatenation(
"WARNING: You seem to be relying on automatically installed redirect functions. ",
"For this, the with given method must have exactly one additional argument compared to the without given method. ",
"This is not the case, so no automatic redirect function will be installed. ",
"Install a custom redirect function to prevent this warning."
) );
else
without_given_rec.redirect_function := CAP_INTERNAL_CREATE_REDIRECTION( without_given_name, with_given_name, object_name, object_filter_list, [ 1 .. Length( object_filter_list ) ] );
fi;
fi;
if not IsBound( without_given_rec.post_function ) then
without_given_rec.post_function := CAP_INTERNAL_CREATE_POST_FUNCTION( with_given_object_position, object_name, object_filter_list, [ 1 .. Length( object_filter_list ) ] );
fi;
fi;
fi;
od;
# loop after detecting With(out)Given pairs
for current_recname in recnames do
current_rec := record.(current_recname);
if IsBound( current_rec.dual_with_given_objects_reversed ) and current_rec.dual_with_given_objects_reversed then
if not current_rec.is_with_given then
Error( "dual_with_given_objects_reversed may only be set for with given records" );
fi;
without_given_rec := record.(current_rec.with_given_without_given_name_pair[1]);
with_given_object_position := without_given_rec.with_given_object_position;
if with_given_object_position <> "both" then
Error( "dual_with_given_objects_reversed may only be set if both source and range are given" );