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


Quelle  oper.g   Sprache: unbekannt

 
#############################################################################
##
##  This file is part of GAP, a system for computational discrete algebra.
##  This file's authors include Thomas Breuer, Frank Celler, Martin Schönert.
##
##  Copyright of GAP belongs to its developers, whose names are too numerous
##  to list here. Please refer to the COPYRIGHT file for details.
##
##  SPDX-License-Identifier: GPL-2.0-or-later
##
##  This file defines operations and such. Some functions have moved
##  to oper1.g so as to be compiled in the default kernel
##


INSTALL_METHOD := false;


#############################################################################
##
#F  INFO_DEBUG( <level>, ... )
##
##  <ManSection>
##  <Func Name="INFO_DEBUG" Arg='level, ...'/>
##
##  <Description>
##  This will delegate to the proper info class <C>InfoDebug</C>
##  as soon as the info classes are available.
##  </Description>
##  </ManSection>
##
BIND_GLOBAL( "INFO_DEBUG", function( arg )
    Print( "#I  " );
    CALL_FUNC_LIST( Print, arg{ [ 2 .. LEN_LIST( arg ) ] } );
    Print( "\n" );
end );


#############################################################################
##
#F  INFO_OBSOLETE( <level>, ... )
##
##  <ManSection>
##  <Func Name="INFO_OBSOLETE" Arg='level, ...'/>
##
##  <Description>
##  This will delegate to the proper info class <C>InfoObsolete</C>
##  as soon as the info classes are available.
##  </Description>
##  </ManSection>
##
BIND_GLOBAL( "INFO_OBSOLETE", function( arg )
    if GAPInfo.CommandLineOptions.O then
        Print( "#I  " );
        CALL_FUNC_LIST( Print, arg{ [ 2 .. LEN_LIST( arg ) ] } );
        Print( "\n" );
    fi;
end );


#############################################################################
##
#V  CATS_AND_REPS
##
##  <ManSection>
##  <Var Name="CATS_AND_REPS"/>
##
##  <Description>
##  a list of filter numbers of categories and representations
##  </Description>
##  </ManSection>
##
BIND_GLOBAL( "CATS_AND_REPS", [] );
if IsHPCGAP then
    ShareSpecialObj(CATS_AND_REPS);
fi;


#############################################################################
##
#V  IMMEDIATES
##
##  <ManSection>
##  <Var Name="IMMEDIATES"/>
##
##  <Description>
##  is a list that contains at position <M>i</M> the description of all those
##  immediate methods for which <C>FILTERS</C><M>[i]</M> belongs to the
##  requirements.
##  <P/>
##  So each entry of <C>IMMEDIATES</C> is a zipped list, where 6 consecutive
##  positions are ..., and the  position of  the  method itself  in the list
##  <C>IMMEDIATE_METHODS</C>.
##  <P/>
##  Note:
##  1. If a method requires two filters <M>F_1</M> and <M>F_2</M> such that
##     <M>F_1</M> implies <M>F_2</M>,
##     it will <E>not</E> be installed for <M>F_2</M>.
##  2. If not all requirements are categories/representations then
##     the category/representation part of the requirements will be ignored;
##  <!-- and if only cats are required? Does this make sense?-->
##  <!-- and what about representations that may change?-->
##     in other words, the only information that may cause to run immediate
##     methods is acquired information.
##  </Description>
##  </ManSection>
##
if IsHPCGAP then
    BIND_GLOBAL( "IMMEDIATES", AtomicList([]) );
else
    BIND_GLOBAL( "IMMEDIATES", [] );
fi;


#############################################################################
##
#V  IMMEDIATE_METHODS
##
##  <ManSection>
##  <Var Name="IMMEDIATE_METHODS"/>
##
##  <Description>
##  is a list of functions that are installed as immediate methods.
##  </Description>
##  </ManSection>
##
if IsHPCGAP then
    BIND_GLOBAL( "IMMEDIATE_METHODS", AtomicList([]) );
else
    BIND_GLOBAL( "IMMEDIATE_METHODS", [] );
fi;


#############################################################################
##
#V  OPERATIONS
##
##  <ManSection>
##  <Var Name="OPERATIONS"/>
##
##  <Description>
##  is a list that stores all &GAP; operations at the odd positions,
##  and the corresponding list of requirements at the even positions.
##  More precisely, if the operation <C>OPERATIONS[<A>n</A>]</C> has been declared
##  by several calls of <C>DeclareOperation</C>,
##  with second arguments <A>req1</A>, <A>req2</A>, \ldots,
##  each being a list of filters, then <C>OPERATIONS[ <A>n</A>+1 ]</C> is the list
##  <C>[</C> <A>flags1</A>, <A>flags2</A>, <M>\ldots</M>, <C>]</C>,
##  where <A>flagsi</A> is the list of flags of the filters in <A>reqi</A>.
##  </Description>
##  </ManSection>
##
if IsHPCGAP then
    OPERATIONS_REGION := ShareSpecialObj("OPERATIONS_REGION");  # FIXME: remove
    BIND_GLOBAL( "OPERATIONS", MakeStrictWriteOnceAtomic( [] ) );
    BIND_GLOBAL( "OPER_FLAGS", MakeStrictWriteOnceAtomic( rec() ) );
else
    BIND_GLOBAL( "OPERATIONS", [] );
    BIND_GLOBAL( "OPER_FLAGS", rec() );
fi;
BIND_GLOBAL( "STORE_OPER_FLAGS",
function(oper, flags)
  local nr, info;
  nr := MASTER_POINTER_NUMBER(oper);
  if not IsBound(OPER_FLAGS.(nr)) then
    # we need a back link to oper for the post-restore function
    if IsHPCGAP then
        OPER_FLAGS.(nr) := FixedAtomicList([oper,
            MakeWriteOnceAtomic([]), MakeWriteOnceAtomic([])]);
    else
        OPER_FLAGS.(nr) := [oper, [], []];
    fi;
    ADD_LIST(OPERATIONS, oper);
  fi;
  info := OPER_FLAGS.(nr);
  ADD_LIST(info[2], MakeImmutable(flags));
  ADD_LIST(info[3], MakeImmutable([INPUT_FILENAME(), READEVALCOMMAND_LINENUMBER, INPUT_LINENUMBER()]));
end);

BIND_GLOBAL( "GET_OPER_FLAGS", function(oper)
  local nr;
  nr := MASTER_POINTER_NUMBER(oper);
  if not IsBound(OPER_FLAGS.(nr)) then
    return fail;
  fi;
  return OPER_FLAGS.(nr)[2];
end);
BIND_GLOBAL( "GET_DECLARATION_LOCATIONS", function(oper)
  local nr;
  nr := MASTER_POINTER_NUMBER(oper);
  if not IsBound(OPER_FLAGS.(nr)) then
    return fail;
  fi;
  return OPER_FLAGS.(nr)[3];
end);

# the object handles change after loading a workspace
ADD_LIST(GAPInfo.PostRestoreFuncs, function()
  local tmp, a;
  tmp := [];
  for a in REC_NAMES(OPER_FLAGS) do
    ADD_LIST(tmp, OPER_FLAGS.(a));
    Unbind(OPER_FLAGS.(a));
  od;
  for a in tmp do
    OPER_FLAGS.(MASTER_POINTER_NUMBER(a[1])) := a;
  od;
end);

#############################################################################
##
#V  WRAPPER_OPERATIONS
##
##  <ManSection>
##  <Var Name="WRAPPER_OPERATIONS"/>
##
##  <Description>
##  is a list that stores all those &GAP; operations for which the default
##  method is to call a related operation if necessary,
##  and to store and look up the result using an attribute.
##  An example is <C>SylowSubgroup</C>, which calls <C>SylowSubgroupOp</C> if the
##  required Sylow subgroup is not yet stored in <C>ComputedSylowSubgroups</C>.
##  </Description>
##  </ManSection>
##
BIND_GLOBAL( "WRAPPER_OPERATIONS", [] );
if IsHPCGAP then
    LockAndMigrateObj( WRAPPER_OPERATIONS, OPERATIONS_REGION);
fi;


#############################################################################
##
#F  IsNoImmediateMethodsObject(<obj>)
##
##  <#GAPDoc Label="IsNoImmediateMethodsObject">
##  <ManSection>
##  <Filt Name="IsNoImmediateMethodsObject" Arg='obj'/>
##
##  <Description>
##  If this filter is set immediate methods will be ignored for <A>obj</A>.
##  This can be crucial for performance for objects like pcgs
##  (see Section <Ref Sect="Polycyclic Generating Systems"/>), of which many
##  are created, which are collections, but for which all those immediate
##  methods for <Ref Prop="IsTrivial"/> et cetera do not really make sense.
##  Other examples of objects in <Ref Filt="IsNoImmediateMethodsObject"/> are
##  compressed vectors and matrices over small finite fields,
##  see the sections <Ref Subsect="Row Vectors over Finite Fields"/> and
##  <Ref Subsect="Matrices over Finite Fields"/>.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL("IsNoImmediateMethodsObject",
  NewFilter("IsNoImmediateMethodsObject"));


