]> git.seodisparate.com - SimpleArchiver/commitdiff
Impl. priority heap, minor tweaks/fixes
authorStephen Seo <seo.disparate@gmail.com>
Sun, 30 Jun 2024 06:32:17 +0000 (15:32 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Sun, 30 Jun 2024 06:32:17 +0000 (15:32 +0900)
Implemented a priority heap.

Added tests for the priority heap.

Add a "default" lcg generator function.

Tweak hash_map to use the default lcg generator function.

`clang-format`.

CMakeLists.txt
cosmopolitan/Makefile
src/algorithms/linear_congruential_gen.c
src/algorithms/linear_congruential_gen.h
src/data_structures/hash_map.c
src/data_structures/priority_heap.c [new file with mode: 0644]
src/data_structures/priority_heap.h [new file with mode: 0644]
src/data_structures/test.c
src/platforms.h

index d929f93f7b400fb1197a4f1569bf9293cc2670fb..3542f022ed660ed0e3eb9cb31df4cc16ee920914 100644 (file)
@@ -8,6 +8,7 @@ set(SimpleArchiver_SOURCES
     src/parser.c
     src/data_structures/linked_list.c
     src/data_structures/hash_map.c
+    src/data_structures/priority_heap.c
     src/algorithms/linear_congruential_gen.c
 )
 
@@ -29,5 +30,6 @@ add_executable(test_datastructures
     src/data_structures/test.c
     src/data_structures/linked_list.c
     src/data_structures/hash_map.c
+    src/data_structures/priority_heap.c
     src/algorithms/linear_congruential_gen.c
 )
index 9f70d86033594627aee2e16d3261b95757195814..098c0a363d60b33c1982049bdb4bd5f6a5226845 100644 (file)
@@ -8,13 +8,15 @@ SOURCES = \
                ../src/parser.c \
                ../src/algorithms/linear_congruential_gen.c \
                ../src/data_structures/linked_list.c \
-               ../src/data_structures/hash_map.c
+               ../src/data_structures/hash_map.c \
+               ../src/data_structures/priority_heap.c
 
 HEADERS = \
                ../src/parser.h \
                ../src/algorithms/linear_congruential_gen.h \
                ../src/data_structures/linked_list.h \
                ../src/data_structures/hash_map.h \
+               ../src/data_structures/priority_heap.h \
                ../src/platforms.h
 
 OBJECTS = $(addprefix ${OBJDIR}/,$(subst ..,PREVDIR,$(patsubst %.c,%.c.o,${SOURCES})))
index da21c7638dd505c2ea45a2d3650acc969db6e62c..374dd5f868f2cb3069b3b17560f78ddb7112b63c 100644 (file)
@@ -25,3 +25,8 @@ unsigned long long simple_archiver_algo_lcg(unsigned long long seed,
   // "m" is implicity 2^64.
   return seed * a + c;
 }
+
+unsigned long long simple_archiver_algo_lcg_defaults(unsigned long long seed) {
+  // "m" is implicity 2^64.
+  return seed * SC_ALGO_LCG_DEFAULT_A + SC_ALGO_LCG_DEFAULT_C;
+}
index a30fe2ab101e4f3640d250a7afcf10794d2b3575..11a59409d39ac2a40ea87938651a086800908ce7 100644 (file)
@@ -27,4 +27,6 @@ unsigned long long simple_archiver_algo_lcg(unsigned long long seed,
                                             unsigned long long a,
                                             unsigned long long c);
 
+unsigned long long simple_archiver_algo_lcg_defaults(unsigned long long seed);
+
 #endif
index f5653ada2f74ca29447ad81450e9218bc38a1eb2..d1f04d1f2a89621ac46d2989976f3c3f3722fbf1 100644 (file)
@@ -86,8 +86,7 @@ unsigned long long simple_archiver_hash_map_internal_key_to_hash(
     seed += temp;
   }
 
-  return simple_archiver_algo_lcg(seed, SC_ALGO_LCG_DEFAULT_A,
-                                  SC_ALGO_LCG_DEFAULT_C);
+  return simple_archiver_algo_lcg_defaults(seed);
 }
 
 /// Returns 0 on success.
