Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/datastructures/gap/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 14.9.2025 mit Größe 13 kB image not shown  

Quelle  skiplist.gi   Sprache: unbekannt

 

#
# Skip Lists as an ordered set implementation (this version is sets)
#

# For documentation of the data structure, see Wikipedia. A skip list is
# essentially set of linked lists called levels -- in this implementation levels
# currently start at 2. The list at level 2 contains all the objects in the set
# in order, the lists at higher levels contain progressively fewer of them
# (roughly 1/3 as many as at the level below). each level is a subset of the one
# below. The nodes of the lists are the same objects, so that you can drop down
# from one list to a lower level as you approach the object you want.
#
#
# Each node is represented by a plain list whose 1 entry is the value at the
# node and whose other entries are next pointers for all of the linked lists
# that that entry is in. The end of the lists is represented by unbound. The
# root object contains the head pointers of all the lists (and fail in position
# 1) This object (along with the less than function and other attributes are
# stored in an outer component object


# Record for local functions and constants to avoid cluttering the global
# namespace
SKIPLISTS := rec();

DeclareRepresentation("IsSkipListRep", IsComponentObjectRep, []);

SKIPLISTS.SkipListDefaultType :=  NewType(OrderedSetDSFamily, IsSkipListRep and IsOrderedSetDS and IsMutable);
SKIPLISTS.SkipListStandardType :=  NewType(OrderedSetDSFamily, IsSkipListRep and IsStandardOrderedSetDS and IsMutable);
SKIPLISTS.nullIterator := Iterator([]);

# This controls the relative sizes of the lists (i.e. how likely each entry is
# to be promoted to the next list above the default value is 1/3 represented by
# its inverse here. It is possible that 1/2 might be better choice when
# comparison is very expensive.
SKIPLISTS.defaultInvProb := 3;

#
# Worker function for all the constructors
#
SKIPLISTS.NewSkipList :=
  function(isLess, iter, rs, invprob)
    local  s, x, type;
    if isLess = \< then
        type := SKIPLISTS.SkipListStandardType;
    else
        type := SKIPLISTS.SkipListDefaultType;
    fi;

    s :=  Objectify( type,     rec(
                  lists := [fail],
                  isLess := isLess,
                  invprob := invprob,
                  size := 0,
                  randomSource := rs) );
    for x in iter do
        AddSet(s,x);
    od;
    return s;
end;

#
# Constructors
#

InstallMethod(OrderedSetDS, [IsSkipListRep and IsOrderedSetDS and IsMutable, IsFunction],
function(type, isLess)
    return SKIPLISTS.NewSkipList(isLess, SKIPLISTS.nullIterator, GlobalMersenneTwister, SKIPLISTS.defaultInvProb);
end);

InstallMethod(OrderedSetDS, [IsSkipListRep and IsOrderedSetDS and IsMutable],
function(type)
    return SKIPLISTS.NewSkipList(\<, SKIPLISTS.nullIterator, GlobalMersenneTwister, SKIPLISTS.defaultInvProb);
end);

InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsFunction, IsRandomSource],
function(type, isLess, rs)
    return SKIPLISTS.NewSkipList(isLess, SKIPLISTS.nullIterator, rs, SKIPLISTS.defaultInvProb);
end);


InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsListOrCollection],
function(type, data)
    return SKIPLISTS.NewSkipList(\<, Iterator(data), GlobalMersenneTwister, SKIPLISTS.defaultInvProb);
end);

InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsListOrCollection, IsRandomSource],
function(type, data, rs)
    return SKIPLISTS.NewSkipList(\<, Iterator(data), rs, SKIPLISTS.defaultInvProb);
end);

InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsOrderedSetDS],
function(type, os)
    return SKIPLISTS.NewSkipList(\<, Iterator(os), GlobalMersenneTwister, SKIPLISTS.defaultInvProb);
end);

InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsFunction, IsListOrCollection],
function(type, isLess, data)
    return SKIPLISTS.NewSkipList(isLess, Iterator(data), GlobalMersenneTwister, SKIPLISTS.defaultInvProb);
end);

InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsIterator],
function(type, iter)
    return SKIPLISTS.NewSkipList(\<, iter, GlobalMersenneTwister, SKIPLISTS.defaultInvProb);
end);

InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsFunction, IsIterator],
function(type, isLess, iter)
    return SKIPLISTS.NewSkipList(isLess, iter, GlobalMersenneTwister, SKIPLISTS.defaultInvProb);
end);

InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsFunction, IsListOrCollection, IsRandomSource],
function(type, isLess, data, rs)
    return SKIPLISTS.NewSkipList(isLess, Iterator(data), rs, SKIPLISTS.defaultInvProb);
