#
# Tests for functions defined in src/opers.c
#
gap> START_TEST("kernel/opers.tst");

#
gap> flags := FLAGS_FILTER(IsMutable);
<flag list>
gap> flags2 := FLAGS_FILTER(IsPGroup and IsMutable);
<flag list>

#
gap> HASH_FLAGS(fail);
Error, HASH_FLAGS: <flags> must be a flags list (not the value 'fail')
gap> HASH_FLAGS(flags);
2

#
gap> TRUES_FLAGS(fail);
Error, TRUES_FLAGS: <flags> must be a flags list (not the value 'fail')
gap> TRUES_FLAGS(flags);
[ 1 ]

#
gap> SIZE_FLAGS(fail);
Error, SIZE_FLAGS: <flags> must be a flags list (not the value 'fail')
gap> SIZE_FLAGS(flags);
1

#
gap> IS_EQUAL_FLAGS(fail, flags);
Error, IS_EQUAL_FLAGS: <flags1> must be a flags list (not the value 'fail')
gap> IS_EQUAL_FLAGS(flags, fail);
Error, IS_EQUAL_FLAGS: <flags2> must be a flags list (not the value 'fail')
gap> IS_EQUAL_FLAGS(flags, flags);
true
gap> IS_EQUAL_FLAGS(flags, flags2);
false
gap> IS_EQUAL_FLAGS(flags2, flags);
false

#
gap> IS_SUBSET_FLAGS(fail, flags);
Error, IS_SUBSET_FLAGS: <flags1> must be a flags list (not the value 'fail')
gap> IS_SUBSET_FLAGS(flags, fail);
Error, IS_SUBSET_FLAGS: <flags2> must be a flags list (not the value 'fail')
gap> IS_SUBSET_FLAGS(flags, flags);
true
gap> IS_SUBSET_FLAGS(flags, flags2);
false
gap> IS_SUBSET_FLAGS(flags2, flags);
true

#
gap> SUB_FLAGS(fail, flags);
Error, SUB_FLAGS: <flags1> must be a flags list (not the value 'fail')
gap> SUB_FLAGS(flags, fail);
Error, SUB_FLAGS: <flags2> must be a flags list (not the value 'fail')
gap> emptyFlags := SUB_FLAGS(flags, flags);
<flag list>
gap> TRUES_FLAGS(emptyFlags);
[  ]
gap> TRUES_FLAGS(SUB_FLAGS(flags, flags2));
[  ]

# test comparison of equal but not identical filters
gap> f1:=SUB_FLAGS(flags2, flags);; f2:=FLAGS_FILTER(IsPGroup);;
gap> IS_EQUAL_FLAGS(f1, f2);
true
gap> f1 = f2;
true
gap> IsIdenticalObj(f1, f2);
false

#
gap> AND_FLAGS(fail, flags);
Error, AND_FLAGS: <flags1> must be a flags list (not the value 'fail')
gap> AND_FLAGS(flags, fail);
Error, AND_FLAGS: <flags2> must be a flags list (not the value 'fail')
gap> TRUES_FLAGS(AND_FLAGS(flags, flags));
[ 1 ]
gap> TRUES_FLAGS(AND_FLAGS(emptyFlags, flags));
[ 1 ]
gap> TRUES_FLAGS(AND_FLAGS(flags, emptyFlags));
[ 1 ]
gap> IS_EQUAL_FLAGS(AND_FLAGS(flags2, flags), flags2);
true
gap> IS_EQUAL_FLAGS(AND_FLAGS(flags, flags2), flags2);
true

#
gap> WITH_HIDDEN_IMPS_FLAGS(fail);
Error, WITH_HIDDEN_IMPS_FLAGS: <flags> must be a flags list (not the value 'fa\
il')
gap> TRUES_FLAGS(WITH_HIDDEN_IMPS_FLAGS(flags));
[ 1, 2 ]

#
gap> WITH_IMPS_FLAGS(fail);
Error, WITH_IMPS_FLAGS: <flags> must be a flags list (not the value 'fail')
gap> TRUES_FLAGS(WITH_IMPS_FLAGS(flags));
[ 1, 2 ]

#
gap> WITH_IMPS_FLAGS_STAT();;

# test DoSetAndFilter
gap> G:=SymmetricGroup(3);;
gap> filter:=Setter(IsPGroup and IsAbelian);;
gap> filter(G, false);
Error, You cannot set an "and-filter" except to true

# test DoSetFilter
gap> filter:=SETTER_FILTER(IsMutable);;
gap> filter(G, false);
gap> filter(G, true);
Error, filter is already set the other way

# test DoSetReturnTrueFilter
gap> Setter(IS_OBJECT)(G, true);
gap> Setter(IS_OBJECT)(G, false);
Error, you cannot set this flag to 'false'

# test DoSetProperty
gap> SetIsPGroup(G, false);
gap> SetIsPGroup(G, true);
Error, property is already set the other way

#
gap> NEW_FILTER(fail);
Error, NEW_FILTER: <name> must be a string (not the value 'fail')

#
gap> FLAG1_FILTER(fail);
Error, FLAG1_FILTER: <oper> must be an operation (not the value 'fail')
gap> FLAG1_FILTER(Normalizer);
0

#
gap> FLAG2_FILTER(fail);
Error, FLAG2_FILTER: <oper> must be an operation (not the value 'fail')
gap> FLAG2_FILTER(Normalizer);
0

#
gap> FLAGS_FILTER(fail);
Error, FLAGS_FILTER: <oper> must be an operation (not the value 'fail')
gap> FLAGS_FILTER(Normalizer);
false

#
gap> SETTER_FILTER(fail);
Error, SETTER_FILTER: <oper> must be an operation (not the value 'fail')
gap> SETTER_FILTER(Normalizer);
false

#
gap> TESTER_FILTER(fail);
Error, TESTER_FILTER: <oper> must be an operation (not the value 'fail')
gap> TESTER_FILTER(Normalizer);
false

#
#
#

# DoOperationNArgs
gap> SymmetricGroupCons(1,2);
Error, Constructor: the first argument must be a filter (not the integer 1)

#
gap> NEW_OPERATION(fail);
Error, NEW_OPERATION: <name> must be a string (not the value 'fail')
gap> NEW_CONSTRUCTOR(fail);
Error, NEW_CONSTRUCTOR: <name> must be a string (not the value 'fail')
gap> NEW_ATTRIBUTE(fail);
Error, NEW_ATTRIBUTE: <name> must be a string (not the value 'fail')
gap> OPER_TO_ATTRIBUTE(fail);
Error, OPER_TO_ATTRIBUTE: <oper> must be an operation (not the value 'fail')
gap> OPER_TO_MUTABLE_ATTRIBUTE(fail);
Error, OPER_TO_MUTABLE_ATTRIBUTE: <oper> must be an operation (not the value '\
fail')
gap> NEW_MUTABLE_ATTRIBUTE(fail);
Error, NEW_MUTABLE_ATTRIBUTE: <name> must be a string (not the value 'fail')
gap> NEW_PROPERTY(fail);
Error, NEW_PROPERTY: <name> must be a string (not the value 'fail')

#
gap> NEW_GLOBAL_FUNCTION(fail);
Error, NEW_GLOBAL_FUNCTION: <name> must be a string (not the value 'fail')
gap> INSTALL_GLOBAL_FUNCTION(fail, fail);
Error, INSTALL_GLOBAL_FUNCTION: <oper> must be a function (not the value 'fail\
')
gap> func := NEW_GLOBAL_FUNCTION("quux");;
gap> INSTALL_GLOBAL_FUNCTION(func, fail);
Error, INSTALL_GLOBAL_FUNCTION: <func> must be a function (not the value 'fail\
')
gap> INSTALL_GLOBAL_FUNCTION(func, Size);
Error, <func> must not be an operation
gap> INSTALL_GLOBAL_FUNCTION(func, x -> x);
gap> INSTALL_GLOBAL_FUNCTION(func, x -> x);
Error, operation already installed

#
gap> METHODS_OPERATION(fail,1);
Error, METHODS_OPERATION: <oper> must be an operation (not the value 'fail')
gap> METHODS_OPERATION(Size,-1);
Error, METHODS_OPERATION: <narg> must be an integer between 0 and 6 (not the i\
nteger -1)
gap> METHODS_OPERATION(Size,0);
[  ]
gap> METHODS_OPERATION(Size,6);
[  ]
gap> METHODS_OPERATION(Size,7);
Error, METHODS_OPERATION: <narg> must be an integer between 0 and 6 (not the i\
nteger 7)

# note: CHANGED_METHODS_OPERATION is not usable on HPC-GAP
gap> CHANGED_METHODS_OPERATION(fail,1);
Error, CHANGED_METHODS_OPERATION: <oper> must be an operation (not the value '\
fail')
gap> CHANGED_METHODS_OPERATION(Size,-1);
Error, CHANGED_METHODS_OPERATION: <narg> must be an integer between 0 and 6 (n\
ot the integer -1)
gap> if not IsHPCGAP then CHANGED_METHODS_OPERATION(Size,0); fi;
gap> if not IsHPCGAP then CHANGED_METHODS_OPERATION(Size,6); fi;
gap> CHANGED_METHODS_OPERATION(Size,7);
Error, CHANGED_METHODS_OPERATION: <narg> must be an integer between 0 and 6 (n\
ot the integer 7)

#
gap> SET_METHODS_OPERATION (fail,1,[]);
Error, SET_METHODS_OPERATION: <oper> must be an operation (not the value 'fail\
')
gap> SET_METHODS_OPERATION (Size,-1,[]);
Error, SET_METHODS_OPERATION: <narg> must be an integer between 0 and 6 (not t\
he integer -1)
gap> SET_METHODS_OPERATION (Size,0,[]);
gap> SET_METHODS_OPERATION (Size,6,[]);
gap> SET_METHODS_OPERATION (Size,7,[]);
Error, SET_METHODS_OPERATION: <narg> must be an integer between 0 and 6 (not t\
he integer 7)

