2024-04-02 03:09:14 +00:00
# pragma GCC push_options
2024-04-03 19:40:39 +00:00
# pragma GCC optimize("O0")
2024-03-21 16:30:37 +00:00
2024-03-30 02:43:46 +00:00
// OPTIONS to set before including sl.h
// ---------------------------------------------------------------------------
# define DEBUG
# define SKIPLIST_DIAGNOSTIC
2024-03-17 14:40:59 +00:00
2024-03-30 02:43:46 +00:00
// Include our monolithic ADT, the Skiplist!
// ---------------------------------------------------------------------------
# include "../include/sl.h"
2024-03-28 00:34:47 +00:00
2024-03-30 02:43:46 +00:00
// Local demo application OPTIONS:
// ---------------------------------------------------------------------------
2024-05-26 01:34:43 +00:00
# define TEST_ARRAY_SIZE 10
2024-03-30 02:43:46 +00:00
# define VALIDATE
2024-05-27 01:37:16 +00:00
// define SNAPSHOTS
// define TODO_RESTORE_SNAPSHOTS
2024-04-08 15:18:36 +00:00
# define STABLE_SEED
# define DOT
2024-04-01 21:14:04 +00:00
# ifdef DOT
size_t gen = 0 ;
FILE * of = 0 ;
# endif
2024-03-28 00:34:47 +00:00
2024-03-30 02:43:46 +00:00
// ---------------------------------------------------------------------------
# ifdef VALIDATE
2024-04-03 19:40:39 +00:00
# define CHECK __skip_integrity_check_ex(list, 0)
2024-03-30 02:43:46 +00:00
# else
# define CHECK ((void)0)
# endif
2024-03-17 14:40:59 +00:00
/*
* SKIPLIST EXAMPLE :
*
2024-04-08 15:18:36 +00:00
* This example creates an " ex " Skiplist where keys are integers , values are
* strings containing the roman numeral for the key allocated on the heap .
2024-03-17 14:40:59 +00:00
*/
2024-03-18 18:58:35 +00:00
/*
* To start , you must create a type node that will contain the
* fields you ' d like to maintain in your Skiplist . In this case
2024-03-29 23:48:11 +00:00
* we map int - > char [ ] on the heap , but what you put here is up
* to you . You don ' t even need a " key " , just a way to compare one
* node against another , logic you ' ll provide in SKIP_DECL as a
* block below .
2024-03-18 18:58:35 +00:00
*/
2024-04-03 19:40:39 +00:00
struct ex_node {
2024-03-20 19:23:27 +00:00
int key ;
2024-03-21 16:30:37 +00:00
char * value ;
2024-04-03 19:40:39 +00:00
SKIPLIST_ENTRY ( ex ) entries ;
2024-03-17 14:40:59 +00:00
} ;
2024-03-18 18:58:35 +00:00
/*
* Generate all the access functions for our type of Skiplist .
2024-03-19 13:41:36 +00:00
*/
2024-03-19 16:33:11 +00:00
SKIPLIST_DECL (
2024-04-03 19:40:39 +00:00
ex , api_ , entries ,
2024-03-30 02:43:46 +00:00
/* compare entries: list, a, b, aux */
{
( void ) list ;
( void ) aux ;
if ( a - > key < b - > key )
return - 1 ;
if ( a - > key > b - > key )
return 1 ;
return 0 ;
} ,
/* free entry: node */
2024-05-26 01:34:43 +00:00
{
free ( node - > value ) ;
node - > value = NULL ;
} ,
2024-03-31 20:37:14 +00:00
/* update entry: rc, node, value */
2024-03-30 02:43:46 +00:00
{
2024-04-03 13:50:31 +00:00
char * numeral = ( char * ) value ;
2024-03-31 20:37:14 +00:00
if ( node - > value )
2024-03-30 02:57:41 +00:00
free ( node - > value ) ;
2024-04-03 13:50:31 +00:00
node - > value = numeral ;
2024-03-30 02:43:46 +00:00
} ,
/* archive an entry: rc, src, dest */
2024-03-21 16:30:37 +00:00
{
2024-03-28 02:41:26 +00:00
dest - > key = src - > key ;
char * nv = calloc ( strlen ( src - > value ) + 1 , sizeof ( char ) ) ;
2024-03-21 18:43:10 +00:00
if ( nv = = NULL )
2024-03-24 13:32:47 +00:00
rc = ENOMEM ;
else {
2024-03-28 02:41:26 +00:00
strncpy ( nv , src - > value , strlen ( src - > value ) ) ;
dest - > value = nv ;
2024-03-24 13:32:47 +00:00
}
2024-03-21 16:30:37 +00:00
} ,
2024-03-30 02:43:46 +00:00
/* size in bytes of the content stored in an entry: bytes */
2024-03-30 19:34:42 +00:00
{ bytes = strlen ( node - > value ) + 1 ; } )
2024-03-19 13:41:36 +00:00
/*
2024-03-30 02:43:46 +00:00
* Skiplists are ordered , we need a way to compare entries .
2024-03-19 13:41:36 +00:00
* Let ' s create a function with four arguments :
2024-03-18 18:58:35 +00:00
* - a reference to the Skiplist , ` slist `
* - the two nodes to compare , ` a ` and ` b `
* - and ` aux ` , which you can use to pass into this function
* any additional information required to compare objects .
* ` aux ` is passed from the value in the Skiplist , you can
* modify that value at any time to suit your needs .
*
2024-03-19 13:41:36 +00:00
* Your function should result in a return statement :
2024-03-18 18:58:35 +00:00
* a < b : return - 1
* a = = b : return 0
* a > b : return 1
*
* This result provides the ordering within the Skiplist . Sometimes
2024-03-19 13:41:36 +00:00
* your function will not be used when comparing nodes . This will
* happen when ` a ` or ` b ` are references to the head or tail of the
2024-03-18 18:58:35 +00:00
* list or when ` a = = b ` . In those cases the comparison function
* returns before using the code in your block , don ' t panic . : )
2024-03-19 01:19:26 +00:00
int
2024-04-03 19:40:39 +00:00
__ex_key_compare ( ex_t * list , ex_node_t * a , ex_node_t * b , void * aux )
2024-03-19 01:19:26 +00:00
{
2024-03-20 19:23:27 +00:00
( void ) list ;
( void ) aux ;
if ( a - > key < b - > key )
return - 1 ;
if ( a - > key > b - > key )
return 1 ;
return 0 ;
2024-03-19 01:19:26 +00:00
}
2024-03-30 02:43:46 +00:00
*/
2024-03-17 14:40:59 +00:00
2024-03-30 02:43:46 +00:00
/*
* Optional : Getters and Setters
* - get , put , dup ( put ) , del , etc . functions
*
* It can be useful to have simple get / put - style API , but to
* do that you ' ll have to supply some blocks of code used to
* extract data from within your nodes .
*/
SKIPLIST_DECL_ACCESS (
2024-04-03 19:40:39 +00:00
ex , api_ , key , int , value , char * ,
2024-03-30 02:43:46 +00:00
/* query blk */ { query . key = key ; } ,
/* return blk */ { return node - > value ; } )
/*
* Optional : Snapshots
*
2024-03-31 00:36:27 +00:00
* Enable functions that enable returning to an earlier point in
* time when a snapshot was created .
2024-03-30 02:43:46 +00:00
*/
2024-04-03 19:40:39 +00:00
SKIPLIST_DECL_SNAPSHOTS ( ex , api_ , entries )
2024-03-30 02:43:46 +00:00
/*
* Optional : Archive to / from bytes
*
2024-03-31 00:36:27 +00:00
* Enable functions that can write / read the content of your Skiplist
* out / in to / from an array of bytes .
2024-03-30 02:43:46 +00:00
*/
2024-04-03 19:40:39 +00:00
// TODO SKIPLIST_DECL_ARCHIVE(ex, api_, entries)
2024-03-30 02:43:46 +00:00
/*
* Optional : As Hashtable
*
2024-03-31 00:36:27 +00:00
* Turn your Skiplist into a hash table .
2024-03-30 02:43:46 +00:00
*/
2024-04-03 19:40:39 +00:00
// TODO SKIPLIST_DECL_HASHTABLE(ex, api_, entries, snaps)
2024-03-30 02:43:46 +00:00
/*
* Optional : Check Skiplists at runtime
*
* Create a functions that validate the integrity of a Skiplist .
*/
2024-04-03 19:40:39 +00:00
SKIPLIST_DECL_VALIDATE ( ex , api_ , entries )
2024-03-30 02:43:46 +00:00
/* Optional: Visualize your Skiplist using DOT/Graphviz in PDF
*
* Create the functions used to annotate a visualization of a Skiplist .
*/
2024-04-03 19:40:39 +00:00
SKIPLIST_DECL_DOT ( ex , api_ , entries )
2024-03-30 02:43:46 +00:00
2024-05-27 01:37:16 +00:00
static void
2024-04-03 19:40:39 +00:00
sprintf_ex_node ( ex_node_t * node , char * buf )
2024-03-30 02:43:46 +00:00
{
2024-04-03 19:40:39 +00:00
sprintf ( buf , " %d:%s " , node - > key , node - > value ) ;
2024-03-30 02:43:46 +00:00
}
// Function for this demo application.
// ---------------------------------------------------------------------------
2024-03-21 16:30:37 +00:00
2024-05-27 01:37:16 +00:00
/* convert a number into the Roman numeral equivalent, allocates a string caller must free */
2024-03-21 18:43:10 +00:00
static char *
int_to_roman_numeral ( int num )
{
int del [ ] = { 1000 , 900 , 500 , 400 , 100 , 90 , 50 , 40 , 10 , 9 , 5 , 4 , 1 } ; // Key value in Roman counting
char * sym [ ] = { " M " , " CM " , " D " , " CD " , " C " , " XC " , " L " , " XL " , " X " , " IX " , " V " , " IV " , " I " } ; // Symbols for key values
// The maximum length of the Roman numeral representation for the maximum signed 64-bit integer would be approximately 19 * 3 = 57 characters, assuming
// every digit is represented by its Roman numeral equivalent up to 3 repetitions. Therefore, 64 should be more than enough.
2024-03-28 01:37:38 +00:00
char * res = ( char * ) calloc ( 4096 , sizeof ( char ) ) ;
2024-03-21 16:30:37 +00:00
int i = 0 ;
if ( num < 0 ) {
res [ 0 ] = ' - ' ;
i + + ;
num = - num ;
}
if ( num = = 0 ) {
res [ 0 ] = ' 0 ' ;
return res ;
}
2024-04-02 17:18:07 +00:00
if ( num > 10000 ) {
sprintf ( res , " The person you were looking for is not here, their mailbox is full, good bye. " ) ;
return res ;
}
2024-03-21 18:43:10 +00:00
while ( num ) { // while input number is not zero
while ( num / del [ i ] ) { // while a number contains the largest key value possible
strcat ( res , sym [ i ] ) ; // append the symbol for this key value to res string
num - = del [ i ] ; // subtract the key value from number
2024-03-21 16:30:37 +00:00
}
2024-03-21 18:43:10 +00:00
i + + ; // proceed to the next key value
2024-03-21 16:30:37 +00:00
}
return res ;
}
2024-05-27 01:37:16 +00:00
/* calculate the floor of the log base 2 of a number m (⌊log2(m)⌋) */
static int
floor_log2 ( unsigned int m )
2024-03-21 18:43:10 +00:00
{
2024-05-27 01:37:16 +00:00
return ( int ) floor ( log ( m ) / log ( 2 ) ) ;
2024-03-21 16:30:37 +00:00
}
2024-04-03 19:40:39 +00:00
# ifdef TODO_RESTORE_SNAPSHOTS
typedef struct {
size_t length ;
size_t key ;
size_t snap_id ;
} snap_info_t ;
# endif
2024-03-30 02:43:46 +00:00
// ---------------------------------------------------------------------------
2024-05-27 01:37:16 +00:00
/* The head node's height is always 1 more than the tallest node, that location
is where we store the total hits , or " m " . */
# define splay_list_m(m) list->slh_head->entries.sle_levels[list->slh_head->entries.sle_height].hits
2024-03-19 01:19:26 +00:00
int
main ( )
{
2024-03-26 13:52:24 +00:00
int rc ;
2024-05-27 01:37:16 +00:00
char * numeral ;
# ifdef DOT
char msg [ 1024 ] ;
memset ( msg , 0 , 1024 ) ;
# endif
2024-04-03 19:40:39 +00:00
# ifdef TODO_RESTORE_SNAPSHOTS
size_t n_snaps = 0 ;
snap_info_t snaps [ TEST_ARRAY_SIZE * 2 + 1 ] ;
2024-03-30 02:43:46 +00:00
# endif
2024-03-22 03:09:16 +00:00
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-04-08 15:18:36 +00:00
of = fopen ( " /tmp/ex1.dot " , " w " ) ;
2024-03-22 03:09:16 +00:00
if ( ! of ) {
2024-04-08 15:18:36 +00:00
perror ( " Failed to open file /tmp/ex1.dot " ) ;
2024-03-22 03:09:16 +00:00
return 1 ;
}
2024-03-24 13:32:47 +00:00
# endif
2024-03-21 16:30:37 +00:00
2024-03-20 19:23:27 +00:00
/* Allocate and initialize a Skiplist. */
2024-04-03 19:40:39 +00:00
ex_t * list = ( ex_t * ) malloc ( sizeof ( ex_t ) ) ;
2024-03-21 16:30:37 +00:00
if ( list = = NULL )
return ENOMEM ;
2024-05-26 01:34:43 +00:00
rc = api_skip_init_ex ( list ) ;
2024-03-19 13:41:36 +00:00
if ( rc )
2024-03-20 19:23:27 +00:00
return rc ;
2024-05-27 01:37:16 +00:00
/* Set the PRNG state to a known constant for reproducible generation, easing debugging. */
list - > slh_prng_state = 12 ;
# ifdef SNAPSHOTS
2024-04-03 19:40:39 +00:00
api_skip_snapshots_init_ex ( list ) ;
2024-05-27 01:37:16 +00:00
# endif
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-04-03 19:40:39 +00:00
api_skip_dot_ex ( of , list , gen + + , " init " , sprintf_ex_node ) ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-20 19:23:27 +00:00
2024-05-27 01:37:16 +00:00
/* This example mirrors the example given in the paper about splay-lists
to test implementation against research . */
2024-03-21 16:30:37 +00:00
2024-05-27 01:37:16 +00:00
for ( int i = 1 ; i < 8 ; i + + ) {
numeral = int_to_roman_numeral ( i ) ;
if ( ( rc = api_skip_put_ex ( list , i , numeral ) ) )
perror ( " put failed " ) ;
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-03-26 13:52:24 +00:00
sprintf ( msg , " put key: %d value: %s " , i , numeral ) ;
2024-04-03 19:40:39 +00:00
api_skip_dot_ex ( of , list , gen + + , msg , sprintf_ex_node ) ;
2024-03-24 13:32:47 +00:00
# endif
2024-03-20 19:23:27 +00:00
}
2024-03-30 02:43:46 +00:00
CHECK ;
2024-03-20 19:23:27 +00:00
2024-05-27 01:37:16 +00:00
/* Now we're going to poke around the internals a bit to set things up.
This first time around we ' re going to build the list by hand , later
we ' ll ensure that we can build this shape using only API calls . */
ex_node_t * head = list - > slh_head ;
ex_node_t * tail = list - > slh_tail ;
ex_node_t * node_1 = head - > entries . sle_levels [ 0 ] . next ;
ex_node_t * node_2 = node_1 - > entries . sle_levels [ 0 ] . next ;
ex_node_t * node_3 = node_2 - > entries . sle_levels [ 0 ] . next ;
ex_node_t * node_4 = node_3 - > entries . sle_levels [ 0 ] . next ;
ex_node_t * node_5 = node_4 - > entries . sle_levels [ 0 ] . next ;
ex_node_t * node_6 = node_5 - > entries . sle_levels [ 0 ] . next ;
// Head/Tail-nodes are height 3, ...
head - > entries . sle_height = tail - > entries . sle_height = 3 ;
// Head-node
head - > entries . sle_levels [ 3 ] . hits = 10 ;
head - > entries . sle_levels [ 2 ] . hits = 5 ;
head - > entries . sle_levels [ 1 ] . hits = 1 ;
head - > entries . sle_levels [ 0 ] . hits = 1 ;
head - > entries . sle_levels [ 1 ] . next = node_2 ;
head - > entries . sle_levels [ 2 ] . next = node_6 ;
head - > entries . sle_levels [ 3 ] . next = tail ;
// Tail-node
tail - > entries . sle_levels [ 3 ] . hits = 0 ;
tail - > entries . sle_levels [ 2 ] . hits = 0 ;
tail - > entries . sle_levels [ 1 ] . hits = 0 ;
tail - > entries . sle_levels [ 0 ] . hits = 1 ;
tail - > entries . sle_levels [ 1 ] . next = tail ;
tail - > entries . sle_levels [ 2 ] . next = tail ;
tail - > entries . sle_levels [ 3 ] . next = tail ;
// First node has key "1", height "0", hits(0) = 1
node_1 - > entries . sle_height = 0 ;
node_1 - > entries . sle_levels [ 0 ] . hits = 1 ;
// Second node has key "2", height "1", hits(0) = 1, hits(1) = 0
node_2 - > entries . sle_height = 1 ;
node_2 - > entries . sle_levels [ 0 ] . hits = 1 ;
node_2 - > entries . sle_levels [ 1 ] . hits = 0 ;
node_2 - > entries . sle_levels [ 1 ] . next = node_3 ;
// Third node has key "3", height "1", hits(0) = 1, hits(1) = 2
node_3 - > entries . sle_height = 1 ;
node_3 - > entries . sle_levels [ 0 ] . hits = 1 ;
node_3 - > entries . sle_levels [ 1 ] . hits = 2 ;
node_3 - > entries . sle_levels [ 1 ] . next = node_6 ;
// Fourth node has key "4", height "0", hits(0) = 1
node_4 - > entries . sle_height = 0 ;
node_4 - > entries . sle_levels [ 0 ] . hits = 1 ;
// Fifth node has key "5", height "0", hits(0) = 1
node_5 - > entries . sle_height = 0 ;
node_5 - > entries . sle_levels [ 0 ] . hits = 1 ;
// Sixth node has key "6", height "2", hits(0) = 5, hits(1) = 0, hits(2) = 0
node_6 - > entries . sle_height = 2 ;
node_6 - > entries . sle_levels [ 0 ] . hits = 5 ;
node_6 - > entries . sle_levels [ 1 ] . hits = 0 ;
node_6 - > entries . sle_levels [ 2 ] . hits = 0 ;
node_6 - > entries . sle_levels [ 1 ] . next = tail ;
node_6 - > entries . sle_levels [ 2 ] . next = tail ;
2024-03-30 19:34:42 +00:00
CHECK ;
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-05-27 01:37:16 +00:00
sprintf ( msg , " manually adjusted " ) ;
2024-04-03 19:40:39 +00:00
api_skip_dot_ex ( of , list , gen + + , msg , sprintf_ex_node ) ;
2024-03-26 00:17:40 +00:00
# endif
2024-03-21 20:35:18 +00:00
2024-05-27 01:37:16 +00:00
printf ( " m = %ld \n " , splay_list_m ( list ) ) ;
printf ( " (⌊log2(m)⌋) = %d \n " , floor_log2 ( splay_list_m ( list ) ) ) ;
2024-04-01 18:26:44 +00:00
2024-05-27 01:37:16 +00:00
if ( ! ( rc = api_skip_contains_ex ( list , 5 ) ) )
perror ( " missing element 5 " ) ;
CHECK ;
# ifdef DOT
sprintf ( msg , " contains(5) " ) ;
api_skip_dot_ex ( of , list , gen + + , msg , sprintf_ex_node ) ;
2024-04-03 19:40:39 +00:00
# endif
2024-03-24 13:32:47 +00:00
# ifdef DOT
2024-04-03 19:40:39 +00:00
api_skip_dot_end_ex ( of , gen ) ;
2024-03-22 03:09:16 +00:00
fclose ( of ) ;
2024-03-24 13:32:47 +00:00
# endif
2024-04-03 19:40:39 +00:00
api_skip_free_ex ( list ) ;
2024-04-03 13:50:31 +00:00
free ( list ) ;
2024-03-20 19:23:27 +00:00
return rc ;
2024-03-17 14:40:59 +00:00
}
2024-04-02 03:09:14 +00:00
# pragma GCC pop_options