## # some options should affect the whole function stack and should hence not be consumed by FunctionWithNamedArguments
BindGlobal( "CAP_INTERNAL_GLOBAL_OPTIONS", [ "no_precompiled_code" ] );
InstallGlobalFunction( "FunctionWithNamedArguments", function ( specification, func )
Assert( 0, IsList( specification ) );
Assert( 0, IsFunction( func ) );
Assert( 0, ForAll( specification, x -> IsList( x ) and Length( x ) = 2 and IsString( x[1] ) and not IsMutable( x[2] ) ) );
Assert( 0, NumberArgumentsFunction( func ) >= 1 or NumberArgumentsFunction( func ) <= -2 );
Assert( 0, NamesLocalVariablesFunction( func )[1] = "CAP_NAMED_ARGUMENTS" );
return function ( args... )
local CAP_NAMED_ARGUMENTS, current_options, result, s;
if not IsIdenticalObj( category, false ) and not HasRangeCategoryOfHomomorphismStructure( category ) then
Display( Concatenation( "WARNING: You are calling an Add function for a CAP operation for \"", Name( category ), "\" which is part of a homomorphism structure but the category has no RangeCategoryOfHomomorphismStructure (yet)" ) );
fi;
if IsIdenticalObj( category, false ) or not HasRangeCategoryOfHomomorphismStructure( category ) then
if not IsIdenticalObj( category, false ) and not HasRangeCategoryOfHomomorphismStructure( category ) then
Display( Concatenation( "WARNING: You are calling an Add function for a CAP operation for \"", Name( category ), "\" which is part of a homomorphism structure but the category has no RangeCategoryOfHomomorphismStructure (yet)" ) );
fi;
if IsIdenticalObj( category, false ) or not HasRangeCategoryOfHomomorphismStructure( category ) then
if not IsIdenticalObj( category, false ) and not HasCommutativeRingOfLinearCategory( category ) then
Print( "WARNING: You are calling an Add function for a CAP operation for \"", Name( category ), "\" which is part of the linear structure over a commutative ring but the category has no CommutativeRingOfLinearCategory (yet).\n" );
fi;
if IsIdenticalObj( category, false ) or not HasCommutativeRingOfLinearCategory( category ) then
elif IsSpecializationOfFilter( IsNTuple, data_type.filter ) then
# `IsNTuple` deliberately does not imply `IsList` because we want to treat tuples and lists in different ways in CompilerForCAP. # However, on the GAP level tuples are just dense lists.
return IsDenseList;
generic_help_string := " You can access the value via the local variable 'value' in a break loop.";
filter := data_type.filter;
# `IsBool( fail ) = true`, but we do not want to include `fail` if IsSpecializationOfFilter( IsBool, filter ) then
return function( value, args... )
if not (value = true or value = false) then
Error( CallFuncList( human_readable_identifier_getter, args ), " is neither `true` nor `false`.", generic_help_string );
fi;
end;
elif IsSpecializationOfFilter( IsFunction, filter ) then
return function( value, args... )
if not filter( value ) then
Error( CallFuncList( human_readable_identifier_getter, args ), " does not lie in the expected filter ", filter, ".", generic_help_string );
fi;
if NumberArgumentsFunction( value ) >= 0 and NumberArgumentsFunction( value ) <> Length( data_type.signature[1] ) then
Error( CallFuncList( human_readable_identifier_getter, args ), " has ", NumberArgumentsFunction( value ), " arguments but ", Length( data_type.signature[1] ), " were expected.", generic_help_string );
fi;
end;
elif IsSpecializationOfFilter( IsList, filter ) then
assert_value_is_of_element_type := CAP_INTERNAL_ASSERT_VALUE_IS_OF_TYPE_GETTER( data_type.element_type, { i, outer_args } -> Concatenation( "the ", String( i ), ". entry of ", CallFuncList( human_readable_identifier_getter, outer_args ) ) );
return function( value, args... )
local i;
if not filter( value ) then
Error( CallFuncList( human_readable_identifier_getter, args ), " does not lie in the expected filter ", filter, ".", generic_help_string );
fi;
for i in [ 1 .. Length( value ) ] do
#= comment for Julia # Julia does not have non-dense lists if not IsBound( value[i] ) then
Error( "the ", i, ". entry of ", CallFuncList( human_readable_identifier_getter, args ), " is not bound.", generic_help_string );
fi; # =#
assert_value_is_of_element_type( value[i], i, args );
od;
end;
elif IsSpecializationOfFilter( IsNTuple, filter ) then
asserts_value_is_of_element_type := List( [ 1 .. Length( data_type.element_types ) ], i -> CAP_INTERNAL_ASSERT_VALUE_IS_OF_TYPE_GETTER( data_type.element_types[i], { outer_args } -> Concatenation( "the ", String( i ), ". entry of ", CallFuncList( human_readable_identifier_getter, outer_args ) ) ) );
return function( value, args... )
local i;
# tuples are modeled as lists if not IsDenseList( value ) then
Error( CallFuncList( human_readable_identifier_getter, args ), " does not lie in the expected filter IsDenseList (implementation filter of IsNTuple).", generic_help_string );
fi;
if Length( value ) <> Length( data_type.element_types ) then
Error( CallFuncList( human_readable_identifier_getter, args ), " has length ", Length( value ), " but ", Length( data_type.element_types ), " was expected.", generic_help_string );
elif IsSpecializationOfFilter( IsCapCategory, filter ) then
return function( value, args... )
if not filter( value ) then
Error( CallFuncList( human_readable_identifier_getter, args ), " does not lie in the expected filter ", filter, ".", generic_help_string );
fi;
if IsBound( data_type.category ) and not IsIdenticalObj( data_type.category, false ) and not IsIdenticalObj( value, data_type.category ) then
Error( CallFuncList( human_readable_identifier_getter, args ), " is not the expected category although it lies in the category filter of the expected category. This should never happen, please report this using the CAP_project's issue tracker.", generic_help_string );
fi;
end;
elif IsSpecializationOfFilter( IsCapCategoryObject, filter ) then
return function( value, args... )
if not filter( value ) then
Error( CallFuncList( human_readable_identifier_getter, args ), " does not lie in the expected filter ", filter, ".", generic_help_string );
# Technically, the head of a "for" loop is only executed once. # We could simply start the detection at "do", but this would exclude the header of "while" loops # which indeed is executed multiple times. elif current_symbol in [ "for", "while", "repeat", "function" ] then
loop_power := loop_power + 1;
# Technically, the part after "until" is also executed multiple times. elif current_symbol in [ "od", "until", "end" ] then
loop_power := loop_power - 1;
fi;
od;
if loop_power <> 0then
Error( "The automated detection of CAP operations could not detect loops properly. If the reserved word `for` appears in <func_as_string> (e.g. in a string), this is probably the cause. If not, please report this as a bug mentioning <func_as_string>." );
#= comment for Julia
BindGlobal( alias_name, function ( args... )
local result;
Print(
Concatenation( "WARNING: ", alias_name, " is deprecated and will not be supported after ", deprecation_date, ". Please use ", function_name, " instead.\n"
)
);
result := CallFuncListWrap( ValueGlobal( function_name ), args );
if not IsEmpty( result ) then
return result[1];
fi;
end ); # =#
end );
##
InstallGlobalFunction( "IsSpecializationOfFilter", function ( filter_1, filter_2 )
local data_type_1, data_type_2;
# COVERAGE_IGNORE_NEXT_LINE
Error( "installing methods without filters is currently not supported" );
fi;
operation_name := NameFunction( operation );
if IsBound( CAP_INTERNAL_METHOD_NAME_RECORD.(operation_name) ) and Length( filters ) = Length( CAP_INTERNAL_METHOD_NAME_RECORD.(operation_name).filter_list ) then
# COVERAGE_IGNORE_NEXT_LINE
Error( operation_name, " is already installed as a CAP operation with the same number of arguments" );
fi;
# check if we deal with a KeyDependentOperation if EndsWith( operation_name, "Op" ) then
if IsSpecializationOfFilter( "category", filters[1] ) then
if ForAny( known_methods, m -> Length( m.filters ) = Length( filters ) and ( IsSpecializationOfFilter( m.filters[1], filters[1] ) or IsSpecializationOfFilter( filters[1], m.filters[1] ) ) ) then
# COVERAGE_IGNORE_NEXT_LINE
Error( "there is already a method known for ", operation_name, " with a category filter which implies the current category filter or is implied by it" );
fi;
else
if IsSpecializationOfFilter( filters[1], "category" ) then
# COVERAGE_IGNORE_NEXT_LINE
Error( "The first filter is implied by `IsCapCategory`, this is not supported." );
fi;
if ForAny( known_methods,
m -> Length( m.filters ) = Length( filters ) and ForAll( [ 1 .. Length( filters ) ], i -> IsSpecializationOfFilter( m.filters[i], filters[i] ) or IsSpecializationOfFilter( filters[i], m.filters[i] ) )
) then
# COVERAGE_IGNORE_NEXT_LINE
Error( "there is already a method known for ", operation_name, " with a comparable filter list (see documentation of `CapJitAddKnownMethod`)" );
InstallGlobalFunction( "CapJitAddTypeSignature", function ( name, input_filters, output_data_type )
#= comment for Julia if IsCategory( ValueGlobal( name ) ) and Length( input_filters ) = 1then
Error( "adding type signatures for GAP categories applied to a single argument is not supported" );
fi;
if input_filters <> "any"then
if not IsList( input_filters ) then
# COVERAGE_IGNORE_NEXT_LINE
Error( "<input_filters> must be a list or the string \"any\"" );
fi;
if not ForAll( input_filters, filter -> IsFilter( filter ) ) then
# COVERAGE_IGNORE_NEXT_LINE
Error( "<input_filters> must be a list of filters or the string \"any\"" );
fi;
if ForAny( input_filters, f -> IsSpecializationOfFilter( IsFunction, f ) ) and (not IsFunction( output_data_type ) or NumberArgumentsFunction( output_data_type ) <> 2) then
if not name in [ "CreateCapCategoryObjectWithAttributes", "CreateCapCategoryMorphismWithAttributes" ] then
# COVERAGE_IGNORE_BLOCK_START
Print( "WARNING: You are adding a type signature for ", name, " which can get a function as input but you do not compute the signature of the function. ", "This will work for references to global functions but not for literal functions. ", "See `List` in `CompilerForCAP/gap/InferDataTypes.gi` for an example of how to handle the signature of functions properly.\n"
); # COVERAGE_IGNORE_BLOCK_END
fi;
fi;
fi;
if not IsBound( CAP_JIT_INTERNAL_TYPE_SIGNATURES.(name) ) then
CAP_JIT_INTERNAL_TYPE_SIGNATURES.(name) := [ ];
fi;
if ForAny( CAP_JIT_INTERNAL_TYPE_SIGNATURES.(name), signature -> IsSpecializationOfFilterList( signature[1], input_filters ) or IsSpecializationOfFilterList( input_filters, signature[1] ) ) then
# COVERAGE_IGNORE_NEXT_LINE
Error( "there already exists a signature for ", name, " with filters implying the current filters or being implied by them" );
fi;
if not ForAny( [ IsFilter, IsRecord, IsFunction ], f -> f( output_data_type ) ) then
# COVERAGE_IGNORE_NEXT_LINE
Error( "<output_data_type> must be a filter, a record, or a function" );
##
InstallGlobalFunction( CapJitTypedExpression, function ( value, data_type_getter )
# We try to execute data_type_getter to increase the code coverage. # This approach has some downsides: # * Previously, the implementation of `CapJitTypedExpression` was simply `ReturnFirst`, which should be faster. # * To avoid runtime overhead, we only execute the code below for assertion level >= 2 (which is used in the tests). # However, this leads to a possibly unexpected difference when executing code in tests and by hand. # * We can only execute data_type_getter if it has 0 arguments.
Assert( 2, (function ( )
local data_type;
if NumberArgumentsFunction( data_type_getter ) = 0then
# we do not have access to the category and hence cannot check anything
return true;
else
# COVERAGE_IGNORE_NEXT_LINE
Error( "the data type getter of CapJitTypedExpression must be a function accepting either no argument or a single argument" );
fi;
if IsFilter( data_type ) then
data_type := rec( filter := data_type );
fi;
if not IsRecord( data_type ) or not IsBound( data_type.filter ) then
# COVERAGE_IGNORE_NEXT_LINE
Error( "CapJitTypedExpression has returned ", data_type, " which is not a valid data type" );
fi;
# simple plausibility check if not data_type.filter( value ) then
# COVERAGE_IGNORE_NEXT_LINE
Error( "<value> does not lie in <data_type.filter>" );
fi;
return true;
end)( ) );
return value;
end );
##
InstallGlobalFunction( CapFixpoint, function ( predicate, func, initial_value )
local x, y;
y := initial_value;
while true do
x := y;
y := func( x ); if predicate( x, y ) then
break; fi;
od;
## #= comment for Julia # We want `args` to be a list but in Julia it's a tuple -> we need a separate implementation for Julia
InstallGlobalFunction( NTuple, function ( n, args... )
Assert( 0, Length( args ) = n );
return args;
end ); # =#
##
InstallGlobalFunction( Pair, function ( first, second ) #% CAP_JIT_RESOLVE_FUNCTION
return NTuple( 2, first, second );
end );
##
InstallGlobalFunction( Triple, function ( first, second, third ) #% CAP_JIT_RESOLVE_FUNCTION
return NTuple( 3, first, second, third );
end );
##
InstallGlobalFunction( TransposedMatWithGivenDimensions, function ( nr_rows, nr_cols, listlist )
if not ( Length( listlist ) = nr_rows and ForAll( listlist, row -> Length( row ) = nr_cols ) ) then
Error( "the arguments passed to 'TransposedMatWithGivenDimensions' are inconsistent!\n"); fi;
if not IsBound( underlying_category!.compiler_hints ) or not IsBound( underlying_category!.compiler_hints.precompiled_towers ) then
return;
fi;
if not IsList( underlying_category!.compiler_hints.precompiled_towers ) then
# COVERAGE_IGNORE_NEXT_LINE
Error( "`underlying_category!.compiler_hints.precompiled_towers` must be a list" );
fi;
precompiled_towers := [ ];
for info in underlying_category!.compiler_hints.precompiled_towers do
if not (IsRecord( info ) and IsBound( info.remaining_constructors_in_tower ) and IsBound( info.precompiled_functions_adder )) then
# COVERAGE_IGNORE_NEXT_LINE
Error( "the entries of `underlying_category!.compiler_hints.precompiled_towers` must be records with components `remaining_constructors_in_tower` and `precompiled_functions_adder`" );
# pass information on to the next level
Add( precompiled_towers, rec(
remaining_constructors_in_tower := remaining_constructors_in_tower{[ 2 .. Length( remaining_constructors_in_tower ) ]},
precompiled_functions_adder := precompiled_functions_adder,
) );
fi;
fi;
od;
if not IsEmpty( precompiled_towers ) then
if not IsBound( category!.compiler_hints ) then
category!.compiler_hints := rec( );
fi;
if not IsBound( category!.compiler_hints.precompiled_towers ) then
## #= comment for Julia # Julia does not have non-dense lists and thus needs a separate implementation
InstallGlobalFunction( ListWithKeys, function ( list, func )
local res, i;
# see implementation of `List`
res := EmptyPlist( Length( list ) );
# hack to save type adjustments and conversions (e.g. to blist) if Length( list ) > 0then
res[Length( list )] := 1;
fi;
for i in [ 1 .. Length( list ) ] do
res[i] := func( i, list[i] );
od;
return res;
end ); # =#
##
InstallGlobalFunction( SumWithKeys, function ( list, func )
local sum, i;
# see implementation of `Sum`
if IsEmpty( list ) then
sum := 0;
else
sum := func( 1, list[1] );
for i in [ 2 .. Length( list ) ] do
sum := sum + func( i, list[i] );
od;
fi;
return sum;
end );
##
InstallGlobalFunction( ProductWithKeys, function ( list, func )
local product, i;
# adapted implementation of `Product`
if IsEmpty( list ) then
product := 1;
else
product := func( 1, list[1] );
for i in [ 2 .. Length( list ) ] do
product := product * func( i, list[i] );
od;
fi;
return product;
end );
##
InstallGlobalFunction( ForAllWithKeys, function ( list, func )
local i;
# adapted implementation of `ForAll`
for i in [ 1 .. Length( list ) ] do
if not func( i, list[i] ) then
return false;
fi;
od;
return true;
end );
##
InstallGlobalFunction( ForAnyWithKeys, function ( list, func )
local i;
# adapted implementation of `ForAny`
for i in [ 1 .. Length( list ) ] do
if func( i, list[i] ) then
return true;
fi;
od;
return false;
end );
##
InstallGlobalFunction( NumberWithKeys, function ( list, func )
local nr, i;
# adapted implementation of `Number`
nr := 0;
for i in [ 1 .. Length( list ) ] do
if func( i, list[i] ) then
nr := nr + 1;
fi;
od;
return nr;
end );
## #= comment for Julia # Julia does not have non-dense lists and thus needs a separate implementation
InstallGlobalFunction( FilteredWithKeys, function ( list, func )
local res, i, elm, j;
# adapted implementation of `Filtered`
res := list{[ ]};
i := 0;
for j in [ 1 .. Length( list ) ] do
elm := list[j];
if func( j, elm ) then
i := i + 1;
res[i] := elm;
fi;
od;
return res;
end ); # =#
##
InstallGlobalFunction( FirstWithKeys, function ( list, func )
local elm, i;
# adapted implementation of `First`
for i in [ 1 .. Length( list ) ] do
elm := list[i];
if func( i, elm ) then
return elm;
fi;
od;
return fail;
end );
##
InstallGlobalFunction( LastWithKeys, function ( list, func )
local elm, i;
# adapted implementation of `Last`
for i in Reversed( [ 1 .. Length( list ) ] ) do
elm := list[i];
if func( i, elm ) then
return elm;
fi;
od;
return fail;
end );
if IsPackageMarkedForLoading( "Browse", ">= 1.5" ) then
generic_help_string := " You can access the category cell and category via the local variables 'cell' and 'category' in a break loop.";
if not IsCapCategoryCell( cell ) then
Error( human_readable_identifier_getter( ), " does not lie in the filter IsCapCategoryCell.", generic_help_string ); fi;
if not HasCapCategory( cell ) then
Error( human_readable_identifier_getter( ), " has no CAP category.", generic_help_string ); fi;
if not IsIdenticalObj( category, false ) and not IsIdenticalObj( CapCategory( cell ), category ) then
Error( "The CapCategory of ", human_readable_identifier_getter( ), " is not identical to the category named \033[1m", Name( category ), "\033[0m.", generic_help_string ); fi;
generic_help_string := " You can access the object and category via the local variables 'object' and 'category' in a break loop.";
if not IsCapCategoryObject( object ) then
Error( human_readable_identifier_getter( ), " does not lie in the filter IsCapCategoryObject.", generic_help_string ); fi;
if not HasCapCategory( object ) then
Error( human_readable_identifier_getter( ), " has no CAP category.", generic_help_string ); fi;
if not IsIdenticalObj( category, false ) and not IsIdenticalObj( CapCategory( object ), category ) then
Error( "The CapCategory of ", human_readable_identifier_getter( ), " is not identical to the category named \033[1m", Name( category ), "\033[0m.", generic_help_string ); fi;
if not IsIdenticalObj( category, false ) and not ObjectFilter( category )( object ) then
Error( human_readable_identifier_getter( ), " does not lie in the object filter of the category named \033[1m", Name( category ), "\033[0m.", generic_help_string ); fi;
generic_help_string := " You can access the morphism and category via the local variables 'morphism' and 'category' in a break loop.";
if not IsCapCategoryMorphism( morphism ) then
Error( human_readable_identifier_getter( ), " does not lie in the filter IsCapCategoryMorphism.", generic_help_string ); fi;
if not HasCapCategory( morphism ) then
Error( human_readable_identifier_getter( ), " has no CAP category.", generic_help_string ); fi;
if not IsIdenticalObj( category, false ) and not IsIdenticalObj( CapCategory( morphism ), category ) then
Error( "the CAP-category of ", human_readable_identifier_getter( ), " is not identical to the category named \033[1m", Name( category ), "\033[0m.", generic_help_string ); fi;
if not IsIdenticalObj( category, false ) and not MorphismFilter( category )( morphism ) then
Error( human_readable_identifier_getter( ), " does not lie in the morphism filter of the category named \033[1m", Name( category ), "\033[0m.", generic_help_string ); fi;
if not HasSource( morphism ) then
Error( human_readable_identifier_getter( ), " has no source.", generic_help_string ); fi;
CAP_INTERNAL_ASSERT_IS_OBJECT_OF_CATEGORY( Source( morphism ), category, {} -> Concatenation( "the source of ", human_readable_identifier_getter( ) ) );
if not HasRange( morphism ) then
Error( human_readable_identifier_getter( ), " has no range.", generic_help_string ); fi;
CAP_INTERNAL_ASSERT_IS_OBJECT_OF_CATEGORY( Range( morphism ), category, {} -> Concatenation( "the range of ", human_readable_identifier_getter( ) ) );
generic_help_string := " You can access the 2-cell and category via the local variables 'two_cell' and 'category' in a break loop.";
if not IsCapCategoryTwoCell( two_cell ) then
Error( human_readable_identifier_getter( ), " does not lie in the filter IsCapCategoryTwoCell.", generic_help_string ); fi;
if not HasCapCategory( two_cell ) then
Error( human_readable_identifier_getter( ), " has no CAP category.", generic_help_string ); fi;
if not IsIdenticalObj( category, false ) and not IsIdenticalObj( CapCategory( two_cell ), category ) then
Error( "the CapCategory of ", human_readable_identifier_getter( ), " is not identical to the category named \033[1m", Name( category ), "\033[0m.", generic_help_string ); fi;
if not IsIdenticalObj( category, false ) and not TwoCellFilter( category )( two_cell ) then
Error( human_readable_identifier_getter( ), " does not lie in the 2-cell filter of the category named \033[1m", Name( category ), "\033[0m.", generic_help_string ); fi;
if not HasSource( two_cell ) then
Error( human_readable_identifier_getter( ), " has no source.", generic_help_string ); fi;
CAP_INTERNAL_ASSERT_IS_MORPHISM_OF_CATEGORY( Source( two_cell ), category, {} -> Concatenation( "the source of ", human_readable_identifier_getter( ) ) );
if not HasRange( two_cell ) then
Error( human_readable_identifier_getter( ), " has no range.", generic_help_string ); fi;
CAP_INTERNAL_ASSERT_IS_MORPHISM_OF_CATEGORY( Range( two_cell ), category, {} -> Concatenation( "the range of ", human_readable_identifier_getter( ) ) );
end );
##
InstallGlobalFunction( PackageOfCAPOperation, function ( operation_name )
local packages;
if not IsBound( CAP_INTERNAL_METHOD_NAME_RECORD.(operation_name) ) then
# COVERAGE_IGNORE_NEXT_LINE
Error( operation_name, " is not a CAP operation" );
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.