#############################################################################
##
#V  IGNORE_IMMEDIATE_METHODS
##
##  <ManSection>
##  <Var Name="IGNORE_IMMEDIATE_METHODS"/>
##
##  <Description>
##  is usually <K>false</K>.
##  Only inside a call of <C>RunImmediateMethods</C> it is set to
##  <K>true</K>,
##  which causes that <C>RunImmediateMethods</C> does not suffer
##  from recursion.
##  </Description>
##  </ManSection>
##
IGNORE_IMMEDIATE_METHODS := false;


#############################################################################
##
#F  INSTALL_IMMEDIATE_METHOD( <oper>, <info>, <filter>, <rank>, <method> )
##
##  <ManSection>
##  <Func Name="INSTALL_IMMEDIATE_METHOD" Arg='oper, info, filter, rank, method'/>
##
##  <Description>
##  </Description>
##  </ManSection>
##
BIND_CONSTANT("SIZE_IMMEDIATE_METHOD_ENTRY", 8);
BIND_GLOBAL( "INSTALL_IMMEDIATE_METHOD",
    function( oper, info, filter, rank, method )

    local   flags,
            relev,
            i,
            rflags,
            wif,
            ignore,
            j,
            k,
            replace,
            pos,
            imm;

    # Check whether <oper> really is an operation.
    if not IS_OPERATION(oper)  then
        Error( "<oper> is not an operation" );
    fi;

    # Check whether this in fact installs an implication.
    if    FLAGS_FILTER(oper) <> false
      and (method = true or method = RETURN_TRUE)
    then
        Error( "use `InstallTrueMethod' for <oper>" );
    fi;

    # Find the requirements.
    flags := TRUES_FLAGS( FLAGS_FILTER( filter ) );
    if LEN_LIST( flags ) = 0 then
        Error( "no immediate methods without requirements!" );
    elif FLAG1_FILTER( IS_MUTABLE_OBJ ) in flags  then
        Error( "no immediate methods for mutable objects!" );
    fi;
    relev := [];
    atomic FILTER_REGION do

    for i  in flags  do
        if not INFO_FILTERS[i] in FNUM_CATS_AND_REPS  then
            ADD_LIST( relev, i );
        fi;
    od;

    # All requirements are categories/representations.
    # Install the method for one of them.
    if LEN_LIST( relev ) = 0  then
        relev:= [ flags[1] ];
    fi;
    flags:= relev;

    # Remove requirements that are implied by the remaining ones.
    # (Note that it is possible to have implications from a filter
    # to another one with a bigger number.)
    relev  := [];
    rflags := [];
    for i  in flags  do

      # Get the implications of this filter.
      wif:= WITH_IMPS_FLAGS( FLAGS_FILTER( FILTERS[i] ) );

      # If the filter is implied by one in `relev', ignore it.
      # Otherwise add it to `relev', and remove all those that
      # are implied by the new filter.
      ignore:= false;
      for j  in [ 1 .. LEN_LIST( relev ) ]  do
          if IsBound( rflags[j] ) then
              if IS_SUBSET_FLAGS( rflags[j], wif ) then

                  # `FILTERS[i]' is implied by one in `relev'.
                  ignore:= true;
                  break;
              elif IS_SUBSET_FLAGS( wif, rflags[j] ) then

                  # `FILTERS[i]' implies one in `relev'.
                  Unbind( relev[j]  );
                  Unbind( rflags[j] );
              fi;
          fi;
      od;
      if not ignore then
          ADD_LIST( relev, i    );
          ADD_LIST( rflags, wif );
      fi;
    od;

    # We install the method for the requirements in `relev'.
    if IsHPCGAP then
        # 'pos' is saved for modifying 'imm' below.
        pos:=AddAtomicList( IMMEDIATE_METHODS, method );
    else
        ADD_LIST( IMMEDIATE_METHODS, method );
        pos := LEN_LIST( IMMEDIATE_METHODS );
    fi;

    for j  in relev  do

      # adjust `IMM_FLAGS'
      IMM_FLAGS:= SUB_FLAGS( IMM_FLAGS, FLAGS_FILTER( FILTERS[j] ) );
#T here it would be better to subtract a flag list
#T with `true' exactly at position `j'!
#T means: When an immed. method gets installed for a property then
#T the property tester should remain in IMM_FLAGS.
#T (This would make an if statement in `RunImmediateMethods' unnecessary!)

      # Find the place to put the new method.
      if not IsHPCGAP then
          if IsBound( IMMEDIATES[j] ) then
              imm := IMMEDIATES[j];
          else
              imm := [];
              IMMEDIATES[j] := imm;
          fi;
      else
          if IsBound( IMMEDIATES[j] ) then
              imm := SHALLOW_COPY_OBJ(IMMEDIATES[j]);
          else
              imm := [];
          fi;
      fi;
      i := 0;
      while i < LEN_LIST(imm) and rank < imm[i+5]  do
          i := i + SIZE_IMMEDIATE_METHOD_ENTRY;
      od;

      # Now is a good time to see if the method is already there
      if REREADING then
          replace := false;
          k := i;
          while k < LEN_LIST(imm) and rank = imm[k+5] do
              if info = imm[k+7] and oper = imm[k+1] and
                 FLAGS_FILTER( filter ) = imm[k+4] then
                  replace := true;
                  i := k;
                  break;
              fi;
              k := k+SIZE_IMMEDIATE_METHOD_ENTRY;
          od;
      fi;

      # push the other functions back
      if not REREADING or not replace then
          imm{[SIZE_IMMEDIATE_METHOD_ENTRY+i+1..SIZE_IMMEDIATE_METHOD_ENTRY+LEN_LIST(imm)]} := imm{[i+1..LEN_LIST(imm)]};
      fi;

      # install the new method
      imm[i+1] := oper;
      imm[i+2] := SETTER_FILTER( oper );
      imm[i+3] := FLAGS_FILTER( TESTER_FILTER( oper ) );
      imm[i+4] := FLAGS_FILTER( filter );
      imm[i+5] := rank;
      imm[i+6] := pos;
      imm[i+7] := IMMUTABLE_COPY_OBJ(info);
      if SIZE_IMMEDIATE_METHOD_ENTRY >= 8 then
          imm[i+8] := MakeImmutable([INPUT_FILENAME(), READEVALCOMMAND_LINENUMBER, INPUT_LINENUMBER()]);
      fi;

      if IsHPCGAP then
          IMMEDIATES[j]:=MakeImmutable(imm);
      fi;
    od;
    od;

end );