end);

InstallMethod(OrderedSetDS, [IsSkipListRep and IsMutable and IsOrderedSetDS, IsFunction, IsIterator, IsRandomSource],
function(type, isLess, iter, rs)
    return SKIPLISTS.NewSkipList(isLess, iter, rs, SKIPLISTS.defaultInvProb);
end);

#
# The general API doesn't provide a way to set this tuning parameter.
#
BindGlobal("SetSkipListParameter", function(sl, p)
    sl!.invprob := p;
end);


#
# This is the worker function which finds a value if it is present, and if not
# finds where it would go.
#
# Specifically it returns a vector whose l entry is the skiplist entry at level
# l which immediately precedes the value we are looking for (or would precede it
# if it were present). So if it returns x with an entry in position l then
# x[l][1] < val (or x[1] is the head node) and x[l][l] is either unbound or has
# x[l][l][1] >= val

#
# There is a C version of this function in skiplist.c. This version is retained
# (and tested) as a reference implementation and/or if the kernel code is not
# compiled.
#
# The first argyment is the head node, not the component object
#
SKIPLISTS.ScanSkipListGAP := function(sl, val, less)
    local  level, ptr, lst, nx;
    #
    # We start at the head node and at the top level
    #
    ptr := sl;
    level := Length(ptr);
    #
    # the result will accumulate in lst
    #
    lst := [];
    while level > 1 do
        if not IsBound(ptr[level]) then
            #
            # end of the list at the current level,
            # remember this location and drop down
            #
            lst[level] := ptr;
            level := level -1;
        else
            #
            # Look ahead on current list
            #
            nx := ptr[level];
            if not less(nx[1],val) then
                #
                # the next node at the current level is AFTER  or equal to the value we want
                # remember this location and drop down
                #
                lst[level] := ptr;
                level := level -1;
            else
                #
                # Move along at current level
                #
                ptr := nx;
            fi;
        fi;
    od;
    return lst;
end;


#
# Use the C version if available
#
if IsBound(DS_Skiplist_Scan) then
    SKIPLISTS.ScanSkipList := DS_Skiplist_Scan;
else
    SKIPLISTS.ScanSkipList := SKIPLISTS.ScanSkipListGAP;
fi;



InstallMethod(AddSet, [IsSkipListRep and IsOrderedSetDS and IsMutable, IsObject],
        function(sl, val)
    local  lst, new, level, rs, ip, node;
    #
    # Scan and check
    #
    lst := SKIPLISTS.ScanSkipList(sl!.lists,val, sl!.isLess);
    #
    # If the object is present, it will be the next item after the node returned by
    # the scan, at the lowest level (level 2)
    #
    # lst[2] unbound implies that the whole skiplist is actually empty
    # lst[2][2] unbound implies that we are about to add at the end of the skiplist
    #
    if IsBound(lst[2]) and IsBound(lst[2][2]) and lst[2][2][1] = val then
        #
        # It's there already
        #
        return;
    fi;

    #
    # Add the new value to as many linked lists as our random numbers dictate
    #
    # We now we'll need 2 entries in the list and 2/3 of the time that will be all, so
    # make a list with room for two entries
    #
    new := EmptyPlist(2);
    new[1] := val;
    level := 2;
    rs := sl!.randomSource;
    ip := sl!.invprob;
    #
    # In this loop we add the new node to the linked list at level level
    # and decide whether to also add it at the next level up.
    # this may involve opening up a whole new level
    #
    repeat
        if not IsBound(lst[level]) then
            #
            # New level
            #
            Add(sl!.lists, new);
        else
            #
            # We're adding after node
            #
            node := lst[level];
            if IsBound(node[level]) then
                new[level] := node[level];
            fi;
            node[level] := new;
        fi;
        level := level+1;
    until Random(rs, 1, ip) <> 1;
    #
    # Keep track of the size
    #
    sl!.size := sl!.size+1;
end);


InstallMethod(\in, [IsObject, IsSkipListRep and IsOrderedSetDS],
        function(val,sl)
    local  lst;
    lst := SKIPLISTS.ScanSkipList(sl!.lists, val, sl!.isLess);
    return IsBound(lst[2]) and IsBound(lst[2][2]) and lst[2][2][1] = val;
end);


#
# This is (profiling showed) the other loop worth moving into C brought
# out a function. It deals with the mechanics of removing a node from all the
# lists it's in
#
# lst is what is returned by the scan function, nx is the node to remove
#

