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


SSL opers.cc   Sprache: C

 
/****************************************************************************
**
**  This file is part of GAP, a system for computational discrete algebra.
**
**  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 contains the functions of the  filters, operations, attributes,
**  and properties package.
*/


extern "C" {

#include "opers.h"

#include "ariths.h"
#include "bits_intern.h"
#include "blister.h"
#include "bool.h"
#include "calls.h"
#include "code.h"
#include "error.h"
#include "gapstate.h"
#ifdef USE_GASMAN
#include "gasman_intern.h"
#endif
#include "gvars.h"
#include "io.h"
#include "lists.h"
#include "modules.h"
#include "plist.h"
#include "precord.h"
#include "range.h"
#include "records.h"
#include "saveload.h"
#include "stringobj.h"
#include "sysfiles.h"
#include "sysstr.h"

#ifdef HPCGAP
#include "hpc/aobjects.h"
#include "hpc/guards.h"
#include "hpc/thread.h"
#include <pthread.h>
#endif

#include "config.h"

// extern "C"

#ifdef GAP_KERNEL_DEBUG
#define COUNT_OPERS
#endif

#ifndef __has_cpp_attribute         // For backwards compatibility
#define __has_cpp_attribute(x) 0
#endif

#if __has_cpp_attribute(fallthrough)
#define FALLTHROUGH [[fallthrough]]
#elif defined(HAVE_FUNC_ATTRIBUTE_FALLTHROUGH)
#define FALLTHROUGH __attribute__((fallthrough))
#else
#define FALLTHROUGH do {} while(0)
#endif


/****************************************************************************
**
*V  TRY_NEXT_METHOD . . . . . . . . . . . . . . . . .  'TRY_NEXT_METHOD' flag
*/

Obj TRY_NEXT_METHOD;


#define CACHE_SIZE 5


static Obj StringFilterSetter;
static Obj ArglistObjVal;
static Obj ArglistObj;


static Obj SetterAndFilter(Obj getter);
static Obj TesterAndFilter(Obj getter);


/****************************************************************************
**
*F * * * * * * * * * * * * internal flags functions * * * * * * * * * * * * *
*/


#define RequireFlags(funcname, op)                                           \
    RequireArgumentCondition(funcname, op, TNUM_OBJ(op) == T_FLAGS,          \
                             "must be a flags list")

#define RequireFilter(funcname, op, argname)                                 \
    RequireArgumentConditionEx(funcname, op, argname, IS_FILTER(op),         \
                               "must be a filter")

#define RequireOperation(op)                                                 \
    RequireArgumentCondition(SELF_NAME, op, IS_OPERATION(op),                \
                             "must be an operation")


/****************************************************************************
**
*F  PrintFlags( <flags> ) . . . . . . . . . . . . . . . .  print a flags list
*/

static void PrintFlags(Obj flags)
{
    Pr("", 0, 0);
}


/****************************************************************************
**
*F  TypeFlags( <flags> )  . . . . . . . . . . . . . . .  type of a flags list
*/

static Obj TYPE_FLAGS;

static Obj TypeFlags(Obj flags)
{
    return TYPE_FLAGS;
}


/****************************************************************************
**
*F  SaveFlags( <flags> )  . . . . . . . . . . . . . . . . . save a flags list
**
*/

#ifdef GAP_ENABLE_SAVELOAD
static void SaveFlags(Obj flags)
{
    UInt        i, len, *ptr;

    SaveSubObj(TRUES_FLAGS(flags));
    SaveSubObj(HASH_FLAGS(flags));
    SaveSubObj(AND_CACHE_FLAGS(flags));

    len = NRB_FLAGS(flags);
    ptr = BLOCKS_FLAGS(flags);
    for ( i = 1;  i <= len;  i++ )
        SaveUInt(*ptr++);
}
#endif


/****************************************************************************
**
*F  LoadFlags( <flags> )  . . . . . . . . . . . . . . . . . load a flags list
**
*/

#ifdef GAP_ENABLE_SAVELOAD
static void LoadFlags(Obj flags)
{
    Obj         sub;
    UInt        i, len, *ptr;

    sub = LoadSubObj();  SET_TRUES_FLAGS( flags, sub );
    sub = LoadSubObj();  SET_HASH_FLAGS( flags, sub );
    sub = LoadSubObj();  SET_AND_CACHE_FLAGS( flags, sub );

    len = NRB_FLAGS(flags);
    ptr = BLOCKS_FLAGS(flags);
    for ( i = 1;  i <= len;  i++ )
        *ptr++ = LoadUInt();
}
#endif


/****************************************************************************
**
*F * * * * * * * * * * * * *  GAP flags functions * * * * * * * * * * * * * *
*/



/****************************************************************************
**
*F  FuncHASH_FLAGS( <self>, <flags> ) . . . . . .  hash value of a flags list
**
**  The hash value is independent of the size of a machine word (32 or 64).
**
**  The rather peculiar cast in the definition of HASH_FLAGS_SIZE is needed
**  to get the calculation to work right on the alpha.
**
*T  The 64 bit version depends on the byte order -- it assumes that
**  the lower addressed half-word is the less significant
**
*/

#define HASH_FLAGS_SIZE (Int4)67108879L

static Obj FuncHASH_FLAGS(Obj self, Obj flags)
{
    Int4                 hash;
    Int4                 x;
    Int                  len;
    UInt4 *              ptr;
    Int                  i;

    // do some trivial checks
    RequireFlags(SELF_NAME, flags);
    if ( HASH_FLAGS(flags) != 0 ) {
        return HASH_FLAGS(flags);
    }

    // do the real work */
#if !defined(SYS_IS_64_BIT) || !defined(WORDS_BIGENDIAN)

    // 32 bit case  -- this is the "defining" case, others are adjusted to
    // comply with this. For 64 bit systems in little endian mode, this
    // amounts to the same code, only the value of NRB_FLAGS has to be
    // adjusted
    len = NRB_FLAGS(flags) * (sizeof(UInt) / sizeof(UInt4));
    ptr = (UInt4 *)BLOCKS_FLAGS(flags);
    hash = 0;
    x    = 1;
    for ( i = len; i >= 1; i-- ) {
        hash = (hash + (*ptr % HASH_FLAGS_SIZE) * x) % HASH_FLAGS_SIZE;
        x    = (31 * x) % HASH_FLAGS_SIZE;
        ptr++;
    }

#else

    // This is the hardest case: 64 bit big endian
    len = NRB_FLAGS(flags);
    ptr = (UInt4 *)BLOCKS_FLAGS(flags);
    hash = 0;
    x    = 1;
    for ( i = len; i >= 1; i-- ) {

        // least significant 32 bits first
        hash = (hash + (ptr[1] % HASH_FLAGS_SIZE) * x) % HASH_FLAGS_SIZE;
        x    = (31 * x) % HASH_FLAGS_SIZE;
        // now the more significant
        hash = (hash + (*ptr % HASH_FLAGS_SIZE) * x) % HASH_FLAGS_SIZE;
        x    = (31 * x) % HASH_FLAGS_SIZE;

        ptr+= 2;
    }
#endif
    SET_HASH_FLAGS( flags, INTOBJ_INT((UInt)hash+1) );
    return HASH_FLAGS(flags);
}


/****************************************************************************
**
*F  FuncTRUES_FLAGS( <self>, <flags> )  . . .  true positions of a flags list
**
**  see 'FuncPositionsTruesBlist' in "blister.c" for information.
*/

static Obj FuncTRUES_FLAGS(Obj self, Obj flags)
{
    Obj                 sub;            // handle of the result
    Int                 len;            // logical length of the list
    UInt *              ptr;            // pointer to flags
    UInt                nrb;            // number of blocks in flags
    UInt                n;              // number of bits in flags
    UInt                nn;
    UInt                i;              // loop variable

    RequireFlags(SELF_NAME, flags);
    if ( TRUES_FLAGS(flags) != 0 ) {
        return TRUES_FLAGS(flags);
    }

    // compute the number of 'true'-s just as in 'FuncSizeBlist'
    nrb = NRB_FLAGS(flags);
    ptr = (UInt*)BLOCKS_FLAGS(flags);
    n = COUNT_TRUES_BLOCKS(ptr, nrb);

    // make the sublist (we now know its size exactly)
    sub = NEW_PLIST_IMM( T_PLIST, n );
    SET_LEN_PLIST( sub, n );

    // loop over the boolean list and stuff elements into <sub>
    len = LEN_FLAGS( flags );
    nn  = 1;
    for ( i = 1; nn <= n && i <= len;  i++ ) {
        if ( C_ELM_FLAGS( flags, i ) ) {
            SET_ELM_PLIST( sub, nn, INTOBJ_INT(i) );
            nn++;
        }
    }
    CHANGED_BAG(sub);

    // return the sublist
    SET_TRUES_FLAGS( flags, sub );
    CHANGED_BAG(flags);
    return sub;
}


/****************************************************************************
**
*F  FuncSIZE_FLAGS( <self>, <flags> ) . . . . number of trues of a flags list
**
**  see 'FuncSIZE_FLAGS'
*/

static Obj FuncSIZE_FLAGS(Obj self, Obj flags)
{
    UInt *              ptr;            // pointer to flags
    UInt                nrb;            // number of blocks in flags
    UInt                n;              // number of bits in flags

    RequireFlags(SELF_NAME, flags);
    if ( TRUES_FLAGS(flags) != 0 ) {
        return INTOBJ_INT( LEN_PLIST( TRUES_FLAGS(flags) ) );
    }

    // get the number of blocks and a pointer
    nrb = NRB_FLAGS(flags);
    ptr = BLOCKS_FLAGS(flags);

    n = COUNT_TRUES_BLOCKS(ptr, nrb);

    // return the number of bits
    return INTOBJ_INT( n );
}


/****************************************************************************
**
*F  EqFlags( <flags1>, <flags2> ) . . . . . . . . . . equality of flags lists
*/

static Int EqFlags(Obj flags1, Obj flags2)
{
    Int                 len1;
    Int                 len2;
    UInt  *             ptr1;
    UInt  *             ptr2;
    Int                 i;

    if ( flags1 == flags2 ) {
        return 1;
    }

    // do the real work
    len1 = NRB_FLAGS(flags1);
    len2 = NRB_FLAGS(flags2);
    ptr1 = BLOCKS_FLAGS(flags1);
    ptr2 = BLOCKS_FLAGS(flags2);
    if ( len1 <= len2 ) {
        for ( i = 1; i <= len1; i++ ) {
            if ( *ptr1 != *ptr2 )
                return 0;
            ptr1++;  ptr2++;
        }
        for ( ; i <= len2; i++ ) {
            if ( 0 != *ptr2 )
                return 0;
            ptr2++;
        }
    }
    else {
        for ( i = 1; i <= len2; i++ ) {
            if ( *ptr1 != *ptr2 )
                return 0;
            ptr1++;  ptr2++;
        }
        for ( ; i <= len1; i++ ) {
            if ( *ptr1 != 0 )
                return 0;
            ptr1++;
        }
    }
    return 1;
}


/****************************************************************************
**
*F  FuncIS_EQUAL_FLAGS( <self>, <flags1>, <flags2> )  equality of flags lists
*/

static Obj FuncIS_EQUAL_FLAGS(Obj self, Obj flags1, Obj flags2)
{
    // do some trivial checks
    RequireFlags(SELF_NAME, flags1);
    RequireFlags(SELF_NAME, flags2);

    return EqFlags(flags1, flags2) ? True : False;
}


#ifdef COUNT_OPERS
static Int IsSubsetFlagsCalls;
#endif

/****************************************************************************
**
*F  IS_SUBSET_FLAGS( <flags1>, <flags2> ) . subset test with no safety check
*/

BOOL IS_SUBSET_FLAGS(Obj flags1, Obj flags2)
{
    Int    len1;
    Int    len2;
    UInt * ptr1;
    UInt * ptr2;
    Int    i;

#ifdef COUNT_OPERS
    IsSubsetFlagsCalls++;
#endif

    // compare the bit lists
    len1 = NRB_FLAGS(flags1);
    len2 = NRB_FLAGS(flags2);
    ptr1 = BLOCKS_FLAGS(flags1);
    ptr2 = BLOCKS_FLAGS(flags2);
    if (len1 < len2) {
        for (i = len2 - 1; i >= len1; i--) {
            if (ptr2[i] != 0)
                return 0;
        }
        for (i = len1 - 1; i >= 0; i--) {
            UInt x = ptr2[i];
            if ((x & ptr1[i]) != x)
                return 0;
        }
    }
    else {
        for (i = len2 - 1; i >= 0; i--) {
            UInt x = ptr2[i];
            if ((x & ptr1[i]) != x)
                return 0;
        }
    }
    return 1;
}

/****************************************************************************
**
*F  FuncIS_SUBSET_FLAGS( <self>, <flags1>, <flags2> ) . . . . . . subset test
*/

static Obj FuncIS_SUBSET_FLAGS(Obj self, Obj flags1, Obj flags2)
{
    // do some correctness checks
    RequireFlags(SELF_NAME, flags1);
    RequireFlags(SELF_NAME, flags2);

    return IS_SUBSET_FLAGS(flags1, flags2) ? True : False;
}

/****************************************************************************
**
*F  FuncSUB_FLAGS( <self>, <flags1>, <flags2> ) . . . . subtract a flags list
*/

static Obj FuncSUB_FLAGS(Obj self, Obj flags1, Obj flags2)
{
    Obj                 flags;
    Int                 len1;
    Int                 len2;
    Int                 size1;
    Int                 size2;
    UInt *              ptr;
    UInt *              ptr1;
    UInt *              ptr2;
    Int                 i;

    // do some trivial checks
    RequireFlags(SELF_NAME, flags1);
    RequireFlags(SELF_NAME, flags2);

    // do the real work
    len1   = LEN_FLAGS(flags1);
    size1  = NRB_FLAGS(flags1);
    len2   = LEN_FLAGS(flags2);
    size2  = NRB_FLAGS(flags2);
    if ( len1 < len2 ) {
        flags = NEW_FLAGS( len1 );
        ptr1 = BLOCKS_FLAGS(flags1);
        ptr2 = BLOCKS_FLAGS(flags2);
        ptr  = BLOCKS_FLAGS(flags);
        for ( i = 1; i <= size1; i++ )
            *ptr++ = *ptr1++ & ~ *ptr2++;
    }
    else {
        flags = NEW_FLAGS( len1 );
        ptr1 = BLOCKS_FLAGS(flags1);
        ptr2 = BLOCKS_FLAGS(flags2);
        ptr  = BLOCKS_FLAGS(flags);
        for ( i = 1; i <= size2; i++ )
            *ptr++ = *ptr1++ & ~ *ptr2++;
        for (      ; i <= size1; i++ )
            *ptr++ = *ptr1++;
    }

    return flags;
}


/****************************************************************************
**
*F  FuncAND_FLAGS( <self>, <flags1>, <flags2> ) . . . .  `and' of flags lists
*/

#define AND_FLAGS_HASH_SIZE             50

#ifdef COUNT_OPERS
static Int AndFlagsCacheHit;
static Int AndFlagsCacheMiss;
static Int AndFlagsCacheLost;
#endif

static Obj FuncAND_FLAGS(Obj self, Obj flags1, Obj flags2)
{
    Obj                 flags;
    Int                 len1;
    Int                 len2;
    Int                 size1;
    Int                 size2;
    UInt *              ptr;
    UInt *              ptr1;
    UInt *              ptr2;
    Int                 i;

#ifdef AND_FLAGS_HASH_SIZE
    Obj                 cache;
    Obj                 entry;
#ifdef HPCGAP
    Obj                 locked = 0;
#endif
    UInt                hash;
    UInt                hash2;
    static UInt         next = 0;   // FIXME HPC-GAP: is usage of this static thread-safe?
#endif

    // do some trivial checks
    RequireFlags(SELF_NAME, flags1);
    RequireFlags(SELF_NAME, flags2);

    if (flags1 == flags2)
        return flags1;
    if (LEN_FLAGS(flags2) == 0)
        return flags1;
    if (LEN_FLAGS(flags1) == 0)
        return flags2;

    // check the cache
#   ifdef AND_FLAGS_HASH_SIZE
        // We want to ensure if we calculate 'flags1 and flags2', then
        // later do 'flags2 and flags1', we will get the value from the cache.
        // Therefore we just compare the location of the Bag masterpointers
        // for both flags (which doesn't change), and use the cache of the
        // smaller. To this end, ensure flags1 is the smaller one.
        if ( flags1 > flags2 ) {
            SWAP(Obj, flags1, flags2);
        }

#       ifdef HPCGAP
            if (!PreThreadCreation) {
                locked = flags1;
                HashLock(locked);
            }
#       endif
        cache  = AND_CACHE_FLAGS(flags1);
        if ( cache == 0 ) {
            cache = NEW_PLIST( T_PLIST, 2*AND_FLAGS_HASH_SIZE );
#ifdef HPCGAP
            MakeBagPublic(cache);
#endif
            SET_AND_CACHE_FLAGS( flags1, cache );
            CHANGED_BAG(flags1);
        }
        hash = (UInt)flags2;
        entry = 0;
        for ( i = 0;  i < 24;  i++ ) {
            hash2 = (hash + 97*i) % AND_FLAGS_HASH_SIZE;
            entry = ELM_PLIST( cache, 2*hash2+1 );
            if ( entry == 0 ) {
                hash = hash2;
                break;
            }
            if ( entry == flags2 ) {
#               ifdef COUNT_OPERS
                    AndFlagsCacheHit++;
#               endif
#               if defined(HPCGAP) && defined(AND_FLAGS_HASH_SIZE)
                    if (locked)
                        HashUnlock(locked);
#               endif
                return ELM_PLIST( cache, 2*hash2+2 );
            }
        }
        if ( entry != 0 ) {
            next = (next+1) % 24;
            hash = (hash + 97*next) % AND_FLAGS_HASH_SIZE;
        }
#       ifdef COUNT_OPERS
            AndFlagsCacheMiss++;
#       endif
#   endif


    // do the real work
    len1   = LEN_FLAGS(flags1);
    size1  = NRB_FLAGS(flags1);
    len2   = LEN_FLAGS(flags2);
    size2  = NRB_FLAGS(flags2);

    if ( len1 < len2 ) {
        flags = NEW_FLAGS( len2 );
        ptr1 = BLOCKS_FLAGS(flags1);
        ptr2 = BLOCKS_FLAGS(flags2);
        ptr  = BLOCKS_FLAGS(flags);
        for ( i = 1; i <= size1; i++ )
            *ptr++ = *ptr1++ | *ptr2++;
        for (      ; i <= size2; i++ )
            *ptr++ =           *ptr2++;
    }
    else {
        flags = NEW_FLAGS( len1 );
        ptr1 = BLOCKS_FLAGS(flags1);
        ptr2 = BLOCKS_FLAGS(flags2);
        ptr  = BLOCKS_FLAGS(flags);
        for ( i = 1; i <= size2; i++ )
            *ptr++ = *ptr1++ | *ptr2++;
        for (      ; i <= size1; i++ )
            *ptr++ = *ptr1++;
    }

    // store result in the cache
#   ifdef AND_FLAGS_HASH_SIZE
#       ifdef COUNT_OPERS
            if ( ELM_PLIST(cache,2*hash+1) != 0 ) {
                    AndFlagsCacheLost++;
            }
#       endif
        SET_ELM_PLIST( cache, 2*hash+1, flags2 );
        SET_ELM_PLIST( cache, 2*hash+2, flags  );
        CHANGED_BAG(cache);
#       ifdef HPCGAP
            if (locked)
                HashUnlock(locked);
#       endif
#   endif

    return flags;
}

/****************************************************************************
**
*/

template <Int len>
static Obj LookupHashTable(Obj ht, Int hash, Obj flags)
{
    GAP_ASSERT(TNUM_OBJ(flags) == T_FLAGS);
    for (int hash_loop = 0; hash_loop < 3; ++hash_loop) {
        if (ELM_PLIST(ht, hash * 2 + 1) == flags) {
            return ELM_PLIST(ht, hash * 2 + 2);
        }
        hash = (hash * 311 + 61) % len;
    }
    return 0;
}

template <Int len>
static void StoreHashTable(Obj ht, Int hash, Obj new_with, Obj new_flags)
{
    Obj old_with, old_flags;
    GAP_ASSERT(TNUM_OBJ(new_flags) == T_FLAGS);

    // add to hash table, shuffling old values along (last one falls off)
    for (int hash_loop = 0; hash_loop < 3; ++hash_loop) {
        old_flags = ELM_PLIST(ht, hash * 2 + 1);
        old_with = ELM_PLIST(ht, hash * 2 + 2);

        SET_ELM_PLIST(ht, hash * 2 + 1, new_flags);
        SET_ELM_PLIST(ht, hash * 2 + 2, new_with);

        if (!old_flags)
            break;

        new_flags = old_flags;
        new_with = old_with;
        hash = (hash * 311 + 61) % len;
    }

    CHANGED_BAG(ht);
}


static Obj HIDDEN_IMPS;
static Obj WITH_HIDDEN_IMPS_FLAGS_CACHE;
enum { HIDDEN_IMPS_CACHE_LENGTH = 20003 };

// Forward declaration of FuncFLAGS_FILTER
static Obj FuncFLAGS_FILTER(Obj self, Obj oper);

/****************************************************************************
**
*F  FuncInstallHiddenTrueMethod( <filter>, <filters> ) Add a hidden true method
*/

static Obj FuncInstallHiddenTrueMethod(Obj self, Obj filter, Obj filters)
{
    Obj imp = FuncFLAGS_FILTER(0, filter);
    Obj imps = FuncFLAGS_FILTER(0, filters);
#ifdef HPCGAP
    RegionWriteLock(REGION(HIDDEN_IMPS));
#endif
    UInt len = LEN_PLIST(HIDDEN_IMPS);
    GROW_PLIST(HIDDEN_IMPS, len + 2);
    SET_LEN_PLIST(HIDDEN_IMPS, len + 2);
    SET_ELM_PLIST(HIDDEN_IMPS, len + 1, imp);
    SET_ELM_PLIST(HIDDEN_IMPS, len + 2, imps);
    CHANGED_BAG(HIDDEN_IMPS);
#ifdef HPCGAP
    RegionWriteUnlock(REGION(HIDDEN_IMPS));
#endif
    return 0;
}

/****************************************************************************
**
*F  FuncCLEAR_HIDDEN_IMP_CACHE( <self>, <flags> ) . . . .clear cache of flags
*/

static Obj FuncCLEAR_HIDDEN_IMP_CACHE(Obj self, Obj filter)
{
  Int i;
  Obj flags = FuncFLAGS_FILTER(0, filter);
#ifdef HPCGAP
  RegionWriteLock(REGION(WITH_HIDDEN_IMPS_FLAGS_CACHE));
#endif
  for(i = 1; i < HIDDEN_IMPS_CACHE_LENGTH * 2 - 1; i += 2)
  {
    if(ELM_PLIST(WITH_HIDDEN_IMPS_FLAGS_CACHE, i) &&
       IS_SUBSET_FLAGS(ELM_PLIST(WITH_HIDDEN_IMPS_FLAGS_CACHE, i+1), flags))
    {
        SET_ELM_PLIST(WITH_HIDDEN_IMPS_FLAGS_CACHE, i, 0);
        SET_ELM_PLIST(WITH_HIDDEN_IMPS_FLAGS_CACHE, i + 1, 0);
    }
  }
#ifdef HPCGAP
  RegionWriteUnlock(REGION(WITH_HIDDEN_IMPS_FLAGS_CACHE));
#endif
  return 0;
}

/****************************************************************************
**
*F  FuncWITH_HIDDEN_IMP_FLAGS( <self>, <flags> ) . . add hidden imps to flags
*/

#ifdef COUNT_OPERS
static Int WITH_HIDDEN_IMPS_MISS=0;
static Int WITH_HIDDEN_IMPS_HIT=0;
#endif
static Obj FuncWITH_HIDDEN_IMPS_FLAGS(Obj self, Obj flags)
{
    // do some trivial checks, so we can use IS_SUBSET_FLAGS
    RequireFlags(SELF_NAME, flags);

#ifdef HPCGAP
    RegionWriteLock(REGION(WITH_HIDDEN_IMPS_FLAGS_CACHE));
#endif
    Int changed, i, lastand, stop;
    Int hidden_imps_length = LEN_PLIST(HIDDEN_IMPS) / 2;
    Int hash =
        INT_INTOBJ(FuncHASH_FLAGS(0, flags)) % HIDDEN_IMPS_CACHE_LENGTH;
    Obj cacheval;
    Obj with = flags;

    cacheval = LookupHashTable<HIDDEN_IMPS_CACHE_LENGTH>(
        WITH_HIDDEN_IMPS_FLAGS_CACHE, hash, flags);
    if (cacheval) {
#ifdef HPCGAP
        RegionWriteUnlock(REGION(WITH_HIDDEN_IMPS_FLAGS_CACHE));
#endif
#ifdef COUNT_OPERS
        WITH_HIDDEN_IMPS_HIT++;
#endif
        return cacheval;
    }

#ifdef COUNT_OPERS
    WITH_HIDDEN_IMPS_MISS++;
#endif
    changed = 1;
    lastand = 0;
    while(changed)
    {
      changed = 0;
      for (i = hidden_imps_length, stop = lastand; i > stop; i--)
      {
        if( IS_SUBSET_FLAGS(with, ELM_PLIST(HIDDEN_IMPS, i*2)) &&
           !IS_SUBSET_FLAGS(with, ELM_PLIST(HIDDEN_IMPS, i*2-1)) )
        {
          with = FuncAND_FLAGS(0, with, ELM_PLIST(HIDDEN_IMPS, i*2-1));
          changed = 1;
          stop = 0;
          lastand = i;
        }
      }
    }

    StoreHashTable<HIDDEN_IMPS_CACHE_LENGTH>(WITH_HIDDEN_IMPS_FLAGS_CACHE,
                                             hash, with, flags);

#ifdef HPCGAP
    RegionWriteUnlock(REGION(WITH_HIDDEN_IMPS_FLAGS_CACHE));
#endif
    return with;
}


static Obj IMPLICATIONS_SIMPLE;
static Obj IMPLICATIONS_COMPOSED;
static Obj WITH_IMPS_FLAGS_CACHE;
enum { IMPS_CACHE_LENGTH = 21001 };

/****************************************************************************
**
*F  FuncCLEAR_IMP_CACHE( <self>, <flags> ) . . . . . . . clear cache of flags
*/

static Obj FuncCLEAR_IMP_CACHE(Obj self)
{
  Int i;
#ifdef HPCGAP
  RegionWriteLock(REGION(IMPLICATIONS_SIMPLE));
#endif
  for(i = 1; i < IMPS_CACHE_LENGTH * 2 - 1; i += 2)
  {
    SET_ELM_PLIST(WITH_IMPS_FLAGS_CACHE, i, 0);
    SET_ELM_PLIST(WITH_IMPS_FLAGS_CACHE, i + 1, 0);
  }
#ifdef HPCGAP
  RegionWriteUnlock(REGION(IMPLICATIONS_SIMPLE));
#endif
  return 0;
}

/****************************************************************************
**
*F  FuncWITH_IMPS_FLAGS( <self>, <flags> ) . . . . . . . . add imps to flags
*/

#ifdef COUNT_OPERS
static Int WITH_IMPS_FLAGS_MISS=0;
static Int WITH_IMPS_FLAGS_HIT=0;
#endif
static Obj FuncWITH_IMPS_FLAGS(Obj self, Obj flags)
{
    // do some trivial checks, so we can use IS_SUBSET_FLAGS
    RequireFlags(SELF_NAME, flags);

#ifdef HPCGAP
    RegionWriteLock(REGION(IMPLICATIONS_SIMPLE));
#endif
    Int changed, lastand, i, j, stop, imps_length;
    Int hash = INT_INTOBJ(FuncHASH_FLAGS(0, flags)) % IMPS_CACHE_LENGTH;
    Obj cacheval;
    Obj with = flags;
    Obj imp;
    Obj trues;

    cacheval = LookupHashTable<IMPS_CACHE_LENGTH>(WITH_IMPS_FLAGS_CACHE, hash,
                                                  flags);
    if (cacheval) {
#ifdef HPCGAP
        RegionWriteUnlock(REGION(IMPLICATIONS_SIMPLE));
#endif
#ifdef COUNT_OPERS
        WITH_IMPS_FLAGS_HIT++;
#endif
        return cacheval;
    }

#ifdef COUNT_OPERS
    WITH_IMPS_FLAGS_MISS++;
#endif
    // first implications from simple filters (need only be checked once)
    trues = FuncTRUES_FLAGS(0, flags);
    for (i=1; i<=LEN_PLIST(trues); i++) {
        j = INT_INTOBJ(ELM_PLIST(trues, i));
        if (j <= LEN_PLIST(IMPLICATIONS_SIMPLE)
            && ELM_PLIST(IMPLICATIONS_SIMPLE, j)) {
           imp = ELM_PLIST(IMPLICATIONS_SIMPLE, j);
           if( IS_SUBSET_FLAGS(with, ELM_PLIST(imp, 2)) &&
              !IS_SUBSET_FLAGS(with, ELM_PLIST(imp, 1)) )
           {
             with = FuncAND_FLAGS(0, with, ELM_PLIST(imp, 1));
           }
        }
    }

    // the other implications have to be considered in a loop
    imps_length = LEN_PLIST(IMPLICATIONS_COMPOSED);
    changed = 1;
    lastand = imps_length+1;
    while(changed)
    {
      changed = 0;
      for (i = 1, stop = lastand; i < stop; i++)
      {
        imp = ELM_PLIST(IMPLICATIONS_COMPOSED, i);
        if( IS_SUBSET_FLAGS(with, ELM_PLIST(imp, 2)) &&
           !IS_SUBSET_FLAGS(with, ELM_PLIST(imp, 1)) )
        {
          with = FuncAND_FLAGS(0, with, ELM_PLIST(imp, 1));
          changed = 1;
          stop = imps_length+1;
          lastand = i;
        }
      }
    }

    StoreHashTable<IMPS_CACHE_LENGTH>(WITH_IMPS_FLAGS_CACHE, hash, with,
                                      flags);

#ifdef HPCGAP
    RegionWriteUnlock(REGION(IMPLICATIONS_SIMPLE));
#endif
    return with;
}

static Obj FuncWITH_IMPS_FLAGS_STAT(Obj self)
{
    Obj res;
    res = NEW_PLIST(T_PLIST, 3);
    SET_LEN_PLIST(res, 3);
    SET_ELM_PLIST(res, 1, WITH_IMPS_FLAGS_CACHE);
#ifdef COUNT_OPERS
    SET_ELM_PLIST(res, 2, INTOBJ_INT(WITH_IMPS_FLAGS_HIT));
    SET_ELM_PLIST(res, 3, INTOBJ_INT(WITH_IMPS_FLAGS_MISS));
#else
    SET_ELM_PLIST(res, 2, Fail);
    SET_ELM_PLIST(res, 3, Fail);
#endif
    return res;
}

/****************************************************************************
**
*F * * * * * * * * * * *  internal filter functions * * * * * * * * * * * * *
*/



/****************************************************************************
**
*V  CountFlags  . . . . . . . . . . . . . . . . . . . . next free flag number
*/

static Int CountFlags;


/****************************************************************************
**
*F  SetterFilter( <oper> )  . . . . . . . . . . . . . . .  setter of a filter
*/

static Obj SetterFilter(Obj oper)
{
    Obj                 setter;

    setter = SETTR_FILT( oper );
    if ( setter == INTOBJ_INT(0xBADBABE) )
        setter = SetterAndFilter( oper );
    return setter;
}


/****************************************************************************
**
*F  SetterAndFilter( <getter> )  . . . . . .  setter of a concatenated filter
*/

static Obj DoSetAndFilter(Obj self, Obj obj, Obj val)
{
    Obj                 op;

    if (val != True)
        ErrorMayQuit("You cannot set an \"and-filter\" except to true", 0, 0);

    // call the first 'and'-ed function
    op = FLAG1_FILT( self );
    CALL_2ARGS( op, obj, val );

    // call the second 'and'-ed function
    op = FLAG2_FILT( self );
    CALL_2ARGS( op, obj, val );

    // return 'void'
    return 0;
}


static Obj SetterAndFilter(Obj getter)
{
    Obj                 setter;
    Obj                 obj;
    if ( SETTR_FILT( getter ) == INTOBJ_INT(0xBADBABE) ) {
        setter = NewFunctionT( T_FUNCTION, sizeof(OperBag),
                                MakeImmString("<>"), 2, ArglistObjVal,
                                (ObjFunc)DoSetAndFilter );
        // assign via 'obj' to avoid GC issues
        obj =  SetterFilter( FLAG1_FILT(getter) );
        SET_FLAG1_FILT(setter, obj);
        obj = SetterFilter( FLAG2_FILT(getter) );
        SET_FLAG2_FILT(setter, obj);
        SET_SETTR_FILT(getter, setter);
        CHANGED_BAG(getter);
    }

    return SETTR_FILT(getter);
}


/****************************************************************************
**
*F  TesterFilter( <oper> )  . . . . . . . . . . . . . . .  tester of a filter
*/

static Obj TesterFilter(Obj oper)
{
    Obj                 tester;

    tester = TESTR_FILT( oper );
    if ( tester == INTOBJ_INT(0xBADBABE) )
        tester = TesterAndFilter( oper );
    return tester;
}


/****************************************************************************
**
*F  TestAndFilter( <getter> )  . . . . . . . .tester of a concatenated filter
*/

static Obj TesterAndFilter(Obj getter)
{
    Obj                 tester;

    if ( TESTR_FILT( getter ) == INTOBJ_INT(0xBADBABE) ) {
        tester = NewAndFilter( TesterFilter( FLAG1_FILT(getter) ),
                               TesterFilter( FLAG2_FILT(getter) ) );
        SET_TESTR_FILT(getter, tester);
        CHANGED_BAG(getter);

    }
    return TESTR_FILT(getter);
}


/****************************************************************************
**
*F  NewFilter( <name>, <nams>, <hdlr> ) . . . . . . . . . . make a new filter
*/

static Obj DoSetFilter(Obj self, Obj obj, Obj val)
{
    Int                 flag1;
    Obj                 type;
    Obj                 flags;

    // get the flag for the getter
    flag1 = INT_INTOBJ( FLAG1_FILT( self ) );

    // get the type of the object and its flags
    type  = TYPE_OBJ( obj );
    flags = FLAGS_TYPE( type );

    // return the value of the feature
    if ( val != SAFE_ELM_FLAGS( flags, flag1 ) ) {
        ErrorMayQuit("filter is already set the other way", 0, 0);
    }

    // return 'void'
    return 0;
}

static Obj NewSetterFilter(Obj getter)
{
    Obj                 setter;

    setter = NewOperation( StringFilterSetter, 2, ArglistObjVal,
                           (ObjFunc)DoSetFilter );
    SET_FLAG1_FILT(setter, FLAG1_FILT(getter));
    SET_FLAG2_FILT(setter, INTOBJ_INT(0));
    CHANGED_BAG(setter);

    return setter;
}


Obj DoFilter (
    Obj                 self,
    Obj                 obj )
{
    Obj                 val;
    Int                 flag1;
    Obj                 type;
    Obj                 flags;

    // get the flag for the getter
    flag1 = INT_INTOBJ( FLAG1_FILT( self ) );

    // get the type of the object and its flags
    type  = TYPE_OBJ( obj );
    flags = FLAGS_TYPE( type );

    // return the value of the feature
    val = SAFE_ELM_FLAGS( flags, flag1 );

    // return the value
    return val;
}


Obj NewFilter (
    Obj                 name,
    Obj                 nams,
    ObjFunc_1ARGS       hdlr )
{
    Obj                 getter;
    Obj                 setter;
    Int                 flag1;
    Obj                 flags;

    flag1 = ++CountFlags;

    GAP_ASSERT(hdlr);
    getter = NewOperation(name, 1, nams, (ObjFunc)hdlr);
    SET_FLAG1_FILT(getter, INTOBJ_INT(flag1));
    SET_FLAG2_FILT(getter, INTOBJ_INT(0));
    flags = NEW_FLAGS( flag1 );
    SET_ELM_FLAGS( flags, flag1 );
    SET_FLAGS_FILT(getter, flags);
    SET_IS_FILTER(getter);
    CHANGED_BAG(getter);

    setter = NewSetterFilter( getter );
    SET_SETTR_FILT(getter, setter);
    SET_TESTR_FILT(getter, ReturnTrueFilter);
    CHANGED_BAG(getter);

    return getter;
}

static Obj FuncIS_FILTER(Obj self, Obj obj)
{
    return IS_FILTER(obj) ? True : False;
}


/****************************************************************************
**
*F  NewAndFilter( <filt1>, <filt2> ) . . . . . make a new concatenated filter
*/

static Obj DoAndFilter(Obj self, Obj obj)
{
    Obj                 val;
    Obj                 op;

    // call the first 'and'-ed function
    op = FLAG1_FILT( self );
    val = CALL_1ARGS( op, obj );
    if ( val != True )  return False;

    // call the second 'and'-ed function
    op = FLAG2_FILT( self );
    val = CALL_1ARGS( op, obj );
    if ( val != True )  return False;

    // return 'true'
    return True;
}

Obj NewAndFilter (
    Obj                 oper1,
    Obj                 oper2 )
{
    Obj                 getter;
    Obj                 flags;

    Int                 str_len;
    Obj                 str;

    RequireFilter(0, oper1, "");
    RequireFilter(0, oper2, "");

    if ( oper1 == ReturnTrueFilter )
        return oper2;

    if ( oper2 == ReturnTrueFilter )
        return oper1;

    if ( oper1 == oper2 )
        return oper1;

    str_len = GET_LEN_STRING(NAME_FUNC(oper1)) + GET_LEN_STRING(NAME_FUNC(oper2)) + 8;
    str = NEW_STRING(str_len);
    SET_LEN_STRING(str, 0);
    AppendCStr(str, "(", 1);
    AppendString(str, NAME_FUNC(oper1));
    AppendCStr(str, " and ", 5);
    AppendString(str, NAME_FUNC(oper2));
    AppendCStr(str, ")", 1);

    getter = NewFunctionT( T_FUNCTION, sizeof(OperBag), str, 1,
                           ArglistObj, (ObjFunc)DoAndFilter );
    SET_FLAG1_FILT(getter, oper1);
    SET_FLAG2_FILT(getter, oper2);
    flags = FuncAND_FLAGS( 0, FLAGS_FILT(oper1), FLAGS_FILT(oper2) );
    SET_FLAGS_FILT(getter, flags);
    SET_SETTR_FILT(getter, INTOBJ_INT(0xBADBABE));
    SET_TESTR_FILT(getter, INTOBJ_INT(0xBADBABE));
    SET_IS_FILTER(getter);
    CHANGED_BAG(getter);

    return getter;
}

static Obj FuncIS_AND_FILTER(Obj self, Obj filt)
{
  return (IS_FUNC(filt) && HDLR_FUNC(filt, 1) == (ObjFunc)DoAndFilter) ? True : False;
}


/****************************************************************************
**
*V  ReturnTrueFilter . . . . . . . . . . . . . . . . the return 'true' filter
*/

Obj ReturnTrueFilter;


/****************************************************************************
**
*F  NewReturnTrueFilter() . . . . . . . . . . create a new return true filter
*/

static Obj DoSetReturnTrueFilter(Obj self, Obj obj, Obj val)
{
    if ( val != True ) {
        ErrorMayQuit("you cannot set this flag to 'false'", 0, 0);
    }
    return 0;
}

static Obj SetterReturnTrueFilter(Obj getter)
{
    Obj                 setter;

    setter = NewFunctionT( T_FUNCTION, sizeof(OperBag),
        MakeImmString("<>"), 2, ArglistObjVal,
        (ObjFunc)DoSetReturnTrueFilter );
    SET_FLAG1_FILT(setter, INTOBJ_INT(0));
    SET_FLAG2_FILT(setter, INTOBJ_INT(0));
    CHANGED_BAG(setter);

    return setter;
}

static Obj DoReturnTrueFilter(Obj self, Obj obj)
{
    return True;
}

static Obj NewReturnTrueFilter(void)
{
    Obj                 getter;
    Obj                 setter;
    Obj                 flags;

    getter = NewFunctionT( T_FUNCTION, sizeof(OperBag),
        MakeImmString("ReturnTrueFilter"), 1, ArglistObj,
        (ObjFunc)DoReturnTrueFilter );
    SET_FLAG1_FILT(getter, INTOBJ_INT(0));
    SET_FLAG2_FILT(getter, INTOBJ_INT(0));
    flags = NEW_FLAGS( 0 );
    SET_FLAGS_FILT(getter, flags);
    SET_IS_FILTER(getter);
    CHANGED_BAG(getter);

    setter = SetterReturnTrueFilter( getter );
    SET_SETTR_FILT(getter, setter);
    CHANGED_BAG(getter);

    // the tester also returns true, so we can reuse the getter
    SET_TESTR_FILT(getter, getter);

    return getter;
}


/****************************************************************************
**
*F * * * * * * * * * * * * * GAP filter functions * * * * * * * * * * * * * *
*/



/****************************************************************************
**
*F  FuncNEW_FILTER( <self>, <name> )  . . . . . . . . . . . . .  new filter
*/

static Obj FuncNEW_FILTER(Obj self, Obj name)
{
    RequireStringRep(SELF_NAME, name);
    return NewFilter(name, 0, DoFilter);
}


/****************************************************************************
**
*F  FuncFLAG1_FILTER( <self>, <oper> )  . . . . . . . . . . . .  `FLAG1_FILT'
*/

static Obj FuncFLAG1_FILTER(Obj self, Obj oper)
{
    Obj                 flag1;

    RequireOperation(oper);
    flag1 = FLAG1_FILT( oper );
    if ( flag1 == 0 )
        flag1 = INTOBJ_INT(0);
    return flag1;
}


/****************************************************************************
**
*F  FuncFLAG2_FILTER( <self>, <oper> )  . . . . . . . . . . . .  `FLAG2_FILT'
*/

static Obj FuncFLAG2_FILTER(Obj self, Obj oper)
{
    Obj                 flag2;

    RequireOperation(oper);
    flag2 = FLAG2_FILT( oper );
    if ( flag2 == 0 )
        flag2 = INTOBJ_INT(0);
    return flag2;
}


/****************************************************************************
**
*F  FuncFLAGS_FILTER( <self>, <oper> )  . . . . . . . . . . . .  `FLAGS_FILT'
*/

static Obj FuncFLAGS_FILTER(Obj self, Obj oper)
{
    Obj                 flags;

    RequireOperation(oper);
    flags = FLAGS_FILT( oper );
    if ( flags == 0 )
        flags = False;
    return flags;
}


/****************************************************************************
**
*F  FuncSETTER_FILTER( <self>, <oper> ) . . . . . . . . .  setter of a filter
*/

static Obj FuncSETTER_FILTER(Obj self, Obj oper)
{
    Obj                 setter;

    RequireOperation(oper);
    setter = SetterFilter( oper );
    if ( setter == 0 )  setter = False;
    return setter;
}


/****************************************************************************
**
*F  FuncTESTER_FILTER( <self>, <oper> ) . . . . . . . . .  tester of a filter
*/

static Obj FuncTESTER_FILTER(Obj self, Obj oper)
{
    Obj                 tester;

    RequireOperation(oper);
    tester = TesterFilter( oper );
    if ( tester == 0 )  tester = False;
    return tester;
}


/****************************************************************************
**
*F * * * * * * * * * *  internal operation functions  * * * * * * * * * * * *
*/



/****************************************************************************
**
*F  HandleMethodNotFound( <oper>, <nargs>, <args>, <verbose>, <constructor>,
**                        <precedence> )
**
**  This enables the special error handling for Method Not Found Errors.
**  It assembles all the necessary information into a form where it can be
**  conveniently accessed from GAP.
**
*/


static UInt RNamOperation;
static UInt RNamArguments;
static UInt RNamIsVerbose;
static UInt RNamIsConstructor;
static UInt RNamPrecedence;
static Obj  HANDLE_METHOD_NOT_FOUND;
static Obj  CHECK_REPEATED_ATTRIBUTE_SET;

static void HandleMethodNotFound(
    Obj oper, Obj arglist, UInt verbose, UInt constructor, Int precedence)
{
  Obj r;
#ifdef HPCGAP
  Region *savedRegion = TLS(currentRegion);
  TLS(currentRegion) = TLS(threadRegion);
#endif

  r = NEW_PREC(5);
  if (RNamOperation == 0)
    {
      // we can't do this in initialization because opers
      // is initialized BEFORE records
      RNamIsConstructor = RNamName("isConstructor");
      RNamIsVerbose = RNamName("isVerbose");
      RNamOperation = RNamName("Operation");
      RNamArguments = RNamName("Arguments");
      RNamPrecedence = RNamName("Precedence");
    }
  AssPRec(r,RNamOperation,oper);
  AssPRec(r,RNamArguments,arglist);
  AssPRec(r,RNamIsVerbose,verbose ? True : False);
  AssPRec(r,RNamIsConstructor,constructor ? True : False);
  AssPRec(r,RNamPrecedence,INTOBJ_INT(precedence));
  SortPRecRNam(r);
  CALL_1ARGS(HANDLE_METHOD_NOT_FOUND, r);
#ifdef HPCGAP
  TLS(currentRegion) = savedRegion;
#endif
  ErrorQuit("panic, HANDLE_METHOD_NOT_FOUND should not return", 0, 0);
}

/****************************************************************************
**
*F  FuncCOMPACT_TYPE_IDS( <self> ) . . . garbage collect the type IDs
**
*/


#ifdef USE_GASMAN

static Obj FLUSH_ALL_METHOD_CACHES;

static Int NextTypeID;
static Obj IsType;

static void FixTypeIDs( Bag b ) {
  if ( (TNUM_OBJ( b )  == T_POSOBJ) &&
       (DoFilter(IsType, b ) == True ))
    {
      SET_ID_TYPE(b, INTOBJ_INT(NextTypeID));
      NextTypeID++;
    }
}

#endif

static Obj FuncCOMPACT_TYPE_IDS(Obj self)
{
#ifdef USE_GASMAN
  NextTypeID = INT_INTOBJ_MIN;
  CallbackForAllBags( FixTypeIDs );
  CALL_0ARGS(FLUSH_ALL_METHOD_CACHES);
  return INTOBJ_INT(NextTypeID);
#else
  // in general garbage collectors, we cannot iterate over
  // all bags ever allocated, so we can't implement this function;
  // however, with 64 bit versions of GAP, we also should never
  // run out of type ids, so this is of little concern
  ErrorQuit("panic, COMPACT_TYPE_IDS is not available", 0, 0);
#endif
}

/****************************************************************************
**
*F  DoOperation<N>Args( <oper>, ... ) . . . . . . . . . .  Operation Handlers
**
**  This section of the file provides handlers for operations. The main ones
**  are DoOperation0Args ... DoOperation6Args and the DoVerboseOperation
**  tracing variants. Then there are variants for constructors. In the
**  following section are handlers for attributes, properties and the
**  operations related to them.
**
**  This code has been refactored to reduce repetition. Its efficiency now
**  depends on the C++ compiler inlining template functions and
**  doing constant folding to effectively produce a specialised version of
**  the main function.
*/


// Helper function to quickly get the type of an object, avoiding
// indirection in the case of external objects with a stored type I.e.,
// the compiler can inline TYPE_COMOBJ etc., while it cannot inline
// TYPE_OBJ
static inline Obj TYPE_OBJ_FEO(Obj obj)
{
#ifdef HPCGAP
    // TODO: We need to be able to automatically derive this.
    ImpliedWriteGuard(obj);
#endif
    switch ( TNUM_OBJ( obj ) ) {
    case T_COMOBJ:
        return TYPE_COMOBJ(obj);
    case T_POSOBJ:
        return TYPE_POSOBJ(obj);
    case T_DATOBJ:
        return TYPE_DATOBJ(obj);
    default:
        return TYPE_OBJ(obj);
    }
}

// Method Cache -- we remember recently selected methods in a cache.
// The effectiveness of this cache is vital for GAP's performance


// The next few functions deal with finding and allocating if necessary the
// cache for a given operation and number of arguments, and some locking in
// HPC-GAP


#ifdef HPCGAP

static pthread_mutex_t CacheLock;
static UInt            CacheSize;

static void LockCache(void)
{
    if (!PreThreadCreation)
        pthread_mutex_lock(&CacheLock);
}

static void UnlockCache(void)
{
    if (!PreThreadCreation)
        pthread_mutex_unlock(&CacheLock);
}

#endif

static inline Obj CacheOper(Obj oper, UInt i)
{
    Obj  cache = CACHE_OPER(oper, i);
    UInt len;

#ifdef HPCGAP
    UInt cacheIndex;

    if (cache == 0) {
        /* This is a safe form of double-checked locking, because
         * the cache value is not a reference. */

        LockCache();
        cache = CACHE_OPER(oper, i);
        if (cache == 0) {
            CacheSize++;
            cacheIndex = CacheSize;
            SET_CACHE_OPER(oper, i, INTOBJ_INT(cacheIndex));
        }
        else
            cacheIndex = INT_INTOBJ(cache);
        UnlockCache();
    }
    else {
        cacheIndex = INT_INTOBJ(cache);
    }

    if (cacheIndex > STATE(MethodCacheSize)) {
        len = STATE(MethodCacheSize);
        while (cacheIndex > len)
            len *= 2;
        GROW_PLIST(STATE(MethodCache), len);
        SET_LEN_PLIST(STATE(MethodCache), len);
        STATE(MethodCacheItems) = ADDR_OBJ(STATE(MethodCache));
        STATE(MethodCacheSize) = len;
    }

    cache = ELM_PLIST(STATE(MethodCache), cacheIndex);
#endif

    if (cache == 0) {
        len = (i < 7 ? CACHE_SIZE * (i + 2) : CACHE_SIZE * (1 + 2));
#ifdef HPCGAP
        len++; // reserve one slot for pointer to methods list
#endif
        cache = NEW_PLIST(T_PLIST, len);
        SET_LEN_PLIST(cache, len);
#ifdef HPCGAP
        MakeBagPublic(cache);
        SET_ELM_PLIST(STATE(MethodCache), cacheIndex, cache);
        CHANGED_BAG(STATE(MethodCache));
#else
        SET_CACHE_OPER(oper, i, cache);
        CHANGED_BAG(oper);
#endif
    }

    return cache;
}

#ifdef COUNT_OPERS
static UInt CacheHitStatistics[CACHE_SIZE][CACHE_SIZE][7];
static UInt CacheMissStatistics[CACHE_SIZE + 1][7];
#endif


#ifndef WARD_ENABLED
// This function actually searches the cache. Normally it should be
// called with n a compile-time constant to allow the optimiser to tidy
// things up.
template <Int n>
static Obj GetMethodCached(Obj cacheBag, Int prec, Obj ids[])
{
    UInt  typematch;
    Obj * cache;
    Obj   method = 0;
    UInt  i;
    const UInt cacheEntrySize = n + 2;

    cache = BASE_PTR_PLIST(cacheBag);
#ifdef HPCGAP
    cache++; // skip over the pointer to the methods list
#endif

    // Up to CACHE_SIZE methods might be in the cache
    if (prec < CACHE_SIZE) {
        // first place to look and also the place we'll put the result:
        UInt target = cacheEntrySize * prec;
        for (i = target; i < cacheEntrySize * CACHE_SIZE;
             i += cacheEntrySize) {
            if (cache[i + 1] == INTOBJ_INT(prec)) {
                typematch = 1;
                // This loop runs over the arguments, should be compiled away
                for (int j = 0; j < n; j++) {
                    if (cache[i + j + 2] != ids[j]) {
                        typematch = 0;
                        break;
                    }
                }
                if (typematch) {
                    method = cache[i];
#ifdef COUNT_OPERS
                    CacheHitStatistics[prec][i / cacheEntrySize][n]++;
#endif
                    if (i > target) {

                        // We found the method, but it was further down the
                        // cache than we would like it to be, so move it up
                        Obj buf[cacheEntrySize];
                        memcpy(buf, cache + i,
                               sizeof(Obj) * cacheEntrySize);
                        SyMemmove(cache + target + cacheEntrySize,
                                cache + target,
                                sizeof(Obj) * (i - target));
                        memcpy(cache + target, buf,
                               sizeof(Obj) * cacheEntrySize);
                    }
                    break;
                }
            }
        }
    }
    return method;
}

// Add a method to the cache -- called when a method is selected that is not
// in the cache
static inline void
CacheMethod(Obj cacheBag, UInt n, Int prec, Obj * ids, Obj method)
{
    if (prec >= CACHE_SIZE)
        return;
    // We insert this method at position <prec> and move
    // the older methods down
    UInt  cacheEntrySize = n + 2;
    Obj * cache = BASE_PTR_PLIST(cacheBag) + prec * cacheEntrySize;
#ifdef HPCGAP
    cache++; // skip over the pointer to the methods list
#endif
    SyMemmove(cache + cacheEntrySize, cache,
            sizeof(Obj) * (CACHE_SIZE - prec - 1) * cacheEntrySize);
    cache[0] = method;
    cache[1] = INTOBJ_INT(prec);
    for (UInt i = 0; i < n; i++)
        cache[2 + i] = ids[i];
    CHANGED_BAG(cacheBag);
}
#endif // WARD_ENABLED

static Obj ReturnTrue;
static Obj VMETHOD_PRINT_INFO;
static Obj NEXT_VMETHOD_PRINT_INFO;

// This function searches through the methods of operation <oper> with
// arity <n>, looking for those matching the given <types>. Among these,
// the <prec>-th is selected (<prec> starts at 0).
//
// If <verbose> is non-zero, the matching method are printed by calling
// 'VMETHOD_PRINT_INFO' resp. 'NEXT_VMETHOD_PRINT_INFO'.
//
// If <constructor> is non-zero, then <oper> is a constructor, leading
// to <types[0]> being treated differently.
//
enum {
    BASE_SIZE_METHODS_OPER_ENTRY = 6,
};
template <UInt n>
static Obj GetMethodUncached(
    UInt verbose, UInt constructor, Obj methods, Int prec, Obj types[])
{
    if (methods == 0)
        return Fail;

    const UInt len = LEN_PLIST(methods);
    UInt       matchCount = 0;
    for (UInt pos = 0; pos < len; pos += n + BASE_SIZE_METHODS_OPER_ENTRY) {
        // each method comprises n + BASE_SIZE_METHODS_OPER_ENTRY
        // entries in the 'methods' list:
        // entry 1 is the family predicate;
        // entries 2 till n+1 are the n argument filters
        // entry n+2 is the actual method
        // entry n+3 is the rank
        // entry n+4 is the info text
        // entry n+5 is, if set, the location where the method was installed
        // entry n+6 is, if set, the relative rank that was supplied when
        //               the method was installed, either as a small integer
        //               or a function of no arguments

        // check argument filters against the given types
        Obj filter;
        int k = 1;
        if (constructor) {
            filter = ELM_PLIST(methods, pos + k + 1);
            GAP_ASSERT(TNUM_OBJ(filter) == T_FLAGS);
            if (!IS_SUBSET_FLAGS(filter, types[0]))
                continue;
            k++;
        }
        for (; k <= n; ++k) {
            filter = ELM_PLIST(methods, pos + k + 1);
            GAP_ASSERT(TNUM_OBJ(filter) == T_FLAGS);
            if (!IS_SUBSET_FLAGS(FLAGS_TYPE(types[k - 1]), filter))
                break;
        }

        // if some filter did not match, go to next method
        if (k <= n)
            continue;

        // check family predicate, with a hot path for the very
        // common trivial predicate 'ReturnTrue'
        Obj fampred = ELM_PLIST(methods, pos + 1);
        if (fampred != ReturnTrue) {
            Obj res = 0;
            switch (n) {
            case 0:
                res = CALL_0ARGS(fampred);
                break;
            case 1:
                res = CALL_1ARGS(fampred, FAMILY_TYPE(types[0]));
                break;
            case 2:
                res = CALL_2ARGS(fampred, FAMILY_TYPE(types[0]),
                                 FAMILY_TYPE(types[1]));
                break;
            case 3:
                res =
                    CALL_3ARGS(fampred, FAMILY_TYPE(types[0]),
                               FAMILY_TYPE(types[1]), FAMILY_TYPE(types[2]));
                break;
            case 4:
                res = CALL_4ARGS(fampred, FAMILY_TYPE(types[0]),
                                 FAMILY_TYPE(types[1]), FAMILY_TYPE(types[2]),
                                 FAMILY_TYPE(types[3]));
                break;
            case 5:
                res =
                    CALL_5ARGS(fampred, FAMILY_TYPE(types[0]),
                               FAMILY_TYPE(types[1]), FAMILY_TYPE(types[2]),
                               FAMILY_TYPE(types[3]), FAMILY_TYPE(types[4]));
                break;
            case 6:
                res = CALL_6ARGS(fampred, FAMILY_TYPE(types[0]),
                                 FAMILY_TYPE(types[1]), FAMILY_TYPE(types[2]),
                                 FAMILY_TYPE(types[3]), FAMILY_TYPE(types[4]),
                                 FAMILY_TYPE(types[5]));
                break;
            default:
                ErrorMayQuit("not supported yet", 0, 0);
            }

            if (res != True)
                continue;
        }

        // we have a match; is it the right one?
        if (prec == matchCount) {
            if (verbose) {
                CALL_3ARGS(prec == 0 ? VMETHOD_PRINT_INFO : NEXT_VMETHOD_PRINT_INFO, methods,
                           INTOBJ_INT(pos / (n + BASE_SIZE_METHODS_OPER_ENTRY) + 1),
                           INTOBJ_INT(n));

            }
            Obj meth = ELM_PLIST(methods, pos + n + 2);
            return meth;
        }
        matchCount++;
    }
    return Fail;
}

#ifdef COUNT_OPERS
static Int OperationHit;
static Int OperationMiss;
static Int OperationNext;
#endif

template <Int n>
static Obj
CallNArgs(Obj method, Obj a1, Obj a2, Obj a3, Obj a4, Obj a5, Obj a6)
{
    switch (n) {
    case 0:
        return CALL_0ARGS(method);
        break;
    case 1:
        return CALL_1ARGS(method, a1);
        break;
    case 2:
        return CALL_2ARGS(method, a1, a2);
        break;
    case 3:
        return CALL_3ARGS(method, a1, a2, a3);
        break;
    case 4:
        return CALL_4ARGS(method, a1, a2, a3, a4);
        break;
    case 5:
        return CALL_5ARGS(method, a1, a2, a3, a4, a5);
        break;
    case 6:
        return CALL_6ARGS(method, a1, a2, a3, a4, a5, a6);
        break;
    default:
        GAP_ASSERT(0);
    }
    return 0; // redundant, but silences a warning
}

template <Int n, BOOL verbose, BOOL constructor>
static Obj
DoOperationNArgs(Obj oper, Obj a1, Obj a2, Obj a3, Obj a4, Obj a5, Obj a6)
{
    // the following two lines look this way to avoid "allocating" a
    // zero-length array, which would result in undefined behavior (even
    // though we don't access the two arrays when n is zero). In addition, we
    // carefully avoid warnings in GCC due to -Wduplicated-branches.
    Obj types[n > 0 ? n : +1];
    Obj ids[n > 0 ? n : +1];
    Int prec;
    Obj method;
    Obj res;

    Obj earlyMethod = CONST_OPER(oper)->earlyMethod[n];
    if (earlyMethod) {
        res = CallNArgs<n>(earlyMethod, a1, a2, a3, a4, a5, a6);
        if (res != TRY_NEXT_METHOD)
            return res;
    }

    switch (n) {
    case 6:
        types[5] = TYPE_OBJ_FEO(a6);
        FALLTHROUGH;
    case 5:
        types[4] = TYPE_OBJ_FEO(a5);
        FALLTHROUGH;
    case 4:
        types[3] = TYPE_OBJ_FEO(a4);
        FALLTHROUGH;
    case 3:
        types[2] = TYPE_OBJ_FEO(a3);
        FALLTHROUGH;
    case 2:
        types[1] = TYPE_OBJ_FEO(a2);
        FALLTHROUGH;
    case 1:
        if (constructor) {
            RequireFilter("Constructor", a1, "the first argument");
            types[0] = FLAGS_FILT(a1);
        }
        else
            types[0] = TYPE_OBJ_FEO(a1);
    case 0:
        break;
    default:
        GAP_ASSERT(0);
    }

    if (n > 0) {
        if (constructor)
            ids[0] = types[0];
        else
            ids[0] = ID_TYPE(types[0]);
    }

    for (int i = 1; i < n; i++)
        ids[i] = ID_TYPE(types[i]);

    Obj cacheBag = CacheOper(oper, n);
    Obj methods = METHS_OPER(oper, n);

#ifdef HPCGAP
    // reset the method cache if necessary
    if (ELM_PLIST(cacheBag, 1) != methods) {
        Obj * cache = BASE_PTR_PLIST(cacheBag);
        cache[0] = methods;
        memset(cache + 1, 0, SIZE_OBJ(cacheBag)-2*sizeof(Obj));
    }
#endif

    // outer loop deals with TryNextMethod
    prec = -1;
    do {
        prec++;
        // Is there a method in the cache
        method = verbose ? 0 : GetMethodCached<n>(cacheBag, prec, ids);

#ifdef COUNT_OPERS
        if (method)
            OperationHit++;
        else {
            OperationMiss++;
            CacheMissStatistics[(prec >= CACHE_SIZE) ? CACHE_SIZE : prec]
                               [n]++;
        }
        if (prec > 0)
            OperationNext++;
#endif

        // otherwise try to find one in the list of methods
        if (!method) {
            method = GetMethodUncached<n>(verbose, constructor, methods, prec,
                                          types);
            // update the cache
            if (!verbose && method)
                CacheMethod(cacheBag, n, prec, ids, method);
        }

        // If there was no method found, then pass the information needed
        // for the error reporting. This function rarely returns
        if (method == Fail) {
            Obj arglist;
            switch (n) {
            case 0:
                arglist = NewEmptyPlist();
                break;
            case 1:
                arglist = NewPlistFromArgs(a1);
                break;
            case 2:
                arglist = NewPlistFromArgs(a1, a2);
                break;
            case 3:
                arglist = NewPlistFromArgs(a1, a2, a3);
                break;
            case 4:
                arglist = NewPlistFromArgs(a1, a2, a3, a4);
                break;
            case 5:
                arglist = NewPlistFromArgs(a1, a2, a3, a4, a5);
                break;
            case 6:
                arglist = NewPlistFromArgs(a1, a2, a3, a4, a5, a6);
                break;
            default:
                GAP_ASSERT(0);
            }
            HandleMethodNotFound(oper, arglist, verbose, constructor, prec);
        }

        if (!method) {
            ErrorQuit("no method returned", 0, 0);
        }

        // call this method
        res = CallNArgs<n>(method, a1, a2, a3, a4, a5, a6);
    } while (res == TRY_NEXT_METHOD);

    return res;
}


Obj DoOperation0Args(Obj oper)
{
    return DoOperationNArgs<0, FALSEFALSE>(oper, 0, 0, 0, 0, 0, 0);
}

Obj DoOperation1Args(Obj oper, Obj a1)
{
    return DoOperationNArgs<1, FALSEFALSE>(oper, a1, 0, 0, 0, 0, 0);
}

Obj DoOperation2Args(Obj oper, Obj a1, Obj a2)
{
    return DoOperationNArgs<2, FALSEFALSE>(oper, a1, a2, 0, 0, 0, 0);
}

Obj DoOperation3Args(Obj oper, Obj a1, Obj a2, Obj a3)
{
    return DoOperationNArgs<3, FALSEFALSE>(oper, a1, a2, a3, 0, 0, 0);
}

Obj DoOperation4Args(Obj oper, Obj a1, Obj a2, Obj a3, Obj a4)
{
    return DoOperationNArgs<4, FALSEFALSE>(oper, a1, a2, a3, a4, 0, 0);
}

Obj DoOperation5Args(Obj oper, Obj a1, Obj a2, Obj a3, Obj a4, Obj a5)
{
    return DoOperationNArgs<5, FALSEFALSE>(oper, a1, a2, a3, a4, a5, 0);
}

Obj DoOperation6Args(Obj oper, Obj a1, Obj a2, Obj a3, Obj a4, Obj a5, Obj a6)
{
    return DoOperationNArgs<6, FALSEFALSE>(oper, a1, a2, a3, a4, a5, a6);
}


/****************************************************************************
**
**  DoOperationXArgs( <oper>, ... )
*/


Obj DoOperationXArgs(Obj self, Obj args)
{
    ErrorQuit("sorry: cannot yet have X argument operations", 0, 0);
    return 0;
}


/****************************************************************************
**
**  DoVerboseOperation0Args( <oper> )
*/

Obj DoVerboseOperation0Args(Obj oper)
{
    return DoOperationNArgs<0, TRUEFALSE>(oper, 0, 0, 0, 0, 0, 0);
}

Obj DoVerboseOperation1Args(Obj oper, Obj a1)
{
    return DoOperationNArgs<1, TRUEFALSE>(oper, a1, 0, 0, 0, 0, 0);
}

Obj DoVerboseOperation2Args(Obj oper, Obj a1, Obj a2)
{
    return DoOperationNArgs<2, TRUEFALSE>(oper, a1, a2, 0, 0, 0, 0);
}

Obj DoVerboseOperation3Args(Obj oper, Obj a1, Obj a2, Obj a3)
{
    return DoOperationNArgs<3, TRUEFALSE>(oper, a1, a2, a3, 0, 0, 0);
}

Obj DoVerboseOperation4Args(Obj oper, Obj a1, Obj a2, Obj a3, Obj a4)
{
    return DoOperationNArgs<4, TRUEFALSE>(oper, a1, a2, a3, a4, 0, 0);
}

Obj DoVerboseOperation5Args(Obj oper, Obj a1, Obj a2, Obj a3, Obj a4, Obj a5)
{
    return DoOperationNArgs<5, TRUEFALSE>(oper, a1, a2, a3, a4, a5, 0);
}

Obj DoVerboseOperation6Args(
    Obj oper, Obj a1, Obj a2, Obj a3, Obj a4, Obj a5, Obj a6)
{
    return DoOperationNArgs<6, TRUEFALSE>(oper, a1, a2, a3, a4, a5, a6);
}


/****************************************************************************
**
**  DoVerboseOperationXArgs( <oper>, ... )
*/

Obj DoVerboseOperationXArgs(Obj self, Obj args)
{
    ErrorQuit("sorry: cannot yet have X argument operations", 0, 0);
    return 0;
}


/****************************************************************************
**
*F  NewOperation( <name>, <narg>, <nams>, <hdlr> )
*/

Obj NewOperation(Obj name, Int narg, Obj nams, ObjFunc hdlr)
{
    Obj oper;

    // create the function
    oper = NewFunctionT(T_FUNCTION, sizeof(OperBag), name, narg, nams, hdlr);

    // enter the handlers
    SET_HDLR_FUNC(oper, 0, (ObjFunc)DoOperation0Args);
    SET_HDLR_FUNC(oper, 1, (ObjFunc)DoOperation1Args);
    SET_HDLR_FUNC(oper, 2, (ObjFunc)DoOperation2Args);
    SET_HDLR_FUNC(oper, 3, (ObjFunc)DoOperation3Args);
    SET_HDLR_FUNC(oper, 4, (ObjFunc)DoOperation4Args);
    SET_HDLR_FUNC(oper, 5, (ObjFunc)DoOperation5Args);
    SET_HDLR_FUNC(oper, 6, (ObjFunc)DoOperation6Args);
    SET_HDLR_FUNC(oper, 7, (ObjFunc)DoOperationXArgs);

    // reenter the given handler
    if (narg != -1)
        SET_HDLR_FUNC(oper, narg, hdlr);

    /*N 1996/06/06 mschoene this should not be done here                   */
    SET_FLAG1_FILT(oper, INTOBJ_INT(0));
    SET_FLAG2_FILT(oper, INTOBJ_INT(0));
    SET_FLAGS_FILT(oper, False);
    SET_SETTR_FILT(oper, False);
    SET_TESTR_FILT(oper, False);

    // This isn't an attribute (yet)
    SET_ENABLED_ATTR(oper, 0);

    // return operation
    return oper;
}


/****************************************************************************
**
*F  DoConstructor0Args( <oper> )
*F  DoConstructor1Args( <oper> )
*F  DoConstructor2Args( <oper> )
*F  DoConstructor3Args( <oper> )
*F  DoConstructor4Args( <oper> )
*F  DoConstructor5Args( <oper> )
*F  DoConstructor6Args( <oper> )
*F  DoConstructorXArgs( <oper> )
*/


static Obj DoConstructor0Args(Obj oper)
{
    ErrorQuit("constructors must have at least one argument", 0, 0);
    return 0;
}

static Obj DoConstructor1Args(Obj oper, Obj a1)
{
    return DoOperationNArgs<1, FALSETRUE>(oper, a1, 0, 0, 0, 0, 0);
}

static Obj DoConstructor2Args(Obj oper, Obj a1, Obj a2)
{
    return DoOperationNArgs<2, FALSETRUE>(oper, a1, a2, 0, 0, 0, 0);
}

static Obj DoConstructor3Args(Obj oper, Obj a1, Obj a2, Obj a3)
{
    return DoOperationNArgs<3, FALSETRUE>(oper, a1, a2, a3, 0, 0, 0);
}

static Obj DoConstructor4Args(Obj oper, Obj a1, Obj a2, Obj a3, Obj a4)
{
    return DoOperationNArgs<4, FALSETRUE>(oper, a1, a2, a3, a4, 0, 0);
}

static Obj
DoConstructor5Args(Obj oper, Obj a1, Obj a2, Obj a3, Obj a4, Obj a5)
{
    return DoOperationNArgs<5, FALSETRUE>(oper, a1, a2, a3, a4, a5, 0);
}

static Obj
DoConstructor6Args(Obj oper, Obj a1, Obj a2, Obj a3, Obj a4, Obj a5, Obj a6)
{
    return DoOperationNArgs<6, FALSETRUE>(oper, a1, a2, a3, a4, a5, a6);
}

static Obj DoConstructorXArgs(Obj self, Obj args)
{
    ErrorQuit("sorry: cannot yet have X argument constructors", 0, 0);
    return 0;
}


/****************************************************************************
**
*F  DoVerboseConstructor0Args( <oper> )
*F  DoVerboseConstructor1Args( <oper> )
*F  DoVerboseConstructor2Args( <oper> )
*F  DoVerboseConstructor3Args( <oper> )
*F  DoVerboseConstructor4Args( <oper> )
*F  DoVerboseConstructor5Args( <oper> )
*F  DoVerboseConstructor6Args( <oper> )
*F  DoVerboseConstructorXArgs( <oper> )
*/


static Obj DoVerboseConstructor0Args(Obj oper)
{
    ErrorQuit("constructors must have at least one argument", 0, 0);
    return 0;
}

static Obj DoVerboseConstructor1Args(Obj oper, Obj a1)
{
    return DoOperationNArgs<1, TRUETRUE>(oper, a1, 0, 0, 0, 0, 0);
}

static Obj DoVerboseConstructor2Args(Obj oper, Obj a1, Obj a2)
{
    return DoOperationNArgs<2, TRUETRUE>(oper, a1, a2, 0, 0, 0, 0);
}

static Obj DoVerboseConstructor3Args(Obj oper, Obj a1, Obj a2, Obj a3)
{
    return DoOperationNArgs<3, TRUETRUE>(oper, a1, a2, a3, 0, 0, 0);
}

static Obj DoVerboseConstructor4Args(Obj oper, Obj a1, Obj a2, Obj a3, Obj a4)
{
    return DoOperationNArgs<4, TRUETRUE>(oper, a1, a2, a3, a4, 0, 0);
}

static Obj
DoVerboseConstructor5Args(Obj oper, Obj a1, Obj a2, Obj a3, Obj a4, Obj a5)
{
    return DoOperationNArgs<5, TRUETRUE>(oper, a1, a2, a3, a4, a5, 0);
}

static Obj DoVerboseConstructor6Args(
    Obj oper, Obj a1, Obj a2, Obj a3, Obj a4, Obj a5, Obj a6)
{
    return DoOperationNArgs<6, TRUETRUE>(oper, a1, a2, a3, a4, a5, a6);
}

static Obj DoVerboseConstructorXArgs(Obj self, Obj args)
{
    ErrorQuit("sorry: cannot yet have X argument constructors", 0, 0);
    return 0;
}


/****************************************************************************
**
*F  NewConstructor( <name>> )
*/

static Obj NewConstructor(Obj name)
{
    Obj                 oper;

    // create the function
    oper = NewFunctionT( T_FUNCTION, sizeof(OperBag), name, -1, 0, 0 );

    // enter the handlers
    SET_HDLR_FUNC(oper, 0, (ObjFunc)DoConstructor0Args);
    SET_HDLR_FUNC(oper, 1, (ObjFunc)DoConstructor1Args);
    SET_HDLR_FUNC(oper, 2, (ObjFunc)DoConstructor2Args);
    SET_HDLR_FUNC(oper, 3, (ObjFunc)DoConstructor3Args);
    SET_HDLR_FUNC(oper, 4, (ObjFunc)DoConstructor4Args);
    SET_HDLR_FUNC(oper, 5, (ObjFunc)DoConstructor5Args);
    SET_HDLR_FUNC(oper, 6, (ObjFunc)DoConstructor6Args);
    SET_HDLR_FUNC(oper, 7, (ObjFunc)DoConstructorXArgs);

    /*N 1996/06/06 mschoene this should not be done here                   */
    SET_FLAG1_FILT(oper, INTOBJ_INT(0));
    SET_FLAG2_FILT(oper, INTOBJ_INT(0));
    SET_FLAGS_FILT(oper, False);
    SET_SETTR_FILT(oper, False);
    SET_TESTR_FILT(oper, False);

    // return constructor
    return oper;
}


/****************************************************************************
**
**  DoTestAttribute( <attr>, <obj> )
*/

Obj DoTestAttribute (
    Obj                 self,
    Obj                 obj )
{
    Int                 flag2;
    Obj                 type;
    Obj                 flags;

    // get the flag for the tester
    flag2 = INT_INTOBJ( FLAG2_FILT( self ) );

    // get type of the object and its flags
    type  = TYPE_OBJ_FEO( obj );
    flags = FLAGS_TYPE( type );

    // return whether the value of the attribute is already known
    return SAFE_ELM_FLAGS( flags, flag2 );
}


/****************************************************************************
**
**  DoAttribute( <attr>, <obj> )
*/

#define DoSetAttribute  DoOperation2Args

Obj DoAttribute (
    Obj                 self,
    Obj                 obj )
{
    Obj                 val;
    Int                 flag2;
    Obj                 type;
    Obj                 flags;

    // get the flag for the tester
    flag2 = INT_INTOBJ( FLAG2_FILT( self ) );

    // get type of the object and its flags
    type  = TYPE_OBJ_FEO( obj );
    flags = FLAGS_TYPE( type );

    // if the value of the attribute is already known, simply return it
    if ( SAFE_C_ELM_FLAGS( flags, flag2 ) ) {
        return DoOperation1Args( self, obj );
    }

    // call the operation to compute the value
    val = DoOperation1Args( self, obj );
    if (val == 0) {
        ErrorMayQuit("Method for an attribute must return a value", 0, 0);
    }
    val = CopyObj( val, 0 );

    // set the value (but not for internal objects)
    if ( ENABLED_ATTR( self ) == 1 && !IS_MUTABLE_OBJ( obj ) ) {
        switch ( TNUM_OBJ( obj ) ) {
        case T_COMOBJ:
        case T_POSOBJ:
        case T_DATOBJ:
#ifdef HPCGAP
        case T_ACOMOBJ:
        case T_APOSOBJ:
#endif
            DoSetAttribute( SETTR_FILT(self), obj, val );
        }
    }

    // return the value
    return val;
}


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

--> maximum size reached

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

92%


¤ 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.0.55Bemerkung:  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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 ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


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