Skip to main content

Contains a variety of ordered structures, in particular a SkipList.

Project description

SkipList

This project contains a SkipList implementation in C++ with Python bindings.

A SkipList behaves as an always sorted list with, typically, O(log(n)) cost for insertion, look-up and removal. This makes it ideal for such operations as computing the rolling median of a large dataset.

See the full documentation on this project at ReadTheDocs.

A SkipList is implemented as a singly linked list of ordered nodes where each node participates in a subset of, sparser, linked lists. These additional 'sparse' linked lists provide rapid indexing and mutation of the underlying linked list. It is a probabilistic data structure using a random function to determine how many 'sparse' linked lists any particular node participates in. As such SkipList is an alternative to binary tree, Wikipedia has a introductory page on SkipLists.

An advantage claimed for SkipLists are that the insert and remove logic is simpler (however I do not subscribe to this). The drawbacks of a SkipList include its larger space requirements and its O(log(N)) lookup behaviour compared to other, more restricted and specialised, data structures that may have either faster runtime behaviour or lower space requirements or both.

This project contains a SkipList implementation in C++ with Python bindings with:

  • No capacity restrictions apart from available memory.
  • Works with any C++ type that has meaningful comparison operators.
  • The C++ SkipList can be compiled as thread safe.
  • The Python SkipList is thread safe.
  • The SkipList has exhaustive internal integrity checks.
  • The Python SkipLists can be long/float/bytes/object types, the latter can have user defined comparison functions.
  • This implementation is extensively performance tested in C++ and Python, see :ref:performance-label

There are a some novel features to this implementation:

Credits

Originally written by Paul Ross with credits to: Wilfred Hughes (AHL), Luke Sewell (AHL) and Terry Tsantagoeds (AHL).

Installation

C++

This SkipList requires:

  • A C++11 compiler.
  • -I<skiplist>/src/cpp as an include path.
  • <skiplist>/src/cpp/SkipList.cpp to be compiled/linked.
  • The macro SKIPLIST_THREAD_SUPPORT set if you want a thread safe SkipList using C++ mutexes.

Python

This SkipList supports Python 2.7 and 3.6, 3.7, 3.8, 3.9 (and, probably, some earlier Python 3 versions).

$ cd <skiplist>
$ python setup.py install

Testing

This SkipList has extensive tests for correctness and performance.

C++

To run all the C++ functional and performance tests:

$ cd <skiplist>/src/cpp
$ make release
$ ./SkipList_R.exe

To run the C++ functional tests with agressive internal integrity checks:

$ cd <skiplist>/src/cpp
$ make debug
$ ./SkipList_D.exe

To run all the C++ functional and performance tests for a thread safe SkipList:

$ cd <skiplist>/src/cpp
$ make release CXXFLAGS=-DSKIPLIST_THREAD_SUPPORT
$ ./SkipList_R.exe

Python

Testing requires pytest and hypothesis:

To run all the C++ functional and performance tests:

$ cd <skiplist>
$ py.test -vs tests/

Examples

Here are some examples of using a SkipList in your code:

C++

#include "SkipList.h"

// Declare with any type that has sane comparison.
OrderedStructs::SkipList::HeadNode<double> sl;

sl.insert(42.0);
sl.insert(21.0);
sl.insert(84.0);

sl.has(42.0) // true
sl.size()    // 3
sl.at(1)     // 42.0, throws OrderedStructs::SkipList::IndexError if index out of range

sl.remove(21.0); // throws OrderedStructs::SkipList::ValueError if value not present

sl.size()    // 2
sl.at(1)     // 84.0

The C++ SkipList is thread safe when compiled with the macro SKIPLIST_THREAD_SUPPORT, then a SkipList can then be shared across threads:

#include <thread>
#include <vector>

#include "SkipList.h"

void do_something(OrderedStructs::SkipList::HeadNode<double> *pSkipList) {
    // Insert/remove items into *pSkipList
    // Read items inserted by other threads.
}

OrderedStructs::SkipList::HeadNode<double> sl;
std::vector<std::thread> threads;

for (size_t i = 0; i < thread_count; ++i) {
    threads.push_back(std::thread(do_something, &sl));
}
for (auto &t: threads) {
    t.join();
}
// The SkipList now contains the totality of the thread actions.

Python

An example of using a SkipList of always ordered floats:

import orderedstructs

# Declare with a type. Supported types are long/float/bytes/object.
sl = orderedstructs.SkipList(float)

sl.insert(42.0)
sl.insert(21.0)
sl.insert(84.0)

sl.has(42.0) # True
sl.size()    # 3
sl.at(1)     # 42.0

sl.has(42.0) # True
sl.size()    # 3
sl.at(1)     # 42.0, raises IndexError if index out of range

sl.remove(21.0); # raises ValueError if value not present

sl.size()    # 2
sl.at(1)     # 84.0

The Python SkipList can be used with user defined objects with a user defined sort order. In this example the last name of the person takes precedence over the first name:

import functools

@functools.total_ordering
class Person:
    """Simple example of ordering based on last name/first name."""
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __eq__(self, other):
        try:
            return self.last_name == other.last_name and self.first_name == other.first_name
        except AttributeError:
            return NotImplemented

    def __lt__(self, other):
        try:
            return self.last_name < other.last_name or self.first_name < other.first_name
        except AttributeError:
            return NotImplemented

    def __str__(self):
        return '{}, {}'.format(self.last_name, self.first_name)

import orderedstructs

sl = orderedstructs.SkipList(object)

sl.insert(Person('Peter', 'Pan'))
sl.insert(Person('Alan', 'Pan'))
assert sl.size() == 2
assert str(sl.at(0)) == 'Pan, Alan' 
assert str(sl.at(1)) == 'Pan, Peter' 