SKIPLISTS.RemoveNodeGAP := function(lst, nx)
    local  level, node;
    for level in [2..Length(lst)] do
        node := lst[level];
        if IsBound(node[level]) and IsIdenticalObj(node[level],nx) then
            if IsBound(nx[level]) then
                node[level] := nx[level];
            else
                #
                # In this case we are removing the last node
                # at this level
                #
                Unbind(node[level]);
            fi;
        fi;
    od;
end;


#
# Prefer the C implemnentation
#
if IsBound(DS_Skiplist_RemoveNode) then
    SKIPLISTS.RemoveNode := DS_Skiplist_RemoveNode;
else
    SKIPLISTS.RemoveNode := SKIPLISTS.RemoveNodeGAP;
fi;


#
# And now what remains of the remove after the worker functions have been split out
#
InstallMethod(RemoveSet, [IsSkipListRep and IsOrderedSetDS and IsMutable, IsObject],
        function(sl, val)
    local  lst, nx;
    lst := SKIPLISTS.ScanSkipList(sl!.lists, val, sl!.isLess);
    if not IsBound(lst[2]) or not IsBound(lst[2][2]) then
        #
        # Wasn't there in the first place
        #
        return 0;
    fi;
    nx := lst[2][2];
    if nx[1] <> val then
        #
        # Wasn't there in the first place
        #
        return 0;
    fi;
    SKIPLISTS.RemoveNode(lst, nx);
    #
    # Book-keeping
    #
    sl!.size := sl!.size -1;
    return 1;
end);

#
# Show the full structure of the skip list in a display
# displays each linked list on a separate line.
#
InstallMethod(DisplayString, [IsSkipListRep],
        function(sl)
    local  l, ptr, s;
    s := [];
    sl := sl!.lists;
    for l in [Length(sl),Length(sl)-1..2] do
        Add(s,"->");
        ptr := sl[l];
        while true do
            Add(s,String(ptr[1]));
            Add(s,"->");
            if not IsBound(ptr[l]) then
                Add(s,"X\n");
                break;
            else
                ptr := ptr[l];
            fi;
        od;
    od;
    if Length(s) = 0 then
        return "<empty skiplist>";
    else
        return Concatenation(s);
    fi;

end);

#
# For inorder access to the skip list we can just ignore all the
# lists except the one at level 2 which is an in-order SLL containing
# all the elements. Hence the next couple of functions are pretty simple
#


InstallMethod(Iterator, [IsSkipListRep and IsOrderedSetDS],
        function(sl)
    return IteratorByFunctions(rec(
               ptr := sl!.lists,
    IsDoneIterator := function(iter)
        return not IsBound(iter!.ptr[2]);
    end,

    NextIterator := function(iter)
        iter!.ptr := iter!.ptr[2];
        return iter!.ptr[1];
    end,

    ShallowCopy := function(iter)
        return rec(ptr := iter!.ptr,
                   IsDoneIterator := iter!.IsDoneIterator,
                   NextIterator := iter!.NextIterator,
                   ShallowCopy := iter!.ShallowCopy,
                   PrintObj := iter!.PrintObj);
    end,

    PrintObj := function(iter)
        Print("Iterator of Skiplist");
    end ));
end);


#
# A debugging function to actually count the nodes and make
# sure our bookkepping is correct
#
SKIPLISTS.CheckSize :=
  function(sl)
    local  count, ptr;
    count := 0;
    ptr := sl!.lists;
    while IsBound(ptr[2]) do
        count := count+1;
        ptr := ptr[2];
    od;
    return count = Size(sl);
end;

#
# Since we keep track of it
#
InstallMethod(Size, [IsSkipListRep and IsOrderedSetDS],
        sl -> sl!.size);

#
# Typical short view
#
InstallMethod(ViewString, [IsSkipListRep and IsOrderedSetDS],
        function(sl)
    return Concatenation(["<skiplist ",String(Size(sl))," entries>"]);
end);

#
# Try and make a string that will return the same object
#

InstallMethod(String, [IsSkipListRep and IsOrderedSetDS],
        function(sl)
    local  s, isLess;
    s := [];
    Add(s,"OrderedSetDS(IsSkipListRep");
    isLess := sl!.isLess;
    if isLess <> \< then
        Add(s,", ");
        Add(s,String(isLess));
    fi;
    if not IsEmpty(sl) then
        Add(s, ", ");
        Add(s, String(AsList(sl)));
    fi;
    Add(s,")");
    return Concatenation(s);
end);

#
# Could do this faster, but copying the lists preserving all the structure
# but not copying the entries would be fiddly
#

InstallMethod(ShallowCopy, [IsSkipListRep and IsOrderedSetDS],
        sl -> OrderedSetDS(IsSkipListRep, sl)
        );


InstallMethod(LessFunction, [IsSkipListRep and IsOrderedSetDS],
        sl -> sl!.isLess);

[ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ]