#include #include #define INVALID_INDEX ((c_size_t)-1) /* ------------------------------------------------------------------------------------------------------------------ */ /* */ // Internal Helper: Swaps two locations inside the heap and updates their inverse map positions C_STATIC_FORCE_INLINE void swap_nodes(c_IndexPQ_t* self, c_size_t i, c_size_t j) { c_size_t temp = self->heap[i]; self->heap[i] = self->heap[j]; self->heap[j] = temp; // Synchronize inverse lookups mapping: ID -> heap position index self->inverse_heap[self->heap[i]] = i; self->inverse_heap[self->heap[j]] = j; } // Internal Helper: Balanced restoration path upwards (O(log N)) C_STATIC_FORCE_INLINE void heapify_up(c_IndexPQ_t* self, c_size_t index) { while (index > 0) { c_size_t parent = (index - 1) / 2; void* curr_val = (char*)self->keys_data + (self->heap[index] * self->obj_size); void* parent_val = (char*)self->keys_data + (self->heap[parent] * self->obj_size); if (self->compare(curr_val, parent_val) < 0) { swap_nodes(self, index, parent); index = parent; } else { break; } } } // Internal Helper: Balanced restoration path downwards (O(log N)) C_STATIC_FORCE_INLINE void heapify_down(c_IndexPQ_t* self, c_size_t index) { c_size_t left, right, smallest; while (1) { left = 2 * index + 1; right = 2 * index + 2; smallest = index; void* smallest_val = (char*)self->keys_data + (self->heap[smallest] * self->obj_size); if (left < self->size) { void* left_val = (char*)self->keys_data + (self->heap[left] * self->obj_size); if (self->compare(left_val, smallest_val) < 0) { smallest = left; smallest_val = left_val; } } if (right < self->size) { void* right_val = (char*)self->keys_data + (self->heap[right] * self->obj_size); if (self->compare(right_val, smallest_val) < 0) { smallest = right; } } if (smallest != index) { swap_nodes(self, index, smallest); index = smallest; } else { break; } } } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ c_err_t c_IndexPQ_Init(c_IndexPQ_t* self, int obj_size, c_size_t max_size, c_IndexPQ_Compare_f compare) { if (!self || obj_size <= 0 || max_size == 0 || !compare) return C_ERR_PARAM; self->obj_size = obj_size; self->max_size = max_size; self->size = 0; self->compare = compare; self->keys_data = C_ALLOC(self->max_size * self->obj_size); self->heap = (c_size_t*)C_ALLOC(self->max_size * sizeof(c_size_t)); self->inverse_heap = (c_size_t*)C_ALLOC(self->max_size * sizeof(c_size_t)); if (!self->keys_data || !self->heap || !self->inverse_heap) { c_IndexPQ_Destroy(self); return C_ERR_NOMEM; } // Initialize inverse heap mappings to an invalid sentinel value representing emptiness for (c_size_t i = 0; i < self->max_size; i++) { self->inverse_heap[i] = INVALID_INDEX; } return C_ERR_SUCCESS; } void c_IndexPQ_Destroy(c_IndexPQ_t* self) { if (!self) return; if (self->keys_data) C_FREE(self->keys_data); if (self->heap) C_FREE(self->heap); if (self->inverse_heap) C_FREE(self->inverse_heap); self->keys_data = NULL; self->heap = NULL; self->inverse_heap = NULL; self->max_size = 0; self->size = 0; self->obj_size = 0; } c_bool_t c_IndexPQ_Contains(const c_IndexPQ_t* self, c_size_t id) { if (!self || id >= self->max_size) return C_FALSE; return self->inverse_heap[id] != INVALID_INDEX; } // Inserts an entry associated with a fixed identifier ID into the heap (O(log N)) c_err_t c_IndexPQ_Push(c_IndexPQ_t* self, c_size_t id, const void* obj) { if (!self || !obj) return C_ERR_PARAM; if (id >= self->max_size) return C_ERR_FULL; if (c_IndexPQ_Contains(self, id)) return C_ERR_ALREADY_EXISTS; // Map user payload value into the slot indexed directly by ID char* slot = (char*)self->keys_data + (id * self->obj_size); memcpy(slot, obj, self->obj_size); // Place the ID at the end of the active binary heap self->heap[self->size] = id; self->inverse_heap[id] = self->size; self->size++; heapify_up(self, self->size - 1); return C_ERR_SUCCESS; } // Pops the minimum elements out while tracking its associated identifier value (O(log N)) c_err_t c_IndexPQ_Pop(c_IndexPQ_t* self, c_size_t* out_id, void* out_obj) { if (!self || !out_id || !out_obj) return C_ERR_PARAM; if (self->size == 0) return C_ERR_EMPTY; c_size_t min_id = self->heap[0]; *out_id = min_id; // Extract values into safe user-allocated buffers void* slot = (char*)self->keys_data + (min_id * self->obj_size); memcpy(out_obj, slot, self->obj_size); // Swap top node with the last active entry node element swap_nodes(self, 0, self->size - 1); self->inverse_heap[min_id] = INVALID_INDEX; // Clear historical identifier tracking values self->size--; if (self->size > 0) { heapify_down(self, 0); } return C_ERR_SUCCESS; } // Modifies the priority value for an arbitrary key ID anywhere inside the queue in O(log N) time c_err_t c_IndexPQ_Change(c_IndexPQ_t* self, c_size_t id, const void* new_obj) { if (!self || !new_obj) return C_ERR_PARAM; if (!c_IndexPQ_Contains(self, id)) return C_ERR_NOT_FOUND; // Overwrite the existing data value char* slot = (char*)self->keys_data + (id * self->obj_size); memcpy(slot, new_obj, self->obj_size); // Retrieve its current heap position to fix the balance path triggers c_size_t heap_pos = self->inverse_heap[id]; heapify_up(self, heap_pos); heapify_down(self, heap_pos); return C_ERR_SUCCESS; } void* c_IndexPQ_PeekValue(c_IndexPQ_t* self) { if (!self || self->size == 0) return NULL; return (char*)self->keys_data + (self->heap[0] * self->obj_size); } long long c_IndexPQ_PeekID(c_IndexPQ_t* self) { if (!self || self->size == 0) return -1; return (long long)self->heap[0]; } c_size_t c_IndexPQ_GetSize(const c_IndexPQ_t* self) { if (!self) return 0; return self->size; } c_bool_t c_IndexPQ_IsEmpty(const c_IndexPQ_t* self) { if (!self) return C_TRUE; return self->size == 0; }