The Python SkipList is thread safe when using any acceptable Python type even if that type has user defined comparison methods. This uses Pythons mutex machinery which is independent of C++ mutexes.

History

0.3.2 (2021-03-18)

  • Fix lambda issues with Python 3.8, 3.9.

0.3.1 (2021-03-17)

  • Support Python 3.7, 3.8, 3.9.

0.3.0 (2017-08-18)

  • Public release.
  • Allows storing of PyObject* and rich comparison.

0.2.0

Python module now named orderedstructs.

0.1.0

Initial release.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

orderedstructs-0.3.2.dev1.tar.gz (14.8 kB view details)

Uploaded Source

Built Distributions

orderedstructs-0.3.2.dev1-cp39-cp39-macosx_10_9_x86_64.whl (49.2 kB view details)

Uploaded CPython 3.9 macOS 10.9+ x86-64

orderedstructs-0.3.2.dev1-cp38-cp38-macosx_10_9_x86_64.whl (49.3 kB view details)

Uploaded CPython 3.8 macOS 10.9+ x86-64

orderedstructs-0.3.2.dev1-cp37-cp37m-macosx_10_9_x86_64.whl (49.2 kB view details)

Uploaded CPython 3.7m macOS 10.9+ x86-64

orderedstructs-0.3.2.dev1-cp36-cp36m-macosx_10_6_intel.whl (101.9 kB view details)

Uploaded CPython 3.6m macOS 10.6+ intel

File details

Details for the file orderedstructs-0.3.2.dev1.tar.gz.

File metadata

  • Download URL: orderedstructs-0.3.2.dev1.tar.gz
  • Upload date:
  • Size: 14.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/3.7.3 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.8.3

File hashes

Hashes for orderedstructs-0.3.2.dev1.tar.gz
Algorithm Hash digest
SHA256 c3dd6217685c73488eb4f9a5e12db61180ef1b43a7355bc96bfc1142611fbb34
MD5 6f99b9d743c8f1a3ac5230c44003209b
BLAKE2b-256 74e2ca37e3a8b1b6352230642af2aba7d2c156ef263805683587f74ff3187241

See more details on using hashes here.

File details

Details for the file orderedstructs-0.3.2.dev1-cp39-cp39-macosx_10_9_x86_64.whl.

File metadata

  • Download URL: orderedstructs-0.3.2.dev1-cp39-cp39-macosx_10_9_x86_64.whl
  • Upload date:
  • Size: 49.2 kB
  • Tags: CPython 3.9, macOS 10.9+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/3.7.3 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.8.3

File hashes

Hashes for orderedstructs-0.3.2.dev1-cp39-cp39-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 72a27f0d43f2610323cf11ae5bd256840165858249590f691f82c02aa84f6e57
MD5 a957d2f462e0a265181c1e41a1968c72
BLAKE2b-256 8ddee37a4754f751b4aad6483824f4fc3560c75d650074eff8d6d1e0ce9321a5

See more details on using hashes here.

File details

Details for the file orderedstructs-0.3.2.dev1-cp38-cp38-macosx_10_9_x86_64.whl.

File metadata

  • Download URL: orderedstructs-0.3.2.dev1-cp38-cp38-macosx_10_9_x86_64.whl
  • Upload date:
  • Size: 49.3 kB
  • Tags: CPython 3.8, macOS 10.9+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/3.7.3 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.8.3

File hashes

Hashes for orderedstructs-0.3.2.dev1-cp38-cp38-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 33ffb1dd54b3f7db09dd6e7a8b485e59970db301de9062fc4c5dd8f487b104f1
MD5 9e640af27828865fd8cd51a69123aa1d
BLAKE2b-256 969208ad5987fde148e3d181fec5bc73be4139002e4b85e481c7e471c7a21266

See more details on using hashes here.

File details

Details for the file orderedstructs-0.3.2.dev1-cp37-cp37m-macosx_10_9_x86_64.whl.

File metadata

  • Download URL: orderedstructs-0.3.2.dev1-cp37-cp37m-macosx_10_9_x86_64.whl
  • Upload date:
  • Size: 49.2 kB
  • Tags: CPython 3.7m, macOS 10.9+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/3.7.3 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.8.3

File hashes

Hashes for orderedstructs-0.3.2.dev1-cp37-cp37m-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 c70bbdc2b85bb9dc3ab1c7c0d60033ab3a7ed313b32ac0b61ed76d00297a2d4f
MD5 75fd1101e7c640733a86884994389f28
BLAKE2b-256 375c5e7d9d0a7814ff3aecb5c976e3a1841a9bb985023f20347d4ba4ee383c48

See more details on using hashes here.

File details

Details for the file orderedstructs-0.3.2.dev1-cp36-cp36m-macosx_10_6_intel.whl.

File metadata

  • Download URL: orderedstructs-0.3.2.dev1-cp36-cp36m-macosx_10_6_intel.whl
  • Upload date:
  • Size: 101.9 kB
  • Tags: CPython 3.6m, macOS 10.6+ intel
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/3.7.3 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.8.3

File hashes

Hashes for orderedstructs-0.3.2.dev1-cp36-cp36m-macosx_10_6_intel.whl
Algorithm Hash digest
SHA256 09ba8a85f1d564d61a762c1bde74a37569efe6831cdd8472152d1eaa3e008ba9
MD5 a942d9b65d1835865482c75b3fd5a46d
BLAKE2b-256 36c3208d2bf2c0d6290b19985208754c47d2a7148a433254d27d6246e145c514

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page