#############################################################################
##
#F  InstallImmediateMethod( <opr>[, <info>], <filter>[, <rank>], <method> )
##
##  <#GAPDoc Label="InstallImmediateMethod">
##  <ManSection>
##  <Func Name="InstallImmediateMethod"
##   Arg='opr[, info], filter, rank, method'/>
##
##  <Description>
##  <Ref Func="InstallImmediateMethod"/> installs <A>method</A> as an
##  immediate method for <A>opr</A>, which must be an attribute or a
##  property, with requirement <A>filter</A> and rank <A>rank</A>
##  (the rank can be omitted, in which case 0 is used as rank).
##  The rank must be an integer value that measures the priority of
##  <A>method</A> among the immediate methods for <A>opr</A>.
##  If supplied, <A>info</A> should be a short but informative string
##  that describes the situation in which the method is called.
##  <P/>
##  An immediate method is called automatically as soon as the object lies
##  in <A>filter</A>, provided that the value is not yet known.
##  Afterwards the attribute setter is called in order to store the value,
##  unless the method exits via <Ref Func="TryNextMethod"/>.
##  <P/>
##  Note the difference to <Ref Func="InstallMethod"/>
##  that no family predicate occurs
##  because <A>opr</A> expects only one argument,
##  and that <A>filter</A> is not a list of requirements but the argument
##  requirement itself.
##  <P/>
##  Immediate methods are thought of as a possibility for objects to gain
##  useful knowledge.
##  They must not be used to force the storing of <Q>defining information</Q>
##  in an object.
##  In other words, &GAP; should work even if all immediate methods are
##  completely disabled.
##  Therefore, the call to <Ref Func="InstallImmediateMethod"/> installs
##  <A>method</A> also as an ordinary method for <A>opr</A>
##  with requirement <A>filter</A>.
##  <P/>
##  Note that in such a case &GAP; executes a computation for which
##  it was not explicitly asked by the user.
##  So one should install only those methods as immediate methods
##  that are <E>extremely cheap</E>.
##  To emphasize this,
##  immediate methods are also called <E>zero cost methods</E>.
##  The time for their execution should really be approximately zero.
##  <P/>
##  For example, the size of a permutation group can be computed very cheaply
##  if a stabilizer chain of the group is known.
##  So it is reasonable to install an immediate method for
##  <Ref Attr="Size"/> with requirement
##  <C>IsGroup and Tester( <A>stab</A> )</C>,
##  where <A>stab</A> is the attribute corresponding to the stabilizer chain.
##  <P/>
##  Another example would be the implementation of the conclusion that
##  every finite group of prime power order is nilpotent.
##  This could be done by installing an immediate method for the attribute
##  <Ref Prop="IsNilpotentGroup"/> with requirement
##  <C>IsGroup and Tester( Size )</C>.
##  This method would then check whether the size is a finite prime power,
##  return <K>true</K> in this case and otherwise call
##  <Ref Func="TryNextMethod"/>.
##  But this requires factoring of an integer,
##  which cannot be guaranteed to be very cheap,
##  so one should not install this method as an immediate method.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "InstallImmediateMethod", function( arg )
    local pos, opr, info, filter, rank, method;

    pos := 1;

    if pos <= LEN_LIST( arg ) and IS_OPERATION( arg[pos] ) then
        opr := arg[pos];
        pos := pos + 1;
    else
        pos := -1;
    fi;

    if pos <= LEN_LIST( arg ) and IS_STRING( arg[pos] ) then
        info := arg[pos];
        pos := pos + 1;
    else
        info := false;
    fi;

    if pos <= LEN_LIST( arg ) and IsFilter( arg[pos] ) then
        filter := arg[pos];
        pos := pos + 1;
    else
        pos := -1;
    fi;

    if pos <= LEN_LIST( arg ) and IS_RAT( arg[pos] ) then
        rank := arg[pos];
        pos := pos + 1;
    else
        rank := 0;
    fi;

    if pos <= LEN_LIST( arg ) and IS_FUNCTION( arg[pos] ) then
        method := arg[pos];
        pos := pos + 1;
    else
        pos := -1;
    fi;

    if pos = LEN_LIST( arg ) + 1 then
        INSTALL_IMMEDIATE_METHOD( opr, info, filter, rank, method );
        INSTALL_METHOD( [ opr, info, [ filter ], method ], false );
    else
        Error("usage: InstallImmediateMethod( <opr>[, <info>], <filter>, <rank>, <method> )");
    fi;

end );


#############################################################################
##
#F  TraceImmediateMethods( <flag> )
##
##  <#GAPDoc Label="TraceImmediateMethods">
##  <ManSection>
##  <Func Name="TraceImmediateMethods" Arg='[flag]'/>
##  <Func Name="UntraceImmediateMethods" Arg=''/>
##
##  <Description>
##  <Ref Func="TraceImmediateMethods"/> enables tracing for all immediate methods
##  if <A>flag</A> is either <K>true</K>, or not present.
##  <Ref Func="UntraceImmediateMethods"/>, or <Ref Func="TraceImmediateMethods"/>
##  with <A>flag</A> equal <K>false</K> turns tracing off.
##  (There is no facility to trace <E>specific</E> immediate methods.)
##  <Log><![CDATA[
##  gap> TraceImmediateMethods( );
##  gap> g:= Group( (1,2,3), (1,2) );;
##  #I RunImmediateMethods
##  #I  immediate: Size
##  #I  immediate: IsCyclic
##  #I  immediate: IsCommutative
##  #I  immediate: IsTrivial
##  gap> Size( g );
##  #I  immediate: IsPerfectGroup
##  #I  immediate: IsNonTrivial
##  #I  immediate: Size
##  #I  immediate: IsFreeAbelian
##  #I  immediate: IsTorsionFree
##  #I  immediate: IsNonTrivial
##  #I  immediate: IsPerfectGroup
##  #I  immediate: GeneralizedPcgs
##  #I  immediate: IsEmpty
##  6
##  gap> UntraceImmediateMethods( );
##  gap> UntraceMethods( [ Size ] );
##  ]]></Log>
##  <P/>
##  This example gives an explanation for the two calls of the
##  <Q>system getter</Q> for <Ref Attr="Size"/>.
##  Namely, there are immediate methods that access the known size
##  of the group.
##  Note that the group <C>g</C> was known to be finitely generated already
##  before the size was computed,
##  the calls of the immediate method for
##  <Ref Prop="IsFinitelyGeneratedGroup"/> after the call of
##  <Ref Attr="Size"/> have other arguments than <C>g</C>.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
TRACE_IMMEDIATE_METHODS := false;

BIND_GLOBAL( "UntraceImmediateMethods", function ()
    TRACE_IMMEDIATE_METHODS := false;
end );

BIND_GLOBAL( "TraceImmediateMethods", function( arg )
    if LENGTH(arg) = 0 then
        TRACE_IMMEDIATE_METHODS := true;
        return;
    fi;

    if LENGTH(arg) > 1 or not IS_BOOL(arg[1]) then
      Error("Usage: TraceImmediateMethods( [bool] )");
    fi;

    if arg[1] then
        TRACE_IMMEDIATE_METHODS := true;
    else
        TRACE_IMMEDIATE_METHODS := false;
    fi;
end );

#############################################################################
##
##
##  <#GAPDoc Label="TraceInternalMethods">
##  <ManSection>
##  <Func Name="TraceInternalMethods" Arg=''/>
##  <Func Name="UntraceInternalMethods" Arg=''/>
##  <Func Name="GetTraceInternalMethodsCounts" Arg=''/>
##  <Func Name="ClearTraceInternalMethodsCounts" Arg=''/>
##
##  <Description>
##  <Ref Func="TraceInternalMethods"/> enables tracing for all internal methods.
##  Internal methods are methods which implement many fundamental operations in GAP.
##  In this version of GAP, the internal methods which can be traced are:
##  <List>
##  <Mark>Zero, ZeroMut</Mark><Item>Mutable and Immutable <Ref Attr="Zero"/></Item>
##  <Mark>AInv, AInvMut</Mark><Item>Mutable and Immutable <Ref Attr="AdditiveInverse"/></Item>
##  <Mark>One, OneMut</Mark><Item>Mutable and Immutable <Ref Attr="One"/></Item>
##  <Mark>Inv, InvMut</Mark><Item>Mutable and Immutable <Ref Attr="Inverse"/></Item>
##  <Mark>Sum</Mark><Item>The operator <Ref Oper="\+"/></Item>
##  <Mark>Diff</Mark><Item>The operator <C>-</C> operator</Item>
##  <Mark>Prod</Mark><Item>The operator <Ref Oper="\*"/></Item>
##  <Mark>Quo</Mark><Item>The operator <Ref Oper="\/"/></Item>
##  <Mark>LQuo</Mark><Item>The left-quotient operator</Item>
##  <Mark>Pow</Mark><Item>The operator <Ref Oper="\^"/></Item>
##  <Mark>Comm</Mark><Item>The operator <Ref Oper="Comm"/></Item>
##  <Mark>Mod</Mark><Item>The operator <Ref Oper="\mod"/></Item>
##  </List>
##  <P/>
##  <Ref Func="UntraceInternalMethods"/> turns tracing off.
##  As these methods can be called hundreds of thousands of times in simple GAP
##  code, there isn't a statement printed each time one is called. Instead, the
##  method <Ref Func="GetTraceInternalMethodsCounts"/> returns how many times
##  each operation has been applied to each type of variable (the type of a
##  variable can be found with the <C>TNAM_OBJ</C> method).
##  The return value for two argument operators is a record of records <C>r</C>, where
##  <C>r.op</C> stores information about operator <C>op</C>. For one argument operators
##  <C>r.op.i</C> stores how many times <C>op</C> was called with an argument of type
##  <C>i</C>, while for two argument operators <C>r.op.i.j</C> stores how many times
##  <C>op</C> was called with arguments of type <C>i</C> and <C>j</C>.
##  <Log><![CDATA[
## gap> TraceInternalMethods();
## true
## gap> 2+3+4+5+6;;
## gap> 2.0+2.0;;
## gap> 3^(1,2,3);;
## gap> GetTraceInternalMethodsCounts();
## rec( Pow := rec( integer := rec( ("permutation (small)") := 1 ) ),
##  Sum := rec( integer := rec( integer := 4 ),
##      macfloat := rec( macfloat := 1 ) ) )
## # 'macfloat' is a floating point number
## gap> UntraceInternalMethods();
##  ]]></Log>
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##

# The return type here is stored as a record, as 0 is a valid TNUM.
BIND_GLOBAL("GetTraceInternalMethodsCounts", function()
    local ret, type, i, j, counts,member, nicename;
    counts := GET_TRACED_INTERNAL_METHODS_COUNTS();
    ret := rec();
    for type in REC_NAMES(counts) do
        # Drop the 'Funcs' part
        nicename := type{[1..LENGTH(type)-5]};
        ret.(nicename) := rec();
        member := counts.(type);
        for i in [1..LENGTH(member)] do
            if IsBound(member[i]) then
                if IS_LIST(member[LENGTH(member)]) then
                    # Is a 2D array
                    ret.(nicename).(GET_TNAM_FROM_TNUM(i-1)) := rec();
                    for j in [1..LENGTH(member[i])] do
                        if IsBound(member[i][j]) then
                            ret.(nicename).(GET_TNAM_FROM_TNUM(i-1)).(GET_TNAM_FROM_TNUM(j-1)) := member[i][j];
                        fi;
                    od;
                else
                    # Is a 1D array
                    ret.(nicename).(GET_TNAM_FROM_TNUM(i-1)) := member[i];
                fi;
            fi;
        od;
    od;
    return ret;
end);

#############################################################################
##
#F  NewOperation( <name>, <args-filts> )
##
##  <#GAPDoc Label="NewOperation">
##  <ManSection>
##  <Func Name="NewOperation" Arg='name, args-filts'/>
##
##  <Description>
##  <Ref Func="NewOperation"/> returns an operation <A>opr</A> with name
##  <A>name</A>.
##  The list <A>args-filts</A> describes requirements about the arguments
##  of <A>opr</A>, namely the number of arguments must be equal to the length
##  of <A>args-filts</A>, and the <M>i</M>-th argument must lie in the filter
##  <A>args-filts</A><M>[i]</M>.
##  <P/>
##  Each method that is installed for <A>opr</A> via
##  <Ref Func="InstallMethod"/> must require that the <M>i</M>-th argument
##  lies in the filter <A>args-filts</A><M>[i]</M>.
##  <P/>
##  One can install methods for other argument tuples via
##  <Ref Func="InstallOtherMethod"/>,
##  this way it is also possible to install methods for a different number
##  of arguments than the length of <A>args-filts</A>.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "NewOperation", function ( name, filters )
    local   oper,  filt,  filter;

    if GAPInfo.MaxNrArgsMethod < LEN_LIST( filters ) then
      Error( "methods can have at most ", GAPInfo.MaxNrArgsMethod,
             " arguments" );
    fi;
    oper := NEW_OPERATION( name );
    filt := [];
    for filter  in filters  do
        if not IS_OPERATION( filter ) then
          Error( "<filter> must be an operation" );
        fi;
        ADD_LIST( filt, FLAGS_FILTER( filter ) );
    od;
    STORE_OPER_FLAGS(oper, filt);
    return oper;
end );


#############################################################################
##
#F  NewConstructor( <name>, <filters> )
##
##  <#GAPDoc Label="NewConstructor">
##  <ManSection>
##  <Func Name="NewConstructor" Arg='name, args-filts'/>
##
##  <Description>
##  <Ref Func="NewConstructor"/> returns a constructor <A>cons</A> with name
##  <A>name</A>.
##  The list <A>args-filts</A> describes requirements about the arguments
##  of <A>cons</A>. Namely the number of arguments must be equal to the length
##  of <A>args-filts</A>, and the <M>i</M>-th argument
##  must lie in the filter <A>args-filts</A><M>[i]</M> for <M>i \neq 1</M>.
##  A constructor expects the first argument to be a <E>filter</E> instead
##  of an object and it must be a subset of the filter
##  <A>args-filts</A><M>[1]</M>.
##  <P/>
##  Each method that is installed for <A>cons</A> via
##  <Ref Func="InstallMethod"/> must require that
##  the <M>i</M>-th argument lies in the filter <A>args-filts</A><M>[i]</M>
##  for <M>i \neq 1</M>.
##  Its first argument is a filter and must be a subset of the filter
##  <A>args-filts</A><M>[1]</M>.
##  <P/>
##  One can install methods for other argument tuples via
##  <Ref Func="InstallOtherMethod"/>,
##  this way it is also possible to install methods for a different number
##  of arguments than the length of <A>args-filts</A>.
##  <P/>
##  Note that the method selection for constructors works slightly differently
##  than for usual operations.
##  As stated above, applicabilty to the first argument in an argument tuple
##  is tested by determining whether the argument-filter is a <E>subset</E> of
##  <A>args-filts</A><M>[1]</M>.
##  <P/>
##  The rank of a method installed for a constructor is determined solely by
##  <A>args-filts</A><M>[1]</M> of the method.
##  Instead of taking the sum of the ranks of filters involved in its
##  <A>args-filts</A><M>[1]</M>, the sum of <M>-1</M> times these values
##  is taken.
##  The result is added to the number <A>val</A> used in the call of
##  <Ref Func="InstallMethod"/>.
##  <P/>
##  This has the following effects on the method selection for constructors.
##  If <A>cons</A> is called with an argument tuple whose first argument is
##  the filter <A>filt</A>, any method whose first argument is
##  <E>more</E> specific than <A>filt</A> is applicable
##  (if its other <A>args-filts</A> also match).
##  Then the method with the <Q>most general</Q> filter <A>args-filts</A><M>[1]</M>
##  is chosen, since the rank is computed by taking <M>-1</M> times the ranks
##  of the involved filters.
##  Thus, a constructor is chosen which returns an object in <A>filt</A> using
##  as few extra filters as possible, which presumably is both more flexible
##  to use and easier to construct.
##  <P/>
##  The following example showcases this behaviour.
##  Note that the argument <A>filter</A> is only used for method dispatch.
##  <Log><![CDATA[
##  DeclareFilter( "IsMyObj" );
##  DeclareFilter( "IsMyFilter" );
##  DeclareFilter( "IsMyOtherFilter" );
##  BindGlobal( "MyFamily", NewFamily( "MyFamily" ) );
##
##  DeclareConstructor( "NewMyObj", [ IsMyObj ] );
##
##  InstallMethod( NewMyObj,
##  [ IsMyObj ],
##  function( filter )
##      local type;
##      Print("General constructor\n");
##      type := NewType( MyFamily, IsMyObj );
##      return Objectify( type, [] );
##  end );
##  InstallMethod( NewMyObj,
##  [ IsMyObj and IsMyFilter and IsMyOtherFilter ],
##  function( filter )
##      local type;
##      Print("Special constructor\n");
##      type := NewType( MyFamily, IsMyObj and IsMyFilter and IsMyOtherFilter );
##      return Objectify( type, [] );
##  end );
##  ]]></Log>
##  If only IsMyObj is given, both methods are applicable and the general
##  constructor is called.
##  If also IsMyFilter is given, only the special constructor is applicable.
##  <Log><![CDATA[
##  gap> a := NewMyObj( IsMyObj );;
##  General constructor
##  gap> IsMyOtherFilter(a);
##  false
##  gap> b := NewMyObj( IsMyObj and IsMyFilter );;
##  Special constructor
##  gap> IsMyOtherFilter(b);
##  true
##  gap> c := NewMyObj( IsMyObj and IsMyFilter and IsMyOtherFilter );;
##  Special constructor
##  gap> IsMyOtherFilter(c);
##  true
##  ]]></Log>
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "NewConstructor", function ( name, filters )
    local   oper,  filt,  filter;

    if LEN_LIST( filters ) = 0 then
      Error( "constructors must have at least one argument" );
    fi;
    if GAPInfo.MaxNrArgsMethod < LEN_LIST( filters ) then
      Error( "methods can have at most ", GAPInfo.MaxNrArgsMethod,
             " arguments" );
    fi;
    oper := NEW_CONSTRUCTOR( name );
    filt := [];
    for filter  in filters  do
        if not IS_OPERATION( filter ) then
          Error( "<filter> must be an operation" );
        fi;
        ADD_LIST( filt, FLAGS_FILTER( filter ) );
    od;
    STORE_OPER_FLAGS(oper, filt);
    return oper;
end );


#############################################################################
##
#F  DeclareOperation( <name>, <filters> )
##
##  <#GAPDoc Label="DeclareOperation">
##  <ManSection>
##  <Func Name="DeclareOperation" Arg='name, filters'/>
##
##  <Description>
##  does the same as <Ref Func="NewOperation"/> and then binds
##  the new operation to the global variable <A>name</A>. The variable
##  must previously be writable, and is made read-only by this function.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "DeclareOperation", function ( name, filters )
    local gvar, pos, req, filt, filter;

    if   GAPInfo.MaxNrArgsMethod < LEN_LIST( filters ) then
      Error( "methods can have at most ", GAPInfo.MaxNrArgsMethod,
             " arguments" );
    elif ISB_GVAR( name ) then

      gvar:= VALUE_GLOBAL( name );

      # Check that the variable is in fact an operation.
      if not IS_OPERATION( gvar ) then
        Error( "variable `", name, "' is not bound to an operation" );
      fi;

      # The operation has already been declared.
      # If it was created as attribute or property,
      # and if the new declaration is unary
      # then ask for re-declaration as attribute or property.
      # (Note that the values computed for objects matching the new
      # requirements will be stored.)
      if LEN_LIST( filters ) = 1 and FLAG2_FILTER( gvar ) <> 0 then

        # `gvar' is an attribute (tester) or property (tester).
        pos:= POS_LIST_DEFAULT( FILTERS, gvar, 0 );
        if pos = fail then

          # `gvar' is an attribute.
          Error( "operation `", name,
                 "' was created as an attribute, use `DeclareAttribute'" );

        elif    INFO_FILTERS[ pos ] in FNUM_TPRS
             or INFO_FILTERS[ pos ] in FNUM_ATTS then

          # `gvar' is an attribute tester or property tester.
          Error( "operation `", name,
                 "' is an attribute tester or property tester" );

        else

          # `gvar' is a property.
          Error( "operation `", name,
                 "' was created as a property, use `DeclareProperty'" );

        fi;

      fi;

      # Add the new requirements if they differ from known ones.
      filt := [];
      for filter  in filters  do
        if not IS_OPERATION( filter ) then
          Error( "<filter> must be an operation" );
        fi;
        ADD_LIST( filt, FLAGS_FILTER( filter ) );
      od;

      req := GET_OPER_FLAGS(gvar);
      if IsHPCGAP then
        req := FromAtomicList(req);  # so that we can search in it
      fi;
      if filt in req then
        if not REREADING then
          INFO_DEBUG( 1, "equal requirements in multiple declarations ",
              "for operation `", name, "'\n" );
        fi;
      else
        STORE_OPER_FLAGS( gvar, filt );
      fi;

    else

      # The operation is new.
      BIND_GLOBAL( name, NewOperation( name, filters ) );

    fi;
end );


#############################################################################
##
#F  DeclareOperationKernel( <name>, <filters>, <kernel-oper> )
##
##  <ManSection>
##  <Func Name="DeclareOperationKernel" Arg='name, filters, kernel-oper'/>
##
##  <Description>
##  This function must not be used to re-declare an operation
##  that has already been declared.
##  </Description>
##  </ManSection>
##
BIND_GLOBAL( "DeclareOperationKernel", function ( name, filters, oper )
    local   filt,  filter;

    if GAPInfo.MaxNrArgsMethod < LEN_LIST( filters ) then
      Error( "methods can have at most ", GAPInfo.MaxNrArgsMethod,
             " arguments" );
    fi;

    # This will yield an error if `name' is already bound.
    BIND_GLOBAL( name, oper );
    SET_NAME_FUNC( oper, name );

    filt := [];
    for filter  in filters  do
        if not IS_OPERATION( filter ) then
          Error( "<filter> must be an operation" );
        fi;
        ADD_LIST( filt, FLAGS_FILTER( filter ) );
    od;

    STORE_OPER_FLAGS(oper, filt);
end );


#############################################################################
##
#F  DeclareConstructor( <name>, <filters> )
##
##  <#GAPDoc Label="DeclareConstructor">
##  <ManSection>
##  <Func Name="DeclareConstructor" Arg='name, filters'/>
##
##  <Description>
##  does the same as <Ref Func="NewConstructor"/> and then binds
##  the result to the global variable <A>name</A>. The variable
##  must previously be writable, and is made read-only by this function.
##  <P/>
##  Note that for operations which are constructors special rules with respect
##  to applicability and rank of the corresponding methods apply
##  (see section <Ref Func="NewConstructor"/>).
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "DeclareConstructor", function ( name, filters )

    local gvar, req, filt, filter;

    if LEN_LIST( filters ) = 0 then
      Error( "constructors must have at least one argument" );
    elif GAPInfo.MaxNrArgsMethod < LEN_LIST( filters ) then
      Error( "methods can have at most ", GAPInfo.MaxNrArgsMethod,
             " arguments" );
    elif ISB_GVAR( name ) then

      gvar:= VALUE_GLOBAL( name );

      # Check that the variable is in fact an operation.
      if not IS_OPERATION( gvar ) then
        Error( "variable `", name, "' is not bound to an operation" );
      fi;

      # The constructor has already been declared.
      # If it was not created as a constructor
      # then ask for re-declaration as an ordinary operation.
      if not IS_CONSTRUCTOR(gvar) then
        Error( "operation `", name, "' was not created as a constructor" );
      fi;

      # Add the new requirements.
      filt := [];
      for filter  in filters  do
        if not IS_OPERATION( filter ) then
          Error( "<filter> must be an operation" );
        fi;
        ADD_LIST( filt, FLAGS_FILTER( filter ) );
      od;

      STORE_OPER_FLAGS( gvar, filt );

    else

      # The operation is new.
      BIND_GLOBAL( name, NewConstructor( name, filters ) );

    fi;
end );


#############################################################################
##
#F  DeclareConstructorKernel( <name>, <filter>, <kernel-oper> )
##
##  <ManSection>
##  <Func Name="DeclareConstructorKernel" Arg='name, filter, kernel-oper'/>
##
##  <Description>
##  This function must not be used to re-declare a constructor
##  that has already been declared.
##  </Description>
##  </ManSection>
##
BIND_GLOBAL( "DeclareConstructorKernel", DeclareOperationKernel);

#############################################################################
##
#F  InstallAttributeFunction( <func> )  . . . run function for each attribute
##
##  <ManSection>
##  <Func Name="InstallAttributeFunction" Arg='func'/>
##
##  <Description>
##  <C>InstallAttributeFunction</C> installs <A>func</A>, so that
##  <C><A>func</A>( <A>name</A>, <A>filter</A>, <A>getter</A>, <A>setter</A>, <A>tester</A>, <A>mutflag</A> )</C>
##  is called for each attribute.
##  </Description>
##  </ManSection>
##
if IsHPCGAP then
    BIND_GLOBAL( "ATTRIBUTES", MakeStrictWriteOnceAtomic( [] ) );
    BIND_GLOBAL( "ATTR_FUNCS", MakeStrictWriteOnceAtomic( [] ) );
else
    BIND_GLOBAL( "ATTRIBUTES", [] );
    BIND_GLOBAL( "ATTR_FUNCS", [] );
fi;

BIND_GLOBAL( "InstallAttributeFunction", function ( func )
    local   attr;
    for attr in ATTRIBUTES do
        func( attr[1], attr[2], attr[3], attr[4], attr[5], attr[6] );
    od;
    ADD_LIST( ATTR_FUNCS, func );
end );

BIND_GLOBAL( "RUN_ATTR_FUNCS",
    function ( filter, getter, setter, tester, mutflag )
    local    name, func;
    name:= NAME_FUNC( getter );
    for func in ATTR_FUNCS do
        func( name, filter, getter, setter, tester, mutflag );
    od;
    ADD_LIST( ATTRIBUTES,
        MakeImmutable( [ name, filter, getter, setter, tester, mutflag ] ) );
end );


#############################################################################
##
BIND_GLOBAL( "BIND_SETTER_TESTER",
function( name, setter, tester)
    local nname;
    nname:= "Set"; APPEND_LIST_INTR( nname, name );
    BIND_GLOBAL( nname, setter );
    nname:= "Has"; APPEND_LIST_INTR( nname, name );
    BIND_GLOBAL( nname, tester );
end );


#############################################################################
##
#F  DeclareAttributeKernel( <name>, <filter>, <getter> )  . . . new attribute
##
##  <ManSection>
##  <Func Name="DeclareAttributeKernel" Arg='name, filter, getter'/>
##
##  <Description>
##  This function must not be used to re-declare an attribute
##  that has already been declared.
##  </Description>
##  </ManSection>
##
BIND_GLOBAL( "DeclareAttributeKernel", function ( name, filter, getter )
    local setter, tester;

    # This will yield an error if `name' is already bound.
    BIND_GLOBAL( name, getter );
    SET_NAME_FUNC( getter, name );

    # construct setter and tester
    setter := SETTER_FILTER( getter );
    tester := TESTER_FILTER( getter );

    # add getter, setter and tester to the list of operations
    STORE_OPER_FLAGS(getter, [ FLAGS_FILTER(filter) ]);
    STORE_OPER_FLAGS(setter, [ FLAGS_FILTER(filter), FLAGS_FILTER(IS_OBJECT) ]);
    STORE_OPER_FLAGS(tester, [ FLAGS_FILTER(filter) ]);

    # store the information about the filter
    REGISTER_FILTER( tester, FLAG2_FILTER( tester ), 1, FNUM_ATTR_KERN );

    # clear the cache because <filter> is something old
    if not GAPInfo.CommandLineOptions.N then
      InstallHiddenTrueMethod( filter, tester );
    fi;
    CLEAR_HIDDEN_IMP_CACHE( tester );

    # run the attribute functions
    RUN_ATTR_FUNCS( filter, getter, setter, tester, false );


    # and make the remaining assignments
    BIND_SETTER_TESTER( name, setter, tester );

end );