#
gap> f:=SETTER_FUNCTION("foobar", IsPGroup);;
gap> f(fail, false);
Error, <obj> must be a component object
gap> SetIsPGroup(fail, false);
Error, property cannot be set for internal objects

#
gap> f:=GETTER_FUNCTION("foobar");;
gap> f(fail);
Error, <obj> must be a component object

#
gap> CLEAR_CACHE_INFO();
gap> opcheck := OPERS_CACHE_INFO();;
#@if GAPInfo. KernelInfo. KernelDebug and IsHPCGAP
gap> opcheck{[1..11]};
[ 0, 0, 0, 6, 0, 0, 4, 0, 0, 0, 0 ]
#@fi

# FIXME: the following code skips entry 4 (OperationHit, which is normally
# equal to 7) because on arm64, ppc64le, and s390x builds on Travis, we see 6
# instead.  To get an idea what's going on, I inserted printf statements each
# time OperationHit is incremented.  For the arm64, ppc64le, and s390x builds
# on Travis, this is what I get:
#
# OperationHit = 1 for ReadLine
# OperationHit = 2 for CloseStream
# OperationHit = 3 for CloseStream
# OperationHit = 4 for InputTextString
# OperationHit = 5 for OutputTextString
# OperationHit = 6 for PrintFormattingStatus
# OperationHit = 7 for ReadLine
# OperationHit = 8 for CloseStream
# ...
#
# In contrast, this is what I get on x86:
# OperationHit = 1 for ReadLine
# OperationHit = 2 for CloseStream
# OperationHit = 3 for CloseStream
# OperationHit = 4 for Int          <--------- this is new
# OperationHit = 5 for InputTextString
# OperationHit = 6 for OutputTextString
# OperationHit = 7 for PrintFormattingStatus
# OperationHit = 8 for ReadLine
# OperationHit = 9 for CloseStream
# ...
#
# I inserted some more code to pinpoint the GAP code leading to this, and I
# found out that the Int call in question is the one in lib/test.gi:213; i.e.,
# in this code:
#
#       Print("\r# line ", pos[i],
#             " of ", nrlines,
#             " (", Int(pos[i] / nrlines * 100), "%)",   # <- this is the Int call
#             "\c");
#
# Thus the cause of this discrepancy remains a mystery. 
#
#@if GAPInfo. KernelInfo. KernelDebug and not IsHPCGAP
gap> opcheck{Difference([1..11], [4])};
[ 0, 0, 0, 0, 0, 2, 0, 0, 0, 0 ]
#@fi
#@if not GAPInfo. KernelInfo. KernelDebug
gap> opcheck{[1..11]};
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
#@fi

#
# method tracing
#
gap> g:= Group( (1,2,3), (1,2) );;  Size( g );
6
gap> TraceMethods();
Error, `TraceMethods' require at least one argument
gap> TraceMethods([ 1 ]);
Error, TRACE_METHODS: <oper> must be an operation (not the integer 1)
gap> TraceMethods( [ Size ] );
gap> UntraceMethods( [ Size ] );

# temporarily override (NEXT_)VMETHOD_PRINT_INFO to avoid system
# specific paths in the output
gap> MakeReadWriteGlobal("VMETHOD_PRINT_INFO");
gap> MakeReadWriteGlobal("NEXT_VMETHOD_PRINT_INFO");
gap> old1:=VMETHOD_PRINT_INFO;;
gap> old2:=NEXT_VMETHOD_PRINT_INFO;;
gap> VMETHOD_PRINT_INFO := function(methods, i, arity)
>     local offset;
>     offset := (arity+BASE_SIZE_METHODS_OPER_ENTRY)*(i-1)+arity;
>     Print("#I  ", methods[offset+4], "\n");
> end;;
gap> NEXT_VMETHOD_PRINT_INFO := function(methods, i, arity)
>     local offset;
>     offset := (arity+BASE_SIZE_METHODS_OPER_ENTRY)*(i-1)+arity;
>     Print("#I Trying next: ", methods[offset+4], "\n");
> end;;

#
gap> TraceMethods( [ IsCyclic ] );
gap> g:= Group( (1,2,3), (1,2) );;
gap> IsCyclic(g);
#I  IsCyclic
#I Trying next: IsCyclic: generic method for groups
false
gap> g:= Group( (1,2,3), (1,3,2) );;
gap> IsCyclic(g);
#I  IsCyclic
#I Trying next: IsCyclic: generic method for groups
true

# restore PRINT_INFO functions
gap> VMETHOD_PRINT_INFO:=old1;;
gap> NEXT_VMETHOD_PRINT_INFO:=old2;;
gap> MakeReadOnlyGlobal("VMETHOD_PRINT_INFO");
gap> MakeReadOnlyGlobal("NEXT_VMETHOD_PRINT_INFO");

#
gap> UntraceMethods();
Error, `UntraceMethods' require at least one argument
gap> UntraceMethods([ 1 ]);
Error, UNTRACE_METHODS: <oper> must be an operation (not the integer 1)
gap> UntraceMethods( [ IsCyclic ] );

#
gap> STOP_TEST("kernel/opers.tst", 1);
