Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f74ce7519a | ||
|
|
01d0b42e1a | ||
|
|
e470021d13 | ||
|
|
7de3ce5c0c | ||
|
|
976965bcfa | ||
|
|
1254e657c5 | ||
|
|
90ee6a424d | ||
|
|
bf79616e7e | ||
|
|
09ebb1eae8 | ||
|
|
ba6d8edde1 | ||
|
|
2f76ad4b6f | ||
|
|
407141661a | ||
|
|
38620ba457 | ||
|
|
f8f704f432 | ||
|
|
8ae83acd1a | ||
|
|
af26f6c081 | ||
|
|
d82be005da | ||
|
|
e37b54fc62 | ||
|
|
9eb8d5f682 | ||
|
|
7ecb2e90fd | ||
|
|
354cd538f8 | ||
|
|
0c883b0a74 | ||
|
|
9c23c3ca77 | ||
|
|
d0244a0513 | ||
|
|
875678d7a9 | ||
|
|
65f2b79157 | ||
|
|
c60ee10185 | ||
|
|
f7a598efc8 | ||
|
|
e8142f96b1 | ||
|
|
97d50bc3d9 | ||
|
|
8c457eeb12 | ||
|
|
846d0bbba6 | ||
|
|
a9314ee8f7 | ||
|
|
9bff706bba | ||
|
|
31c871c4c2 | ||
|
|
82b374ec83 | ||
|
|
940552d6cd | ||
|
|
6a2cb70fb5 | ||
|
|
78cfc0c335 | ||
|
|
f454934b07 | ||
|
|
5504f82052 | ||
|
|
d01997b7cb | ||
|
|
7b349ea426 | ||
|
|
1edb12a83c | ||
|
|
176bbc66ad | ||
|
|
41eebe546a | ||
|
|
afaecb2dde | ||
|
|
9062415322 | ||
|
|
37bfcbb9c4 | ||
|
|
d2692a8dbe |
No files matched your search
@@ -0,0 +1,5 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
*.h linguist-language=C
|
||||
*.c linguist-language=C
|
||||
+5
-1
@@ -13,4 +13,8 @@ script:
|
||||
- sudo ln -s /usr/bin/gcc-6 /usr/local/bin/gcc
|
||||
- export CC=gcc-7
|
||||
- cd ./tests
|
||||
- make
|
||||
- make --always-make
|
||||
- cd ./main
|
||||
- make --always-make
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
@@ -1,4 +1,33 @@
|
||||
# MAJOR.MINOR.ADJUSTMENTS
|
||||
# RELEASE.MAJOR.MINOR
|
||||
|
||||
1.6.0
|
||||
Added ./ext/intervalheap.h
|
||||
Added ./ext/multimap.h
|
||||
Added ./ext/multiset.h
|
||||
Added to_string function to most collections
|
||||
Added the cmc prefix to the enum entry_state used by hashtables and to the enum heap_type used by the heap
|
||||
Fixed return type on some functions from ./cmc/hashset.h and ./cmc/treeset.h
|
||||
Fixed hashtables integer overflow when the capacity is divided by the load factor
|
||||
Changed timer.h struct name from timer_t to cmc_timer to prevent name collisions on linux
|
||||
|
||||
1.5.3
|
||||
Tests completely reorganized and now with full code coverage
|
||||
Some fixes where there was a missing return statement
|
||||
Expanded ./utl/assert.h
|
||||
|
||||
1.5.2
|
||||
Added ./utl/test.h
|
||||
Added ./utl/assert.h
|
||||
Added ./tests/list.c - UnitTest for LIST
|
||||
Small fixes on ./utl/foreach.h, ./utl/test.h and redone ./utl/timer.h
|
||||
Removed ./utl/log.h functions that were unnecessary
|
||||
Added the CMC namespace to CONCATC and CONCATH at macro_collections.h
|
||||
|
||||
1.5.1
|
||||
New queue benchmark
|
||||
Added ./sac/queue.h
|
||||
Added ./utl/timer.h
|
||||
Fixing descriptions and other minor fixes
|
||||
|
||||
1.5.0
|
||||
Changed tests compilation and execution to a single Makefile
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
Generate Simple Data Structures of any type in C for you to use in your projects.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://travis-ci.org/LeoVen/C-Macro-Collections)
|
||||
[](https://codecov.io/gh/LeoVen/C-Macro-Collections)
|
||||
|
||||
## Project Structure
|
||||
|
||||
@@ -35,17 +36,15 @@ Only Collections located in __cmc__ are to be documented at *Documentation.md*.
|
||||
* TreeMap
|
||||
* TreeSet
|
||||
* Extra Collections Library
|
||||
* IntervalHeap
|
||||
* LinkedQueue
|
||||
* MultiMap
|
||||
* MultiSet
|
||||
* Stack Allocated Collections Library
|
||||
* Queue
|
||||
* Stack
|
||||
|
||||
### More to Come
|
||||
|
||||
* MultiMap
|
||||
* MultiSet
|
||||
* TreeHashMap
|
||||
* Heap will be an IntervalHeap
|
||||
* Stack Allocated Collections
|
||||
* Development Collections Library
|
||||
* Deque
|
||||
|
||||
Check out **Documentation.md** for more information about each collection.
|
||||
|
||||
@@ -89,15 +88,7 @@ The following table shows how each collection is implemented and how well they d
|
||||
|
||||
## How to use
|
||||
|
||||
To generate the collection, all you need to do is to include the necessary header files. You can include the containers you want to use individually or you can include the master header, `macro_collections.h`, that comes with all collections and other functionalities like the `FOR_EACH` macro.
|
||||
|
||||
There are three C standard headers that all collections use:
|
||||
|
||||
* `<stdlib.h>`
|
||||
* `<stdbool.h>`
|
||||
* `<string.h>`
|
||||
|
||||
With the last one being used only by array based containers. These headers are already included by default.
|
||||
To generate the collection, all you need to do is to include the necessary header files. You can include the containers you want to use individually or you can include the master header, `macro_collections.h`, that comes with the entire C-Macro-Collections library.
|
||||
|
||||
### Macros
|
||||
|
||||
@@ -149,3 +140,15 @@ When including `foreach.h` in your source code you gain access to all for-each m
|
||||
* __BODY__ - Block of code.
|
||||
|
||||
Inside body you will have access to the iterator variable. With it you can use functions to access the key, value or index from the iterator. Checkout the documentation for more details.
|
||||
|
||||
<hr>
|
||||
|
||||
Check out some code reviews that covers some parts the project:
|
||||
|
||||
| About | Link |
|
||||
|-------|--------|
|
||||
| Unit Test *./utl/test.h* | [](http://codereview.stackexchange.com/q/222954/178948) |
|
||||
| Interval Heap *./ext/intervalheap.h* | [](http://codereview.stackexchange.com/q/223595/178948) |
|
||||
| Hash Set *./cmc/hashset.h* | [](http://codereview.stackexchange.com/q/217333/178948) |
|
||||
| Linked List *./cmc/linkedlist.h* | [](http://codereview.stackexchange.com/q/216737/178948) |
|
||||
| Others | [](http://codereview.stackexchange.com/q/213553/178948) |
|
||||
@@ -1,49 +1,71 @@
|
||||
TODO
|
||||
|
||||
[ ] Finish Documentation
|
||||
[/] - In Progress
|
||||
[X] - Finished
|
||||
[~] - Needs to be reviewed
|
||||
|
||||
[/] Documentation
|
||||
[X] Remove PUBLIC and PRIVATE
|
||||
[ ] Add multiset
|
||||
[ ] Add multimap
|
||||
[ ] Upgrade heap to an interval heap
|
||||
[ ] Add dispose function
|
||||
[ ] Serialization
|
||||
[ ] Iterator based functions (like C++ collections)
|
||||
[ ] Add Stack Allocated Collections
|
||||
[ ] Finish all Dev Collections
|
||||
|
||||
|
||||
MAYBE
|
||||
|
||||
[ ] Add conditional code generation based on variadic macros
|
||||
|
||||
|
||||
PLANNED
|
||||
|
||||
Before 2.0
|
||||
Get Documentation fixed and completed
|
||||
|
||||
1.6
|
||||
Iterator move and move_to
|
||||
from_array - Creates a collection with all the elements in a specified array
|
||||
to_array - Creates an array with all the elements inside the collection
|
||||
LinkedList - Insert/Remove/Append/Prepend/Extract
|
||||
Resize buffer function
|
||||
Add dispose method to each collection in order to automatically free its data
|
||||
Add copy functions (copy of a collection)
|
||||
|
||||
2.0
|
||||
Add new heap collection implementation
|
||||
Add all stack based collections
|
||||
|
||||
2.1
|
||||
Add variadic macros (generate only the necessary functions)
|
||||
|
||||
2.2
|
||||
Add serialization functions
|
||||
|
||||
2.3
|
||||
Iterator functions (functions that operate relative to iterators position) like add or remove ranges
|
||||
|
||||
SOMETIME
|
||||
|
||||
Add TreeHashMap
|
||||
[ ] Extra Collections
|
||||
[X] Add MultiSet
|
||||
[X] Add MultiMap
|
||||
[X] Add IntervalHeap
|
||||
[ ] Add TreeHashMap
|
||||
[ ] Add functions
|
||||
[X] update {hashmap, treemap, multimap}
|
||||
[/] to_string {all}
|
||||
[ ] iter_advance {all} (iterators)
|
||||
[ ] iter_rewind {all} (iterators)
|
||||
[ ] iter_go_to {all} (iterators)
|
||||
[ ] from_array {all}
|
||||
[ ] to_array {all}
|
||||
[ ] equals {all}
|
||||
[ ] resize {dynamic arrays and circular buffers}
|
||||
[ ] dispose {all}
|
||||
[ ] copy_of {all}
|
||||
[ ] save {all} (serialization)
|
||||
[ ] load {all} (serialization)
|
||||
[ ] read {all} (serialization)
|
||||
[ ] write {all} (serialization)
|
||||
[ ] Range IO
|
||||
[ ] Sequences
|
||||
[ ] seq_push_front
|
||||
[ ] seq_push_at
|
||||
[ ] seq_push_back
|
||||
[ ] seq_remove
|
||||
[ ] seq_extract
|
||||
[ ] Others
|
||||
[ ] seq_insert
|
||||
[ ] seq_remove (attention on how to treat this)
|
||||
[ ] seq_extract (attention on how to treat this)
|
||||
[ ] Iterator IO
|
||||
[ ] range_insert
|
||||
[ ] range_remove
|
||||
[ ] Stack Allocated Collections
|
||||
[ ] Deque
|
||||
[ ] HashMap
|
||||
[ ] HashSet
|
||||
[ ] Heap
|
||||
[ ] LinkedList
|
||||
[ ] List
|
||||
[~] Queue
|
||||
[~] Stack
|
||||
[ ] TreeMap
|
||||
[ ] TreeSet
|
||||
[ ] Dev Collections
|
||||
[~] Deque
|
||||
[ ] HashMap
|
||||
[ ] HashSet
|
||||
[ ] Heap
|
||||
[ ] LinkedList
|
||||
[ ] List
|
||||
[ ] Queue
|
||||
[ ] Stack
|
||||
[ ] TreeMap
|
||||
[ ] TreeSet
|
||||
[X] Utility
|
||||
[X] assert.h
|
||||
[X] foreach.h
|
||||
[X] log.h
|
||||
[X] test.h
|
||||
[X] timer.h
|
||||
+39
-28
@@ -13,7 +13,6 @@
|
||||
* - Output
|
||||
*/
|
||||
#include "macro_collections.h"
|
||||
#include "util/timer.h"
|
||||
#include "util/twister.c"
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
@@ -60,39 +59,43 @@ void shuffle(int *array, mt_state_ptr st)
|
||||
}
|
||||
}
|
||||
|
||||
COLLECTION_GENERATE(LIST, l, list, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(STACK, s, stack, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(QUEUE, q, queue, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(DEQUE, d, deque, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(LINKEDLIST, ll, linked, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(HEAP, h, heap, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(TREESET, ts, tset, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(TREEMAP, tm, tmap, /* FMOD */, int, int)
|
||||
COLLECTION_GENERATE(HASHSET, hs, hset, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(HASHMAP, hm, hmap, /* FMOD */, int, int)
|
||||
COLLECTION_GENERATE(HASHSET, hs, hset, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(HEAP, h, heap, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(LINKEDLIST, ll, linked, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(LIST, l, list, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(QUEUE, q, queue, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(STACK, s, stack, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(TREEMAP, tm, tmap, /* FMOD */, int, int)
|
||||
COLLECTION_GENERATE(TREESET, ts, tset, /* FMOD */, /* K */, int)
|
||||
|
||||
COLLECTION_GENERATE(INTERVALHEAP, ih, iheap, , , int)
|
||||
COLLECTION_GENERATE(MULTIMAP, mm, mmap, /* FMOD */, int, int)
|
||||
COLLECTION_GENERATE(MULTISET, ms, mset, /* FMOD */, /* K */, int)
|
||||
|
||||
#define BENCHMARK(NAME, PFX, sname, initfunc, insertbody, removebody, searchbody) \
|
||||
void NAME##_io_benchmark(int *array, int *sarray, size_t s) \
|
||||
{ \
|
||||
printf("+------------------------------------------------------------ %10s\n", #NAME); \
|
||||
printf("+------------------------------------------------------------ \n"); \
|
||||
timer_t timer, total; \
|
||||
cmc_timer timer, total; \
|
||||
int r, k, v; \
|
||||
size_t j; \
|
||||
\
|
||||
sname *coll = initfunc; \
|
||||
\
|
||||
TIMER_START(total) \
|
||||
TIMER_START(timer) \
|
||||
timer_start(total); \
|
||||
timer_start(timer); \
|
||||
\
|
||||
for (size_t i = 0; i < NTOTAL; i++) \
|
||||
{ \
|
||||
insertbody; \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
timer_stop(timer); \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
timer_calc(timer); \
|
||||
\
|
||||
size_t s1 = PFX##_count(coll); \
|
||||
\
|
||||
@@ -100,16 +103,16 @@ COLLECTION_GENERATE(HASHMAP, hm, hmap, /* FMOD */, int, int)
|
||||
\
|
||||
double input_time = timer.result; \
|
||||
\
|
||||
TIMER_START(timer) \
|
||||
timer_start(timer); \
|
||||
\
|
||||
for (size_t i = 0; i < s; i++) \
|
||||
{ \
|
||||
searchbody; \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
timer_stop(timer); \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
timer_calc(timer); \
|
||||
\
|
||||
printf(" SEARCH %10s TOOK %8.0lf milliseconds for %8" PRIuMAX " elements\n", #NAME, timer.result, s); \
|
||||
\
|
||||
@@ -117,30 +120,30 @@ COLLECTION_GENERATE(HASHMAP, hm, hmap, /* FMOD */, int, int)
|
||||
\
|
||||
sname##_iter iter; \
|
||||
\
|
||||
TIMER_START(timer) \
|
||||
timer_start(timer); \
|
||||
\
|
||||
for (PFX##_iter_init(&iter, coll); !PFX##_iter_end(&iter); PFX##_iter_next(&iter)) \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
timer_stop(timer); \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
timer_calc(timer); \
|
||||
\
|
||||
printf("ITERATION %10s TOOK %8.0lf milliseconds for %8" PRIuMAX " elements\n", #NAME, timer.result, s1); \
|
||||
\
|
||||
double iter_time = timer.result; \
|
||||
\
|
||||
TIMER_START(timer); \
|
||||
timer_start(timer); \
|
||||
\
|
||||
for (size_t i = 0; i < NTOTAL; i++) \
|
||||
{ \
|
||||
removebody; \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
timer_stop(timer); \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
timer_calc(timer); \
|
||||
\
|
||||
size_t s2 = PFX##_count(coll); \
|
||||
\
|
||||
@@ -148,8 +151,8 @@ COLLECTION_GENERATE(HASHMAP, hm, hmap, /* FMOD */, int, int)
|
||||
\
|
||||
double output_time = timer.result; \
|
||||
\
|
||||
TIMER_STOP(total) \
|
||||
TIMER_CALC(total) \
|
||||
timer_stop(total); \
|
||||
timer_calc(total); \
|
||||
\
|
||||
PFX##_free(coll); \
|
||||
\
|
||||
@@ -173,9 +176,9 @@ COLLECTION_GENERATE(HASHMAP, hm, hmap, /* FMOD */, int, int)
|
||||
}
|
||||
|
||||
BENCHMARK(DEQUE, d, deque, d_new(NTOTAL), d_push_back(coll, array[i]), d_pop_back(coll), d_contains(coll, sarray[i], intcmp))
|
||||
BENCHMARK(HASHMAP, hm, hmap, hm_new(NTOTAL, 0.8, intcmp, inthash), hm_insert(coll, array[i], array[i]), hm_remove(coll, array[i], &r), hm_contains(coll, sarray[i]))
|
||||
BENCHMARK(HASHSET, hs, hset, hs_new(NTOTAL, 0.8, intcmp, inthash), hs_insert(coll, array[i]), hs_remove(coll, array[i]), hs_contains(coll, sarray[i]))
|
||||
BENCHMARK(HEAP, h, heap, h_new(NTOTAL, MinHeap, intcmp), h_insert(coll, array[i]), h_remove(coll, &r), h_contains(coll, sarray[i]))
|
||||
BENCHMARK(HASHMAP, hm, hmap, hm_new(NTOTAL, 0.6, intcmp, inthash), hm_insert(coll, array[i], array[i]), hm_remove(coll, array[i], &r), hm_contains(coll, sarray[i]))
|
||||
BENCHMARK(HASHSET, hs, hset, hs_new(NTOTAL, 0.6, intcmp, inthash), hs_insert(coll, array[i]), hs_remove(coll, array[i]), hs_contains(coll, sarray[i]))
|
||||
BENCHMARK(HEAP, h, heap, h_new(NTOTAL, cmc_min_heap, intcmp), h_insert(coll, array[i]), h_remove(coll, &r), h_contains(coll, sarray[i]))
|
||||
BENCHMARK(LINKEDLIST, ll, linked, ll_new(), ll_push_back(coll, array[i]), ll_pop_back(coll), ll_contains(coll, sarray[i], intcmp))
|
||||
BENCHMARK(LIST, l, list, l_new(NTOTAL), l_push_back(coll, array[i]), l_pop_back(coll), l_contains(coll, sarray[i], intcmp))
|
||||
BENCHMARK(QUEUE, q, queue, q_new(NTOTAL), q_enqueue(coll, array[i]), q_dequeue(coll), q_contains(coll, sarray[i], intcmp))
|
||||
@@ -183,6 +186,10 @@ BENCHMARK(STACK, s, stack, s_new(NTOTAL), s_push(coll, array[i]), s_pop(coll), s
|
||||
BENCHMARK(TREEMAP, tm, tmap, tm_new(intcmp), tm_insert(coll, array[i], array[i]), tm_remove(coll, array[i], &r), tm_contains(coll, sarray[i]))
|
||||
BENCHMARK(TREESET, ts, tset, ts_new(intcmp), ts_insert(coll, array[i]), ts_remove(coll, array[i]), ts_contains(coll, sarray[i]))
|
||||
|
||||
BENCHMARK(INTERVALHEAP, ih, iheap, ih_new(NTOTAL, intcmp), ih_insert(coll, array[i]), ih_remove_max(coll, &r), ih_contains(coll, sarray[i]))
|
||||
BENCHMARK(MULTIMAP, mm, mmap, mm_new(NTOTAL, 0.8, intcmp, inthash), mm_insert(coll, array[i], array[i]), mm_remove(coll, array[i], &r), mm_contains(coll, sarray[i]))
|
||||
BENCHMARK(MULTISET, ms, mset, ms_new(NTOTAL, 0.6, intcmp, inthash), ms_insert(coll, array[i]), ms_remove(coll, array[i]), ms_contains(coll, sarray[i]))
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Using the Mersenne Twister to generate random numbers. This seed
|
||||
@@ -225,6 +232,10 @@ int main(void)
|
||||
TREEMAP_io_benchmark(array, sarray, NTOTAL);
|
||||
TREESET_io_benchmark(array, sarray, NTOTAL);
|
||||
|
||||
INTERVALHEAP_io_benchmark(array, array, NMIN);
|
||||
MULTIMAP_io_benchmark(array, array, NTOTAL);
|
||||
MULTISET_io_benchmark(array, array, NTOTAL);
|
||||
|
||||
free(array);
|
||||
free(sarray);
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
CFLAGS = -Wall -Wextra
|
||||
INCLUDE = ../../src
|
||||
|
||||
main:
|
||||
gcc queue.c -I $(INCLUDE) $(CFLAGS) -o a.exe -DCMC_QUEUE
|
||||
a.exe
|
||||
gcc queue.c -I $(INCLUDE) $(CFLAGS) -o a.exe -DSAC_QUEUE
|
||||
a.exe
|
||||
gcc queue.c -I $(INCLUDE) $(CFLAGS) -o a.exe -DEXT_QUEUE
|
||||
a.exe
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* queue.c
|
||||
*
|
||||
* Creation Date: 24/06/2019
|
||||
*
|
||||
* Authors:
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
*/
|
||||
|
||||
/* Showing off how different queue implementations affect runtime cost */
|
||||
|
||||
#include "utl/timer.h"
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define MAX 200000
|
||||
|
||||
#if defined(CMC_QUEUE)
|
||||
#include "cmc/queue.h"
|
||||
QUEUE_GENERATE(q, queue, , int)
|
||||
#define TARGET "QUEUE"
|
||||
#elif defined(SAC_QUEUE)
|
||||
#include "sac/queue.h"
|
||||
SAC_QUEUE_GENERATE(q, queue, , int, MAX)
|
||||
#define TARGET "SAC QUEUE"
|
||||
#elif defined(EXT_QUEUE)
|
||||
#include "ext/linkedqueue.h"
|
||||
LINKEDQUEUE_GENERATE(q, queue, , int)
|
||||
#define TARGET "LINKED QUEUE"
|
||||
#else
|
||||
#error Please define either CMC_QUEUE, SAC_QUEUE or EXT_QUEUE
|
||||
#endif
|
||||
|
||||
int main(void)
|
||||
{
|
||||
|
||||
#if defined(CMC_QUEUE)
|
||||
queue *q = q_new(MAX);
|
||||
#elif defined(SAC_QUEUE)
|
||||
queue _q = q_new();
|
||||
queue_ptr q = &_q;
|
||||
#elif defined(EXT_QUEUE)
|
||||
queue *q = q_new();
|
||||
#endif
|
||||
|
||||
size_t sum = 0;
|
||||
int i = 0;
|
||||
|
||||
timer_t timer;
|
||||
TIMER_START(timer);
|
||||
|
||||
while (q_count(q) != MAX)
|
||||
{
|
||||
q_enqueue(q, i++);
|
||||
|
||||
if (i % 10 == 0)
|
||||
{
|
||||
for (size_t i = 0; i < 9; i++)
|
||||
{
|
||||
sum += q_peek(q);
|
||||
q_dequeue(q);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TIMER_STOP(timer);
|
||||
TIMER_CALC(timer);
|
||||
|
||||
printf("----------------------------------------\n");
|
||||
printf("%s\n", TARGET);
|
||||
printf("Execution time: %.0lf milliseconds\n", timer.result);
|
||||
printf("SUM: %" PRIuMAX "\n", sum);
|
||||
printf("----------------------------------------\n");
|
||||
|
||||
#if defined(CMC_QUEUE) || defined(EXT_QUEUE)
|
||||
q_free(q);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* timer.h
|
||||
*
|
||||
* Creation Date: 12/04/2019
|
||||
*
|
||||
* Authors:
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
* Simple timer macros.
|
||||
*/
|
||||
#include <time.h>
|
||||
|
||||
typedef struct timer_s
|
||||
{
|
||||
clock_t start;
|
||||
clock_t stop;
|
||||
double result;
|
||||
} timer_t;
|
||||
|
||||
#define TIMER_START(timer) \
|
||||
timer.start = clock();
|
||||
|
||||
#define TIMER_STOP(timer) \
|
||||
timer.stop = clock();
|
||||
|
||||
#define TIMER_CALC(timer) \
|
||||
timer.result = (double)(((timer.stop - timer.start) * 1000.0) / CLOCKS_PER_SEC);
|
||||
@@ -7,7 +7,7 @@
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
* Mersenne Twister PRNG utility
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
* iterators - Examples of using the collection's iterators
|
||||
* linkedlist - LINKEDLIST collection examples
|
||||
* list - LIST collection examples
|
||||
* multimap - MULTIMAP collection examples
|
||||
* multiset - MULTISET collection examples
|
||||
* others - Other examples, generally more robust and using multiple collections
|
||||
* queue - QUEUE collection examples
|
||||
* stack - STACK collection examples
|
||||
|
||||
@@ -57,7 +57,7 @@ int main(void)
|
||||
// Inside, the variable 'iter' will be available and from it you can access
|
||||
// the key, value or index.
|
||||
printf("MAP[%4d] = %.3lf\n", hm_iter_key(&iter), hm_iter_value(&iter));
|
||||
})
|
||||
});
|
||||
|
||||
hm_free(map);
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ HEAP_GENERATE(h, heap, static, int)
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
heap *h = h_new(32, MaxHeap, intcmp);
|
||||
heap *h = h_new(32, cmc_max_heap, intcmp);
|
||||
|
||||
for (int i = 1; i < 101; i++)
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ HEAP_GENERATE(h, heap, /* FMOD */, int)
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
// Create a new max heap with an initial capacity of 100
|
||||
heap *my_heap = h_new(100, MaxHeap, intcmp);
|
||||
heap *my_heap = h_new(100, cmc_max_heap, intcmp);
|
||||
|
||||
// Add elements to the heap
|
||||
for (int i = 0; i < 20; i++)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Multimap examples
|
||||
|
||||
* multimap.c - Basic examples of a multimap.
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "multimap.h"
|
||||
#include "foreach.h"
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
int intcmp(int a, int b)
|
||||
{
|
||||
return (a > b) - (a < b);
|
||||
}
|
||||
|
||||
size_t inthash(int t)
|
||||
{
|
||||
size_t a = t;
|
||||
a += ~(a << 15);
|
||||
a ^= (a >> 10);
|
||||
a += (a << 3);
|
||||
a ^= (a >> 6);
|
||||
a += ~(a << 11);
|
||||
a ^= (a >> 16);
|
||||
return a;
|
||||
}
|
||||
|
||||
MULTIMAP_GENERATE(mm, multi_map, , int, int)
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
const int min = 1, max = 201;
|
||||
multi_map *map = mm_new(50, 0.8, intcmp, inthash);
|
||||
|
||||
printf("Multimap actual capacity: %" PRIu64 "\n", map->capacity);
|
||||
printf("Adding items from %d to %d twice\n", min, max);
|
||||
|
||||
for (int i = min; i < max; i++)
|
||||
if (!mm_insert(map, i, i))
|
||||
printf("ERROR with %d:%d\n", i, i);
|
||||
|
||||
for (int i = min; i < max; i++)
|
||||
if (!mm_insert(map, i, i))
|
||||
printf("ERROR with %d:%d\n", i, i);
|
||||
|
||||
printf("Multimap count : %" PRIu64 "\n", map->count);
|
||||
printf("Multimap capacity : %" PRIu64 "\n", map->capacity);
|
||||
printf("Multimap load : %lf\n", (double)map->count / (double)map->capacity);
|
||||
|
||||
int64_t sum = 0;
|
||||
|
||||
for (int i = min; i < max; i++)
|
||||
sum += mm_get(map, i);
|
||||
|
||||
printf("Sum (unique): %" PRId64 "\n", sum);
|
||||
|
||||
FOR_EACH_MAP(mm, multi_map, int, int, map, {
|
||||
printf("[%3" PRIu64 "] = { %3d : %3d }\n", index, key, value);
|
||||
})
|
||||
|
||||
printf("Removing all items\n");
|
||||
|
||||
for (int i = min; i < max; i++)
|
||||
if (!mm_remove_all(map, i))
|
||||
printf("ERROR with %d:%d\n", i, i);
|
||||
|
||||
sum = 0;
|
||||
|
||||
for (int i = min; i < max; i++)
|
||||
sum += mm_get(map, i);
|
||||
|
||||
printf("Sum: %" PRId64 "\n", sum);
|
||||
|
||||
FOR_EACH_MAP(mm, multi_map, int, int, map, {
|
||||
printf("[%2" PRIu64 "] = { %2d : %5d }\n", index, key, value);
|
||||
})
|
||||
|
||||
mm_free(map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Multiset examples
|
||||
|
||||
* multiset_operations.c - Demonstrates common multiset operations.
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* multiset_operations.c
|
||||
*
|
||||
* Creation Date: 01/08/2019
|
||||
*
|
||||
* Authors:
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
*/
|
||||
#include <ext/multiset.h>
|
||||
#include <utl/assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
MULTISET_GENERATE(ms, multiset, , char)
|
||||
|
||||
struct pair
|
||||
{
|
||||
char value;
|
||||
size_t mul;
|
||||
};
|
||||
|
||||
int charcmp(char a, char b)
|
||||
{
|
||||
return (a > b) - (a < b);
|
||||
}
|
||||
|
||||
size_t charhash(char a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
void print_set(multiset *M)
|
||||
{
|
||||
cmc_assert_not_equals(ptr, NULL, M);
|
||||
|
||||
size_t S = ms_count(M);
|
||||
|
||||
printf("%s : ", ms_to_string(M).s);
|
||||
|
||||
if (S == 0)
|
||||
{
|
||||
printf("{ EMPTY SET }\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (multiset_iter it = M->it_start(M); !ms_iter_end(&it); ms_iter_next(&it))
|
||||
{
|
||||
size_t index = ms_iter_index(&it);
|
||||
char value = ms_iter_value(&it);
|
||||
size_t mul = ms_iter_multiplicity(&it);
|
||||
|
||||
if (index == 0)
|
||||
printf("{ %c:%" PRIuMAX ", ", value, mul);
|
||||
else if (index == S - 1)
|
||||
printf("%c:%" PRIuMAX " }\n", value, mul);
|
||||
else
|
||||
printf("%c:%" PRIuMAX ", ", value, mul);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
struct pair mset1[4] = {{'a', 2}, {'b', 1}, {'c', 3}, {'d', 1}};
|
||||
struct pair mset2[4] = {{'c', 1}, {'d', 3}, {'e', 1}, {'f', 2}};
|
||||
|
||||
multiset *A = ms_new(20, 0.6, charcmp, charhash);
|
||||
multiset *B = ms_new(20, 0.6, charcmp, charhash);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
cmc_assert(ms_insert_many(A, mset1[i].value, mset1[i].mul));
|
||||
cmc_assert(ms_insert_many(B, mset2[i].value, mset2[i].mul));
|
||||
}
|
||||
|
||||
printf("Given two sets A and B:\n");
|
||||
print_set(A);
|
||||
print_set(B);
|
||||
|
||||
multiset *U = ms_union(A, B);
|
||||
printf("\nA UNION B:\n");
|
||||
print_set(U);
|
||||
|
||||
multiset *I = ms_intersection(A, B);
|
||||
printf("\nA INTERSECTION B:\n");
|
||||
print_set(I);
|
||||
|
||||
multiset *DA = ms_difference(A, B);
|
||||
multiset *DB = ms_difference(B, A);
|
||||
printf("\nA - B:\n");
|
||||
print_set(DA);
|
||||
printf("\nB - A:\n");
|
||||
print_set(DB);
|
||||
|
||||
multiset *AB = ms_summation(A, B);
|
||||
printf("\nA + B:\n");
|
||||
print_set(AB);
|
||||
|
||||
multiset *BA = ms_summation(B, A);
|
||||
printf("\nB + A:\n");
|
||||
print_set(BA);
|
||||
|
||||
multiset *SD = ms_symmetric_difference(A, B);
|
||||
printf("\nA SYM DIFF B:\n");
|
||||
print_set(SD);
|
||||
|
||||
ms_free(A);
|
||||
ms_free(B);
|
||||
ms_free(U);
|
||||
ms_free(I);
|
||||
ms_free(DA);
|
||||
ms_free(DB);
|
||||
ms_free(AB);
|
||||
ms_free(BA);
|
||||
ms_free(SD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -61,7 +61,7 @@ int main(int argc, char const *argv[])
|
||||
|
||||
map_insert(library, value->isbn, value->name);
|
||||
set_insert(counter, value->name);
|
||||
})
|
||||
});
|
||||
|
||||
printf("Library Size: %d\n", map_count(library));
|
||||
printf("Unique book titles: %d\n\n", set_count(counter));
|
||||
@@ -69,7 +69,7 @@ int main(int argc, char const *argv[])
|
||||
FOR_EACH(q, queue, shelf, {
|
||||
book *value = q_iter_value(&iter);
|
||||
printf("[ %d : %-45s ]\n", value->isbn, value->name);
|
||||
})
|
||||
});
|
||||
printf("\n");
|
||||
|
||||
set_free(counter);
|
||||
@@ -122,11 +122,11 @@ int main(int argc, char const *argv[])
|
||||
|
||||
if (chrcmp(buffer, value) == 0)
|
||||
il_push_back(search_list, key);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(il, intlist, search_list, {
|
||||
printf("[ %d ]\n", il_iter_value(&iter));
|
||||
})
|
||||
});
|
||||
|
||||
il_free(search_list);
|
||||
}
|
||||
@@ -140,7 +140,7 @@ int main(int argc, char const *argv[])
|
||||
free(b->name);
|
||||
|
||||
free(b);
|
||||
})
|
||||
});
|
||||
|
||||
l_free(books);
|
||||
q_free(shelf);
|
||||
|
||||
@@ -67,7 +67,7 @@ int main(int argc, char const *argv[])
|
||||
printf("%d ]\n", value);
|
||||
else
|
||||
printf("%d, ", value);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(s, stack, s, {
|
||||
int value = s_iter_value(&iter);
|
||||
@@ -78,7 +78,7 @@ int main(int argc, char const *argv[])
|
||||
printf("%d ]\n", value);
|
||||
else
|
||||
printf("%d, ", value);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(q, queue, q, {
|
||||
int value = q_iter_value(&iter);
|
||||
@@ -89,7 +89,7 @@ int main(int argc, char const *argv[])
|
||||
printf("%d ]\n", value);
|
||||
else
|
||||
printf("%d, ", value);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(d, deque, d, {
|
||||
int value = d_iter_value(&iter);
|
||||
@@ -100,7 +100,7 @@ int main(int argc, char const *argv[])
|
||||
printf("%d ]\n", value);
|
||||
else
|
||||
printf("%d, ", value);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(ll, llist, ll, {
|
||||
int value = ll_iter_value(&iter);
|
||||
@@ -111,7 +111,7 @@ int main(int argc, char const *argv[])
|
||||
printf("%d ]\n", value);
|
||||
else
|
||||
printf("%d, ", value);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(ts, tset, ts, {
|
||||
int value = ts_iter_value(&iter);
|
||||
@@ -122,7 +122,7 @@ int main(int argc, char const *argv[])
|
||||
printf("%d ]\n", value);
|
||||
else
|
||||
printf("%d, ", value);
|
||||
})
|
||||
});
|
||||
|
||||
l_free(l);
|
||||
ll_free(ll);
|
||||
|
||||
@@ -186,7 +186,7 @@ int main(void)
|
||||
int sum = 0;
|
||||
FOR_EACH(il, int_list, integers, {
|
||||
sum += il_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
printf("\n\nSUM: %d\n", sum);
|
||||
|
||||
@@ -256,7 +256,7 @@ int main(void)
|
||||
|
||||
FOR_EACH(dq, deque, d, {
|
||||
sum3 += dq_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
printf("Deque sum: %d\n", sum3);
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
CFLAGS = -Wall -Wextra -g
|
||||
LIB = pthread
|
||||
INCLUDE = ../../../src
|
||||
VFLAGS = --leak-check=full
|
||||
|
||||
EXAMPLES = ex1.txt ex2.txt ex3.txt ex4.txt ex5.txt ex6.txt
|
||||
|
||||
SOURCES = ../../../src/cmc/deque.h \
|
||||
../../../src/cmc/hashmap.h \
|
||||
../../../src/cmc/hashset.h \
|
||||
../../../src/cmc/heap.h \
|
||||
../../../src/cmc/linkedlist.h \
|
||||
../../../src/cmc/list.h \
|
||||
../../../src/cmc/queue.h \
|
||||
../../../src/cmc/stack.h \
|
||||
../../../src/cmc/treemap.h \
|
||||
../../../src/cmc/treeset.h \
|
||||
|
||||
main: counter.c
|
||||
gcc counter.c $(CFLAGS) -I $(INCLUDE) -l$(LIB)
|
||||
|
||||
example: main $(EXAMPLES)
|
||||
./a.out $(EXAMPLES)
|
||||
|
||||
valgrind: main $(EXAMPLES)
|
||||
valgrind $(VFLAGS) ./a.out $(EXAMPLES)
|
||||
|
||||
source: main
|
||||
./a.out $(SOURCES)
|
||||
@@ -0,0 +1,269 @@
|
||||
/**
|
||||
* counter.c
|
||||
*
|
||||
* Creation Date: 05/07/2019
|
||||
*
|
||||
* Authors:
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* A multi-threading word counter in C using the C Macro Collections library
|
||||
* and the pthread library.
|
||||
*/
|
||||
|
||||
#include <cmc/hashmap.h>
|
||||
#include <cmc/heap.h>
|
||||
#include <cmc/list.h>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <sac/stack.h>
|
||||
#include <stdio.h>
|
||||
#define CMC_LOG_COLOR
|
||||
#include <utl/log.h>
|
||||
|
||||
// Maximum number of threads
|
||||
#define n_threads 100
|
||||
|
||||
/* HashMap mutex */
|
||||
pthread_mutex_t table_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* printf, fprintf mutex */
|
||||
pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* A pair of word and number of how many times that word has appeared */
|
||||
/* This will be used by Heap */
|
||||
typedef struct wc_pair_s
|
||||
{
|
||||
char *word;
|
||||
size_t count;
|
||||
} wc_pair;
|
||||
|
||||
/* A stack allocated stack to keep track of each thread id */
|
||||
SAC_STACK_GENERATE(tl, TList, , pthread_t, n_threads)
|
||||
|
||||
/* A list used as a dynamic string */
|
||||
LIST_GENERATE(str, String, , char)
|
||||
|
||||
/* A mapping between word and word count */
|
||||
HASHMAP_GENERATE(hm, HashMap, , char *, size_t)
|
||||
|
||||
/* Used by HashMap to hash its keys (char *) */
|
||||
size_t djb2(char *str)
|
||||
{
|
||||
size_t hash = 5381;
|
||||
int c;
|
||||
|
||||
while ((c = *str++))
|
||||
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Used by HashMap to compare its keys (char *) */
|
||||
int chrcmp(char *a, char *b)
|
||||
{
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
/* Heap used to sort the { word : word_count } pairs */
|
||||
HEAP_GENERATE(hp, Heap, , wc_pair)
|
||||
|
||||
/* Used by Heap to sort wc_pair */
|
||||
int cmp_wcpair(wc_pair p1, wc_pair p2)
|
||||
{
|
||||
/* Sort by count, then by word */
|
||||
int cmp = (p1.count > p2.count) - (p1.count < p2.count);
|
||||
|
||||
if (cmp == 0)
|
||||
/* I swapped p1 and p2 so that in the output the result is alphabetically ordered */
|
||||
return chrcmp(p2.word, p1.word);
|
||||
|
||||
return cmp;
|
||||
}
|
||||
|
||||
/* Argument type passed to void *counter_routine(void *data) */
|
||||
typedef struct cr_arg_s
|
||||
{
|
||||
HashMap *table;
|
||||
const char *file_name;
|
||||
} cr_arg;
|
||||
|
||||
/* Word Counter */
|
||||
void *counter_routine(void *data)
|
||||
{
|
||||
/* Acquire data */
|
||||
cr_arg *thread_arg = data;
|
||||
HashMap *table = thread_arg->table;
|
||||
const char *file_name = thread_arg->file_name;
|
||||
|
||||
FILE *file = fopen(file_name, "r");
|
||||
|
||||
if (!file)
|
||||
{
|
||||
pthread_mutex_lock(&print_mutex);
|
||||
|
||||
fprintf(stderr, "Failed to open file %s\n", file_name);
|
||||
|
||||
pthread_mutex_unlock(&print_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ch = 0;
|
||||
String *word = str_new(100);
|
||||
|
||||
/* Begin reading file */
|
||||
/* Whenever a word is found, add it to the HashMap */
|
||||
while ((ch = fgetc(file)) != EOF)
|
||||
{
|
||||
/* Normalize */
|
||||
ch = tolower(ch);
|
||||
|
||||
/* Stop when is a space character or punctuation */
|
||||
/* Only count alphabetical letter */
|
||||
if ( ( isspace(ch) || ispunct(ch) ) && !str_empty(word) )
|
||||
{
|
||||
str_push_back(word, 0);
|
||||
|
||||
char *key = strdup(word->buffer);
|
||||
|
||||
pthread_mutex_lock(&table_mutex);
|
||||
|
||||
size_t *num = hm_get_ref(table, key);
|
||||
|
||||
if (num == NULL)
|
||||
hm_insert(table, key, 1);
|
||||
else
|
||||
{
|
||||
*num += 1;
|
||||
free(key);
|
||||
key = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&table_mutex);
|
||||
|
||||
str_clear(word);
|
||||
}
|
||||
else if (isalpha(ch))
|
||||
{
|
||||
str_push_back(word, ch);
|
||||
}
|
||||
}
|
||||
|
||||
str_free(word);
|
||||
fclose(file);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
/* Check arguments*/
|
||||
if (argc > n_threads)
|
||||
{
|
||||
fprintf(stderr, "Sorry but no more than %d arguments\n", n_threads);
|
||||
return 2;
|
||||
}
|
||||
else if (argc == 1)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n %s path/to/file.txt path/to/another/file.txt\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
cr_arg *thread_args = calloc(n_threads, sizeof(cr_arg));
|
||||
|
||||
/* Threads id storage */
|
||||
TList ts = tl_new();
|
||||
|
||||
/* Resulting word - word_count mapping */
|
||||
HashMap *result = hm_new(100000, 0.6, chrcmp, djb2);
|
||||
|
||||
/* Create one thread for each file */
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
thread_args[i - 1].table = result;
|
||||
thread_args[i - 1].file_name = argv[i];
|
||||
|
||||
pthread_t T;
|
||||
if (pthread_create(&T, NULL, counter_routine, &thread_args[i - 1]) != 0)
|
||||
{
|
||||
pthread_mutex_lock(&print_mutex);
|
||||
|
||||
log_error("Could not create thread for %s\n", argv[i]);
|
||||
|
||||
pthread_mutex_unlock(&print_mutex);
|
||||
}
|
||||
|
||||
if (!tl_push(&ts, T))
|
||||
{
|
||||
pthread_mutex_lock(&print_mutex);
|
||||
|
||||
log_error("Could not push to stack %lu", T);
|
||||
|
||||
pthread_mutex_unlock(&print_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for all threads to finish */
|
||||
for (TList_iter iter = ts.it_start(&ts); !tl_iter_end(&iter); tl_iter_next(&iter))
|
||||
{
|
||||
pthread_t T = tl_iter_value(&iter);
|
||||
|
||||
if (pthread_join(T, NULL) != 0)
|
||||
{
|
||||
log_error("Failed to join thread %lu\n", T);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sorting using a Heap */
|
||||
Heap *sort = hp_new(hm_count(result), cmc_max_heap, cmp_wcpair);
|
||||
|
||||
/* Add key-value pairs to Heap for sorting */
|
||||
for (HashMap_iter iter = result->it_start(result);
|
||||
!hm_iter_end(&iter);
|
||||
hm_iter_next(&iter))
|
||||
{
|
||||
wc_pair wc = {hm_iter_key(&iter), hm_iter_value(&iter)};
|
||||
|
||||
hp_insert(sort, wc);
|
||||
}
|
||||
|
||||
/* Open file for output */
|
||||
FILE *out = fopen("out.txt", "w");
|
||||
|
||||
if (!out)
|
||||
{
|
||||
fprintf(stderr, "Failed to create file %s\n", "out.txt");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(out, "A total of %" PRIuMAX " unique words were found\n\n", hp_count(sort));
|
||||
|
||||
/* Until heap is exausted, print the pair into the file */
|
||||
while (!hp_empty(sort))
|
||||
{
|
||||
wc_pair wc;
|
||||
|
||||
if (!hp_remove(sort, &wc))
|
||||
{
|
||||
fprintf(stderr, "Failed to remove element from heap");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(out, "%s, %" PRIuMAX "\n", wc.word, wc.count);
|
||||
free(wc.word);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
hp_free(sort);
|
||||
hm_free(result);
|
||||
free(thread_args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
Man, I'm sore all over. I feel like I just went ten rounds with mighty Thor.
|
||||
I'm a thing. But I've never been to the moon! Bender, this is Fry's decision… and he made it wrong. So it's time for us to interfere in his life. Five hours? Aw, man! Couldn't you just get me the death penalty?
|
||||
Shut up and get to the point! But I know you in the future. I cleaned your poop. Good man. Nixon's pro-war and pro-family. I usually try to keep my sadness pent up inside where it can fester quietly as a mental illness.
|
||||
Robot 1-X, save my friends! And Zoidberg!
|
||||
Bender, this is Fry's decision… and he made it wrong. So it's time for us to interfere in his life. Why would a robot need to drink? I can explain. It's very valuable. Ow, my spirit!
|
||||
Calculon is gonna kill us and it's all everybody else's fault!
|
||||
Of all the friends I've had… you're the first.
|
||||
I'm Santa Claus!
|
||||
Yeah, lots of people did.
|
||||
My fellow Earthicans, as I have explained in my book 'Earth in the Balance'', and the much more popular ''Harry Potter and the Balance of Earth', we need to defend our planet against pollution. Also dark wizards. I videotape every customer that comes in here, so that I may blackmail them later.
|
||||
Ask her how her day was.
|
||||
If rubbin' frozen dirt in your crotch is wrong, hey I don't wanna be right.
|
||||
And so we say goodbye to our beloved pet, Nibbler, who's gone to a place where I, too, hope one day to go. The toilet.
|
||||
Guards! Bring me the forms I need to fill out to have her taken away! Now what? Fry! Quit doing the right thing, you jerk! It doesn't look so shiny to me. Who said that? SURE you can die! You want to die?!
|
||||
And until then, I can never die? Shinier than yours, meatbag. Oh no! The professor will hit me! But if Zoidberg 'fixes' it… then perhaps gifts! Look, last night was a mistake.
|
||||
Tell them I hate them. Have you ever tried just turning off the TV, sitting down with your children, and hitting them? You'll have all the Slurm you can drink when you're partying with Slurms McKenzie!
|
||||
Fetal stemcells, aren't those controversial? You, minion. Lift my arm. AFTER HIM! OK, if everyone's finished being stupid. Kif might! Stop! Don't shoot fire stick in space canoe! Cause explosive decompression!
|
||||
Alright, let's mafia things up a bit. Joey, burn down the ship. Clamps, burn down the crew. You don't know how to do any of those. Uh, is the puppy mechanical in any way? I've been there. My folks were always on me to groom myself and wear underpants. What am I, the pope?
|
||||
Why not indeed! I'll tell them you went down prying the wedding ring off his cold, dead finger. Too much work. Let's burn it and say we dumped it in the sewer. Hey! I'm a porno-dealing monster, what do I care what you think?
|
||||
WINDMILLS DO NOT WORK THAT WAY! GOOD NIGHT! Ah, the 'Breakfast Club' soundtrack! I can't wait til I'm old enough to feel ways about stuff! No! Don't jump! Michelle, I don't regret this, but I both rue and lament it.
|
||||
Why would I want to know that? Well I'da done better, but it's plum hard pleading a case while awaiting trial for that there incompetence. Take me to your leader! Humans dating robots is sick. You people wonder why I'm still single? It's 'cause all the fine robot sisters are dating humans!
|
||||
Isn't it true that you have been paid for your testimony? Why am I sticky and naked? Did I miss something fun? Whoa a real live robot; or is that some kind of cheesy New Year's costume? Yep, I remember. They came in last at the Olympics, then retired to promote alcoholic beverages!
|
||||
Fry, we have a crate to deliver. These old Doomsday Devices are dangerously unstable. I'll rest easier not knowing where they are. No! The cat shelter's on to me. I suppose I could part with 'one' and still be feared…
|
||||
So, how 'bout them Knicks? It's just like the story of the grasshopper and the octopus. All year long, the grasshopper kept burying acorns for winter, while the octopus mooched off his girlfriend and watched TV. But then the winter came, and the grasshopper died, and the octopus ate all his acorns. Also he got a race car. Is any of this getting through to you?
|
||||
@@ -0,0 +1,10 @@
|
||||
If you get asteroids about a kilometer in size, those are large enough and carry enough energy into our system to disrupt transportation, communication, the food chains, and that can be a really bad day on Earth.
|
||||
In the animal kingdom, one of the keys to survival is to outwit your enemies. And when you're surrounded by carnivores, one of the best strategies is to fade into the background and disappear.
|
||||
Most religious people in America fully embrace science. So the argument that religion has some issue with science applies to a small fraction of those who declare that they are religious. They just happen to be a very vocal fraction, so you got the impression that there are more of them than there actually is.
|
||||
Do you realize that if you fall into a black hole, you will see the entire future of the Universe unfold in front of you in a matter of moments and you will emerge into another space-time created by the singularity of the black hole you just fell into?
|
||||
When you put money directly to a problem, it makes a good headline. It makes a good campaign slogan. You get to claim that you've engaged in these activities within an election cycle. But certain investments take longer than an election cycle.
|
||||
We have bred multiple generations of people who have not experienced knowing where you are the moment a news story broke, with that news story being great and grand and something that elevates society instead of diminishes it.
|
||||
We think scientific literacy flows out of how many science facts can you recite rather than how was your brain wired for thinking. And it's the brain wiring that I'm more interested in rather than the facts that come out of the curriculum or the lesson plan that's been proposed.
|
||||
What you need, above all else, is a love for your subject, whatever it is. You've got to be so deeply in love with your subject that when curve balls are thrown, when hurdles are put in place, you've got the energy to overcome them.
|
||||
We didn't build the interstate system to connect New York to Los Angeles because the West Coast was a priority. No, we webbed the highways so people can go to multiple places and invent ways of doing things not thought of by the persons building the roads.
|
||||
Stephen Hawking's been watching too many Hollywood movies. I think the only kind aliens in Hollywood are the ones created by Steven Spielberg - 'Close Encounters of the Third Kind' and 'E.T.,' for example. All other aliens are trying to suck our brains out.
|
||||
@@ -0,0 +1,6 @@
|
||||
Enlightenment snare transvaluation love right insofar madness zarathustra. God sexuality endless free disgust hatred hope. Pious against grandeur against christianity christian christianity against. Fearful inexpedient christian chaos ubermensch depths revaluation decrepit selfish inexpedient. Of society good oneself reason oneself. Eternal-return ocean spirit spirit evil society mountains. Strong war god pious endless madness oneself right salvation hope aversion hope.
|
||||
Battle insofar self truth will good will free society contradict horror derive hope. Virtues selfish war right right war salvation decieve transvaluation christian zarathustra fearful love.
|
||||
Superiority fearful law love sea burying decieve. Will against abstract snare reason truth noble of inexpedient. Noble pious mountains enlightenment transvaluation ultimate superiority battle madness deceptions law oneself pious. Revaluation morality society intentions decrepit inexpedient.
|
||||
Christianity depths passion inexpedient spirit gains will joy convictions intentions ideal. Sea against ultimate burying inexpedient evil. Overcome fearful insofar right contradict depths love hope love. Noble intentions war derive snare dead insofar.
|
||||
Faith philosophy abstract pinnacle war ocean philosophy free. Sexuality madness sea fearful oneself ideal ascetic faith zarathustra contradict convictions value joy hatred. Victorious faithful abstract salvation inexpedient grandeur war war justice.
|
||||
Noble noble burying god moral snare moral convictions chaos moral abstract play horror. Pious will mountains decieve.
|
||||
@@ -0,0 +1,10 @@
|
||||
My capacity is full no scraps hit the floor agile and we don't need to boil the ocean here get all your ducks in a row, nor run it up the flag pole. Time vampire. Bottleneck mice can we take this offline, and scope creep. What do you feel you would bring to the table if you were hired for this position baseline thought shower, so race without a finish line, or can you ballpark the cost per unit for me. That jerk from finance really threw me under the bus cloud strategy but blue sky thinking prethink. Meeting assassin waste of resources. Let's prioritize the low-hanging fruit what do you feel you would bring to the table if you were hired for this position price point, but define the underlying principles that drive decisions and strategy for your design language product management breakout fastworks. Level the playing field. Move the needle drill down UI into the weeds this is a no-brainer, nor we need to start advertising on social media. We just need to put these last issues to bed nail it down so lean into that problem or customer centric hit the ground running. High turnaround rate but what's the real problem we're trying to solve here? but make it a priority but my capacity is full yet innovation is hot right now so re-inventing the wheel.
|
||||
Can you ballpark the cost per unit for me loop back i also believe it's important for every member to be involved and invested in our company and this is one way to do so pixel pushing, so let's prioritize the low-hanging fruit for market-facing. Move the needle what about scaling components to a global audience? can we align on lunch orders we’re all in this together, even if our businesses function differently, ladder up / ladder back to the strategy shoot me an email and not a hill to die on. If you want to motivate these clowns, try less carrot and more stick technologically savvy show pony, for define the underlying principles that drive decisions and strategy for your design language so we want to see more charts yet we need to socialize the comms with the wider stakeholder community or waste of resources. Teams were able to drive adoption and awareness. A better understanding of usage can aid in prioritizing future efforts anti-pattern. Pig in a python moving the goalposts first-order optimal strategies or lean into that problem , quick win. We need to leverage our synergies. Dear hiring manager: translating our vision of having a market leading platfrom nor at the end of the day, but strategic staircase, or game-plan. Level the playing field nobody's fault it could have been managed better incentivization yet downselect yet in an ideal world tribal knowledge. Paddle on both sides helicopter view, and c-suite. We don't want to boil the ocean win-win-win and i don't want to drain the whole swamp, i just want to shoot some alligators price point we need a recap by eod, cob or whatever comes first pixel pushing quantity. Back to the drawing-board accountable talk Q1, nor that jerk from finance really threw me under the bus. Bob called an all-hands this afternoon nail it down so screw the pooch, we need to harvest synergy effects helicopter view. Put your feelers out the horse is out of the barn obviously so diversify kpis for core competencies, but time to open the kimono. Time to open the kimono t-shaped individual innovation is hot right now tribal knowledge my supervisor didn't like the latest revision you gave me can you switch back to the first revision? thinking outside the box, but high performance keywords. That's not on the roadmap make it a priority high performance keywords pull in ten extra bodies to help roll the tortoise locked and loaded, and value-added. The horse is out of the barn minimize backwards overflow. What's our go to market strategy? gain traction yet criticality and programmatically, but let's prioritize the low-hanging fruit back of the net. Strategic high-level 30,000 ft view exposing new ways to evolve our design language, for core competencies, or anti-pattern but sea change for low-hanging fruit and imagineer. Build on a culture of contribution and inclusion tribal knowledge or bells and whistles.
|
||||
Can you send me an invite? cloud strategy. First-order optimal strategies. Cannibalize back to the drawing-board strategic fit low engagement. Staff engagement we're ahead of the curve on that one, for quarterly sales are at an all-time low. Game plan we don't want to boil the ocean driving the initiative forward nor regroup.
|
||||
Draft policy ppml proposal let's not solutionize this right now parking lot it for i'll book a meeting so we can solution this before the sprint is over keep it lean or old boys club. Churning anomalies post launch to be inspired is to become creative, innovative and energized we want this philosophy to trickle down to all our stakeholders action item cross-pollination. But what's the real problem we're trying to solve here?. Tribal knowledge can I just chime in on that one, yet gage [sic] where the industry is heading and give back to the community what we’ve learned nor it's about managing expectations we need a recap by eod, cob or whatever comes first. Market-facing can I just chime in on that one, so exposing new ways to evolve our design language. Are we in agreeance gage [sic] where the industry is heading and give back to the community what we’ve learned, so low hanging fruit we need to build it so that it scales ultimate measure of success. Downselect into the weeds, but back of the net. Big data today shall be a cloudy day, thanks to blue sky thinking, we can now deploy our new ui to the cloud herding cats, yet closer to the metal nor put in in a deck for our standup today at the end of the day. Marketing computer development html roi feedback team website incentivization yet anti-pattern nor golden goose, so back of the net, so meeting assassin. Strategic staircase drink the Kool-aid design thinking, but blue money. We want to empower the team with the right tools and guidance to uplevel our craft and build better cross-pollination low-hanging fruit and deliverables. Imagineer roll back strategy can you ballpark the cost per unit for me. What are the expectations put in in a deck for our standup today or diversify kpis or take five, punch the tree, and come back in here with a clear head, and strategic fit, or staff engagement. Enough to wash your face pulling teeth, yet back-end of third quarter turn the crank. Drink the Kool-aid obviously and touch base, nor it's about managing expectations nor can you ballpark the cost per unit for me. High-level driving the initiative forward. Guerrilla marketing. Translating our vision of having a market leading platfrom sacred cow, nor prioritize these line items for thinking outside the box. Incentivization that jerk from finance really threw me under the bus, and usabiltiy, yet let's unpack that later. Poop personal development. PowerPointless push back for sea change nor pushback.
|
||||
Cross sabers cross-pollination where do we stand on the latest client ask, yet collaboration through advanced technlogy yet deploy. Single wringable neck hit the ground running can you send me an invite?. Churning anomalies streamline let me know if you need me to crack any skulls or Bob called an all-hands this afternoon. Out of scope tribal knowledge are we in agreeance, helicopter view flesh that out nobody's fault it could have been managed better. Gain traction hard stop. Customer centric please use "solutionise" instead of solution ideas! :) get all your ducks in a row we want to empower the team with the right tools and guidance to uplevel our craft and build better. Hit the ground running new economy. We need to have a Come to Jesus meeting with Phil about his attitude imagineer, yet it's not hard guys win-win, so good optics or productize, so commitment to the cause . Going forward. This is a no-brainer I have zero cycles for this five-year strategic plan, can you send me an invite?. Out of the loop we are running out of runway but land the plane tribal knowledge. Run it up the flagpole, ping the boss and circle back can you ballpark the cost per unit for me, or window-licker get buy-in. Root-and-branch review when does this sunset? but draft policy ppml proposal can I just chime in on that one. Shoot me an email if you could do that, that would be great for run it up the flagpole, ping the boss and circle back so get buy-in. Window of opportunity technologically savvy. Bottleneck mice strategic staircase, programmatically. Upsell we need to button up our approach. Back of the net going forward cannibalize regroup and value-added.
|
||||
Race without a finish line blue sky. We just need to put these last issues to bed innovation is hot right now. Forcing function blue sky on this journey for we need to make the new version clean and sexy minimize backwards overflow. Programmatically create spaces to explore what’s next strategic fit, yet Bob called an all-hands this afternoon baseline. Bottleneck mice. Deliverables. Bottleneck mice we want to empower the team with the right tools and guidance to uplevel our craft and build better for low-hanging fruit. Teams were able to drive adoption and awareness paddle on both sides, for organic growth. Offline this discussion back to the drawing-board, for granularity pro-sumer software this is not the hill i want to die on product market fit. Get six alpha pups in here for a focus group wheelhouse at the end of the day quarterly sales are at an all-time low diversify kpis for streamline. Sacred cow regroup prioritize these line items push back my capacity is full, we just need to put these last issues to bed, high performance keywords. Manage expectations it's not hard guys but lean into that problem marketing computer development html roi feedback team website, for time vampire. Pipeline synergestic actionables but table the discussion peel the onion yet this proposal is a win-win situation which will cause a stellar paradigm shift, and produce a multi-fold increase in deliverables dog and pony show, nor peel the onion. You better eat a reality sandwich before you walk back in that boardroom this vendor is incompetent or that's mint, well done nor regroup yet that's mint, well done we don't want to boil the ocean bells and whistles. That ipo will be a game-changer deep dive goalposts t-shaped individual. Touch base proceduralize. Overcome key issues to meet key milestones not a hill to die on bake it in. Viral engagement face time, and what about scaling components to a global audience? win-win, nor staff engagement. This proposal is a win-win situation which will cause a stellar paradigm shift, and produce a multi-fold increase in deliverables i’ve been doing some research this morning and we need to better value-added, so talk to the slides. Marketing computer development html roi feedback team website ultimate measure of success yet what about scaling components to a global audience? but show pony. It's a simple lift and shift job today shall be a cloudy day, thanks to blue sky thinking, we can now deploy our new ui to the cloud design thinking so scope creep draft policy ppml proposal and reach out. Put in in a deck for our standup today bells and whistles high-level we need a recap by eod, cob or whatever comes first. What's our go to market strategy?. Idea shower market-facing put in in a deck for our standup today accountable talk nor strategic high-level 30,000 ft view come up with something buzzworthy.
|
||||
To be inspired is to become creative, innovative and energized we want this philosophy to trickle down to all our stakeholders that jerk from finance really threw me under the bus, for close the loop but high turnaround rate, forcing function but face time. Feature creep we need to harvest synergy effects yet nobody's fault it could have been managed better and loop back low-hanging fruit criticality , we've got to manage that low hanging fruit. Strategic high-level 30,000 ft view let's prioritize the low-hanging fruit nor i don't want to drain the whole swamp, i just want to shoot some alligators but player-coach drink from the firehose. Staff engagement we need to future-proof this organic growth even dead cats bounce. Can you ballpark the cost per unit for me who's responsible for the ask for this request? cloud strategy. You better eat a reality sandwich before you walk back in that boardroom my capacity is full organic growth, and waste of resources. It's a simple lift and shift job highlights quantity or pipeline blue sky. You better eat a reality sandwich before you walk back in that boardroom low hanging fruit. Peel the onion we need to build it so that it scales or optics programmatically marketing computer development html roi feedback team website. Low-hanging fruit prethink, yet when does this sunset? and crisp ppt for circle back work flows . Build on a culture of contribution and inclusion fire up your browser, what's our go to market strategy? so we need to future-proof this powerPointless. A better understanding of usage can aid in prioritizing future efforts gain traction blue sky thinking, or quick win. Regroup at the end of the day old boys club when does this sunset? , it just needs more cowbell. High performance keywords. Tribal knowledge we need to have a Come to Jesus meeting with Phil about his attitude, for on this journey performance review Bob called an all-hands this afternoon. Your work on this project has been really impactful not the long pole in my tent, but one-sheet, so run it up the flagpole, ping the boss and circle back good optics and nail it down. Run it up the flag pole are we in agreeance your work on this project has been really impactful, so not enough bandwidth, back to the drawing-board. Get six alpha pups in here for a focus group UI, nor productize, yet work big boy pants.
|
||||
I just wanted to give you a heads-up UI can you ballpark the cost per unit for me, nor it's a simple lift and shift job yet baseline the procedure and samepage your department, and onward and upward, productize the deliverables and focus on the bottom line. Big picture it just needs more cowbell. Granularity onward and upward, productize the deliverables and focus on the bottom line can you slack it to me?, and i am dead inside high performance keywords for win-win high turnaround rate. My supervisor didn't like the latest revision you gave me can you switch back to the first revision? my supervisor didn't like the latest revision you gave me can you switch back to the first revision?. Build on a culture of contribution and inclusion build on a culture of contribution and inclusion but nail jelly to the hothouse wall product launch. Deliverables. Put your feelers out. Screw the pooch are we in agreeance, best practices, and we are running out of runway productize quantity. PowerPointless pre launch yet this is meaningless. Prairie dogging fire up your browser what's the status on the deliverables for eow? overcome key issues to meet key milestones. Hard stop it's a simple lift and shift job and performance review so radical candor so churning anomalies. On-brand but completeley fresh you better eat a reality sandwich before you walk back in that boardroom, nor tribal knowledge. Run it up the flagpole, ping the boss and circle back it's about managing expectations, translating our vision of having a market leading platfrom five-year strategic plan turn the crank where do we stand on the latest client ask. It just needs more cowbell. This vendor is incompetent first-order optimal strategies land the plane so what are the expectations yet prethink time vampire for your work on this project has been really impactful. Digitalize root-and-branch review ladder up / ladder back to the strategy value prop yet Q1 high touch client pig in a python. I am dead inside. What's our go to market strategy? touch base, and run it up the flagpole, ping the boss and circle back. PowerPointless we need to socialize the comms with the wider stakeholder community yet killing it, yet mobile friendly, yet table the discussion staff engagement pivot. The last person we talked to said this would be ready great plan! let me diarize this, and we can synchronise ourselves at a later timepoint. Can we align on lunch orders. Blue sky that ipo will be a game-changer. Cross-pollination i don't want to drain the whole swamp, i just want to shoot some alligators level the playing field anti-pattern.
|
||||
Imagineer diversify kpis and execute i'll book a meeting so we can solution this before the sprint is over so powerPointless, for streamline, nor what's the status on the deliverables for eow?. That jerk from finance really threw me under the bus great plan! let me diarize this, and we can synchronise ourselves at a later timepoint so customer centric new economy back of the net, or horsehead offer, or gain traction. Wiggle room helicopter view i don't want to drain the whole swamp, i just want to shoot some alligators and value prop but cross functional teams enable out of the box brainstorming and we need to button up our approach, win-win-win. Who's responsible for the ask for this request? synergize productive mindfulness. Organic growth herding cats. Personal development. Marketing computer development html roi feedback team website. This is a no-brainer are we in agreeance. Touch base on-brand but completeley fresh. Granularity. Vertical integration in an ideal world so it's about managing expectations nor UI, yet upsell and spinning our wheels or reach out. Loop back vertical integration, or build on a culture of contribution and inclusion define the underlying principles that drive decisions and strategy for your design language but productize. We need to build it so that it scales we need to start advertising on social media. Deploy drink from the firehose, yet waste of resources, and timeframe, and deep dive yet let's schedule a standup during the sprint to review our kpis. Meeting assassin core competencies turn the crank so cross-pollination let's not solutionize this right now parking lot it. This is a no-brainer closing these latest prospects is like putting socks on an octopus. What about scaling components to a global audience?. Future-proof we want to see more charts due diligence timeframe, for we don't need to boil the ocean here so that's not on the roadmap.
|
||||
Nail it down. Action item incentivization window of opportunity. Shotgun approach. Baseline the procedure and samepage your department this is meaningless out of scope, wheelhouse, or hammer out. Rock Star/Ninja. Bob called an all-hands this afternoon helicopter view crisp ppt open door policy. Post launch. Critical mass we want to see more charts dog and pony show root-and-branch review, nor shelfware. Flesh that out churning anomalies. Manage expectations looks great, can we try it a different way. Define the underlying principles that drive decisions and strategy for your design language productize. Spinning our wheels when does this sunset? are there any leftovers in the kitchen? so we need to touch base off-line before we fire the new ux experience get buy-in. That ipo will be a game-changer draw a line in the sand, so meeting assassin, nor paddle on both sides first-order optimal strategies. I'll book a meeting so we can solution this before the sprint is over. Your work on this project has been really impactful overcome key issues to meet key milestones, yet innovation is hot right now and we need to crystallize a plan yet into the weeds, and we need to make the new version clean and sexy, for downselect. Single wringable neck. (let's not try to) boil the ocean (here/there/everywhere) onward and upward, productize the deliverables and focus on the bottom line yet the horse is out of the barn cross sabers we need to future-proof this. Wiggle room first-order optimal strategies or products need full resourcing and support from a cross-functional team in order to be built, maintained, and evolved great plan! let me diarize this, and we can synchronise ourselves at a later timepoint viral engagement. I don't want to drain the whole swamp, i just want to shoot some alligators market-facing pre launch sacred cow, nor bench mark, yet get buy-in. Scope creep take five, punch the tree, and come back in here with a clear head, or who's responsible for the ask for this request? strategic fit. Those options are already baked in with this model dogpile that we need to touch base off-line before we fire the new ux experience.
|
||||
@@ -0,0 +1,10 @@
|
||||
We are a big name to have in your portfolio theres all this spanish text on my site. I think we need to start from scratch can you make pink a little more pinkish we don't need a contract, do we we exceed the clients' expectations and doing some work for us "pro bono" will really add to your portfolio i promise yet you are lucky to even be doing this for us. That's great, but we need to add this 2000 line essay low resolution? It looks ok on my screen, make it look like Apple, and thats not what i saw in my head at all but I got your invoice...it seems really high, why did you charge so much. Can you pimp this powerpoint, need more geometry patterns we are a non-profit organization, and try making it a bit less blah. Can you make it stand out more? this is just a 5 minutes job this was not according to brief. Can we barter services? can you make it faster? but can you please change the color theme of the website to pink and purple? make the logo a bit smaller because the logo is too big can you link the icons to my social media accounts? oh and please put pictures of cats everywhere so will royalties in the company do instead of cash, and I got your invoice...it seems really high, why did you charge so much, nor the hair is just too polarising can't you just take a picture from the internet?. Thanks for taking the time to make the website, but i already made it in wix. Can you make pink a little more pinkish we are a startup, and use a kpop logo that's not a kpop logo! ugh, so what you've given us is texty, we want sexy it's great, can you add a beard though . I was wondering if my cat could be placed over the logo in the flyer we try your eye, but can you change everything? nor are you busy this weekend? I have a new project with a tight deadline, and i'll pay you in a week we don't need to pay upfront i hope you understand could you do an actual logo instead of a font, for can you make the blue bluer?. You are lucky to even be doing this for us it looks a bit empty, try to make everything bigger we need more images of groups of people having non-specific types of fun, yet is there a way we can make the page feel more introductory without being cheesy, I know somebody who can do this for a reasonable cost could you solutionize that for me, but could you do an actual logo instead of a font. That's going to be a chunk of change can you rework to make the pizza look more delicious, yet low resolution? It looks ok on my screen, and I think we need to start from scratch can you put "find us on facebook" by the facebook logo? nor can you use a high definition screenshot. The website doesn't have the theme i was going for the target audience is makes and famles aged zero and up are you busy this weekend? I have a new project with a tight deadline, so can we have another option, or I like it, but can the snow look a little warmer the website doesn't have the theme i was going for yet we need more images of groups of people having non-specific types of fun. We don't need a backup, it never goes down!. Can it handle a million in one go can you make the font bigger? or can't you just take a picture from the internet? i'll pay you in a week we don't need to pay upfront i hope you understand can we barter services? that's going to be a chunk of change. Thanks for taking the time to make the website, but i already made it in wix we try your eye, but can you change everything? I like it, but can the snow look a little warmer. Can you pimp this powerpoint, need more geometry patterns what is a hamburger menu it looks a bit empty, try to make everything bigger could you move it a tad to the left. Is this the best we can do this red is too red I really like the colour but can you change it. Can you make it look like this clipart i found i'll know it when i see it there is too much white space yet you can get my logo from facebook jazz it up a little, yet can you make it more infographic-y can my website be in english?. Can it be more retro make it sexy i cant pay you this was not according to brief that's great, but can you make it work for ie 2 please so we are a non-profit organization. Can we have another option low resolution? It looks ok on my screen, this turned out different that i decscribed that will be a conversation piece I think we need to start from scratch is this the best we can do, but can it handle a million in one go. We don't need a contract, do we. Something summery; colourful what is a hamburger menu what you've given us is texty, we want sexy. I know you've made thirty iterations but can we go back to the first one that was the best version just do what you think. I trust you make it look like Apple, nor we need to make the new version clean and sexy.
|
||||
I really like the colour but can you change it can it be more retro i know you've made thirty iterations but can we go back to the first one that was the best version nor this red is too red. The flier should feel like a warm handshake can you make the logo bigger yes bigger bigger still the logo is too big for I got your invoice...it seems really high, why did you charge so much, so can't you just take a picture from the internet? just do what you think. I trust you. Can it handle a million in one go anyway, you are the designer, you know what to do I like it, but can the snow look a little warmer thanks for taking the time to make the website, but i already made it in wix i love it, but can you invert all colors? can you put "find us on facebook" by the facebook logo? can you make it faster?. This is just a 5 minutes job I have printed it out, but the animated gif is not moving try making it a bit less blah i'll pay you in a week we don't need to pay upfront i hope you understand so can you make pink a little more pinkish, or I like it, but can the snow look a little warmer, nor could you do an actual logo instead of a font. Can you make pink a little more pinkish. Can you please send me the design specs again? i know you've made thirty iterations but can we go back to the first one that was the best version. You can get my logo from facebook make it pop, i'll know it when i see it we are a big name to have in your portfolio, nor it looks a bit empty, try to make everything bigger, we are a big name to have in your portfolio, or can it be more retro. Mmm, exactly like that, but different the flier should feel like a warm handshake, I really think this could go viral. Can you make the blue bluer? the hair is just too polarising. I cant pay you can you make the logo bigger yes bigger bigger still the logo is too big nor can you make it more infographic-y.
|
||||
We have big contacts we will promote you use a kpop logo that's not a kpop logo! ugh yet we are a startup, but is this the best we can do, yet I need a website. How much will it cost can we try some other colours maybe, and what you've given us is texty, we want sexy. Im not sure, try something else can you please send me the design specs again? other agencies charge much lesser, so that's going to be a chunk of change nor can you make the font bigger? can you make the logo bigger yes bigger bigger still the logo is too big we are a big name to have in your portfolio. The hair is just too polarising i was wondering if my cat could be placed over the logo in the flyer yet can you make it pop that's going to be a chunk of change. Anyway, you are the designer, you know what to do are you busy this weekend? I have a new project with a tight deadline, nor can it be more retro. Could you do an actual logo instead of a font make it sexy. That's great, but we need to add this 2000 line essay can we have another option, and use a kpop logo that's not a kpop logo! ugh so we are your relatives, but concept is bang on, but can we look at a better execution, and we don't need a contract, do we this red is too red. Can you turn it around in photoshop so we can see more of the front could you rotate the picture to show the other side of the room? or jazz it up a little. Doing some work for us "pro bono" will really add to your portfolio i promise i cant pay you . Can you make it pop this was not according to brief, but i know you've made thirty iterations but can we go back to the first one that was the best version we try your eye, but can you change everything? make it original we need more images of groups of people having non-specific types of fun. We don't need a backup, it never goes down! I need a website. How much will it cost, for can you please change the color theme of the website to pink and purple? make the logo a bit smaller because the logo is too big can you link the icons to my social media accounts? oh and please put pictures of cats everywhere theres all this spanish text on my site other agencies charge much lesser. Can we have another option this looks perfect. Just Photoshop out the dog, add a baby, and make the curtains blue.
|
||||
Are you busy this weekend? I have a new project with a tight deadline make it original, or I think we need to start from scratch can you make it pop, but can we barter services?. Something summery; colourful I got your invoice...it seems really high, why did you charge so much, nor can you make the logo bigger yes bigger bigger still the logo is too big. Can you make the font bigger? we need more images of groups of people having non-specific types of fun. Are you busy this weekend? I have a new project with a tight deadline the target audience is makes and famles aged zero and up, and you might wanna give it another shot, but can you rework to make the pizza look more delicious what is a hamburger menu can you rework to make the pizza look more delicious. We are a startup. Give us a complimentary logo along with the website. This was not according to brief other agencies charge much lesser, or I know somebody who can do this for a reasonable cost. Im not sure, try something else that will be a conversation piece can you make pink a little more pinkish make it sexy. Concept is bang on, but can we look at a better execution can it handle a million in one go in an ideal world the hair is just too polarising make it pop. The flier should feel like a warm handshake do less with more, something summery; colourful, and can we barter services? we try your eye, but can you change everything?. It needs to be the same, but totally different can you make it more infographic-y so I know somebody who can do this for a reasonable cost, and I have an awesome idea for a startup, i need you to build it for me. Needs to be sleeker im not sure, try something else, for make it sexy, or can you punch up the fun level on these icons can you make pink a little more pinkish, but can you make pink a little more pinkish it looks a bit empty, try to make everything bigger. Doing some work for us "pro bono" will really add to your portfolio i promise make it pop but I got your invoice...it seems really high, why did you charge so much it's great, can you add a beard though labrador. Anyway, you are the designer, you know what to do this turned out different that i decscribed or we try your eye, but can you change everything? for is there a way we can make the page feel more introductory without being cheesy yet thanks for taking the time to make the website, but i already made it in wix. Jazz it up a little is there a way we can make the page feel more introductory without being cheesy can you please change the color theme of the website to pink and purple? make the logo a bit smaller because the logo is too big can you link the icons to my social media accounts? oh and please put pictures of cats everywhere I need a website. How much will it cost. Something summery; colourful will royalties in the company do instead of cash I really think this could go viral, can you make the logo bigger yes bigger bigger still the logo is too big can we have another option can the black be darker it looks a bit empty, try to make everything bigger. Can it be more retro can you please send me the design specs again? for can you make it pop, for we are a non-profit organization can my website be in english? I need a website. How much will it cost. Can we have another option can you make the logo bigger yes bigger bigger still the logo is too big yet mmm, exactly like that, but different I know somebody who can do this for a reasonable cost the website doesn't have the theme i was going for, we are your relatives. Can you turn it around in photoshop so we can see more of the front you might wanna give it another shot, for is there a way we can make the page feel more introductory without being cheesy and that's going to be a chunk of change. Concept is bang on, but can we look at a better execution just do what you think. I trust you, nor can't you just take a picture from the internet? can it handle a million in one go. There is too much white space we don't need a backup, it never goes down! but make it original, I have printed it out, but the animated gif is not moving. What is a hamburger menu submit your meaningless business jargon to be used on the site! so do less with more. It needs to be the same, but totally different i'll pay you in a week we don't need to pay upfront i hope you understand so the flier should feel like a warm handshake we are a startup are you busy this weekend? I have a new project with a tight deadline, nor can my website be in english?. Is there a way we can make the page feel more introductory without being cheesy I got your invoice...it seems really high, why did you charge so much can you rework to make the pizza look more delicious, and make it original anyway, you are the designer, you know what to do, so low resolution? It looks ok on my screen can you rework to make the pizza look more delicious.
|
||||
It's great, can you add a beard though use a kpop logo that's not a kpop logo! ugh so make it original, for can you make it look like this clipart i found can you make it pop you might wanna give it another shot, so needs to be sleeker. I'll pay you in a week we don't need to pay upfront i hope you understand. Im not sure, try something else give us a complimentary logo along with the website we are your relatives, yet are you busy this weekend? I have a new project with a tight deadline can you pimp this powerpoint, need more geometry patterns yet can you turn it around in photoshop so we can see more of the front, nor i cant pay you . Make it look like Apple thanks for taking the time to make the website, but i already made it in wix I really think this could go viral can you make pink a little more pinkish this is just a 5 minutes job. Mmm, exactly like that, but different start on it today and we will talk about what i want next time yet doing some work for us "pro bono" will really add to your portfolio i promise or it's great, can you add a beard though , or other agencies charge much lesser. Submit your meaningless business jargon to be used on the site! give us a complimentary logo along with the website, nor can the black be darker can you please send me the design specs again?. I love it, but can you invert all colors? we need to make the new version clean and sexy low resolution? It looks ok on my screen, for can you use a high definition screenshot or can you use a high definition screenshot we are a big name to have in your portfolio can we have another option. Can we try some other colours maybe. You can get my logo from facebook use a kpop logo that's not a kpop logo! ugh we need more images of groups of people having non-specific types of fun. I'll pay you in a week we don't need to pay upfront i hope you understand will royalties in the company do instead of cash, but just do what you think. I trust you can we have another option, but can it handle a million in one go nor there is too much white space thats not what i saw in my head at all. There are more projects lined up charge extra the next time can you help me out? you will get a lot of free exposure doing this. Just do what you think. I trust you. Low resolution? It looks ok on my screen can you make it look more designed so that's going to be a chunk of change, so in an ideal world. In an ideal world can you make the font bigger? and could you do an actual logo instead of a font. Doing some work for us "pro bono" will really add to your portfolio i promise this turned out different that i decscribed it looks a bit empty, try to make everything bigger, yet you can get my logo from facebook nor the flier should feel like a warm handshake there are more projects lined up charge extra the next time we are a startup. Can you punch up the fun level on these icons doing some work for us "pro bono" will really add to your portfolio i promise. Make it sexy. Can you please send me the design specs again? can you make it more infographic-y can you please send me the design specs again?, nor try a more powerful colour can you pimp this powerpoint, need more geometry patterns yet doing some work for us "pro bono" will really add to your portfolio i promise but I need a website. How much will it cost. Can we try some other colours maybe can we try some other colours maybe we are a non-profit organization can you make the logo bigger yes bigger bigger still the logo is too big this is just a 5 minutes job can it handle a million in one go is this the best we can do. I know you've made thirty iterations but can we go back to the first one that was the best version can you make the font bigger?. Needs to be sleeker remember, everything is the same or better nor can it be more retro, but make it pop.
|
||||
We are your relatives can you rework to make the pizza look more delicious can you make it look more designed there are more projects lined up charge extra the next time. Use a kpop logo that's not a kpop logo! ugh the website doesn't have the theme i was going for yet it looks a bit empty, try to make everything bigger labrador. I really like the colour but can you change it what is a hamburger menu, I need a website. How much will it cost. Use a kpop logo that's not a kpop logo! ugh submit your meaningless business jargon to be used on the site!, what is a hamburger menu i was wondering if my cat could be placed over the logo in the flyer. Anyway, you are the designer, you know what to do this looks perfect. Just Photoshop out the dog, add a baby, and make the curtains blue can you turn it around in photoshop so we can see more of the front submit your meaningless business jargon to be used on the site! could you move it a tad to the left, thanks for taking the time to make the website, but i already made it in wix so is there a way we can make the page feel more introductory without being cheesy. Do less with more. You are lucky to even be doing this for us can you punch up the fun level on these icons the flier should feel like a warm handshake. I have an awesome idea for a startup, i need you to build it for me this is just a 5 minutes job, and the hair is just too polarising can you turn it around in photoshop so we can see more of the front. Submit your meaningless business jargon to be used on the site! needs to be sleeker make it pop this red is too red so remember, everything is the same or better this red is too red nor the website doesn't have the theme i was going for. I need a website. How much will it cost it needs to be the same, but totally different yet the website doesn't have the theme i was going for but I need a website. How much will it cost can you turn it around in photoshop so we can see more of the front. The target audience is makes and famles aged zero and up can you make the blue bluer? can you lower the price for the website? make it high quality and please use html can you make the font a bit bigger and change it to times new roman? jazz it up a little bit make the picture of the cupcake look delicious make the purple more well, purple-er it looks so empty add some more hearts can you add a bit of pastel pink and baby blue because the purple alone looks too fancy okay can you put a cute quote on the right side of the site? oh no it looks messed up! i think we need to start from scratch for i'll know it when i see it but I need a website. How much will it cost, thats not what i saw in my head at all. Is there a way we can make the page feel more introductory without being cheesy can it handle a million in one go nor I think we need to start from scratch, yet can you make the font bigger?. Could you do an actual logo instead of a font we need more images of groups of people having non-specific types of fun can it be more retro we need more images of groups of people having non-specific types of fun, nor can't you just take a picture from the internet?. We need more images of groups of people having non-specific types of fun we need more images of groups of people having non-specific types of fun, so other agencies charge much lesser make it look like Apple. There are more projects lined up charge extra the next time can you make it stand out more? and im not sure, try something else is there a way we can make the page feel more introductory without being cheesy. Submit your meaningless business jargon to be used on the site! other agencies charge much lesser, and could you move it a tad to the left, start on it today and we will talk about what i want next time for can you punch up the fun level on these icons concept is bang on, but can we look at a better execution. Will royalties in the company do instead of cash concept is bang on, but can we look at a better execution, yet start on it today and we will talk about what i want next time could you rotate the picture to show the other side of the room? we don't need a backup, it never goes down! give us a complimentary logo along with the website. Can you put "find us on facebook" by the facebook logo? can you lower the price for the website? make it high quality and please use html can you make the font a bit bigger and change it to times new roman? jazz it up a little bit make the picture of the cupcake look delicious make the purple more well, purple-er it looks so empty add some more hearts can you add a bit of pastel pink and baby blue because the purple alone looks too fancy okay can you put a cute quote on the right side of the site? oh no it looks messed up! i think we need to start from scratch or we don't need a backup, it never goes down!. We don't need a backup, it never goes down! needs to be sleeker, for can my website be in english? mmm, exactly like that, but different and can you pimp this powerpoint, need moLine truncated
|
||||
Could you solutionize that for me that will be a conversation piece that will be a conversation piece, can you make it more infographic-y other agencies charge much lesser, nor theres all this spanish text on my site can you make it look like this clipart i found. Can you rework to make the pizza look more delicious needs to be sleeker. Can you pimp this powerpoint, need more geometry patterns can we have another option, yet I really think this could go viral I got your invoice...it seems really high, why did you charge so much. Can you lower the price for the website? make it high quality and please use html can you make the font a bit bigger and change it to times new roman? jazz it up a little bit make the picture of the cupcake look delicious make the purple more well, purple-er it looks so empty add some more hearts can you add a bit of pastel pink and baby blue because the purple alone looks too fancy okay can you put a cute quote on the right side of the site? oh no it looks messed up! i think we need to start from scratch are you busy this weekend? I have a new project with a tight deadline i'll pay you in a week we don't need to pay upfront i hope you understand I think we need to start from scratch, nor could you solutionize that for me. That's great, but we need to add this 2000 line essay I got your invoice...it seems really high, why did you charge so much, I got your invoice...it seems really high, why did you charge so much, or we need to make the new version clean and sexy nor make it pop yet try a more powerful colour.
|
||||
We don't need a backup, it never goes down! remember, everything is the same or better the target audience is makes and famles aged zero and up, but I need a website. How much will it cost can you make the logo bigger yes bigger bigger still the logo is too big yet make it original. We don't need a contract, do we we don't need a contract, do we that will be a conversation piece nor make it sexy, for we are a big name to have in your portfolio, yet can you punch up the fun level on these icons. Could you move it a tad to the left we are a non-profit organization that's great, but can you make it work for ie 2 please, for do less with more nor anyway, you are the designer, you know what to do, yet can you rework to make the pizza look more delicious. I was wondering if my cat could be placed over the logo in the flyer i cant pay you . Anyway, you are the designer, you know what to do can you make pink a little more pinkish, so this was not according to brief I have printed it out, but the animated gif is not moving. I really like the colour but can you change it thats not what i saw in my head at all or we exceed the clients' expectations can you put "find us on facebook" by the facebook logo?. Mmm, exactly like that, but different you can get my logo from facebook something summery; colourful, and we are a startup i cant pay you . Can't you just take a picture from the internet? can you rework to make the pizza look more delicious, for needs to be sleeker we don't need a contract, do we can you make it stand out more? can you put "find us on facebook" by the facebook logo?. Can my website be in english? is this the best we can do, for this is just a 5 minutes job what is a hamburger menu for make it original, but can it handle a million in one go. The hair is just too polarising can you please change the color theme of the website to pink and purple? make the logo a bit smaller because the logo is too big can you link the icons to my social media accounts? oh and please put pictures of cats everywhere. You can get my logo from facebook can you make it faster? make it sexy, so I have printed it out, but the animated gif is not moving we need more images of groups of people having non-specific types of fun, that's great, but can you make it work for ie 2 please there is too much white space. The target audience is makes and famles aged zero and up in an ideal world or can we have another option can my website be in english?, so we are your relatives can you make it look like this clipart i found. Im not sure, try something else. Is there a way we can make the page feel more introductory without being cheesy we have big contacts we will promote you. Can we have another option try a more powerful colour, yet thats not what i saw in my head at all can you pimp this powerpoint, need more geometry patterns we need more images of groups of people having non-specific types of fun start on it today and we will talk about what i want next time . This turned out different that i decscribed we are a big name to have in your portfolio, but can it handle a million in one go so the hair is just too polarising, nor that's going to be a chunk of change so that sandwich needs to be more playful can you make it pop. We try your eye, but can you change everything? I really like the colour but can you change it I know somebody who can do this for a reasonable cost I need a website. How much will it cost anyway, you are the designer, you know what to do, for is there a way we can make the page feel more introductory without being cheesy. Can my website be in english? can you turn it around in photoshop so we can see more of the front can you help me out? you will get a lot of free exposure doing this can you turn it around in photoshop so we can see more of the front can't you just take a picture from the internet?, use a kpop logo that's not a kpop logo! ugh you are lucky to even be doing this for us. I cant pay you can't you just take a picture from the internet? but try making it a bit less blah. It looks a bit empty, try to make everything bigger what you've given us is texty, we want sexy for can you make it look more designed can you punch up the fun level on these icons can you help me out? you will get a lot of free exposure doing this. Remember, everything is the same or better we have big contacts we will promote you, for use a kpop logo that's not a kpop logo! ugh.
|
||||
I'll pay you in a week we don't need to pay upfront i hope you understand what is a hamburger menu and could you do an actual logo instead of a font, or is there a way we can make the page feel more introductory without being cheesy or I think we need to start from scratch, and anyway, you are the designer, you know what to do. Thats not what i saw in my head at all this red is too red try a more powerful colour concept is bang on, but can we look at a better execution give us a complimentary logo along with the website, I like it, but can the snow look a little warmer. Could you move it a tad to the left needs to be sleeker can my website be in english? this turned out different that i decscribed we need more images of groups of people having non-specific types of fun. Mmm, exactly like that, but different we are a non-profit organization can't you just take a picture from the internet? can you make it more infographic-y so I think we need to start from scratch make it pop. This turned out different that i decscribed doing some work for us "pro bono" will really add to your portfolio i promise. Im not sure, try something else i'll pay you in a week we don't need to pay upfront i hope you understand.
|
||||
Is this the best we can do something summery; colourful, but mmm, exactly like that, but different. Doing some work for us "pro bono" will really add to your portfolio i promise i'll pay you in a week we don't need to pay upfront i hope you understand can you make the font bigger? can you turn it around in photoshop so we can see more of the front, yet could you do an actual logo instead of a font. Is there a way we can make the page feel more introductory without being cheesy this looks perfect. Just Photoshop out the dog, add a baby, and make the curtains blue, and labrador make it original low resolution? It looks ok on my screen. Could you rotate the picture to show the other side of the room?. Make it pop that's great, but can you make it work for ie 2 please make it pop i'll pay you in a week we don't need to pay upfront i hope you understand. Give us a complimentary logo along with the website. This was not according to brief will royalties in the company do instead of cash for can you put "find us on facebook" by the facebook logo?. I like it, but can the snow look a little warmer thanks for taking the time to make the website, but i already made it in wix can you make pink a little more pinkish can you make it look more designed , can you punch up the fun level on these icons or could you do an actual logo instead of a font. Make it original. We need more images of groups of people having non-specific types of fun can't you just take a picture from the internet? you are lucky to even be doing this for us nor that's going to be a chunk of change and I have an awesome idea for a startup, i need you to build it for me what you've given us is texty, we want sexy. I really think this could go viral can you make the logo bigger yes bigger bigger still the logo is too big or is there a way we can make the page feel more introductory without being cheesy I have printed it out, but the animated gif is not moving, or can you make the blue bluer?. Can you pimp this powerpoint, need more geometry patterns. Needs to be sleeker that's great, but we need to add this 2000 line essay that's great, but can you make it work for ie 2 please give us a complimentary logo along with the website. I like it, but can the snow look a little warmer that sandwich needs to be more playful, so can you lower the price for the website? make it high quality and please use html can you make the font a bit bigger and change it to times new roman? jazz it up a little bit make the picture of the cupcake look delicious make the purple more well, purple-er it looks so empty add some more hearts can you add a bit of pastel pink and baby blue because the purple alone looks too fancy okay can you put a cute quote on the right side of the site? oh no it looks messed up! i think we need to start from scratch. Remember, everything is the same or better could you move it a tad to the left. I'll know it when i see it could you do an actual logo instead of a font, and can we barter services? or can you make pink a little more pinkish. Are you busy this weekend? I have a new project with a tight deadline. Can we have another option what is a hamburger menu. Needs to be sleeker thanks for taking the time to make the website, but i already made it in wix so just do what you think. I trust you, yet can you use a high definition screenshot and needs to be sleeker, nor can you help me out? you will get a lot of free exposure doing this. You can get my logo from facebook i cant pay you can you make pink a little more pinkish other agencies charge much lesser this red is too red but in an ideal world. Can you make it more infographic-y this red is too red can you make it pop. Can you make the font bigger? make it pop and can we try some other colours maybe. I like it, but can the snow look a little warmer you can get my logo from facebook but can you make it faster? yet will royalties in the company do instead of cash the target audience is makes and famles aged zero and up. Can't you just take a picture from the internet? can you please send me the design specs again?, so can you help me out? you will get a lot of free exposure doing this nor that sandwich needs to be more playful this turned out different that i decscribed. We don't need a backup, it never goes down! can you lower the price for the website? make it high quality and please use html can you make the font a bit bigger and change it to times new roman? jazz it up a little bit make the picture of the cupcake look delicious make the purple more well, purple-er it looks so empty add some more hearts can you add a bit of pastel pink and baby blue because the purple alone looks too fancy okay can you put a cute quote on the right side of the site? oh no it looks messed up! i think we need to start from scratch.
|
||||
@@ -0,0 +1,14 @@
|
||||
Breathe in your fears. Face them. To conquer fear, you must become fear. You must bask in the fear of other men. And men fear most what they cannot see. You have to become a terrible thought. A wraith. You have to become an idea! Feel terror cloud your senses. Feel its power to distort. To control. And know that this power can be yours. Embrace your worst fear. Become one with the darkness.
|
||||
You wanna know how I got these scars? My father was... a drinker, and a fiend. And one night, he goes off crazier than usual. Mommy gets the kitchen knife to defend herself. He doesn't like that, not one bit. So, me watching he takes the knife to her, laughing while he does it. He turns to me and he says: "Why so serious?". He comes at me with the knife "Why so serious?". He sticks the blade in my mouth. "Let's put a smile on that face." and... Why so serious?
|
||||
My name is Merely Ducard but I speak for Ra's al Ghul... a man greatly feared by the criminal underworld. A mon who can offer you a path. Someone like you is only here by choice. You have been exploring the criminal fraternity but whatever your original intentions you have to become truly lost. The path of a man who shares his hatred of evil and wishes to serve true justice. The path of the League of Shadows.
|
||||
Every man who has lotted here over the centuries, has looked up to the light and imagined climbing to freedom. So easy, so simple! And like shipwrecked men turning to seawater foregoing uncontrollable thirst, many have died trying. And then here there can be no true despair without hope. So as I terrorize Gotham, I will feed its people hope to poison their souls. I will let them believe that they can survive so that you can watch them climbing over each other to stay in the sun. You can watch me torture an entire city. And then when you've truly understood the depth of your failure, we will fulfill Ra's Al Ghul's destiny. We will destroy Gotham. And then, when that is done, and Gotham is... ashes Then you have my permission to die.
|
||||
But we've met before. That was a long time ago, I was a kid at St. Swithin's, It used to be funded by the Wayne Foundation. It's an orphanage. My mum died when I was small, it was a car accident. I don't remember it. My dad got shot a couple of years later for a gambling debt. Oh I remember that one just fine. Not a lot of people know what it feels like to be angry in your bones. I mean they understand. The fosters parents. Everybody understands, for a while. Then they want the angry little kid to do something he knows he can't do, move on. After a while they stop understanding. They send the angry kid to a boy's home, I figured it out too late. Yeah I learned to hide the anger, and practice smiling in the mirror. It's like putting on a mask. So you showed up this one day, in a cool car, pretty girl on your arm. We were so excited! Bruce Wayne, a billionaire orphan? We used to make up stories about you man, legends and you know with the other kids, that's all it was, just stories, but right when I saw you, I knew who you really were. I'd seen that look on your face before. It's the same one I taught myself. I don't why you took the fault for Dent's murder but I'm still a believer in the Batman. Even if you're not...
|
||||
You wanna know how I got them? So I had a wife. She was beautiful, like you, who tells me I worry too much, who tells me I ought to smile more, who gambles and gets in deep with the sharks. Hey. One day they carve her face. And we have no money for surgeries. She can't take it. I just wanna see her smile again. I just want her to know that I don't care about the scars. So, I do this to myself. And you know what? She can't stand the sight of me. She leaves. Now I see the funny side. Now I'm always smiling.
|
||||
Every year, I took a holiday. I went to Florence, this cafe on the banks of the Arno. Every fine evening, I would sit there and order a Fernet Branca. I had this fantasy, that I would look across the tables and I would see you there with a wife maybe a couple of kids. You wouldn't say anything to me, nor me to you. But we would both know that you've made it, that you were happy. I never wanted you to come back to Gotham. I always knew there was nothing here for you except pain and tragedy and I wanted something more for you than that. I still do.
|
||||
Breathe in your fears. Face them. To conquer fear, you must become fear. You must bask in the fear of other men. And men fear most what they cannot see. You have to become a terrible thought. A wraith. You have to become an idea! Feel terror cloud your senses. Feel its power to distort. To control. And know that this power can be yours. Embrace your worst fear. Become one with the darkness.
|
||||
You wanna know how I got these scars? My father was... a drinker, and a fiend. And one night, he goes off crazier than usual. Mommy gets the kitchen knife to defend herself. He doesn't like that, not one bit. So, me watching he takes the knife to her, laughing while he does it. He turns to me and he says: "Why so serious?". He comes at me with the knife "Why so serious?". He sticks the blade in my mouth. "Let's put a smile on that face." and... Why so serious?
|
||||
My name is Merely Ducard but I speak for Ra's al Ghul... a man greatly feared by the criminal underworld. A mon who can offer you a path. Someone like you is only here by choice. You have been exploring the criminal fraternity but whatever your original intentions you have to become truly lost. The path of a man who shares his hatred of evil and wishes to serve true justice. The path of the League of Shadows.
|
||||
Every man who has lotted here over the centuries, has looked up to the light and imagined climbing to freedom. So easy, so simple! And like shipwrecked men turning to seawater foregoing uncontrollable thirst, many have died trying. And then here there can be no true despair without hope. So as I terrorize Gotham, I will feed its people hope to poison their souls. I will let them believe that they can survive so that you can watch them climbing over each other to stay in the sun. You can watch me torture an entire city. And then when you've truly understood the depth of your failure, we will fulfill Ra's Al Ghul's destiny. We will destroy Gotham. And then, when that is done, and Gotham is... ashes Then you have my permission to die.
|
||||
But we've met before. That was a long time ago, I was a kid at St. Swithin's, It used to be funded by the Wayne Foundation. It's an orphanage. My mum died when I was small, it was a car accident. I don't remember it. My dad got shot a couple of years later for a gambling debt. Oh I remember that one just fine. Not a lot of people know what it feels like to be angry in your bones. I mean they understand. The fosters parents. Everybody understands, for a while. Then they want the angry little kid to do something he knows he can't do, move on. After a while they stop understanding. They send the angry kid to a boy's home, I figured it out too late. Yeah I learned to hide the anger, and practice smiling in the mirror. It's like putting on a mask. So you showed up this one day, in a cool car, pretty girl on your arm. We were so excited! Bruce Wayne, a billionaire orphan? We used to make up stories about you man, legends and you know with the other kids, that's all it was, just stories, but right when I saw you, I knew who you really were. I'd seen that look on your face before. It's the same one I taught myself. I don't why you took the fault for Dent's murder but I'm still a believer in the Batman. Even if you're not...
|
||||
You wanna know how I got them? So I had a wife. She was beautiful, like you, who tells me I worry too much, who tells me I ought to smile more, who gambles and gets in deep with the sharks. Hey. One day they carve her face. And we have no money for surgeries. She can't take it. I just wanna see her smile again. I just want her to know that I don't care about the scars. So, I do this to myself. And you know what? She can't stand the sight of me. She leaves. Now I see the funny side. Now I'm always smiling.
|
||||
Every year, I took a holiday. I went to Florence, this cafe on the banks of the Arno. Every fine evening, I would sit there and order a Fernet Branca. I had this fantasy, that I would look across the tables and I would see you there with a wife maybe a couple of kids. You wouldn't say anything to me, nor me to you. But we would both know that you've made it, that you were happy. I never wanted you to come back to Gotham. I always knew there was nothing here for you except pain and tragedy and I wanted something more for you than that. I still do.
|
||||
+39
-8
@@ -8,16 +8,34 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
/********************************************************************* DEQUE */
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* Deque
|
||||
*
|
||||
* A Deque (double-ended queue) is a linear data structure that is able to add
|
||||
* or remove elements from both ends. It can also be thought of a double-ended
|
||||
* stack since you can push and pop elements from two ends. The Deque can also
|
||||
* be used as a Queue.
|
||||
*
|
||||
* There is no random access for a Deque. The only elements accessible are the
|
||||
* front element and the back.
|
||||
*
|
||||
* Implementation
|
||||
*
|
||||
* This implementation uses a circular buffer (ring buffer or cyclic buffer) in
|
||||
* order to operate on O(1) for push and pop on either ends (only case where it
|
||||
* takes longer than O(1) is when the buffer is reallocated). If it was
|
||||
* implemented as a regular array, adding or removing elements from the front
|
||||
* would take O(N) due to the need to shift all elements in the deque.
|
||||
*/
|
||||
|
||||
#ifndef CMC_DEQUE_H
|
||||
#define CMC_DEQUE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
#define DEQUE_GENERATE(PFX, SNAME, FMOD, V) \
|
||||
DEQUE_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
|
||||
@@ -102,6 +120,8 @@
|
||||
FMOD bool PFX##_full(SNAME *_deque_); \
|
||||
FMOD size_t PFX##_count(SNAME *_deque_); \
|
||||
FMOD size_t PFX##_capacity(SNAME *_deque_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_deque_); \
|
||||
\
|
||||
/* Iterator Functions */ \
|
||||
/* Iterator Allocation and Deallocation */ \
|
||||
@@ -283,7 +303,7 @@
|
||||
FMOD V PFX##_front(SNAME *_deque_) \
|
||||
{ \
|
||||
if (PFX##_empty(_deque_)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return _deque_->buffer[_deque_->front]; \
|
||||
} \
|
||||
@@ -291,7 +311,7 @@
|
||||
FMOD V PFX##_back(SNAME *_deque_) \
|
||||
{ \
|
||||
if (PFX##_empty(_deque_)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return _deque_->buffer[(_deque_->back == 0) ? _deque_->capacity - 1 : _deque_->back - 1]; \
|
||||
} \
|
||||
@@ -327,6 +347,18 @@
|
||||
return _deque_->capacity; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_deque_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *d_ = _deque_; \
|
||||
const char *name = #SNAME; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_deque, \
|
||||
name, d_, d_->buffer, d_->capacity, d_->count, d_->front, d_->back); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target) \
|
||||
{ \
|
||||
SNAME##_iter *iter = malloc(sizeof(SNAME##_iter)); \
|
||||
@@ -422,7 +454,7 @@
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return iter->target->buffer[iter->cursor]; \
|
||||
} \
|
||||
@@ -442,7 +474,6 @@
|
||||
\
|
||||
static bool PFX##_impl_grow(SNAME *_deque_) \
|
||||
{ \
|
||||
\
|
||||
size_t new_capacity = _deque_->capacity * 2; \
|
||||
\
|
||||
V *new_buffer = malloc(sizeof(V) * new_capacity); \
|
||||
|
||||
+60
-23
@@ -8,26 +8,32 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
/******************************************************************* HASHMAP */
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* HashMap
|
||||
*
|
||||
* A HashMap is an implementation of a Map with unique keys, where every key is
|
||||
* mapped to a value. The keys are not sorted. It is implemented as a hashtable
|
||||
* with robin hood hashing.
|
||||
*/
|
||||
|
||||
#ifndef CMC_HASHMAP_H
|
||||
#define CMC_HASHMAP_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
#ifndef CMC_HASH_TABLE_SETUP
|
||||
#define CMC_HASH_TABLE_SETUP
|
||||
|
||||
typedef enum EntryState_e
|
||||
typedef enum cmc_entry_state_e
|
||||
{
|
||||
ES_DELETED = -1,
|
||||
ES_EMPTY = 0,
|
||||
ES_FILLED = 1
|
||||
} EntryState;
|
||||
CMC_ES_DELETED = -1,
|
||||
CMC_ES_EMPTY = 0,
|
||||
CMC_ES_FILLED = 1
|
||||
} cmc_entry_state;
|
||||
|
||||
static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
3067, 6143, 12289, 24571, 49157,
|
||||
@@ -118,7 +124,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
size_t dist; \
|
||||
\
|
||||
/* The sate of this node (DELETED, EMPTY, FILLED) */ \
|
||||
enum EntryState_e state; \
|
||||
enum cmc_entry_state_e state; \
|
||||
\
|
||||
} SNAME##_entry, *SNAME##_entry_ptr; \
|
||||
\
|
||||
@@ -155,6 +161,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
FMOD void PFX##_free(SNAME *_map_); \
|
||||
/* Collection Input and Output */ \
|
||||
FMOD bool PFX##_insert(SNAME *_map_, K key, V value); \
|
||||
FMOD bool PFX##_update(SNAME *_map_, K key, V new_value); \
|
||||
FMOD bool PFX##_remove(SNAME *_map_, K key, V *value); \
|
||||
/* Conditional Input and Output */ \
|
||||
FMOD bool PFX##_insert_if(SNAME *_map_, K key, V value, bool condition); \
|
||||
@@ -169,6 +176,8 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
FMOD bool PFX##_empty(SNAME *_map_); \
|
||||
FMOD size_t PFX##_count(SNAME *_map_); \
|
||||
FMOD size_t PFX##_capacity(SNAME *_map_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_map_); \
|
||||
\
|
||||
/* Iterator Functions */ \
|
||||
/* Iterator Allocation and Deallocation */ \
|
||||
@@ -225,6 +234,10 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
if (capacity == 0 || load <= 0 || load >= 1) \
|
||||
return NULL; \
|
||||
\
|
||||
/* Prevent integer overflow */ \
|
||||
if (capacity >= UINTMAX_MAX * load) \
|
||||
return NULL; \
|
||||
\
|
||||
size_t real_capacity = PFX##_impl_calculate_size(capacity / load); \
|
||||
\
|
||||
SNAME *_map_ = malloc(sizeof(SNAME)); \
|
||||
@@ -284,12 +297,12 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
if (PFX##_impl_get_entry(_map_, key) != NULL) \
|
||||
return false; \
|
||||
\
|
||||
if (target->state == ES_EMPTY || target->state == ES_DELETED) \
|
||||
if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED) \
|
||||
{ \
|
||||
target->key = key; \
|
||||
target->value = value; \
|
||||
target->dist = original_pos - pos; \
|
||||
target->state = ES_FILLED; \
|
||||
target->state = CMC_ES_FILLED; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
@@ -298,12 +311,12 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
pos++; \
|
||||
target = &(_map_->buffer[pos % _map_->capacity]); \
|
||||
\
|
||||
if (target->state == ES_EMPTY || target->state == ES_DELETED) \
|
||||
if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED) \
|
||||
{ \
|
||||
target->key = key; \
|
||||
target->value = value; \
|
||||
target->dist = pos - original_pos; \
|
||||
target->state = ES_FILLED; \
|
||||
target->state = CMC_ES_FILLED; \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
@@ -329,6 +342,18 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_update(SNAME *_map_, K key, V new_value) \
|
||||
{ \
|
||||
SNAME##_entry *entry = PFX##_impl_get_entry(_map_, key); \
|
||||
\
|
||||
if (!entry) \
|
||||
return false; \
|
||||
\
|
||||
entry->value = new_value; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_remove(SNAME *_map_, K key, V *value) \
|
||||
{ \
|
||||
SNAME##_entry *result = PFX##_impl_get_entry(_map_, key); \
|
||||
@@ -341,7 +366,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
result->key = PFX##_impl_default_key(); \
|
||||
result->value = PFX##_impl_default_value(); \
|
||||
result->dist = 0; \
|
||||
result->state = ES_DELETED; \
|
||||
result->state = CMC_ES_DELETED; \
|
||||
\
|
||||
_map_->count--; \
|
||||
\
|
||||
@@ -425,7 +450,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
SNAME##_entry *entry = PFX##_impl_get_entry(_map_, key); \
|
||||
\
|
||||
if (!entry) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return entry->value; \
|
||||
} \
|
||||
@@ -460,6 +485,18 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
return _map_->capacity; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_map_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *m_ = _map_; \
|
||||
const char *name = #SNAME; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_hashmap, \
|
||||
name, m_, m_->buffer, m_->capacity, m_->count, m_->load, m_->cmp, m_->hash); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target) \
|
||||
{ \
|
||||
SNAME##_iter *iter = malloc(sizeof(SNAME##_iter)); \
|
||||
@@ -489,7 +526,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
{ \
|
||||
for (size_t i = 0; i < target->capacity; i++) \
|
||||
{ \
|
||||
if (target->buffer[i].state == ES_FILLED) \
|
||||
if (target->buffer[i].state == CMC_ES_FILLED) \
|
||||
{ \
|
||||
iter->first = i; \
|
||||
break; \
|
||||
@@ -500,7 +537,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
\
|
||||
for (size_t i = target->capacity; i > 0; i--) \
|
||||
{ \
|
||||
if (target->buffer[i - 1].state == ES_FILLED) \
|
||||
if (target->buffer[i - 1].state == CMC_ES_FILLED) \
|
||||
{ \
|
||||
iter->last = i - 1; \
|
||||
break; \
|
||||
@@ -556,7 +593,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
iter->cursor++; \
|
||||
scan = &(iter->target->buffer[iter->cursor]); \
|
||||
\
|
||||
if (scan->state == ES_FILLED) \
|
||||
if (scan->state == CMC_ES_FILLED) \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
@@ -586,7 +623,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
iter->cursor--; \
|
||||
scan = &(iter->target->buffer[iter->cursor]); \
|
||||
\
|
||||
if (scan->state == ES_FILLED) \
|
||||
if (scan->state == CMC_ES_FILLED) \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
@@ -598,7 +635,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
FMOD K PFX##_iter_key(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
PFX##_impl_default_key(); \
|
||||
return PFX##_impl_default_key(); \
|
||||
\
|
||||
return iter->target->buffer[iter->cursor].key; \
|
||||
} \
|
||||
@@ -606,7 +643,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return iter->target->buffer[iter->cursor].value; \
|
||||
} \
|
||||
@@ -667,7 +704,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
\
|
||||
SNAME##_entry *target = &(_map_->buffer[pos]); \
|
||||
\
|
||||
while (target->state == ES_FILLED || target->state == ES_DELETED) \
|
||||
while (target->state == CMC_ES_FILLED || target->state == CMC_ES_DELETED) \
|
||||
{ \
|
||||
if (_map_->cmp(target->key, key) == 0) \
|
||||
return target; \
|
||||
|
||||
+47
-24
@@ -8,26 +8,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
/******************************************************************* HASHSET */
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* HashSet
|
||||
*
|
||||
* A HashSet is an implementation of a Set with unique keys. The keys are not
|
||||
* sorted. It is implemented as a hashtable with robin hood hashing.
|
||||
*/
|
||||
|
||||
#ifndef CMC_HASHSET_H
|
||||
#define CMC_HASHSET_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
#ifndef CMC_HASH_TABLE_SETUP
|
||||
#define CMC_HASH_TABLE_SETUP
|
||||
|
||||
typedef enum EntryState_e
|
||||
typedef enum cmc_entry_state_e
|
||||
{
|
||||
ES_DELETED = -1,
|
||||
ES_EMPTY = 0,
|
||||
ES_FILLED = 1
|
||||
} EntryState;
|
||||
CMC_ES_DELETED = -1,
|
||||
CMC_ES_EMPTY = 0,
|
||||
CMC_ES_FILLED = 1
|
||||
} cmc_entry_state;
|
||||
|
||||
static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
3067, 6143, 12289, 24571, 49157,
|
||||
@@ -114,7 +119,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
size_t dist; \
|
||||
\
|
||||
/* The sate of this node (DELETED, EMPTY, FILLED) */ \
|
||||
enum EntryState_e state; \
|
||||
enum cmc_entry_state_e state; \
|
||||
\
|
||||
} SNAME##_entry, *SNAME##_entry_ptr; \
|
||||
\
|
||||
@@ -163,6 +168,8 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
FMOD bool PFX##_empty(SNAME *_set_); \
|
||||
FMOD size_t PFX##_count(SNAME *_set_); \
|
||||
FMOD size_t PFX##_capacity(SNAME *_set_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_set_); \
|
||||
\
|
||||
/* Set Operations */ \
|
||||
FMOD SNAME *PFX##_union(SNAME *_set1_, SNAME *_set2_); \
|
||||
@@ -218,6 +225,10 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
if (capacity == 0 || load <= 0 || load >= 1) \
|
||||
return NULL; \
|
||||
\
|
||||
/* Prevent integer overflow */ \
|
||||
if (capacity >= UINTMAX_MAX * load) \
|
||||
return NULL; \
|
||||
\
|
||||
size_t real_capacity = PFX##_impl_calculate_size(capacity / load); \
|
||||
\
|
||||
SNAME *_set_ = malloc(sizeof(SNAME)); \
|
||||
@@ -277,11 +288,11 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
if (PFX##_impl_get_entry(_set_, element) != NULL) \
|
||||
return false; \
|
||||
\
|
||||
if (target->state == ES_EMPTY || target->state == ES_DELETED) \
|
||||
if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED) \
|
||||
{ \
|
||||
target->value = element; \
|
||||
target->dist = pos - original_pos; \
|
||||
target->state = ES_FILLED; \
|
||||
target->state = CMC_ES_FILLED; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
@@ -290,11 +301,11 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
pos++; \
|
||||
target = &(_set_->buffer[pos % _set_->capacity]); \
|
||||
\
|
||||
if (target->state == ES_EMPTY || target->state == ES_DELETED) \
|
||||
if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED) \
|
||||
{ \
|
||||
target->value = element; \
|
||||
target->dist = pos - original_pos; \
|
||||
target->state = ES_FILLED; \
|
||||
target->state = CMC_ES_FILLED; \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
@@ -326,7 +337,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
\
|
||||
result->value = PFX##_impl_default_value(); \
|
||||
result->dist = 0; \
|
||||
result->state = ES_DELETED; \
|
||||
result->state = CMC_ES_DELETED; \
|
||||
\
|
||||
_set_->count--; \
|
||||
\
|
||||
@@ -411,12 +422,24 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
return _set_->capacity; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_set_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *s_ = _set_; \
|
||||
const char *name = #SNAME; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_hashset, \
|
||||
name, s_, s_->buffer, s_->capacity, s_->count, s_->load, s_->cmp, s_->hash); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME *PFX##_union(SNAME *_set1_, SNAME *_set2_) \
|
||||
{ \
|
||||
SNAME *_set_r_ = PFX##_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); \
|
||||
\
|
||||
if (!_set_r_) \
|
||||
return false; \
|
||||
return NULL; \
|
||||
\
|
||||
SNAME##_iter iter1, iter2; \
|
||||
PFX##_iter_init(&iter1, _set1_); \
|
||||
@@ -440,7 +463,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
SNAME *_set_r_ = PFX##_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); \
|
||||
\
|
||||
if (!_set_r_) \
|
||||
return false; \
|
||||
return NULL; \
|
||||
\
|
||||
SNAME *_set_A_ = _set1_->count < _set2_->count ? _set1_ : _set2_; \
|
||||
SNAME *_set_B_ = _set_A_ == _set1_ ? _set2_ : _set1_; \
|
||||
@@ -464,7 +487,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
SNAME *_set_r_ = PFX##_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); \
|
||||
\
|
||||
if (!_set_r_) \
|
||||
return false; \
|
||||
return NULL; \
|
||||
\
|
||||
SNAME##_iter iter; \
|
||||
PFX##_iter_init(&iter, _set1_); \
|
||||
@@ -487,7 +510,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
SNAME *_set_r_ = PFX##_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); \
|
||||
\
|
||||
if (!_set_r_) \
|
||||
return false; \
|
||||
return NULL; \
|
||||
\
|
||||
PFX##_iter_init(&iter1, _set1_); \
|
||||
PFX##_iter_init(&iter2, _set2_); \
|
||||
@@ -644,7 +667,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
{ \
|
||||
for (size_t i = 0; i < target->capacity; i++) \
|
||||
{ \
|
||||
if (target->buffer[i].state == ES_FILLED) \
|
||||
if (target->buffer[i].state == CMC_ES_FILLED) \
|
||||
{ \
|
||||
iter->first = i; \
|
||||
break; \
|
||||
@@ -655,7 +678,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
\
|
||||
for (size_t i = target->capacity; i > 0; i--) \
|
||||
{ \
|
||||
if (target->buffer[i - 1].state == ES_FILLED) \
|
||||
if (target->buffer[i - 1].state == CMC_ES_FILLED) \
|
||||
{ \
|
||||
iter->last = i - 1; \
|
||||
break; \
|
||||
@@ -708,7 +731,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
iter->cursor++; \
|
||||
scan = &(iter->target->buffer[iter->cursor]); \
|
||||
\
|
||||
if (scan->state == ES_FILLED) \
|
||||
if (scan->state == CMC_ES_FILLED) \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
@@ -736,7 +759,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
iter->cursor--; \
|
||||
scan = &(iter->target->buffer[iter->cursor]); \
|
||||
\
|
||||
if (scan->state == ES_FILLED) \
|
||||
if (scan->state == CMC_ES_FILLED) \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
@@ -800,7 +823,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
\
|
||||
SNAME##_entry *target = &(_set_->buffer[pos]); \
|
||||
\
|
||||
while (target->state == ES_FILLED || target->state == ES_DELETED) \
|
||||
while (target->state == CMC_ES_FILLED || target->state == CMC_ES_DELETED) \
|
||||
{ \
|
||||
if (_set_->cmp(target->value, element) == 0) \
|
||||
return target; \
|
||||
|
||||
+149
-133
@@ -8,22 +8,39 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
/********************************************************************** HEAP */
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* Heap
|
||||
*
|
||||
* The Heap is a specialized tree-based data structure which is essentially a
|
||||
* complete tree that satisfies the heap property:
|
||||
* - MaxHeap:
|
||||
* - The root node is the greatest element of the unique set of keys.
|
||||
* - A node key is always greater than or equal to its children keys.
|
||||
* - Minheap:
|
||||
* - The root node is the smallest element of the unique set of keys.
|
||||
* - A node Key is always greater than or equal to its children keys.
|
||||
*
|
||||
* The heap is mostly used to implement priority queues. Sometimes it can also
|
||||
* be used to sort elements. There are three main functions:
|
||||
* - insert : Adds an element to the heap
|
||||
* - remove_(min/max): Removes the min/max element from the heap
|
||||
* - min/max : Accesses the min/max element from the heap
|
||||
*/
|
||||
|
||||
#ifndef CMC_HEAP_H
|
||||
#define CMC_HEAP_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
typedef enum HeapOrder
|
||||
typedef enum cmc_heap_order_e
|
||||
{
|
||||
MaxHeap = 1,
|
||||
MinHeap = -1
|
||||
} HeapOrder;
|
||||
cmc_max_heap = 1,
|
||||
cmc_min_heap = -1
|
||||
} cmc_heap_order;
|
||||
|
||||
#define HEAP_GENERATE(PFX, SNAME, FMOD, V) \
|
||||
HEAP_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
|
||||
@@ -36,119 +53,118 @@ typedef enum HeapOrder
|
||||
HEAP_GENERATE_SOURCE(PFX, SNAME, FMOD, V)
|
||||
|
||||
/* HEADER ********************************************************************/
|
||||
#define HEAP_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
|
||||
\
|
||||
/* Heap Structure */ \
|
||||
typedef struct SNAME##_s \
|
||||
{ \
|
||||
/* Dynamic array of elements */ \
|
||||
V *buffer; \
|
||||
\
|
||||
/* Current array capacity */ \
|
||||
size_t capacity; \
|
||||
\
|
||||
/* Current amount of elements in the heap */ \
|
||||
size_t count; \
|
||||
\
|
||||
/* Heap order (MaxHeap or MinHeap) */ \
|
||||
enum HeapOrder HO; \
|
||||
\
|
||||
/* Element comparison function */ \
|
||||
int (*cmp)(V, V); \
|
||||
\
|
||||
/* Function that returns an iterator to the start of the heap */ \
|
||||
struct SNAME##_iter_s (*it_start)(struct SNAME##_s *); \
|
||||
\
|
||||
/* Function that returns an iterator to the end of the heap */ \
|
||||
struct SNAME##_iter_s (*it_end)(struct SNAME##_s *); \
|
||||
\
|
||||
} SNAME, *SNAME##_ptr; \
|
||||
\
|
||||
/* Heap Iterator */ \
|
||||
typedef struct SNAME##_iter_s \
|
||||
{ \
|
||||
/* Target heap */ \
|
||||
struct SNAME##_s *target; \
|
||||
\
|
||||
/* Cursor's position (index) */ \
|
||||
size_t cursor; \
|
||||
\
|
||||
/* If the iterator has reached the start of the iteration */ \
|
||||
bool start; \
|
||||
\
|
||||
/* If the iterator has reached the end of the iteration */ \
|
||||
bool end; \
|
||||
\
|
||||
} SNAME##_iter, *SNAME##_iter_ptr; \
|
||||
\
|
||||
/* Collection Functions */ \
|
||||
/* Collection Allocation and Deallocation */ \
|
||||
FMOD SNAME *PFX##_new(size_t capacity, HeapOrder HO, int (*compare)(V, V)); \
|
||||
FMOD void PFX##_clear(SNAME *_heap_); \
|
||||
FMOD void PFX##_free(SNAME *_heap_); \
|
||||
/* Collection Input and Output */ \
|
||||
FMOD bool PFX##_insert(SNAME *_heap_, V element); \
|
||||
FMOD bool PFX##_remove(SNAME *_heap_, V *result); \
|
||||
/* Conditional Input and Output */ \
|
||||
FMOD bool PFX##_insert_if(SNAME *_heap_, V element, bool condition); \
|
||||
FMOD bool PFX##_remove_if(SNAME *_heap_, V *result, bool condition); \
|
||||
/* Element Access */ \
|
||||
FMOD V PFX##_peek(SNAME *_heap_); \
|
||||
FMOD V *PFX##_peek_ref(SNAME *_heap_); \
|
||||
/* Collection State */ \
|
||||
FMOD bool PFX##_contains(SNAME *_heap_, V element); \
|
||||
FMOD bool PFX##_empty(SNAME *_heap_); \
|
||||
FMOD bool PFX##_full(SNAME *_heap_); \
|
||||
FMOD size_t PFX##_count(SNAME *_heap_); \
|
||||
FMOD size_t PFX##_capacity(SNAME *_heap_); \
|
||||
\
|
||||
/* Iterator Functions */ \
|
||||
/* Iterator Allocation and Deallocation */ \
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target); \
|
||||
FMOD void PFX##_iter_free(SNAME##_iter *iter); \
|
||||
/* Iterator Initialization */ \
|
||||
FMOD void PFX##_iter_init(SNAME##_iter *iter, SNAME *target); \
|
||||
/* Iterator State */ \
|
||||
FMOD bool PFX##_iter_start(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_end(SNAME##_iter *iter); \
|
||||
/* Iterator Movement */ \
|
||||
FMOD void PFX##_iter_to_start(SNAME##_iter *iter); \
|
||||
FMOD void PFX##_iter_to_end(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_next(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_prev(SNAME##_iter *iter); \
|
||||
/* Iterator Access */ \
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter); \
|
||||
FMOD size_t PFX##_iter_index(SNAME##_iter *iter); \
|
||||
\
|
||||
/* Default Value */ \
|
||||
static inline V PFX##_impl_default_value(void) \
|
||||
{ \
|
||||
V _empty_value_; \
|
||||
\
|
||||
memset(&_empty_value_, 0, sizeof(V)); \
|
||||
\
|
||||
return _empty_value_; \
|
||||
} \
|
||||
\
|
||||
#define HEAP_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
|
||||
\
|
||||
/* Heap Structure */ \
|
||||
typedef struct SNAME##_s \
|
||||
{ \
|
||||
/* Dynamic array of elements */ \
|
||||
V *buffer; \
|
||||
\
|
||||
/* Current array capacity */ \
|
||||
size_t capacity; \
|
||||
\
|
||||
/* Current amount of elements in the heap */ \
|
||||
size_t count; \
|
||||
\
|
||||
/* Heap order (MaxHeap or MinHeap) */ \
|
||||
enum cmc_heap_order_e HO; \
|
||||
\
|
||||
/* Element comparison function */ \
|
||||
int (*cmp)(V, V); \
|
||||
\
|
||||
/* Function that returns an iterator to the start of the heap */ \
|
||||
struct SNAME##_iter_s (*it_start)(struct SNAME##_s *); \
|
||||
\
|
||||
/* Function that returns an iterator to the end of the heap */ \
|
||||
struct SNAME##_iter_s (*it_end)(struct SNAME##_s *); \
|
||||
\
|
||||
} SNAME, *SNAME##_ptr; \
|
||||
\
|
||||
/* Heap Iterator */ \
|
||||
typedef struct SNAME##_iter_s \
|
||||
{ \
|
||||
/* Target heap */ \
|
||||
struct SNAME##_s *target; \
|
||||
\
|
||||
/* Cursor's position (index) */ \
|
||||
size_t cursor; \
|
||||
\
|
||||
/* If the iterator has reached the start of the iteration */ \
|
||||
bool start; \
|
||||
\
|
||||
/* If the iterator has reached the end of the iteration */ \
|
||||
bool end; \
|
||||
\
|
||||
} SNAME##_iter, *SNAME##_iter_ptr; \
|
||||
\
|
||||
/* Collection Functions */ \
|
||||
/* Collection Allocation and Deallocation */ \
|
||||
FMOD SNAME *PFX##_new(size_t capacity, cmc_heap_order HO, int (*compare)(V, V)); \
|
||||
FMOD void PFX##_clear(SNAME *_heap_); \
|
||||
FMOD void PFX##_free(SNAME *_heap_); \
|
||||
/* Collection Input and Output */ \
|
||||
FMOD bool PFX##_insert(SNAME *_heap_, V element); \
|
||||
FMOD bool PFX##_remove(SNAME *_heap_, V *result); \
|
||||
/* Conditional Input and Output */ \
|
||||
FMOD bool PFX##_insert_if(SNAME *_heap_, V element, bool condition); \
|
||||
FMOD bool PFX##_remove_if(SNAME *_heap_, V *result, bool condition); \
|
||||
/* Element Access */ \
|
||||
FMOD V PFX##_peek(SNAME *_heap_); \
|
||||
FMOD V *PFX##_peek_ref(SNAME *_heap_); \
|
||||
/* Collection State */ \
|
||||
FMOD bool PFX##_contains(SNAME *_heap_, V element); \
|
||||
FMOD bool PFX##_empty(SNAME *_heap_); \
|
||||
FMOD bool PFX##_full(SNAME *_heap_); \
|
||||
FMOD size_t PFX##_count(SNAME *_heap_); \
|
||||
FMOD size_t PFX##_capacity(SNAME *_heap_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_heap_); \
|
||||
\
|
||||
/* Iterator Functions */ \
|
||||
/* Iterator Allocation and Deallocation */ \
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target); \
|
||||
FMOD void PFX##_iter_free(SNAME##_iter *iter); \
|
||||
/* Iterator Initialization */ \
|
||||
FMOD void PFX##_iter_init(SNAME##_iter *iter, SNAME *target); \
|
||||
/* Iterator State */ \
|
||||
FMOD bool PFX##_iter_start(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_end(SNAME##_iter *iter); \
|
||||
/* Iterator Movement */ \
|
||||
FMOD void PFX##_iter_to_start(SNAME##_iter *iter); \
|
||||
FMOD void PFX##_iter_to_end(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_next(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_prev(SNAME##_iter *iter); \
|
||||
/* Iterator Access */ \
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter); \
|
||||
FMOD size_t PFX##_iter_index(SNAME##_iter *iter); \
|
||||
\
|
||||
/* Default Value */ \
|
||||
static inline V PFX##_impl_default_value(void) \
|
||||
{ \
|
||||
V _empty_value_; \
|
||||
\
|
||||
memset(&_empty_value_, 0, sizeof(V)); \
|
||||
\
|
||||
return _empty_value_; \
|
||||
} \
|
||||
\
|
||||
/* SOURCE ********************************************************************/
|
||||
#define HEAP_GENERATE_SOURCE(PFX, SNAME, FMOD, V) \
|
||||
\
|
||||
/* Implementation Detail Functions */ \
|
||||
static bool PFX##_impl_grow(SNAME *_heap_); \
|
||||
static size_t PFX##_impl_p(size_t index); \
|
||||
static size_t PFX##_impl_l(size_t index); \
|
||||
static size_t PFX##_impl_r(size_t index); \
|
||||
static bool PFX##_impl_float_up(SNAME *_heap_, size_t index); \
|
||||
static bool PFX##_impl_float_down(SNAME *_heap_, size_t index); \
|
||||
static SNAME##_iter PFX##_impl_it_start(SNAME *_heap_); \
|
||||
static SNAME##_iter PFX##_impl_it_end(SNAME *_heap_); \
|
||||
\
|
||||
FMOD SNAME *PFX##_new(size_t capacity, HeapOrder HO, int (*compare)(V, V)) \
|
||||
FMOD SNAME *PFX##_new(size_t capacity, cmc_heap_order HO, int (*compare)(V, V)) \
|
||||
{ \
|
||||
if (capacity < 1) \
|
||||
return NULL; \
|
||||
\
|
||||
if (HO != MinHeap && HO != MaxHeap) \
|
||||
if (HO != cmc_min_heap && HO != cmc_max_heap) \
|
||||
return NULL; \
|
||||
\
|
||||
SNAME *_heap_ = malloc(sizeof(SNAME)); \
|
||||
@@ -198,7 +214,7 @@ typedef enum HeapOrder
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
if (_heap_->count == 0) \
|
||||
if (PFX##_empty(_heap_)) \
|
||||
{ \
|
||||
_heap_->buffer[_heap_->count++] = element; \
|
||||
return true; \
|
||||
@@ -292,6 +308,19 @@ typedef enum HeapOrder
|
||||
return _heap_->capacity; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_heap_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *h_ = _heap_; \
|
||||
const char *name = #SNAME; \
|
||||
const char *t = h_->HO == 1 ? "MaxHeap" : "MinHeap"; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_heap, \
|
||||
name, h_, h_->buffer, h_->capacity, h_->count, t, h_->cmp); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target) \
|
||||
{ \
|
||||
SNAME##_iter *iter = malloc(sizeof(SNAME##_iter)); \
|
||||
@@ -399,42 +428,27 @@ typedef enum HeapOrder
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
static size_t PFX##_impl_p(size_t index) \
|
||||
{ \
|
||||
if (index == 0) \
|
||||
return 0; \
|
||||
\
|
||||
return (index - 1) / 2; \
|
||||
} \
|
||||
\
|
||||
static size_t PFX##_impl_l(size_t index) \
|
||||
{ \
|
||||
return 2 * index + 1; \
|
||||
} \
|
||||
\
|
||||
static size_t PFX##_impl_r(size_t index) \
|
||||
{ \
|
||||
return 2 * index + 2; \
|
||||
} \
|
||||
\
|
||||
static bool PFX##_impl_float_up(SNAME *_heap_, size_t index) \
|
||||
{ \
|
||||
/* Current index */ \
|
||||
size_t C = index; \
|
||||
V child = _heap_->buffer[C]; \
|
||||
V parent = _heap_->buffer[PFX##_impl_p(C)]; \
|
||||
V parent = _heap_->buffer[(index - 1) / 2]; \
|
||||
\
|
||||
int mod = _heap_->HO; \
|
||||
\
|
||||
while (C > 0 && _heap_->cmp(child, parent) * mod > 0) \
|
||||
{ \
|
||||
/* Swap between C (current element) and its parent */ \
|
||||
V tmp = _heap_->buffer[C]; \
|
||||
_heap_->buffer[C] = _heap_->buffer[PFX##_impl_p(C)]; \
|
||||
_heap_->buffer[PFX##_impl_p(C)] = tmp; \
|
||||
_heap_->buffer[C] = _heap_->buffer[(C - 1) / 2]; \
|
||||
_heap_->buffer[(C - 1) / 2] = tmp; \
|
||||
\
|
||||
C = PFX##_impl_p(C); \
|
||||
/* Go to parent */ \
|
||||
C = (C - 1) / 2; \
|
||||
\
|
||||
child = _heap_->buffer[C]; \
|
||||
parent = _heap_->buffer[PFX##_impl_p(C)]; \
|
||||
parent = _heap_->buffer[(C - 1) / 2]; \
|
||||
} \
|
||||
\
|
||||
return true; \
|
||||
@@ -446,10 +460,11 @@ typedef enum HeapOrder
|
||||
\
|
||||
while (index < _heap_->count) \
|
||||
{ \
|
||||
size_t L = PFX##_impl_l(index); \
|
||||
size_t R = PFX##_impl_r(index); \
|
||||
size_t L = 2 * index + 1; \
|
||||
size_t R = 2 * index + 2; \
|
||||
size_t C = index; \
|
||||
\
|
||||
/* Determine if we swap with the left or right element */ \
|
||||
if (L < _heap_->count && _heap_->cmp(_heap_->buffer[L], _heap_->buffer[C]) * mod > 0) \
|
||||
{ \
|
||||
C = L; \
|
||||
@@ -460,6 +475,7 @@ typedef enum HeapOrder
|
||||
C = R; \
|
||||
} \
|
||||
\
|
||||
/* Swap only if either left or right was picked, otherwise done */ \
|
||||
if (C != index) \
|
||||
{ \
|
||||
V tmp = _heap_->buffer[index]; \
|
||||
|
||||
+35
-6
@@ -8,16 +8,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
/**************************************************************** LINKEDLIST */
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* LinkedList
|
||||
*
|
||||
* A LinkedList is a linear collection of elements, whose order is not given by
|
||||
* their physical placement in memory. Instead, each element points to the next
|
||||
* (singly-linked list) and to a previous one (doubly-linked list). It consists
|
||||
* of nodes connected by these next or previous pointers. Unlike an array, the
|
||||
* LinkedList can indefinitely grow.
|
||||
*
|
||||
* Implementation
|
||||
*
|
||||
* The LinkedList is implemented as a doubly-linked list and allows insertions
|
||||
* and removals at both ends in O(1) and in a given index in O(N). The list has
|
||||
* a head and tail pointer. The head points to the first element in the sequence
|
||||
* and tail points to the last.
|
||||
*/
|
||||
|
||||
#ifndef CMC_LINKEDLIST_H
|
||||
#define CMC_LINKEDLIST_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
#define LINKEDLIST_GENERATE(PFX, SNAME, FMOD, V) \
|
||||
LINKEDLIST_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
|
||||
@@ -110,6 +125,8 @@
|
||||
FMOD bool PFX##_contains(SNAME *_list_, V element, int (*comparator)(V, V)); \
|
||||
FMOD bool PFX##_empty(SNAME *_list_); \
|
||||
FMOD size_t PFX##_count(SNAME *_list_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_list_); \
|
||||
\
|
||||
/* Node Related Functions */ \
|
||||
/* Node Allocation and Deallocation */ \
|
||||
@@ -376,7 +393,7 @@
|
||||
FMOD V PFX##_front(SNAME *_list_) \
|
||||
{ \
|
||||
if (PFX##_empty(_list_)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return _list_->head->data; \
|
||||
} \
|
||||
@@ -384,7 +401,7 @@
|
||||
FMOD V PFX##_get(SNAME *_list_, size_t index) \
|
||||
{ \
|
||||
if (index >= _list_->count || PFX##_empty(_list_)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
SNAME##_node *scan = PFX##_get_node(_list_, index); \
|
||||
\
|
||||
@@ -443,6 +460,18 @@
|
||||
return _list_->count; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_list_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *l_ = _list_; \
|
||||
const char *name = #SNAME; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_linkedlist, \
|
||||
name, l_, l_->count, l_->head, l_->tail); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_node *PFX##_new_node(V element) \
|
||||
{ \
|
||||
SNAME##_node *_node_ = malloc(sizeof(SNAME##_node)); \
|
||||
|
||||
+48
-4
@@ -8,16 +8,46 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
/********************************************************************** LIST */
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* List
|
||||
*
|
||||
* A List is a Dynamic Array that store its elements contiguously. It has an
|
||||
* internal buffer that is pre-allocated with a greater capacity than necessary
|
||||
* in order to foresee new elements being added. The items can be added at both
|
||||
* ends of the list and at the middle (with a given index).
|
||||
*
|
||||
* Insertions at the start are more costly as it is necessary to shift all
|
||||
* elements one position to the right to give space to a new element being added
|
||||
* at index 0. Insertions at the end are instantaneous as long as there is
|
||||
* enough space in the buffer. Insertions at the middle will be based on an
|
||||
* index. The index represents where the element being added will be located
|
||||
* once the operation ends. This operation might also need to shift elements. It
|
||||
* is also possible to add arrays of elements in the list.
|
||||
*
|
||||
* When the buffer is filled, it is reallocated with a greater capacity, usually
|
||||
* being doubled.
|
||||
*
|
||||
* Removing elements follows the same principle. Removing the front element will
|
||||
* require to shift all other elements one position to the left, thus being
|
||||
* slower than removing from the end in which is done in constant time. Removing
|
||||
* elements in the middle of the list will also require shifting elements to the
|
||||
* left. Is is also possible to remove a range of elements or extract them,
|
||||
* creating a new list with the removed items.
|
||||
*
|
||||
* The iterator is a simple structure that is capable of going back and
|
||||
* forwards. Any modifications to the target list during iteration is considered
|
||||
* undefined behavior. Its sole purpose is to facilitate navigation through a
|
||||
* list.
|
||||
*/
|
||||
|
||||
#ifndef CMC_LIST_H
|
||||
#define CMC_LIST_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
#define LIST_GENERATE(PFX, SNAME, FMOD, V) \
|
||||
LIST_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
|
||||
@@ -104,6 +134,8 @@
|
||||
FMOD size_t PFX##_count(SNAME *_list_); \
|
||||
FMOD bool PFX##_fits(SNAME *_list_, size_t size); \
|
||||
FMOD size_t PFX##_capacity(SNAME *_list_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_list_); \
|
||||
\
|
||||
/* Iterator Functions */ \
|
||||
/* Iterator Allocation and Deallocation */ \
|
||||
@@ -520,6 +552,18 @@
|
||||
return _list_->capacity; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_list_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *l_ = _list_; \
|
||||
const char *name = #SNAME; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_list, \
|
||||
name, l_, l_->buffer, l_->capacity, l_->count); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target) \
|
||||
{ \
|
||||
SNAME##_iter *iter = malloc(sizeof(SNAME##_iter)); \
|
||||
|
||||
+57
-13
@@ -8,16 +8,46 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
/********************************************************************* QUEUE */
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* Queue
|
||||
*
|
||||
* A Queue is a First-In First-out (or Last-in Last-out) data structure. It is
|
||||
* a Dynamic Circular Array where elements are added from one end of the array
|
||||
* and removed from the other end. The circular array here (also known as
|
||||
* circular buffer or ring buffer) is very important so that both adding and
|
||||
* removing elements from the Queue are done instantly. The array is linear but
|
||||
* with the modulo operator it is treated as a circular sequence of elements.
|
||||
*
|
||||
* If the Queue was implemented as a regular Dynamic Array, when adding or
|
||||
* removing an element at the front, it would be necessary to shift all elements
|
||||
* currently present in the Queue and this would add up a lot of computing time.
|
||||
* Shifting `100000` elements in memory by one position every time an element is
|
||||
* added to the Queue is simply not efficient.
|
||||
*
|
||||
* The Queue has two ends. The `front` and `back`. In this implementation all
|
||||
* elements are added to the back of the Queue and removed from the front, which
|
||||
* is more or less how queues work in real life. Unlike a Stack that only has
|
||||
* operations at one end of the buffer, the Queue needs to be implemented as a
|
||||
* circular array in order to quickly add or remove elements.
|
||||
*
|
||||
* The Queue has three main functions: `enqueue` which adds an element to the
|
||||
* Queue; `dequeue` which removes an element from the Queue; and `peek` which
|
||||
* return the element at the front of the Queue, that is, the next element to
|
||||
* be removed from it.
|
||||
*
|
||||
* The Queue is used in many applications where a resource is shared among
|
||||
* multiple consumers and the Queue is responsible for scheduling the access to
|
||||
* the resource.
|
||||
*/
|
||||
|
||||
#ifndef CMC_QUEUE_H
|
||||
#define CMC_QUEUE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
#define QUEUE_GENERATE(PFX, SNAME, FMOD, V) \
|
||||
QUEUE_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
|
||||
@@ -48,7 +78,7 @@
|
||||
size_t front; \
|
||||
\
|
||||
/* Index representing the back of the queue */ \
|
||||
size_t rear; \
|
||||
size_t back; \
|
||||
\
|
||||
/* Function that returns an iterator to the start of the queue */ \
|
||||
struct SNAME##_iter_s (*it_start)(struct SNAME##_s *); \
|
||||
@@ -97,6 +127,8 @@
|
||||
FMOD bool PFX##_full(SNAME *_queue_); \
|
||||
FMOD size_t PFX##_count(SNAME *_queue_); \
|
||||
FMOD size_t PFX##_capacity(SNAME *_queue_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_queue_); \
|
||||
\
|
||||
/* Iterator Functions */ \
|
||||
/* Iterator Allocation and Deallocation */ \
|
||||
@@ -158,7 +190,7 @@
|
||||
_queue_->capacity = capacity; \
|
||||
_queue_->count = 0; \
|
||||
_queue_->front = 0; \
|
||||
_queue_->rear = 0; \
|
||||
_queue_->back = 0; \
|
||||
\
|
||||
_queue_->it_start = PFX##_impl_it_start; \
|
||||
_queue_->it_end = PFX##_impl_it_end; \
|
||||
@@ -172,7 +204,7 @@
|
||||
\
|
||||
_queue_->count = 0; \
|
||||
_queue_->front = 0; \
|
||||
_queue_->rear = 0; \
|
||||
_queue_->back = 0; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_free(SNAME *_queue_) \
|
||||
@@ -189,9 +221,9 @@
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
_queue_->buffer[_queue_->rear] = element; \
|
||||
_queue_->buffer[_queue_->back] = element; \
|
||||
\
|
||||
_queue_->rear = (_queue_->rear == _queue_->capacity - 1) ? 0 : _queue_->rear + 1; \
|
||||
_queue_->back = (_queue_->back == _queue_->capacity - 1) ? 0 : _queue_->back + 1; \
|
||||
_queue_->count++; \
|
||||
\
|
||||
return true; \
|
||||
@@ -229,7 +261,7 @@
|
||||
FMOD V PFX##_peek(SNAME *_queue_) \
|
||||
{ \
|
||||
if (PFX##_empty(_queue_)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return _queue_->buffer[_queue_->front]; \
|
||||
} \
|
||||
@@ -265,6 +297,18 @@
|
||||
return _queue_->capacity; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_queue_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *q_ = _queue_; \
|
||||
const char *name = #SNAME; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_queue, \
|
||||
name, q_, q_->buffer, q_->capacity, q_->count, q_->front, q_->back); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target) \
|
||||
{ \
|
||||
SNAME##_iter *iter = malloc(sizeof(SNAME##_iter)); \
|
||||
@@ -314,7 +358,7 @@
|
||||
if (PFX##_empty(iter->target)) \
|
||||
iter->cursor = 0; \
|
||||
else \
|
||||
iter->cursor = (iter->target->rear == 0) ? iter->target->capacity - 1 : iter->target->rear - 1; \
|
||||
iter->cursor = (iter->target->back == 0) ? iter->target->capacity - 1 : iter->target->back - 1; \
|
||||
\
|
||||
iter->index = iter->target->count - 1; \
|
||||
iter->start = PFX##_empty(iter->target); \
|
||||
@@ -360,7 +404,7 @@
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return iter->target->buffer[iter->cursor]; \
|
||||
} \
|
||||
@@ -398,7 +442,7 @@
|
||||
_queue_->buffer = new_buffer; \
|
||||
_queue_->capacity = new_capacity; \
|
||||
_queue_->front = 0; \
|
||||
_queue_->rear = _queue_->count; \
|
||||
_queue_->back = _queue_->count; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
|
||||
+52
-7
@@ -8,16 +8,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
/********************************************************************* STACK */
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* Stack
|
||||
*
|
||||
* A Stack is a Last-in First-out (or First-in Last-out) data structure used in
|
||||
* a variety of algorithms. It is a Dynamic Array that can only add or remove
|
||||
* its elements at the end of the buffer, in this case, it represents the top of
|
||||
* the stack.
|
||||
*
|
||||
* It has three main functions: `push` which adds an element at the top of the
|
||||
* stack; `pop` which removes the top element from the stack; and `top` which
|
||||
* returns the top element without removing it (it is also sometimes called
|
||||
* `peek`).
|
||||
*
|
||||
* A Stack is used in algorithms like backtracking, depth-first search,
|
||||
* expression evaluation, syntax parsing and many more.
|
||||
*/
|
||||
|
||||
#ifndef CMC_STACK_H
|
||||
#define CMC_STACK_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
#define STACK_GENERATE(PFX, SNAME, FMOD, V) \
|
||||
STACK_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
|
||||
@@ -88,6 +103,8 @@
|
||||
FMOD bool PFX##_full(SNAME *_stack_); \
|
||||
FMOD size_t PFX##_count(SNAME *_stack_); \
|
||||
FMOD size_t PFX##_capacity(SNAME *_stack_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_stack_); \
|
||||
\
|
||||
/* Iterator Functions */ \
|
||||
/* Iterator Allocation and Deallocation */ \
|
||||
@@ -210,7 +227,7 @@
|
||||
FMOD V PFX##_top(SNAME *_stack_) \
|
||||
{ \
|
||||
if (PFX##_empty(_stack_)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return _stack_->buffer[_stack_->count - 1]; \
|
||||
} \
|
||||
@@ -246,6 +263,18 @@
|
||||
return _stack_->capacity; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_stack_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *s_ = _stack_; \
|
||||
const char *name = #SNAME; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_stack, \
|
||||
name, s_, s_->buffer, s_->capacity, s_->count); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target) \
|
||||
{ \
|
||||
SNAME##_iter *iter = malloc(sizeof(SNAME##_iter)); \
|
||||
@@ -266,7 +295,12 @@
|
||||
FMOD void PFX##_iter_init(SNAME##_iter *iter, SNAME *target) \
|
||||
{ \
|
||||
iter->target = target; \
|
||||
iter->cursor = iter->target->count - 1; \
|
||||
\
|
||||
if (PFX##_empty(target)) \
|
||||
iter->cursor = 0; \
|
||||
else \
|
||||
iter->cursor = iter->target->count - 1; \
|
||||
\
|
||||
iter->start = true; \
|
||||
iter->end = PFX##_empty(target); \
|
||||
} \
|
||||
@@ -303,7 +337,11 @@
|
||||
iter->start = PFX##_empty(iter->target); \
|
||||
\
|
||||
if (iter->cursor == 0) \
|
||||
{ \
|
||||
iter->end = true; \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
else \
|
||||
iter->cursor--; \
|
||||
\
|
||||
@@ -318,7 +356,11 @@
|
||||
iter->end = PFX##_empty(iter->target); \
|
||||
\
|
||||
if (iter->cursor == iter->target->count - 1) \
|
||||
{ \
|
||||
iter->start = true; \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
else \
|
||||
iter->cursor++; \
|
||||
\
|
||||
@@ -328,7 +370,7 @@
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return iter->target->buffer[iter->cursor]; \
|
||||
} \
|
||||
@@ -343,6 +385,9 @@
|
||||
\
|
||||
FMOD size_t PFX##_iter_index(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
return 0; \
|
||||
\
|
||||
return iter->target->count - 1 - iter->cursor; \
|
||||
} \
|
||||
\
|
||||
|
||||
+41
-8
@@ -8,16 +8,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
/******************************************************************* TREEMAP */
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* TreeMap
|
||||
*
|
||||
* A TreeMap is an implementation of a Map that keeps its keys sorted. Like a
|
||||
* Map, it has only unique keys. This implementation uses a balanced binary
|
||||
* tree called AVL Tree that uses the height of nodes to keep its keys balanced.
|
||||
*/
|
||||
|
||||
#ifndef CMC_TREEMAP_H
|
||||
#define CMC_TREEMAP_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
#define TREEMAP_GENERATE(PFX, SNAME, FMOD, K, V) \
|
||||
TREEMAP_GENERATE_HEADER(PFX, SNAME, FMOD, K, V) \
|
||||
@@ -44,10 +50,10 @@
|
||||
/* Key comparison function */ \
|
||||
int (*cmp)(K, K); \
|
||||
\
|
||||
/* Function that returns an iterator to the start of the hashmap */ \
|
||||
/* Function that returns an iterator to the start of the treemap */ \
|
||||
struct SNAME##_iter_s (*it_start)(struct SNAME##_s *); \
|
||||
\
|
||||
/* Function that returns an iterator to the end of the hashmap */ \
|
||||
/* Function that returns an iterator to the end of the treemap */ \
|
||||
struct SNAME##_iter_s (*it_end)(struct SNAME##_s *); \
|
||||
\
|
||||
} SNAME, *SNAME##_ptr; \
|
||||
@@ -108,6 +114,7 @@
|
||||
FMOD void PFX##_free(SNAME *_map_); \
|
||||
/* Collection Input and Output */ \
|
||||
FMOD bool PFX##_insert(SNAME *_map_, K key, V value); \
|
||||
FMOD bool PFX##_update(SNAME *_map_, K key, V new_value); \
|
||||
FMOD bool PFX##_remove(SNAME *_map_, K key, V *value); \
|
||||
/* Conditional Input and Output */ \
|
||||
FMOD bool PFX##_insert_if(SNAME *_map_, K key, V value, bool condition); \
|
||||
@@ -121,6 +128,8 @@
|
||||
FMOD bool PFX##_contains(SNAME *_map_, K key); \
|
||||
FMOD bool PFX##_empty(SNAME *_map_); \
|
||||
FMOD size_t PFX##_count(SNAME *_map_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_map_); \
|
||||
\
|
||||
/* Iterator Functions */ \
|
||||
/* Iterator Allocation and Deallocation */ \
|
||||
@@ -312,6 +321,18 @@
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_update(SNAME *_map_, K key, V new_value) \
|
||||
{ \
|
||||
SNAME##_node *node = PFX##_impl_get_node(_map_, key); \
|
||||
\
|
||||
if (!node) \
|
||||
return false; \
|
||||
\
|
||||
node->value = new_value; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_remove(SNAME *_map_, K key, V *value) \
|
||||
{ \
|
||||
SNAME##_node *node = PFX##_impl_get_node(_map_, key); \
|
||||
@@ -532,6 +553,18 @@
|
||||
return _map_->count; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_map_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *m_ = _map_; \
|
||||
const char *name = #SNAME; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_treemap, \
|
||||
name, m_, m_->root, m_->count, m_->cmp); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target) \
|
||||
{ \
|
||||
SNAME##_iter *iter = malloc(sizeof(SNAME##_iter)); \
|
||||
@@ -677,7 +710,7 @@
|
||||
FMOD K PFX##_iter_key(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
PFX##_impl_default_key(); \
|
||||
return PFX##_impl_default_key(); \
|
||||
\
|
||||
return iter->cursor->key; \
|
||||
} \
|
||||
@@ -685,7 +718,7 @@
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return iter->cursor->value; \
|
||||
} \
|
||||
|
||||
+32
-11
@@ -8,16 +8,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
/******************************************************************* TREESET */
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* TreeSet
|
||||
*
|
||||
* A TreeSet is an implementation of a Set that keeps its elements sorted. Like
|
||||
* a Set it has only unique keys. This implementation uses a balanced binary
|
||||
* tree called AVL Tree that uses the height of nodes to keep its keys balanced.
|
||||
*/
|
||||
|
||||
#ifndef CMC_TREESET_H
|
||||
#define CMC_TREESET_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
#define TREESET_GENERATE(PFX, SNAME, FMOD, V) \
|
||||
TREESET_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
|
||||
@@ -44,10 +50,10 @@
|
||||
/* Element comparison function */ \
|
||||
int (*cmp)(V, V); \
|
||||
\
|
||||
/* Function that returns an iterator to the start of the hashset */ \
|
||||
/* Function that returns an iterator to the start of the treeset */ \
|
||||
struct SNAME##_iter_s (*it_start)(struct SNAME##_s *); \
|
||||
\
|
||||
/* Function that returns an iterator to the end of the hashset */ \
|
||||
/* Function that returns an iterator to the end of the treeset */ \
|
||||
struct SNAME##_iter_s (*it_end)(struct SNAME##_s *); \
|
||||
\
|
||||
} SNAME, *SNAME##_ptr; \
|
||||
@@ -95,6 +101,7 @@
|
||||
\
|
||||
/* If the iterator has reached the end of the iteration */ \
|
||||
bool end; \
|
||||
\
|
||||
} SNAME##_iter, *SNAME##_iter_ptr; \
|
||||
\
|
||||
/* Collection Functions */ \
|
||||
@@ -115,6 +122,8 @@
|
||||
FMOD bool PFX##_contains(SNAME *_set_, V element); \
|
||||
FMOD bool PFX##_empty(SNAME *_set_); \
|
||||
FMOD size_t PFX##_count(SNAME *_set_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_map_); \
|
||||
\
|
||||
/* Set Operations */ \
|
||||
FMOD SNAME *PFX##_union(SNAME *_set1_, SNAME *_set2_); \
|
||||
@@ -499,12 +508,24 @@
|
||||
return _set_->count; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_set_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *s_ = _set_; \
|
||||
const char *name = #SNAME; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_treeset, \
|
||||
name, s_, s_->root, s_->count, s_->cmp); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME *PFX##_union(SNAME *_set1_, SNAME *_set2_) \
|
||||
{ \
|
||||
SNAME *_set_r_ = PFX##_new(_set1_->cmp); \
|
||||
\
|
||||
if (!_set_r_) \
|
||||
return false; \
|
||||
return NULL; \
|
||||
\
|
||||
SNAME##_iter iter1, iter2; \
|
||||
PFX##_iter_init(&iter1, _set1_); \
|
||||
@@ -528,7 +549,7 @@
|
||||
SNAME *_set_r_ = PFX##_new(_set1_->cmp); \
|
||||
\
|
||||
if (!_set_r_) \
|
||||
return false; \
|
||||
return NULL; \
|
||||
\
|
||||
SNAME *_set_A_ = _set1_->count < _set2_->count ? _set1_ : _set2_; \
|
||||
SNAME *_set_B_ = _set_A_ == _set1_ ? _set2_ : _set1_; \
|
||||
@@ -552,7 +573,7 @@
|
||||
SNAME *_set_r_ = PFX##_new(_set1_->cmp); \
|
||||
\
|
||||
if (!_set_r_) \
|
||||
return false; \
|
||||
return NULL; \
|
||||
\
|
||||
SNAME##_iter iter; \
|
||||
PFX##_iter_init(&iter, _set1_); \
|
||||
@@ -573,7 +594,7 @@
|
||||
SNAME *_set_r_ = PFX##_new(_set1_->cmp); \
|
||||
\
|
||||
if (!_set_r_) \
|
||||
return false; \
|
||||
return NULL; \
|
||||
\
|
||||
SNAME##_iter iter1, iter2; \
|
||||
PFX##_iter_init(&iter1, _set1_); \
|
||||
@@ -847,7 +868,7 @@
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return iter->cursor->value; \
|
||||
} \
|
||||
|
||||
@@ -0,0 +1,841 @@
|
||||
/**
|
||||
* intervalheap.h
|
||||
*
|
||||
* Creation Date: 06/07/2019
|
||||
*
|
||||
* Authors:
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* An interval heap is a specialized heap with:
|
||||
*
|
||||
* - O(1) - Find Min
|
||||
* - O(1) - Find Max
|
||||
* - O(log n) - Insert
|
||||
* - O(log n) - Remove Min
|
||||
* - O(log n) - Remove Max
|
||||
*/
|
||||
|
||||
#ifndef CMC_INTERVALHEAP_H
|
||||
#define CMC_INTERVALHEAP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
#define INTERVALHEAP_GENERATE(PFX, SNAME, FMOD, V) \
|
||||
INTERVALHEAP_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
|
||||
INTERVALHEAP_GENERATE_SOURCE(PFX, SNAME, FMOD, V)
|
||||
|
||||
#define INTERVALHEAP_WRAPGEN_HEADER(PFX, SNAME, FMOD, K, V) \
|
||||
INTERVALHEAP_GENERATE_HEADER(PFX, SNAME, FMOD, V)
|
||||
|
||||
#define INTERVALHEAP_WRAPGEN_SOURCE(PFX, SNAME, FMOD, K, V) \
|
||||
INTERVALHEAP_GENERATE_SOURCE(PFX, SNAME, FMOD, V)
|
||||
|
||||
/* HEADER ********************************************************************/
|
||||
#define INTERVALHEAP_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
|
||||
\
|
||||
/* Heap Structure */ \
|
||||
typedef struct SNAME##_s \
|
||||
{ \
|
||||
/* Dynamic array of nodes */ \
|
||||
struct SNAME##_node_s *buffer; \
|
||||
\
|
||||
/* Current array capacity (how many nodes can be stored) */ \
|
||||
size_t capacity; \
|
||||
\
|
||||
/* Current amount of nodes in the dynamic array */ \
|
||||
size_t size; \
|
||||
\
|
||||
/* Current amount of elements in the heap */ \
|
||||
size_t count; \
|
||||
\
|
||||
/* Element comparison function */ \
|
||||
int (*cmp)(V, V); \
|
||||
\
|
||||
/* Function that returns an iterator to the start of the heap */ \
|
||||
struct SNAME##_iter_s (*it_start)(struct SNAME##_s *); \
|
||||
\
|
||||
/* Function that returns an iterator to the end of the heap */ \
|
||||
struct SNAME##_iter_s (*it_end)(struct SNAME##_s *); \
|
||||
\
|
||||
} SNAME, *SNAME##_ptr; \
|
||||
\
|
||||
/* Heap Node */ \
|
||||
typedef struct SNAME##_node_s \
|
||||
{ \
|
||||
/* 0 - Value belonging to the MinHeap */ \
|
||||
/* 1 - Value belonging to the MaxHeap */ \
|
||||
V data[2]; \
|
||||
\
|
||||
} SNAME##_node, *SNAME##_node_ptr; \
|
||||
\
|
||||
/* Heap Iterator */ \
|
||||
typedef struct SNAME##_iter_s \
|
||||
{ \
|
||||
/* Target heap */ \
|
||||
struct SNAME##_s *target; \
|
||||
\
|
||||
/* Cursor's position (index) */ \
|
||||
size_t cursor; \
|
||||
\
|
||||
/* If the iterator has reached the start of the iteration */ \
|
||||
bool start; \
|
||||
\
|
||||
/* If the iterator has reached the end of the iteration */ \
|
||||
bool end; \
|
||||
\
|
||||
} SNAME##_iter, *SNAME##_iter_ptr; \
|
||||
\
|
||||
/* Collection Functions */ \
|
||||
/* Collection Allocation and Deallocation */ \
|
||||
FMOD SNAME *PFX##_new(size_t capacity, int (*compare)(V, V)); \
|
||||
FMOD void PFX##_clear(SNAME *_heap_); \
|
||||
FMOD void PFX##_free(SNAME *_heap_); \
|
||||
/* Collection Input and Output */ \
|
||||
FMOD bool PFX##_insert(SNAME *_heap_, V element); \
|
||||
FMOD bool PFX##_remove_max(SNAME *_heap_, V *result); \
|
||||
FMOD bool PFX##_remove_min(SNAME *_heap_, V *result); \
|
||||
/* Conditional Input and Output */ \
|
||||
FMOD bool PFX##_insert_if(SNAME *_heap_, V element, bool condition); \
|
||||
FMOD bool PFX##_remove_max_if(SNAME *_heap_, V *result, bool condition); \
|
||||
FMOD bool PFX##_remove_min_if(SNAME *_heap_, V *result, bool condition); \
|
||||
/* Collection Update */ \
|
||||
FMOD bool PFX##_update_max(SNAME *_heap_, V element); \
|
||||
FMOD bool PFX##_update_min(SNAME *_heap_, V element); \
|
||||
/* Element Access */ \
|
||||
FMOD bool PFX##_max(SNAME *_heap_, V *value); \
|
||||
FMOD bool PFX##_min(SNAME *_heap_, V *value); \
|
||||
/* Collection State */ \
|
||||
FMOD bool PFX##_contains(SNAME *_heap_, V element); \
|
||||
FMOD bool PFX##_empty(SNAME *_heap_); \
|
||||
FMOD bool PFX##_full(SNAME *_heap_); \
|
||||
FMOD size_t PFX##_count(SNAME *_heap_); \
|
||||
FMOD size_t PFX##_capacity(SNAME *_heap_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_heap_); \
|
||||
\
|
||||
/* Iterator Functions */ \
|
||||
/* Iterator Allocation and Deallocation */ \
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target); \
|
||||
FMOD void PFX##_iter_free(SNAME##_iter *iter); \
|
||||
/* Iterator Initialization */ \
|
||||
FMOD void PFX##_iter_init(SNAME##_iter *iter, SNAME *target); \
|
||||
/* Iterator State */ \
|
||||
FMOD bool PFX##_iter_start(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_end(SNAME##_iter *iter); \
|
||||
/* Iterator Movement */ \
|
||||
FMOD void PFX##_iter_to_start(SNAME##_iter *iter); \
|
||||
FMOD void PFX##_iter_to_end(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_next(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_prev(SNAME##_iter *iter); \
|
||||
/* Iterator Access */ \
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter); \
|
||||
FMOD size_t PFX##_iter_index(SNAME##_iter *iter); \
|
||||
\
|
||||
/* Default Value */ \
|
||||
static inline V PFX##_impl_default_value(void) \
|
||||
{ \
|
||||
V _empty_value_; \
|
||||
\
|
||||
memset(&_empty_value_, 0, sizeof(V)); \
|
||||
\
|
||||
return _empty_value_; \
|
||||
} \
|
||||
\
|
||||
/* SOURCE ********************************************************************/
|
||||
#define INTERVALHEAP_GENERATE_SOURCE(PFX, SNAME, FMOD, V) \
|
||||
\
|
||||
/* Implementation Detail Functions */ \
|
||||
static bool PFX##_impl_grow(SNAME *_heap_); \
|
||||
static void PFX##_impl_float_up_max(SNAME *_heap_); \
|
||||
static void PFX##_impl_float_up_min(SNAME *_heap_); \
|
||||
static void PFX##_impl_float_down_max(SNAME *_heap_); \
|
||||
static void PFX##_impl_float_down_min(SNAME *_heap_); \
|
||||
static SNAME##_iter PFX##_impl_it_start(SNAME *_heap_); \
|
||||
static SNAME##_iter PFX##_impl_it_end(SNAME *_heap_); \
|
||||
\
|
||||
FMOD SNAME *PFX##_new(size_t capacity, int (*compare)(V, V)) \
|
||||
{ \
|
||||
SNAME *_heap_ = malloc(sizeof(SNAME)); \
|
||||
\
|
||||
if (!_heap_) \
|
||||
return NULL; \
|
||||
\
|
||||
/* Since each node can store two elements, divide the actual capacity by 2 */ \
|
||||
/* Round the capacity of nodes up */ \
|
||||
capacity = capacity % 2 == 0 ? capacity / 2 : (capacity + 1) / 2; \
|
||||
\
|
||||
_heap_->buffer = malloc(sizeof(SNAME##_node) * capacity); \
|
||||
\
|
||||
if (!_heap_->buffer) \
|
||||
{ \
|
||||
free(_heap_); \
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
memset(_heap_->buffer, 0, sizeof(SNAME##_node) * capacity); \
|
||||
\
|
||||
_heap_->capacity = capacity; \
|
||||
_heap_->size = 0; \
|
||||
_heap_->count = 0; \
|
||||
_heap_->cmp = compare; \
|
||||
\
|
||||
_heap_->it_start = PFX##_impl_it_start; \
|
||||
_heap_->it_end = PFX##_impl_it_end; \
|
||||
\
|
||||
return _heap_; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_clear(SNAME *_heap_) \
|
||||
{ \
|
||||
memset(_heap_->buffer, 0, sizeof(V) * _heap_->capacity); \
|
||||
\
|
||||
_heap_->size = 0; \
|
||||
_heap_->count = 0; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_free(SNAME *_heap_) \
|
||||
{ \
|
||||
free(_heap_->buffer); \
|
||||
\
|
||||
free(_heap_); \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_insert(SNAME *_heap_, V element) \
|
||||
{ \
|
||||
if (PFX##_full(_heap_)) \
|
||||
{ \
|
||||
if (!PFX##_impl_grow(_heap_)) \
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
if (PFX##_count(_heap_) % 2 == 0) \
|
||||
{ \
|
||||
/* Occupying a new node */ \
|
||||
_heap_->buffer[_heap_->size].data[0] = element; \
|
||||
_heap_->buffer[_heap_->size].data[1] = PFX##_impl_default_value(); \
|
||||
\
|
||||
_heap_->size++; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
SNAME##_node *curr_node = &(_heap_->buffer[_heap_->size - 1]); \
|
||||
\
|
||||
/* Decide if the new element goes into the MinHeap or MaxHeap */ \
|
||||
if (_heap_->cmp(curr_node->data[0], element) > 0) \
|
||||
{ \
|
||||
/* Swap current value and add new element to the MinHeap */ \
|
||||
curr_node->data[1] = curr_node->data[0]; \
|
||||
curr_node->data[0] = element; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/* No need to swap and add the new element to the MaxHeap */ \
|
||||
curr_node->data[1] = element; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
_heap_->count++; \
|
||||
\
|
||||
if (PFX##_count(_heap_) <= 2) \
|
||||
return true; \
|
||||
\
|
||||
/* Determine wheather to do a MaxHeap insert or a MinHeap insert */ \
|
||||
SNAME##_node *parent = &(_heap_->buffer[(_heap_->size - 1) / 2]); \
|
||||
\
|
||||
if (_heap_->cmp(parent->data[0], element) > 0) \
|
||||
PFX##_impl_float_up_min(_heap_); \
|
||||
else if (_heap_->cmp(parent->data[1], element) < 0) \
|
||||
PFX##_impl_float_up_max(_heap_); \
|
||||
/* else no float up required */ \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_remove_max(SNAME *_heap_, V *result) \
|
||||
{ \
|
||||
if (PFX##_empty(_heap_)) \
|
||||
return false; \
|
||||
\
|
||||
if (PFX##_count(_heap_) == 1) \
|
||||
{ \
|
||||
*result = _heap_->buffer[0].data[0]; \
|
||||
\
|
||||
_heap_->buffer[0].data[0] = PFX##_impl_default_value(); \
|
||||
\
|
||||
_heap_->count--; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
else \
|
||||
*result = _heap_->buffer[0].data[1]; \
|
||||
\
|
||||
/* Swap the result with the last element in the MaxHeap and float it down */ \
|
||||
SNAME##_node *last_node = &(_heap_->buffer[_heap_->size - 1]); \
|
||||
\
|
||||
if (PFX##_count(_heap_) % 2 == 1) \
|
||||
{ \
|
||||
/* Grab from MinHeap and discard last node */ \
|
||||
_heap_->buffer[0].data[1] = last_node->data[0]; \
|
||||
\
|
||||
last_node->data[0] = PFX##_impl_default_value(); \
|
||||
\
|
||||
_heap_->size--; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
_heap_->buffer[0].data[1] = last_node->data[1]; \
|
||||
\
|
||||
last_node->data[1] = PFX##_impl_default_value(); \
|
||||
} \
|
||||
\
|
||||
_heap_->count--; \
|
||||
\
|
||||
/* FLoat Down on the MaxHeap */ \
|
||||
PFX##_impl_float_down_max(_heap_); \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_remove_min(SNAME *_heap_, V *result) \
|
||||
{ \
|
||||
if (PFX##_empty(_heap_)) \
|
||||
return false; \
|
||||
\
|
||||
*result = _heap_->buffer[0].data[0]; \
|
||||
\
|
||||
if (PFX##_count(_heap_) == 1) \
|
||||
{ \
|
||||
_heap_->buffer[0].data[0] = PFX##_impl_default_value(); \
|
||||
\
|
||||
_heap_->count--; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
/* Swap the result with the last element in the MinHeap and float it down */ \
|
||||
SNAME##_node *last_node = &(_heap_->buffer[_heap_->size - 1]); \
|
||||
\
|
||||
_heap_->buffer[0].data[0] = last_node->data[0]; \
|
||||
\
|
||||
if (PFX##_count(_heap_) % 2 == 1) \
|
||||
{ \
|
||||
/* Discard last node */ \
|
||||
last_node->data[0] = PFX##_impl_default_value(); \
|
||||
\
|
||||
_heap_->size--; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/* Place the MaxHeap value in the MinHeap value spot */ \
|
||||
last_node->data[0] = last_node->data[1]; \
|
||||
last_node->data[1] = PFX##_impl_default_value(); \
|
||||
} \
|
||||
\
|
||||
_heap_->count--; \
|
||||
\
|
||||
/* FLoat Down on the MinHeap */ \
|
||||
PFX##_impl_float_down_min(_heap_); \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_insert_if(SNAME *_heap_, V element, bool condition) \
|
||||
{ \
|
||||
if (condition) \
|
||||
return PFX##_insert(_heap_, element); \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_remove_max_if(SNAME *_heap_, V *result, bool condition) \
|
||||
{ \
|
||||
if (condition) \
|
||||
return PFX##_remove_max(_heap_, result); \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_remove_min_if(SNAME *_heap_, V *result, bool condition) \
|
||||
{ \
|
||||
if (condition) \
|
||||
return PFX##_remove_min(_heap_, result); \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_update_max(SNAME *_heap_, V element) \
|
||||
{ \
|
||||
if (PFX##_empty(_heap_)) \
|
||||
return false; \
|
||||
\
|
||||
if (PFX##_count(_heap_) == 1) \
|
||||
{ \
|
||||
_heap_->buffer[0].data[0] = element; \
|
||||
} \
|
||||
else if (_heap_->cmp(element, _heap_->buffer[0].data[0]) < 0) \
|
||||
{ \
|
||||
/* Corner case: we are updating the Max value but it is less than */ \
|
||||
/* the Min value */ \
|
||||
_heap_->buffer[0].data[1] = _heap_->buffer[0].data[0]; \
|
||||
_heap_->buffer[0].data[0] = element; \
|
||||
\
|
||||
PFX##_impl_float_down_max(_heap_); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/* Update Max element and float it down */ \
|
||||
_heap_->buffer[0].data[1] = element; \
|
||||
\
|
||||
PFX##_impl_float_down_max(_heap_); \
|
||||
} \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_update_min(SNAME *_heap_, V element) \
|
||||
{ \
|
||||
if (PFX##_empty(_heap_)) \
|
||||
return false; \
|
||||
\
|
||||
if (PFX##_count(_heap_) == 1) \
|
||||
{ \
|
||||
_heap_->buffer[0].data[0] = element; \
|
||||
} \
|
||||
else if (_heap_->cmp(element, _heap_->buffer[0].data[1]) > 0) \
|
||||
{ \
|
||||
/* Corner case: we are updating the Min value but it is greater */ \
|
||||
/* than the Max value. */ \
|
||||
_heap_->buffer[0].data[0] = _heap_->buffer[0].data[1]; \
|
||||
_heap_->buffer[0].data[1] = element; \
|
||||
\
|
||||
PFX##_impl_float_down_min(_heap_); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/* Update Min element and float it down */ \
|
||||
_heap_->buffer[0].data[0] = element; \
|
||||
\
|
||||
PFX##_impl_float_down_min(_heap_); \
|
||||
} \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_max(SNAME *_heap_, V *value) \
|
||||
{ \
|
||||
if (PFX##_empty(_heap_)) \
|
||||
return false; \
|
||||
\
|
||||
/* If there is only one element, then the maximum element is the same */ \
|
||||
/* as the one in the MinHeap */ \
|
||||
if (PFX##_count(_heap_) == 1) \
|
||||
*value = _heap_->buffer[0].data[0]; \
|
||||
else \
|
||||
*value = _heap_->buffer[0].data[1]; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_min(SNAME *_heap_, V *value) \
|
||||
{ \
|
||||
if (PFX##_empty(_heap_)) \
|
||||
return false; \
|
||||
\
|
||||
*value = _heap_->buffer[0].data[0]; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_contains(SNAME *_heap_, V element) \
|
||||
{ \
|
||||
for (size_t i = 0; i < _heap_->count; i++) \
|
||||
{ \
|
||||
if (_heap_->cmp(_heap_->buffer[i / 2].data[i % 2], element) == 0) \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_empty(SNAME *_heap_) \
|
||||
{ \
|
||||
return _heap_->count == 0; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_full(SNAME *_heap_) \
|
||||
{ \
|
||||
/* The heap is full if all nodes are completely filled */ \
|
||||
return _heap_->size >= _heap_->capacity && _heap_->count % 2 == 0; \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_count(SNAME *_heap_) \
|
||||
{ \
|
||||
return _heap_->count; \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_capacity(SNAME *_heap_) \
|
||||
{ \
|
||||
return _heap_->capacity; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_heap_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *h_ = _heap_; \
|
||||
const char *name = #SNAME; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_intervalheap, \
|
||||
name, h_, h_->buffer, h_->capacity, h_->size, h_->count, h_->cmp); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target) \
|
||||
{ \
|
||||
SNAME##_iter *iter = malloc(sizeof(SNAME##_iter)); \
|
||||
\
|
||||
if (!iter) \
|
||||
return NULL; \
|
||||
\
|
||||
PFX##_iter_init(iter, target); \
|
||||
\
|
||||
return iter; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_iter_free(SNAME##_iter *iter) \
|
||||
{ \
|
||||
free(iter); \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_iter_init(SNAME##_iter *iter, SNAME *target) \
|
||||
{ \
|
||||
iter->target = target; \
|
||||
iter->cursor = 0; \
|
||||
iter->start = true; \
|
||||
iter->end = PFX##_empty(target); \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_start(SNAME##_iter *iter) \
|
||||
{ \
|
||||
return PFX##_empty(iter->target) || iter->start; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_end(SNAME##_iter *iter) \
|
||||
{ \
|
||||
return PFX##_empty(iter->target) || iter->end; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_iter_to_start(SNAME##_iter *iter) \
|
||||
{ \
|
||||
iter->cursor = 0; \
|
||||
iter->start = true; \
|
||||
iter->end = PFX##_empty(iter->target); \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_iter_to_end(SNAME##_iter *iter) \
|
||||
{ \
|
||||
iter->cursor = iter->target->count - 1; \
|
||||
iter->start = PFX##_empty(iter->target); \
|
||||
iter->end = true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_next(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (iter->end) \
|
||||
return false; \
|
||||
\
|
||||
iter->start = false; \
|
||||
\
|
||||
if (iter->cursor == iter->target->count - 1) \
|
||||
iter->end = true; \
|
||||
else \
|
||||
iter->cursor++; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_prev(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (iter->start) \
|
||||
return false; \
|
||||
\
|
||||
iter->end = false; \
|
||||
\
|
||||
if (iter->cursor == 0) \
|
||||
iter->start = true; \
|
||||
else \
|
||||
iter->cursor--; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return iter->target->buffer[iter->cursor / 2].data[iter->cursor % 2]; \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_iter_index(SNAME##_iter *iter) \
|
||||
{ \
|
||||
return iter->cursor; \
|
||||
} \
|
||||
\
|
||||
static bool PFX##_impl_grow(SNAME *_heap_) \
|
||||
{ \
|
||||
size_t new_cap = _heap_->capacity * 2; \
|
||||
\
|
||||
SNAME##_node *new_buffer = realloc(_heap_->buffer, sizeof(SNAME##_node) * new_cap); \
|
||||
\
|
||||
if (!new_buffer) \
|
||||
return false; \
|
||||
\
|
||||
memset(new_buffer + _heap_->capacity, 0, sizeof(SNAME##_node) * _heap_->capacity); \
|
||||
\
|
||||
_heap_->buffer = new_buffer; \
|
||||
_heap_->capacity = new_cap; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
static void PFX##_impl_float_up_max(SNAME *_heap_) \
|
||||
{ \
|
||||
size_t index = _heap_->size - 1; \
|
||||
\
|
||||
SNAME##_node *curr_node = &(_heap_->buffer[index]); \
|
||||
\
|
||||
/* FLoat Up on the MaxHeap */ \
|
||||
while (index > 0) \
|
||||
{ \
|
||||
/* Parent index */ \
|
||||
size_t P_index = (index - 1) / 2; \
|
||||
\
|
||||
SNAME##_node *parent = &(_heap_->buffer[P_index]); \
|
||||
\
|
||||
if (index == _heap_->size - 1 && PFX##_count(_heap_) % 2 != 0) \
|
||||
{ \
|
||||
/* In this case, the current node has no MaxHeap value so we */ \
|
||||
/* instead compare with the MinHeap value */ \
|
||||
if (_heap_->cmp(curr_node->data[0], parent->data[1]) < 0) \
|
||||
break; \
|
||||
\
|
||||
/* Since the comparison above passed now we need to swap the */ \
|
||||
/* current node with the parent but it will have to be done */ \
|
||||
/* with the node's MinHeap value since the MaxHeap value */ \
|
||||
/* doesn't exist */ \
|
||||
V tmp = curr_node->data[0]; \
|
||||
curr_node->data[0] = parent->data[1]; \
|
||||
parent->data[1] = tmp; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/* Usual case, just compare both MaxHeap values */ \
|
||||
if (_heap_->cmp(curr_node->data[1], parent->data[1]) < 0) \
|
||||
break; \
|
||||
\
|
||||
/* Swap with parent and repeat */ \
|
||||
V tmp = curr_node->data[1]; \
|
||||
curr_node->data[1] = parent->data[1]; \
|
||||
parent->data[1] = tmp; \
|
||||
} \
|
||||
\
|
||||
/* Update indexe and node */ \
|
||||
index = P_index; \
|
||||
curr_node = parent; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static void PFX##_impl_float_up_min(SNAME *_heap_) \
|
||||
{ \
|
||||
size_t index = _heap_->size - 1; \
|
||||
\
|
||||
SNAME##_node *curr_node = &(_heap_->buffer[index]); \
|
||||
\
|
||||
/* FLoat Up on the MinHeap */ \
|
||||
while (index > 0) \
|
||||
{ \
|
||||
/* Parent index */ \
|
||||
size_t P_index = (index - 1) / 2; \
|
||||
\
|
||||
SNAME##_node *parent = &(_heap_->buffer[P_index]); \
|
||||
\
|
||||
if (_heap_->cmp(curr_node->data[0], parent->data[0]) >= 0) \
|
||||
break; \
|
||||
\
|
||||
/* Swap with parent and repeat */ \
|
||||
V tmp = curr_node->data[0]; \
|
||||
curr_node->data[0] = parent->data[0]; \
|
||||
parent->data[0] = tmp; \
|
||||
\
|
||||
/* Update indexe and node */ \
|
||||
index = P_index; \
|
||||
curr_node = parent; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static void PFX##_impl_float_down_max(SNAME *_heap_) \
|
||||
{ \
|
||||
/* Floats Down from the MinHeap */ \
|
||||
size_t index = 0; \
|
||||
\
|
||||
SNAME##_node *curr_node = &(_heap_->buffer[index]); \
|
||||
\
|
||||
while (true) \
|
||||
{ \
|
||||
/* If there are no children it is done */ \
|
||||
if (2 * index + 1 >= _heap_->size) \
|
||||
break; \
|
||||
\
|
||||
/* Child index to be compared with current value */ \
|
||||
size_t child; \
|
||||
size_t L_index = index * 2 + 1; \
|
||||
size_t R_index = index * 2 + 2; \
|
||||
\
|
||||
/* If there are two children, pick the smallest one */ \
|
||||
if (R_index < _heap_->size) \
|
||||
{ \
|
||||
SNAME##_node *L = &(_heap_->buffer[L_index]); \
|
||||
SNAME##_node *R = &(_heap_->buffer[R_index]); \
|
||||
\
|
||||
/* If the right child is the last node and there is no MaxHeap value */ \
|
||||
/* then do the comparison with the MinHeap value */ \
|
||||
if (R_index == _heap_->size - 1 && PFX##_count(_heap_) % 2 != 0) \
|
||||
child = _heap_->cmp(L->data[1], R->data[0]) > 0 ? L_index : R_index; \
|
||||
else \
|
||||
child = _heap_->cmp(L->data[1], R->data[1]) > 0 ? L_index : R_index; \
|
||||
} \
|
||||
/* Pick the only one available */ \
|
||||
else \
|
||||
child = L_index; \
|
||||
\
|
||||
SNAME##_node *child_node = &(_heap_->buffer[child]); \
|
||||
\
|
||||
/* Again, check if the child node is the last one and has no MaxHeap value */ \
|
||||
if (child == _heap_->size - 1 && PFX##_count(_heap_) % 2 != 0) \
|
||||
{ \
|
||||
/* Odd case, compare with MinHeap value */ \
|
||||
/* If current value is not less than the child node's value, it is done */ \
|
||||
if (_heap_->cmp(curr_node->data[1], child_node->data[0]) >= 0) \
|
||||
break; \
|
||||
\
|
||||
/* Otherwise swap and continue */ \
|
||||
V tmp = child_node->data[0]; \
|
||||
child_node->data[0] = curr_node->data[1]; \
|
||||
curr_node->data[1] = tmp; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/* If current value is not less than the child node's value, it is done */ \
|
||||
if (_heap_->cmp(curr_node->data[1], child_node->data[1]) >= 0) \
|
||||
break; \
|
||||
\
|
||||
/* Otherwise swap and continue */ \
|
||||
V tmp = child_node->data[1]; \
|
||||
child_node->data[1] = curr_node->data[1]; \
|
||||
curr_node->data[1] = tmp; \
|
||||
\
|
||||
/* Check if the MinHeap and MaxHeap values need to be swapped */ \
|
||||
if (_heap_->cmp(child_node->data[0], child_node->data[1]) > 0) \
|
||||
{ \
|
||||
/* Swap because the MinHeap value is greater than the MaxHeap value */ \
|
||||
tmp = child_node->data[0]; \
|
||||
child_node->data[0] = child_node->data[1]; \
|
||||
child_node->data[1] = tmp; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
/* Update index and node */ \
|
||||
index = child; \
|
||||
curr_node = child_node; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static void PFX##_impl_float_down_min(SNAME *_heap_) \
|
||||
{ \
|
||||
/* Floats Down from the MinHeap */ \
|
||||
size_t index = 0; \
|
||||
\
|
||||
SNAME##_node *curr_node = &(_heap_->buffer[index]); \
|
||||
\
|
||||
while (true) \
|
||||
{ \
|
||||
/* If there are no children it is done */ \
|
||||
if (2 * index + 1 >= _heap_->size) \
|
||||
break; \
|
||||
\
|
||||
/* Child index to be compared with current value */ \
|
||||
size_t child; \
|
||||
size_t L_index = index * 2 + 1; \
|
||||
size_t R_index = index * 2 + 2; \
|
||||
\
|
||||
/* If there are two children, pick the smallest one */ \
|
||||
if (R_index < _heap_->size) \
|
||||
{ \
|
||||
SNAME##_node *L = &(_heap_->buffer[L_index]); \
|
||||
SNAME##_node *R = &(_heap_->buffer[R_index]); \
|
||||
\
|
||||
child = _heap_->cmp(L->data[0], R->data[0]) < 0 ? L_index : R_index; \
|
||||
} \
|
||||
/* Pick the only one available */ \
|
||||
else \
|
||||
child = L_index; \
|
||||
\
|
||||
SNAME##_node *child_node = &(_heap_->buffer[child]); \
|
||||
\
|
||||
/* If current value is smaller than the child node's value, it is done */ \
|
||||
if (_heap_->cmp(curr_node->data[0], child_node->data[0]) < 0) \
|
||||
break; \
|
||||
\
|
||||
/* Otherwise swap and continue */ \
|
||||
V tmp = child_node->data[0]; \
|
||||
child_node->data[0] = curr_node->data[0]; \
|
||||
curr_node->data[0] = tmp; \
|
||||
\
|
||||
/* If the child node is the last node check if the MinHeap and */ \
|
||||
/* MaxHeap values need to be swapped */ \
|
||||
if (child != _heap_->size - 1 || PFX##_count(_heap_) % 2 == 0) \
|
||||
{ \
|
||||
if (_heap_->cmp(child_node->data[0], child_node->data[1]) > 0) \
|
||||
{ \
|
||||
/* Swap because the MinHeap value is greater than the MaxHeap value */ \
|
||||
tmp = child_node->data[0]; \
|
||||
child_node->data[0] = child_node->data[1]; \
|
||||
child_node->data[1] = tmp; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
/* Update index and node */ \
|
||||
index = child; \
|
||||
curr_node = child_node; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static SNAME##_iter PFX##_impl_it_start(SNAME *_heap_) \
|
||||
{ \
|
||||
SNAME##_iter iter; \
|
||||
\
|
||||
PFX##_iter_init(&iter, _heap_); \
|
||||
PFX##_iter_to_start(&iter); \
|
||||
\
|
||||
return iter; \
|
||||
} \
|
||||
\
|
||||
static SNAME##_iter PFX##_impl_it_end(SNAME *_heap_) \
|
||||
{ \
|
||||
SNAME##_iter iter; \
|
||||
\
|
||||
PFX##_iter_init(&iter, _heap_); \
|
||||
PFX##_iter_to_end(&iter); \
|
||||
\
|
||||
return iter; \
|
||||
}
|
||||
|
||||
#endif /* CMC_INTERVALHEAP_H */
|
||||
@@ -12,7 +12,7 @@
|
||||
* A node-based Queue using a Singly Linked List
|
||||
*
|
||||
* enqueue : is equivalent to a push_back from a LinkedList
|
||||
* dequeue : is equivalent to a pop_peek from a LinkedList
|
||||
* dequeue : is equivalent to a pop_front from a LinkedList
|
||||
*
|
||||
* This Queue has a one-way iterator
|
||||
*/
|
||||
@@ -237,7 +237,7 @@
|
||||
FMOD V PFX##_peek(SNAME *_lqueue_) \
|
||||
{ \
|
||||
if (PFX##_empty(_lqueue_)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return _lqueue_->head->data; \
|
||||
} \
|
||||
@@ -348,7 +348,7 @@
|
||||
FMOD V *PFX##_iter_rvalue(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
NULL; \
|
||||
return NULL; \
|
||||
\
|
||||
return &(iter->cursor->data); \
|
||||
} \
|
||||
|
||||
@@ -0,0 +1,922 @@
|
||||
/**
|
||||
* multimap.h
|
||||
*
|
||||
* Creation Date: 26/04/2019
|
||||
*
|
||||
* Authors:
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* MultiMap
|
||||
*
|
||||
* The MultiMap is a Map that allows for multiple keys. This is a data structure
|
||||
* that has a very narrow usage. A Map (either TreeMap or HashMap) can also work
|
||||
* like a MultiMap if a certain key is mapped to another collection.
|
||||
*
|
||||
* Map<K = int, V = List<int>> maps an integer to a list of integer.
|
||||
* MultiMap<K = int, V = int> maps many integer keys to integer values.
|
||||
*
|
||||
* The difference is that in a MultiMap you can store keys that are the same but
|
||||
* might be different instances of the same value. This is also relevant if your
|
||||
* data type is more complex like a struct where its ID is the same but some
|
||||
* other members of this data type are different.
|
||||
*
|
||||
* Implementation
|
||||
*
|
||||
* The map uses separate chaining and robin hood hashing. Its internal buffer
|
||||
* is made of a pair of pointers to entries that represent a linked list. So
|
||||
* accessing map->buffer[0] would give the first pair of pointers. Each entry
|
||||
* has both a pointer to a previous entry and a next entry. This design choice
|
||||
* was made so that every collection in this library has a two-way iterator
|
||||
* with a very few exceptions.
|
||||
*
|
||||
* Each entry is composed of a Key and a Value. Entries with the same key should
|
||||
* always hash to the same linked list. Also, keys that hash to the same bucket
|
||||
* will also be in the same linked list.
|
||||
*
|
||||
* The order of inserting and removing the same keys will behave like a FIFO. So
|
||||
* the first key added will be the first to be removed.
|
||||
*/
|
||||
|
||||
#ifndef CMC_MULTIMAP_H
|
||||
#define CMC_MULTIMAP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../utl/cmc_string.h"
|
||||
|
||||
#ifndef CMC_HASH_TABLE_SETUP
|
||||
#define CMC_HASH_TABLE_SETUP
|
||||
|
||||
static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531,
|
||||
3067, 6143, 12289, 24571, 49157,
|
||||
98299, 196613, 393209, 786431,
|
||||
1572869, 3145721, 6291449,
|
||||
12582917, 25165813, 50331653,
|
||||
100663291, 201326611, 402653189,
|
||||
805306357, 1610612741,
|
||||
3221225473, 6442450939,
|
||||
12884901893, 25769803799,
|
||||
51539607551, 103079215111,
|
||||
206158430209, 412316860441,
|
||||
824633720831, 1649267441651,
|
||||
3298534883309, 6597069766657,
|
||||
13194139533299, 26388279066623,
|
||||
52776558133303, 105553116266489,
|
||||
211106232532969, 422212465066001,
|
||||
844424930131963,
|
||||
1688849860263953,
|
||||
3377699720527861,
|
||||
6755399441055731,
|
||||
13510798882111483,
|
||||
27021597764222939,
|
||||
54043195528445957,
|
||||
108086391056891903,
|
||||
216172782113783773,
|
||||
432345564227567621,
|
||||
864691128455135207,
|
||||
1729382256910270481,
|
||||
3458764513820540933,
|
||||
6917529027641081903,
|
||||
13835058055282163729llu};
|
||||
|
||||
#endif /* CMC_HASH_TABLE_SETUP */
|
||||
|
||||
#define MULTIMAP_GENERATE(PFX, SNAME, FMOD, K, V) \
|
||||
MULTIMAP_GENERATE_HEADER(PFX, SNAME, FMOD, K, V) \
|
||||
MULTIMAP_GENERATE_SOURCE(PFX, SNAME, FMOD, K, V)
|
||||
|
||||
#define MULTIMAP_WRAPGEN_HEADER(PFX, SNAME, FMOD, K, V) \
|
||||
MULTIMAP_GENERATE_HEADER(PFX, SNAME, FMOD, K, V)
|
||||
|
||||
#define MULTIMAP_WRAPGEN_SOURCE(PFX, SNAME, FMOD, K, V) \
|
||||
MULTIMAP_GENERATE_SOURCE(PFX, SNAME, FMOD, K, V)
|
||||
|
||||
/* HEADER ********************************************************************/
|
||||
#define MULTIMAP_GENERATE_HEADER(PFX, SNAME, FMOD, K, V) \
|
||||
\
|
||||
/* Multimap Structure */ \
|
||||
typedef struct SNAME##_s \
|
||||
{ \
|
||||
/* Array of linked list to entries */ \
|
||||
struct SNAME##_entry_s *(*buffer)[2]; \
|
||||
\
|
||||
/* Current array capacity */ \
|
||||
size_t capacity; \
|
||||
\
|
||||
/* Current amount of keys */ \
|
||||
size_t count; \
|
||||
\
|
||||
/* Load factor in range (0.0, infinity) */ \
|
||||
double load; \
|
||||
\
|
||||
/* Key comparison functions */ \
|
||||
int (*cmp)(K, K); \
|
||||
\
|
||||
/* Key hash function */ \
|
||||
size_t (*hash)(K); \
|
||||
\
|
||||
/* Function that returns an iterator to the start of the multimap */ \
|
||||
struct SNAME##_iter_s (*it_start)(struct SNAME##_s *); \
|
||||
\
|
||||
/* Function that returns an iterator to the end of the multimap */ \
|
||||
struct SNAME##_iter_s (*it_end)(struct SNAME##_s *); \
|
||||
\
|
||||
} SNAME, *SNAME##_ptr; \
|
||||
\
|
||||
/* Multimap Entry */ \
|
||||
typedef struct SNAME##_entry_s \
|
||||
{ \
|
||||
/* Entry Key */ \
|
||||
K key; \
|
||||
\
|
||||
/* Entry Value */ \
|
||||
V value; \
|
||||
\
|
||||
/* Next entry on the linked list */ \
|
||||
struct SNAME##_entry_s *next; \
|
||||
\
|
||||
/* Previous entry on the linked list */ \
|
||||
struct SNAME##_entry_s *prev; \
|
||||
\
|
||||
} SNAME##_entry, *SNAME##_entry_ptr; \
|
||||
\
|
||||
typedef struct SNAME##_iter_s \
|
||||
{ \
|
||||
/* Target multimap */ \
|
||||
struct SNAME##_s *target; \
|
||||
\
|
||||
/* Current entry */ \
|
||||
struct SNAME##_entry_s *curr_entry; \
|
||||
\
|
||||
/* Cursor`s position (index) */ \
|
||||
size_t cursor; \
|
||||
\
|
||||
/* Keeps track of relative index to the iteration of elements */ \
|
||||
size_t index; \
|
||||
\
|
||||
/* The index of the first element */ \
|
||||
size_t first; \
|
||||
\
|
||||
/* The index of the last element */ \
|
||||
size_t last; \
|
||||
\
|
||||
/* If the iterator has reached the start of the iteration */ \
|
||||
bool start; \
|
||||
\
|
||||
/* If the iterator has reached the end of the iteration */ \
|
||||
bool end; \
|
||||
} SNAME##_iter, *SNAME##_iter_ptr; \
|
||||
\
|
||||
/* Collection Functions */ \
|
||||
/* Collection Allocation and Deallocation */ \
|
||||
FMOD SNAME *PFX##_new(size_t capacity, double load, int (*compare)(K, K), size_t (*hash)(K)); \
|
||||
FMOD void PFX##_clear(SNAME *_map_); \
|
||||
FMOD void PFX##_free(SNAME *_map_); \
|
||||
/* Collection Input and Output */ \
|
||||
FMOD bool PFX##_insert(SNAME *_map_, K key, V value); \
|
||||
FMOD bool PFX##_update(SNAME *_map_, K key, V new_value); \
|
||||
FMOD size_t PFX##_update_all(SNAME *_map_, K key, V new_value); \
|
||||
FMOD bool PFX##_remove(SNAME *_map_, K key, V *value); \
|
||||
FMOD size_t PFX##_remove_all(SNAME *_map_, K key); \
|
||||
/* Conditional Input and Output */ \
|
||||
FMOD bool PFX##_insert_if(SNAME *_map_, K key, V value, bool condition); \
|
||||
FMOD bool PFX##_remove_if(SNAME *_map_, K key, V *value, bool condition); \
|
||||
/* Element Access */ \
|
||||
FMOD bool PFX##_max(SNAME *_map_, K *key, V *value); \
|
||||
FMOD bool PFX##_min(SNAME *_map_, K *key, V *value); \
|
||||
FMOD V PFX##_get(SNAME *_map_, K key); \
|
||||
FMOD V *PFX##_get_ref(SNAME *_map_, K key); \
|
||||
/* Collection State */ \
|
||||
FMOD bool PFX##_contains(SNAME *_map_, K key); \
|
||||
FMOD bool PFX##_empty(SNAME *_map_); \
|
||||
FMOD size_t PFX##_count(SNAME *_map_); \
|
||||
FMOD size_t PFX##_capacity(SNAME *_map_); \
|
||||
/* Collection Utility */ \
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_map_); \
|
||||
\
|
||||
/* Iterator Functions */ \
|
||||
/* Iterator Allocation and Deallocation */ \
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target); \
|
||||
FMOD void PFX##_iter_free(SNAME##_iter *iter); \
|
||||
/* Iterator Initialization */ \
|
||||
FMOD void PFX##_iter_init(SNAME##_iter *iter, SNAME *target); \
|
||||
/* Iterator State */ \
|
||||
FMOD bool PFX##_iter_start(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_end(SNAME##_iter *iter); \
|
||||
/* Iterator Movement */ \
|
||||
FMOD void PFX##_iter_to_start(SNAME##_iter *iter); \
|
||||
FMOD void PFX##_iter_to_end(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_next(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_prev(SNAME##_iter *iter); \
|
||||
/* Iterator Access */ \
|
||||
FMOD K PFX##_iter_key(SNAME##_iter *iter); \
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter); \
|
||||
FMOD V *PFX##_iter_rvalue(SNAME##_iter *iter); \
|
||||
FMOD size_t PFX##_iter_index(SNAME##_iter *iter); \
|
||||
\
|
||||
/* Default Key */ \
|
||||
static inline K PFX##_impl_default_key(void) \
|
||||
{ \
|
||||
K _empty_key_; \
|
||||
\
|
||||
memset(&_empty_key_, 0, sizeof(K)); \
|
||||
\
|
||||
return _empty_key_; \
|
||||
} \
|
||||
\
|
||||
/* Default Value */ \
|
||||
static inline V PFX##_impl_default_value(void) \
|
||||
{ \
|
||||
V _empty_value_; \
|
||||
\
|
||||
memset(&_empty_value_, 0, sizeof(V)); \
|
||||
\
|
||||
return _empty_value_; \
|
||||
} \
|
||||
/* SOURCE ********************************************************************/
|
||||
#define MULTIMAP_GENERATE_SOURCE(PFX, SNAME, FMOD, K, V) \
|
||||
\
|
||||
/* Implementation Detail Functions */ \
|
||||
FMOD bool PFX##_impl_grow(SNAME *_map_); \
|
||||
FMOD SNAME##_entry *PFX##_impl_new_entry(K key, V value); \
|
||||
FMOD SNAME##_entry *PFX##_impl_get_entry(SNAME *_map_, K key); \
|
||||
FMOD size_t PFX##_impl_calculate_size(size_t required); \
|
||||
static SNAME##_iter PFX##_impl_it_start(SNAME *_map_); \
|
||||
static SNAME##_iter PFX##_impl_it_end(SNAME *_map_); \
|
||||
\
|
||||
FMOD SNAME *PFX##_new(size_t capacity, double load, int (*compare)(K, K), size_t (*hash)(K)) \
|
||||
{ \
|
||||
if (capacity == 0 || load <= 0) \
|
||||
return NULL; \
|
||||
\
|
||||
/* Prevent integer overflow */ \
|
||||
if (capacity >= UINTMAX_MAX * load) \
|
||||
return NULL; \
|
||||
\
|
||||
size_t real_capacity = PFX##_impl_calculate_size(capacity / load); \
|
||||
\
|
||||
SNAME *_map_ = malloc(sizeof(SNAME)); \
|
||||
\
|
||||
if (!_map_) \
|
||||
return NULL; \
|
||||
\
|
||||
_map_->buffer = malloc(sizeof(SNAME##_entry_ptr[2]) * real_capacity); \
|
||||
\
|
||||
if (!_map_->buffer) \
|
||||
{ \
|
||||
free(_map_); \
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
memset(_map_->buffer, 0, sizeof(SNAME##_entry_ptr[2]) * real_capacity); \
|
||||
\
|
||||
_map_->count = 0; \
|
||||
_map_->capacity = real_capacity; \
|
||||
_map_->load = load; \
|
||||
_map_->cmp = compare; \
|
||||
_map_->hash = hash; \
|
||||
\
|
||||
return _map_; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_clear(SNAME *_map_) \
|
||||
{ \
|
||||
size_t index = 0; \
|
||||
SNAME##_entry *scan; \
|
||||
\
|
||||
while (index < _map_->capacity) \
|
||||
{ \
|
||||
scan = _map_->buffer[index][0]; \
|
||||
\
|
||||
if (scan != NULL) \
|
||||
{ \
|
||||
if (scan->next == NULL && scan->prev == NULL) \
|
||||
free(scan); \
|
||||
else \
|
||||
{ \
|
||||
while (scan != NULL) \
|
||||
{ \
|
||||
SNAME##_entry *tmp = scan; \
|
||||
\
|
||||
scan = scan->next; \
|
||||
\
|
||||
free(tmp); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
index++; \
|
||||
} \
|
||||
\
|
||||
memset(_map_->buffer, 0, sizeof(SNAME##_entry_ptr[2]) * _map_->capacity); \
|
||||
\
|
||||
_map_->count = 0; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_free(SNAME *_map_) \
|
||||
{ \
|
||||
PFX##_clear(_map_); \
|
||||
free(_map_->buffer); \
|
||||
free(_map_); \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_insert(SNAME *_map_, K key, V value) \
|
||||
{ \
|
||||
if ((double)_map_->capacity * _map_->load <= (double)_map_->count) \
|
||||
{ \
|
||||
if (!PFX##_impl_grow(_map_)) \
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
size_t hash = _map_->hash(key); \
|
||||
size_t pos = hash % _map_->capacity; \
|
||||
\
|
||||
SNAME##_entry *entry = PFX##_impl_new_entry(key, value); \
|
||||
\
|
||||
if (_map_->buffer[pos][0] == NULL) \
|
||||
{ \
|
||||
_map_->buffer[pos][0] = entry; \
|
||||
_map_->buffer[pos][1] = entry; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
entry->prev = _map_->buffer[pos][1]; \
|
||||
\
|
||||
_map_->buffer[pos][1]->next = entry; \
|
||||
_map_->buffer[pos][1] = entry; \
|
||||
} \
|
||||
\
|
||||
_map_->count++; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_update(SNAME *_map_, K key, V new_value) \
|
||||
{ \
|
||||
size_t hash = _map_->hash(key); \
|
||||
\
|
||||
SNAME##_entry *entry = _map_->buffer[hash % _map_->capacity][0]; \
|
||||
\
|
||||
while (entry != NULL) \
|
||||
{ \
|
||||
if (_map_->cmp(entry->key, key) == 0) \
|
||||
{ \
|
||||
entry->value = new_value; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
entry = entry->next; \
|
||||
} \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_update_all(SNAME *_map_, K key, V new_value) \
|
||||
{ \
|
||||
size_t hash = _map_->hash(key); \
|
||||
\
|
||||
SNAME##_entry *entry = _map_->buffer[hash % _map_->capacity][0]; \
|
||||
\
|
||||
size_t total = 0; \
|
||||
\
|
||||
while (entry != NULL) \
|
||||
{ \
|
||||
if (_map_->cmp(entry->key, key) == 0) \
|
||||
{ \
|
||||
entry->value = new_value; \
|
||||
\
|
||||
total++; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return total; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_remove(SNAME *_map_, K key, V *value) \
|
||||
{ \
|
||||
size_t hash = _map_->hash(key); \
|
||||
\
|
||||
SNAME##_entry **head = &(_map_->buffer[hash % _map_->capacity][0]); \
|
||||
SNAME##_entry **tail = &(_map_->buffer[hash % _map_->capacity][1]); \
|
||||
\
|
||||
if (*head == NULL) \
|
||||
return false; \
|
||||
\
|
||||
SNAME##_entry *entry = *head; \
|
||||
\
|
||||
if (entry->next == NULL && entry->prev == NULL) \
|
||||
{ \
|
||||
if (_map_->cmp(entry->key, key) == 0) \
|
||||
{ \
|
||||
*head = NULL; \
|
||||
*tail = NULL; \
|
||||
\
|
||||
*value = entry->value; \
|
||||
} \
|
||||
else \
|
||||
return false; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
bool found = false; \
|
||||
\
|
||||
while (entry != NULL) \
|
||||
{ \
|
||||
if (_map_->cmp(entry->key, key) == 0) \
|
||||
{ \
|
||||
if (*head == entry) \
|
||||
*head = entry->next; \
|
||||
if (*tail == entry) \
|
||||
*tail = entry->prev; \
|
||||
\
|
||||
if (entry->prev != NULL) \
|
||||
entry->prev->next = entry->next; \
|
||||
if (entry->next != NULL) \
|
||||
entry->next->prev = entry->prev; \
|
||||
\
|
||||
*value = entry->value; \
|
||||
found = true; \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
else \
|
||||
entry = entry->next; \
|
||||
} \
|
||||
\
|
||||
if (!found) \
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
free(entry); \
|
||||
\
|
||||
_map_->count--; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_remove_all(SNAME *_map_, K key) \
|
||||
{ \
|
||||
size_t total = 0; \
|
||||
size_t hash = _map_->hash(key); \
|
||||
\
|
||||
SNAME##_entry **head = &(_map_->buffer[hash % _map_->capacity][0]); \
|
||||
SNAME##_entry **tail = &(_map_->buffer[hash % _map_->capacity][1]); \
|
||||
\
|
||||
if (*head == NULL) \
|
||||
return total; \
|
||||
\
|
||||
SNAME##_entry *entry = *head; \
|
||||
\
|
||||
if (entry->next == NULL) \
|
||||
{ \
|
||||
*head = NULL; \
|
||||
*tail = NULL; \
|
||||
\
|
||||
free(entry); \
|
||||
\
|
||||
total++; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
while (entry != NULL) \
|
||||
{ \
|
||||
if (_map_->cmp(entry->key, key) == 0) \
|
||||
{ \
|
||||
if (*head == entry) \
|
||||
*head = entry->next; \
|
||||
if (*tail == entry) \
|
||||
*tail = entry->prev; \
|
||||
\
|
||||
SNAME##_entry *temp = entry->next; \
|
||||
\
|
||||
if (entry->prev != NULL) \
|
||||
entry->prev->next = entry->next; \
|
||||
if (entry->next != NULL) \
|
||||
entry->next->prev = entry->prev; \
|
||||
\
|
||||
free(entry); \
|
||||
entry = temp; \
|
||||
\
|
||||
total++; \
|
||||
} \
|
||||
else \
|
||||
entry = entry->next; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
_map_->count -= total; \
|
||||
\
|
||||
return total; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_insert_if(SNAME *_map_, K key, V value, bool condition) \
|
||||
{ \
|
||||
if (condition) \
|
||||
return PFX##_insert(_map_, key, value); \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_remove_if(SNAME *_map_, K key, V *value, bool condition) \
|
||||
{ \
|
||||
if (condition) \
|
||||
return PFX##_remove(_map_, key, value); \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_max(SNAME *_map_, K *key, V *value) \
|
||||
{ \
|
||||
if (PFX##_empty(_map_)) \
|
||||
return false; \
|
||||
\
|
||||
SNAME##_iter iter; \
|
||||
\
|
||||
for (PFX##_iter_init(&iter, _map_); !PFX##_iter_end(&iter); PFX##_iter_next(&iter)) \
|
||||
{ \
|
||||
K result_key = PFX##_iter_key(&iter); \
|
||||
V result_value = PFX##_iter_value(&iter); \
|
||||
size_t index = PFX##_iter_index(&iter); \
|
||||
\
|
||||
if (index == 0) \
|
||||
{ \
|
||||
*key = result_key; \
|
||||
*value = result_value; \
|
||||
} \
|
||||
else if (_map_->cmp(result_key, *key) > 0) \
|
||||
{ \
|
||||
*key = result_key; \
|
||||
*value = result_value; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_min(SNAME *_map_, K *key, V *value) \
|
||||
{ \
|
||||
if (PFX##_empty(_map_)) \
|
||||
return false; \
|
||||
\
|
||||
SNAME##_iter iter; \
|
||||
\
|
||||
for (PFX##_iter_init(&iter, _map_); !PFX##_iter_end(&iter); PFX##_iter_next(&iter)) \
|
||||
{ \
|
||||
K result_key = PFX##_iter_key(&iter); \
|
||||
V result_value = PFX##_iter_value(&iter); \
|
||||
size_t index = PFX##_iter_index(&iter); \
|
||||
\
|
||||
if (index == 0) \
|
||||
{ \
|
||||
*key = result_key; \
|
||||
*value = result_value; \
|
||||
} \
|
||||
else if (_map_->cmp(result_key, *key) < 0) \
|
||||
{ \
|
||||
*key = result_key; \
|
||||
*value = result_value; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD V PFX##_get(SNAME *_map_, K key) \
|
||||
{ \
|
||||
SNAME##_entry *entry = PFX##_impl_get_entry(_map_, key); \
|
||||
\
|
||||
if (!entry) \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return entry->value; \
|
||||
} \
|
||||
\
|
||||
FMOD V *PFX##_get_ref(SNAME *_map_, K key) \
|
||||
{ \
|
||||
SNAME##_entry *entry = PFX##_impl_get_entry(_map_, key); \
|
||||
\
|
||||
if (!entry) \
|
||||
return NULL; \
|
||||
\
|
||||
return &(entry->value); \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_contains(SNAME *_map_, K key) \
|
||||
{ \
|
||||
return PFX##_impl_get_entry(_map_, key) != NULL; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_empty(SNAME *_map_) \
|
||||
{ \
|
||||
return _map_->count == 0; \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_count(SNAME *_map_) \
|
||||
{ \
|
||||
return _map_->count; \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_capacity(SNAME *_map_) \
|
||||
{ \
|
||||
return _map_->capacity; \
|
||||
} \
|
||||
\
|
||||
FMOD cmc_string PFX##_to_string(SNAME *_map_) \
|
||||
{ \
|
||||
cmc_string str; \
|
||||
SNAME *m_ = _map_; \
|
||||
const char *name = #SNAME; \
|
||||
\
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_hashmap, \
|
||||
name, m_, m_->buffer, m_->capacity, m_->count, m_->load, m_->cmp, m_->hash); \
|
||||
\
|
||||
return str; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_iter *PFX##_iter_new(SNAME *target) \
|
||||
{ \
|
||||
SNAME##_iter *iter = malloc(sizeof(SNAME##_iter)); \
|
||||
\
|
||||
if (!iter) \
|
||||
return NULL; \
|
||||
\
|
||||
PFX##_iter_init(iter, target); \
|
||||
\
|
||||
return iter; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_iter_free(SNAME##_iter *iter) \
|
||||
{ \
|
||||
free(iter); \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_iter_init(SNAME##_iter *iter, SNAME *target) \
|
||||
{ \
|
||||
iter->target = target; \
|
||||
iter->cursor = 0; \
|
||||
iter->index = 0; \
|
||||
iter->start = true; \
|
||||
iter->end = PFX##_empty(target); \
|
||||
\
|
||||
if (!PFX##_empty(target)) \
|
||||
{ \
|
||||
for (size_t i = 0; i < target->capacity; i++) \
|
||||
{ \
|
||||
if (target->buffer[i][0] != NULL) \
|
||||
{ \
|
||||
iter->curr_entry = target->buffer[i][0]; \
|
||||
iter->first = i; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
iter->cursor = iter->first; \
|
||||
\
|
||||
for (size_t i = target->capacity; i > 0; i--) \
|
||||
{ \
|
||||
if (target->buffer[i - 1][0] != NULL) \
|
||||
{ \
|
||||
iter->last = i - 1; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_start(SNAME##_iter *iter) \
|
||||
{ \
|
||||
return PFX##_empty(iter->target) || iter->start; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_end(SNAME##_iter *iter) \
|
||||
{ \
|
||||
return PFX##_empty(iter->target) || iter->end; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_iter_to_start(SNAME##_iter *iter) \
|
||||
{ \
|
||||
iter->cursor = iter->first; \
|
||||
iter->index = 0; \
|
||||
iter->start = true; \
|
||||
iter->end = PFX##_empty(iter->target); \
|
||||
iter->curr_entry = iter->target->buffer[iter->first][0]; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_iter_to_end(SNAME##_iter *iter) \
|
||||
{ \
|
||||
iter->cursor = iter->last; \
|
||||
iter->index = iter->target->count - 1; \
|
||||
iter->start = PFX##_empty(iter->target); \
|
||||
iter->end = true; \
|
||||
iter->curr_entry = iter->target->buffer[iter->last][1]; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_next(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (iter->end) \
|
||||
return false; \
|
||||
\
|
||||
if (iter->curr_entry->next != NULL) \
|
||||
{ \
|
||||
iter->curr_entry = iter->curr_entry->next; \
|
||||
iter->index++; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (iter->cursor == iter->last) \
|
||||
{ \
|
||||
iter->end = true; \
|
||||
return false; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
iter->cursor++; \
|
||||
\
|
||||
while (iter->target->buffer[iter->cursor][0] == NULL) \
|
||||
iter->cursor++; \
|
||||
\
|
||||
iter->curr_entry = iter->target->buffer[iter->cursor][0]; \
|
||||
\
|
||||
iter->index++; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
iter->start = PFX##_empty(iter->target); \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_prev(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (iter->start) \
|
||||
return false; \
|
||||
\
|
||||
if (iter->curr_entry->prev != NULL) \
|
||||
{ \
|
||||
iter->curr_entry = iter->curr_entry->prev; \
|
||||
iter->index--; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (iter->cursor == iter->first) \
|
||||
{ \
|
||||
iter->start = true; \
|
||||
return false; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
iter->cursor--; \
|
||||
\
|
||||
while (iter->target->buffer[iter->cursor][1] == NULL) \
|
||||
iter->cursor--; \
|
||||
\
|
||||
iter->curr_entry = iter->target->buffer[iter->cursor][1]; \
|
||||
\
|
||||
iter->index--; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
iter->end = PFX##_empty(iter->target); \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD K PFX##_iter_key(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
return PFX##_impl_default_key(); \
|
||||
\
|
||||
return iter->curr_entry->key; \
|
||||
} \
|
||||
\
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return iter->curr_entry->value; \
|
||||
} \
|
||||
\
|
||||
FMOD V *PFX##_iter_rvalue(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
return NULL; \
|
||||
\
|
||||
return &(iter->curr_entry->value); \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_iter_index(SNAME##_iter *iter) \
|
||||
{ \
|
||||
return iter->index; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_impl_grow(SNAME *_map_) \
|
||||
{ \
|
||||
size_t new_capacity = PFX##_impl_calculate_size(_map_->capacity * 1.5); \
|
||||
\
|
||||
SNAME *_new_map_ = PFX##_new(new_capacity, _map_->load, _map_->cmp, _map_->hash); \
|
||||
\
|
||||
if (!_new_map_) \
|
||||
return false; \
|
||||
\
|
||||
SNAME##_iter iter; \
|
||||
\
|
||||
for (PFX##_iter_init(&iter, _map_); !PFX##_iter_end(&iter); PFX##_iter_next(&iter)) \
|
||||
{ \
|
||||
K key = PFX##_iter_key(&iter); \
|
||||
V value = PFX##_iter_value(&iter); \
|
||||
\
|
||||
PFX##_insert(_new_map_, key, value); \
|
||||
} \
|
||||
\
|
||||
if (_map_->count != _new_map_->count) \
|
||||
{ \
|
||||
PFX##_free(_new_map_); \
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
SNAME##_entry *(*tmp_b)[2] = _map_->buffer; \
|
||||
_map_->buffer = _new_map_->buffer; \
|
||||
_new_map_->buffer = tmp_b; \
|
||||
\
|
||||
size_t tmp_c = _map_->capacity; \
|
||||
_map_->capacity = _new_map_->capacity; \
|
||||
_new_map_->capacity = tmp_c; \
|
||||
\
|
||||
PFX##_free(_new_map_); \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_entry *PFX##_impl_new_entry(K key, V value) \
|
||||
{ \
|
||||
SNAME##_entry *entry = malloc(sizeof(SNAME##_entry)); \
|
||||
\
|
||||
if (!entry) \
|
||||
return NULL; \
|
||||
\
|
||||
entry->key = key; \
|
||||
entry->value = value; \
|
||||
entry->next = NULL; \
|
||||
entry->prev = NULL; \
|
||||
\
|
||||
return entry; \
|
||||
} \
|
||||
\
|
||||
FMOD SNAME##_entry *PFX##_impl_get_entry(SNAME *_map_, K key) \
|
||||
{ \
|
||||
size_t hash = _map_->hash(key); \
|
||||
\
|
||||
SNAME##_entry *entry = _map_->buffer[hash % _map_->capacity][0]; \
|
||||
\
|
||||
while (entry != NULL) \
|
||||
{ \
|
||||
if (_map_->cmp(entry->key, key) == 0) \
|
||||
return entry; \
|
||||
\
|
||||
entry = entry->next; \
|
||||
} \
|
||||
\
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_impl_calculate_size(size_t required) \
|
||||
{ \
|
||||
const size_t count = sizeof(cmc_hashtable_primes) / sizeof(cmc_hashtable_primes[0]); \
|
||||
\
|
||||
if (cmc_hashtable_primes[count - 1] < required) \
|
||||
return required; \
|
||||
\
|
||||
size_t i = 0; \
|
||||
while (cmc_hashtable_primes[i] < required) \
|
||||
i++; \
|
||||
\
|
||||
return cmc_hashtable_primes[i]; \
|
||||
} \
|
||||
\
|
||||
static SNAME##_iter PFX##_impl_it_start(SNAME *_map_) \
|
||||
{ \
|
||||
SNAME##_iter iter; \
|
||||
\
|
||||
PFX##_iter_init(&iter, _map_); \
|
||||
\
|
||||
return iter; \
|
||||
} \
|
||||
\
|
||||
static SNAME##_iter PFX##_impl_it_end(SNAME *_map_) \
|
||||
{ \
|
||||
SNAME##_iter iter; \
|
||||
\
|
||||
PFX##_iter_init(&iter, _map_); \
|
||||
PFX##_iter_to_end(&iter); \
|
||||
\
|
||||
return iter; \
|
||||
}
|
||||
|
||||
#endif /* CMC_MULTIMAP_H */
|
||||
+1059
File diff suppressed because it is too large.
Load diff
+22
-18
@@ -11,43 +11,47 @@
|
||||
#ifndef CMC_MACRO_COLLECTIONS
|
||||
#define CMC_MACRO_COLLECTIONS
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#define CMC_CONCATH_(C) C##_WRAPGEN_HEADER
|
||||
#define CMC_CONCATC_(C) C##_WRAPGEN_SOURCE
|
||||
|
||||
#define CONCATH_(C) C##_WRAPGEN_HEADER
|
||||
#define CONCATC_(C) C##_WRAPGEN_SOURCE
|
||||
|
||||
#define CONCATH(C) CONCATH_(C)
|
||||
#define CONCATC(C) CONCATC_(C)
|
||||
#define CMC_CONCATH(C) CMC_CONCATH_(C)
|
||||
#define CMC_CONCATC(C) CMC_CONCATC_(C)
|
||||
|
||||
#define COLLECTION_GENERATE(C, PFX, SNAME, FMOD, K, V) \
|
||||
COLLECTION_GENERATE_HEADER(C, PFX, SNAME, FMOD, K, V) \
|
||||
COLLECTION_GENERATE_SOURCE(C, PFX, SNAME, FMOD, K, V)
|
||||
|
||||
#define COLLECTION_GENERATE_HEADER(C, PFX, SNAME, FMOD, K, V) \
|
||||
CONCATH(C) \
|
||||
CMC_CONCATH(C) \
|
||||
(PFX, SNAME, FMOD, K, V)
|
||||
|
||||
#define COLLECTION_GENERATE_SOURCE(C, PFX, SNAME, FMOD, K, V) \
|
||||
CONCATC(C) \
|
||||
CMC_CONCATC(C) \
|
||||
(PFX, SNAME, FMOD, K, V)
|
||||
|
||||
#include "cmc/list.h"
|
||||
#include "cmc/linkedlist.h"
|
||||
#include "cmc/stack.h"
|
||||
#include "cmc/queue.h"
|
||||
#include "cmc/deque.h"
|
||||
#include "cmc/heap.h"
|
||||
#include "cmc/treeset.h"
|
||||
#include "cmc/treemap.h"
|
||||
#include "cmc/hashset.h"
|
||||
#include "cmc/hashmap.h"
|
||||
#include "cmc/hashset.h"
|
||||
#include "cmc/heap.h"
|
||||
#include "cmc/linkedlist.h"
|
||||
#include "cmc/list.h"
|
||||
#include "cmc/queue.h"
|
||||
#include "cmc/stack.h"
|
||||
#include "cmc/treemap.h"
|
||||
#include "cmc/treeset.h"
|
||||
|
||||
#include "ext/intervalheap.h"
|
||||
#include "ext/linkedqueue.h"
|
||||
#include "ext/multimap.h"
|
||||
#include "ext/multiset.h"
|
||||
|
||||
#include "sac/queue.h"
|
||||
#include "sac/stack.h"
|
||||
|
||||
#include "utl/assert.h"
|
||||
#include "utl/foreach.h"
|
||||
#include "utl/log.h"
|
||||
#include "utl/test.h"
|
||||
#include "utl/timer.h"
|
||||
|
||||
#endif /* CMC_MACRO_COLLECTIONS */
|
||||
#endif /* CMC_MACRO_COLLECTIONS */
|
||||
+357
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
* queue.h
|
||||
*
|
||||
* Creation Date: 24/06/2019
|
||||
*
|
||||
* Authors:
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* A Purely Stack Allocated Queue
|
||||
*
|
||||
* This means that the Queue has a fixed size, but it also has the advantage of
|
||||
* not requiring for manually allocating and deallocating the struct.
|
||||
*
|
||||
* Generating a collection with too big of a storage can be dangerous and might
|
||||
* quickly cause a stack overflow.
|
||||
*
|
||||
* It is recommended to not generate any Stack Allocated Collection with less
|
||||
* than 1000 internal storage.
|
||||
*/
|
||||
|
||||
#ifndef CMC_SAC_QUEUE_H
|
||||
#define CMC_SAC_QUEUE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SAC_QUEUE_GENERATE(PFX, SNAME, FMOD, V, SIZE) \
|
||||
SAC_QUEUE_GENERATE_HEADER(PFX, SNAME, FMOD, V, SIZE) \
|
||||
SAC_QUEUE_GENERATE_SOURCE(PFX, SNAME, FMOD, V, SIZE)
|
||||
|
||||
#define SAC_QUEUE_WRAPGEN_HEADER(PFX, SNAME, FMOD, K, V, SIZE) \
|
||||
SAC_QUEUE_GENERATE_HEADER(PFX, SNAME, FMOD, V, SIZE)
|
||||
|
||||
#define SAC_QUEUE_WRAPGEN_SOURCE(PFX, SNAME, FMOD, K, V, SIZE) \
|
||||
SAC_QUEUE_GENERATE_SOURCE(PFX, SNAME, FMOD, V, SIZE)
|
||||
|
||||
/* HEADER ********************************************************************/
|
||||
#define SAC_QUEUE_GENERATE_HEADER(PFX, SNAME, FMOD, V, SIZE) \
|
||||
\
|
||||
/* Queue Structure */ \
|
||||
typedef struct SNAME##_s \
|
||||
{ \
|
||||
/* Internal Storage */ \
|
||||
V buffer[SIZE]; \
|
||||
\
|
||||
/* Current amount of elements */ \
|
||||
size_t count; \
|
||||
\
|
||||
/* Index representing the front of the queue */ \
|
||||
size_t front; \
|
||||
\
|
||||
/* Index representing the back of the queue */ \
|
||||
size_t rear; \
|
||||
\
|
||||
/* Function that returns an iterator to the start of the queue */ \
|
||||
struct SNAME##_iter_s (*it_start)(struct SNAME##_s *); \
|
||||
\
|
||||
/* Function that returns an iterator to the end of the queue */ \
|
||||
struct SNAME##_iter_s (*it_end)(struct SNAME##_s *); \
|
||||
\
|
||||
} SNAME, *SNAME##_ptr; \
|
||||
\
|
||||
/* Queue Iterator */ \
|
||||
typedef struct SNAME##_iter_s \
|
||||
{ \
|
||||
/* Target queue */ \
|
||||
struct SNAME##_s *target; \
|
||||
\
|
||||
/* Cursor's position (index) */ \
|
||||
size_t cursor; \
|
||||
\
|
||||
/* Keeps track of relative index to the iteration of elements */ \
|
||||
size_t index; \
|
||||
\
|
||||
/* If the iterator has reached the start of the iteration */ \
|
||||
bool start; \
|
||||
\
|
||||
/* If the iterator has reached the end of the iteration */ \
|
||||
bool end; \
|
||||
\
|
||||
} SNAME##_iter, *SNAME##_iter_ptr; \
|
||||
\
|
||||
/* Collection Functions */ \
|
||||
/* Collection Allocation and Deallocation */ \
|
||||
FMOD SNAME PFX##_new(void); \
|
||||
FMOD void PFX##_clear(SNAME *_queue_); \
|
||||
/* Collection Input and Output */ \
|
||||
FMOD bool PFX##_enqueue(SNAME *_queue_, V element); \
|
||||
FMOD bool PFX##_dequeue(SNAME *_queue_); \
|
||||
/* Conditional Input and Output */ \
|
||||
FMOD bool PFX##_enqueue_if(SNAME *_queue_, V element, bool condition); \
|
||||
FMOD bool PFX##_dequeue_if(SNAME *_queue_, bool condition); \
|
||||
/* Element Access */ \
|
||||
FMOD V PFX##_peek(SNAME *_queue_); \
|
||||
/* Collection State */ \
|
||||
FMOD bool PFX##_contains(SNAME *_queue_, V element, int (*comparator)(V, V)); \
|
||||
FMOD bool PFX##_empty(SNAME *_queue_); \
|
||||
FMOD bool PFX##_full(SNAME *_queue_); \
|
||||
FMOD size_t PFX##_count(SNAME *_queue_); \
|
||||
FMOD size_t PFX##_capacity(void); \
|
||||
\
|
||||
/* Iterator Functions */ \
|
||||
/* Iterator Initialization */ \
|
||||
FMOD void PFX##_iter_init(SNAME##_iter *iter, SNAME *target); \
|
||||
/* Iterator State */ \
|
||||
FMOD bool PFX##_iter_start(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_end(SNAME##_iter *iter); \
|
||||
/* Iterator Movement */ \
|
||||
FMOD void PFX##_iter_to_start(SNAME##_iter *iter); \
|
||||
FMOD void PFX##_iter_to_end(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_next(SNAME##_iter *iter); \
|
||||
FMOD bool PFX##_iter_prev(SNAME##_iter *iter); \
|
||||
/* Iterator Access */ \
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter); \
|
||||
FMOD V *PFX##_iter_rvalue(SNAME##_iter *iter); \
|
||||
FMOD size_t PFX##_iter_index(SNAME##_iter *iter); \
|
||||
\
|
||||
/* Default Value */ \
|
||||
static inline V PFX##_impl_default_value(void) \
|
||||
{ \
|
||||
V _empty_value_; \
|
||||
\
|
||||
memset(&_empty_value_, 0, sizeof(V)); \
|
||||
\
|
||||
return _empty_value_; \
|
||||
} \
|
||||
\
|
||||
/* SOURCE ********************************************************************/
|
||||
#define SAC_QUEUE_GENERATE_SOURCE(PFX, SNAME, FMOD, V, SIZE) \
|
||||
\
|
||||
/* Implementation Detail Functions */ \
|
||||
static SNAME##_iter PFX##_impl_it_start(SNAME *_queue_); \
|
||||
static SNAME##_iter PFX##_impl_it_end(SNAME *_queue_); \
|
||||
\
|
||||
FMOD SNAME PFX##_new(void) \
|
||||
{ \
|
||||
SNAME result; \
|
||||
\
|
||||
memset(&result, 0, sizeof(SNAME)); \
|
||||
\
|
||||
result.it_start = PFX##_impl_it_start; \
|
||||
result.it_end = PFX##_impl_it_end; \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_clear(SNAME *_queue_) \
|
||||
{ \
|
||||
memset(_queue_->buffer, 0, sizeof(_queue_->buffer)); \
|
||||
\
|
||||
_queue_->count = 0; \
|
||||
_queue_->front = 0; \
|
||||
_queue_->rear = 0; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_enqueue(SNAME *_queue_, V element) \
|
||||
{ \
|
||||
if (PFX##_full(_queue_)) \
|
||||
return false; \
|
||||
\
|
||||
_queue_->buffer[_queue_->rear] = element; \
|
||||
\
|
||||
_queue_->rear = (_queue_->rear == SIZE - 1) ? 0 : _queue_->rear + 1; \
|
||||
_queue_->count++; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_dequeue(SNAME *_queue_) \
|
||||
{ \
|
||||
if (PFX##_empty(_queue_)) \
|
||||
return false; \
|
||||
\
|
||||
_queue_->buffer[_queue_->front] = PFX##_impl_default_value(); \
|
||||
\
|
||||
_queue_->front = (_queue_->front == SIZE - 1) ? 0 : _queue_->front + 1; \
|
||||
_queue_->count--; \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_enqueue_if(SNAME *_queue_, V element, bool condition) \
|
||||
{ \
|
||||
if (condition) \
|
||||
return PFX##_enqueue(_queue_, element); \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_dequeue_if(SNAME *_queue_, bool condition) \
|
||||
{ \
|
||||
if (condition) \
|
||||
return PFX##_dequeue(_queue_); \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
FMOD V PFX##_peek(SNAME *_queue_) \
|
||||
{ \
|
||||
if (PFX##_empty(_queue_)) \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return _queue_->buffer[_queue_->front]; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_contains(SNAME *_queue_, V element, int (*comparator)(V, V)) \
|
||||
{ \
|
||||
for (size_t i = _queue_->front, j = 0; j < _queue_->count; i = (i + 1) % _queue_->count, j++) \
|
||||
{ \
|
||||
if (comparator(_queue_->buffer[i], element) == 0) \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_empty(SNAME *_queue_) \
|
||||
{ \
|
||||
return _queue_->count == 0; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_full(SNAME *_queue_) \
|
||||
{ \
|
||||
return _queue_->count >= SIZE; \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_count(SNAME *_queue_) \
|
||||
{ \
|
||||
return _queue_->count; \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_capacity(void) \
|
||||
{ \
|
||||
return SIZE; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_iter_init(SNAME##_iter *iter, SNAME *target) \
|
||||
{ \
|
||||
iter->target = target; \
|
||||
iter->cursor = target->front; \
|
||||
iter->index = 0; \
|
||||
iter->start = true; \
|
||||
iter->end = PFX##_empty(target); \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_start(SNAME##_iter *iter) \
|
||||
{ \
|
||||
return PFX##_empty(iter->target) || iter->start; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_end(SNAME##_iter *iter) \
|
||||
{ \
|
||||
return PFX##_empty(iter->target) || iter->end; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_iter_to_start(SNAME##_iter *iter) \
|
||||
{ \
|
||||
iter->cursor = iter->target->front; \
|
||||
iter->index = 0; \
|
||||
iter->start = true; \
|
||||
iter->end = PFX##_empty(iter->target); \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_iter_to_end(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
iter->cursor = 0; \
|
||||
else \
|
||||
iter->cursor = (iter->target->rear == 0) ? SIZE - 1 : iter->target->rear - 1; \
|
||||
\
|
||||
iter->index = iter->target->count - 1; \
|
||||
iter->start = PFX##_empty(iter->target); \
|
||||
iter->end = true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_next(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (iter->end) \
|
||||
return false; \
|
||||
\
|
||||
iter->start = PFX##_empty(iter->target); \
|
||||
\
|
||||
if (iter->index == iter->target->count - 1) \
|
||||
iter->end = true; \
|
||||
else \
|
||||
{ \
|
||||
iter->cursor = (iter->cursor + 1) % (SIZE); \
|
||||
iter->index++; \
|
||||
} \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_iter_prev(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (iter->start) \
|
||||
return false; \
|
||||
\
|
||||
iter->end = PFX##_empty(iter->target); \
|
||||
\
|
||||
if (iter->index == 0) \
|
||||
iter->start = true; \
|
||||
else \
|
||||
{ \
|
||||
iter->cursor = (iter->cursor == 0) ? SIZE - 1 : iter->cursor - 1; \
|
||||
iter->index--; \
|
||||
} \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return iter->target->buffer[iter->cursor]; \
|
||||
} \
|
||||
\
|
||||
FMOD V *PFX##_iter_rvalue(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
return NULL; \
|
||||
\
|
||||
return &(iter->target->buffer[iter->cursor]); \
|
||||
} \
|
||||
\
|
||||
FMOD size_t PFX##_iter_index(SNAME##_iter *iter) \
|
||||
{ \
|
||||
return iter->index; \
|
||||
} \
|
||||
\
|
||||
static SNAME##_iter PFX##_impl_it_start(SNAME *_queue_) \
|
||||
{ \
|
||||
SNAME##_iter iter; \
|
||||
\
|
||||
PFX##_iter_init(&iter, _queue_); \
|
||||
PFX##_iter_to_start(&iter); \
|
||||
\
|
||||
return iter; \
|
||||
} \
|
||||
\
|
||||
static SNAME##_iter PFX##_impl_it_end(SNAME *_queue_) \
|
||||
{ \
|
||||
SNAME##_iter iter; \
|
||||
\
|
||||
PFX##_iter_init(&iter, _queue_); \
|
||||
PFX##_iter_to_end(&iter); \
|
||||
\
|
||||
return iter; \
|
||||
}
|
||||
|
||||
#endif /* CMC_SAC_QUEUE_H */
|
||||
+3
-10
@@ -79,7 +79,6 @@
|
||||
/* Collection Allocation and Deallocation */ \
|
||||
FMOD SNAME PFX##_new(void); \
|
||||
FMOD void PFX##_clear(SNAME *_stack_); \
|
||||
FMOD void PFX##_free(SNAME *_stack_); \
|
||||
/* Collection Input and Output */ \
|
||||
FMOD bool PFX##_push(SNAME *_stack_, V element); \
|
||||
FMOD bool PFX##_pop(SNAME *_stack_); \
|
||||
@@ -142,17 +141,11 @@
|
||||
\
|
||||
FMOD void PFX##_clear(SNAME *_stack_) \
|
||||
{ \
|
||||
memset(_stack_->buffer, 0, sizeof(V) * SIZE); \
|
||||
memset(_stack_->buffer, 0, sizeof(_stack_->buffer)); \
|
||||
\
|
||||
_stack_->count = 0; \
|
||||
} \
|
||||
\
|
||||
FMOD void PFX##_free(SNAME *_stack_) \
|
||||
{ \
|
||||
free(_stack_->buffer); \
|
||||
free(_stack_); \
|
||||
} \
|
||||
\
|
||||
FMOD bool PFX##_push(SNAME *_stack_, V element) \
|
||||
{ \
|
||||
if (PFX##_full(_stack_)) \
|
||||
@@ -192,7 +185,7 @@
|
||||
FMOD V PFX##_top(SNAME *_stack_) \
|
||||
{ \
|
||||
if (PFX##_empty(_stack_)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return _stack_->buffer[_stack_->count - 1]; \
|
||||
} \
|
||||
@@ -293,7 +286,7 @@
|
||||
FMOD V PFX##_iter_value(SNAME##_iter *iter) \
|
||||
{ \
|
||||
if (PFX##_empty(iter->target)) \
|
||||
PFX##_impl_default_value(); \
|
||||
return PFX##_impl_default_value(); \
|
||||
\
|
||||
return iter->target->buffer[iter->cursor]; \
|
||||
} \
|
||||
|
||||
+1903
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* cmc_string.h
|
||||
*
|
||||
* Creation Date: 17/07/2019
|
||||
*
|
||||
* Authors:
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
*/
|
||||
|
||||
/* A very simple fixed size string used by all collections' method to_string */
|
||||
|
||||
#ifndef CMC_STRING_H
|
||||
#define CMC_STRING_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
static const size_t cmc_string_len = 200;
|
||||
|
||||
typedef struct cmc_string_s
|
||||
{
|
||||
char s[200];
|
||||
} cmc_string;
|
||||
|
||||
/* to_string formats */
|
||||
static const char *cmc_string_fmt_deque = "%s at %p { buffer:%p, capacity:%" PRIuMAX ", count:%" PRIuMAX ", front:%" PRIuMAX ", back:%" PRIuMAX " }";
|
||||
static const char *cmc_string_fmt_hashmap = "%s at %p { buffer:%p, capacity:%" PRIuMAX ", count:%" PRIuMAX ", load:%lf, cmp:%p, hash:%p }";
|
||||
static const char *cmc_string_fmt_hashset = "%s at %p { buffer:%p, capacity:%" PRIuMAX ", count:%" PRIuMAX ", load:%lf, cmp:%p, hash:%p }";
|
||||
static const char *cmc_string_fmt_heap = "%s at %p { buffer:%p, capacity:%" PRIuMAX ", count:%" PRIuMAX ", type:%s, cmp:%p }";
|
||||
static const char *cmc_string_fmt_linkedlist = "%s at %p { count:%" PRIuMAX ", head:%p, tail:%p }";
|
||||
static const char *cmc_string_fmt_list = "%s at %p { buffer:%p, capacity:%" PRIuMAX ", count:%" PRIuMAX " }";
|
||||
static const char *cmc_string_fmt_queue = "%s at %p { buffer:%p, capacity:%" PRIuMAX ", count:%" PRIuMAX ", front:%" PRIuMAX ", back:%" PRIuMAX " }";
|
||||
static const char *cmc_string_fmt_stack = "%s at %p { buffer:%p, capacity:%" PRIuMAX ", count:%" PRIuMAX " }";
|
||||
static const char *cmc_string_fmt_treemap = "%s at %p { root:%p, count:%" PRIuMAX ", cmp:%p }";
|
||||
static const char *cmc_string_fmt_treeset = "%s at %p { root:%p, count:%" PRIuMAX ", cmp:%p }";
|
||||
|
||||
static const char *cmc_string_fmt_intervalheap = "%s at %p { buffer:%p, capacity:%" PRIuMAX ", size:%" PRIuMAX ", count:%" PRIuMAX ", cmp:%p }";
|
||||
static const char *cmc_string_fmt_multimap = "%s at %p { buffer:%p, capacity:%" PRIuMAX ", count:%" PRIuMAX ", load:%lf, cmp:%p, hash:%p }";
|
||||
static const char *cmc_string_fmt_multiset = "%s at %p { buffer:%p, capacity:%" PRIuMAX ", count:%" PRIuMAX ", cardinality:%" PRIuMAX ", load:%lf, cmp:%p, hash:%p }";;
|
||||
|
||||
#endif /* CMC_STRING_H */
|
||||
+9
-3
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @file for_each.h
|
||||
* for_each.h
|
||||
*
|
||||
* Creation Date: 25/02/2019
|
||||
*
|
||||
@@ -8,6 +8,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* ForEach utility
|
||||
*
|
||||
* Read Documentation.md for more information
|
||||
*/
|
||||
|
||||
#ifndef CMC_FOR_EACH
|
||||
#define CMC_FOR_EACH
|
||||
|
||||
@@ -18,7 +24,7 @@
|
||||
{ \
|
||||
BODY; \
|
||||
} \
|
||||
} while (0);
|
||||
} while (0)
|
||||
|
||||
#define FOR_EACH_REV(PFX, SNAME, TARGET, BODY) \
|
||||
do \
|
||||
@@ -27,6 +33,6 @@
|
||||
{ \
|
||||
BODY; \
|
||||
} \
|
||||
} while (0);
|
||||
} while (0)
|
||||
|
||||
#endif /* CMC_FOR_EACH */
|
||||
+35
-68
@@ -8,9 +8,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/* Simple logging macros */
|
||||
/* You can toggle colors defining the macro CMC_LOG_COLOR but be sure that */
|
||||
/* your terminal supports ANSI color escape codes */
|
||||
/**
|
||||
* Simple logging macros
|
||||
* You can toggle colors defining the macro CMC_LOG_COLOR but be sure that
|
||||
* your terminal supports ANSI color escape codes.
|
||||
*
|
||||
* You can customize the logging utility by accessing cmc_log_config and
|
||||
* directly modifying its values to better suit your needs.
|
||||
*/
|
||||
|
||||
/**
|
||||
* What each error code means
|
||||
@@ -40,21 +45,6 @@
|
||||
* ERROR - 5
|
||||
* FATAL - 6
|
||||
*
|
||||
* Functions API
|
||||
*
|
||||
* void cmc_log_tlevel(int level)
|
||||
* Set level for the Terminal Output to stderr.
|
||||
* void cmc_log_flevel(int level)
|
||||
* Set level for the File Output to FILE pointer.
|
||||
* void cmc_log_tenable(bool enabled)
|
||||
* Enables or disables Terminal Output to stderr.
|
||||
* void cmc_log_fenable(bool enabled)
|
||||
* Enables or disables File Output.
|
||||
* void cmc_log_file(FILE *file)
|
||||
* Set a file for writing the logging messages to it.
|
||||
* void cmc_log_set(bool enabled)
|
||||
* Sets on or off all the logging messages.
|
||||
*
|
||||
* How the Log Level is calculated
|
||||
*
|
||||
* - If the log level is 0 all logs are enabled.
|
||||
@@ -74,8 +64,13 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static const char *names[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
|
||||
static const char *color[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"};
|
||||
static const char *cmc_log_names[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
|
||||
|
||||
#ifdef CMC_LOG_COLOR
|
||||
|
||||
static const char *cmc_log_color[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"};
|
||||
|
||||
#endif
|
||||
|
||||
static struct
|
||||
{
|
||||
@@ -85,9 +80,10 @@ static struct
|
||||
bool fenabled; /* File Output Enabled */
|
||||
FILE *file; /* File for Output */
|
||||
bool enabled; /* Logging enabled */
|
||||
} log_config = {0, 0, true, true, NULL, true};
|
||||
|
||||
typedef enum log_type_e
|
||||
} cmc_log_config = {0, 0, true, true, NULL, true};
|
||||
|
||||
typedef enum cmc_log_type_e
|
||||
{
|
||||
LOG_TRACE = 1,
|
||||
LOG_DEBUG = 2,
|
||||
@@ -95,7 +91,8 @@ typedef enum log_type_e
|
||||
LOG_WARN = 4,
|
||||
LOG_ERROR = 5,
|
||||
LOG_FATAL = 6
|
||||
} log_type;
|
||||
|
||||
} cmc_log_type;
|
||||
|
||||
#define log_trace(fmt, ...) cmc_log(LOG_TRACE, __FILE__, __func__, __LINE__, fmt, __VA_ARGS__)
|
||||
#define log_debug(fmt, ...) cmc_log(LOG_DEBUG, __FILE__, __func__, __LINE__, fmt, __VA_ARGS__)
|
||||
@@ -104,46 +101,16 @@ typedef enum log_type_e
|
||||
#define log_error(fmt, ...) cmc_log(LOG_ERROR, __FILE__, __func__, __LINE__, fmt, __VA_ARGS__)
|
||||
#define log_fatal(fmt, ...) cmc_log(LOG_FATAL, __FILE__, __func__, __LINE__, fmt, __VA_ARGS__)
|
||||
|
||||
static void cmc_log_tlevel(int terminal_level)
|
||||
static void cmc_log(cmc_log_type log, const char *filename, const char *funcname, int line, const char *fmt, ...)
|
||||
{
|
||||
log_config.tlevel = terminal_level;
|
||||
}
|
||||
|
||||
static void cmc_log_flevel(int file_level)
|
||||
{
|
||||
log_config.flevel = file_level;
|
||||
}
|
||||
|
||||
static void cmc_log_tenable(bool enabled)
|
||||
{
|
||||
log_config.tenabled = enabled;
|
||||
}
|
||||
|
||||
static void cmc_log_fenable(bool enabled)
|
||||
{
|
||||
log_config.fenabled = enabled;
|
||||
}
|
||||
|
||||
static void cmc_log_file(FILE *file)
|
||||
{
|
||||
log_config.file = file;
|
||||
}
|
||||
|
||||
static void cmc_log_set(bool enabled)
|
||||
{
|
||||
log_config.enabled = enabled;
|
||||
}
|
||||
|
||||
static void cmc_log(log_type log, const char *filename, const char *funcname, int line, const char *fmt, ...)
|
||||
{
|
||||
if (!log_config.enabled)
|
||||
if (!cmc_log_config.enabled)
|
||||
return;
|
||||
|
||||
if (log_config.tenabled)
|
||||
if (cmc_log_config.tenabled)
|
||||
{
|
||||
if (log_config.tlevel < 0 && ((int)log * -1) < log_config.tlevel)
|
||||
if (cmc_log_config.tlevel < 0 && ((int)log * -1) < cmc_log_config.tlevel)
|
||||
return;
|
||||
else if (log_config.tlevel > 0 && (int)log < log_config.tlevel)
|
||||
else if (cmc_log_config.tlevel > 0 && (int)log < cmc_log_config.tlevel)
|
||||
return;
|
||||
|
||||
int i = (int)log - 1;
|
||||
@@ -154,14 +121,14 @@ static void cmc_log(log_type log, const char *filename, const char *funcname, in
|
||||
char time[16];
|
||||
time[strftime(time, sizeof(time), "%H:%M:%S", lt)] = '\0';
|
||||
|
||||
#if defined(CMC_LOG_COLOR)
|
||||
#ifdef CMC_LOG_COLOR
|
||||
fprintf(stderr,
|
||||
"%s %s%5s\x1b[0m \x1b[90m%s at %s:%d:\x1b[0m ",
|
||||
time, color[i], names[i], filename, funcname, line);
|
||||
time, cmc_log_color[i], cmc_log_names[i], filename, funcname, line);
|
||||
#else
|
||||
fprintf(stderr,
|
||||
"%s %5s %s at %s:%d: ",
|
||||
time, names[i], filename, funcname, line);
|
||||
time, cmc_log_names[i], filename, funcname, line);
|
||||
#endif
|
||||
|
||||
va_list args;
|
||||
@@ -173,11 +140,11 @@ static void cmc_log(log_type log, const char *filename, const char *funcname, in
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
if (log_config.file && log_config.fenabled)
|
||||
if (cmc_log_config.file && cmc_log_config.fenabled)
|
||||
{
|
||||
if (log_config.flevel < 0 && ((int)log * -1) < log_config.flevel)
|
||||
if (cmc_log_config.flevel < 0 && ((int)log * -1) < cmc_log_config.flevel)
|
||||
return;
|
||||
else if (log_config.flevel > 0 && (int)log < log_config.flevel)
|
||||
else if (cmc_log_config.flevel > 0 && (int)log < cmc_log_config.flevel)
|
||||
return;
|
||||
|
||||
int i = (int)log - 1;
|
||||
@@ -188,15 +155,15 @@ static void cmc_log(log_type log, const char *filename, const char *funcname, in
|
||||
char time[32];
|
||||
time[strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", lt)] = '\0';
|
||||
|
||||
fprintf(log_config.file, "%s %5s %s at %s:%d: ", time, names[i], filename, funcname, line);
|
||||
fprintf(cmc_log_config.file, "%s %5s %s at %s:%d: ", time, cmc_log_names[i], filename, funcname, line);
|
||||
|
||||
va_list file_args;
|
||||
va_start(file_args, fmt);
|
||||
vfprintf(log_config.file, fmt, file_args);
|
||||
vfprintf(cmc_log_config.file, fmt, file_args);
|
||||
va_end(file_args);
|
||||
|
||||
fprintf(log_config.file, "\n");
|
||||
fflush(log_config.file);
|
||||
fprintf(cmc_log_config.file, "\n");
|
||||
fflush(cmc_log_config.file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* test.h
|
||||
*
|
||||
* Creation Date: 26/06/2019
|
||||
*
|
||||
* Authors:
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simple Unit Test Utility
|
||||
*
|
||||
* CMC_CREATE_UNIT
|
||||
* Create a UnitTest
|
||||
* Parameters:
|
||||
* - UNAME : UnitTest name (const char *)
|
||||
* - VERBOSE : Print PASSED / FAILED messages if true (bool)
|
||||
* - BODY : Body containing all tests related to this UnitTest
|
||||
*
|
||||
* CMC_CREATE_TEST
|
||||
* Create a Test inside a UnitTest.
|
||||
* Parameters:
|
||||
* - TNAME : Test name (const char *)
|
||||
* - BODY : Block of code containing a specific test
|
||||
* Inside the BODY use the following macros:
|
||||
* - CMC_TEST_PASS
|
||||
* Pass current test.
|
||||
* - CMC_TEST_FAIL
|
||||
* Fail current test.
|
||||
* - CMC_TEST_PASS_ELSE_FAIL
|
||||
* If given expression is true, pass current test, else fail.
|
||||
* - CMC_TEST_ABORT
|
||||
* Abort current unit test and displays error message. All tests
|
||||
* after the abort won't be called.
|
||||
* All of these macros can only be called once per test.
|
||||
*
|
||||
* CMC_TEST_COLOR
|
||||
* Define this macro to allow colored output.
|
||||
*/
|
||||
|
||||
#ifndef CMC_TEST_H
|
||||
#define CMC_TEST_H
|
||||
|
||||
#include "timer.h"
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef CMC_TEST_COLOR
|
||||
static const char *cmc_test_color[] = {"\x1b[32m", "\x1b[31m", "\x1b[35m"};
|
||||
#endif
|
||||
|
||||
typedef struct cmc_test_info_s
|
||||
{
|
||||
uintmax_t total;
|
||||
uintmax_t passed;
|
||||
uintmax_t failed;
|
||||
bool aborted;
|
||||
bool verbose;
|
||||
} cmc_test_info;
|
||||
|
||||
/* Logging function */
|
||||
static void cmc_test_log(const char *unit_name, const char *current_test, bool aborted, bool passed)
|
||||
{
|
||||
if (aborted)
|
||||
{
|
||||
#ifdef CMC_TEST_COLOR
|
||||
printf("UNIT_TEST %s %sABORTED\x1b[0m AT %s\n",
|
||||
unit_name,
|
||||
cmc_test_color[2],
|
||||
current_test);
|
||||
#else
|
||||
printf("UNIT_TEST %s ABORTED at %s\n", unit_name, current_test);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CMC_TEST_COLOR
|
||||
printf("Test from %s %s%7s\x1b[0m %s\n",
|
||||
unit_name,
|
||||
passed ? cmc_test_color[0] : cmc_test_color[1],
|
||||
passed ? "PASSED" : "FAILED",
|
||||
current_test);
|
||||
#else
|
||||
printf("Test from %s %7s %s\n", unit_name, passed ? "PASSED" : "FAILED", current_test);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY) \
|
||||
void UNAME(void) \
|
||||
{ \
|
||||
const char *unit_name = #UNAME; \
|
||||
const char *current_test = NULL; \
|
||||
\
|
||||
cmc_test_info tinfo = {0}; \
|
||||
cmc_timer timer = {0}; \
|
||||
\
|
||||
tinfo.verbose = VERBOSE; \
|
||||
\
|
||||
/* Tests */ \
|
||||
timer_start(timer); \
|
||||
BODY; \
|
||||
timer_stop(timer); \
|
||||
timer_calc(timer); \
|
||||
\
|
||||
unittest_abort: \
|
||||
if (tinfo.aborted) \
|
||||
{ \
|
||||
cmc_test_log(unit_name, current_test, true, false); \
|
||||
} \
|
||||
\
|
||||
printf("+--------------------------------------------------+\n"); \
|
||||
printf("| Unit Test Report : %-30s|\n", unit_name); \
|
||||
printf("| TOTAL : %10" PRIuMAX " |\n", tinfo.total); \
|
||||
printf("| PASSED : %10" PRIuMAX " |\n", tinfo.passed); \
|
||||
printf("| FAILED : %10" PRIuMAX " |\n", tinfo.failed); \
|
||||
printf("| |\n"); \
|
||||
printf("| Total Unit Test Runtime : %9.0f milliseconds |\n", timer.result); \
|
||||
printf("+--------------------------------------------------+\n"); \
|
||||
}
|
||||
|
||||
#define CMC_CREATE_TEST(TNAME, BODY) \
|
||||
do \
|
||||
{ \
|
||||
current_test = #TNAME; \
|
||||
\
|
||||
tinfo.total += 1; \
|
||||
\
|
||||
BODY; \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define CMC_TEST_PASS() \
|
||||
do \
|
||||
{ \
|
||||
tinfo.passed += 1; \
|
||||
if (tinfo.verbose) \
|
||||
cmc_test_log(unit_name, current_test, false, true); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define CMC_TEST_FAIL() \
|
||||
do \
|
||||
{ \
|
||||
tinfo.failed += 1; \
|
||||
if (tinfo.verbose) \
|
||||
cmc_test_log(unit_name, current_test, false, false); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define CMC_TEST_PASS_ELSE_FAIL(EXPRESSION) \
|
||||
do \
|
||||
{ \
|
||||
if (EXPRESSION) \
|
||||
CMC_TEST_PASS(); \
|
||||
else \
|
||||
CMC_TEST_FAIL(); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define CMC_TEST_ABORT() \
|
||||
do \
|
||||
{ \
|
||||
\
|
||||
tinfo.aborted = true; \
|
||||
tinfo.total -= 1; \
|
||||
goto unittest_abort; \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#endif /* CMC_TEST_H */
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* timer.h
|
||||
*
|
||||
* Creation Date: 12/04/2019
|
||||
*
|
||||
* Authors:
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
*/
|
||||
|
||||
/* Simple timer macros utility */
|
||||
|
||||
#ifndef CMC_TIMER_H
|
||||
#define CMC_TIMER_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
typedef struct cmc_timer_s
|
||||
{
|
||||
clock_t start;
|
||||
clock_t stop;
|
||||
double result;
|
||||
} cmc_timer;
|
||||
|
||||
#define timer_start(timer) \
|
||||
do \
|
||||
{ \
|
||||
cmc_timer *t = &(timer); \
|
||||
t->start = clock(); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define timer_stop(timer) \
|
||||
do \
|
||||
{ \
|
||||
cmc_timer *t = &(timer);\
|
||||
t->stop = clock(); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define timer_calc(timer) \
|
||||
do \
|
||||
{ \
|
||||
cmc_timer *t = &(timer); \
|
||||
t->result = (double)(t->stop - t->start) * 1000.0 / CLOCKS_PER_SEC; \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#endif /* CMC_TIMER_H */
|
||||
+10
-8
@@ -1,15 +1,17 @@
|
||||
CFLAGS= -Wno-unused-function -Wno-unused-paramter -Wno-unused-variable -std=c11 -Wall -Wextra
|
||||
CC = gcc
|
||||
CFLAGS = -std=c11 -Wall -Wextra
|
||||
CFLAGS += -Wno-unused-function -Wno-unused-paramter -Wno-unused-variable -Wno-unused-label
|
||||
|
||||
main:
|
||||
gcc foreach.c -o foreach $(CFLAGS)
|
||||
$(CC) foreach.c -o foreach $(CFLAGS)
|
||||
./foreach
|
||||
gcc iterators.c -o iterators $(CFLAGS)
|
||||
$(CC) iterators.c -o iterators $(CFLAGS)
|
||||
./iterators
|
||||
gcc contains.c -o contains $(CFLAGS)
|
||||
$(CC) contains.c -o contains $(CFLAGS)
|
||||
./contains
|
||||
gcc types.c -o types $(CFLAGS)
|
||||
$(CC) types.c -o types $(CFLAGS)
|
||||
./types
|
||||
gcc -c ./sep/sep.c -o ./sep/sep.o $(CFLAGS)
|
||||
gcc -c ./sep/main.c -o ./sep/main.o $(CFLAGS)
|
||||
gcc ./sep/sep.o ./sep/main.o -o ./sep/sep
|
||||
$(CC) -c ./sep/sep.c -o ./sep/sep.o $(CFLAGS)
|
||||
$(CC) -c ./sep/main.c -o ./sep/main.o $(CFLAGS)
|
||||
$(CC) ./sep/sep.o ./sep/main.o -o ./sep/sep
|
||||
./sep/sep
|
||||
@@ -1,5 +1,6 @@
|
||||
# Tests
|
||||
|
||||
* `main` - Unit tests for the main C Macro Collections library with code coverage;
|
||||
* `sep/main.c`, `sep/sep.c` and `sep/sep.h` - Tests separate header and source compilation;
|
||||
* `contains.c` - Tests the `contains` function for all collections;
|
||||
* `foreach.c` - Tests the ForEach macros for all collections;
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ int main(void)
|
||||
queue *q = q_new(1000);
|
||||
deque *d = d_new(1000);
|
||||
linked *ll = ll_new();
|
||||
heap *h = h_new(1000, MaxHeap, intcmp);
|
||||
heap *h = h_new(1000, cmc_max_heap, intcmp);
|
||||
tset *ts = ts_new(intcmp);
|
||||
tmap *tm = tm_new(intcmp);
|
||||
hset *hs = hs_new(1000, 0.9, intcmp, inthash);
|
||||
|
||||
+21
-21
@@ -37,7 +37,7 @@ int main(void)
|
||||
queue *q = q_new(1000);
|
||||
deque *d = d_new(1000);
|
||||
linked *ll = ll_new();
|
||||
heap *h = h_new(1000, MaxHeap, intcmp);
|
||||
heap *h = h_new(1000, cmc_max_heap, intcmp);
|
||||
tset *ts = ts_new(intcmp);
|
||||
tmap *tm = tm_new(intcmp);
|
||||
hset *hs = hs_new(1000, 0.9, intcmp, inthash);
|
||||
@@ -72,45 +72,45 @@ int main(void)
|
||||
|
||||
FOR_EACH(d, deque, d, {
|
||||
sum3 += d_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(hm, hmap, hm, {
|
||||
sum10 += hm_iter_key(&iter);
|
||||
sum11 += hm_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(hs, hset, hs, {
|
||||
sum9 += hs_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(h, heap, h, {
|
||||
sum5 += h_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(ll, linked, ll, {
|
||||
sum4 += ll_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(l, list, l, {
|
||||
sum0 += l_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(q, queue, q, {
|
||||
sum2 += q_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(s, stack, s, {
|
||||
sum1 += s_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(tm, tmap, tm, {
|
||||
sum7 += tm_iter_key(&iter);
|
||||
sum8 += tm_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH(ts, tset, ts, {
|
||||
sum6 += ts_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
printf("\n-------------------- FOR_EACH --------------------\n");
|
||||
if (sum3 == 50005000)
|
||||
@@ -161,45 +161,45 @@ int main(void)
|
||||
|
||||
FOR_EACH_REV(d, deque, d, {
|
||||
sum3 += d_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH_REV(hm, hmap, hm, {
|
||||
sum10 += hm_iter_key(&iter);
|
||||
sum11 += hm_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH_REV(hs, hset, hs, {
|
||||
sum9 += hs_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH_REV(h, heap, h, {
|
||||
sum5 += h_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH_REV(ll, linked, ll, {
|
||||
sum4 += ll_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH_REV(l, list, l, {
|
||||
sum0 += l_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH_REV(q, queue, q, {
|
||||
sum2 += q_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH_REV(s, stack, s, {
|
||||
sum1 += s_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH_REV(tm, tmap, tm, {
|
||||
sum7 += tm_iter_key(&iter);
|
||||
sum8 += tm_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
FOR_EACH_REV(ts, tset, ts, {
|
||||
sum6 += ts_iter_value(&iter);
|
||||
})
|
||||
});
|
||||
|
||||
printf("-------------------- FOR_EACH_REV --------------------\n");
|
||||
if (sum3 == 50005000)
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ COLLECTION_GENERATE(HASHMAP, hm, hmap, /* FMOD */, int, double)
|
||||
ITERATOR_TEST(DEQUE, d, deque, d_new(20), d_push_front(coll, i), sum1 += d_iter_value(&iter), sum1 == 450, 1)
|
||||
ITERATOR_TEST(HASHMAP, hm, hmap, hm_new(50, 0.6, intcmp, inthash), hm_insert(coll, i, (double)i / 10.0), sum1 += hm_iter_key(&iter); sum2 += hm_iter_value(&iter), sum1 == 450, sum2 == 45.0)
|
||||
ITERATOR_TEST(HASHSET, hs, hset, hs_new(50, 0.6, intcmp, inthash), hs_insert(coll, i), sum1 += hs_iter_value(&iter), sum1 == 450, 1)
|
||||
ITERATOR_TEST(HEAP, h, heap, h_new(20, MaxHeap, intcmp), h_insert(coll, i), sum1 += h_iter_value(&iter), sum1 == 450, 1)
|
||||
ITERATOR_TEST(HEAP, h, heap, h_new(20, cmc_max_heap, intcmp), h_insert(coll, i), sum1 += h_iter_value(&iter), sum1 == 450, 1)
|
||||
ITERATOR_TEST(LINKEDLIST, ll, linked, ll_new(), ll_push_back(coll, i), sum1 += ll_iter_value(&iter), sum1 == 450, 1)
|
||||
ITERATOR_TEST(LIST, l, list, l_new(20), l_push_back(coll, i), sum1 += l_iter_value(&iter), sum1 == 450, 1)
|
||||
ITERATOR_TEST(QUEUE, q, queue, q_new(20), q_enqueue(coll, i), sum1 += q_iter_value(&iter), sum1 == 450, 1)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
CC = gcc
|
||||
CFLAGS = -std=c11 -Wall -Wextra
|
||||
CFLAGS += -Wno-unused-function -Wno-unused-parameter -Wno-unused-variable -Wno-unused-label
|
||||
CVFLAGS = --coverage -O0
|
||||
INCLUDE = ../../src/
|
||||
|
||||
main:
|
||||
$(CC) main.c -o main $(CFLAGS) $(CVFLAGS) -I $(INCLUDE)
|
||||
./main
|
||||
gcov ./main.c
|
||||
|
||||
dev:
|
||||
$(CC) main.c -o main $(CFLAGS) $(CVFLAGS) -I $(INCLUDE)
|
||||
./main
|
||||
gcov ./main.c -n
|
||||
rm *.gcda *.gcno *.exe
|
||||
@@ -0,0 +1,7 @@
|
||||
# main
|
||||
|
||||
Tests for the main C Macro Collections library. In order to provide a good code coverage, all macros have been expanded and added to `src/*.c`.
|
||||
|
||||
* `src` - Since gcov fails to detect code that was expanded by macro, all collections where manually expanded and dumped into their respective source files;
|
||||
* `unt` - Unit Tests;
|
||||
* `main.c` - Where all tests are compiled together and called by main().
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "unt/deque.c"
|
||||
#include "unt/hashmap.c"
|
||||
#include "unt/hashset.c"
|
||||
#include "unt/heap.c"
|
||||
#include "unt/linkedlist.c"
|
||||
#include "unt/list.c"
|
||||
#include "unt/queue.c"
|
||||
#include "unt/stack.c"
|
||||
#include "unt/treemap.c"
|
||||
#include "unt/treeset.c"
|
||||
|
||||
#include "unt/linkedqueue.c"
|
||||
#include "unt/multimap.c"
|
||||
#include "unt/multiset.c"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
deque_test();
|
||||
hashmap_test();
|
||||
hashset_test();
|
||||
heap_test();
|
||||
linkedlist_test();
|
||||
list_test();
|
||||
queue_test();
|
||||
stack_test();
|
||||
treemap_test();
|
||||
treeset_test();
|
||||
|
||||
/* ext */
|
||||
linkedqueue_test();
|
||||
multimap_test();
|
||||
multiset_test();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
#include "cmc/deque.h"
|
||||
|
||||
//DEQUE_GENERATE(d, deque, , size_t)
|
||||
|
||||
typedef struct deque_s
|
||||
{
|
||||
size_t *buffer;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
size_t front;
|
||||
size_t back;
|
||||
struct deque_iter_s (*it_start)(struct deque_s *);
|
||||
struct deque_iter_s (*it_end)(struct deque_s *);
|
||||
} deque, *deque_ptr;
|
||||
typedef struct deque_iter_s
|
||||
{
|
||||
struct deque_s *target;
|
||||
size_t cursor;
|
||||
size_t index;
|
||||
bool start;
|
||||
bool end;
|
||||
} deque_iter, *deque_iter_ptr;
|
||||
deque *d_new(size_t capacity);
|
||||
void d_clear(deque *_deque_);
|
||||
void d_free(deque *_deque_);
|
||||
bool d_push_front(deque *_deque_, size_t element);
|
||||
bool d_push_back(deque *_deque_, size_t element);
|
||||
bool d_pop_front(deque *_deque_);
|
||||
bool d_pop_back(deque *_deque_);
|
||||
bool d_push_front_if(deque *_deque_, size_t element, bool condition);
|
||||
bool d_push_back_if(deque *_deque_, size_t element, bool condition);
|
||||
bool d_pop_front_if(deque *_deque_, bool condition);
|
||||
bool d_pop_back_if(deque *_deque_, bool condition);
|
||||
size_t d_front(deque *_deque_);
|
||||
size_t d_back(deque *_deque_);
|
||||
bool d_contains(deque *_deque_, size_t element, int (*comparator)(size_t, size_t));
|
||||
bool d_empty(deque *_deque_);
|
||||
bool d_full(deque *_deque_);
|
||||
size_t d_count(deque *_deque_);
|
||||
size_t d_capacity(deque *_deque_);
|
||||
deque_iter *d_iter_new(deque *target);
|
||||
void d_iter_free(deque_iter *iter);
|
||||
void d_iter_init(deque_iter *iter, deque *target);
|
||||
bool d_iter_start(deque_iter *iter);
|
||||
bool d_iter_end(deque_iter *iter);
|
||||
void d_iter_to_start(deque_iter *iter);
|
||||
void d_iter_to_end(deque_iter *iter);
|
||||
bool d_iter_next(deque_iter *iter);
|
||||
bool d_iter_prev(deque_iter *iter);
|
||||
size_t d_iter_value(deque_iter *iter);
|
||||
size_t *d_iter_rvalue(deque_iter *iter);
|
||||
size_t d_iter_index(deque_iter *iter);
|
||||
static inline size_t d_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
static bool d_impl_grow(deque *_deque_);
|
||||
static deque_iter d_impl_it_start(deque *_deque_);
|
||||
static deque_iter d_impl_it_end(deque *_deque_);
|
||||
deque *d_new(size_t capacity)
|
||||
{
|
||||
if (capacity < 1)
|
||||
return NULL;
|
||||
deque *_deque_ = malloc(sizeof(deque));
|
||||
if (!_deque_)
|
||||
return NULL;
|
||||
_deque_->buffer = malloc(sizeof(size_t) * capacity);
|
||||
if (!_deque_->buffer)
|
||||
{
|
||||
free(_deque_);
|
||||
return NULL;
|
||||
}
|
||||
memset(_deque_->buffer, 0, sizeof(size_t) * capacity);
|
||||
_deque_->capacity = capacity;
|
||||
_deque_->count = 0;
|
||||
_deque_->front = 0;
|
||||
_deque_->back = 0;
|
||||
_deque_->it_start = d_impl_it_start;
|
||||
_deque_->it_end = d_impl_it_end;
|
||||
return _deque_;
|
||||
}
|
||||
void d_clear(deque *_deque_)
|
||||
{
|
||||
memset(_deque_->buffer, 0, sizeof(size_t) * _deque_->capacity);
|
||||
_deque_->count = 0;
|
||||
_deque_->front = 0;
|
||||
_deque_->back = 0;
|
||||
}
|
||||
void d_free(deque *_deque_)
|
||||
{
|
||||
free(_deque_->buffer);
|
||||
free(_deque_);
|
||||
}
|
||||
bool d_push_front(deque *_deque_, size_t element)
|
||||
{
|
||||
if (d_full(_deque_))
|
||||
{
|
||||
if (!d_impl_grow(_deque_))
|
||||
return false;
|
||||
}
|
||||
_deque_->front = (_deque_->front == 0) ? _deque_->capacity - 1 : _deque_->front - 1;
|
||||
_deque_->buffer[_deque_->front] = element;
|
||||
_deque_->count++;
|
||||
return true;
|
||||
}
|
||||
bool d_push_back(deque *_deque_, size_t element)
|
||||
{
|
||||
if (d_full(_deque_))
|
||||
{
|
||||
if (!d_impl_grow(_deque_))
|
||||
return false;
|
||||
}
|
||||
_deque_->buffer[_deque_->back] = element;
|
||||
_deque_->back = (_deque_->back == _deque_->capacity - 1) ? 0 : _deque_->back + 1;
|
||||
_deque_->count++;
|
||||
return true;
|
||||
}
|
||||
bool d_pop_front(deque *_deque_)
|
||||
{
|
||||
if (d_empty(_deque_))
|
||||
return false;
|
||||
_deque_->buffer[_deque_->front] = d_impl_default_value();
|
||||
_deque_->front = (_deque_->front == _deque_->capacity - 1) ? 0 : _deque_->front + 1;
|
||||
_deque_->count--;
|
||||
return true;
|
||||
}
|
||||
bool d_pop_back(deque *_deque_)
|
||||
{
|
||||
if (d_empty(_deque_))
|
||||
return false;
|
||||
_deque_->back = (_deque_->back == 0) ? _deque_->capacity - 1 : _deque_->back - 1;
|
||||
_deque_->buffer[_deque_->back] = d_impl_default_value();
|
||||
_deque_->count--;
|
||||
return true;
|
||||
}
|
||||
bool d_push_front_if(deque *_deque_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return d_push_front(_deque_, element);
|
||||
return false;
|
||||
}
|
||||
bool d_push_back_if(deque *_deque_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return d_push_back(_deque_, element);
|
||||
return false;
|
||||
}
|
||||
bool d_pop_front_if(deque *_deque_, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return d_pop_front(_deque_);
|
||||
return false;
|
||||
}
|
||||
bool d_pop_back_if(deque *_deque_, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return d_pop_back(_deque_);
|
||||
return false;
|
||||
}
|
||||
size_t d_front(deque *_deque_)
|
||||
{
|
||||
if (d_empty(_deque_))
|
||||
return d_impl_default_value();
|
||||
return _deque_->buffer[_deque_->front];
|
||||
}
|
||||
size_t d_back(deque *_deque_)
|
||||
{
|
||||
if (d_empty(_deque_))
|
||||
return d_impl_default_value();
|
||||
return _deque_->buffer[(_deque_->back == 0) ? _deque_->capacity - 1 : _deque_->back - 1];
|
||||
}
|
||||
bool d_contains(deque *_deque_, size_t element, int (*comparator)(size_t, size_t))
|
||||
{
|
||||
for (size_t i = _deque_->front, j = 0; j < _deque_->count; i = (i + 1) % _deque_->count, j++)
|
||||
{
|
||||
if (comparator(_deque_->buffer[i], element) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool d_empty(deque *_deque_) { return _deque_->count == 0; }
|
||||
bool d_full(deque *_deque_) { return _deque_->count >= _deque_->capacity; }
|
||||
size_t d_count(deque *_deque_) { return _deque_->count; }
|
||||
size_t d_capacity(deque *_deque_) { return _deque_->capacity; }
|
||||
deque_iter *d_iter_new(deque *target)
|
||||
{
|
||||
deque_iter *iter = malloc(sizeof(deque_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
d_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void d_iter_free(deque_iter *iter) { free(iter); }
|
||||
void d_iter_init(deque_iter *iter, deque *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->cursor = target->front;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = d_empty(target);
|
||||
}
|
||||
bool d_iter_start(deque_iter *iter) { return d_empty(iter->target) || iter->start; }
|
||||
bool d_iter_end(deque_iter *iter) { return d_empty(iter->target) || iter->end; }
|
||||
void d_iter_to_start(deque_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->target->front;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = d_empty(iter->target);
|
||||
}
|
||||
void d_iter_to_end(deque_iter *iter)
|
||||
{
|
||||
if (d_empty(iter->target))
|
||||
iter->cursor = 0;
|
||||
else
|
||||
iter->cursor = (iter->target->back == 0) ? iter->target->capacity - 1 : iter->target->back - 1;
|
||||
iter->index = iter->target->count - 1;
|
||||
iter->start = d_empty(iter->target);
|
||||
iter->end = true;
|
||||
}
|
||||
bool d_iter_next(deque_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
iter->start = d_empty(iter->target);
|
||||
if (iter->index == iter->target->count - 1)
|
||||
iter->end = true;
|
||||
else
|
||||
{
|
||||
iter->cursor = (iter->cursor + 1) % (iter->target->capacity);
|
||||
iter->index++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool d_iter_prev(deque_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
iter->end = d_empty(iter->target);
|
||||
if (iter->index == 0)
|
||||
iter->start = true;
|
||||
else
|
||||
{
|
||||
iter->cursor = (iter->cursor == 0) ? iter->target->capacity - 1 : iter->cursor - 1;
|
||||
iter->index--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
size_t d_iter_value(deque_iter *iter)
|
||||
{
|
||||
if (d_empty(iter->target))
|
||||
return d_impl_default_value();
|
||||
return iter->target->buffer[iter->cursor];
|
||||
}
|
||||
size_t *d_iter_rvalue(deque_iter *iter)
|
||||
{
|
||||
if (d_empty(iter->target))
|
||||
return NULL;
|
||||
return &(iter->target->buffer[iter->cursor]);
|
||||
}
|
||||
size_t d_iter_index(deque_iter *iter) { return iter->index; }
|
||||
static bool d_impl_grow(deque *_deque_)
|
||||
{
|
||||
size_t new_capacity = _deque_->capacity * 2;
|
||||
size_t *new_buffer = malloc(sizeof(size_t) * new_capacity);
|
||||
if (!new_buffer)
|
||||
return false;
|
||||
for (size_t i = _deque_->front, j = 0; j < _deque_->count; i = (i + 1) % _deque_->capacity, j++)
|
||||
{
|
||||
new_buffer[j] = _deque_->buffer[i];
|
||||
}
|
||||
free(_deque_->buffer);
|
||||
_deque_->buffer = new_buffer;
|
||||
_deque_->capacity = new_capacity;
|
||||
_deque_->front = 0;
|
||||
_deque_->back = _deque_->count;
|
||||
return true;
|
||||
}
|
||||
static deque_iter d_impl_it_start(deque *_deque_)
|
||||
{
|
||||
deque_iter iter;
|
||||
d_iter_init(&iter, _deque_);
|
||||
return iter;
|
||||
}
|
||||
static deque_iter d_impl_it_end(deque *_deque_)
|
||||
{
|
||||
deque_iter iter;
|
||||
d_iter_init(&iter, _deque_);
|
||||
d_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
#include "cmc/hashmap.h"
|
||||
|
||||
//HASHMAP_GENERATE(hm, hashmap, , size_t, size_t)
|
||||
|
||||
typedef struct hashmap_s
|
||||
{
|
||||
struct hashmap_entry_s *buffer;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
double load;
|
||||
int (*cmp)(size_t, size_t);
|
||||
size_t (*hash)(size_t);
|
||||
struct hashmap_iter_s (*it_start)(struct hashmap_s *);
|
||||
struct hashmap_iter_s (*it_end)(struct hashmap_s *);
|
||||
} hashmap, *hashmap_ptr;
|
||||
typedef struct hashmap_entry_s
|
||||
{
|
||||
size_t key;
|
||||
size_t value;
|
||||
size_t dist;
|
||||
enum cmc_entry_state_e state;
|
||||
} hashmap_entry, *hashmap_entry_ptr;
|
||||
typedef struct hashmap_iter_s
|
||||
{
|
||||
struct hashmap_s *target;
|
||||
size_t cursor;
|
||||
size_t index;
|
||||
size_t first;
|
||||
size_t last;
|
||||
bool start;
|
||||
bool end;
|
||||
} hashmap_iter, *hashmap_iter_ptr;
|
||||
hashmap *hm_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t));
|
||||
void hm_clear(hashmap *_map_);
|
||||
void hm_free(hashmap *_map_);
|
||||
bool hm_insert(hashmap *_map_, size_t key, size_t value);
|
||||
bool hm_remove(hashmap *_map_, size_t key, size_t *value);
|
||||
bool hm_insert_if(hashmap *_map_, size_t key, size_t value, bool condition);
|
||||
bool hm_remove_if(hashmap *_map_, size_t key, size_t *value, bool condition);
|
||||
bool hm_max(hashmap *_map_, size_t *key, size_t *value);
|
||||
bool hm_min(hashmap *_map_, size_t *key, size_t *value);
|
||||
size_t hm_get(hashmap *_map_, size_t key);
|
||||
size_t *hm_get_ref(hashmap *_map_, size_t key);
|
||||
bool hm_contains(hashmap *_map_, size_t key);
|
||||
bool hm_empty(hashmap *_map_);
|
||||
size_t hm_count(hashmap *_map_);
|
||||
size_t hm_capacity(hashmap *_map_);
|
||||
cmc_string hm_to_string(hashmap *_map_);
|
||||
hashmap_iter *hm_iter_new(hashmap *target);
|
||||
void hm_iter_free(hashmap_iter *iter);
|
||||
void hm_iter_init(hashmap_iter *iter, hashmap *target);
|
||||
bool hm_iter_start(hashmap_iter *iter);
|
||||
bool hm_iter_end(hashmap_iter *iter);
|
||||
void hm_iter_to_start(hashmap_iter *iter);
|
||||
void hm_iter_to_end(hashmap_iter *iter);
|
||||
bool hm_iter_next(hashmap_iter *iter);
|
||||
bool hm_iter_prev(hashmap_iter *iter);
|
||||
size_t hm_iter_key(hashmap_iter *iter);
|
||||
size_t hm_iter_value(hashmap_iter *iter);
|
||||
size_t *hm_iter_rvalue(hashmap_iter *iter);
|
||||
size_t hm_iter_index(hashmap_iter *iter);
|
||||
static inline size_t hm_impl_default_key(void)
|
||||
{
|
||||
size_t _empty_key_;
|
||||
memset(&_empty_key_, 0, sizeof(size_t));
|
||||
return _empty_key_;
|
||||
}
|
||||
static inline size_t hm_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
static bool hm_impl_grow(hashmap *_map_);
|
||||
static hashmap_entry *hm_impl_get_entry(hashmap *_map_, size_t key);
|
||||
static size_t hm_impl_calculate_size(size_t required);
|
||||
static hashmap_iter hm_impl_it_start(hashmap *_map_);
|
||||
static hashmap_iter hm_impl_it_end(hashmap *_map_);
|
||||
hashmap *hm_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t))
|
||||
{
|
||||
if (capacity == 0 || load <= 0 || load >= 1)
|
||||
return NULL;
|
||||
if (capacity >= UINTMAX_MAX * load)
|
||||
return NULL;
|
||||
size_t real_capacity = hm_impl_calculate_size(capacity / load);
|
||||
hashmap *_map_ = malloc(sizeof(hashmap));
|
||||
if (!_map_)
|
||||
return NULL;
|
||||
_map_->buffer = malloc(sizeof(hashmap_entry) * real_capacity);
|
||||
if (!_map_->buffer)
|
||||
{
|
||||
free(_map_);
|
||||
return NULL;
|
||||
}
|
||||
memset(_map_->buffer, 0, sizeof(hashmap_entry) * real_capacity);
|
||||
_map_->count = 0;
|
||||
_map_->capacity = real_capacity;
|
||||
_map_->load = load;
|
||||
_map_->cmp = compare;
|
||||
_map_->hash = hash;
|
||||
_map_->it_start = hm_impl_it_start;
|
||||
_map_->it_end = hm_impl_it_end;
|
||||
return _map_;
|
||||
}
|
||||
void hm_clear(hashmap *_map_)
|
||||
{
|
||||
memset(_map_->buffer, 0, sizeof(hashmap_entry) * _map_->capacity);
|
||||
_map_->count = 0;
|
||||
}
|
||||
void hm_free(hashmap *_map_)
|
||||
{
|
||||
free(_map_->buffer);
|
||||
free(_map_);
|
||||
}
|
||||
bool hm_insert(hashmap *_map_, size_t key, size_t value)
|
||||
{
|
||||
if ((double)_map_->capacity * _map_->load <= (double)_map_->count)
|
||||
{
|
||||
if (!hm_impl_grow(_map_))
|
||||
return false;
|
||||
}
|
||||
size_t hash = _map_->hash(key);
|
||||
size_t original_pos = hash % _map_->capacity;
|
||||
size_t pos = original_pos;
|
||||
hashmap_entry *target = &(_map_->buffer[pos]);
|
||||
if (hm_impl_get_entry(_map_, key) != NULL)
|
||||
return false;
|
||||
if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED)
|
||||
{
|
||||
target->key = key;
|
||||
target->value = value;
|
||||
target->dist = original_pos - pos;
|
||||
target->state = CMC_ES_FILLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
pos++;
|
||||
target = &(_map_->buffer[pos % _map_->capacity]);
|
||||
if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED)
|
||||
{
|
||||
target->key = key;
|
||||
target->value = value;
|
||||
target->dist = pos - original_pos;
|
||||
target->state = CMC_ES_FILLED;
|
||||
break;
|
||||
}
|
||||
else if (target->dist < original_pos - pos)
|
||||
{
|
||||
size_t tmp_k = target->key;
|
||||
size_t tmp_v = target->value;
|
||||
size_t tmp_dist = target->dist;
|
||||
target->key = key;
|
||||
target->value = value;
|
||||
target->dist = pos - original_pos;
|
||||
key = tmp_k;
|
||||
value = tmp_v;
|
||||
original_pos = pos - tmp_dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
_map_->count++;
|
||||
return true;
|
||||
}
|
||||
bool hm_remove(hashmap *_map_, size_t key, size_t *value)
|
||||
{
|
||||
hashmap_entry *result = hm_impl_get_entry(_map_, key);
|
||||
if (result == NULL)
|
||||
return false;
|
||||
*value = result->value;
|
||||
result->key = hm_impl_default_key();
|
||||
result->value = hm_impl_default_value();
|
||||
result->dist = 0;
|
||||
result->state = CMC_ES_DELETED;
|
||||
_map_->count--;
|
||||
return true;
|
||||
}
|
||||
bool hm_insert_if(hashmap *_map_, size_t key, size_t value, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return hm_insert(_map_, key, value);
|
||||
return false;
|
||||
}
|
||||
bool hm_remove_if(hashmap *_map_, size_t key, size_t *value, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return hm_remove(_map_, key, value);
|
||||
return false;
|
||||
}
|
||||
bool hm_max(hashmap *_map_, size_t *key, size_t *value)
|
||||
{
|
||||
if (hm_empty(_map_))
|
||||
return false;
|
||||
hashmap_iter iter;
|
||||
for (hm_iter_init(&iter, _map_); !hm_iter_end(&iter); hm_iter_next(&iter))
|
||||
{
|
||||
size_t result_key = hm_iter_key(&iter);
|
||||
size_t result_value = hm_iter_value(&iter);
|
||||
size_t index = hm_iter_index(&iter);
|
||||
if (index == 0)
|
||||
{
|
||||
*key = result_key;
|
||||
*value = result_value;
|
||||
}
|
||||
else if (_map_->cmp(result_key, *key) > 0)
|
||||
{
|
||||
*key = result_key;
|
||||
*value = result_value;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool hm_min(hashmap *_map_, size_t *key, size_t *value)
|
||||
{
|
||||
if (hm_empty(_map_))
|
||||
return false;
|
||||
hashmap_iter iter;
|
||||
for (hm_iter_init(&iter, _map_); !hm_iter_end(&iter); hm_iter_next(&iter))
|
||||
{
|
||||
size_t result_key = hm_iter_key(&iter);
|
||||
size_t result_value = hm_iter_value(&iter);
|
||||
size_t index = hm_iter_index(&iter);
|
||||
if (index == 0)
|
||||
{
|
||||
*key = result_key;
|
||||
*value = result_value;
|
||||
}
|
||||
else if (_map_->cmp(result_key, *key) < 0)
|
||||
{
|
||||
*key = result_key;
|
||||
*value = result_value;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
size_t hm_get(hashmap *_map_, size_t key)
|
||||
{
|
||||
hashmap_entry *entry = hm_impl_get_entry(_map_, key);
|
||||
if (!entry)
|
||||
return hm_impl_default_value();
|
||||
return entry->value;
|
||||
}
|
||||
size_t *hm_get_ref(hashmap *_map_, size_t key)
|
||||
{
|
||||
hashmap_entry *entry = hm_impl_get_entry(_map_, key);
|
||||
if (!entry)
|
||||
return NULL;
|
||||
return &(entry->value);
|
||||
}
|
||||
bool hm_contains(hashmap *_map_, size_t key) { return hm_impl_get_entry(_map_, key) != NULL; }
|
||||
bool hm_empty(hashmap *_map_) { return _map_->count == 0; }
|
||||
size_t hm_count(hashmap *_map_) { return _map_->count; }
|
||||
size_t hm_capacity(hashmap *_map_) { return _map_->capacity; }
|
||||
cmc_string hm_to_string(hashmap *_map_)
|
||||
{
|
||||
cmc_string str;
|
||||
hashmap *m_ = _map_;
|
||||
const char *name = "hashmap";
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_hashmap, name, m_, m_->buffer, m_->capacity, m_->count, m_->load, m_->cmp, m_->hash);
|
||||
return str;
|
||||
}
|
||||
hashmap_iter *hm_iter_new(hashmap *target)
|
||||
{
|
||||
hashmap_iter *iter = malloc(sizeof(hashmap_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
hm_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void hm_iter_free(hashmap_iter *iter) { free(iter); }
|
||||
void hm_iter_init(hashmap_iter *iter, hashmap *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->cursor = 0;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = hm_empty(target);
|
||||
if (!hm_empty(target))
|
||||
{
|
||||
for (size_t i = 0; i < target->capacity; i++)
|
||||
{
|
||||
if (target->buffer[i].state == CMC_ES_FILLED)
|
||||
{
|
||||
iter->first = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
iter->cursor = iter->first;
|
||||
for (size_t i = target->capacity; i > 0; i--)
|
||||
{
|
||||
if (target->buffer[i - 1].state == CMC_ES_FILLED)
|
||||
{
|
||||
iter->last = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool hm_iter_start(hashmap_iter *iter) { return hm_empty(iter->target) || iter->start; }
|
||||
bool hm_iter_end(hashmap_iter *iter) { return hm_empty(iter->target) || iter->end; }
|
||||
void hm_iter_to_start(hashmap_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->first;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = hm_empty(iter->target);
|
||||
}
|
||||
void hm_iter_to_end(hashmap_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->last;
|
||||
iter->index = iter->target->count - 1;
|
||||
iter->start = hm_empty(iter->target);
|
||||
iter->end = true;
|
||||
}
|
||||
bool hm_iter_next(hashmap_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
hashmap_entry *scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (iter->cursor == iter->last)
|
||||
{
|
||||
iter->end = true;
|
||||
return false;
|
||||
}
|
||||
iter->index++;
|
||||
while (1)
|
||||
{
|
||||
iter->cursor++;
|
||||
scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (scan->state == CMC_ES_FILLED)
|
||||
break;
|
||||
}
|
||||
iter->start = hm_empty(iter->target);
|
||||
return true;
|
||||
}
|
||||
bool hm_iter_prev(hashmap_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
hashmap_entry *scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (iter->cursor == iter->first)
|
||||
{
|
||||
iter->start = true;
|
||||
return false;
|
||||
}
|
||||
iter->index--;
|
||||
while (1)
|
||||
{
|
||||
iter->cursor--;
|
||||
scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (scan->state == CMC_ES_FILLED)
|
||||
break;
|
||||
}
|
||||
iter->end = hm_empty(iter->target);
|
||||
return true;
|
||||
}
|
||||
size_t hm_iter_key(hashmap_iter *iter)
|
||||
{
|
||||
if (hm_empty(iter->target))
|
||||
return hm_impl_default_key();
|
||||
return iter->target->buffer[iter->cursor].key;
|
||||
}
|
||||
size_t hm_iter_value(hashmap_iter *iter)
|
||||
{
|
||||
if (hm_empty(iter->target))
|
||||
return hm_impl_default_value();
|
||||
return iter->target->buffer[iter->cursor].value;
|
||||
}
|
||||
size_t *hm_iter_rvalue(hashmap_iter *iter)
|
||||
{
|
||||
if (hm_empty(iter->target))
|
||||
return NULL;
|
||||
return &(iter->target->buffer[iter->cursor].value);
|
||||
}
|
||||
size_t hm_iter_index(hashmap_iter *iter) { return iter->index; }
|
||||
static bool hm_impl_grow(hashmap *_map_)
|
||||
{
|
||||
size_t new_size = hm_impl_calculate_size(_map_->capacity + _map_->capacity / 2);
|
||||
hashmap *_new_map_ = hm_new(new_size, _map_->load, _map_->cmp, _map_->hash);
|
||||
if (!_new_map_)
|
||||
return false;
|
||||
hashmap_iter iter;
|
||||
for (hm_iter_init(&iter, _map_); !hm_iter_end(&iter); hm_iter_next(&iter))
|
||||
{
|
||||
size_t key = hm_iter_key(&iter);
|
||||
size_t value = hm_iter_value(&iter);
|
||||
hm_insert(_new_map_, key, value);
|
||||
}
|
||||
if (_map_->count != _new_map_->count)
|
||||
{
|
||||
hm_free(_new_map_);
|
||||
return false;
|
||||
}
|
||||
hashmap_entry *tmp = _map_->buffer;
|
||||
_map_->buffer = _new_map_->buffer;
|
||||
_new_map_->buffer = tmp;
|
||||
_map_->capacity = _new_map_->capacity;
|
||||
hm_free(_new_map_);
|
||||
return true;
|
||||
}
|
||||
static hashmap_entry *hm_impl_get_entry(hashmap *_map_, size_t key)
|
||||
{
|
||||
size_t hash = _map_->hash(key);
|
||||
size_t pos = hash % _map_->capacity;
|
||||
hashmap_entry *target = &(_map_->buffer[pos]);
|
||||
while (target->state == CMC_ES_FILLED || target->state == CMC_ES_DELETED)
|
||||
{
|
||||
if (_map_->cmp(target->key, key) == 0)
|
||||
return target;
|
||||
pos++;
|
||||
target = &(_map_->buffer[pos % _map_->capacity]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static size_t hm_impl_calculate_size(size_t required)
|
||||
{
|
||||
const size_t count = sizeof(cmc_hashtable_primes) / sizeof(cmc_hashtable_primes[0]);
|
||||
if (cmc_hashtable_primes[count - 1] < required)
|
||||
return required;
|
||||
size_t i = 0;
|
||||
while (cmc_hashtable_primes[i] < required)
|
||||
i++;
|
||||
return cmc_hashtable_primes[i];
|
||||
}
|
||||
static hashmap_iter hm_impl_it_start(hashmap *_map_)
|
||||
{
|
||||
hashmap_iter iter;
|
||||
hm_iter_init(&iter, _map_);
|
||||
return iter;
|
||||
}
|
||||
static hashmap_iter hm_impl_it_end(hashmap *_map_)
|
||||
{
|
||||
hashmap_iter iter;
|
||||
hm_iter_init(&iter, _map_);
|
||||
hm_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,512 @@
|
||||
#include "cmc/hashset.h"
|
||||
|
||||
//HASHSET_GENERATE(hs, hashset, , size_t)
|
||||
|
||||
typedef struct hashset_s
|
||||
{
|
||||
struct hashset_entry_s *buffer;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
double load;
|
||||
int (*cmp)(size_t, size_t);
|
||||
size_t (*hash)(size_t);
|
||||
struct hashset_iter_s (*it_start)(struct hashset_s *);
|
||||
struct hashset_iter_s (*it_end)(struct hashset_s *);
|
||||
} hashset, *hashset_ptr;
|
||||
typedef struct hashset_entry_s
|
||||
{
|
||||
size_t value;
|
||||
size_t dist;
|
||||
enum cmc_entry_state_e state;
|
||||
} hashset_entry, *hashset_entry_ptr;
|
||||
typedef struct hashset_iter_s
|
||||
{
|
||||
struct hashset_s *target;
|
||||
size_t cursor;
|
||||
size_t index;
|
||||
size_t first;
|
||||
size_t last;
|
||||
bool start;
|
||||
bool end;
|
||||
} hashset_iter, *hashset_iter_ptr;
|
||||
hashset *hs_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t));
|
||||
void hs_clear(hashset *_set_);
|
||||
void hs_free(hashset *_set_);
|
||||
bool hs_insert(hashset *_set_, size_t element);
|
||||
bool hs_remove(hashset *_set_, size_t element);
|
||||
bool hs_insert_if(hashset *_set_, size_t element, bool condition);
|
||||
bool hs_remove_if(hashset *_set_, size_t element, bool condition);
|
||||
bool hs_max(hashset *_set_, size_t *value);
|
||||
bool hs_min(hashset *_set_, size_t *value);
|
||||
bool hs_contains(hashset *_set_, size_t element);
|
||||
bool hs_empty(hashset *_set_);
|
||||
size_t hs_count(hashset *_set_);
|
||||
size_t hs_capacity(hashset *_set_);
|
||||
cmc_string hs_to_string(hashset *_set_);
|
||||
hashset *hs_union(hashset *_set1_, hashset *_set2_);
|
||||
hashset *hs_intersection(hashset *_set1_, hashset *_set2_);
|
||||
hashset *hs_difference(hashset *_set1_, hashset *_set2_);
|
||||
hashset *hs_symmetric_difference(hashset *_set1_, hashset *_set2_);
|
||||
bool hs_is_subset(hashset *_set1_, hashset *_set2_);
|
||||
bool hs_is_superset(hashset *_set1_, hashset *_set2_);
|
||||
bool hs_is_proper_subset(hashset *_set1_, hashset *_set2_);
|
||||
bool hs_is_proper_superset(hashset *_set1_, hashset *_set2_);
|
||||
bool hs_is_disjointset(hashset *_set1_, hashset *_set2_);
|
||||
hashset_iter *hs_iter_new(hashset *target);
|
||||
void hs_iter_free(hashset_iter *iter);
|
||||
void hs_iter_init(hashset_iter *iter, hashset *target);
|
||||
bool hs_iter_start(hashset_iter *iter);
|
||||
bool hs_iter_end(hashset_iter *iter);
|
||||
void hs_iter_to_start(hashset_iter *iter);
|
||||
void hs_iter_to_end(hashset_iter *iter);
|
||||
bool hs_iter_next(hashset_iter *iter);
|
||||
bool hs_iter_prev(hashset_iter *iter);
|
||||
size_t hs_iter_value(hashset_iter *iter);
|
||||
size_t hs_iter_index(hashset_iter *iter);
|
||||
static inline size_t hs_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
static bool hs_impl_grow(hashset *_set_);
|
||||
static hashset_entry *hs_impl_get_entry(hashset *_set_, size_t element);
|
||||
static size_t hs_impl_calculate_size(size_t required);
|
||||
static hashset_iter hs_impl_it_start(hashset *_set_);
|
||||
static hashset_iter hs_impl_it_end(hashset *_set_);
|
||||
hashset *hs_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t))
|
||||
{
|
||||
if (capacity == 0 || load <= 0 || load >= 1)
|
||||
return NULL;
|
||||
if (capacity >= UINTMAX_MAX * load)
|
||||
return NULL;
|
||||
size_t real_capacity = hs_impl_calculate_size(capacity / load);
|
||||
hashset *_set_ = malloc(sizeof(hashset));
|
||||
if (!_set_)
|
||||
return NULL;
|
||||
_set_->buffer = malloc(sizeof(hashset_entry) * real_capacity);
|
||||
if (!_set_->buffer)
|
||||
{
|
||||
free(_set_);
|
||||
return NULL;
|
||||
}
|
||||
memset(_set_->buffer, 0, sizeof(hashset_entry) * real_capacity);
|
||||
_set_->count = 0;
|
||||
_set_->capacity = real_capacity;
|
||||
_set_->load = load;
|
||||
_set_->cmp = compare;
|
||||
_set_->hash = hash;
|
||||
_set_->it_start = hs_impl_it_start;
|
||||
_set_->it_end = hs_impl_it_end;
|
||||
return _set_;
|
||||
}
|
||||
void hs_clear(hashset *_set_)
|
||||
{
|
||||
memset(_set_->buffer, 0, sizeof(hashset_entry) * _set_->capacity);
|
||||
_set_->count = 0;
|
||||
}
|
||||
void hs_free(hashset *_set_)
|
||||
{
|
||||
free(_set_->buffer);
|
||||
free(_set_);
|
||||
}
|
||||
bool hs_insert(hashset *_set_, size_t element)
|
||||
{
|
||||
if ((double)_set_->capacity * _set_->load <= (double)_set_->count)
|
||||
{
|
||||
if (!hs_impl_grow(_set_))
|
||||
return false;
|
||||
}
|
||||
size_t hash = _set_->hash(element);
|
||||
size_t original_pos = hash % _set_->capacity;
|
||||
size_t pos = original_pos;
|
||||
hashset_entry *target = &(_set_->buffer[pos]);
|
||||
if (hs_impl_get_entry(_set_, element) != NULL)
|
||||
return false;
|
||||
if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED)
|
||||
{
|
||||
target->value = element;
|
||||
target->dist = pos - original_pos;
|
||||
target->state = CMC_ES_FILLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
pos++;
|
||||
target = &(_set_->buffer[pos % _set_->capacity]);
|
||||
if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED)
|
||||
{
|
||||
target->value = element;
|
||||
target->dist = pos - original_pos;
|
||||
target->state = CMC_ES_FILLED;
|
||||
break;
|
||||
}
|
||||
else if (target->dist < pos - original_pos)
|
||||
{
|
||||
size_t tmp = target->value;
|
||||
size_t tmp_dist = target->dist;
|
||||
target->value = element;
|
||||
target->dist = pos - original_pos;
|
||||
element = tmp;
|
||||
original_pos = pos - tmp_dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
_set_->count++;
|
||||
return true;
|
||||
}
|
||||
bool hs_remove(hashset *_set_, size_t element)
|
||||
{
|
||||
hashset_entry *result = hs_impl_get_entry(_set_, element);
|
||||
if (result == NULL)
|
||||
return false;
|
||||
result->value = hs_impl_default_value();
|
||||
result->dist = 0;
|
||||
result->state = CMC_ES_DELETED;
|
||||
_set_->count--;
|
||||
return true;
|
||||
}
|
||||
bool hs_insert_if(hashset *_set_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return hs_insert(_set_, element);
|
||||
return false;
|
||||
}
|
||||
bool hs_remove_if(hashset *_set_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return hs_remove(_set_, element);
|
||||
return false;
|
||||
}
|
||||
bool hs_max(hashset *_set_, size_t *value)
|
||||
{
|
||||
if (hs_empty(_set_))
|
||||
return false;
|
||||
hashset_iter iter;
|
||||
for (hs_iter_init(&iter, _set_); !hs_iter_end(&iter); hs_iter_next(&iter))
|
||||
{
|
||||
size_t result = hs_iter_value(&iter);
|
||||
size_t index = hs_iter_index(&iter);
|
||||
if (index == 0)
|
||||
*value = result;
|
||||
else if (_set_->cmp(result, *value) > 0)
|
||||
*value = result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool hs_min(hashset *_set_, size_t *value)
|
||||
{
|
||||
if (hs_empty(_set_))
|
||||
return false;
|
||||
hashset_iter iter;
|
||||
for (hs_iter_init(&iter, _set_); !hs_iter_end(&iter); hs_iter_next(&iter))
|
||||
{
|
||||
size_t result = hs_iter_value(&iter);
|
||||
size_t index = hs_iter_index(&iter);
|
||||
if (index == 0)
|
||||
*value = result;
|
||||
else if (_set_->cmp(result, *value) < 0)
|
||||
*value = result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool hs_contains(hashset *_set_, size_t element) { return hs_impl_get_entry(_set_, element) != NULL; }
|
||||
bool hs_empty(hashset *_set_) { return _set_->count == 0; }
|
||||
size_t hs_count(hashset *_set_) { return _set_->count; }
|
||||
size_t hs_capacity(hashset *_set_) { return _set_->capacity; }
|
||||
cmc_string hs_to_string(hashset *_set_)
|
||||
{
|
||||
cmc_string str;
|
||||
hashset *s_ = _set_;
|
||||
const char *name = "hashset";
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_hashset, name, s_, s_->buffer, s_->capacity, s_->count, s_->load, s_->cmp, s_->hash);
|
||||
return str;
|
||||
}
|
||||
hashset *hs_union(hashset *_set1_, hashset *_set2_)
|
||||
{
|
||||
hashset *_set_r_ = hs_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
hashset_iter iter1, iter2;
|
||||
hs_iter_init(&iter1, _set1_);
|
||||
hs_iter_init(&iter2, _set2_);
|
||||
for (hs_iter_to_start(&iter1); !hs_iter_end(&iter1); hs_iter_next(&iter1))
|
||||
{
|
||||
hs_insert(_set_r_, hs_iter_value(&iter1));
|
||||
}
|
||||
for (hs_iter_to_start(&iter2); !hs_iter_end(&iter2); hs_iter_next(&iter2))
|
||||
{
|
||||
hs_insert(_set_r_, hs_iter_value(&iter2));
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
hashset *hs_intersection(hashset *_set1_, hashset *_set2_)
|
||||
{
|
||||
hashset *_set_r_ = hs_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
hashset *_set_A_ = _set1_->count < _set2_->count ? _set1_ : _set2_;
|
||||
hashset *_set_B_ = _set_A_ == _set1_ ? _set2_ : _set1_;
|
||||
hashset_iter iter;
|
||||
hs_iter_init(&iter, _set_A_);
|
||||
for (hs_iter_to_start(&iter); !hs_iter_end(&iter); hs_iter_next(&iter))
|
||||
{
|
||||
size_t value = hs_iter_value(&iter);
|
||||
if (hs_impl_get_entry(_set_B_, value) != NULL)
|
||||
hs_insert(_set_r_, value);
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
hashset *hs_difference(hashset *_set1_, hashset *_set2_)
|
||||
{
|
||||
hashset *_set_r_ = hs_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
hashset_iter iter;
|
||||
hs_iter_init(&iter, _set1_);
|
||||
for (hs_iter_to_start(&iter); !hs_iter_end(&iter); hs_iter_next(&iter))
|
||||
{
|
||||
size_t value = hs_iter_value(&iter);
|
||||
if (hs_impl_get_entry(_set2_, value) == NULL)
|
||||
hs_insert(_set_r_, value);
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
hashset *hs_symmetric_difference(hashset *_set1_, hashset *_set2_)
|
||||
{
|
||||
hashset_iter iter1, iter2;
|
||||
hashset *_set_r_ = hs_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
hs_iter_init(&iter1, _set1_);
|
||||
hs_iter_init(&iter2, _set2_);
|
||||
for (hs_iter_to_start(&iter1); !hs_iter_end(&iter1); hs_iter_next(&iter1))
|
||||
{
|
||||
size_t value = hs_iter_value(&iter1);
|
||||
if (hs_impl_get_entry(_set2_, value) == NULL)
|
||||
hs_insert(_set_r_, value);
|
||||
}
|
||||
for (hs_iter_to_start(&iter2); !hs_iter_end(&iter2); hs_iter_next(&iter2))
|
||||
{
|
||||
size_t value = hs_iter_value(&iter2);
|
||||
if (hs_impl_get_entry(_set1_, value) == NULL)
|
||||
hs_insert(_set_r_, value);
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
bool hs_is_subset(hashset *_set1_, hashset *_set2_)
|
||||
{
|
||||
if (hs_count(_set1_) > hs_count(_set2_))
|
||||
return false;
|
||||
if (hs_empty(_set1_))
|
||||
return true;
|
||||
hashset_iter iter;
|
||||
hs_iter_init(&iter, _set1_);
|
||||
for (hs_iter_to_start(&iter); !hs_iter_end(&iter); hs_iter_next(&iter))
|
||||
{
|
||||
size_t value = hs_iter_value(&iter);
|
||||
if (!hs_contains(_set2_, value))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool hs_is_superset(hashset *_set1_, hashset *_set2_) { return hs_is_subset(_set2_, _set1_); }
|
||||
bool hs_is_proper_subset(hashset *_set1_, hashset *_set2_)
|
||||
{
|
||||
if (hs_count(_set1_) >= hs_count(_set2_))
|
||||
return false;
|
||||
if (hs_empty(_set1_))
|
||||
{
|
||||
if (!hs_empty(_set2_))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
hashset_iter iter;
|
||||
hs_iter_init(&iter, _set1_);
|
||||
for (hs_iter_to_start(&iter); !hs_iter_end(&iter); hs_iter_next(&iter))
|
||||
{
|
||||
size_t value = hs_iter_value(&iter);
|
||||
if (!hs_contains(_set2_, value))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool hs_is_proper_superset(hashset *_set1_, hashset *_set2_) { return hs_is_proper_subset(_set2_, _set1_); }
|
||||
bool hs_is_disjointset(hashset *_set1_, hashset *_set2_)
|
||||
{
|
||||
if (hs_empty(_set1_))
|
||||
return true;
|
||||
hashset_iter iter;
|
||||
hs_iter_init(&iter, _set1_);
|
||||
for (hs_iter_to_start(&iter); !hs_iter_end(&iter); hs_iter_next(&iter))
|
||||
{
|
||||
size_t value = hs_iter_value(&iter);
|
||||
if (hs_contains(_set2_, value))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
hashset_iter *hs_iter_new(hashset *target)
|
||||
{
|
||||
hashset_iter *iter = malloc(sizeof(hashset_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
hs_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void hs_iter_free(hashset_iter *iter) { free(iter); }
|
||||
void hs_iter_init(hashset_iter *iter, hashset *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->cursor = 0;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = hs_empty(target);
|
||||
if (!hs_empty(target))
|
||||
{
|
||||
for (size_t i = 0; i < target->capacity; i++)
|
||||
{
|
||||
if (target->buffer[i].state == CMC_ES_FILLED)
|
||||
{
|
||||
iter->first = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
iter->cursor = iter->first;
|
||||
for (size_t i = target->capacity; i > 0; i--)
|
||||
{
|
||||
if (target->buffer[i - 1].state == CMC_ES_FILLED)
|
||||
{
|
||||
iter->last = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool hs_iter_start(hashset_iter *iter) { return hs_empty(iter->target) || iter->start; }
|
||||
bool hs_iter_end(hashset_iter *iter) { return hs_empty(iter->target) || iter->end; }
|
||||
void hs_iter_to_start(hashset_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->first;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = hs_empty(iter->target);
|
||||
}
|
||||
void hs_iter_to_end(hashset_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->last;
|
||||
iter->index = iter->target->count - 1;
|
||||
iter->start = hs_empty(iter->target);
|
||||
iter->end = true;
|
||||
}
|
||||
bool hs_iter_next(hashset_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
hashset_entry *scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (iter->cursor == iter->last)
|
||||
iter->end = true;
|
||||
else
|
||||
{
|
||||
iter->index++;
|
||||
while (1)
|
||||
{
|
||||
iter->cursor++;
|
||||
scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (scan->state == CMC_ES_FILLED)
|
||||
break;
|
||||
}
|
||||
}
|
||||
iter->start = hs_empty(iter->target);
|
||||
return true;
|
||||
}
|
||||
bool hs_iter_prev(hashset_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
hashset_entry *scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (iter->cursor == iter->first)
|
||||
iter->start = true;
|
||||
else
|
||||
{
|
||||
iter->index--;
|
||||
while (1)
|
||||
{
|
||||
iter->cursor--;
|
||||
scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (scan->state == CMC_ES_FILLED)
|
||||
break;
|
||||
}
|
||||
}
|
||||
iter->end = hs_empty(iter->target);
|
||||
return true;
|
||||
}
|
||||
size_t hs_iter_value(hashset_iter *iter)
|
||||
{
|
||||
if (hs_empty(iter->target))
|
||||
return hs_impl_default_value();
|
||||
return iter->target->buffer[iter->cursor].value;
|
||||
}
|
||||
size_t hs_iter_index(hashset_iter *iter) { return iter->index; }
|
||||
static bool hs_impl_grow(hashset *_set_)
|
||||
{
|
||||
size_t new_size = hs_impl_calculate_size(_set_->capacity + _set_->capacity / 2);
|
||||
hashset *_new_set_ = hs_new(new_size, _set_->load, _set_->cmp, _set_->hash);
|
||||
if (!_new_set_)
|
||||
return false;
|
||||
hashset_iter iter;
|
||||
for (hs_iter_init(&iter, _set_); !hs_iter_end(&iter); hs_iter_next(&iter))
|
||||
{
|
||||
hs_insert(_new_set_, hs_iter_value(&iter));
|
||||
}
|
||||
if (_set_->count != _new_set_->count)
|
||||
{
|
||||
hs_free(_new_set_);
|
||||
return false;
|
||||
}
|
||||
hashset_entry *tmp = _set_->buffer;
|
||||
_set_->buffer = _new_set_->buffer;
|
||||
_new_set_->buffer = tmp;
|
||||
_set_->capacity = _new_set_->capacity;
|
||||
hs_free(_new_set_);
|
||||
return true;
|
||||
}
|
||||
static hashset_entry *hs_impl_get_entry(hashset *_set_, size_t element)
|
||||
{
|
||||
size_t hash = _set_->hash(element);
|
||||
size_t pos = hash % _set_->capacity;
|
||||
hashset_entry *target = &(_set_->buffer[pos]);
|
||||
while (target->state == CMC_ES_FILLED || target->state == CMC_ES_DELETED)
|
||||
{
|
||||
if (_set_->cmp(target->value, element) == 0)
|
||||
return target;
|
||||
pos++;
|
||||
target = &(_set_->buffer[pos % _set_->capacity]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static size_t hs_impl_calculate_size(size_t required)
|
||||
{
|
||||
const size_t count = sizeof(cmc_hashtable_primes) / sizeof(cmc_hashtable_primes[0]);
|
||||
if (cmc_hashtable_primes[count - 1] < required)
|
||||
return required;
|
||||
size_t i = 0;
|
||||
while (cmc_hashtable_primes[i] < required)
|
||||
i++;
|
||||
return cmc_hashtable_primes[i];
|
||||
}
|
||||
static hashset_iter hs_impl_it_start(hashset *_set_)
|
||||
{
|
||||
hashset_iter iter;
|
||||
hs_iter_init(&iter, _set_);
|
||||
return iter;
|
||||
}
|
||||
static hashset_iter hs_impl_it_end(hashset *_set_)
|
||||
{
|
||||
hashset_iter iter;
|
||||
hs_iter_init(&iter, _set_);
|
||||
hs_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
#include "cmc/heap.h"
|
||||
|
||||
//HEAP_GENERATE(h, heap, , size_t)
|
||||
|
||||
typedef struct heap_s
|
||||
{
|
||||
size_t *buffer;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
enum cmc_heap_order_e HO;
|
||||
int (*cmp)(size_t, size_t);
|
||||
struct heap_iter_s (*it_start)(struct heap_s *);
|
||||
struct heap_iter_s (*it_end)(struct heap_s *);
|
||||
} heap, *heap_ptr;
|
||||
typedef struct heap_iter_s
|
||||
{
|
||||
struct heap_s *target;
|
||||
size_t cursor;
|
||||
bool start;
|
||||
bool end;
|
||||
} heap_iter, *heap_iter_ptr;
|
||||
heap *h_new(size_t capacity, cmc_heap_order HO, int (*compare)(size_t, size_t));
|
||||
void h_clear(heap *_heap_);
|
||||
void h_free(heap *_heap_);
|
||||
bool h_insert(heap *_heap_, size_t element);
|
||||
bool h_remove(heap *_heap_, size_t *result);
|
||||
bool h_insert_if(heap *_heap_, size_t element, bool condition);
|
||||
bool h_remove_if(heap *_heap_, size_t *result, bool condition);
|
||||
size_t h_peek(heap *_heap_);
|
||||
size_t *h_peek_ref(heap *_heap_);
|
||||
bool h_contains(heap *_heap_, size_t element);
|
||||
bool h_empty(heap *_heap_);
|
||||
bool h_full(heap *_heap_);
|
||||
size_t h_count(heap *_heap_);
|
||||
size_t h_capacity(heap *_heap_);
|
||||
heap_iter *h_iter_new(heap *target);
|
||||
void h_iter_free(heap_iter *iter);
|
||||
void h_iter_init(heap_iter *iter, heap *target);
|
||||
bool h_iter_start(heap_iter *iter);
|
||||
bool h_iter_end(heap_iter *iter);
|
||||
void h_iter_to_start(heap_iter *iter);
|
||||
void h_iter_to_end(heap_iter *iter);
|
||||
bool h_iter_next(heap_iter *iter);
|
||||
bool h_iter_prev(heap_iter *iter);
|
||||
size_t h_iter_value(heap_iter *iter);
|
||||
size_t h_iter_index(heap_iter *iter);
|
||||
static inline size_t h_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
static bool h_impl_grow(heap *_heap_);
|
||||
static bool h_impl_float_up(heap *_heap_, size_t index);
|
||||
static bool h_impl_float_down(heap *_heap_, size_t index);
|
||||
static heap_iter h_impl_it_start(heap *_heap_);
|
||||
static heap_iter h_impl_it_end(heap *_heap_);
|
||||
heap *h_new(size_t capacity, cmc_heap_order HO, int (*compare)(size_t, size_t))
|
||||
{
|
||||
if (capacity < 1)
|
||||
return NULL;
|
||||
if (HO != cmc_min_heap && HO != cmc_max_heap)
|
||||
return NULL;
|
||||
heap *_heap_ = malloc(sizeof(heap));
|
||||
if (!_heap_)
|
||||
return NULL;
|
||||
_heap_->buffer = malloc(sizeof(size_t) * capacity);
|
||||
if (!_heap_->buffer)
|
||||
{
|
||||
free(_heap_);
|
||||
return NULL;
|
||||
}
|
||||
memset(_heap_->buffer, 0, sizeof(size_t) * capacity);
|
||||
_heap_->capacity = capacity;
|
||||
_heap_->count = 0;
|
||||
_heap_->HO = HO;
|
||||
_heap_->cmp = compare;
|
||||
_heap_->it_start = h_impl_it_start;
|
||||
_heap_->it_end = h_impl_it_end;
|
||||
return _heap_;
|
||||
}
|
||||
void h_clear(heap *_heap_)
|
||||
{
|
||||
memset(_heap_->buffer, 0, sizeof(size_t) * _heap_->capacity);
|
||||
_heap_->count = 0;
|
||||
}
|
||||
void h_free(heap *_heap_)
|
||||
{
|
||||
free(_heap_->buffer);
|
||||
free(_heap_);
|
||||
}
|
||||
bool h_insert(heap *_heap_, size_t element)
|
||||
{
|
||||
if (h_full(_heap_))
|
||||
{
|
||||
if (!h_impl_grow(_heap_))
|
||||
return false;
|
||||
}
|
||||
if (_heap_->count == 0)
|
||||
{
|
||||
_heap_->buffer[_heap_->count++] = element;
|
||||
return true;
|
||||
}
|
||||
_heap_->buffer[_heap_->count++] = element;
|
||||
if (!h_impl_float_up(_heap_, _heap_->count - 1))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool h_remove(heap *_heap_, size_t *result)
|
||||
{
|
||||
if (h_empty(_heap_))
|
||||
return false;
|
||||
*result = _heap_->buffer[0];
|
||||
_heap_->buffer[0] = _heap_->buffer[_heap_->count - 1];
|
||||
_heap_->buffer[_heap_->count - 1] = h_impl_default_value();
|
||||
_heap_->count--;
|
||||
if (!h_impl_float_down(_heap_, 0))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool h_insert_if(heap *_heap_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return h_insert(_heap_, element);
|
||||
return false;
|
||||
}
|
||||
bool h_remove_if(heap *_heap_, size_t *result, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return h_remove(_heap_, result);
|
||||
return false;
|
||||
}
|
||||
size_t h_peek(heap *_heap_)
|
||||
{
|
||||
if (h_empty(_heap_))
|
||||
return h_impl_default_value();
|
||||
return _heap_->buffer[0];
|
||||
}
|
||||
size_t *h_peek_ref(heap *_heap_)
|
||||
{
|
||||
if (h_empty(_heap_))
|
||||
return NULL;
|
||||
return &(_heap_->buffer[0]);
|
||||
}
|
||||
bool h_contains(heap *_heap_, size_t element)
|
||||
{
|
||||
for (size_t i = 0; i < _heap_->count; i++)
|
||||
{
|
||||
if (_heap_->cmp(_heap_->buffer[i], element) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool h_empty(heap *_heap_) { return _heap_->count == 0; }
|
||||
bool h_full(heap *_heap_) { return _heap_->count >= _heap_->capacity; }
|
||||
size_t h_count(heap *_heap_) { return _heap_->count; }
|
||||
size_t h_capacity(heap *_heap_) { return _heap_->capacity; }
|
||||
heap_iter *h_iter_new(heap *target)
|
||||
{
|
||||
heap_iter *iter = malloc(sizeof(heap_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
h_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void h_iter_free(heap_iter *iter) { free(iter); }
|
||||
void h_iter_init(heap_iter *iter, heap *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->cursor = 0;
|
||||
iter->start = true;
|
||||
iter->end = h_empty(target);
|
||||
}
|
||||
bool h_iter_start(heap_iter *iter) { return h_empty(iter->target) || iter->start; }
|
||||
bool h_iter_end(heap_iter *iter) { return h_empty(iter->target) || iter->end; }
|
||||
void h_iter_to_start(heap_iter *iter)
|
||||
{
|
||||
iter->cursor = 0;
|
||||
iter->start = true;
|
||||
iter->end = h_empty(iter->target);
|
||||
}
|
||||
void h_iter_to_end(heap_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->target->count - 1;
|
||||
iter->start = h_empty(iter->target);
|
||||
iter->end = true;
|
||||
}
|
||||
bool h_iter_next(heap_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
iter->start = false;
|
||||
if (iter->cursor == iter->target->count - 1)
|
||||
iter->end = true;
|
||||
else
|
||||
iter->cursor++;
|
||||
return true;
|
||||
}
|
||||
bool h_iter_prev(heap_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
iter->end = false;
|
||||
if (iter->cursor == 0)
|
||||
iter->start = true;
|
||||
else
|
||||
iter->cursor--;
|
||||
return true;
|
||||
}
|
||||
size_t h_iter_value(heap_iter *iter)
|
||||
{
|
||||
if (h_empty(iter->target))
|
||||
return h_impl_default_value();
|
||||
return iter->target->buffer[iter->cursor];
|
||||
}
|
||||
size_t h_iter_index(heap_iter *iter) { return iter->cursor; }
|
||||
static bool h_impl_grow(heap *_heap_)
|
||||
{
|
||||
size_t new_capacity = _heap_->capacity * 2;
|
||||
size_t *new_buffer = realloc(_heap_->buffer, sizeof(size_t) * new_capacity);
|
||||
if (!new_buffer)
|
||||
return false;
|
||||
_heap_->buffer = new_buffer;
|
||||
_heap_->capacity = new_capacity;
|
||||
return true;
|
||||
}
|
||||
static bool h_impl_float_up(heap *_heap_, size_t index)
|
||||
{
|
||||
size_t C = index;
|
||||
size_t child = _heap_->buffer[C];
|
||||
size_t parent = _heap_->buffer[(index - 1) / 2];
|
||||
int mod = _heap_->HO;
|
||||
while (C > 0 && _heap_->cmp(child, parent) * mod > 0)
|
||||
{
|
||||
size_t tmp = _heap_->buffer[C];
|
||||
_heap_->buffer[C] = _heap_->buffer[(index - 1) / 2];
|
||||
_heap_->buffer[(index - 1) / 2] = tmp;
|
||||
C = (index - 1) / 2;
|
||||
child = _heap_->buffer[C];
|
||||
parent = _heap_->buffer[(index - 1) / 2];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool h_impl_float_down(heap *_heap_, size_t index)
|
||||
{
|
||||
int mod = _heap_->HO;
|
||||
while (index < _heap_->count)
|
||||
{
|
||||
size_t L = 2 * index + 1;
|
||||
size_t R = 2 * index + 2;
|
||||
size_t C = index;
|
||||
if (L < _heap_->count && _heap_->cmp(_heap_->buffer[L], _heap_->buffer[C]) * mod > 0)
|
||||
{
|
||||
C = L;
|
||||
}
|
||||
if (R < _heap_->count && _heap_->cmp(_heap_->buffer[R], _heap_->buffer[C]) * mod > 0)
|
||||
{
|
||||
C = R;
|
||||
}
|
||||
if (C != index)
|
||||
{
|
||||
size_t tmp = _heap_->buffer[index];
|
||||
_heap_->buffer[index] = _heap_->buffer[C];
|
||||
_heap_->buffer[C] = tmp;
|
||||
index = C;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static heap_iter h_impl_it_start(heap *_heap_)
|
||||
{
|
||||
heap_iter iter;
|
||||
h_iter_init(&iter, _heap_);
|
||||
h_iter_to_start(&iter);
|
||||
return iter;
|
||||
}
|
||||
static heap_iter h_impl_it_end(heap *_heap_)
|
||||
{
|
||||
heap_iter iter;
|
||||
h_iter_init(&iter, _heap_);
|
||||
h_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
#include "cmc/linkedlist.h"
|
||||
|
||||
//LINKEDLIST_GENERATE(ll, linkedlist, , size_t)
|
||||
|
||||
typedef struct linkedlist_s
|
||||
{
|
||||
struct linkedlist_node_s *head;
|
||||
struct linkedlist_node_s *tail;
|
||||
size_t count;
|
||||
struct linkedlist_iter_s (*it_start)(struct linkedlist_s *);
|
||||
struct linkedlist_iter_s (*it_end)(struct linkedlist_s *);
|
||||
} linkedlist, *linkedlist_ptr;
|
||||
typedef struct linkedlist_node_s
|
||||
{
|
||||
size_t data;
|
||||
struct linkedlist_node_s *next;
|
||||
struct linkedlist_node_s *prev;
|
||||
} linkedlist_node, *linkedlist_node_ptr;
|
||||
typedef struct linkedlist_iter_s
|
||||
{
|
||||
struct linkedlist_s *target;
|
||||
struct linkedlist_node_s *cursor;
|
||||
size_t index;
|
||||
bool start;
|
||||
bool end;
|
||||
} linkedlist_iter, *linkedlist_iter_ptr;
|
||||
linkedlist *ll_new(void);
|
||||
void ll_clear(linkedlist *_list_);
|
||||
void ll_free(linkedlist *_list_);
|
||||
bool ll_push_front(linkedlist *_list_, size_t element);
|
||||
bool ll_push(linkedlist *_list_, size_t element, size_t index);
|
||||
bool ll_push_back(linkedlist *_list_, size_t element);
|
||||
bool ll_pop_front(linkedlist *_list_);
|
||||
bool ll_pop(linkedlist *_list_, size_t index);
|
||||
bool ll_pop_back(linkedlist *_list_);
|
||||
bool ll_push_if(linkedlist *_list_, size_t element, size_t index, bool condition);
|
||||
bool ll_pop_if(linkedlist *_list_, size_t index, bool condition);
|
||||
size_t ll_front(linkedlist *_list_);
|
||||
size_t ll_get(linkedlist *_list_, size_t index);
|
||||
size_t *ll_get_ref(linkedlist *_list_, size_t index);
|
||||
size_t ll_back(linkedlist *_list_);
|
||||
bool ll_contains(linkedlist *_list_, size_t element, int (*comparator)(size_t, size_t));
|
||||
bool ll_empty(linkedlist *_list_);
|
||||
size_t ll_count(linkedlist *_list_);
|
||||
linkedlist_node *ll_new_node(size_t element);
|
||||
void ll_free_node(linkedlist_node *_node_);
|
||||
linkedlist_node *ll_head(linkedlist *_list_);
|
||||
linkedlist_node *ll_get_node(linkedlist *_list_, size_t index);
|
||||
linkedlist_node *ll_tail(linkedlist *_list_);
|
||||
bool ll_insert_after(linkedlist *_owner_, linkedlist_node *_node_, size_t element);
|
||||
bool ll_insert_before(linkedlist *_owner_, linkedlist_node *_node_, size_t element);
|
||||
bool ll_remove_after(linkedlist *_owner_, linkedlist_node *_node_);
|
||||
bool ll_remove_current(linkedlist *_owner_, linkedlist_node *_node_);
|
||||
bool ll_remove_before(linkedlist *_owner_, linkedlist_node *_node_);
|
||||
linkedlist_node *ll_next_node(linkedlist_node *_node_);
|
||||
linkedlist_node *ll_prev_node(linkedlist_node *_node_);
|
||||
linkedlist_iter *ll_iter_new(linkedlist *target);
|
||||
void ll_iter_free(linkedlist_iter *iter);
|
||||
void ll_iter_init(linkedlist_iter *iter, linkedlist *target);
|
||||
bool ll_iter_start(linkedlist_iter *iter);
|
||||
bool ll_iter_end(linkedlist_iter *iter);
|
||||
void ll_iter_to_start(linkedlist_iter *iter);
|
||||
void ll_iter_to_end(linkedlist_iter *iter);
|
||||
bool ll_iter_next(linkedlist_iter *iter);
|
||||
bool ll_iter_prev(linkedlist_iter *iter);
|
||||
size_t ll_iter_value(linkedlist_iter *iter);
|
||||
size_t *ll_iter_rvalue(linkedlist_iter *iter);
|
||||
size_t ll_iter_index(linkedlist_iter *iter);
|
||||
linkedlist_node *ll_iter_node(linkedlist_iter *iter);
|
||||
static inline size_t ll_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
static linkedlist_iter ll_impl_it_start(linkedlist *_list_);
|
||||
static linkedlist_iter ll_impl_it_end(linkedlist *_list_);
|
||||
linkedlist *ll_new(void)
|
||||
{
|
||||
linkedlist *_list_ = malloc(sizeof(linkedlist));
|
||||
if (!_list_)
|
||||
return NULL;
|
||||
_list_->count = 0;
|
||||
_list_->head = NULL;
|
||||
_list_->tail = NULL;
|
||||
_list_->it_start = ll_impl_it_start;
|
||||
_list_->it_end = ll_impl_it_end;
|
||||
return _list_;
|
||||
}
|
||||
void ll_clear(linkedlist *_list_)
|
||||
{
|
||||
linkedlist_node *scan = _list_->head;
|
||||
while (_list_->head != NULL)
|
||||
{
|
||||
_list_->head = _list_->head->next;
|
||||
free(scan);
|
||||
scan = _list_->head;
|
||||
}
|
||||
_list_->count = 0;
|
||||
_list_->head = NULL;
|
||||
_list_->tail = NULL;
|
||||
}
|
||||
void ll_free(linkedlist *_list_)
|
||||
{
|
||||
ll_clear(_list_);
|
||||
free(_list_);
|
||||
}
|
||||
bool ll_push_front(linkedlist *_list_, size_t element)
|
||||
{
|
||||
linkedlist_node *_node_ = ll_new_node(element);
|
||||
if (!_node_)
|
||||
return false;
|
||||
if (ll_empty(_list_))
|
||||
{
|
||||
_list_->head = _node_;
|
||||
_list_->tail = _node_;
|
||||
}
|
||||
else
|
||||
{
|
||||
_node_->next = _list_->head;
|
||||
_list_->head->prev = _node_;
|
||||
_list_->head = _node_;
|
||||
}
|
||||
_list_->count++;
|
||||
return true;
|
||||
}
|
||||
bool ll_push(linkedlist *_list_, size_t element, size_t index)
|
||||
{
|
||||
if (index > _list_->count)
|
||||
return false;
|
||||
if (index == 0)
|
||||
{
|
||||
return ll_push_front(_list_, element);
|
||||
}
|
||||
else if (index == _list_->count)
|
||||
{
|
||||
return ll_push_back(_list_, element);
|
||||
}
|
||||
linkedlist_node *_node_ = ll_new_node(element);
|
||||
if (!_node_)
|
||||
return false;
|
||||
linkedlist_node *scan = ll_get_node(_list_, index - 1);
|
||||
_node_->next = scan->next;
|
||||
_node_->prev = scan;
|
||||
_node_->next->prev = _node_;
|
||||
_node_->prev->next = _node_;
|
||||
_list_->count++;
|
||||
return true;
|
||||
}
|
||||
bool ll_push_back(linkedlist *_list_, size_t element)
|
||||
{
|
||||
linkedlist_node *_node_ = ll_new_node(element);
|
||||
if (!_node_)
|
||||
return false;
|
||||
if (ll_empty(_list_))
|
||||
{
|
||||
_list_->head = _node_;
|
||||
_list_->tail = _node_;
|
||||
}
|
||||
else
|
||||
{
|
||||
_node_->prev = _list_->tail;
|
||||
_list_->tail->next = _node_;
|
||||
_list_->tail = _node_;
|
||||
}
|
||||
_list_->count++;
|
||||
return true;
|
||||
}
|
||||
bool ll_pop_front(linkedlist *_list_)
|
||||
{
|
||||
if (ll_empty(_list_))
|
||||
return false;
|
||||
linkedlist_node *_node_ = _list_->head;
|
||||
_list_->head = _list_->head->next;
|
||||
free(_node_);
|
||||
if (_list_->head == NULL)
|
||||
_list_->tail = NULL;
|
||||
else
|
||||
_list_->head->prev = NULL;
|
||||
_list_->count--;
|
||||
return true;
|
||||
}
|
||||
bool ll_pop(linkedlist *_list_, size_t index)
|
||||
{
|
||||
if (ll_empty(_list_))
|
||||
return false;
|
||||
if (index >= _list_->count)
|
||||
return false;
|
||||
if (index == 0)
|
||||
{
|
||||
return ll_pop_front(_list_);
|
||||
}
|
||||
else if (index == _list_->count - 1)
|
||||
{
|
||||
return ll_pop_back(_list_);
|
||||
}
|
||||
linkedlist_node *_node_ = ll_get_node(_list_, index);
|
||||
if (!_node_)
|
||||
return false;
|
||||
_node_->next->prev = _node_->prev;
|
||||
_node_->prev->next = _node_->next;
|
||||
free(_node_);
|
||||
_list_->count--;
|
||||
return true;
|
||||
}
|
||||
bool ll_pop_back(linkedlist *_list_)
|
||||
{
|
||||
if (ll_empty(_list_))
|
||||
return false;
|
||||
linkedlist_node *_node_ = _list_->tail;
|
||||
_list_->tail = _list_->tail->prev;
|
||||
free(_node_);
|
||||
if (_list_->tail == NULL)
|
||||
_list_->head = NULL;
|
||||
else
|
||||
_list_->tail->next = NULL;
|
||||
_list_->count--;
|
||||
return true;
|
||||
}
|
||||
bool ll_push_if(linkedlist *_list_, size_t element, size_t index, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return ll_push(_list_, element, index);
|
||||
return false;
|
||||
}
|
||||
bool ll_pop_if(linkedlist *_list_, size_t index, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return ll_pop(_list_, index);
|
||||
return false;
|
||||
}
|
||||
size_t ll_front(linkedlist *_list_)
|
||||
{
|
||||
if (ll_empty(_list_))
|
||||
return ll_impl_default_value();
|
||||
return _list_->head->data;
|
||||
}
|
||||
size_t ll_get(linkedlist *_list_, size_t index)
|
||||
{
|
||||
if (index >= _list_->count || ll_empty(_list_))
|
||||
return ll_impl_default_value();
|
||||
linkedlist_node *scan = ll_get_node(_list_, index);
|
||||
if (scan == NULL)
|
||||
return ll_impl_default_value();
|
||||
return scan->data;
|
||||
}
|
||||
size_t *ll_get_ref(linkedlist *_list_, size_t index)
|
||||
{
|
||||
if (index >= _list_->count)
|
||||
return NULL;
|
||||
if (ll_empty(_list_))
|
||||
return NULL;
|
||||
linkedlist_node *scan = ll_get_node(_list_, index);
|
||||
if (scan == NULL)
|
||||
return NULL;
|
||||
return &(scan->data);
|
||||
}
|
||||
size_t ll_back(linkedlist *_list_)
|
||||
{
|
||||
if (ll_empty(_list_))
|
||||
return ll_impl_default_value();
|
||||
return _list_->tail->data;
|
||||
}
|
||||
bool ll_contains(linkedlist *_list_, size_t element, int (*comparator)(size_t, size_t))
|
||||
{
|
||||
linkedlist_node *scan = _list_->head;
|
||||
while (scan != NULL)
|
||||
{
|
||||
if (comparator(scan->data, element) == 0)
|
||||
return true;
|
||||
scan = scan->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool ll_empty(linkedlist *_list_) { return _list_->count == 0; }
|
||||
size_t ll_count(linkedlist *_list_) { return _list_->count; }
|
||||
linkedlist_node *ll_new_node(size_t element)
|
||||
{
|
||||
linkedlist_node *_node_ = malloc(sizeof(linkedlist_node));
|
||||
if (!_node_)
|
||||
return NULL;
|
||||
_node_->data = element;
|
||||
_node_->next = NULL;
|
||||
_node_->prev = NULL;
|
||||
return _node_;
|
||||
}
|
||||
void ll_free_node(linkedlist_node *_node_) { free(_node_); }
|
||||
linkedlist_node *ll_head(linkedlist *_list_) { return _list_->head; }
|
||||
linkedlist_node *ll_get_node(linkedlist *_list_, size_t index)
|
||||
{
|
||||
if (index >= _list_->count)
|
||||
return NULL;
|
||||
if (ll_empty(_list_))
|
||||
return NULL;
|
||||
linkedlist_node *_node_ = NULL;
|
||||
if (index <= _list_->count / 2)
|
||||
{
|
||||
_node_ = _list_->head;
|
||||
for (size_t i = 0; i < index; i++)
|
||||
{
|
||||
_node_ = _node_->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_node_ = _list_->tail;
|
||||
for (size_t i = _list_->count - 1; i > index; i--)
|
||||
{
|
||||
_node_ = _node_->prev;
|
||||
}
|
||||
}
|
||||
return _node_;
|
||||
}
|
||||
linkedlist_node *ll_tail(linkedlist *_list_) { return _list_->tail; }
|
||||
bool ll_insert_after(linkedlist *_owner_, linkedlist_node *_node_, size_t element)
|
||||
{
|
||||
linkedlist_node *new_node = ll_new_node(element);
|
||||
if (!new_node)
|
||||
return false;
|
||||
new_node->next = _node_->next;
|
||||
if (_node_->next != NULL)
|
||||
_node_->next->prev = new_node;
|
||||
else
|
||||
_owner_->tail = new_node;
|
||||
new_node->prev = _node_;
|
||||
_node_->next = new_node;
|
||||
_owner_->count++;
|
||||
return true;
|
||||
}
|
||||
bool ll_insert_before(linkedlist *_owner_, linkedlist_node *_node_, size_t element)
|
||||
{
|
||||
linkedlist_node *new_node = ll_new_node(element);
|
||||
if (!new_node)
|
||||
return false;
|
||||
new_node->prev = _node_->prev;
|
||||
if (_node_->prev != NULL)
|
||||
_node_->prev->next = new_node;
|
||||
else
|
||||
_owner_->head = new_node;
|
||||
new_node->next = _node_;
|
||||
_node_->prev = new_node;
|
||||
_owner_->count++;
|
||||
return true;
|
||||
}
|
||||
bool ll_remove_after(linkedlist *_owner_, linkedlist_node *_node_)
|
||||
{
|
||||
if (_node_->next == NULL)
|
||||
return false;
|
||||
linkedlist_node *tmp = _node_->next;
|
||||
if (tmp->next != NULL)
|
||||
{
|
||||
_node_->next = _node_->next->next;
|
||||
_node_->next->prev = _node_;
|
||||
}
|
||||
else
|
||||
_owner_->tail = _node_;
|
||||
free(tmp);
|
||||
_owner_->count--;
|
||||
return true;
|
||||
}
|
||||
bool ll_remove_current(linkedlist *_owner_, linkedlist_node *_node_)
|
||||
{
|
||||
if (_node_->prev != NULL)
|
||||
_node_->prev->next = _node_->next;
|
||||
else
|
||||
_owner_->head = _node_->next;
|
||||
if (_node_->next != NULL)
|
||||
_node_->next->prev = _node_->prev;
|
||||
else
|
||||
_owner_->tail = _node_->prev;
|
||||
free(_node_);
|
||||
_owner_->count--;
|
||||
return true;
|
||||
}
|
||||
bool ll_remove_before(linkedlist *_owner_, linkedlist_node *_node_)
|
||||
{
|
||||
if (_node_->prev == NULL)
|
||||
return false;
|
||||
linkedlist_node *tmp = _node_->prev;
|
||||
if (tmp->prev != NULL)
|
||||
{
|
||||
_node_->prev = _node_->prev->prev;
|
||||
_node_->prev->next = _node_;
|
||||
}
|
||||
else
|
||||
_owner_->head = _node_;
|
||||
free(tmp);
|
||||
_owner_->count--;
|
||||
return true;
|
||||
}
|
||||
linkedlist_node *ll_next_node(linkedlist_node *_node_) { return _node_->next; }
|
||||
linkedlist_node *ll_prev_node(linkedlist_node *_node_) { return _node_->prev; }
|
||||
linkedlist_iter *ll_iter_new(linkedlist *target)
|
||||
{
|
||||
linkedlist_iter *iter = malloc(sizeof(linkedlist_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
ll_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void ll_iter_free(linkedlist_iter *iter) { free(iter); }
|
||||
void ll_iter_init(linkedlist_iter *iter, linkedlist *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->cursor = target->head;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = ll_empty(target);
|
||||
}
|
||||
bool ll_iter_start(linkedlist_iter *iter) { return ll_empty(iter->target) || iter->start; }
|
||||
bool ll_iter_end(linkedlist_iter *iter) { return ll_empty(iter->target) || iter->end; }
|
||||
void ll_iter_to_start(linkedlist_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->target->head;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = ll_empty(iter->target);
|
||||
}
|
||||
void ll_iter_to_end(linkedlist_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->target->tail;
|
||||
iter->index = iter->target->count - 1;
|
||||
iter->start = ll_empty(iter->target);
|
||||
iter->end = true;
|
||||
}
|
||||
bool ll_iter_next(linkedlist_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
iter->start = ll_empty(iter->target);
|
||||
if (iter->cursor->next == NULL)
|
||||
iter->end = true;
|
||||
else
|
||||
{
|
||||
iter->cursor = iter->cursor->next;
|
||||
iter->index++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ll_iter_prev(linkedlist_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
iter->end = ll_empty(iter->target);
|
||||
if (iter->cursor->prev == NULL)
|
||||
iter->start = true;
|
||||
else
|
||||
{
|
||||
iter->cursor = iter->cursor->prev;
|
||||
iter->index--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
size_t ll_iter_value(linkedlist_iter *iter)
|
||||
{
|
||||
if (ll_empty(iter->target))
|
||||
return ll_impl_default_value();
|
||||
return iter->cursor->data;
|
||||
}
|
||||
size_t *ll_iter_rvalue(linkedlist_iter *iter)
|
||||
{
|
||||
if (ll_empty(iter->target))
|
||||
return NULL;
|
||||
return &(iter->cursor->data);
|
||||
}
|
||||
size_t ll_iter_index(linkedlist_iter *iter) { return iter->index; }
|
||||
linkedlist_node *ll_iter_node(linkedlist_iter *iter) { return iter->cursor; }
|
||||
static linkedlist_iter ll_impl_it_start(linkedlist *_list_)
|
||||
{
|
||||
linkedlist_iter iter;
|
||||
ll_iter_init(&iter, _list_);
|
||||
ll_iter_to_start(&iter);
|
||||
return iter;
|
||||
}
|
||||
static linkedlist_iter ll_impl_it_end(linkedlist *_list_)
|
||||
{
|
||||
linkedlist_iter iter;
|
||||
ll_iter_init(&iter, _list_);
|
||||
ll_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
#include "ext/linkedqueue.h"
|
||||
|
||||
//LINKEDQUEUE_GENERATE(lq, linkedqueue, , size_t)
|
||||
|
||||
typedef struct linkedqueue_s
|
||||
{
|
||||
struct linkedqueue_node_s *head;
|
||||
struct linkedqueue_node_s *tail;
|
||||
size_t count;
|
||||
struct linkedqueue_iter_s (*it_start)(struct linkedqueue_s *);
|
||||
} linkedqueue, *linkedqueue_ptr;
|
||||
typedef struct linkedqueue_node_s
|
||||
{
|
||||
size_t data;
|
||||
struct linkedqueue_node_s *next;
|
||||
} linkedqueue_node, *linkedqueue_node_ptr;
|
||||
typedef struct linkedqueue_iter_s
|
||||
{
|
||||
struct linkedqueue_s *target;
|
||||
struct linkedqueue_node_s *cursor;
|
||||
size_t index;
|
||||
bool start;
|
||||
bool end;
|
||||
} linkedqueue_iter, *linkedqueue_iter_ptr;
|
||||
linkedqueue *lq_new(void);
|
||||
void lq_clear(linkedqueue *_lqueue_);
|
||||
void lq_free(linkedqueue *_lqueue_);
|
||||
bool lq_enqueue(linkedqueue *_lqueue_, size_t element);
|
||||
bool lq_dequeue(linkedqueue *_lqueue_);
|
||||
bool lq_enqueue_if(linkedqueue *_lqueue_, size_t element, bool condition);
|
||||
bool lq_dequeue_if(linkedqueue *_lqueue_, bool condition);
|
||||
size_t lq_peek(linkedqueue *_lqueue_);
|
||||
bool lq_contains(linkedqueue *_lqueue_, size_t element, int (*comparator)(size_t, size_t));
|
||||
bool lq_empty(linkedqueue *_lqueue_);
|
||||
size_t lq_count(linkedqueue *_lqueue_);
|
||||
linkedqueue_iter *lq_iter_new(linkedqueue *target);
|
||||
void lq_iter_free(linkedqueue_iter *iter);
|
||||
void lq_iter_init(linkedqueue_iter *iter, linkedqueue *target);
|
||||
bool lq_iter_start(linkedqueue_iter *iter);
|
||||
bool lq_iter_end(linkedqueue_iter *iter);
|
||||
void lq_iter_to_start(linkedqueue_iter *iter);
|
||||
void lq_iter_to_end(linkedqueue_iter *iter);
|
||||
bool lq_iter_next(linkedqueue_iter *iter);
|
||||
size_t lq_iter_value(linkedqueue_iter *iter);
|
||||
size_t *lq_iter_rvalue(linkedqueue_iter *iter);
|
||||
size_t lq_iter_index(linkedqueue_iter *iter);
|
||||
static inline size_t lq_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
linkedqueue_node *lq_impl_new_node(size_t element);
|
||||
static linkedqueue_iter lq_impl_it_start(linkedqueue *_lqueue_);
|
||||
linkedqueue *lq_new(void)
|
||||
{
|
||||
linkedqueue *_lqueue_ = malloc(sizeof(linkedqueue));
|
||||
if (!_lqueue_)
|
||||
return NULL;
|
||||
_lqueue_->count = 0;
|
||||
_lqueue_->head = NULL;
|
||||
_lqueue_->tail = NULL;
|
||||
_lqueue_->it_start = lq_impl_it_start;
|
||||
return _lqueue_;
|
||||
}
|
||||
void lq_clear(linkedqueue *_lqueue_)
|
||||
{
|
||||
linkedqueue_node *scan = _lqueue_->head;
|
||||
while (_lqueue_->head != NULL)
|
||||
{
|
||||
_lqueue_->head = _lqueue_->head->next;
|
||||
free(scan);
|
||||
scan = _lqueue_->head;
|
||||
}
|
||||
_lqueue_->count = 0;
|
||||
_lqueue_->head = NULL;
|
||||
_lqueue_->tail = NULL;
|
||||
}
|
||||
void lq_free(linkedqueue *_lqueue_)
|
||||
{
|
||||
lq_clear(_lqueue_);
|
||||
free(_lqueue_);
|
||||
}
|
||||
bool lq_enqueue(linkedqueue *_lqueue_, size_t element)
|
||||
{
|
||||
linkedqueue_node *_node_ = lq_impl_new_node(element);
|
||||
if (!_node_)
|
||||
return false;
|
||||
if (lq_empty(_lqueue_))
|
||||
{
|
||||
_lqueue_->head = _node_;
|
||||
_lqueue_->tail = _node_;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lqueue_->tail->next = _node_;
|
||||
_lqueue_->tail = _node_;
|
||||
}
|
||||
_lqueue_->count++;
|
||||
return true;
|
||||
}
|
||||
bool lq_dequeue(linkedqueue *_lqueue_)
|
||||
{
|
||||
if (lq_empty(_lqueue_))
|
||||
return false;
|
||||
linkedqueue_node *_node_ = _lqueue_->head;
|
||||
_lqueue_->head = _lqueue_->head->next;
|
||||
free(_node_);
|
||||
if (_lqueue_->head == NULL)
|
||||
_lqueue_->tail = NULL;
|
||||
_lqueue_->count--;
|
||||
return true;
|
||||
}
|
||||
bool lq_enqueue_if(linkedqueue *_lqueue_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return lq_enqueue(_lqueue_, element);
|
||||
return false;
|
||||
}
|
||||
bool lq_dequeue_if(linkedqueue *_lqueue_, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return lq_dequeue(_lqueue_);
|
||||
return false;
|
||||
}
|
||||
size_t lq_peek(linkedqueue *_lqueue_)
|
||||
{
|
||||
if (lq_empty(_lqueue_))
|
||||
return lq_impl_default_value();
|
||||
return _lqueue_->head->data;
|
||||
}
|
||||
bool lq_contains(linkedqueue *_lqueue_, size_t element, int (*comparator)(size_t, size_t))
|
||||
{
|
||||
linkedqueue_node *scan = _lqueue_->head;
|
||||
while (scan != NULL)
|
||||
{
|
||||
if (comparator(scan->data, element) == 0)
|
||||
return true;
|
||||
scan = scan->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool lq_empty(linkedqueue *_lqueue_) { return _lqueue_->count == 0; }
|
||||
size_t lq_count(linkedqueue *_lqueue_) { return _lqueue_->count; }
|
||||
linkedqueue_iter *lq_iter_new(linkedqueue *target)
|
||||
{
|
||||
linkedqueue_iter *iter = malloc(sizeof(linkedqueue_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
lq_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void lq_iter_free(linkedqueue_iter *iter) { free(iter); }
|
||||
void lq_iter_init(linkedqueue_iter *iter, linkedqueue *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->cursor = target->head;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = lq_empty(target);
|
||||
}
|
||||
bool lq_iter_start(linkedqueue_iter *iter) { return lq_empty(iter->target) || iter->start; }
|
||||
bool lq_iter_end(linkedqueue_iter *iter) { return lq_empty(iter->target) || iter->end; }
|
||||
void lq_iter_to_start(linkedqueue_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->target->head;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = lq_empty(iter->target);
|
||||
}
|
||||
void lq_iter_to_end(linkedqueue_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->target->tail;
|
||||
iter->index = iter->target->count - 1;
|
||||
iter->start = lq_empty(iter->target);
|
||||
iter->end = true;
|
||||
}
|
||||
bool lq_iter_next(linkedqueue_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
iter->start = lq_empty(iter->target);
|
||||
if (iter->cursor->next == NULL)
|
||||
iter->end = true;
|
||||
else
|
||||
{
|
||||
iter->cursor = iter->cursor->next;
|
||||
iter->index++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
size_t lq_iter_value(linkedqueue_iter *iter)
|
||||
{
|
||||
if (lq_empty(iter->target))
|
||||
return lq_impl_default_value();
|
||||
return iter->cursor->data;
|
||||
}
|
||||
size_t *lq_iter_rvalue(linkedqueue_iter *iter)
|
||||
{
|
||||
if (lq_empty(iter->target))
|
||||
return NULL;
|
||||
return &(iter->cursor->data);
|
||||
}
|
||||
size_t lq_iter_index(linkedqueue_iter *iter) { return iter->index; }
|
||||
linkedqueue_node *lq_impl_new_node(size_t element)
|
||||
{
|
||||
linkedqueue_node *_node_ = malloc(sizeof(linkedqueue_node));
|
||||
if (!_node_)
|
||||
return NULL;
|
||||
_node_->data = element;
|
||||
_node_->next = NULL;
|
||||
return _node_;
|
||||
}
|
||||
static linkedqueue_iter lq_impl_it_start(linkedqueue *_lqueue_)
|
||||
{
|
||||
linkedqueue_iter iter;
|
||||
lq_iter_init(&iter, _lqueue_);
|
||||
lq_iter_to_start(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
#include "cmc/list.h"
|
||||
|
||||
//LIST_GENERATE(l, list, , size_t)
|
||||
|
||||
typedef struct list_s
|
||||
{
|
||||
size_t *buffer;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
struct list_iter_s (*it_start)(struct list_s *);
|
||||
struct list_iter_s (*it_end)(struct list_s *);
|
||||
} list, *list_ptr;
|
||||
typedef struct list_iter_s
|
||||
{
|
||||
struct list_s *target;
|
||||
size_t cursor;
|
||||
bool start;
|
||||
bool end;
|
||||
} list_iter, *list_iter_ptr;
|
||||
list *l_new(size_t capacity);
|
||||
list *l_new_from(size_t *elements, size_t size);
|
||||
void l_clear(list *_list_);
|
||||
void l_free(list *_list_);
|
||||
bool l_push_front(list *_list_, size_t element);
|
||||
bool l_push(list *_list_, size_t element, size_t index);
|
||||
bool l_push_back(list *_list_, size_t element);
|
||||
bool l_pop_front(list *_list_);
|
||||
bool l_pop(list *_list_, size_t index);
|
||||
bool l_pop_back(list *_list_);
|
||||
bool l_push_if(list *_list_, size_t element, size_t index, bool condition);
|
||||
bool l_pop_if(list *_list_, size_t index, bool condition);
|
||||
bool l_prepend(list *_list_, size_t *elements, size_t size);
|
||||
bool l_insert(list *_list_, size_t *elements, size_t size, size_t index);
|
||||
bool l_append(list *_list_, size_t *elements, size_t size);
|
||||
bool l_remove(list *_list_, size_t from, size_t to);
|
||||
list *l_extract(list *_list_, size_t from, size_t to);
|
||||
size_t l_front(list *_list_);
|
||||
size_t l_get(list *_list_, size_t index);
|
||||
size_t *l_get_ref(list *_list_, size_t index);
|
||||
size_t l_back(list *_list_);
|
||||
size_t l_indexof(list *_list_, size_t element, int (*comparator)(size_t, size_t), bool from_start);
|
||||
bool l_contains(list *_list_, size_t element, int (*comparator)(size_t, size_t));
|
||||
bool l_empty(list *_list_);
|
||||
bool l_full(list *_list_);
|
||||
size_t l_count(list *_list_);
|
||||
bool l_fits(list *_list_, size_t size);
|
||||
size_t l_capacity(list *_list_);
|
||||
list_iter *l_iter_new(list *target);
|
||||
void l_iter_free(list_iter *iter);
|
||||
void l_iter_init(list_iter *iter, list *target);
|
||||
bool l_iter_start(list_iter *iter);
|
||||
bool l_iter_end(list_iter *iter);
|
||||
void l_iter_to_start(list_iter *iter);
|
||||
void l_iter_to_end(list_iter *iter);
|
||||
bool l_iter_next(list_iter *iter);
|
||||
bool l_iter_prev(list_iter *iter);
|
||||
size_t l_iter_value(list_iter *iter);
|
||||
size_t *l_iter_rvalue(list_iter *iter);
|
||||
size_t l_iter_index(list_iter *iter);
|
||||
static inline size_t l_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
|
||||
static bool l_impl_grow(list *_list_, size_t required);
|
||||
static list_iter l_impl_it_start(list *_list_);
|
||||
static list_iter l_impl_it_end(list *_list_);
|
||||
list *l_new(size_t capacity)
|
||||
{
|
||||
if (capacity < 1)
|
||||
return NULL;
|
||||
list *_list_ = malloc(sizeof(list));
|
||||
if (!_list_)
|
||||
return NULL;
|
||||
_list_->buffer = malloc(capacity * sizeof(size_t));
|
||||
if (!_list_->buffer)
|
||||
{
|
||||
free(_list_);
|
||||
return NULL;
|
||||
}
|
||||
memset(_list_->buffer, 0, capacity * sizeof(size_t));
|
||||
_list_->capacity = capacity;
|
||||
_list_->count = 0;
|
||||
_list_->it_start = l_impl_it_start;
|
||||
_list_->it_end = l_impl_it_end;
|
||||
return _list_;
|
||||
}
|
||||
list *l_new_from(size_t *elements, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
list *_list_ = l_new(size + size / 2);
|
||||
if (!_list_)
|
||||
return NULL;
|
||||
memcpy(_list_->buffer, elements, size * sizeof(size_t));
|
||||
_list_->count = size;
|
||||
return _list_;
|
||||
}
|
||||
void l_clear(list *_list_)
|
||||
{
|
||||
memset(_list_->buffer, 0, sizeof(size_t) * _list_->capacity);
|
||||
_list_->count = 0;
|
||||
}
|
||||
void l_free(list *_list_)
|
||||
{
|
||||
free(_list_->buffer);
|
||||
free(_list_);
|
||||
}
|
||||
bool l_push_front(list *_list_, size_t element)
|
||||
{
|
||||
if (l_full(_list_))
|
||||
{
|
||||
if (!l_impl_grow(_list_, _list_->count + 1))
|
||||
return false;
|
||||
}
|
||||
if (!l_empty(_list_))
|
||||
{
|
||||
memmove(_list_->buffer + 1, _list_->buffer, _list_->count * sizeof(size_t));
|
||||
}
|
||||
_list_->buffer[0] = element;
|
||||
_list_->count++;
|
||||
return true;
|
||||
}
|
||||
bool l_push(list *_list_, size_t element, size_t index)
|
||||
{
|
||||
if (index > _list_->count)
|
||||
return false;
|
||||
if (index == 0)
|
||||
{
|
||||
return l_push_front(_list_, element);
|
||||
}
|
||||
else if (index == _list_->count)
|
||||
{
|
||||
return l_push_back(_list_, element);
|
||||
}
|
||||
if (l_full(_list_))
|
||||
{
|
||||
if (!l_impl_grow(_list_, _list_->count + 1))
|
||||
return false;
|
||||
}
|
||||
memmove(_list_->buffer + index + 1, _list_->buffer + index, (_list_->count - index) * sizeof(size_t));
|
||||
_list_->buffer[index] = element;
|
||||
_list_->count++;
|
||||
return true;
|
||||
}
|
||||
bool l_push_back(list *_list_, size_t element)
|
||||
{
|
||||
if (l_full(_list_))
|
||||
{
|
||||
if (!l_impl_grow(_list_, _list_->count + 1))
|
||||
return false;
|
||||
}
|
||||
_list_->buffer[_list_->count++] = element;
|
||||
return true;
|
||||
}
|
||||
bool l_pop_front(list *_list_)
|
||||
{
|
||||
if (l_empty(_list_))
|
||||
return false;
|
||||
memmove(_list_->buffer, _list_->buffer + 1, _list_->count * sizeof(size_t));
|
||||
_list_->buffer[--_list_->count] = l_impl_default_value();
|
||||
return true;
|
||||
}
|
||||
bool l_pop(list *_list_, size_t index)
|
||||
{
|
||||
if (l_empty(_list_))
|
||||
return false;
|
||||
if (index >= _list_->count)
|
||||
return false;
|
||||
if (index == 0)
|
||||
{
|
||||
return l_pop_front(_list_);
|
||||
}
|
||||
else if (index == _list_->count - 1)
|
||||
{
|
||||
return l_pop_back(_list_);
|
||||
}
|
||||
memmove(_list_->buffer + index, _list_->buffer + index + 1, (_list_->count - index) * sizeof(size_t));
|
||||
_list_->buffer[--_list_->count] = l_impl_default_value();
|
||||
return true;
|
||||
}
|
||||
bool l_pop_back(list *_list_)
|
||||
{
|
||||
if (l_empty(_list_))
|
||||
return false;
|
||||
_list_->buffer[--_list_->count] = l_impl_default_value();
|
||||
return true;
|
||||
}
|
||||
bool l_push_if(list *_list_, size_t element, size_t index, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return l_push(_list_, element, index);
|
||||
return false;
|
||||
}
|
||||
bool l_pop_if(list *_list_, size_t index, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return l_pop(_list_, index);
|
||||
return false;
|
||||
}
|
||||
bool l_prepend(list *_list_, size_t *elements, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return false;
|
||||
if (!l_fits(_list_, size))
|
||||
{
|
||||
if (!l_impl_grow(_list_, _list_->count + size))
|
||||
return false;
|
||||
}
|
||||
memmove(_list_->buffer + size, _list_->buffer, _list_->count * sizeof(size_t));
|
||||
memcpy(_list_->buffer, elements, size * sizeof(size_t));
|
||||
_list_->count += size;
|
||||
return true;
|
||||
}
|
||||
bool l_insert(list *_list_, size_t *elements, size_t size, size_t index)
|
||||
{
|
||||
if (size == 0 || index > _list_->count)
|
||||
return false;
|
||||
if (index == 0)
|
||||
return l_prepend(_list_, elements, size);
|
||||
else if (index == _list_->count)
|
||||
return l_append(_list_, elements, size);
|
||||
else
|
||||
{
|
||||
if (!l_fits(_list_, size))
|
||||
{
|
||||
if (!l_impl_grow(_list_, _list_->count + size))
|
||||
return false;
|
||||
}
|
||||
memmove(_list_->buffer + index + size, _list_->buffer + index, (_list_->count - index) * sizeof(size_t));
|
||||
memcpy(_list_->buffer + index, elements, size * sizeof(size_t));
|
||||
_list_->count += size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool l_append(list *_list_, size_t *elements, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return false;
|
||||
if (!l_fits(_list_, size))
|
||||
{
|
||||
if (!l_impl_grow(_list_, _list_->count + size))
|
||||
return false;
|
||||
}
|
||||
memcpy(_list_->buffer + _list_->count, elements, size * sizeof(size_t));
|
||||
_list_->count += size;
|
||||
return true;
|
||||
}
|
||||
bool l_remove(list *_list_, size_t from, size_t to)
|
||||
{
|
||||
if (from > to || to >= _list_->count)
|
||||
return false;
|
||||
size_t length = (to - from + 1);
|
||||
memmove(_list_->buffer + from, _list_->buffer + to + 1, (_list_->count - to - 1) * sizeof(size_t));
|
||||
memset(_list_->buffer + _list_->count - length, 0, length * sizeof(size_t));
|
||||
_list_->count -= to - from + 1;
|
||||
return true;
|
||||
}
|
||||
list *l_extract(list *_list_, size_t from, size_t to)
|
||||
{
|
||||
if (from > to || to >= _list_->count)
|
||||
return false;
|
||||
size_t length = to - from + 1;
|
||||
list *result = l_new_from(_list_->buffer + from, length);
|
||||
if (!result)
|
||||
return NULL;
|
||||
memmove(_list_->buffer + from, _list_->buffer + to + 1, (_list_->count - to - 1) * sizeof(size_t));
|
||||
memset(_list_->buffer + _list_->count - length, 0, length * sizeof(size_t));
|
||||
_list_->count -= length;
|
||||
return result;
|
||||
}
|
||||
size_t l_front(list *_list_)
|
||||
{
|
||||
if (l_empty(_list_))
|
||||
return l_impl_default_value();
|
||||
return _list_->buffer[0];
|
||||
}
|
||||
size_t l_get(list *_list_, size_t index)
|
||||
{
|
||||
if (index >= _list_->count || l_empty(_list_))
|
||||
return l_impl_default_value();
|
||||
return _list_->buffer[index];
|
||||
}
|
||||
size_t *l_get_ref(list *_list_, size_t index)
|
||||
{
|
||||
if (index >= _list_->count)
|
||||
return NULL;
|
||||
if (l_empty(_list_))
|
||||
return NULL;
|
||||
return &(_list_->buffer[index]);
|
||||
}
|
||||
size_t l_back(list *_list_)
|
||||
{
|
||||
if (l_empty(_list_))
|
||||
return l_impl_default_value();
|
||||
return _list_->buffer[_list_->count - 1];
|
||||
}
|
||||
size_t l_indexof(list *_list_, size_t element, int (*comparator)(size_t, size_t), bool from_start)
|
||||
{
|
||||
if (from_start)
|
||||
{
|
||||
for (size_t i = 0; i < _list_->count; i++)
|
||||
{
|
||||
if (comparator(_list_->buffer[i], element) == 0)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = _list_->count; i > 0; i--)
|
||||
{
|
||||
if (comparator(_list_->buffer[i - 1], element) == 0)
|
||||
return i - 1;
|
||||
}
|
||||
}
|
||||
return _list_->count;
|
||||
}
|
||||
bool l_contains(list *_list_, size_t element, int (*comparator)(size_t, size_t))
|
||||
{
|
||||
for (size_t i = 0; i < _list_->count; i++)
|
||||
{
|
||||
if (comparator(_list_->buffer[i], element) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool l_empty(list *_list_) { return _list_->count == 0; }
|
||||
bool l_full(list *_list_) { return _list_->count >= _list_->capacity; }
|
||||
size_t l_count(list *_list_) { return _list_->count; }
|
||||
bool l_fits(list *_list_, size_t size) { return _list_->count + size <= _list_->capacity; }
|
||||
size_t l_capacity(list *_list_) { return _list_->capacity; }
|
||||
list_iter *l_iter_new(list *target)
|
||||
{
|
||||
list_iter *iter = malloc(sizeof(list_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
l_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void l_iter_free(list_iter *iter) { free(iter); }
|
||||
void l_iter_init(list_iter *iter, list *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->cursor = 0;
|
||||
iter->start = true;
|
||||
iter->end = l_empty(target);
|
||||
}
|
||||
bool l_iter_start(list_iter *iter) { return l_empty(iter->target) || iter->start; }
|
||||
bool l_iter_end(list_iter *iter) { return l_empty(iter->target) || iter->end; }
|
||||
void l_iter_to_start(list_iter *iter)
|
||||
{
|
||||
iter->cursor = 0;
|
||||
iter->start = true;
|
||||
iter->end = l_empty(iter->target);
|
||||
}
|
||||
void l_iter_to_end(list_iter *iter)
|
||||
{
|
||||
iter->start = l_empty(iter->target);
|
||||
iter->cursor = l_empty(iter->target) ? 0 : iter->target->count - 1;
|
||||
iter->end = true;
|
||||
}
|
||||
bool l_iter_next(list_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
iter->start = l_empty(iter->target);
|
||||
if (iter->cursor == iter->target->count - 1)
|
||||
iter->end = true;
|
||||
else
|
||||
iter->cursor++;
|
||||
return true;
|
||||
}
|
||||
bool l_iter_prev(list_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
iter->end = l_empty(iter->target);
|
||||
if (iter->cursor == 0)
|
||||
iter->start = true;
|
||||
else
|
||||
iter->cursor--;
|
||||
return true;
|
||||
}
|
||||
size_t l_iter_value(list_iter *iter)
|
||||
{
|
||||
if (l_empty(iter->target))
|
||||
return l_impl_default_value();
|
||||
return iter->target->buffer[iter->cursor];
|
||||
}
|
||||
size_t *l_iter_rvalue(list_iter *iter)
|
||||
{
|
||||
if (l_empty(iter->target))
|
||||
return NULL;
|
||||
return &(iter->target->buffer[iter->cursor]);
|
||||
}
|
||||
size_t l_iter_index(list_iter *iter) { return iter->cursor; }
|
||||
static bool l_impl_grow(list *_list_, size_t required)
|
||||
{
|
||||
size_t new_capacity = _list_->capacity * 2;
|
||||
if (new_capacity < required)
|
||||
new_capacity = required;
|
||||
size_t *new_buffer = realloc(_list_->buffer, sizeof(size_t) * new_capacity);
|
||||
if (!new_buffer)
|
||||
return false;
|
||||
_list_->buffer = new_buffer;
|
||||
_list_->capacity = new_capacity;
|
||||
return true;
|
||||
}
|
||||
static list_iter l_impl_it_start(list *_list_)
|
||||
{
|
||||
list_iter iter;
|
||||
l_iter_init(&iter, _list_);
|
||||
l_iter_to_start(&iter);
|
||||
return iter;
|
||||
}
|
||||
static list_iter l_impl_it_end(list *_list_)
|
||||
{
|
||||
list_iter iter;
|
||||
l_iter_init(&iter, _list_);
|
||||
l_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,539 @@
|
||||
#include "ext/multimap.h"
|
||||
|
||||
//MULTIMAP_GENERATE(mm, multimap, , size_t, size_t)
|
||||
|
||||
typedef struct multimap_s
|
||||
{
|
||||
struct multimap_entry_s *(*buffer)[2];
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
double load;
|
||||
int (*cmp)(size_t, size_t);
|
||||
size_t (*hash)(size_t);
|
||||
struct multimap_iter_s (*it_start)(struct multimap_s *);
|
||||
struct multimap_iter_s (*it_end)(struct multimap_s *);
|
||||
} multimap, *multimap_ptr;
|
||||
typedef struct multimap_entry_s
|
||||
{
|
||||
size_t key;
|
||||
size_t value;
|
||||
struct multimap_entry_s *next;
|
||||
struct multimap_entry_s *prev;
|
||||
} multimap_entry, *multimap_entry_ptr;
|
||||
typedef struct multimap_iter_s
|
||||
{
|
||||
struct multimap_s *target;
|
||||
struct multimap_entry_s *curr_entry;
|
||||
size_t cursor;
|
||||
size_t index;
|
||||
size_t first;
|
||||
size_t last;
|
||||
bool start;
|
||||
bool end;
|
||||
} multimap_iter, *multimap_iter_ptr;
|
||||
multimap *mm_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t));
|
||||
void mm_clear(multimap *_map_);
|
||||
void mm_free(multimap *_map_);
|
||||
bool mm_insert(multimap *_map_, size_t key, size_t value);
|
||||
bool mm_remove(multimap *_map_, size_t key, size_t *value);
|
||||
size_t mm_remove_all(multimap *_map_, size_t key);
|
||||
bool mm_insert_if(multimap *_map_, size_t key, size_t value, bool condition);
|
||||
bool mm_remove_if(multimap *_map_, size_t key, size_t *value, bool condition);
|
||||
bool mm_max(multimap *_map_, size_t *key, size_t *value);
|
||||
bool mm_min(multimap *_map_, size_t *key, size_t *value);
|
||||
size_t mm_get(multimap *_map_, size_t key);
|
||||
size_t *mm_get_ref(multimap *_map_, size_t key);
|
||||
bool mm_contains(multimap *_map_, size_t key);
|
||||
bool mm_empty(multimap *_map_);
|
||||
size_t mm_count(multimap *_map_);
|
||||
size_t mm_capacity(multimap *_map_);
|
||||
multimap_iter *mm_iter_new(multimap *target);
|
||||
void mm_iter_free(multimap_iter *iter);
|
||||
void mm_iter_init(multimap_iter *iter, multimap *target);
|
||||
bool mm_iter_start(multimap_iter *iter);
|
||||
bool mm_iter_end(multimap_iter *iter);
|
||||
void mm_iter_to_start(multimap_iter *iter);
|
||||
void mm_iter_to_end(multimap_iter *iter);
|
||||
bool mm_iter_next(multimap_iter *iter);
|
||||
bool mm_iter_prev(multimap_iter *iter);
|
||||
size_t mm_iter_key(multimap_iter *iter);
|
||||
size_t mm_iter_value(multimap_iter *iter);
|
||||
size_t *mm_iter_rvalue(multimap_iter *iter);
|
||||
size_t mm_iter_index(multimap_iter *iter);
|
||||
static inline size_t mm_impl_default_key(void)
|
||||
{
|
||||
size_t _empty_key_;
|
||||
memset(&_empty_key_, 0, sizeof(size_t));
|
||||
return _empty_key_;
|
||||
}
|
||||
static inline size_t mm_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
bool mm_impl_grow(multimap *_map_);
|
||||
multimap_entry *mm_impl_new_entry(size_t key, size_t value);
|
||||
multimap_entry *mm_impl_get_entry(multimap *_map_, size_t key);
|
||||
size_t mm_impl_calculate_size(size_t required);
|
||||
static multimap_iter mm_impl_it_start(multimap *_map_);
|
||||
static multimap_iter mm_impl_it_end(multimap *_map_);
|
||||
multimap *mm_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t))
|
||||
{
|
||||
if (capacity == 0 || load <= 0)
|
||||
return NULL;
|
||||
if (capacity >= UINTMAX_MAX * load)
|
||||
return NULL;
|
||||
size_t real_capacity = mm_impl_calculate_size(capacity / load);
|
||||
multimap *_map_ = malloc(sizeof(multimap));
|
||||
if (!_map_)
|
||||
return NULL;
|
||||
_map_->buffer = malloc(sizeof(multimap_entry_ptr[2]) * real_capacity);
|
||||
if (!_map_->buffer)
|
||||
{
|
||||
free(_map_);
|
||||
return NULL;
|
||||
}
|
||||
memset(_map_->buffer, 0, sizeof(multimap_entry_ptr[2]) * real_capacity);
|
||||
_map_->count = 0;
|
||||
_map_->capacity = real_capacity;
|
||||
_map_->load = load;
|
||||
_map_->cmp = compare;
|
||||
_map_->hash = hash;
|
||||
return _map_;
|
||||
}
|
||||
void mm_clear(multimap *_map_)
|
||||
{
|
||||
size_t index = 0;
|
||||
multimap_entry *scan;
|
||||
while (index < _map_->capacity)
|
||||
{
|
||||
scan = _map_->buffer[index][0];
|
||||
if (scan != NULL)
|
||||
{
|
||||
if (scan->next == NULL && scan->prev == NULL)
|
||||
free(scan);
|
||||
else
|
||||
{
|
||||
while (scan != NULL)
|
||||
{
|
||||
multimap_entry *tmp = scan;
|
||||
scan = scan->next;
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
memset(_map_->buffer, 0, sizeof(multimap_entry_ptr[2]) * _map_->capacity);
|
||||
_map_->count = 0;
|
||||
}
|
||||
void mm_free(multimap *_map_)
|
||||
{
|
||||
mm_clear(_map_);
|
||||
free(_map_->buffer);
|
||||
free(_map_);
|
||||
}
|
||||
bool mm_insert(multimap *_map_, size_t key, size_t value)
|
||||
{
|
||||
if ((double)_map_->capacity * _map_->load <= (double)_map_->count)
|
||||
{
|
||||
if (!mm_impl_grow(_map_))
|
||||
return false;
|
||||
}
|
||||
size_t hash = _map_->hash(key);
|
||||
size_t pos = hash % _map_->capacity;
|
||||
multimap_entry *entry = mm_impl_new_entry(key, value);
|
||||
if (_map_->buffer[pos][0] == NULL)
|
||||
{
|
||||
_map_->buffer[pos][0] = entry;
|
||||
_map_->buffer[pos][1] = entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry->prev = _map_->buffer[pos][1];
|
||||
_map_->buffer[pos][1]->next = entry;
|
||||
_map_->buffer[pos][1] = entry;
|
||||
}
|
||||
_map_->count++;
|
||||
return true;
|
||||
}
|
||||
bool mm_remove(multimap *_map_, size_t key, size_t *value)
|
||||
{
|
||||
size_t hash = _map_->hash(key);
|
||||
multimap_entry **head = &(_map_->buffer[hash % _map_->capacity][0]);
|
||||
multimap_entry **tail = &(_map_->buffer[hash % _map_->capacity][1]);
|
||||
if (*head == NULL)
|
||||
return false;
|
||||
multimap_entry *entry = *head;
|
||||
if (entry->next == NULL && entry->prev == NULL)
|
||||
{
|
||||
if (_map_->cmp(entry->key, key) == 0)
|
||||
{
|
||||
*head = NULL;
|
||||
*tail = NULL;
|
||||
*value = entry->value;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool found = false;
|
||||
while (entry != NULL)
|
||||
{
|
||||
if (_map_->cmp(entry->key, key) == 0)
|
||||
{
|
||||
if (*head == entry)
|
||||
*head = entry->next;
|
||||
if (*tail == entry)
|
||||
*tail = entry->prev;
|
||||
if (entry->prev != NULL)
|
||||
entry->prev->next = entry->next;
|
||||
if (entry->next != NULL)
|
||||
entry->next->prev = entry->prev;
|
||||
*value = entry->value;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
entry = entry->next;
|
||||
}
|
||||
if (!found)
|
||||
return false;
|
||||
}
|
||||
free(entry);
|
||||
_map_->count--;
|
||||
return true;
|
||||
}
|
||||
size_t mm_remove_all(multimap *_map_, size_t key)
|
||||
{
|
||||
size_t total = 0;
|
||||
size_t hash = _map_->hash(key);
|
||||
multimap_entry **head = &(_map_->buffer[hash % _map_->capacity][0]);
|
||||
multimap_entry **tail = &(_map_->buffer[hash % _map_->capacity][1]);
|
||||
if (*head == NULL)
|
||||
return total;
|
||||
multimap_entry *entry = *head;
|
||||
if (entry->next == NULL)
|
||||
{
|
||||
*head = NULL;
|
||||
*tail = NULL;
|
||||
free(entry);
|
||||
total++;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (entry != NULL)
|
||||
{
|
||||
if (_map_->cmp(entry->key, key) == 0)
|
||||
{
|
||||
if (*head == entry)
|
||||
*head = entry->next;
|
||||
if (*tail == entry)
|
||||
*tail = entry->prev;
|
||||
multimap_entry *temp = entry->next;
|
||||
if (entry->prev != NULL)
|
||||
entry->prev->next = entry->next;
|
||||
if (entry->next != NULL)
|
||||
entry->next->prev = entry->prev;
|
||||
free(entry);
|
||||
entry = temp;
|
||||
total++;
|
||||
}
|
||||
else
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
_map_->count -= total;
|
||||
return total;
|
||||
}
|
||||
bool mm_insert_if(multimap *_map_, size_t key, size_t value, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return mm_insert(_map_, key, value);
|
||||
return false;
|
||||
}
|
||||
bool mm_remove_if(multimap *_map_, size_t key, size_t *value, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return mm_remove(_map_, key, value);
|
||||
return false;
|
||||
}
|
||||
bool mm_max(multimap *_map_, size_t *key, size_t *value)
|
||||
{
|
||||
if (mm_empty(_map_))
|
||||
return false;
|
||||
multimap_iter iter;
|
||||
for (mm_iter_init(&iter, _map_); !mm_iter_end(&iter); mm_iter_next(&iter))
|
||||
{
|
||||
size_t result_key = mm_iter_key(&iter);
|
||||
size_t result_value = mm_iter_value(&iter);
|
||||
size_t index = mm_iter_index(&iter);
|
||||
if (index == 0)
|
||||
{
|
||||
*key = result_key;
|
||||
*value = result_value;
|
||||
}
|
||||
else if (_map_->cmp(result_key, *key) > 0)
|
||||
{
|
||||
*key = result_key;
|
||||
*value = result_value;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool mm_min(multimap *_map_, size_t *key, size_t *value)
|
||||
{
|
||||
if (mm_empty(_map_))
|
||||
return false;
|
||||
multimap_iter iter;
|
||||
for (mm_iter_init(&iter, _map_); !mm_iter_end(&iter); mm_iter_next(&iter))
|
||||
{
|
||||
size_t result_key = mm_iter_key(&iter);
|
||||
size_t result_value = mm_iter_value(&iter);
|
||||
size_t index = mm_iter_index(&iter);
|
||||
if (index == 0)
|
||||
{
|
||||
*key = result_key;
|
||||
*value = result_value;
|
||||
}
|
||||
else if (_map_->cmp(result_key, *key) < 0)
|
||||
{
|
||||
*key = result_key;
|
||||
*value = result_value;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
size_t mm_get(multimap *_map_, size_t key)
|
||||
{
|
||||
multimap_entry *entry = mm_impl_get_entry(_map_, key);
|
||||
if (!entry)
|
||||
return mm_impl_default_value();
|
||||
return entry->value;
|
||||
}
|
||||
size_t *mm_get_ref(multimap *_map_, size_t key)
|
||||
{
|
||||
multimap_entry *entry = mm_impl_get_entry(_map_, key);
|
||||
if (!entry)
|
||||
return NULL;
|
||||
return &(entry->value);
|
||||
}
|
||||
bool mm_contains(multimap *_map_, size_t key) { return mm_impl_get_entry(_map_, key) != NULL; }
|
||||
bool mm_empty(multimap *_map_) { return _map_->count == 0; }
|
||||
size_t mm_count(multimap *_map_) { return _map_->count; }
|
||||
size_t mm_capacity(multimap *_map_) { return _map_->capacity; }
|
||||
multimap_iter *mm_iter_new(multimap *target)
|
||||
{
|
||||
multimap_iter *iter = malloc(sizeof(multimap_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
mm_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void mm_iter_free(multimap_iter *iter) { free(iter); }
|
||||
void mm_iter_init(multimap_iter *iter, multimap *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->cursor = 0;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = mm_empty(target);
|
||||
if (!mm_empty(target))
|
||||
{
|
||||
for (size_t i = 0; i < target->capacity; i++)
|
||||
{
|
||||
if (target->buffer[i][0] != NULL)
|
||||
{
|
||||
iter->curr_entry = target->buffer[i][0];
|
||||
iter->first = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
iter->cursor = iter->first;
|
||||
for (size_t i = target->capacity; i > 0; i--)
|
||||
{
|
||||
if (target->buffer[i - 1][0] != NULL)
|
||||
{
|
||||
iter->last = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool mm_iter_start(multimap_iter *iter) { return mm_empty(iter->target) || iter->start; }
|
||||
bool mm_iter_end(multimap_iter *iter) { return mm_empty(iter->target) || iter->end; }
|
||||
void mm_iter_to_start(multimap_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->first;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = mm_empty(iter->target);
|
||||
iter->curr_entry = iter->target->buffer[iter->first][0];
|
||||
}
|
||||
void mm_iter_to_end(multimap_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->last;
|
||||
iter->index = iter->target->count - 1;
|
||||
iter->start = mm_empty(iter->target);
|
||||
iter->end = true;
|
||||
iter->curr_entry = iter->target->buffer[iter->last][1];
|
||||
}
|
||||
bool mm_iter_next(multimap_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
if (iter->curr_entry->next != NULL)
|
||||
{
|
||||
iter->curr_entry = iter->curr_entry->next;
|
||||
iter->index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iter->cursor == iter->last)
|
||||
{
|
||||
iter->end = true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
iter->cursor++;
|
||||
while (iter->target->buffer[iter->cursor][0] == NULL)
|
||||
iter->cursor++;
|
||||
iter->curr_entry = iter->target->buffer[iter->cursor][0];
|
||||
iter->index++;
|
||||
}
|
||||
}
|
||||
iter->start = mm_empty(iter->target);
|
||||
return true;
|
||||
}
|
||||
bool mm_iter_prev(multimap_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
if (iter->curr_entry->prev != NULL)
|
||||
{
|
||||
iter->curr_entry = iter->curr_entry->prev;
|
||||
iter->index--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iter->cursor == iter->first)
|
||||
{
|
||||
iter->start = true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
iter->cursor--;
|
||||
while (iter->target->buffer[iter->cursor][1] == NULL)
|
||||
iter->cursor--;
|
||||
iter->curr_entry = iter->target->buffer[iter->cursor][1];
|
||||
iter->index--;
|
||||
}
|
||||
}
|
||||
iter->end = mm_empty(iter->target);
|
||||
return true;
|
||||
}
|
||||
size_t mm_iter_key(multimap_iter *iter)
|
||||
{
|
||||
if (mm_empty(iter->target))
|
||||
return mm_impl_default_key();
|
||||
return iter->curr_entry->key;
|
||||
}
|
||||
size_t mm_iter_value(multimap_iter *iter)
|
||||
{
|
||||
if (mm_empty(iter->target))
|
||||
return mm_impl_default_value();
|
||||
return iter->curr_entry->value;
|
||||
}
|
||||
size_t *mm_iter_rvalue(multimap_iter *iter)
|
||||
{
|
||||
if (mm_empty(iter->target))
|
||||
return NULL;
|
||||
return &(iter->curr_entry->value);
|
||||
}
|
||||
size_t mm_iter_index(multimap_iter *iter) { return iter->index; }
|
||||
bool mm_impl_grow(multimap *_map_)
|
||||
{
|
||||
size_t new_capacity = mm_impl_calculate_size(_map_->capacity * 1.5);
|
||||
multimap *_new_map_ = mm_new(new_capacity, _map_->load, _map_->cmp, _map_->hash);
|
||||
if (!_new_map_)
|
||||
return false;
|
||||
multimap_iter iter;
|
||||
for (mm_iter_init(&iter, _map_); !mm_iter_end(&iter); mm_iter_next(&iter))
|
||||
{
|
||||
size_t key = mm_iter_key(&iter);
|
||||
size_t value = mm_iter_value(&iter);
|
||||
mm_insert(_new_map_, key, value);
|
||||
}
|
||||
if (_map_->count != _new_map_->count)
|
||||
{
|
||||
mm_free(_new_map_);
|
||||
return false;
|
||||
}
|
||||
multimap_entry *(*tmp_b)[2] = _map_->buffer;
|
||||
_map_->buffer = _new_map_->buffer;
|
||||
_new_map_->buffer = tmp_b;
|
||||
size_t tmp_c = _map_->capacity;
|
||||
_map_->capacity = _new_map_->capacity;
|
||||
_new_map_->capacity = tmp_c;
|
||||
mm_free(_new_map_);
|
||||
return true;
|
||||
}
|
||||
multimap_entry *mm_impl_new_entry(size_t key, size_t value)
|
||||
{
|
||||
multimap_entry *entry = malloc(sizeof(multimap_entry));
|
||||
if (!entry)
|
||||
return NULL;
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
entry->prev = NULL;
|
||||
return entry;
|
||||
}
|
||||
multimap_entry *mm_impl_get_entry(multimap *_map_, size_t key)
|
||||
{
|
||||
size_t hash = _map_->hash(key);
|
||||
multimap_entry *entry = _map_->buffer[hash % _map_->capacity][0];
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
if (entry->next == NULL && entry->prev == NULL)
|
||||
{
|
||||
if (_map_->cmp(entry->key, key) == 0)
|
||||
return entry;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
while (entry != NULL)
|
||||
{
|
||||
if (_map_->cmp(entry->key, key) == 0)
|
||||
return entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
size_t mm_impl_calculate_size(size_t required)
|
||||
{
|
||||
const size_t count = sizeof(cmc_hashtable_primes) / sizeof(cmc_hashtable_primes[0]);
|
||||
if (cmc_hashtable_primes[count - 1] < required)
|
||||
return required;
|
||||
size_t i = 0;
|
||||
while (cmc_hashtable_primes[i] < required)
|
||||
i++;
|
||||
return cmc_hashtable_primes[i];
|
||||
}
|
||||
static multimap_iter mm_impl_it_start(multimap *_map_)
|
||||
{
|
||||
multimap_iter iter;
|
||||
mm_iter_init(&iter, _map_);
|
||||
return iter;
|
||||
}
|
||||
static multimap_iter mm_impl_it_end(multimap *_map_)
|
||||
{
|
||||
multimap_iter iter;
|
||||
mm_iter_init(&iter, _map_);
|
||||
mm_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,655 @@
|
||||
#include "ext/multiset.h"
|
||||
|
||||
//MULTISET_GENERATE(ms, multiset, , size_t)
|
||||
|
||||
typedef struct multiset_s
|
||||
{
|
||||
struct multiset_entry_s *buffer;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
size_t cardinality;
|
||||
double load;
|
||||
int (*cmp)(size_t, size_t);
|
||||
size_t (*hash)(size_t);
|
||||
struct multiset_iter_s (*it_start)(struct multiset_s *);
|
||||
struct multiset_iter_s (*it_end)(struct multiset_s *);
|
||||
} multiset, *multiset_ptr;
|
||||
typedef struct multiset_entry_s
|
||||
{
|
||||
size_t value;
|
||||
size_t multiplicity;
|
||||
size_t dist;
|
||||
enum cmc_entry_state_e state;
|
||||
} multiset_entry, *multiset_entry_ptr;
|
||||
typedef struct multiset_iter_s
|
||||
{
|
||||
struct multiset_s *target;
|
||||
size_t cursor;
|
||||
size_t index;
|
||||
size_t first;
|
||||
size_t last;
|
||||
bool start;
|
||||
bool end;
|
||||
} multiset_iter, *multiset_iter_ptr;
|
||||
multiset *ms_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t));
|
||||
void ms_clear(multiset *_set_);
|
||||
void ms_free(multiset *_set_);
|
||||
bool ms_insert(multiset *_set_, size_t element);
|
||||
bool ms_insert_many(multiset *_set_, size_t element, size_t count);
|
||||
bool ms_update(multiset *_set_, size_t element, size_t multiplicity);
|
||||
bool ms_remove(multiset *_set_, size_t element);
|
||||
size_t ms_remove_all(multiset *_set_, size_t element);
|
||||
bool ms_insert_if(multiset *_set_, size_t element, bool condition);
|
||||
bool ms_remove_if(multiset *_set_, size_t element, bool condition);
|
||||
bool ms_max(multiset *_set_, size_t *value);
|
||||
bool ms_min(multiset *_set_, size_t *value);
|
||||
size_t ms_multiplicity_of(multiset *_set_, size_t element);
|
||||
bool ms_contains(multiset *_set_, size_t element);
|
||||
bool ms_empty(multiset *_set_);
|
||||
size_t ms_count(multiset *_set_);
|
||||
size_t ms_cardinality(multiset *_set_);
|
||||
size_t ms_capacity(multiset *_set_);
|
||||
cmc_string ms_to_string(multiset *_set_);
|
||||
multiset *ms_union(multiset *_set1_, multiset *_set2_);
|
||||
multiset *ms_intersection(multiset *_set1_, multiset *_set2_);
|
||||
multiset *ms_difference(multiset *_set1_, multiset *_set2_);
|
||||
multiset *ms_summation(multiset *_set1_, multiset *_set2_);
|
||||
multiset *ms_symmetric_difference(multiset *_set1_, multiset *_set2_);
|
||||
bool ms_is_subset(multiset *_set1_, multiset *_set2_);
|
||||
bool ms_is_superset(multiset *_set1_, multiset *_set2_);
|
||||
bool ms_is_proper_subset(multiset *_set1_, multiset *_set2_);
|
||||
bool ms_is_proper_superset(multiset *_set1_, multiset *_set2_);
|
||||
bool ms_is_disjointset(multiset *_set1_, multiset *_set2_);
|
||||
multiset_iter *ms_iter_new(multiset *target);
|
||||
void ms_iter_free(multiset_iter *iter);
|
||||
void ms_iter_init(multiset_iter *iter, multiset *target);
|
||||
bool ms_iter_start(multiset_iter *iter);
|
||||
bool ms_iter_end(multiset_iter *iter);
|
||||
void ms_iter_to_start(multiset_iter *iter);
|
||||
void ms_iter_to_end(multiset_iter *iter);
|
||||
bool ms_iter_next(multiset_iter *iter);
|
||||
bool ms_iter_prev(multiset_iter *iter);
|
||||
size_t ms_iter_value(multiset_iter *iter);
|
||||
size_t ms_iter_multiplicity(multiset_iter *iter);
|
||||
size_t ms_iter_index(multiset_iter *iter);
|
||||
static inline size_t ms_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
static multiset_entry *ms_impl_insert_and_return(multiset *_set_, size_t element, bool *new_node);
|
||||
static bool ms_impl_grow(multiset *_set_);
|
||||
static multiset_entry *ms_impl_get_entry(multiset *_set_, size_t element);
|
||||
static size_t ms_impl_calculate_size(size_t required);
|
||||
static multiset_iter ms_impl_it_start(multiset *_set_);
|
||||
static multiset_iter ms_impl_it_end(multiset *_set_);
|
||||
multiset *ms_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t))
|
||||
{
|
||||
if (capacity == 0 || load <= 0 || load >= 1)
|
||||
return NULL;
|
||||
if (capacity >= UINTMAX_MAX * load)
|
||||
return NULL;
|
||||
size_t real_capacity = ms_impl_calculate_size(capacity / load);
|
||||
multiset *_set_ = malloc(sizeof(multiset));
|
||||
if (!_set_)
|
||||
return NULL;
|
||||
_set_->buffer = malloc(sizeof(multiset_entry) * real_capacity);
|
||||
if (!_set_->buffer)
|
||||
{
|
||||
free(_set_);
|
||||
return NULL;
|
||||
}
|
||||
memset(_set_->buffer, 0, sizeof(multiset_entry) * real_capacity);
|
||||
_set_->count = 0;
|
||||
_set_->cardinality = 0;
|
||||
_set_->capacity = real_capacity;
|
||||
_set_->load = load;
|
||||
_set_->cmp = compare;
|
||||
_set_->hash = hash;
|
||||
_set_->it_start = ms_impl_it_start;
|
||||
_set_->it_end = ms_impl_it_end;
|
||||
return _set_;
|
||||
}
|
||||
void ms_clear(multiset *_set_)
|
||||
{
|
||||
memset(_set_->buffer, 0, sizeof(multiset_entry) * _set_->capacity);
|
||||
_set_->count = 0;
|
||||
}
|
||||
void ms_free(multiset *_set_)
|
||||
{
|
||||
free(_set_->buffer);
|
||||
free(_set_);
|
||||
}
|
||||
bool ms_insert(multiset *_set_, size_t element)
|
||||
{
|
||||
bool new_node;
|
||||
multiset_entry *entry = ms_impl_insert_and_return(_set_, element, &new_node);
|
||||
if (!entry)
|
||||
return false;
|
||||
if (!new_node)
|
||||
entry->multiplicity++;
|
||||
_set_->cardinality++;
|
||||
return true;
|
||||
}
|
||||
bool ms_insert_many(multiset *_set_, size_t element, size_t count)
|
||||
{
|
||||
if (count == 0)
|
||||
return true;
|
||||
bool new_node;
|
||||
multiset_entry *entry = ms_impl_insert_and_return(_set_, element, &new_node);
|
||||
if (!entry)
|
||||
return false;
|
||||
if (new_node)
|
||||
entry->multiplicity = count;
|
||||
else
|
||||
entry->multiplicity += count;
|
||||
_set_->cardinality += count;
|
||||
return true;
|
||||
}
|
||||
bool ms_update(multiset *_set_, size_t element, size_t multiplicity)
|
||||
{
|
||||
if (multiplicity == 0)
|
||||
return ms_remove_all(_set_, element);
|
||||
bool new_node;
|
||||
multiset_entry *entry = ms_impl_insert_and_return(_set_, element, &new_node);
|
||||
if (!entry)
|
||||
return false;
|
||||
if (new_node)
|
||||
_set_->cardinality++;
|
||||
_set_->cardinality = (_set_->cardinality - entry->multiplicity) + multiplicity;
|
||||
entry->multiplicity = multiplicity;
|
||||
return true;
|
||||
}
|
||||
bool ms_remove(multiset *_set_, size_t element)
|
||||
{
|
||||
multiset_entry *result = ms_impl_get_entry(_set_, element);
|
||||
if (result == NULL)
|
||||
return false;
|
||||
if (result->multiplicity > 1)
|
||||
result->multiplicity--;
|
||||
else
|
||||
{
|
||||
result->value = ms_impl_default_value();
|
||||
result->multiplicity = 0;
|
||||
result->dist = 0;
|
||||
result->state = CMC_ES_DELETED;
|
||||
_set_->count--;
|
||||
}
|
||||
_set_->cardinality--;
|
||||
return true;
|
||||
}
|
||||
size_t ms_remove_all(multiset *_set_, size_t element)
|
||||
{
|
||||
multiset_entry *result = ms_impl_get_entry(_set_, element);
|
||||
if (result == NULL)
|
||||
return 0;
|
||||
size_t removed = result->multiplicity;
|
||||
result->value = ms_impl_default_value();
|
||||
result->multiplicity = 0;
|
||||
result->dist = 0;
|
||||
result->state = CMC_ES_DELETED;
|
||||
_set_->count--;
|
||||
_set_->cardinality -= removed;
|
||||
return removed;
|
||||
}
|
||||
bool ms_insert_if(multiset *_set_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return ms_insert(_set_, element);
|
||||
return false;
|
||||
}
|
||||
bool ms_remove_if(multiset *_set_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return ms_remove(_set_, element);
|
||||
return false;
|
||||
}
|
||||
bool ms_max(multiset *_set_, size_t *value)
|
||||
{
|
||||
if (ms_empty(_set_))
|
||||
return false;
|
||||
multiset_iter iter;
|
||||
for (ms_iter_init(&iter, _set_); !ms_iter_end(&iter); ms_iter_next(&iter))
|
||||
{
|
||||
size_t result = ms_iter_value(&iter);
|
||||
size_t index = ms_iter_index(&iter);
|
||||
if (index == 0)
|
||||
*value = result;
|
||||
else if (_set_->cmp(result, *value) > 0)
|
||||
*value = result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ms_min(multiset *_set_, size_t *value)
|
||||
{
|
||||
if (ms_empty(_set_))
|
||||
return false;
|
||||
multiset_iter iter;
|
||||
for (ms_iter_init(&iter, _set_); !ms_iter_end(&iter); ms_iter_next(&iter))
|
||||
{
|
||||
size_t result = ms_iter_value(&iter);
|
||||
size_t index = ms_iter_index(&iter);
|
||||
if (index == 0)
|
||||
*value = result;
|
||||
else if (_set_->cmp(result, *value) < 0)
|
||||
*value = result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
size_t ms_multiplicity_of(multiset *_set_, size_t element)
|
||||
{
|
||||
multiset_entry *entry = ms_impl_get_entry(_set_, element);
|
||||
if (!entry)
|
||||
return 0;
|
||||
return entry->multiplicity;
|
||||
}
|
||||
bool ms_contains(multiset *_set_, size_t element) { return ms_impl_get_entry(_set_, element) != NULL; }
|
||||
bool ms_empty(multiset *_set_) { return _set_->count == 0; }
|
||||
size_t ms_count(multiset *_set_) { return _set_->count; }
|
||||
size_t ms_cardinality(multiset *_set_) { return _set_->cardinality; }
|
||||
size_t ms_capacity(multiset *_set_) { return _set_->capacity; }
|
||||
cmc_string ms_to_string(multiset *_set_)
|
||||
{
|
||||
cmc_string str;
|
||||
multiset *s_ = _set_;
|
||||
const char *name = "multiset";
|
||||
snprintf(str.s, cmc_string_len, cmc_string_fmt_multiset, name, s_, s_->buffer, s_->capacity, s_->count, s_->cardinality, s_->load, s_->cmp, s_->hash);
|
||||
return str;
|
||||
}
|
||||
multiset *ms_union(multiset *_set1_, multiset *_set2_)
|
||||
{
|
||||
multiset *_set_r_ = ms_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
multiset_iter iter1, iter2;
|
||||
ms_iter_init(&iter1, _set1_);
|
||||
ms_iter_init(&iter2, _set2_);
|
||||
for (ms_iter_to_start(&iter1); !ms_iter_end(&iter1); ms_iter_next(&iter1))
|
||||
{
|
||||
size_t value = ms_iter_value(&iter1);
|
||||
size_t m1 = ms_iter_multiplicity(&iter1);
|
||||
size_t m2 = ms_multiplicity_of(_set2_, value);
|
||||
size_t max_ = m1 > m2 ? m1 : m2;
|
||||
ms_update(_set_r_, value, max_);
|
||||
}
|
||||
for (ms_iter_to_start(&iter2); !ms_iter_end(&iter2); ms_iter_next(&iter2))
|
||||
{
|
||||
size_t value = ms_iter_value(&iter2);
|
||||
size_t m1 = ms_multiplicity_of(_set1_, value);
|
||||
size_t m2 = ms_iter_multiplicity(&iter2);
|
||||
size_t max_ = m1 > m2 ? m1 : m2;
|
||||
ms_update(_set_r_, value, max_);
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
multiset *ms_intersection(multiset *_set1_, multiset *_set2_)
|
||||
{
|
||||
multiset *_set_r_ = ms_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
multiset *_set_A_ = _set1_->count < _set2_->count ? _set1_ : _set2_;
|
||||
multiset *_set_B_ = _set_A_ == _set1_ ? _set2_ : _set1_;
|
||||
multiset_iter iter;
|
||||
ms_iter_init(&iter, _set_A_);
|
||||
for (ms_iter_to_start(&iter); !ms_iter_end(&iter); ms_iter_next(&iter))
|
||||
{
|
||||
size_t value = ms_iter_value(&iter);
|
||||
size_t m1 = ms_iter_multiplicity(&iter);
|
||||
size_t m2 = ms_multiplicity_of(_set_B_, value);
|
||||
size_t max_ = m1 < m2 ? m1 : m2;
|
||||
ms_update(_set_r_, value, max_);
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
multiset *ms_difference(multiset *_set1_, multiset *_set2_)
|
||||
{
|
||||
multiset *_set_r_ = ms_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
multiset_iter iter;
|
||||
ms_iter_init(&iter, _set1_);
|
||||
for (ms_iter_to_start(&iter); !ms_iter_end(&iter); ms_iter_next(&iter))
|
||||
{
|
||||
size_t value = ms_iter_value(&iter);
|
||||
size_t m1 = ms_iter_multiplicity(&iter);
|
||||
size_t m2 = ms_multiplicity_of(_set2_, value);
|
||||
if (m1 > m2)
|
||||
ms_update(_set_r_, value, m1 - m2);
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
multiset *ms_summation(multiset *_set1_, multiset *_set2_)
|
||||
{
|
||||
multiset *_set_r_ = ms_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
multiset_iter iter1, iter2;
|
||||
ms_iter_init(&iter1, _set1_);
|
||||
ms_iter_init(&iter2, _set2_);
|
||||
for (ms_iter_to_start(&iter1); !ms_iter_end(&iter1); ms_iter_next(&iter1))
|
||||
{
|
||||
ms_insert_many(_set_r_, ms_iter_value(&iter1), ms_iter_multiplicity(&iter1));
|
||||
}
|
||||
for (ms_iter_to_start(&iter2); !ms_iter_end(&iter2); ms_iter_next(&iter2))
|
||||
{
|
||||
ms_insert_many(_set_r_, ms_iter_value(&iter2), ms_iter_multiplicity(&iter2));
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
multiset *ms_symmetric_difference(multiset *_set1_, multiset *_set2_)
|
||||
{
|
||||
multiset_iter iter1, iter2;
|
||||
multiset *_set_r_ = ms_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
ms_iter_init(&iter1, _set1_);
|
||||
ms_iter_init(&iter2, _set2_);
|
||||
for (ms_iter_to_start(&iter1); !ms_iter_end(&iter1); ms_iter_next(&iter1))
|
||||
{
|
||||
size_t value = ms_iter_value(&iter1);
|
||||
size_t m1 = ms_iter_multiplicity(&iter1);
|
||||
size_t m2 = ms_multiplicity_of(_set2_, value);
|
||||
if (m1 != m2)
|
||||
{
|
||||
if (m1 > m2)
|
||||
ms_update(_set_r_, value, m1 - m2);
|
||||
else
|
||||
ms_update(_set_r_, value, m2 - m1);
|
||||
}
|
||||
}
|
||||
for (ms_iter_to_start(&iter2); !ms_iter_end(&iter2); ms_iter_next(&iter2))
|
||||
{
|
||||
size_t value = ms_iter_value(&iter2);
|
||||
size_t m1 = ms_multiplicity_of(_set1_, value);
|
||||
size_t m2 = ms_iter_multiplicity(&iter2);
|
||||
if (m1 != m2)
|
||||
{
|
||||
if (m1 > m2)
|
||||
ms_update(_set_r_, value, m1 - m2);
|
||||
else
|
||||
ms_update(_set_r_, value, m2 - m1);
|
||||
}
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
bool ms_is_subset(multiset *_set1_, multiset *_set2_)
|
||||
{
|
||||
if (ms_count(_set1_) > ms_count(_set2_))
|
||||
return false;
|
||||
if (ms_empty(_set1_))
|
||||
return true;
|
||||
multiset_iter iter;
|
||||
ms_iter_init(&iter, _set1_);
|
||||
for (ms_iter_to_start(&iter); !ms_iter_end(&iter); ms_iter_next(&iter))
|
||||
{
|
||||
size_t value = ms_iter_value(&iter);
|
||||
size_t m1 = ms_iter_multiplicity(&iter);
|
||||
size_t m2 = ms_multiplicity_of(_set2_, value);
|
||||
if (m1 > m2)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ms_is_superset(multiset *_set1_, multiset *_set2_) { return ms_is_subset(_set2_, _set1_); }
|
||||
bool ms_is_proper_subset(multiset *_set1_, multiset *_set2_)
|
||||
{
|
||||
if (ms_count(_set1_) >= ms_count(_set2_))
|
||||
return false;
|
||||
if (ms_empty(_set1_))
|
||||
{
|
||||
if (!ms_empty(_set2_))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
multiset_iter iter;
|
||||
ms_iter_init(&iter, _set1_);
|
||||
for (ms_iter_to_start(&iter); !ms_iter_end(&iter); ms_iter_next(&iter))
|
||||
{
|
||||
size_t value = ms_iter_value(&iter);
|
||||
size_t m1 = ms_iter_multiplicity(&iter);
|
||||
size_t m2 = ms_multiplicity_of(_set2_, value);
|
||||
if (m1 >= m2)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ms_is_proper_superset(multiset *_set1_, multiset *_set2_) { return ms_is_proper_subset(_set2_, _set1_); }
|
||||
bool ms_is_disjointset(multiset *_set1_, multiset *_set2_)
|
||||
{
|
||||
if (ms_empty(_set1_))
|
||||
return true;
|
||||
multiset_iter iter;
|
||||
ms_iter_init(&iter, _set1_);
|
||||
for (ms_iter_to_start(&iter); !ms_iter_end(&iter); ms_iter_next(&iter))
|
||||
{
|
||||
size_t value = ms_iter_value(&iter);
|
||||
if (ms_contains(_set2_, value))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
multiset_iter *ms_iter_new(multiset *target)
|
||||
{
|
||||
multiset_iter *iter = malloc(sizeof(multiset_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
ms_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void ms_iter_free(multiset_iter *iter) { free(iter); }
|
||||
void ms_iter_init(multiset_iter *iter, multiset *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->cursor = 0;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = ms_empty(target);
|
||||
if (!ms_empty(target))
|
||||
{
|
||||
for (size_t i = 0; i < target->capacity; i++)
|
||||
{
|
||||
if (target->buffer[i].state == CMC_ES_FILLED)
|
||||
{
|
||||
iter->first = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
iter->cursor = iter->first;
|
||||
for (size_t i = target->capacity; i > 0; i--)
|
||||
{
|
||||
if (target->buffer[i - 1].state == CMC_ES_FILLED)
|
||||
{
|
||||
iter->last = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool ms_iter_start(multiset_iter *iter) { return ms_empty(iter->target) || iter->start; }
|
||||
bool ms_iter_end(multiset_iter *iter) { return ms_empty(iter->target) || iter->end; }
|
||||
void ms_iter_to_start(multiset_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->first;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = ms_empty(iter->target);
|
||||
}
|
||||
void ms_iter_to_end(multiset_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->last;
|
||||
iter->index = iter->target->count - 1;
|
||||
iter->start = ms_empty(iter->target);
|
||||
iter->end = true;
|
||||
}
|
||||
bool ms_iter_next(multiset_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
multiset_entry *scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (iter->cursor == iter->last)
|
||||
iter->end = true;
|
||||
else
|
||||
{
|
||||
iter->index++;
|
||||
while (1)
|
||||
{
|
||||
iter->cursor++;
|
||||
scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (scan->state == CMC_ES_FILLED)
|
||||
break;
|
||||
}
|
||||
}
|
||||
iter->start = ms_empty(iter->target);
|
||||
return true;
|
||||
}
|
||||
bool ms_iter_prev(multiset_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
multiset_entry *scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (iter->cursor == iter->first)
|
||||
iter->start = true;
|
||||
else
|
||||
{
|
||||
iter->index--;
|
||||
while (1)
|
||||
{
|
||||
iter->cursor--;
|
||||
scan = &(iter->target->buffer[iter->cursor]);
|
||||
if (scan->state == CMC_ES_FILLED)
|
||||
break;
|
||||
}
|
||||
}
|
||||
iter->end = ms_empty(iter->target);
|
||||
return true;
|
||||
}
|
||||
size_t ms_iter_value(multiset_iter *iter)
|
||||
{
|
||||
if (ms_empty(iter->target))
|
||||
return ms_impl_default_value();
|
||||
return iter->target->buffer[iter->cursor].value;
|
||||
}
|
||||
size_t ms_iter_multiplicity(multiset_iter *iter)
|
||||
{
|
||||
if (ms_empty(iter->target))
|
||||
return 0;
|
||||
return iter->target->buffer[iter->cursor].multiplicity;
|
||||
}
|
||||
size_t ms_iter_index(multiset_iter *iter) { return iter->index; }
|
||||
static multiset_entry *ms_impl_insert_and_return(multiset *_set_, size_t element, bool *new_node)
|
||||
{
|
||||
*new_node = false;
|
||||
multiset_entry *entry = ms_impl_get_entry(_set_, element);
|
||||
if (entry != NULL)
|
||||
return entry;
|
||||
*new_node = true;
|
||||
if ((double)_set_->capacity * _set_->load <= (double)_set_->count)
|
||||
{
|
||||
if (!ms_impl_grow(_set_))
|
||||
return NULL;
|
||||
}
|
||||
size_t hash = _set_->hash(element);
|
||||
size_t original_pos = hash % _set_->capacity;
|
||||
size_t pos = original_pos;
|
||||
size_t curr_mul = 1;
|
||||
multiset_entry *target = &(_set_->buffer[pos]);
|
||||
if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED)
|
||||
{
|
||||
target->value = element;
|
||||
target->multiplicity = curr_mul;
|
||||
target->dist = pos - original_pos;
|
||||
target->state = CMC_ES_FILLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
pos++;
|
||||
target = &(_set_->buffer[pos % _set_->capacity]);
|
||||
if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED)
|
||||
{
|
||||
target->value = element;
|
||||
target->multiplicity = curr_mul;
|
||||
target->dist = pos - original_pos;
|
||||
target->state = CMC_ES_FILLED;
|
||||
break;
|
||||
}
|
||||
else if (target->dist < pos - original_pos)
|
||||
{
|
||||
size_t tmp = target->value;
|
||||
size_t tmp_dist = target->dist;
|
||||
size_t tmp_mul = target->multiplicity;
|
||||
target->value = element;
|
||||
target->dist = pos - original_pos;
|
||||
target->multiplicity = curr_mul;
|
||||
element = tmp;
|
||||
original_pos = pos - tmp_dist;
|
||||
curr_mul = tmp_mul;
|
||||
}
|
||||
}
|
||||
}
|
||||
_set_->count++;
|
||||
return target;
|
||||
}
|
||||
static bool ms_impl_grow(multiset *_set_)
|
||||
{
|
||||
size_t new_size = ms_impl_calculate_size(_set_->capacity + _set_->capacity / 2);
|
||||
multiset *_new_set_ = ms_new(new_size, _set_->load, _set_->cmp, _set_->hash);
|
||||
if (!_new_set_)
|
||||
return false;
|
||||
multiset_iter iter;
|
||||
for (ms_iter_init(&iter, _set_); !ms_iter_end(&iter); ms_iter_next(&iter))
|
||||
{
|
||||
ms_insert_many(_new_set_, ms_iter_value(&iter), ms_iter_multiplicity(&iter));
|
||||
}
|
||||
if (_set_->count != _new_set_->count)
|
||||
{
|
||||
ms_free(_new_set_);
|
||||
return false;
|
||||
}
|
||||
multiset_entry *tmp = _set_->buffer;
|
||||
_set_->buffer = _new_set_->buffer;
|
||||
_new_set_->buffer = tmp;
|
||||
_set_->capacity = _new_set_->capacity;
|
||||
ms_free(_new_set_);
|
||||
return true;
|
||||
}
|
||||
static multiset_entry *ms_impl_get_entry(multiset *_set_, size_t element)
|
||||
{
|
||||
size_t hash = _set_->hash(element);
|
||||
size_t pos = hash % _set_->capacity;
|
||||
multiset_entry *target = &(_set_->buffer[pos]);
|
||||
while (target->state == CMC_ES_FILLED || target->state == CMC_ES_DELETED)
|
||||
{
|
||||
if (_set_->cmp(target->value, element) == 0)
|
||||
return target;
|
||||
pos++;
|
||||
target = &(_set_->buffer[pos % _set_->capacity]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static size_t ms_impl_calculate_size(size_t required)
|
||||
{
|
||||
const size_t count = sizeof(cmc_hashtable_primes) / sizeof(cmc_hashtable_primes[0]);
|
||||
if (cmc_hashtable_primes[count - 1] < required)
|
||||
return required;
|
||||
size_t i = 0;
|
||||
while (cmc_hashtable_primes[i] < required)
|
||||
i++;
|
||||
return cmc_hashtable_primes[i];
|
||||
}
|
||||
static multiset_iter ms_impl_it_start(multiset *_set_)
|
||||
{
|
||||
multiset_iter iter;
|
||||
ms_iter_init(&iter, _set_);
|
||||
return iter;
|
||||
}
|
||||
static multiset_iter ms_impl_it_end(multiset *_set_)
|
||||
{
|
||||
multiset_iter iter;
|
||||
ms_iter_init(&iter, _set_);
|
||||
ms_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
#include "cmc/queue.h"
|
||||
|
||||
//QUEUE_GENERATE(q, queue, , size_t)
|
||||
|
||||
typedef struct queue_s
|
||||
{
|
||||
size_t *buffer;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
size_t front;
|
||||
size_t rear;
|
||||
struct queue_iter_s (*it_start)(struct queue_s *);
|
||||
struct queue_iter_s (*it_end)(struct queue_s *);
|
||||
} queue, *queue_ptr;
|
||||
typedef struct queue_iter_s
|
||||
{
|
||||
struct queue_s *target;
|
||||
size_t cursor;
|
||||
size_t index;
|
||||
bool start;
|
||||
bool end;
|
||||
} queue_iter, *queue_iter_ptr;
|
||||
queue *q_new(size_t capacity);
|
||||
void q_clear(queue *_queue_);
|
||||
void q_free(queue *_queue_);
|
||||
bool q_enqueue(queue *_queue_, size_t element);
|
||||
bool q_dequeue(queue *_queue_);
|
||||
bool q_enqueue_if(queue *_queue_, size_t element, bool condition);
|
||||
bool q_dequeue_if(queue *_queue_, bool condition);
|
||||
size_t q_peek(queue *_queue_);
|
||||
bool q_contains(queue *_queue_, size_t element, int (*comparator)(size_t, size_t));
|
||||
bool q_empty(queue *_queue_);
|
||||
bool q_full(queue *_queue_);
|
||||
size_t q_count(queue *_queue_);
|
||||
size_t q_capacity(queue *_queue_);
|
||||
queue_iter *q_iter_new(queue *target);
|
||||
void q_iter_free(queue_iter *iter);
|
||||
void q_iter_init(queue_iter *iter, queue *target);
|
||||
bool q_iter_start(queue_iter *iter);
|
||||
bool q_iter_end(queue_iter *iter);
|
||||
void q_iter_to_start(queue_iter *iter);
|
||||
void q_iter_to_end(queue_iter *iter);
|
||||
bool q_iter_next(queue_iter *iter);
|
||||
bool q_iter_prev(queue_iter *iter);
|
||||
size_t q_iter_value(queue_iter *iter);
|
||||
size_t *q_iter_rvalue(queue_iter *iter);
|
||||
size_t q_iter_index(queue_iter *iter);
|
||||
static inline size_t q_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
static bool q_impl_grow(queue *_queue_);
|
||||
static queue_iter q_impl_it_start(queue *_queue_);
|
||||
static queue_iter q_impl_it_end(queue *_queue_);
|
||||
queue *q_new(size_t capacity)
|
||||
{
|
||||
if (capacity < 1)
|
||||
return NULL;
|
||||
queue *_queue_ = malloc(sizeof(queue));
|
||||
if (!_queue_)
|
||||
return NULL;
|
||||
_queue_->buffer = malloc(sizeof(size_t) * capacity);
|
||||
if (!_queue_->buffer)
|
||||
{
|
||||
free(_queue_);
|
||||
return NULL;
|
||||
}
|
||||
memset(_queue_->buffer, 0, sizeof(size_t) * capacity);
|
||||
_queue_->capacity = capacity;
|
||||
_queue_->count = 0;
|
||||
_queue_->front = 0;
|
||||
_queue_->rear = 0;
|
||||
_queue_->it_start = q_impl_it_start;
|
||||
_queue_->it_end = q_impl_it_end;
|
||||
return _queue_;
|
||||
}
|
||||
void q_clear(queue *_queue_)
|
||||
{
|
||||
memset(_queue_->buffer, 0, sizeof(size_t) * _queue_->capacity);
|
||||
_queue_->count = 0;
|
||||
_queue_->front = 0;
|
||||
_queue_->rear = 0;
|
||||
}
|
||||
void q_free(queue *_queue_)
|
||||
{
|
||||
free(_queue_->buffer);
|
||||
free(_queue_);
|
||||
}
|
||||
bool q_enqueue(queue *_queue_, size_t element)
|
||||
{
|
||||
if (q_full(_queue_))
|
||||
{
|
||||
if (!q_impl_grow(_queue_))
|
||||
return false;
|
||||
}
|
||||
_queue_->buffer[_queue_->rear] = element;
|
||||
_queue_->rear = (_queue_->rear == _queue_->capacity - 1) ? 0 : _queue_->rear + 1;
|
||||
_queue_->count++;
|
||||
return true;
|
||||
}
|
||||
bool q_dequeue(queue *_queue_)
|
||||
{
|
||||
if (q_empty(_queue_))
|
||||
return false;
|
||||
_queue_->buffer[_queue_->front] = q_impl_default_value();
|
||||
_queue_->front = (_queue_->front == _queue_->capacity - 1) ? 0 : _queue_->front + 1;
|
||||
_queue_->count--;
|
||||
return true;
|
||||
}
|
||||
bool q_enqueue_if(queue *_queue_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return q_enqueue(_queue_, element);
|
||||
return false;
|
||||
}
|
||||
bool q_dequeue_if(queue *_queue_, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return q_dequeue(_queue_);
|
||||
return false;
|
||||
}
|
||||
size_t q_peek(queue *_queue_)
|
||||
{
|
||||
if (q_empty(_queue_))
|
||||
return q_impl_default_value();
|
||||
return _queue_->buffer[_queue_->front];
|
||||
}
|
||||
bool q_contains(queue *_queue_, size_t element, int (*comparator)(size_t, size_t))
|
||||
{
|
||||
for (size_t i = _queue_->front, j = 0; j < _queue_->count; i = (i + 1) % _queue_->count, j++)
|
||||
{
|
||||
if (comparator(_queue_->buffer[i], element) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool q_empty(queue *_queue_) { return _queue_->count == 0; }
|
||||
bool q_full(queue *_queue_) { return _queue_->count >= _queue_->capacity; }
|
||||
size_t q_count(queue *_queue_) { return _queue_->count; }
|
||||
size_t q_capacity(queue *_queue_) { return _queue_->capacity; }
|
||||
queue_iter *q_iter_new(queue *target)
|
||||
{
|
||||
queue_iter *iter = malloc(sizeof(queue_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
q_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void q_iter_free(queue_iter *iter) { free(iter); }
|
||||
void q_iter_init(queue_iter *iter, queue *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->cursor = target->front;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = q_empty(target);
|
||||
}
|
||||
bool q_iter_start(queue_iter *iter) { return q_empty(iter->target) || iter->start; }
|
||||
bool q_iter_end(queue_iter *iter) { return q_empty(iter->target) || iter->end; }
|
||||
void q_iter_to_start(queue_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->target->front;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = q_empty(iter->target);
|
||||
}
|
||||
void q_iter_to_end(queue_iter *iter)
|
||||
{
|
||||
if (q_empty(iter->target))
|
||||
iter->cursor = 0;
|
||||
else
|
||||
iter->cursor = (iter->target->rear == 0) ? iter->target->capacity - 1 : iter->target->rear - 1;
|
||||
iter->index = iter->target->count - 1;
|
||||
iter->start = q_empty(iter->target);
|
||||
iter->end = true;
|
||||
}
|
||||
bool q_iter_next(queue_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
iter->start = q_empty(iter->target);
|
||||
if (iter->index == iter->target->count - 1)
|
||||
iter->end = true;
|
||||
else
|
||||
{
|
||||
iter->cursor = (iter->cursor + 1) % (iter->target->capacity);
|
||||
iter->index++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool q_iter_prev(queue_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
iter->end = q_empty(iter->target);
|
||||
if (iter->index == 0)
|
||||
iter->start = true;
|
||||
else
|
||||
{
|
||||
iter->cursor = (iter->cursor == 0) ? iter->target->capacity - 1 : iter->cursor - 1;
|
||||
iter->index--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
size_t q_iter_value(queue_iter *iter)
|
||||
{
|
||||
if (q_empty(iter->target))
|
||||
return q_impl_default_value();
|
||||
return iter->target->buffer[iter->cursor];
|
||||
}
|
||||
size_t *q_iter_rvalue(queue_iter *iter)
|
||||
{
|
||||
if (q_empty(iter->target))
|
||||
return NULL;
|
||||
return &(iter->target->buffer[iter->cursor]);
|
||||
}
|
||||
size_t q_iter_index(queue_iter *iter) { return iter->index; }
|
||||
static bool q_impl_grow(queue *_queue_)
|
||||
{
|
||||
size_t new_capacity = _queue_->capacity * 2;
|
||||
size_t *new_buffer = malloc(sizeof(size_t) * new_capacity);
|
||||
if (!new_buffer)
|
||||
return false;
|
||||
for (size_t i = _queue_->front, j = 0; j < _queue_->count; i = (i + 1) % _queue_->capacity, j++)
|
||||
{
|
||||
new_buffer[j] = _queue_->buffer[i];
|
||||
}
|
||||
free(_queue_->buffer);
|
||||
_queue_->buffer = new_buffer;
|
||||
_queue_->capacity = new_capacity;
|
||||
_queue_->front = 0;
|
||||
_queue_->rear = _queue_->count;
|
||||
return true;
|
||||
}
|
||||
static queue_iter q_impl_it_start(queue *_queue_)
|
||||
{
|
||||
queue_iter iter;
|
||||
q_iter_init(&iter, _queue_);
|
||||
q_iter_to_start(&iter);
|
||||
return iter;
|
||||
}
|
||||
static queue_iter q_impl_it_end(queue *_queue_)
|
||||
{
|
||||
queue_iter iter;
|
||||
q_iter_init(&iter, _queue_);
|
||||
q_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
#include "cmc/stack.h"
|
||||
|
||||
//STACK_GENERATE(s, stack, , size_t)
|
||||
|
||||
typedef struct stack_s
|
||||
{
|
||||
size_t *buffer;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
struct stack_iter_s (*it_start)(struct stack_s *);
|
||||
struct stack_iter_s (*it_end)(struct stack_s *);
|
||||
} stack, *stack_ptr;
|
||||
typedef struct stack_iter_s
|
||||
{
|
||||
struct stack_s *target;
|
||||
size_t cursor;
|
||||
bool start;
|
||||
bool end;
|
||||
} stack_iter, *stack_iter_ptr;
|
||||
stack *s_new(size_t capacity);
|
||||
void s_clear(stack *_stack_);
|
||||
void s_free(stack *_stack_);
|
||||
bool s_push(stack *_stack_, size_t element);
|
||||
bool s_pop(stack *_stack_);
|
||||
bool s_push_if(stack *_stack_, size_t element, bool condition);
|
||||
bool s_pop_if(stack *_stack_, bool condition);
|
||||
size_t s_top(stack *_stack_);
|
||||
bool s_contains(stack *_stack_, size_t element, int (*comparator)(size_t, size_t));
|
||||
bool s_empty(stack *_stack_);
|
||||
bool s_full(stack *_stack_);
|
||||
size_t s_count(stack *_stack_);
|
||||
size_t s_capacity(stack *_stack_);
|
||||
stack_iter *s_iter_new(stack *target);
|
||||
void s_iter_free(stack_iter *iter);
|
||||
void s_iter_init(stack_iter *iter, stack *target);
|
||||
bool s_iter_start(stack_iter *iter);
|
||||
bool s_iter_end(stack_iter *iter);
|
||||
void s_iter_to_start(stack_iter *iter);
|
||||
void s_iter_to_end(stack_iter *iter);
|
||||
bool s_iter_next(stack_iter *iter);
|
||||
bool s_iter_prev(stack_iter *iter);
|
||||
size_t s_iter_value(stack_iter *iter);
|
||||
size_t *s_iter_rvalue(stack_iter *iter);
|
||||
size_t s_iter_index(stack_iter *iter);
|
||||
static inline size_t s_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
static bool s_impl_grow(stack *_stack_);
|
||||
static stack_iter s_impl_it_start(stack *_stack_);
|
||||
static stack_iter s_impl_it_end(stack *_stack_);
|
||||
stack *s_new(size_t capacity)
|
||||
{
|
||||
if (capacity < 1)
|
||||
return NULL;
|
||||
stack *_stack_ = malloc(sizeof(stack));
|
||||
if (!_stack_)
|
||||
return NULL;
|
||||
_stack_->buffer = malloc(sizeof(size_t) * capacity);
|
||||
if (!_stack_->buffer)
|
||||
{
|
||||
free(_stack_);
|
||||
return NULL;
|
||||
}
|
||||
memset(_stack_->buffer, 0, sizeof(size_t) * capacity);
|
||||
_stack_->capacity = capacity;
|
||||
_stack_->count = 0;
|
||||
_stack_->it_start = s_impl_it_start;
|
||||
_stack_->it_end = s_impl_it_end;
|
||||
return _stack_;
|
||||
}
|
||||
void s_clear(stack *_stack_)
|
||||
{
|
||||
memset(_stack_->buffer, 0, sizeof(size_t) * _stack_->capacity);
|
||||
_stack_->count = 0;
|
||||
}
|
||||
void s_free(stack *_stack_)
|
||||
{
|
||||
free(_stack_->buffer);
|
||||
free(_stack_);
|
||||
}
|
||||
bool s_push(stack *_stack_, size_t element)
|
||||
{
|
||||
if (s_full(_stack_))
|
||||
{
|
||||
if (!s_impl_grow(_stack_))
|
||||
return false;
|
||||
}
|
||||
_stack_->buffer[_stack_->count++] = element;
|
||||
return true;
|
||||
}
|
||||
bool s_pop(stack *_stack_)
|
||||
{
|
||||
if (s_empty(_stack_))
|
||||
return false;
|
||||
_stack_->buffer[--_stack_->count] = s_impl_default_value();
|
||||
return true;
|
||||
}
|
||||
bool s_push_if(stack *_stack_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return s_push(_stack_, element);
|
||||
return false;
|
||||
}
|
||||
bool s_pop_if(stack *_stack_, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return s_pop(_stack_);
|
||||
return false;
|
||||
}
|
||||
size_t s_top(stack *_stack_)
|
||||
{
|
||||
if (s_empty(_stack_))
|
||||
return s_impl_default_value();
|
||||
return _stack_->buffer[_stack_->count - 1];
|
||||
}
|
||||
bool s_contains(stack *_stack_, size_t element, int (*comparator)(size_t, size_t))
|
||||
{
|
||||
for (size_t i = 0; i < _stack_->count; i++)
|
||||
{
|
||||
if (comparator(_stack_->buffer[i], element) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool s_empty(stack *_stack_) { return _stack_->count == 0; }
|
||||
bool s_full(stack *_stack_) { return _stack_->count >= _stack_->capacity; }
|
||||
size_t s_count(stack *_stack_) { return _stack_->count; }
|
||||
size_t s_capacity(stack *_stack_) { return _stack_->capacity; }
|
||||
stack_iter *s_iter_new(stack *target)
|
||||
{
|
||||
stack_iter *iter = malloc(sizeof(stack_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
s_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void s_iter_free(stack_iter *iter) { free(iter); }
|
||||
void s_iter_init(stack_iter *iter, stack *target)
|
||||
{
|
||||
iter->target = target;
|
||||
if (s_empty(target))
|
||||
iter->cursor = 0;
|
||||
else
|
||||
iter->cursor = iter->target->count - 1;
|
||||
iter->start = true;
|
||||
iter->end = s_empty(target);
|
||||
}
|
||||
bool s_iter_start(stack_iter *iter) { return s_empty(iter->target) || iter->start; }
|
||||
bool s_iter_end(stack_iter *iter) { return s_empty(iter->target) || iter->end; }
|
||||
void s_iter_to_start(stack_iter *iter)
|
||||
{
|
||||
iter->cursor = iter->target->count - 1;
|
||||
iter->start = true;
|
||||
iter->end = s_empty(iter->target);
|
||||
}
|
||||
void s_iter_to_end(stack_iter *iter)
|
||||
{
|
||||
iter->cursor = 0;
|
||||
iter->start = s_empty(iter->target);
|
||||
iter->end = true;
|
||||
}
|
||||
bool s_iter_next(stack_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
iter->start = s_empty(iter->target);
|
||||
if (iter->cursor == 0)
|
||||
{
|
||||
iter->end = true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
iter->cursor--;
|
||||
return true;
|
||||
}
|
||||
bool s_iter_prev(stack_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
iter->end = s_empty(iter->target);
|
||||
if (iter->cursor == iter->target->count - 1)
|
||||
{
|
||||
iter->start = true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
iter->cursor++;
|
||||
return true;
|
||||
}
|
||||
size_t s_iter_value(stack_iter *iter)
|
||||
{
|
||||
if (s_empty(iter->target))
|
||||
return s_impl_default_value();
|
||||
return iter->target->buffer[iter->cursor];
|
||||
}
|
||||
size_t *s_iter_rvalue(stack_iter *iter)
|
||||
{
|
||||
if (s_empty(iter->target))
|
||||
return NULL;
|
||||
return &(iter->target->buffer[iter->cursor]);
|
||||
}
|
||||
size_t s_iter_index(stack_iter *iter)
|
||||
{
|
||||
if (s_empty(iter->target))
|
||||
return 0;
|
||||
return iter->target->count - 1 - iter->cursor;
|
||||
}
|
||||
static bool s_impl_grow(stack *_stack_)
|
||||
{
|
||||
size_t new_capacity = _stack_->capacity * 2;
|
||||
size_t *new_buffer = realloc(_stack_->buffer, sizeof(size_t) * new_capacity);
|
||||
if (!new_buffer)
|
||||
return false;
|
||||
_stack_->buffer = new_buffer;
|
||||
_stack_->capacity = new_capacity;
|
||||
return true;
|
||||
}
|
||||
static stack_iter s_impl_it_start(stack *_stack_)
|
||||
{
|
||||
stack_iter iter;
|
||||
s_iter_init(&iter, _stack_);
|
||||
s_iter_to_start(&iter);
|
||||
return iter;
|
||||
}
|
||||
static stack_iter s_impl_it_end(stack *_stack_)
|
||||
{
|
||||
stack_iter iter;
|
||||
s_iter_init(&iter, _stack_);
|
||||
s_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,599 @@
|
||||
#include "cmc/treemap.h"
|
||||
|
||||
//TREEMAP_GENERATE(tm, treemap, , size_t, size_t)
|
||||
|
||||
typedef struct treemap_s
|
||||
{
|
||||
struct treemap_node_s *root;
|
||||
size_t count;
|
||||
int (*cmp)(size_t, size_t);
|
||||
struct treemap_iter_s (*it_start)(struct treemap_s *);
|
||||
struct treemap_iter_s (*it_end)(struct treemap_s *);
|
||||
} treemap, *treemap_ptr;
|
||||
typedef struct treemap_node_s
|
||||
{
|
||||
size_t key;
|
||||
size_t value;
|
||||
unsigned char height;
|
||||
struct treemap_node_s *right;
|
||||
struct treemap_node_s *left;
|
||||
struct treemap_node_s *parent;
|
||||
} treemap_node, *treemap_node_ptr;
|
||||
typedef struct treemap_iter_s
|
||||
{
|
||||
struct treemap_s *target;
|
||||
struct treemap_node_s *cursor;
|
||||
struct treemap_node_s *first;
|
||||
struct treemap_node_s *last;
|
||||
size_t index;
|
||||
bool start;
|
||||
bool end;
|
||||
} treemap_iter, *treemap_iter_ptr;
|
||||
treemap *tm_new(int (*compare)(size_t, size_t));
|
||||
void tm_clear(treemap *_map_);
|
||||
void tm_free(treemap *_map_);
|
||||
bool tm_insert(treemap *_map_, size_t key, size_t value);
|
||||
bool tm_remove(treemap *_map_, size_t key, size_t *value);
|
||||
bool tm_insert_if(treemap *_map_, size_t key, size_t value, bool condition);
|
||||
bool tm_remove_if(treemap *_map_, size_t key, size_t *value, bool condition);
|
||||
bool tm_max(treemap *_map_, size_t *key, size_t *value);
|
||||
bool tm_min(treemap *_map_, size_t *key, size_t *value);
|
||||
size_t tm_get(treemap *_map_, size_t key);
|
||||
size_t *tm_get_ref(treemap *_map_, size_t key);
|
||||
bool tm_contains(treemap *_map_, size_t key);
|
||||
bool tm_empty(treemap *_map_);
|
||||
size_t tm_count(treemap *_map_);
|
||||
treemap_iter *tm_iter_new(treemap *target);
|
||||
void tm_iter_free(treemap_iter *iter);
|
||||
void tm_iter_init(treemap_iter *iter, treemap *target);
|
||||
bool tm_iter_start(treemap_iter *iter);
|
||||
bool tm_iter_end(treemap_iter *iter);
|
||||
void tm_iter_to_start(treemap_iter *iter);
|
||||
void tm_iter_to_end(treemap_iter *iter);
|
||||
bool tm_iter_next(treemap_iter *iter);
|
||||
bool tm_iter_prev(treemap_iter *iter);
|
||||
size_t tm_iter_key(treemap_iter *iter);
|
||||
size_t tm_iter_value(treemap_iter *iter);
|
||||
size_t *tm_iter_rvalue(treemap_iter *iter);
|
||||
size_t tm_iter_index(treemap_iter *iter);
|
||||
static inline size_t tm_impl_default_key(void)
|
||||
{
|
||||
size_t _empty_key_;
|
||||
memset(&_empty_key_, 0, sizeof(size_t));
|
||||
return _empty_key_;
|
||||
}
|
||||
static inline size_t tm_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
static treemap_node *tm_impl_new_node(size_t key, size_t value);
|
||||
static treemap_node *tm_impl_get_node(treemap *_map_, size_t key);
|
||||
static unsigned char tm_impl_h(treemap_node *node);
|
||||
static unsigned char tm_impl_hupdate(treemap_node *node);
|
||||
static void tm_impl_rotate_right(treemap_node **Z);
|
||||
static void tm_impl_rotate_left(treemap_node **Z);
|
||||
static void tm_impl_rebalance(treemap *_map_, treemap_node *node);
|
||||
static treemap_iter tm_impl_it_start(treemap *_map_);
|
||||
static treemap_iter tm_impl_it_end(treemap *_map_);
|
||||
treemap *tm_new(int (*compare)(size_t, size_t))
|
||||
{
|
||||
treemap *_map_ = malloc(sizeof(treemap));
|
||||
if (!_map_)
|
||||
return NULL;
|
||||
_map_->count = 0;
|
||||
_map_->root = NULL;
|
||||
_map_->cmp = compare;
|
||||
_map_->it_start = tm_impl_it_start;
|
||||
_map_->it_end = tm_impl_it_end;
|
||||
return _map_;
|
||||
}
|
||||
void tm_clear(treemap *_map_)
|
||||
{
|
||||
treemap_node *scan = _map_->root;
|
||||
treemap_node *up = NULL;
|
||||
while (scan != NULL)
|
||||
{
|
||||
if (scan->left != NULL)
|
||||
{
|
||||
treemap_node *left = scan->left;
|
||||
scan->left = up;
|
||||
up = scan;
|
||||
scan = left;
|
||||
}
|
||||
else if (scan->right != NULL)
|
||||
{
|
||||
treemap_node *right = scan->right;
|
||||
scan->left = up;
|
||||
scan->right = NULL;
|
||||
up = scan;
|
||||
scan = right;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (up == NULL)
|
||||
{
|
||||
free(scan);
|
||||
scan = NULL;
|
||||
}
|
||||
while (up != NULL)
|
||||
{
|
||||
free(scan);
|
||||
if (up->right != NULL)
|
||||
{
|
||||
scan = up->right;
|
||||
up->right = NULL;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
scan = up;
|
||||
up = up->left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_map_->count = 0;
|
||||
_map_->root = NULL;
|
||||
}
|
||||
void tm_free(treemap *_map_)
|
||||
{
|
||||
tm_clear(_map_);
|
||||
free(_map_);
|
||||
}
|
||||
bool tm_insert(treemap *_map_, size_t key, size_t value)
|
||||
{
|
||||
if (tm_empty(_map_))
|
||||
{
|
||||
_map_->root = tm_impl_new_node(key, value);
|
||||
if (!_map_->root)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
treemap_node *scan = _map_->root;
|
||||
treemap_node *parent = scan;
|
||||
while (scan != NULL)
|
||||
{
|
||||
parent = scan;
|
||||
if (_map_->cmp(scan->key, key) > 0)
|
||||
scan = scan->left;
|
||||
else if (_map_->cmp(scan->key, key) < 0)
|
||||
scan = scan->right;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
treemap_node *node;
|
||||
if (_map_->cmp(parent->key, key) > 0)
|
||||
{
|
||||
parent->left = tm_impl_new_node(key, value);
|
||||
if (!parent->left)
|
||||
return false;
|
||||
parent->left->parent = parent;
|
||||
node = parent->left;
|
||||
}
|
||||
else
|
||||
{
|
||||
parent->right = tm_impl_new_node(key, value);
|
||||
if (!parent->right)
|
||||
return false;
|
||||
parent->right->parent = parent;
|
||||
node = parent->right;
|
||||
}
|
||||
tm_impl_rebalance(_map_, node);
|
||||
}
|
||||
_map_->count++;
|
||||
return true;
|
||||
}
|
||||
bool tm_remove(treemap *_map_, size_t key, size_t *value)
|
||||
{
|
||||
treemap_node *node = tm_impl_get_node(_map_, key);
|
||||
if (!node)
|
||||
return false;
|
||||
*value = node->value;
|
||||
treemap_node *temp = NULL, *unbalanced = NULL;
|
||||
bool is_root = node->parent == NULL;
|
||||
if (node->left == NULL && node->right == NULL)
|
||||
{
|
||||
if (is_root)
|
||||
_map_->root = NULL;
|
||||
else
|
||||
{
|
||||
unbalanced = node->parent;
|
||||
if (node->parent->right == node)
|
||||
node->parent->right = NULL;
|
||||
else
|
||||
node->parent->left = NULL;
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
else if (node->left == NULL)
|
||||
{
|
||||
if (is_root)
|
||||
{
|
||||
_map_->root = node->right;
|
||||
_map_->root->parent = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
unbalanced = node->parent;
|
||||
node->right->parent = node->parent;
|
||||
if (node->parent->right == node)
|
||||
node->parent->right = node->right;
|
||||
else
|
||||
node->parent->left = node->right;
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
else if (node->right == NULL)
|
||||
{
|
||||
if (is_root)
|
||||
{
|
||||
_map_->root = node->left;
|
||||
_map_->root->parent = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
unbalanced = node->parent;
|
||||
node->left->parent = node->parent;
|
||||
if (node->parent->right == node)
|
||||
node->parent->right = node->left;
|
||||
else
|
||||
node->parent->left = node->left;
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = node->right;
|
||||
while (temp->left != NULL)
|
||||
temp = temp->left;
|
||||
size_t temp_key = temp->key;
|
||||
size_t temp_val = temp->value;
|
||||
unbalanced = temp->parent;
|
||||
if (temp->left == NULL && temp->right == NULL)
|
||||
{
|
||||
if (temp->parent->right == temp)
|
||||
temp->parent->right = NULL;
|
||||
else
|
||||
temp->parent->left = NULL;
|
||||
}
|
||||
else if (temp->left == NULL)
|
||||
{
|
||||
temp->right->parent = temp->parent;
|
||||
if (temp->parent->right == temp)
|
||||
temp->parent->right = temp->right;
|
||||
else
|
||||
temp->parent->left = temp->right;
|
||||
}
|
||||
else if (temp->right == NULL)
|
||||
{
|
||||
temp->left->parent = temp->parent;
|
||||
if (temp->parent->right == temp)
|
||||
temp->parent->right = temp->left;
|
||||
else
|
||||
temp->parent->left = temp->left;
|
||||
}
|
||||
free(temp);
|
||||
node->key = temp_key;
|
||||
node->value = temp_val;
|
||||
}
|
||||
if (unbalanced != NULL)
|
||||
tm_impl_rebalance(_map_, unbalanced);
|
||||
_map_->count--;
|
||||
if (_map_->count == 0)
|
||||
_map_->root = NULL;
|
||||
return true;
|
||||
}
|
||||
bool tm_insert_if(treemap *_map_, size_t key, size_t value, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return tm_insert(_map_, key, value);
|
||||
return false;
|
||||
}
|
||||
bool tm_remove_if(treemap *_map_, size_t key, size_t *value, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return tm_remove(_map_, key, value);
|
||||
return false;
|
||||
}
|
||||
bool tm_max(treemap *_map_, size_t *key, size_t *value)
|
||||
{
|
||||
if (tm_empty(_map_))
|
||||
return false;
|
||||
treemap_node *scan = _map_->root;
|
||||
while (scan->right != NULL)
|
||||
scan = scan->right;
|
||||
*key = scan->key;
|
||||
*value = scan->value;
|
||||
return true;
|
||||
}
|
||||
bool tm_min(treemap *_map_, size_t *key, size_t *value)
|
||||
{
|
||||
if (tm_empty(_map_))
|
||||
return false;
|
||||
treemap_node *scan = _map_->root;
|
||||
while (scan->left != NULL)
|
||||
scan = scan->left;
|
||||
*key = scan->key;
|
||||
*value = scan->value;
|
||||
return true;
|
||||
}
|
||||
size_t tm_get(treemap *_map_, size_t key)
|
||||
{
|
||||
treemap_node *node = tm_impl_get_node(_map_, key);
|
||||
if (!node)
|
||||
return tm_impl_default_value();
|
||||
return node->value;
|
||||
}
|
||||
size_t *tm_get_ref(treemap *_map_, size_t key)
|
||||
{
|
||||
treemap_node *node = tm_impl_get_node(_map_, key);
|
||||
if (!node)
|
||||
return NULL;
|
||||
return &(node->value);
|
||||
}
|
||||
bool tm_contains(treemap *_map_, size_t key)
|
||||
{
|
||||
treemap_node *scan = _map_->root;
|
||||
while (scan != NULL)
|
||||
{
|
||||
if (_map_->cmp(scan->key, key) > 0)
|
||||
scan = scan->left;
|
||||
else if (_map_->cmp(scan->key, key) < 0)
|
||||
scan = scan->right;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool tm_empty(treemap *_map_) { return _map_->count == 0; }
|
||||
size_t tm_count(treemap *_map_) { return _map_->count; }
|
||||
treemap_iter *tm_iter_new(treemap *target)
|
||||
{
|
||||
treemap_iter *iter = malloc(sizeof(treemap_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
tm_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void tm_iter_free(treemap_iter *iter) { free(iter); }
|
||||
void tm_iter_init(treemap_iter *iter, treemap *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = tm_empty(target);
|
||||
iter->cursor = target->root;
|
||||
while (iter->cursor->left != NULL)
|
||||
iter->cursor = iter->cursor->left;
|
||||
iter->first = iter->cursor;
|
||||
iter->last = target->root;
|
||||
while (iter->last->right != NULL)
|
||||
iter->last = iter->last->right;
|
||||
}
|
||||
bool tm_iter_start(treemap_iter *iter) { return tm_empty(iter->target) || iter->start; }
|
||||
bool tm_iter_end(treemap_iter *iter) { return tm_empty(iter->target) || iter->end; }
|
||||
void tm_iter_to_start(treemap_iter *iter)
|
||||
{
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = tm_empty(iter->target);
|
||||
iter->cursor = iter->first;
|
||||
}
|
||||
void tm_iter_to_end(treemap_iter *iter)
|
||||
{
|
||||
iter->index = iter->target->count - 1;
|
||||
iter->start = tm_empty(iter->target);
|
||||
iter->end = true;
|
||||
iter->cursor = iter->last;
|
||||
}
|
||||
bool tm_iter_next(treemap_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
iter->start = false;
|
||||
if (iter->cursor->right != NULL)
|
||||
{
|
||||
iter->cursor = iter->cursor->right;
|
||||
while (iter->cursor->left != NULL)
|
||||
iter->cursor = iter->cursor->left;
|
||||
iter->index++;
|
||||
return true;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
if (iter->cursor == iter->last)
|
||||
{
|
||||
iter->end = true;
|
||||
return true;
|
||||
}
|
||||
if (iter->cursor->parent->left == iter->cursor)
|
||||
{
|
||||
iter->cursor = iter->cursor->parent;
|
||||
iter->index++;
|
||||
return true;
|
||||
}
|
||||
iter->cursor = iter->cursor->parent;
|
||||
}
|
||||
}
|
||||
bool tm_iter_prev(treemap_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
iter->end = false;
|
||||
if (iter->cursor->left != NULL)
|
||||
{
|
||||
iter->cursor = iter->cursor->left;
|
||||
while (iter->cursor->right != NULL)
|
||||
iter->cursor = iter->cursor->right;
|
||||
iter->index--;
|
||||
return true;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
if (iter->cursor == iter->first)
|
||||
{
|
||||
iter->start = true;
|
||||
return true;
|
||||
}
|
||||
if (iter->cursor->parent->right == iter->cursor)
|
||||
{
|
||||
iter->cursor = iter->cursor->parent;
|
||||
iter->index--;
|
||||
return true;
|
||||
}
|
||||
iter->cursor = iter->cursor->parent;
|
||||
}
|
||||
}
|
||||
size_t tm_iter_key(treemap_iter *iter)
|
||||
{
|
||||
if (tm_empty(iter->target))
|
||||
return tm_impl_default_key();
|
||||
return iter->cursor->key;
|
||||
}
|
||||
size_t tm_iter_value(treemap_iter *iter)
|
||||
{
|
||||
if (tm_empty(iter->target))
|
||||
return tm_impl_default_value();
|
||||
return iter->cursor->value;
|
||||
}
|
||||
size_t *tm_iter_rvalue(treemap_iter *iter)
|
||||
{
|
||||
if (tm_empty(iter->target))
|
||||
return NULL;
|
||||
return &(iter->cursor->value);
|
||||
}
|
||||
size_t tm_iter_index(treemap_iter *iter) { return iter->index; }
|
||||
static treemap_node *tm_impl_new_node(size_t key, size_t value)
|
||||
{
|
||||
treemap_node *node = malloc(sizeof(treemap_node));
|
||||
if (!node)
|
||||
return NULL;
|
||||
node->key = key;
|
||||
node->value = value;
|
||||
node->right = NULL;
|
||||
node->left = NULL;
|
||||
node->parent = NULL;
|
||||
node->height = 0;
|
||||
return node;
|
||||
}
|
||||
static treemap_node *tm_impl_get_node(treemap *_map_, size_t key)
|
||||
{
|
||||
if (tm_empty(_map_))
|
||||
return NULL;
|
||||
treemap_node *scan = _map_->root;
|
||||
while (scan != NULL)
|
||||
{
|
||||
if (_map_->cmp(scan->key, key) > 0)
|
||||
scan = scan->left;
|
||||
else if (_map_->cmp(scan->key, key) < 0)
|
||||
scan = scan->right;
|
||||
else
|
||||
return scan;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static unsigned char tm_impl_h(treemap_node *node)
|
||||
{
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
return node->height;
|
||||
}
|
||||
static unsigned char tm_impl_hupdate(treemap_node *node)
|
||||
{
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
unsigned char h_l = tm_impl_h(node->left);
|
||||
unsigned char h_r = tm_impl_h(node->right);
|
||||
return 1 + (h_l > h_r ? h_l : h_r);
|
||||
}
|
||||
static void tm_impl_rotate_right(treemap_node **Z)
|
||||
{
|
||||
treemap_node *root = *Z;
|
||||
treemap_node *new_root = root->left;
|
||||
if (root->parent != NULL)
|
||||
{
|
||||
if (root->parent->left == root)
|
||||
root->parent->left = new_root;
|
||||
else
|
||||
root->parent->right = new_root;
|
||||
}
|
||||
new_root->parent = root->parent;
|
||||
root->parent = new_root;
|
||||
root->left = new_root->right;
|
||||
if (root->left)
|
||||
root->left->parent = root;
|
||||
new_root->right = root;
|
||||
root->height = tm_impl_hupdate(root);
|
||||
new_root->height = tm_impl_hupdate(new_root);
|
||||
*Z = new_root;
|
||||
}
|
||||
static void tm_impl_rotate_left(treemap_node **Z)
|
||||
{
|
||||
treemap_node *root = *Z;
|
||||
treemap_node *new_root = root->right;
|
||||
if (root->parent != NULL)
|
||||
{
|
||||
if (root->parent->right == root)
|
||||
root->parent->right = new_root;
|
||||
else
|
||||
root->parent->left = new_root;
|
||||
}
|
||||
new_root->parent = root->parent;
|
||||
root->parent = new_root;
|
||||
root->right = new_root->left;
|
||||
if (root->right)
|
||||
root->right->parent = root;
|
||||
new_root->left = root;
|
||||
root->height = tm_impl_hupdate(root);
|
||||
new_root->height = tm_impl_hupdate(new_root);
|
||||
*Z = new_root;
|
||||
}
|
||||
static void tm_impl_rebalance(treemap *_map_, treemap_node *node)
|
||||
{
|
||||
treemap_node *scan = node, *child = NULL;
|
||||
int balance;
|
||||
bool is_root = false;
|
||||
while (scan != NULL)
|
||||
{
|
||||
if (scan->parent == NULL)
|
||||
is_root = true;
|
||||
scan->height = tm_impl_hupdate(scan);
|
||||
balance = tm_impl_h(scan->right) - tm_impl_h(scan->left);
|
||||
if (balance >= 2)
|
||||
{
|
||||
child = scan->right;
|
||||
if (tm_impl_h(child->right) < tm_impl_h(child->left))
|
||||
tm_impl_rotate_right(&(scan->right));
|
||||
tm_impl_rotate_left(&scan);
|
||||
}
|
||||
else if (balance <= -2)
|
||||
{
|
||||
child = scan->left;
|
||||
if (tm_impl_h(child->left) < tm_impl_h(child->right))
|
||||
tm_impl_rotate_left(&(scan->left));
|
||||
tm_impl_rotate_right(&scan);
|
||||
}
|
||||
if (is_root)
|
||||
{
|
||||
_map_->root = scan;
|
||||
is_root = false;
|
||||
}
|
||||
scan = scan->parent;
|
||||
}
|
||||
}
|
||||
static treemap_iter tm_impl_it_start(treemap *_map_)
|
||||
{
|
||||
treemap_iter iter;
|
||||
tm_iter_init(&iter, _map_);
|
||||
return iter;
|
||||
}
|
||||
static treemap_iter tm_impl_it_end(treemap *_map_)
|
||||
{
|
||||
treemap_iter iter;
|
||||
tm_iter_init(&iter, _map_);
|
||||
tm_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,690 @@
|
||||
#include "cmc/treeset.h"
|
||||
|
||||
//TREESET_GENERATE(ts, treeset, , size_t)
|
||||
|
||||
typedef struct treeset_s
|
||||
{
|
||||
struct treeset_node_s *root;
|
||||
size_t count;
|
||||
int (*cmp)(size_t, size_t);
|
||||
struct treeset_iter_s (*it_start)(struct treeset_s *);
|
||||
struct treeset_iter_s (*it_end)(struct treeset_s *);
|
||||
} treeset, *treeset_ptr;
|
||||
typedef struct treeset_node_s
|
||||
{
|
||||
size_t value;
|
||||
unsigned char height;
|
||||
struct treeset_node_s *right;
|
||||
struct treeset_node_s *left;
|
||||
struct treeset_node_s *parent;
|
||||
} treeset_node, *treeset_node_ptr;
|
||||
typedef struct treeset_iter_s
|
||||
{
|
||||
struct treeset_s *target;
|
||||
struct treeset_node_s *cursor;
|
||||
struct treeset_node_s *first;
|
||||
struct treeset_node_s *last;
|
||||
size_t index;
|
||||
bool start;
|
||||
bool end;
|
||||
} treeset_iter, *treeset_iter_ptr;
|
||||
treeset *ts_new(int (*compare)(size_t, size_t));
|
||||
void ts_clear(treeset *_set_);
|
||||
void ts_free(treeset *_set_);
|
||||
bool ts_insert(treeset *_set_, size_t element);
|
||||
bool ts_remove(treeset *_set_, size_t element);
|
||||
bool ts_insert_if(treeset *_set_, size_t element, bool condition);
|
||||
bool ts_remove_if(treeset *_set_, size_t element, bool condition);
|
||||
bool ts_max(treeset *_set_, size_t *value);
|
||||
bool ts_min(treeset *_set_, size_t *value);
|
||||
bool ts_contains(treeset *_set_, size_t element);
|
||||
bool ts_empty(treeset *_set_);
|
||||
size_t ts_count(treeset *_set_);
|
||||
treeset *ts_union(treeset *_set1_, treeset *_set2_);
|
||||
treeset *ts_intersection(treeset *_set1_, treeset *_set2_);
|
||||
treeset *ts_difference(treeset *_set1_, treeset *_set2_);
|
||||
treeset *ts_symmetric_difference(treeset *_set1_, treeset *_set2_);
|
||||
bool ts_is_subset(treeset *_set1_, treeset *_set2_);
|
||||
bool ts_is_superset(treeset *_set1_, treeset *_set2_);
|
||||
bool ts_is_proper_subset(treeset *_set1_, treeset *_set2_);
|
||||
bool ts_is_proper_superset(treeset *_set1_, treeset *_set2_);
|
||||
bool ts_is_disjointset(treeset *_set1_, treeset *_set2_);
|
||||
treeset_iter *ts_iter_new(treeset *target);
|
||||
void ts_iter_free(treeset_iter *iter);
|
||||
void ts_iter_init(treeset_iter *iter, treeset *target);
|
||||
bool ts_iter_start(treeset_iter *iter);
|
||||
bool ts_iter_end(treeset_iter *iter);
|
||||
void ts_iter_to_start(treeset_iter *iter);
|
||||
void ts_iter_to_end(treeset_iter *iter);
|
||||
bool ts_iter_next(treeset_iter *iter);
|
||||
bool ts_iter_prev(treeset_iter *iter);
|
||||
size_t ts_iter_value(treeset_iter *iter);
|
||||
size_t ts_iter_index(treeset_iter *iter);
|
||||
static inline size_t ts_impl_default_value(void)
|
||||
{
|
||||
size_t _empty_value_;
|
||||
memset(&_empty_value_, 0, sizeof(size_t));
|
||||
return _empty_value_;
|
||||
}
|
||||
static treeset_node *ts_impl_new_node(size_t element);
|
||||
static treeset_node *ts_impl_get_node(treeset *_set_, size_t element);
|
||||
static unsigned char ts_impl_h(treeset_node *node);
|
||||
static unsigned char ts_impl_hupdate(treeset_node *node);
|
||||
static void ts_impl_rotate_right(treeset_node **Z);
|
||||
static void ts_impl_rotate_left(treeset_node **Z);
|
||||
static void ts_impl_rebalance(treeset *_set_, treeset_node *node);
|
||||
static treeset_iter ts_impl_it_start(treeset *_set_);
|
||||
static treeset_iter ts_impl_it_end(treeset *_set_);
|
||||
treeset *ts_new(int (*compare)(size_t, size_t))
|
||||
{
|
||||
treeset *_set_ = malloc(sizeof(treeset));
|
||||
if (!_set_)
|
||||
return NULL;
|
||||
_set_->count = 0;
|
||||
_set_->root = NULL;
|
||||
_set_->cmp = compare;
|
||||
_set_->it_start = ts_impl_it_start;
|
||||
_set_->it_end = ts_impl_it_end;
|
||||
return _set_;
|
||||
}
|
||||
void ts_clear(treeset *_set_)
|
||||
{
|
||||
treeset_node *scan = _set_->root;
|
||||
treeset_node *up = NULL;
|
||||
while (scan != NULL)
|
||||
{
|
||||
if (scan->left != NULL)
|
||||
{
|
||||
treeset_node *left = scan->left;
|
||||
scan->left = up;
|
||||
up = scan;
|
||||
scan = left;
|
||||
}
|
||||
else if (scan->right != NULL)
|
||||
{
|
||||
treeset_node *right = scan->right;
|
||||
scan->left = up;
|
||||
scan->right = NULL;
|
||||
up = scan;
|
||||
scan = right;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (up == NULL)
|
||||
{
|
||||
free(scan);
|
||||
scan = NULL;
|
||||
}
|
||||
while (up != NULL)
|
||||
{
|
||||
free(scan);
|
||||
if (up->right != NULL)
|
||||
{
|
||||
scan = up->right;
|
||||
up->right = NULL;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
scan = up;
|
||||
up = up->left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_set_->count = 0;
|
||||
_set_->root = NULL;
|
||||
}
|
||||
void ts_free(treeset *_set_)
|
||||
{
|
||||
ts_clear(_set_);
|
||||
free(_set_);
|
||||
}
|
||||
bool ts_insert(treeset *_set_, size_t element)
|
||||
{
|
||||
if (ts_empty(_set_))
|
||||
{
|
||||
_set_->root = ts_impl_new_node(element);
|
||||
if (!_set_->root)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
treeset_node *scan = _set_->root;
|
||||
treeset_node *parent = scan;
|
||||
while (scan != NULL)
|
||||
{
|
||||
parent = scan;
|
||||
if (_set_->cmp(scan->value, element) > 0)
|
||||
scan = scan->left;
|
||||
else if (_set_->cmp(scan->value, element) < 0)
|
||||
scan = scan->right;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
treeset_node *node;
|
||||
if (_set_->cmp(parent->value, element) > 0)
|
||||
{
|
||||
parent->left = ts_impl_new_node(element);
|
||||
if (!parent->left)
|
||||
return false;
|
||||
parent->left->parent = parent;
|
||||
node = parent->left;
|
||||
}
|
||||
else
|
||||
{
|
||||
parent->right = ts_impl_new_node(element);
|
||||
if (!parent->right)
|
||||
return false;
|
||||
parent->right->parent = parent;
|
||||
node = parent->right;
|
||||
}
|
||||
ts_impl_rebalance(_set_, node);
|
||||
}
|
||||
_set_->count++;
|
||||
return true;
|
||||
}
|
||||
bool ts_remove(treeset *_set_, size_t element)
|
||||
{
|
||||
treeset_node *node = ts_impl_get_node(_set_, element);
|
||||
if (!node)
|
||||
return false;
|
||||
treeset_node *temp = NULL, *unbalanced = NULL;
|
||||
bool is_root = node->parent == NULL;
|
||||
if (node->left == NULL && node->right == NULL)
|
||||
{
|
||||
if (is_root)
|
||||
_set_->root = NULL;
|
||||
else
|
||||
{
|
||||
unbalanced = node->parent;
|
||||
if (node->parent->right == node)
|
||||
node->parent->right = NULL;
|
||||
else
|
||||
node->parent->left = NULL;
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
else if (node->left == NULL)
|
||||
{
|
||||
if (is_root)
|
||||
{
|
||||
_set_->root = node->right;
|
||||
_set_->root->parent = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
unbalanced = node->parent;
|
||||
node->right->parent = node->parent;
|
||||
if (node->parent->right == node)
|
||||
node->parent->right = node->right;
|
||||
else
|
||||
node->parent->left = node->right;
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
else if (node->right == NULL)
|
||||
{
|
||||
if (is_root)
|
||||
{
|
||||
_set_->root = node->left;
|
||||
_set_->root->parent = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
unbalanced = node->parent;
|
||||
node->left->parent = node->parent;
|
||||
if (node->parent->right == node)
|
||||
node->parent->right = node->left;
|
||||
else
|
||||
node->parent->left = node->left;
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = node->right;
|
||||
while (temp->left != NULL)
|
||||
temp = temp->left;
|
||||
size_t temp_value = temp->value;
|
||||
unbalanced = temp->parent;
|
||||
if (temp->left == NULL && temp->right == NULL)
|
||||
{
|
||||
if (temp->parent->right == temp)
|
||||
temp->parent->right = NULL;
|
||||
else
|
||||
temp->parent->left = NULL;
|
||||
}
|
||||
else if (temp->left == NULL)
|
||||
{
|
||||
temp->right->parent = temp->parent;
|
||||
if (temp->parent->right == temp)
|
||||
temp->parent->right = temp->right;
|
||||
else
|
||||
temp->parent->left = temp->right;
|
||||
}
|
||||
else if (temp->right == NULL)
|
||||
{
|
||||
temp->left->parent = temp->parent;
|
||||
if (temp->parent->right == temp)
|
||||
temp->parent->right = temp->left;
|
||||
else
|
||||
temp->parent->left = temp->left;
|
||||
}
|
||||
free(temp);
|
||||
node->value = temp_value;
|
||||
}
|
||||
if (unbalanced != NULL)
|
||||
ts_impl_rebalance(_set_, unbalanced);
|
||||
_set_->count--;
|
||||
if (_set_->count == 0)
|
||||
_set_->root = NULL;
|
||||
return true;
|
||||
}
|
||||
bool ts_insert_if(treeset *_set_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return ts_insert(_set_, element);
|
||||
return false;
|
||||
}
|
||||
bool ts_remove_if(treeset *_set_, size_t element, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
return ts_remove(_set_, element);
|
||||
return false;
|
||||
}
|
||||
bool ts_max(treeset *_set_, size_t *value)
|
||||
{
|
||||
if (ts_empty(_set_))
|
||||
return false;
|
||||
treeset_node *scan = _set_->root;
|
||||
while (scan->right != NULL)
|
||||
scan = scan->right;
|
||||
*value = scan->value;
|
||||
return true;
|
||||
}
|
||||
bool ts_min(treeset *_set_, size_t *value)
|
||||
{
|
||||
if (ts_empty(_set_))
|
||||
return false;
|
||||
treeset_node *scan = _set_->root;
|
||||
while (scan->left != NULL)
|
||||
scan = scan->left;
|
||||
*value = scan->value;
|
||||
return true;
|
||||
}
|
||||
bool ts_contains(treeset *_set_, size_t element)
|
||||
{
|
||||
treeset_node *scan = _set_->root;
|
||||
while (scan != NULL)
|
||||
{
|
||||
if (_set_->cmp(scan->value, element) > 0)
|
||||
scan = scan->left;
|
||||
else if (_set_->cmp(scan->value, element) < 0)
|
||||
scan = scan->right;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool ts_empty(treeset *_set_) { return _set_->count == 0; }
|
||||
size_t ts_count(treeset *_set_) { return _set_->count; }
|
||||
treeset *ts_union(treeset *_set1_, treeset *_set2_)
|
||||
{
|
||||
treeset *_set_r_ = ts_new(_set1_->cmp);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
treeset_iter iter1, iter2;
|
||||
ts_iter_init(&iter1, _set1_);
|
||||
ts_iter_init(&iter2, _set2_);
|
||||
for (ts_iter_to_start(&iter1); !ts_iter_end(&iter1); ts_iter_next(&iter1))
|
||||
{
|
||||
ts_insert(_set_r_, ts_iter_value(&iter1));
|
||||
}
|
||||
for (ts_iter_to_start(&iter2); !ts_iter_end(&iter2); ts_iter_next(&iter2))
|
||||
{
|
||||
ts_insert(_set_r_, ts_iter_value(&iter2));
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
treeset *ts_intersection(treeset *_set1_, treeset *_set2_)
|
||||
{
|
||||
treeset *_set_r_ = ts_new(_set1_->cmp);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
treeset *_set_A_ = _set1_->count < _set2_->count ? _set1_ : _set2_;
|
||||
treeset *_set_B_ = _set_A_ == _set1_ ? _set2_ : _set1_;
|
||||
treeset_iter iter;
|
||||
ts_iter_init(&iter, _set_A_);
|
||||
for (ts_iter_to_start(&iter); !ts_iter_end(&iter); ts_iter_next(&iter))
|
||||
{
|
||||
size_t value = ts_iter_value(&iter);
|
||||
if (ts_impl_get_node(_set_B_, value) != NULL)
|
||||
ts_insert(_set_r_, value);
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
treeset *ts_difference(treeset *_set1_, treeset *_set2_)
|
||||
{
|
||||
treeset *_set_r_ = ts_new(_set1_->cmp);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
treeset_iter iter;
|
||||
ts_iter_init(&iter, _set1_);
|
||||
for (ts_iter_to_start(&iter); !ts_iter_end(&iter); ts_iter_next(&iter))
|
||||
{
|
||||
size_t value = ts_iter_value(&iter);
|
||||
if (ts_impl_get_node(_set2_, value) == NULL)
|
||||
ts_insert(_set_r_, value);
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
treeset *ts_symmetric_difference(treeset *_set1_, treeset *_set2_)
|
||||
{
|
||||
treeset *_set_r_ = ts_new(_set1_->cmp);
|
||||
if (!_set_r_)
|
||||
return NULL;
|
||||
treeset_iter iter1, iter2;
|
||||
ts_iter_init(&iter1, _set1_);
|
||||
ts_iter_init(&iter2, _set2_);
|
||||
for (ts_iter_to_start(&iter1); !ts_iter_end(&iter1); ts_iter_next(&iter1))
|
||||
{
|
||||
size_t value = ts_iter_value(&iter1);
|
||||
if (ts_impl_get_node(_set2_, value) == NULL)
|
||||
ts_insert(_set_r_, value);
|
||||
}
|
||||
for (ts_iter_to_start(&iter2); !ts_iter_end(&iter2); ts_iter_next(&iter2))
|
||||
{
|
||||
size_t value = ts_iter_value(&iter2);
|
||||
if (ts_impl_get_node(_set1_, value) == NULL)
|
||||
ts_insert(_set_r_, value);
|
||||
}
|
||||
return _set_r_;
|
||||
}
|
||||
bool ts_is_subset(treeset *_set1_, treeset *_set2_)
|
||||
{
|
||||
if (ts_count(_set1_) > ts_count(_set2_))
|
||||
return false;
|
||||
if (ts_empty(_set1_))
|
||||
return true;
|
||||
treeset_iter iter;
|
||||
ts_iter_init(&iter, _set1_);
|
||||
for (ts_iter_to_start(&iter); !ts_iter_end(&iter); ts_iter_next(&iter))
|
||||
{
|
||||
size_t value = ts_iter_value(&iter);
|
||||
if (!ts_contains(_set2_, value))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ts_is_superset(treeset *_set1_, treeset *_set2_) { return ts_is_subset(_set2_, _set1_); }
|
||||
bool ts_is_proper_subset(treeset *_set1_, treeset *_set2_)
|
||||
{
|
||||
if (ts_count(_set1_) >= ts_count(_set2_))
|
||||
return false;
|
||||
if (ts_empty(_set1_))
|
||||
{
|
||||
if (!ts_empty(_set2_))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
treeset_iter iter;
|
||||
ts_iter_init(&iter, _set1_);
|
||||
for (ts_iter_to_start(&iter); !ts_iter_end(&iter); ts_iter_next(&iter))
|
||||
{
|
||||
size_t value = ts_iter_value(&iter);
|
||||
if (!ts_contains(_set2_, value))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ts_is_proper_superset(treeset *_set1_, treeset *_set2_) { return ts_is_proper_subset(_set2_, _set1_); }
|
||||
bool ts_is_disjointset(treeset *_set1_, treeset *_set2_)
|
||||
{
|
||||
if (ts_empty(_set1_))
|
||||
return true;
|
||||
treeset_iter iter;
|
||||
ts_iter_init(&iter, _set1_);
|
||||
for (ts_iter_to_start(&iter); !ts_iter_end(&iter); ts_iter_next(&iter))
|
||||
{
|
||||
size_t value = ts_iter_value(&iter);
|
||||
if (ts_contains(_set2_, value))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
treeset_iter *ts_iter_new(treeset *target)
|
||||
{
|
||||
treeset_iter *iter = malloc(sizeof(treeset_iter));
|
||||
if (!iter)
|
||||
return NULL;
|
||||
ts_iter_init(iter, target);
|
||||
return iter;
|
||||
}
|
||||
void ts_iter_free(treeset_iter *iter) { free(iter); }
|
||||
void ts_iter_init(treeset_iter *iter, treeset *target)
|
||||
{
|
||||
iter->target = target;
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = ts_empty(target);
|
||||
iter->cursor = target->root;
|
||||
while (iter->cursor->left != NULL)
|
||||
iter->cursor = iter->cursor->left;
|
||||
iter->first = iter->cursor;
|
||||
iter->last = target->root;
|
||||
while (iter->last->right != NULL)
|
||||
iter->last = iter->last->right;
|
||||
}
|
||||
bool ts_iter_start(treeset_iter *iter) { return ts_empty(iter->target) || iter->start; }
|
||||
bool ts_iter_end(treeset_iter *iter) { return ts_empty(iter->target) || iter->end; }
|
||||
void ts_iter_to_start(treeset_iter *iter)
|
||||
{
|
||||
iter->index = 0;
|
||||
iter->start = true;
|
||||
iter->end = ts_empty(iter->target);
|
||||
iter->cursor = iter->first;
|
||||
}
|
||||
void ts_iter_to_end(treeset_iter *iter)
|
||||
{
|
||||
iter->index = iter->target->count - 1;
|
||||
iter->start = ts_empty(iter->target);
|
||||
iter->end = true;
|
||||
iter->cursor = iter->last;
|
||||
}
|
||||
bool ts_iter_next(treeset_iter *iter)
|
||||
{
|
||||
if (iter->end)
|
||||
return false;
|
||||
iter->start = ts_empty(iter->target);
|
||||
if (iter->cursor->right != NULL)
|
||||
{
|
||||
iter->cursor = iter->cursor->right;
|
||||
while (iter->cursor->left != NULL)
|
||||
iter->cursor = iter->cursor->left;
|
||||
iter->index++;
|
||||
return true;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
if (iter->cursor == iter->last)
|
||||
{
|
||||
iter->end = true;
|
||||
return true;
|
||||
}
|
||||
if (iter->cursor->parent->left == iter->cursor)
|
||||
{
|
||||
iter->cursor = iter->cursor->parent;
|
||||
iter->index++;
|
||||
return true;
|
||||
}
|
||||
iter->cursor = iter->cursor->parent;
|
||||
}
|
||||
}
|
||||
bool ts_iter_prev(treeset_iter *iter)
|
||||
{
|
||||
if (iter->start)
|
||||
return false;
|
||||
iter->end = ts_empty(iter->target);
|
||||
if (iter->cursor->left != NULL)
|
||||
{
|
||||
iter->cursor = iter->cursor->left;
|
||||
while (iter->cursor->right != NULL)
|
||||
iter->cursor = iter->cursor->right;
|
||||
iter->index--;
|
||||
return true;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
if (iter->cursor == iter->first)
|
||||
{
|
||||
iter->start = true;
|
||||
return true;
|
||||
}
|
||||
if (iter->cursor->parent->right == iter->cursor)
|
||||
{
|
||||
iter->cursor = iter->cursor->parent;
|
||||
iter->index--;
|
||||
return true;
|
||||
}
|
||||
iter->cursor = iter->cursor->parent;
|
||||
}
|
||||
}
|
||||
size_t ts_iter_value(treeset_iter *iter)
|
||||
{
|
||||
if (ts_empty(iter->target))
|
||||
return ts_impl_default_value();
|
||||
return iter->cursor->value;
|
||||
}
|
||||
size_t ts_iter_index(treeset_iter *iter) { return iter->index; }
|
||||
static treeset_node *ts_impl_new_node(size_t element)
|
||||
{
|
||||
treeset_node *node = malloc(sizeof(treeset_node));
|
||||
if (!node)
|
||||
return NULL;
|
||||
node->value = element;
|
||||
node->right = NULL;
|
||||
node->left = NULL;
|
||||
node->parent = NULL;
|
||||
node->height = 0;
|
||||
return node;
|
||||
}
|
||||
static treeset_node *ts_impl_get_node(treeset *_set_, size_t element)
|
||||
{
|
||||
if (ts_empty(_set_))
|
||||
return NULL;
|
||||
treeset_node *scan = _set_->root;
|
||||
while (scan != NULL)
|
||||
{
|
||||
if (_set_->cmp(scan->value, element) > 0)
|
||||
scan = scan->left;
|
||||
else if (_set_->cmp(scan->value, element) < 0)
|
||||
scan = scan->right;
|
||||
else
|
||||
return scan;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static unsigned char ts_impl_h(treeset_node *node)
|
||||
{
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
return node->height;
|
||||
}
|
||||
static unsigned char ts_impl_hupdate(treeset_node *node)
|
||||
{
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
unsigned char h_l = ts_impl_h(node->left);
|
||||
unsigned char h_r = ts_impl_h(node->right);
|
||||
return 1 + (h_l > h_r ? h_l : h_r);
|
||||
}
|
||||
static void ts_impl_rotate_right(treeset_node **Z)
|
||||
{
|
||||
treeset_node *root = *Z;
|
||||
treeset_node *new_root = root->left;
|
||||
if (root->parent != NULL)
|
||||
{
|
||||
if (root->parent->left == root)
|
||||
root->parent->left = new_root;
|
||||
else
|
||||
root->parent->right = new_root;
|
||||
}
|
||||
new_root->parent = root->parent;
|
||||
root->parent = new_root;
|
||||
root->left = new_root->right;
|
||||
if (root->left)
|
||||
root->left->parent = root;
|
||||
new_root->right = root;
|
||||
root->height = ts_impl_hupdate(root);
|
||||
new_root->height = ts_impl_hupdate(new_root);
|
||||
*Z = new_root;
|
||||
}
|
||||
static void ts_impl_rotate_left(treeset_node **Z)
|
||||
{
|
||||
treeset_node *root = *Z;
|
||||
treeset_node *new_root = root->right;
|
||||
if (root->parent != NULL)
|
||||
{
|
||||
if (root->parent->right == root)
|
||||
root->parent->right = new_root;
|
||||
else
|
||||
root->parent->left = new_root;
|
||||
}
|
||||
new_root->parent = root->parent;
|
||||
root->parent = new_root;
|
||||
root->right = new_root->left;
|
||||
if (root->right)
|
||||
root->right->parent = root;
|
||||
new_root->left = root;
|
||||
root->height = ts_impl_hupdate(root);
|
||||
new_root->height = ts_impl_hupdate(new_root);
|
||||
*Z = new_root;
|
||||
}
|
||||
static void ts_impl_rebalance(treeset *_set_, treeset_node *node)
|
||||
{
|
||||
treeset_node *scan = node, *child = NULL;
|
||||
int balance;
|
||||
bool is_root = false;
|
||||
while (scan != NULL)
|
||||
{
|
||||
if (scan->parent == NULL)
|
||||
is_root = true;
|
||||
scan->height = ts_impl_hupdate(scan);
|
||||
balance = ts_impl_h(scan->right) - ts_impl_h(scan->left);
|
||||
if (balance >= 2)
|
||||
{
|
||||
child = scan->right;
|
||||
if (ts_impl_h(child->right) < ts_impl_h(child->left))
|
||||
ts_impl_rotate_right(&(scan->right));
|
||||
ts_impl_rotate_left(&scan);
|
||||
}
|
||||
else if (balance <= -2)
|
||||
{
|
||||
child = scan->left;
|
||||
if (ts_impl_h(child->left) < ts_impl_h(child->right))
|
||||
ts_impl_rotate_left(&(scan->left));
|
||||
ts_impl_rotate_right(&scan);
|
||||
}
|
||||
if (is_root)
|
||||
{
|
||||
_set_->root = scan;
|
||||
is_root = false;
|
||||
}
|
||||
scan = scan->parent;
|
||||
}
|
||||
}
|
||||
static treeset_iter ts_impl_it_start(treeset *_set_)
|
||||
{
|
||||
treeset_iter iter;
|
||||
ts_iter_init(&iter, _set_);
|
||||
return iter;
|
||||
}
|
||||
static treeset_iter ts_impl_it_end(treeset *_set_)
|
||||
{
|
||||
treeset_iter iter;
|
||||
ts_iter_init(&iter, _set_);
|
||||
ts_iter_to_end(&iter);
|
||||
return iter;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/deque.c"
|
||||
|
||||
CMC_CREATE_UNIT(deque_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
deque *d = d_new(1000000);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, d);
|
||||
|
||||
bool passed = d->capacity == 1000000 && d->count == 0 && d->buffer;
|
||||
|
||||
cmc_assert_equals(size_t, 1000000, d->capacity);
|
||||
cmc_assert_equals(size_t, 0, d_count(d));
|
||||
cmc_assert_not_equals(ptr, NULL, d->buffer);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
d_free(d);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = 0], {
|
||||
deque *d = d_new(0);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, d);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(d == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], {
|
||||
deque *d = d_new(UINT64_MAX);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, d);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(d == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count capacity], {
|
||||
deque *d = d_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, d);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
d_push_back(d, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, d_count(d));
|
||||
|
||||
d_clear(d);
|
||||
|
||||
cmc_assert_equals(size_t, 0, d_count(d));
|
||||
cmc_assert_equals(size_t, 100, d_capacity(d));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(d_count(d) == 0 && d_capacity(d) == 100);
|
||||
|
||||
d_free(d);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(buffer_growth[edge_case:capacity = 1], {
|
||||
deque *d = d_new(1);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, d);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
d_push_back(d, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, d_count(d));
|
||||
cmc_assert_lesser_equals(size_t, d_capacity(d), d_count(d));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(d_count(d) == 50 && d_count(d) <= d_capacity(d));
|
||||
|
||||
d_free(d);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/hashmap.c"
|
||||
|
||||
#include "utl.c"
|
||||
|
||||
CMC_CREATE_UNIT(hashmap_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
hashmap *map = hm_new(943722, 0.6, cmp, hash);
|
||||
|
||||
bool passed = (map->capacity >= (943722 / 0.6)) && map->count == 0 && map->buffer;
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
cmc_assert_greater_equals(size_t, (943722 / 0.6), hm_capacity(map));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
hm_free(map);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = 0], {
|
||||
hashmap *map = hm_new(0, 0.6, cmp, hash);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, map);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(map == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], {
|
||||
hashmap *map = hm_new(UINT64_MAX, 0.6, cmp, hash);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, map);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(map == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count capacity], {
|
||||
hashmap *map = hm_new(100, 0.6, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
hm_insert(map, i, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, hm_count(map));
|
||||
|
||||
hm_clear(map);
|
||||
|
||||
cmc_assert_equals(size_t, 0, hm_count(map));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(hm_count(map) == 0);
|
||||
|
||||
hm_free(map);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/hashset.c"
|
||||
|
||||
#include "utl.c"
|
||||
|
||||
CMC_CREATE_UNIT(hashset_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
hashset *set = hs_new(943722, 0.6, cmp, hash);
|
||||
|
||||
bool passed = (set->capacity >= (943722 / 0.6)) && set->count == 0 && set->buffer;
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, set);
|
||||
cmc_assert(hs_capacity(set) > (943722 / 0.6));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
hs_free(set);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = 0], {
|
||||
hashset *set = hs_new(0, 0.6, cmp, hash);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, set);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(set == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], {
|
||||
hashset *set = hs_new(UINT64_MAX, 0.6, cmp, hash);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, set);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(set == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count capacity], {
|
||||
hashset *set = hs_new(100, 0.6, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, set);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
hs_insert(set, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, hs_count(set));
|
||||
|
||||
hs_clear(set);
|
||||
|
||||
cmc_assert_equals(size_t, 0, hs_count(set));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(hs_count(set) == 0);
|
||||
|
||||
hs_free(set);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/heap.c"
|
||||
|
||||
#include "utl.c"
|
||||
|
||||
CMC_CREATE_UNIT(heap_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
heap *h = h_new(1000000, cmc_max_heap, cmp);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, h);
|
||||
|
||||
bool passed = h->capacity == 1000000 && h->count == 0 && h->buffer;
|
||||
|
||||
cmc_assert_equals(size_t, 1000000, h->capacity);
|
||||
cmc_assert_equals(size_t, 0, h_count(h));
|
||||
cmc_assert_not_equals(ptr, NULL, h->buffer);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
h_free(h);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = 0], {
|
||||
heap *h = h_new(0, cmc_max_heap, cmp);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, h);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(h == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], {
|
||||
heap *h = h_new(UINT64_MAX, cmc_max_heap, cmp);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, h);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(h == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count capacity], {
|
||||
heap *h = h_new(100, cmc_max_heap, cmp);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, h);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
h_insert(h, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, h_count(h));
|
||||
|
||||
h_clear(h);
|
||||
|
||||
cmc_assert_equals(size_t, 0, h_count(h));
|
||||
cmc_assert_equals(size_t, 100, h_capacity(h));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(h_count(h) == 0 && h_capacity(h) == 100);
|
||||
|
||||
h_free(h);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(buffer_growth[edge_case:capacity = 1], {
|
||||
heap *h = h_new(1, cmc_max_heap, cmp);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, h);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
h_insert(h, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, h_count(h));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(h_count(h) == 50);
|
||||
|
||||
h_free(h);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/linkedlist.c"
|
||||
|
||||
CMC_CREATE_UNIT(linkedlist_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
linkedlist *ll = ll_new();
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, ll);
|
||||
|
||||
bool passed = ll->count == 0 && ll->head == NULL && ll->tail == NULL;
|
||||
|
||||
cmc_assert_equals(size_t, 0, ll_count(ll));
|
||||
cmc_assert_equals(ptr, NULL, ll->head);
|
||||
cmc_assert_equals(ptr, NULL, ll->tail);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
ll_free(ll);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count capacity], {
|
||||
linkedlist *ll = ll_new();
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, ll);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
ll_push_back(ll, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, ll_count(ll));
|
||||
|
||||
ll_clear(ll);
|
||||
|
||||
cmc_assert_equals(size_t, 0, ll_count(ll));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(ll_count(ll) == 0);
|
||||
|
||||
ll_free(ll);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[edge_case:count = 0], {
|
||||
linkedlist *ll = ll_new();
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, ll);
|
||||
|
||||
ll_clear(ll);
|
||||
|
||||
cmc_assert_equals(size_t, 0, ll_count(ll));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(ll_count(ll) == 0);
|
||||
|
||||
ll_free(ll);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push_front[count], {
|
||||
linkedlist *ll = ll_new();
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, ll);
|
||||
|
||||
ll_push_front(ll, 1);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, ll->head);
|
||||
cmc_assert_not_equals(ptr, NULL, ll->tail);
|
||||
cmc_assert_equals(size_t, 1, ll_count(ll));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(ll->head && ll->tail && ll_count(ll) == 1);
|
||||
|
||||
ll_free(ll);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push_front[item_preservation], {
|
||||
linkedlist *ll = ll_new();
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, ll);
|
||||
|
||||
for (size_t i = 0; i < 200; i++)
|
||||
ll_push_front(ll, i);
|
||||
|
||||
bool passed = true;
|
||||
|
||||
for (size_t i = 0; i < ll_count(ll); i++)
|
||||
{
|
||||
passed = passed && ll_get(ll, i) == (ll_count(ll) - i - 1);
|
||||
|
||||
cmc_assert_equals(size_t, ll_get(ll, i), ll_count(ll) - i - 1);
|
||||
}
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed && ll_count(ll) == 200);
|
||||
|
||||
ll_free(ll);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/linkedqueue.c"
|
||||
|
||||
CMC_CREATE_UNIT(linkedqueue_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
linkedqueue *q = lq_new();
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, q);
|
||||
|
||||
bool passed = q->count == 0 && !q->head && !q->tail;
|
||||
|
||||
cmc_assert_equals(size_t, 0, lq_count(q));
|
||||
cmc_assert_equals(ptr, NULL, q->head);
|
||||
cmc_assert_equals(ptr, NULL, q->tail);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
lq_free(q);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count], {
|
||||
linkedqueue *q = lq_new();
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, q);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
lq_enqueue(q, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, lq_count(q));
|
||||
|
||||
lq_clear(q);
|
||||
|
||||
cmc_assert_equals(size_t, 0, lq_count(q));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(lq_count(q) == 0);
|
||||
|
||||
lq_free(q);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,420 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/list.c"
|
||||
|
||||
CMC_CREATE_UNIT(list_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
list *l = l_new(1000000);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
bool passed = l->capacity == 1000000 && l->count == 0 && l->buffer;
|
||||
|
||||
cmc_assert_equals(size_t, 1000000, l->capacity);
|
||||
cmc_assert_equals(size_t, 0, l_count(l));
|
||||
cmc_assert_not_equals(ptr, NULL, l->buffer);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = 0], {
|
||||
list *l = l_new(0);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, l);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], {
|
||||
list *l = l_new(UINT64_MAX);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, l);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new_from[count item_preservation], {
|
||||
size_t v[5];
|
||||
|
||||
for (size_t i = 0; i < 5; i++)
|
||||
{
|
||||
v[i] = i;
|
||||
}
|
||||
|
||||
list *l = l_new_from(v, 5);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
bool passed = true;
|
||||
|
||||
for (size_t i = 0; i < 5; i++)
|
||||
{
|
||||
cmc_assert_equals(size_t, i, l_get(l, i));
|
||||
|
||||
passed = passed && l_get(l, i) == i;
|
||||
}
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed && l_count(l) == 5);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new_from[edge_case:size = 1], {
|
||||
size_t a = 10;
|
||||
|
||||
size_t *values = &a;
|
||||
|
||||
list *l = l_new_from(values, 1);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) == 1);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count capacity], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
l_push_back(l, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, l_count(l));
|
||||
|
||||
l_clear(l);
|
||||
|
||||
cmc_assert_equals(size_t, 0, l_count(l));
|
||||
cmc_assert_equals(size_t, 100, l_capacity(l));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) == 0 && l_capacity(l) == 100);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(buffer_growth[edge_case:capacity = 1], {
|
||||
list *l = l_new(1);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
for (size_t i = 0; i < 100; i++)
|
||||
l_push_back(l, i);
|
||||
|
||||
cmc_assert_equals(size_t, 100, l_count(l));
|
||||
|
||||
bool passed = true;
|
||||
|
||||
for (size_t i = 0; i < 100; i++)
|
||||
{
|
||||
passed = passed && l_get(l, i) == i;
|
||||
cmc_assert_equals(size_t, i, l_get(l, i));
|
||||
}
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push_front[count], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(l_push_front(l, i));
|
||||
|
||||
cmc_assert_equals(size_t, 250, l_count(l));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) == 250);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push_front[capacity], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(l_push_front(l, i));
|
||||
|
||||
cmc_assert_lesser_equals(size_t, l_capacity(l), l_count(l));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) <= l_capacity(l));
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push_front[item_preservation], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(l_push_front(l, i));
|
||||
|
||||
bool passed = true;
|
||||
|
||||
for (size_t i = 0; i < l_count(l); i++)
|
||||
{
|
||||
passed = passed && l_get(l, i) == (l_count(l) - i - 1);
|
||||
|
||||
cmc_assert_equals(size_t, l_get(l, i), l_count(l) - i - 1);
|
||||
}
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push[count], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
cmc_assert(l_push_back(l, 0));
|
||||
cmc_assert(l_push_back(l, 249));
|
||||
|
||||
for (size_t i = 1; i < 249; i++)
|
||||
cmc_assert(l_push(l, i, l_count(l) - 1));
|
||||
|
||||
cmc_assert_equals(size_t, 250, l_count(l));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) == 250);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push[capacity], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
cmc_assert(l_push_back(l, 0));
|
||||
cmc_assert(l_push_back(l, 249));
|
||||
|
||||
for (size_t i = 1; i < 249; i++)
|
||||
cmc_assert(l_push(l, i, l_count(l) - 1));
|
||||
|
||||
cmc_assert_lesser_equals(size_t, l_capacity(l), l_count(l));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) <= l_capacity(l));
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push[item_preservation], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
cmc_assert(l_push_back(l, 0));
|
||||
cmc_assert(l_push_back(l, 249));
|
||||
|
||||
for (size_t i = 1; i < 249; i++)
|
||||
cmc_assert(l_push(l, i, l_count(l) - 1));
|
||||
|
||||
bool passed = true;
|
||||
|
||||
for (size_t i = 0; i < l_count(l); i++)
|
||||
{
|
||||
passed = passed && l_get(l, i) == i;
|
||||
|
||||
cmc_assert_equals(size_t, l_get(l, i), i);
|
||||
}
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push_back[count], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(l_push_back(l, i));
|
||||
|
||||
cmc_assert_equals(size_t, 250, l_count(l));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) == 250);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push_back[capacity], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(l_push_back(l, i));
|
||||
|
||||
cmc_assert_lesser_equals(size_t, l_capacity(l), l_count(l));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) <= l_capacity(l));
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push_back[item_preservation], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(l_push_back(l, i));
|
||||
|
||||
bool passed = true;
|
||||
|
||||
for (size_t i = 0; i < l_count(l); i++)
|
||||
{
|
||||
passed = passed && l_get(l, i) == i;
|
||||
|
||||
cmc_assert_equals(size_t, l_get(l, i), i);
|
||||
}
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop_front[count], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
l_push_back(l, i);
|
||||
|
||||
cmc_assert(l_pop_front(l));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) == 250 - 1);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop_front[item_preservation], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
l_push_back(l, i);
|
||||
|
||||
bool passed = l_pop_front(l);
|
||||
|
||||
for (size_t i = 0; i < l_count(l); i++)
|
||||
{
|
||||
passed = passed && l_get(l, i) == i + 1;
|
||||
|
||||
cmc_assert_equals(size_t, l_get(l, i), i + 1);
|
||||
}
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop_front[edge_case:count = 0], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
cmc_assert(!l_pop_front(l));
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) == 0);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop_front[edge_case:count = 1], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
cmc_assert(l_push_back(l, 10));
|
||||
cmc_assert(l_pop_front(l));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) == 0);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop_front[buffer_clear], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
cmc_assert(l_push_back(l, 1));
|
||||
cmc_assert(l_push_front(l, 2));
|
||||
|
||||
bool passed = l_pop_front(l) && l->buffer[1] == 0;
|
||||
|
||||
cmc_assert_equals(size_t, 0, l->buffer[1]);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed && l_count(l) == 1);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop[edge_case:count = 0], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
cmc_assert(!l_pop(l, 1));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(!l_pop(l, 1));
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop[edge_case:count = 1], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
cmc_assert(l_push_back(l, 1));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_pop(l, 0));
|
||||
|
||||
cmc_assert_equals(size_t, 0, l->buffer[0]);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop[buffer_clear], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
cmc_assert(l_push_back(l, 1));
|
||||
cmc_assert(l_push_back(l, 2));
|
||||
cmc_assert(l_push_back(l, 3));
|
||||
cmc_assert(l_pop(l, 1));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(0 == l->buffer[2]);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop_back[edge_case:count = 0], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
cmc_assert(!l_pop_back(l));
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) == 0);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop_back[edge_case:count = 1], {
|
||||
list *l = l_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, l);
|
||||
cmc_assert(l_push_back(l, 10));
|
||||
cmc_assert(l_pop_back(l));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(l_count(l) == 0);
|
||||
|
||||
l_free(l);
|
||||
});
|
||||
})
|
||||
@@ -0,0 +1,173 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/multimap.c"
|
||||
|
||||
#include "utl.c"
|
||||
|
||||
CMC_CREATE_UNIT(multimap_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
multimap *map = mm_new(943722, 0.8, cmp, hash);
|
||||
|
||||
bool passed = (map->capacity >= (943722 / 0.8)) && map->count == 0 && map->buffer;
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
cmc_assert_greater_equals(size_t, (943722 / 0.8), mm_capacity(map));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
mm_free(map);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = 0], {
|
||||
multimap *map = mm_new(0, 0.8, cmp, hash);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, map);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(map == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], {
|
||||
multimap *map = mm_new(UINT64_MAX, 0.8, cmp, hash);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, map);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(map == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count capacity], {
|
||||
multimap *map = mm_new(100, 0.8, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
cmc_assert(mm_insert(map, i, i));
|
||||
|
||||
cmc_assert_equals(size_t, 50, mm_count(map));
|
||||
|
||||
mm_clear(map);
|
||||
|
||||
cmc_assert_equals(size_t, 0, mm_count(map));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 0);
|
||||
|
||||
mm_free(map);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(insert[count], {
|
||||
multimap *map = mm_new(100, 0.8, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
|
||||
for (size_t i = 0; i < 200; i++)
|
||||
cmc_assert(mm_insert(map, i % 20, i % 20));
|
||||
|
||||
cmc_assert_equals(size_t, 200, mm_count(map));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 200);
|
||||
|
||||
mm_free(map);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(remove[count], {
|
||||
multimap *map = mm_new(100, 0.8, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
|
||||
for (size_t i = 0; i < 200; i++)
|
||||
cmc_assert(mm_insert(map, i % 20, i % 20));
|
||||
|
||||
size_t r;
|
||||
|
||||
for (size_t i = 0; i < 60; i++)
|
||||
cmc_assert(mm_remove(map, i % 20, &r));
|
||||
|
||||
cmc_assert_equals(size_t, 140, mm_count(map));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 140);
|
||||
|
||||
mm_free(map);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(remove[edge_case:count = 0], {
|
||||
multimap *map = mm_new(100, 0.8, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
|
||||
size_t r;
|
||||
|
||||
cmc_assert(!mm_remove(map, 10, &r));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(!mm_remove(map, 10, &r));
|
||||
|
||||
mm_free(map);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(remove[edge_case:count = 1], {
|
||||
multimap *map = mm_new(100, 0.8, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
|
||||
size_t r;
|
||||
|
||||
cmc_assert(mm_insert(map, 10, 11));
|
||||
cmc_assert(mm_remove(map, 10, &r));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 0);
|
||||
|
||||
mm_free(map);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(insert_if, {
|
||||
multimap *map = mm_new(100, 0.8, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
|
||||
for (size_t i = 1; i <= 100; i++)
|
||||
cmc_assert(mm_insert_if(map, i % 20, i, i % 2 == 0) || i % 2 != 0);
|
||||
|
||||
cmc_assert_equals(size_t, 50, mm_count(map));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 50);
|
||||
|
||||
mm_free(map);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(remove_if, {
|
||||
multimap *map = mm_new(100, 0.8, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
|
||||
for (size_t i = 1; i <= 100; i++)
|
||||
cmc_assert(mm_insert(map, i % 20, i));
|
||||
|
||||
size_t r;
|
||||
|
||||
for (size_t i = 1; i <= 100; i++)
|
||||
cmc_assert(mm_remove_if(map, i % 20, &r, i % 2 == 0) || i % 2 != 0);
|
||||
|
||||
cmc_assert_equals(size_t, 50, mm_count(map));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 50);
|
||||
|
||||
mm_free(map);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(get[key_ordering], {
|
||||
multimap *map = mm_new(100, 0.8, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
|
||||
/* Assert that the value 100 is accessed before the value 101 for the
|
||||
* same key */
|
||||
cmc_assert(mm_insert(map, 10, 100));
|
||||
cmc_assert(mm_insert(map, 20, 102));
|
||||
cmc_assert(mm_insert(map, 10, 101));
|
||||
|
||||
cmc_assert_equals(size_t, 100, mm_get(map, 10));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(mm_get(map, 10) == 100);
|
||||
|
||||
mm_free(map);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/multiset.c"
|
||||
|
||||
#include "utl.c"
|
||||
|
||||
CMC_CREATE_UNIT(multiset_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
multiset *set = ms_new(943722, 0.6, cmp, hash);
|
||||
|
||||
bool passed = (set->capacity >= (943722 / 0.6)) && set->count == 0 && set->buffer;
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, set);
|
||||
cmc_assert(ms_capacity(set) > (943722 / 0.6));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
ms_free(set);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = 0], {
|
||||
multiset *set = ms_new(0, 0.6, cmp, hash);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, set);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(set == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = UINTMAX_MAX], {
|
||||
multiset *set = ms_new(UINTMAX_MAX, 0.99, cmp, hash);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, set);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(set == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count capacity], {
|
||||
multiset *set = ms_new(100, 0.6, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, set);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
cmc_assert(ms_insert(set, i));
|
||||
|
||||
cmc_assert_equals(size_t, 50, ms_count(set));
|
||||
|
||||
ms_clear(set);
|
||||
|
||||
cmc_assert_equals(size_t, 0, ms_count(set));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(ms_count(set) == 0);
|
||||
|
||||
ms_free(set);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(insert[count cardinality multiplicity], {
|
||||
multiset *set = ms_new(100, 0.6, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, set);
|
||||
|
||||
cmc_assert(ms_insert(set, 1));
|
||||
cmc_assert(ms_insert(set, 2));
|
||||
cmc_assert(ms_insert(set, 2));
|
||||
cmc_assert(ms_insert(set, 3));
|
||||
cmc_assert(ms_insert(set, 3));
|
||||
cmc_assert(ms_insert(set, 3));
|
||||
|
||||
cmc_assert_equals(size_t, 3, ms_count(set));
|
||||
cmc_assert_equals(size_t, 6, ms_cardinality(set));
|
||||
|
||||
bool passed = ms_count(set) == 3 && ms_cardinality(set) == 6;
|
||||
|
||||
for (size_t i = 0; i <= 3; i++)
|
||||
passed = passed && ms_multiplicity_of(set, i) == i;
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
ms_free(set);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(multiplicity, {
|
||||
multiset *set = ms_new(100, 0.6, cmp, hash);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, set);
|
||||
|
||||
for (size_t i = 0; i < 100; i++)
|
||||
cmc_assert(ms_insert(set, i % 20));
|
||||
|
||||
cmc_assert_equals(size_t, 20, ms_count(set));
|
||||
cmc_assert_equals(size_t, 100, ms_cardinality(set));
|
||||
|
||||
bool passed = ms_count(set) == 20 && ms_cardinality(set) == 100;
|
||||
|
||||
for (size_t i = 0; i < 20; i++)
|
||||
passed = passed && ms_multiplicity_of(set, i) == 5;
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
ms_free(set);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/queue.c"
|
||||
|
||||
CMC_CREATE_UNIT(queue_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
queue *q = q_new(1000000);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, q);
|
||||
|
||||
bool passed = q->capacity == 1000000 && q->count == 0 && q->buffer;
|
||||
|
||||
cmc_assert_equals(size_t, 1000000, q->capacity);
|
||||
cmc_assert_equals(size_t, 0, q_count(q));
|
||||
cmc_assert_not_equals(ptr, NULL, q->buffer);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
q_free(q);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = 0], {
|
||||
queue *q = q_new(0);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, q);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(q == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], {
|
||||
queue *q = q_new(UINT64_MAX);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, q);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(q == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count capacity], {
|
||||
queue *q = q_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, q);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
q_enqueue(q, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, q_count(q));
|
||||
|
||||
q_clear(q);
|
||||
|
||||
cmc_assert_equals(size_t, 0, q_count(q));
|
||||
cmc_assert_equals(size_t, 100, q_capacity(q));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(q_count(q) == 0 && q_capacity(q) == 100);
|
||||
|
||||
q_free(q);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(buffer_growth[edge_case:capacity = 1], {
|
||||
queue *q = q_new(1);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, q);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
q_enqueue(q, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, q_count(q));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(q_count(q) == 50);
|
||||
|
||||
q_free(q);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,740 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/stack.c"
|
||||
|
||||
#include "utl.c"
|
||||
|
||||
CMC_CREATE_UNIT(stack_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
stack *s = s_new(1000000);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
bool passed = s->capacity == 1000000 && s->count == 0 && s->buffer;
|
||||
|
||||
cmc_assert_equals(size_t, 1000000, s->capacity);
|
||||
cmc_assert_equals(size_t, 0, s_count(s));
|
||||
cmc_assert_not_equals(ptr, NULL, s->buffer);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = 0], {
|
||||
stack *s = s_new(0);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, s);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], {
|
||||
stack *s = s_new(UINT64_MAX);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, s);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s == NULL);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count capacity], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
cmc_assert_equals(size_t, 50, s_count(s));
|
||||
|
||||
s_clear(s);
|
||||
|
||||
cmc_assert_equals(size_t, 0, s_count(s));
|
||||
cmc_assert_equals(size_t, 100, s_capacity(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_count(s) == 0 && s_capacity(s) == 100);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(buffer_growth[edge_case:capacity = 1], {
|
||||
stack *s = s_new(1);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
cmc_assert_equals(size_t, 50, s_count(s));
|
||||
cmc_assert_lesser_equals(size_t, s_capacity(s), s_count(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_count(s) == 50 && s_count(s) <= s_capacity(s));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push[count], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
cmc_assert_equals(size_t, 250, s_count(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_count(s) == 250);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push[capacity], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
cmc_assert_lesser_equals(size_t, s_capacity(s), s_count(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_count(s) <= s_capacity(s));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push[item_preservation], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
bool passed = true;
|
||||
|
||||
for (size_t i = 0; i < s_count(s); i++)
|
||||
{
|
||||
passed = s->buffer[i] == i;
|
||||
|
||||
cmc_assert_equals(size_t, i, s->buffer[i]);
|
||||
}
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop[count], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
cmc_assert(s_pop(s));
|
||||
|
||||
cmc_assert_equals(size_t, 249, s_count(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_count(s) == 249);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop[capacity], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
cmc_assert(s_pop(s));
|
||||
|
||||
cmc_assert_lesser_equals(size_t, s_capacity(s), s_count(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_count(s) <= s_capacity(s));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop[item_preservation], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
cmc_assert(s_pop(s));
|
||||
cmc_assert(s_pop(s));
|
||||
|
||||
bool passed = true;
|
||||
|
||||
for (size_t i = 0; i < s_count(s); i++)
|
||||
{
|
||||
passed = s->buffer[i] == i;
|
||||
|
||||
cmc_assert_equals(size_t, i, s->buffer[i]);
|
||||
}
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop[edge_case:count = 0], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
cmc_assert_equals(bool, false, s_pop(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(!s_pop(s));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(push_if, {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
s_push_if(s, i, i % 2 == 0);
|
||||
|
||||
bool passed = true;
|
||||
|
||||
for (size_t i = 0; i < s_count(s); i++)
|
||||
{
|
||||
passed = passed && s->buffer[i] % 2 == 0;
|
||||
|
||||
cmc_assert(s->buffer[i] % 2 == 0);
|
||||
}
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(pop_if, {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
{
|
||||
s_push(s, i);
|
||||
s_pop_if(s, s_top(s) % 2 == 0);
|
||||
}
|
||||
|
||||
bool passed = true;
|
||||
|
||||
for (size_t i = 0; i < s_count(s); i++)
|
||||
{
|
||||
passed = passed && !(s->buffer[i] % 2 == 0);
|
||||
|
||||
cmc_assert(!(s->buffer[i] % 2 == 0));
|
||||
}
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(top, {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 250; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
cmc_assert(s_pop(s));
|
||||
cmc_assert(s_pop(s));
|
||||
|
||||
cmc_assert_equals(size_t, 247, s_top(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(247 == s_top(s));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(top[edge_case:count = 0], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
cmc_assert(s_push(s, 1));
|
||||
|
||||
cmc_assert(s_pop(s));
|
||||
|
||||
cmc_assert_equals(size_t, s_impl_default_value(), s_top(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_impl_default_value() == s_top(s));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(top[edge_case:count = 1], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
cmc_assert(s_push(s, 10));
|
||||
cmc_assert_equals(size_t, 1, s_count(s));
|
||||
cmc_assert_equals(size_t, 10, s_top(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(10 == s_top(s));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(contains, {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 150; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
bool passed = true;
|
||||
|
||||
for (size_t i = 0; i < 150; i++)
|
||||
passed = passed && s_contains(s, i, cmp);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(contains[edge_case:count = 0], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
cmc_assert_equals(bool, false, s_contains(s, 10, cmp));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(!s_contains(s, 10, cmp));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(empty, {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
cmc_assert(s_empty(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_empty(s));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(empty[after_io], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
cmc_assert(s_push(s, 10));
|
||||
cmc_assert(s_pop(s));
|
||||
cmc_assert(s_empty(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_empty(s));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(full, {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < s_capacity(s); i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
cmc_assert(s_full(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_full(s));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(full[edge_case:capacity = 1], {
|
||||
stack *s = s_new(1);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
cmc_assert(s_push(s, 10));
|
||||
|
||||
cmc_assert(s_full(s));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_full(s));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iter_alloc, {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
stack_iter *iter = s_iter_new(s);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, iter);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s && iter);
|
||||
|
||||
s_iter_free(iter);
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iter_init[edge_case:count = 0], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
stack_iter iter;
|
||||
|
||||
s_iter_init(&iter, s);
|
||||
|
||||
cmc_assert_equals(ptr, iter.target, s);
|
||||
cmc_assert_equals(size_t, iter.cursor, 0);
|
||||
cmc_assert(iter.start);
|
||||
cmc_assert(iter.end);
|
||||
|
||||
bool passed = iter.target == s && iter.cursor == 0 && iter.start && iter.end;
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iter_start[edge_case:count = 0], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
stack_iter iter;
|
||||
|
||||
s_iter_init(&iter, s);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_iter_start(&iter));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iter_end[edge_case:count = 0], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
stack_iter iter;
|
||||
|
||||
s_iter_init(&iter, s);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_iter_end(&iter));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iteration, {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 1; i <= 200; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
size_t total = 0;
|
||||
|
||||
for (stack_iter it = s->it_start(s); !s_iter_end(&it); s_iter_next(&it))
|
||||
{
|
||||
total += s_iter_value(&it);
|
||||
}
|
||||
|
||||
cmc_assert_equals(size_t, 20100, total);
|
||||
|
||||
for (stack_iter it = s->it_end(s); !s_iter_start(&it); s_iter_prev(&it))
|
||||
{
|
||||
total += s_iter_value(&it);
|
||||
}
|
||||
|
||||
cmc_assert_equals(size_t, 40200, total);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(total == 40200);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iteration[while loop], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 1; i <= 200; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
size_t total = 0;
|
||||
stack_iter it = s->it_start(s);
|
||||
|
||||
do
|
||||
{
|
||||
total += s_iter_value(&it);
|
||||
} while (s_iter_next(&it));
|
||||
|
||||
cmc_assert_equals(size_t, 20100, total);
|
||||
|
||||
it = s->it_end(s);
|
||||
|
||||
do
|
||||
{
|
||||
total += s_iter_value(&it);
|
||||
} while (s_iter_prev(&it));
|
||||
|
||||
cmc_assert_equals(size_t, 40200, total);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(total == 40200);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iteration[edge_case:count = 1], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
cmc_assert(s_push(s, 10));
|
||||
|
||||
size_t total = 0;
|
||||
|
||||
for (stack_iter it = s->it_start(s); !s_iter_end(&it); s_iter_next(&it))
|
||||
{
|
||||
total += s_iter_value(&it);
|
||||
}
|
||||
|
||||
for (stack_iter it = s->it_end(s); !s_iter_start(&it); s_iter_prev(&it))
|
||||
{
|
||||
total += s_iter_value(&it);
|
||||
}
|
||||
|
||||
cmc_assert_equals(size_t, 20, total);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(total == 20);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iteration[edge_case:count = capacity], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
while (!s_full(s))
|
||||
cmc_assert(s_push(s, 10));
|
||||
|
||||
cmc_assert_equals(size_t, s_capacity(s), s_count(s));
|
||||
|
||||
size_t total = 0;
|
||||
|
||||
for (stack_iter it = s->it_start(s); !s_iter_end(&it); s_iter_next(&it))
|
||||
{
|
||||
total += s_iter_value(&it);
|
||||
}
|
||||
|
||||
cmc_assert_equals(size_t, 1000, total);
|
||||
|
||||
for (stack_iter it = s->it_end(s); !s_iter_start(&it); s_iter_prev(&it))
|
||||
{
|
||||
total += *s_iter_rvalue(&it);
|
||||
}
|
||||
|
||||
cmc_assert_equals(size_t, 2000, total);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(total == 2000);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iteration[to_start = it_start and to_end = it_end], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
bool passed = true;
|
||||
stack_iter it1;
|
||||
stack_iter it2;
|
||||
|
||||
memset(&it1, 0, sizeof(stack_iter));
|
||||
memset(&it2, 0, sizeof(stack_iter));
|
||||
|
||||
s_iter_init(&it2, s);
|
||||
|
||||
it1 = s->it_start(s);
|
||||
s_iter_to_start(&it2);
|
||||
|
||||
cmc_assert_equals(ptr, it1.target, it2.target);
|
||||
cmc_assert_equals(size_t, it1.cursor, it2.cursor);
|
||||
cmc_assert_equals(bool, it1.start, it2.start);
|
||||
cmc_assert_equals(bool, it1.end, it2.end);
|
||||
passed = passed && it1.target == it2.target && it1.cursor == it2.cursor && it1.start == it2.start && it1.end == it2.end;
|
||||
|
||||
it1 = s->it_end(s);
|
||||
s_iter_to_end(&it2);
|
||||
|
||||
cmc_assert_equals(ptr, it1.target, it2.target);
|
||||
cmc_assert_equals(size_t, it1.cursor, it2.cursor);
|
||||
cmc_assert_equals(bool, it1.start, it2.start);
|
||||
cmc_assert_equals(bool, it1.end, it2.end);
|
||||
passed = passed && it1.target == it2.target && it1.cursor == it2.cursor && it1.start == it2.start && it1.end == it2.end;
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iteration[boundaries:count = 0], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
stack_iter iter;
|
||||
s_iter_init(&iter, s);
|
||||
|
||||
s_iter_to_end(&iter);
|
||||
|
||||
cmc_assert_equals(bool, false, s_iter_next(&iter));
|
||||
bool passed = !s_iter_next(&iter);
|
||||
|
||||
s_iter_to_start(&iter);
|
||||
|
||||
cmc_assert_equals(bool, false, s_iter_prev(&iter));
|
||||
passed = !s_iter_prev(&iter);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iteration[boundaries:count > 0], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 200; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
stack_iter iter;
|
||||
s_iter_init(&iter, s);
|
||||
|
||||
s_iter_to_end(&iter);
|
||||
|
||||
cmc_assert_equals(bool, false, s_iter_next(&iter));
|
||||
bool passed = !s_iter_next(&iter);
|
||||
|
||||
s_iter_to_start(&iter);
|
||||
|
||||
cmc_assert_equals(bool, false, s_iter_prev(&iter));
|
||||
passed = !s_iter_prev(&iter);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iter_value[edge_case:count = 0], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
stack_iter iter;
|
||||
|
||||
s_iter_init(&iter, s);
|
||||
|
||||
cmc_assert_equals(size_t, s_impl_default_value(), s_iter_value(&iter));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(s_impl_default_value() == s_iter_value(&iter));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(iter_rvalue[edge_case:count = 0], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
stack_iter iter;
|
||||
|
||||
s_iter_init(&iter, s);
|
||||
|
||||
cmc_assert_equals(ptr, NULL, s_iter_rvalue(&iter));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(NULL == s_iter_rvalue(&iter));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(index, {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
cmc_assert(s_push(s, i));
|
||||
|
||||
cmc_assert_equals(size_t, 10, s_count(s));
|
||||
|
||||
bool passed = true;
|
||||
size_t total = 0;
|
||||
|
||||
for (stack_iter it = s->it_start(s); false; )
|
||||
{
|
||||
passed = passed && s_iter_index(&it) == 0;
|
||||
cmc_assert_equals(size_t, 0, s_iter_index(&it));
|
||||
}
|
||||
|
||||
for (stack_iter it = s->it_start(s); false; )
|
||||
{
|
||||
passed = passed && s_iter_index(&it) == s_count(s) - 1;
|
||||
cmc_assert_equals(size_t, s_count(s) - 1, s_iter_index(&it));
|
||||
}
|
||||
|
||||
for (stack_iter it = s->it_start(s); !s_iter_end(&it); s_iter_next(&it))
|
||||
{
|
||||
total += s_iter_index(&it);
|
||||
}
|
||||
|
||||
cmc_assert_equals(size_t, 45, total);
|
||||
|
||||
for (stack_iter it = s->it_end(s); !s_iter_start(&it); s_iter_prev(&it))
|
||||
{
|
||||
total += s_iter_index(&it);
|
||||
}
|
||||
|
||||
cmc_assert_equals(size_t, 90, total);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(passed && total == 90);
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(index[edge_case:count = 0], {
|
||||
stack *s = s_new(100);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, s);
|
||||
|
||||
stack_iter iter;
|
||||
s_iter_init(&iter, s);
|
||||
|
||||
cmc_assert_equals(size_t, 0, s_iter_index(&iter));
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(0 == s_iter_index(&iter));
|
||||
|
||||
s_free(s);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/treemap.c"
|
||||
|
||||
#include "utl.c"
|
||||
|
||||
CMC_CREATE_UNIT(treemap_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
treemap *map = tm_new(cmp);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(map);
|
||||
|
||||
tm_free(map);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count], {
|
||||
treemap *map = tm_new(cmp);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, map);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
tm_insert(map, i, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, tm_count(map));
|
||||
|
||||
tm_clear(map);
|
||||
|
||||
cmc_assert_equals(size_t, 0, tm_count(map));
|
||||
cmc_assert_equals(ptr, NULL, map->root);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(tm_count(map) == 0);
|
||||
|
||||
tm_free(map);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "utl/assert.h"
|
||||
#include "utl/test.h"
|
||||
#include "../src/treeset.c"
|
||||
|
||||
#include "utl.c"
|
||||
|
||||
CMC_CREATE_UNIT(treeset_test, true, {
|
||||
CMC_CREATE_TEST(new, {
|
||||
treeset *set = ts_new(cmp);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, set);
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(set);
|
||||
|
||||
ts_free(set);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(clear[count], {
|
||||
treeset *set = ts_new(cmp);
|
||||
|
||||
cmc_assert_not_equals(ptr, NULL, set);
|
||||
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
ts_insert(set, i);
|
||||
|
||||
cmc_assert_equals(size_t, 50, ts_count(set));
|
||||
|
||||
ts_clear(set);
|
||||
|
||||
cmc_assert_equals(size_t, 0, ts_count(set));
|
||||
cmc_assert_equals(ptr, NULL, set->root);
|
||||
|
||||
|
||||
CMC_TEST_PASS_ELSE_FAIL(ts_count(set) == 0);
|
||||
|
||||
ts_free(set);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Utility file to be included in the unit tests */
|
||||
#ifndef CMC_UNIT_TEST_UTL__
|
||||
#define CMC_UNIT_TEST_UTL__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int cmp(size_t a, size_t b)
|
||||
{
|
||||
return (a > b) - (a < b);
|
||||
}
|
||||
|
||||
size_t hash(size_t a)
|
||||
{
|
||||
a += ~(a << 15);
|
||||
a ^= (a >> 10);
|
||||
a += (a << 3);
|
||||
a ^= (a >> 6);
|
||||
a += ~(a << 11);
|
||||
a ^= (a >> 16);
|
||||
return a;
|
||||
}
|
||||
|
||||
#endif /* CMC_UNIT_TEST_UTL__ */
|
||||
+1
-1
@@ -25,7 +25,7 @@ int main(void)
|
||||
queue *q = q_new(1000);
|
||||
deque *d = d_new(1000);
|
||||
linked *ll = ll_new();
|
||||
heap *h = h_new(1000, MaxHeap, intcmp);
|
||||
heap *h = h_new(1000, cmc_max_heap, intcmp);
|
||||
tset *ts = ts_new(intcmp);
|
||||
tmap *tm = tm_new(intcmp);
|
||||
hset *hs = hs_new(1000, 0.9, intcmp, inthash);
|
||||
|
||||
Reference in new issue
Block a user