#############################################################################
##
#F  NewAttribute( <name>, <filter>[, <mutable>][, <rank>] ) . . new attribute
##
##  <#GAPDoc Label="NewAttribute">
##  <ManSection>
##  <Func Name="NewAttribute" Arg='name, filter[, "mutable"][, rank]'/>
##
##  <Description>
##  <Ref Func="NewAttribute"/> returns a new attribute getter with name
##  <A>name</A> that is applicable to objects with the property
##  <A>filter</A>.
##  <P/>
##  Contrary to the situation with categories and representations,
##  the tester of the new attribute does <E>not</E> imply <A>filter</A>.
##  This is exactly because of the possibility to install methods
##  that do not require <A>filter</A>.
##  <P/>
##  For example, the attribute <Ref Attr="Size"/> was created
##  with second argument a list or a collection,
##  but there is also a method for <Ref Attr="Size"/> that is
##  applicable to a character table,
##  which is neither a list nor a collection.
##  <P/>
##  For the optional third and fourth arguments, there are the following
##  possibilities.
##  <List>
##  <Item> The integer argument <A>rank</A> causes the attribute tester to have
##  this incremental rank (see <Ref Sect="Filters"/>),
##  </Item>
##  <Item> If the argument <A>mutable</A> is the string <C>"mutable"</C> or
##  the boolean <K>true</K>, then the values of the attribute are mutable.
##  </Item>
##  <Item> If the argument <A>mutable</A> is the boolean <K>false</K>, then
##  the values of the attribute are immutable.
##  </Item>
##  </List>
##  <P/>
##  When a value of such mutable attribute is set
##  then this value itself is stored, not an immutable copy of it,
##  and it is the user's responsibility to set an object that is mutable.
##  This is useful for an attribute whose value is some partial information
##  that may be completed later.
##  For example, there is an attribute <C>ComputedSylowSubgroups</C>
##  for the list holding those Sylow subgroups of a group that have been
##  computed already by the function
##  <Ref Oper="SylowSubgroup"/>,
##  and this list is mutable because one may want to enter groups into it
##  as they are computed.
##  <!-- in the current implementation, one can overwrite values of mutable-->
##  <!-- attributes; is this really intended?-->
##  <!-- if yes then it should be documented!-->
##  <P/>
##  If no argument for <A>rank</A> is given, then the rank of the tester is 1.
##  <P/>
##  Each method for the new attribute that does <E>not</E> require
##  its argument to lie in <A>filter</A> must be installed using
##  <Ref Func="InstallOtherMethod"/>.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "OPER_SetupAttribute", function(getter, flags, mutflag, filter, rank, name)
    local   setter,  tester;

    # add  setter and tester to the list of operations
    setter := SETTER_FILTER( getter );
    tester := TESTER_FILTER( getter );

    STORE_OPER_FLAGS(setter, [ flags, FLAGS_FILTER( IS_OBJECT ) ]);
    STORE_OPER_FLAGS(tester, [ flags ]);

    # store information about the filter
    REGISTER_FILTER( tester, FLAG2_FILTER( tester ), rank, FNUM_ATTR );

    # the <tester> is newly made, therefore  the cache cannot contain a  flag
    # list involving <tester>
    if not GAPInfo.CommandLineOptions.N then
      InstallHiddenTrueMethod( filter, tester );
    fi;
    # CLEAR_HIDDEN_IMP_CACHE();

    # run the attribute functions
    RUN_ATTR_FUNCS( filter, getter, setter, tester, mutflag );

end);

# construct getter, setter and tester
BIND_GLOBAL( "NewAttribute", function ( name, filter, args... )
    local  flags, mutflag, getter, rank;

    if not IS_STRING( name ) then
        Error( "<name> must be a string");
    fi;

    if not IsFilter( filter ) then
        Error( "<filter> must be a filter" );
    fi;

    rank := 1;
    mutflag := false;
    if LEN_LIST(args) = 0 then
        # this is fine, but does nothing
    elif LEN_LIST(args) = 1 and args[1] in [ "mutable", true, false ] then
        mutflag := args[1] in [ "mutable", true];
    elif LEN_LIST(args) = 1 and IS_INT(args[1]) then
        rank := args[1];
    elif LEN_LIST(args) = 2
         and args[1] in [ "mutable", true, false ]
         and IS_INT(args[2]) then
        mutflag := args[1] in [ "mutable", true ];
        rank := args[2];
    else
        Error("Usage: NewAttribute( <name>, <filter>[, <mutable>][, <rank>] )");
    fi;

    flags:= FLAGS_FILTER( filter );

    # construct a new attribute
    if mutflag then
        getter := NEW_MUTABLE_ATTRIBUTE( name );
    else
        getter := NEW_ATTRIBUTE( name );
    fi;
    STORE_OPER_FLAGS(getter, [ flags ]);

    OPER_SetupAttribute(getter, flags, mutflag, filter, rank, name);

    # And return the getter
    return getter;
end );


#############################################################################
##
#F  DeclareAttribute( <name>, <filter>[, "mutable"][, <rank>] ) new attribute
##
##  <#GAPDoc Label="DeclareAttribute">
##  <ManSection>
##  <Func Name="DeclareAttribute" Arg='name, filter[, "mutable"][, rank]'/>
##
##  <Description>
##  does the same as <Ref Func="NewAttribute"/> and then binds
##  the result to the global variable <A>name</A>. The variable
##  must previously be writable, and is made read-only by this function.
##  It also binds read-only global variables with names
##  <C>Has<A>name</A></C> and <C>Set<A>name</A></C>
##  for the tester and setter of the attribute (see Section
##  <Ref Sect="Setter and Tester for Attributes"/>).
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##

BIND_GLOBAL( "ConvertToAttribute",
function(name, op, filter, rank, mutable)
    local req, reqs, flags;
    # `op' is not an attribute (tester) and not a property (tester),
    # or `op' is a filter; in any case, `op' is not an attribute.

    # if `op' has no one argument declarations we can turn it into
    # an attribute
    req := GET_OPER_FLAGS(op);
    for reqs in req do
        if LENGTH(reqs)  = 1 then
            Error( "operation `", name, "' has been declared as a one ",
                   "argument Operation and cannot also be an Attribute");
        fi;
    od;

    flags := FLAGS_FILTER(filter);
    STORE_OPER_FLAGS( op, [ flags ] );

    # kernel magic for the conversion
    if mutable then
        OPER_TO_MUTABLE_ATTRIBUTE(op);
    else
        OPER_TO_ATTRIBUTE(op);
    fi;

    OPER_SetupAttribute(op, flags, mutable, filter, rank, name);

    # and make the remaining assignments
    BIND_SETTER_TESTER( name, SETTER_FILTER(op), TESTER_FILTER(op) );
end);

BIND_GLOBAL( "DeclareAttribute", function ( name, filter, args... )
    local gvar, req, attr, mutflag, rank;

    if not IS_STRING( name ) then
        Error( "<name> must be a string");
    fi;

    if not IsFilter( filter ) then
        Error( "<filter> must be a filter" );
    fi;

    rank := 1;
    mutflag := false;
    if LEN_LIST(args) = 0 then
        # this is fine, but does nothing
    elif LEN_LIST(args) = 1 and args[1] in [ "mutable", true, false ] then
        mutflag := args[1] in [ "mutable", true];
    elif LEN_LIST(args) = 1 and IS_INT(args[1]) then
        rank := args[1];
    elif LEN_LIST(args) = 2
         and args[1] in [ "mutable", true, false ]
         and IS_INT(args[2]) then
        mutflag := args[1] in [ "mutable", true ];
        rank := args[2];
    else
        Error("Usage: DeclareAttribute( <name>, <filter>[, <mutable>][, <rank>] )");
    fi;

    if ISB_GVAR( name ) then
        # The variable exists already.
        gvar := VALUE_GLOBAL( name );

        # Check that the variable is in fact bound to an operation.
        if not IS_OPERATION( gvar ) then
            Error( "variable `", name, "' is not bound to an operation" );
        fi;

        # Check whether the variable is in fact bound to an attribute, i.e.,
        # it has an associated tester (whose id is in FLAG2_FILTER) but is not
        # a filter itself (to exclude properties, and also and-filters for which
        # FLAG2_FILTER also is non-zero).
        if FLAG2_FILTER( gvar ) <> 0 and not IsFilter(gvar) then
            # gvar is already an attribute, extend it by the new filter
            STORE_OPER_FLAGS( gvar, [ FLAGS_FILTER( filter ) ] );

            # also set the extended range for the setter
            req := GET_OPER_FLAGS( Setter(gvar) );
            STORE_OPER_FLAGS( Setter(gvar), [ FLAGS_FILTER( filter), req[1][2] ] );
        else
            # gvar is an existing non-attribute operation, try to convert it
            # into an attribute
            ConvertToAttribute(name, gvar, filter, rank, mutflag);
        fi;
    else
        # The attribute is new.
        attr := NewAttribute(name, filter, mutflag, rank);
        BIND_GLOBAL( name, attr );

        # and make the remaining assignments
        BIND_SETTER_TESTER( name, SETTER_FILTER(attr), TESTER_FILTER(attr) );
    fi;
end );