diff --git a/src/data_structures/priority_heap.c b/src/data_structures/priority_heap.c
new file mode 100644 (file)
index 0000000..9e5a0b1
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2024 Stephen Seo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * `priority_heap.c` is the source for a priority heap implementation.
+ */
+
+#include "priority_heap.h"
+
+#include <stdlib.h>
+
+void simple_archiver_priority_heap_internal_realloc(
+    SDArchiverPHeap **priority_heap) {
+  SDArchiverPHeap *new_priority_heap = malloc(sizeof(SDArchiverPHeap));
+
+  new_priority_heap->capacity = (*priority_heap)->capacity * 2;
+  new_priority_heap->size = 0;
+
+  new_priority_heap->nodes =
+      calloc(new_priority_heap->capacity, sizeof(SDArchiverPHNode));
+
+  for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
+    if ((*priority_heap)->nodes[idx].is_valid != 0) {
+      simple_archiver_priority_heap_insert(
+          &new_priority_heap, (*priority_heap)->nodes[idx].priority,
+          (*priority_heap)->nodes[idx].data,
+          (*priority_heap)->nodes[idx].data_cleanup_fn);
+      (*priority_heap)->nodes[idx].is_valid = 0;
+    }
+  }
+
+  simple_archiver_priority_heap_free(priority_heap);
+
+  *priority_heap = new_priority_heap;
+}
+
+SDArchiverPHeap *simple_archiver_priority_heap_init(void) {
+  SDArchiverPHeap *priority_heap = malloc(sizeof(SDArchiverPHeap));
+
+  priority_heap->capacity = SC_SA_DS_PRIORITY_HEAP_START_SIZE;
+  priority_heap->size = 0;
+
+  priority_heap->nodes =
+      calloc(priority_heap->capacity, sizeof(SDArchiverPHNode));
+
+  return priority_heap;
+}
+
+void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap) {
+  if (priority_heap && *priority_heap) {
+    for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
+      if ((*priority_heap)->nodes[idx].is_valid != 0) {
+        if ((*priority_heap)->nodes[idx].data_cleanup_fn) {
+          (*priority_heap)
+              ->nodes[idx]
+              .data_cleanup_fn((*priority_heap)->nodes[idx].data);
+        } else {
+          free((*priority_heap)->nodes[idx].data);
+        }
+        (*priority_heap)->nodes[idx].is_valid = 0;
+      }
+    }
+
+    free((*priority_heap)->nodes);
+    free(*priority_heap);
+    *priority_heap = NULL;
+  }
+}
+
+void simple_archiver_priority_heap_insert(SDArchiverPHeap **priority_heap,
+                                          long long priority, void *data,
+                                          void (*data_cleanup_fn)(void *)) {
+  if (!priority_heap || !*priority_heap) {
+    return;
+  }
+
+  if ((*priority_heap)->size + 1 >= (*priority_heap)->capacity) {
+    simple_archiver_priority_heap_internal_realloc(priority_heap);
+  }
+
+  unsigned int hole = (*priority_heap)->size + 1;
+
+  while (hole > 1 && priority < (*priority_heap)->nodes[hole / 2].priority) {
+    (*priority_heap)->nodes[hole] = (*priority_heap)->nodes[hole / 2];
+    hole /= 2;
+  }
+
+  (*priority_heap)->nodes[hole].priority = priority;
+  (*priority_heap)->nodes[hole].data = data;
+  (*priority_heap)->nodes[hole].data_cleanup_fn = data_cleanup_fn;
+  (*priority_heap)->nodes[hole].is_valid = 1;
+
+  ++(*priority_heap)->size;
+}
+
+void *simple_archiver_priority_heap_top(SDArchiverPHeap *priority_heap) {
+  if (priority_heap && priority_heap->size != 0) {
+    return priority_heap->nodes[1].data;
+  }
+
+  return NULL;
+}
+
+void *simple_archiver_priority_heap_pop(SDArchiverPHeap *priority_heap) {
+  if (!priority_heap || priority_heap->size == 0) {
+    return NULL;
+  }
+
+  void *data = priority_heap->nodes[1].data;
+  priority_heap->nodes[1].is_valid = 0;
+
+  SDArchiverPHNode end = priority_heap->nodes[priority_heap->size];
+  priority_heap->nodes[priority_heap->size].is_valid = 0;
+
+  unsigned int hole = 1;
+  while (hole * 2 + 1 <= priority_heap->size) {
+    if (priority_heap->nodes[hole * 2].is_valid != 0 &&
+        priority_heap->nodes[hole * 2 + 1].is_valid != 0) {
+      if (end.priority < priority_heap->nodes[hole * 2].priority &&
+          end.priority < priority_heap->nodes[hole * 2 + 1].priority) {
+        break;
+      }
+      if (priority_heap->nodes[hole * 2].priority <
+          priority_heap->nodes[hole * 2 + 1].priority) {
+        priority_heap->nodes[hole] = priority_heap->nodes[hole * 2];
+        hole = hole * 2;
+      } else {
+        priority_heap->nodes[hole] = priority_heap->nodes[hole * 2 + 1];
+        hole = hole * 2 + 1;
+      }
+    } else if (priority_heap->nodes[hole * 2].is_valid != 0) {
+      if (end.priority < priority_heap->nodes[hole * 2].priority) {
+        break;
+      }
+      priority_heap->nodes[hole] = priority_heap->nodes[hole * 2];
+      hole = hole * 2;
+      break;
+    } else {
+      break;
+    }
+  }
+
+  priority_heap->nodes[hole] = end;
+
+  --priority_heap->size;
+
+  return data;
+}
diff --git a/src/data_structures/priority_heap.h b/src/data_structures/priority_heap.h
new file mode 100644 (file)
index 0000000..840ed5f
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 Stephen Seo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * `priority_heap.h` is the header for a priority heap implementation.
+ */
+
+#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_
+#define SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_
+
+#define SC_SA_DS_PRIORITY_HEAP_START_SIZE 32
+
+typedef struct SDArchiverPHNode {
+  long long priority;
+  void *data;
+  void (*data_cleanup_fn)(void *);
+  /// Is non-zero if valid.
+  int is_valid;
+} SDArchiverPHNode;
+
+typedef struct SDArchiverPHeap {
+  SDArchiverPHNode *nodes;
+  unsigned long long capacity;
+  unsigned long long size;
+} SDArchiverPHeap;
+
+SDArchiverPHeap *simple_archiver_priority_heap_init(void);
+void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap);
+
+/// If data_cleanup_fn is NULL, then "free()" is used on data when popped or
+/// freed.
+void simple_archiver_priority_heap_insert(SDArchiverPHeap **priority_heap,
+                                          long long priority, void *data,
+                                          void (*data_cleanup_fn)(void *));
+
+/// Returns NULL if empty or if priority_heap is NULL.
+void *simple_archiver_priority_heap_top(SDArchiverPHeap *priority_heap);
+
+/// Returns NULL if empty or if priority_heap is NULL.
+/// When data is popped, the data_cleanup_fn is ignored and the user must take
+/// ownership of the returned data pointer.
+void *simple_archiver_priority_heap_pop(SDArchiverPHeap *priority_heap);
+
+#endif
index 721232e7e0a33965d1a92ac9e5f665d404580b1c..db909579207f205ba1504f0672614211b54c79bd 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include "../algorithms/linear_congruential_gen.h"
 #include "hash_map.h"
 #include "linked_list.h"