#############################################################################
##
#V  LENGTH_SETTER_METHODS_2
##
##  <ManSection>
##  <Var Name="LENGTH_SETTER_METHODS_2"/>
##
##  <Description>
##  is the current length of <C>METHODS_OPERATION( <A>attr</A>, 2 )</C>
##  for an attribute <A>attr</A> for which no individual setter methods
##  are installed.
##  (This is used for <C>ObjectifyWithAttributes</C>.)
##  </Description>
##  </ManSection>
##
LENGTH_SETTER_METHODS_2 := 0;


#############################################################################
##
#F  DeclarePropertyKernel( <name>, <filter>, <getter> ) . . . .  new property
##
##  <ManSection>
##  <Func Name="DeclarePropertyKernel" Arg='name, filter, getter'/>
##
##  <Description>
##  This function must not be used to re-declare a property
##  that has already been declared.
##  </Description>
##  </ManSection>
##
BIND_GLOBAL( "DeclarePropertyKernel", function ( name, filter, getter )
    local setter, tester;

    # This will yield an error if `name' is already bound.
    BIND_GLOBAL( name, getter );
    SET_NAME_FUNC( getter, name );

    # construct setter and tester
    setter := SETTER_FILTER( getter );
    tester := TESTER_FILTER( getter );

    # add getter, setter and tester to the list of operations
    STORE_OPER_FLAGS(getter, [ FLAGS_FILTER(filter) ]);
    STORE_OPER_FLAGS(setter, [ FLAGS_FILTER(filter), FLAGS_FILTER(IS_BOOL) ]);
    STORE_OPER_FLAGS(tester, [ FLAGS_FILTER(filter) ]);

    # store information about the filters
    REGISTER_FILTER( getter, FLAG1_FILTER( getter ), 1, FNUM_PROP_KERN );
    REGISTER_FILTER( tester, FLAG2_FILTER( tester ), 1, FNUM_TPR_KERN );

    # clear the cache because <filter> is something old
    if not GAPInfo.CommandLineOptions.N then
      InstallHiddenTrueMethod( tester, getter );
      CLEAR_HIDDEN_IMP_CACHE( getter );
      InstallHiddenTrueMethod( filter, tester );
      CLEAR_HIDDEN_IMP_CACHE( tester );
    fi;

    # run the attribute functions
    RUN_ATTR_FUNCS( filter, getter, setter, tester, false );


    # and make the remaining assignments
    BIND_SETTER_TESTER( name, setter, tester );
end );


#############################################################################
##
#F  NewProperty( <name>, <filter>[, <rank>] ) . . . . . . . . .  new property
##
##  <#GAPDoc Label="NewProperty">
##  <ManSection>
##  <Func Name="NewProperty" Arg='name, filter[, rank]'/>
##
##  <Description>
##  <Ref Func="NewProperty"/> returns a new property <A>prop</A> with name
##  <A>name</A> (see also <Ref Sect="Properties"/>).
##  The filter <A>filter</A> describes the involved filters of <A>prop</A>.
##  As in the case of attributes,
##  <A>filter</A> is not implied by <A>prop</A>.
##  <P/>
##  The optional third argument <A>rank</A> denotes the incremental rank
##  (see <Ref Sect="Filters"/>) of the property
##  <A>prop</A> itself, i.e. <E>not</E> of its tester;
##  the default value is 1.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "NewProperty", function ( arg )
    local name, filter, rank, flags, getter, setter, tester;

    name   := arg[1];
    filter := arg[2];
    if LEN_LIST( arg ) = 3 and IS_INT( arg[3] ) then
        rank := arg[3];
    else
        rank := 1;
    fi;

    if not IS_OPERATION( filter ) then
      Error( "<filter> must be an operation" );
    fi;
    flags:= FLAGS_FILTER( filter );

    # construct getter, setter and tester
    getter := NEW_PROPERTY(  name );
    setter := SETTER_FILTER( getter );
    tester := TESTER_FILTER( getter );

    # add getter, setter and tester to the list of operations
    STORE_OPER_FLAGS(getter, [ flags ]);
    STORE_OPER_FLAGS(setter, [ flags, FLAGS_FILTER(IS_BOOL) ]);
    STORE_OPER_FLAGS(tester, [ flags ]);

    # store information about the filters
    REGISTER_FILTER( getter, FLAG1_FILTER( getter ), rank, FNUM_PROP );
    REGISTER_FILTER( tester, FLAG2_FILTER( tester ), 1, FNUM_TPR );

    # the <tester> and  <getter> are newly  made, therefore the cache cannot
    # contain a flag list involving <tester> or <getter>
    if not GAPInfo.CommandLineOptions.N then
      InstallHiddenTrueMethod( tester, getter );
      InstallHiddenTrueMethod( filter, tester );
    fi;
    # CLEAR_HIDDEN_IMP_CACHE();

    # run the attribute functions
    RUN_ATTR_FUNCS( filter, getter, setter, tester, false );


    # and return the getter
    return getter;
end );


#############################################################################
##
#F  DeclareProperty( <name>, <filter> [,<rank>] ) . . . . . . .  new property
##
##  <#GAPDoc Label="DeclareProperty">
##  <ManSection>
##  <Func Name="DeclareProperty" Arg='name, filter [,rank]'/>
##
##  <Description>
##  does the same as <Ref Func="NewProperty"/> and then binds
##  the result to the global variable <A>name</A>. The variable
##  must previously be writable, and is made read-only by this function.
##  It also binds read-only global variables with names
##  <C>Has<A>name</A></C> and <C>Set<A>name</A></C>
##  for the tester and setter of the property (see Section
##  <Ref Sect="Setter and Tester for Attributes"/>).
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "DeclareProperty", function ( arg )

    local prop, name, gvar, req, filter;

    name:= arg[1];

    if ISB_GVAR( name ) then

      gvar:= VALUE_GLOBAL( name );

      # Check that the variable is in fact an operation.
      if not IS_OPERATION( gvar ) then
        Error( "variable `", name, "' is not bound to an operation" );
      fi;

      # The property has already been declared.
      # If it was not created as a property
      # then ask for re-declaration as an ordinary operation.
      # (Note that the values computed for objects matching the new
      # requirements cannot be stored.)
      if FLAG1_FILTER( gvar ) = 0 or FLAG2_FILTER( gvar ) = 0 then

        # `gvar' is not a property (tester).
        Error( "operation `", name, "' was not created as a property,",
               " use `DeclareOperation'" );

      fi;

      # Add the new requirements.
      filter:= arg[2];
      if not IS_OPERATION( filter ) then
        Error( "<filter> must be an operation" );
      fi;

      STORE_OPER_FLAGS( gvar, [ FLAGS_FILTER( filter ) ] );

    else

      # The property is new.
      prop:= CALL_FUNC_LIST( NewProperty, arg );
      BIND_GLOBAL( name, prop );
      BIND_SETTER_TESTER( name, SETTER_FILTER( prop ), TESTER_FILTER( prop ) );

    fi;
end );



#############################################################################
##
#F  InstallAtExit( <func> ) . . . . . . . . . . function to call when exiting
##
BIND_GLOBAL( "InstallAtExit", function( func )
    local f;
    if not IS_FUNCTION(func)  then
        Error( "<func> must be a function" );
    fi;
    if CHECK_INSTALL_METHOD  then
        if not NARG_FUNC(func) in [ -1, 0 ]  then
            Error( "<func> must accept zero arguments" );
        fi;
    fi;
    # Return if function has already been installed
    # Use this long form to support both List and AtomicList
    for f in GAPInfo.AtExitFuncs do
        if f = func then
            return;
        fi;
    od;
    ADD_LIST( GAPInfo.AtExitFuncs, func );
end );


#############################################################################
##
#O  ViewObj( <obj> )  . . . . . . . . . . . . . . . . . . . .  view an object
##
##  <ManSection>
##  <Oper Name="ViewObj" Arg='obj'/>
##
##  <Description>
##  <Ref Oper="ViewObj"/> prints information about the object <A>obj</A>.
##  This information is thought to be short and human readable,
##  in particular <E>not</E> necessarily detailed enough for defining <A>obj</A>,
##  an in general <E>not</E> &GAP; readable.
##  <P/>
##  More detailed information can be obtained by <Ref Func="PrintObj"/>
##  </Description>
##  </ManSection>
##
DeclareOperationKernel( "ViewObj", [ IS_OBJECT ], VIEW_OBJ );