+#include "priority_heap.h"
 
 static int checks_checked = 0;
 static int checks_passed = 0;
@@ -151,6 +153,114 @@ int main(void) {
     simple_archiver_hash_map_free(&hash_map);
   }
 
+  // Test PriorityHeap.
+  {
+    SDArchiverPHeap *priority_heap = simple_archiver_priority_heap_init();
+    simple_archiver_priority_heap_free(&priority_heap);
+
+    priority_heap = simple_archiver_priority_heap_init();
+
+    // Just 3 elements.
+    for (unsigned int idx = 0; idx < 3; ++idx) {
+      unsigned int *data = malloc(sizeof(unsigned int));
+      *data = idx;
+      simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
+    }
+    for (unsigned int idx = 0; idx < 3; ++idx) {
+      unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
+      CHECK_TRUE(*data == idx);
+      if (*data != idx) {
+        printf("idx is %u, data is %u\n", idx, *data);
+      }
+      data = simple_archiver_priority_heap_pop(priority_heap);
+      CHECK_TRUE(*data == idx);
+      if (*data != idx) {
+        printf("idx is %u, data is %u\n", idx, *data);
+      }
+      free(data);
+    }
+
+    // 100 elements.
+    unsigned int max = 100;
+
+    for (unsigned int idx = 0; idx < max; ++idx) {
+      unsigned int *data = malloc(sizeof(unsigned int));
+      *data = idx;
+      simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
+    }
+
+    for (unsigned int idx = 0; idx < max; ++idx) {
+      unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
+      CHECK_TRUE(*data == idx);
+      data = simple_archiver_priority_heap_pop(priority_heap);
+      CHECK_TRUE(*data == idx);
+      free(data);
+    }
+
+    // Insert in reverse order.
+    for (unsigned int idx = max; idx-- > 0;) {
+      unsigned int *data = malloc(sizeof(unsigned int));
+      *data = idx;
+      simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
+    }
+
+    for (unsigned int idx = 0; idx < max; ++idx) {
+      unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
+      CHECK_TRUE(*data == idx);
+      data = simple_archiver_priority_heap_pop(priority_heap);
+      CHECK_TRUE(*data == idx);
+      free(data);
+    }
+
+    // Insert in random order.
+    unsigned int *array = malloc(sizeof(unsigned int) * max);
+    for (unsigned int idx = 0; idx < max; ++idx) {
+      array[idx] = idx;
+    }
+
+    // Deterministic randomization.
+    for (unsigned int idx = max - 1; idx-- > 0;) {
+      unsigned int other_idx = simple_archiver_algo_lcg_defaults(idx) %
+                               (unsigned long long)(idx + 1);
+      if (idx != other_idx) {
+        unsigned int temp = array[max - 1];
+        array[max - 1] = array[other_idx];
+        array[other_idx] = temp;
+      }
+    }
+
+    // Insert the deterministically randomized array.
+    for (unsigned int idx = 0; idx < max; ++idx) {
+      simple_archiver_priority_heap_insert(&priority_heap, array[idx],
+                                           array + idx, no_free_fn);
+    }
+
+    for (unsigned int idx = 0; idx < max; ++idx) {
+      unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
+      CHECK_TRUE(*data == idx);
+      if (*data != idx) {
+        printf("idx is %u, data is %u\n", idx, *data);
+      }
+      data = simple_archiver_priority_heap_pop(priority_heap);
+      CHECK_TRUE(*data == idx);
+      if (*data != idx) {
+        printf("idx is %u, data is %u\n", idx, *data);
+      }
+    }
+    free(array);
+
+    simple_archiver_priority_heap_free(&priority_heap);
+
+    // Insert, don't pop, do free, for memcheck.
+    priority_heap = simple_archiver_priority_heap_init();
+    for (unsigned int idx = 0; idx < max; ++idx) {
+      unsigned int *data = malloc(sizeof(unsigned int));
+      *data = idx;
+      simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
+    }
+    simple_archiver_priority_heap_free(&priority_heap);
+  }
+
   printf("Checks checked: %u\n", checks_checked);
   printf("Checks passed:  %u\n", checks_passed);
   return checks_passed == checks_checked ? 0 : 1;
index 950f683aa331b39f8b8bbf65c60614e130cb22c9..c7fd36a3c667b6b7aa780ce194f2b166087a4521 100644 (file)
 #define SIMPLE_ARCHIVER_PLATFORM_UNKNOWN 0
 
 #if defined __COSMOPOLITAN__
-# define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN
+#define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN
 #elif defined _WIN32
-# define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_WINDOWS
+#define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_WINDOWS
 #elif defined __APPLE__
-# define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_MAC
+#define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_MAC
 #elif defined __linux__
-# define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_LINUX
+#define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_LINUX
 #else
-# define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_UNKNOWN
+#define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_UNKNOWN
 #endif