#############################################################################
##
#O  ViewString( <obj> )  . . . . . . . . . . . . . . . . . . . view an object
##
##  <#GAPDoc Label="ViewString">
##  <ManSection>
##  <Oper Name="ViewString" Arg='obj'/>
##
##  <Description>
##  <Ref Oper="ViewString"/> returns a string which would be displayed
##  by <Ref Oper="ViewObj"/> for an
##  object. Note that no method for <Ref Oper="ViewString"/> may
##  delegate to any of
##  the operations <Ref Oper="Display"/>, <Ref Oper="ViewObj"/>,
##  <Ref Oper="DisplayString"/> or <Ref Oper="PrintObj"/> to avoid
##  circular delegations.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
DeclareOperation( "ViewString", [ IS_OBJECT ]);


#############################################################################
##
#F  View( <obj1>, <obj2>... ) . . . . . . . . . . . . . . . . .  view objects
##
##  <#GAPDoc Label="View">
##  <ManSection>
##  <Func Name="View" Arg='obj1, obj2...'/>
##
##  <Description>
##  <Ref Func="View"/> shows the objects <A>obj1</A>, <A>obj2</A>... etc.
##  <E>in a short form</E> on the standard output by calling the
##  <Ref Oper="ViewObj"/> operation on each of them.
##  <Ref Func="View"/> is called in the read-eval-print loop,
##  thus the output looks exactly like the representation of the
##  objects shown by the main loop.
##  Note that no space or newline is printed between the objects.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "View", function( arg )
    local   obj;

    for obj  in arg  do
        ViewObj(obj);
    od;
end );


#############################################################################
##
#F  TraceMethods( <oprs> )
##
##  <#GAPDoc Label="TraceMethods">
##  <ManSection>
##  <Func Name="TraceMethods" Arg='opr1, opr2, ...' Label ="for operations"/>
##  <Func Name="TraceMethods" Arg='oprs' Label ="for a list of operations"/>
##
##  <Description>
##  After the call of <C>TraceMethods</C>,  whenever a method of one of
##  the operations <A>opr1</A>, <A>opr2</A>, ... is called, the
##  information string used in the installation of the method is printed.
##  The second form has the same effect for each operation from the list
##  <A>oprs</A> of operations.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "TraceMethods", function( arg )
    local   fun;
    if LEN_LIST( arg ) = 0 then
      Error("`TraceMethods' require at least one argument");
    fi;
    if IS_LIST(arg[1])  then
        arg := arg[1];
    fi;
    for fun  in arg  do
        TRACE_METHODS(fun);
    od;

end );

#############################################################################
##
#F  TraceAllMethods( )
##
##  <#GAPDoc Label="TraceAllMethods">
##  <ManSection>
##  <Func Name="TraceAllMethods" Arg=""/>
##
##  <Description>
##  Invokes <C>TraceMethods</C> for all operations.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "TraceAllMethods", function( arg )
    TraceMethods(OPERATIONS);
end );


#############################################################################
##
#F  UntraceMethods( <oprs>)
##
##  <#GAPDoc Label="UntraceMethods">
##  <ManSection>
##  <Func Name="UntraceMethods" Arg='opr1, opr2, ...' Label ="for operations"/>
##  <Func Name="UntraceMethods" Arg='oprs' Label ="for a list of operations"/>
##
##  <Description>
##  turns the tracing off for all operations <A>opr1</A>, <A>opr2</A>, ... or
##  in the second form, for all operations in the list <A>oprs</A>.
##  <Log><![CDATA[
##  gap> TraceMethods( [ Size ] );
##  gap> g:= Group( (1,2,3), (1,2) );;
##  gap> Size( g );
##  #I  Size: for a permutation group at /gap5/lib/grpperm.gi:487
##  #I  Setter(Size): system setter
##  #I  Size: system getter
##  #I  Size: system getter
##  6
##  gap> UntraceMethods( [ Size ] );
##  ]]></Log>
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "UntraceMethods", function( arg )
    local   fun;
    if LEN_LIST( arg ) = 0 then
      Error("`UntraceMethods' require at least one argument");
    fi;
    if IS_LIST(arg[1])  then
        arg := arg[1];
    fi;
    for fun  in arg  do
        UNTRACE_METHODS(fun);
    od;

end );


#############################################################################
##
#F  UntraceAllMethods( <oprs>)
##
##  <#GAPDoc Label="UntraceAllMethods">
##  <ManSection>
##  <Func Name="UntraceAllMethods" Arg=""/>
##
##  <Description>
##  Equivalent to calling <C>UntraceMethods</C> for all operations.
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BIND_GLOBAL( "UntraceAllMethods", function( arg )
    UntraceMethods(OPERATIONS);
end );

#############################################################################
##
#F  DeclareGlobalFunction( <name> ) . . . . . .  create a new global function
#F  InstallGlobalFunction( <oper>, <func> )
##
##  <#GAPDoc Label="DeclareGlobalFunction">
##  <ManSection>
##  <Func Name="DeclareGlobalFunction" Arg='name'/>
##  <Func Name="InstallGlobalFunction" Arg='oper, func'/>
##
##  <Description>
##  &GAP; functions that are not operations and that are intended to be
##  called by users should be notified to &GAP;
##  via <Ref Func="DeclareGlobalFunction"/>.
##  <Ref Func="DeclareGlobalFunction"/>
##  returns a function that serves as a placeholder for the function that will
##  be installed later.
##  The placeholder will print an error message if it is called.
##  See also <Ref Func="DeclareSynonym"/>.
##  <P/>
##
##  In the past the main application of this was to allow access to variables
##  before they were assigned. Starting with &GAP; 4.12 we recommend to use
##  <Ref Func="DeclareGlobalName"/>/<Ref Func="BindGlobal"/> instead of
##  <Ref Func="DeclareGlobalVariable"/>/<Ref Func="InstallGlobalFunction"/>
##  whenever possible.
##  <P/>
##
##  If used at all, then
##  <Ref Func="DeclareGlobalVariable"/> shall be used in the declaration part
##  of the respective package
##  (see <Ref Sect="Declaration and Implementation Part"/>).
##  <P/>
##
##  A global function declared with <Ref Func="DeclareGlobalFunction"/>
##  can be given its value <A>func</A> via
##  <Ref Func="InstallGlobalFunction"/>;
##  <A>gvar</A> is the global variable (or a string denoting its name)
##  named with the <A>name</A> argument of the call to
##  <Ref Func="DeclareGlobalFunction"/>.
##  For example, a declaration like
##  <P/>
##  <Log><![CDATA[
##  DeclareGlobalFunction( "SumOfTwoCubes" );
##  ]]></Log>
##  <P/>
##  in the <Q>declaration part</Q>
##  (see Section <Ref Sect="Declaration and Implementation Part"/>)
##  might have a corresponding <Q>implementation part</Q> of:
##  <P/>
##  <Log><![CDATA[
##  InstallGlobalFunction( SumOfTwoCubes, function(x, y) return x^3 + y^3; end);
##  ]]></Log>
##  <P/>
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
##  Global functions of the &GAP; library must be distinguished from other
##  global variables (see <C>variable.g</C>) because of the completion
##  mechanism.
##
if IsHPCGAP then
    BIND_GLOBAL( "GLOBAL_FUNCTION_NAMES", ShareSpecialObj([], "GLOBAL_FUNCTION_NAMES") );
else
    BIND_GLOBAL( "GLOBAL_FUNCTION_NAMES", [] );
fi;

BIND_GLOBAL( "DeclareGlobalFunction", function( arg )
    local   name;

    name := arg[1];
    if LEN_LIST(arg) > 1 then
        INFO_DEBUG(1, "DeclareGlobalFunction: too many arguments in ",
            INPUT_FILENAME(), ":", STRING_INT(INPUT_LINENUMBER()));
    fi;
    atomic GLOBAL_FUNCTION_NAMES do
    ADD_SET( GLOBAL_FUNCTION_NAMES, IMMUTABLE_COPY_OBJ(name) );
    od;
    BIND_GLOBAL( name, NEW_GLOBAL_FUNCTION( name ) );
end );

BIND_GLOBAL( "InstallGlobalFunction", function( arg )
    local   oper,  func;

    if LEN_LIST(arg) > 2  then
        INFO_DEBUG(1, "InstallGlobalFunction: too many arguments in ",
            INPUT_FILENAME(), ":", STRING_INT(INPUT_LINENUMBER()));
    fi;
    if LEN_LIST(arg) = 3  then
        oper := arg[1];
        func := arg[3];
    else
        oper := arg[1];
--> --------------------

--> maximum size reached

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

[ Dauer der Verarbeitung: 0.53 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