#pragma once
#include <stdint.h>
#include <stdlib.h>
#include "../sk_memory.h"
#ifndef ARRAY_MALLOC
#define ARRAY_MALLOC sk::sk_malloc
#endif
#ifndef ARRAY_FREE
#define ARRAY_FREE sk::_sk_free
#endif
#ifndef ARRAY_REALLOC
#define ARRAY_REALLOC sk::sk_realloc
#endif
#ifndef ARRAY_MEMCPY
#include <string.h>
#define ARRAY_MEMCPY memcpy
#endif
#ifndef ARRAY_MEMMOVE
#include <string.h>
#define ARRAY_MEMMOVE memmove
#endif
#ifndef ARRAY_ASSERT
#include <assert.h>
#define ARRAY_ASSERT assert
#endif
template <typename T>
struct array_view_t {
void *data;
int32_t count;
int32_t stride;
size_t offset;
void each(void (*e)(T &)) { for (int32_t i=0; i<count; i++) e((T&)((uint8_t*)data + i*stride + offset)); }
T &last() const { return (T&)((uint8_t*)data + (count-1)*stride + offset); }
T *copy_deinterlace() const;
inline void set (int32_t id, const T &val) { *(T*)((uint8_t*)data + id*stride + offset) = val; }
inline T &get (int32_t id) const { return ((T*)((uint8_t*)data + id*stride + offset))[0]; }
inline T &operator[] (int32_t id) const { return ((T*)((uint8_t*)data + id*stride + offset))[0]; }
};
template <typename T>
struct array_t {
T *data;
int32_t count;
int32_t capacity;
int32_t add (const T &item) { if (count+1 > capacity) { resize(capacity * 2 < 4 ? 4 : capacity * 2); } data[count] = item; count += 1; return count - 1; }
void add_range (const T *list, int32_t num) { if (count+num > capacity) { resize(capacity * 2 < count+num ? count+num : capacity * 2); } ARRAY_MEMCPY(&data[count], list, sizeof(T)*num); count += num; }
void add_empties( int32_t num) { if (count+num > capacity) { resize(capacity * 2 < count+num ? count+num : capacity * 2); } memset(&data[count],0, sizeof(T) * num); count += num; }
void insert (int32_t at, const T &item);
void resize (int32_t to_capacity);
void trim () { resize(count); }
void remove (int32_t at);
void pop () { remove(count - 1); }
void clear () { count = 0; }
T &last () const { return data[count - 1]; }
inline void set (int32_t id, const T &val) { data[id] = val; }
inline T &get (int32_t id) const { return data[id]; }
inline T &operator[] (int32_t id) const { return data[id]; }
void reverse ();
array_t<T> copy () const;
void each (void (*e)(T &)) { for (int32_t i=0; i<count; i++) e(data[i]); }
void each (void (*e)(const T &)) const { for (int32_t i=0; i<count; i++) e(data[i]); }
void each (void (*e)(void *)) { for (int32_t i=0; i<count; i++) e(data[i]); }
template <typename U>
void each_with (U *with, void (*e)(U*, const T &)) const { for (int32_t i=0; i<count; i++) e(with, data[i]); }
template <typename U>
U each_with (void (*e)(U *, const T &)) const { U result = {}; for (int32_t i = 0; i < count; i++) e(&result, data[i]); }
template <typename U>
U each_sum (U (*e)(const T &)) const { U result = 0; for (int32_t i = 0; i < count; i++) result += e(data[i]); return result; }
template <typename U>
array_t<U> each_new (U (*e)(const T &)) const { array_t<U> result = {}; result.resize(count); for (int32_t i=0; i<count; i++) result.add(e(data[i])); return result; }
void free ();
static array_t<T> make (int32_t capacity) { array_t<T> result = {}; result.resize(capacity); return result; }
static array_t<T> make_fill(int32_t capacity, const T ©_from) { array_t<T> result = {}; result.resize(capacity); result.count = capacity; for(int32_t i=0;i<capacity;i+=1) result.data[i]=copy_from; return result; }
static array_t<T> make_from(T *use_memory, int32_t count) { return {use_memory, count, count}; }
int32_t index_of (const T &item) const { for (int32_t i = 0; i < count; i++) if (memcmp(&data[i], &item, sizeof(T)) == 0) return i; return -1; }
template <typename _T, typename D>
int32_t index_where(const D _T::*key, const D &item) const { const size_t offset = (size_t)&((_T*)0->*key); for (int32_t i = 0; i < count; i++) if (memcmp(((uint8_t *)&data[i]) + offset, &item, sizeof(D)) == 0) return i; return -1; }
int32_t index_where(bool (*c)(const T &item, void *user_data), void *user_data) const { for (int32_t i=0; i<count; i++) if (c(data[i], user_data)) return i; return -1;}
int32_t index_where(bool (*c)(const T &item)) const { for (int32_t i=0; i<count; i++) if (c(data[i])) return i; return -1;}
int32_t index_best_small(int32_t (*c)(const T &item)) const { int32_t best = 0x7fffffff; int32_t result = -1; for (int32_t i=0; i<count; i++) { int32_t r = c(data[i]); if (r < best) { best = r; result = i;} } return result; }
int32_t index_best_large(int32_t (*c)(const T &item)) const { int32_t best = 0x80000000; int32_t result = -1; for (int32_t i=0; i<count; i++) { int32_t r = c(data[i]); if (r > best) { best = r; result = i;} } return result;}
template <typename U>
int32_t index_best_small_with(U with, int32_t (*c)(U, const T &item)) const { int32_t best = 0x7fffffff; int32_t result = -1; for (int32_t i=0; i<count; i++) { int32_t r = c(with, data[i]); if (r < best) { best = r; result = i;} } return result; }
template <typename U>
int32_t index_best_large_with(U with, int32_t (*c)(U, const T &item)) const { int32_t best = -0x80000000; int32_t result = -1; for (int32_t i=0; i<count; i++) { int32_t r = c(with, data[i]); if (r > best) { best = r; result = i;} } return result; }
int32_t binary_search(const T &item) const;
template <typename _T, typename D>
int32_t binary_search(const D _T::*key, const D &item) const {
array_view_t<D> view = array_view_t<D>{data, count, sizeof(_T), (size_t)&((_T*)nullptr->*key)};
int32_t l = 0, r = view.count - 1;
while (l <= r) {
int32_t mid = (l+r) / 2;
D mid_val = view[mid];
if (mid_val < item) l = mid + 1;
else if (mid_val > item) r = mid - 1;
else return mid;
}
return r < 0 ? r : -(r+2);
}
void sort (int32_t (*compare)(const T&a, const T&b)) { qsort(data, count, sizeof(T), (int (*)(void const*, void const*))compare); }
void sort () { qsort(data, count, sizeof(T), [](const void *a, const void *b) {T fa = *(T*)a, fb = *(T*)b; return (int32_t)((fa > fb) - (fa < fb));}); }
void sort_desc() { qsort(data, count, sizeof(T), [](const void *a, const void *b) {T fa = *(T*)a, fb = *(T*)b; return (int32_t)((fa < fb) - (fa > fb));}); }
template <typename _T, typename D, D _T::*key>
void sort() {
qsort((uint8_t*)data, count, sizeof(_T), [](const void *a, const void *b) {
size_t offset = (size_t)&((_T*)0->*key); D fa = *(D*)((uint8_t*)a + offset), fb = *(D*)((uint8_t*)b + offset); return (int32_t)((fa > fb) - (fa < fb));
});
}
template <typename _T, typename D, D _T::*key>
void sort_desc() {
qsort((uint8_t*)data, count, sizeof(_T), [](const void *a, const void *b) {
size_t offset = (size_t)&((_T*)0->*key); D fa = *(D*)((uint8_t*)a + offset), fb = *(D*)((uint8_t*)b + offset); return (int32_t)((fa < fb) - (fa > fb));
});
}
void _array_reorder(void** list, int32_t item_size, int32_t sort_order_count, int32_t* sort_order) {
uint8_t* src = (uint8_t*)*list;
uint8_t* result = (uint8_t*)ARRAY_MALLOC(item_size * capacity);
for (int32_t i = 0; i < sort_order_count; i++) {
memcpy(&result[i * item_size], &src[sort_order[i] * item_size], item_size);
}
*list = result;
ARRAY_FREE(src);
}
void reorder(int32_t* item_order) {
_array_reorder((void**)&data, sizeof(T), count, item_order);
}
};
const float _hashmap_search_dist_pct = 0.001f;
const int32_t _hashmap_search_dist_min = 3;
template <typename K, typename T>
struct hashmap_t {
struct entry_t {
uint64_t hash;
K key;
T value;
};
entry_t* items;
int32_t count;
int32_t capacity;
uint64_t _hash(const K &key) const {
uint64_t hash = 14695981039346656037UL;
const uint8_t *bytes = (const uint8_t *)&key;
for (int32_t i=0; i<sizeof(K); i++)
hash = (hash ^ bytes[i]) * 1099511628211;
return hash;
}
void resize(int32_t size) {
if (size < count)
size = count;
if (size == capacity) return;
entry_t* old_items = items;
int32_t old_capacity = capacity;
items = (entry_t*)ARRAY_MALLOC(sizeof(entry_t) * size);
capacity = size;
count = 0;
memset(items, 0, sizeof(entry_t) * size);
for (int32_t i = 0; i < old_capacity; i++) {
if (old_items[i].hash == 0) continue;
set(old_items[i].key, old_items[i].value);
}
ARRAY_FREE(old_items);
}
int32_t set(const K &key, const T &value) {
if (count+1 >= capacity) {
resize(capacity == 0 ? 4 : capacity * 2);
}
int32_t search_distance = (int32_t)(capacity * _hashmap_search_dist_pct);
if (search_distance < _hashmap_search_dist_min)
search_distance = _hashmap_search_dist_min;
uint64_t hash = _hash(key);
int32_t id = hash % capacity;
while (items[id].hash != 0 && search_distance > 0) {
if (items[id].hash == hash && memcmp(&items[id].key, &key, sizeof(K)) == 0)
break;
id += 1;
search_distance -= 1;
if (id >= capacity) id = 0;
}
if (items[id].hash != 0 && items[id].hash != hash) {
resize(capacity * 2);
id = set(key, value);
return id;
}
if (items[id].hash != hash) {
items[id].key = key;
count += 1;
}
items[id].hash = hash;
items[id].value = value;
return id;
}
int32_t contains(const K& key) {
if (capacity == 0) return -1;
uint64_t hash = _hash(key);
int32_t id = hash % capacity;
int32_t search_distance = (int32_t)(capacity * _hashmap_search_dist_pct);
if (search_distance < _hashmap_search_dist_min)
search_distance = _hashmap_search_dist_min;
while (search_distance >= 0) {
if (items[id].hash == 0) return -1;
if (memcmp(&items[id].key, &key, sizeof(K)) == 0)
return id;
id += 1;
search_distance -= 1;
if (id >= capacity) id = 0;
}
return -1;
}
T *get(const K &key) {
int32_t id = contains(key);
if (id == -1)
return nullptr;
return id == -1
? nullptr
: &items[id].value;
}
const T* get_or(const K& key, const T& default_value) {
int32_t id = contains(key);
return id == -1
? default_value
: &items[id].value;
}
void free () { ARRAY_FREE(items); *this = {}; }
bool remove (const K& key) { int32_t at = contains(key); if (at != -1) { if (items[at].hash != 0) { count--; } items[at].hash = 0; } return at != -1; }
void remove_at(const int32_t at) { if (items[at].hash != 0) { count--; } items[at].hash = 0; }
};
template <typename T>
struct dictionary_t {
struct entry_t {
uint64_t hash;
char* key;
T value;
};
entry_t* items;
int32_t count;
int32_t capacity;
uint64_t _hash(const char* string) const {
uint64_t hash = 14695981039346656037UL;
uint8_t c = string[0];
for (size_t i = 0; i < 64; i++) {
if (c != 0) c = string[i];
hash = (hash ^ c) * 1099511628211;
}
return hash;
}
char *_string_copy(const char *string) {
size_t size = strlen(string) + 1;
char *result = (char*)ARRAY_MALLOC(size);
memcpy(result, string, size);
return result;
}
void resize(int32_t size) {
if (size < count)
size = count;
if (size == capacity) return;
entry_t* old_items = items;
int32_t old_capacity = capacity;
items = (entry_t*)ARRAY_MALLOC(sizeof(entry_t) * size);
capacity = size;
count = 0;
memset(items, 0, sizeof(entry_t) * size);
for (int32_t i = 0; i < old_capacity; i++) {
if (old_items[i].hash == 0) continue;
set(old_items[i].key, old_items[i].value);
}
ARRAY_FREE(old_items);
}
int32_t set(const char *key, const T &value) {
if (count+1 >= capacity) {
resize(capacity == 0 ? 4 : capacity * 2);
}
int32_t search_distance = (int32_t)(capacity * _hashmap_search_dist_pct);
if (search_distance < _hashmap_search_dist_min)
search_distance = _hashmap_search_dist_min;
uint64_t hash = _hash(key);
int32_t id = hash % capacity;
while (items[id].hash != 0 && search_distance > 0) {
if (items[id].hash == hash && strcmp(items[id].key, key) == 0)
break;
id += 1;
search_distance -= 1;
if (id >= capacity) id = 0;
}
if (items[id].hash != 0 && items[id].hash != hash) {
resize(capacity * 2);
id = set(key, value);
return id;
}
if (items[id].hash != hash) {
items[id].key = _string_copy(key);
count += 1;
}
items[id].hash = hash;
items[id].value = value;
return id;
}
int32_t contains(const char* key) {
if (capacity == 0) return -1;
uint64_t hash = _hash(key);
int32_t id = hash % capacity;
int32_t search_distance = (int32_t)(capacity * _hashmap_search_dist_pct);
if (search_distance < _hashmap_search_dist_min)
search_distance = _hashmap_search_dist_min;
while (search_distance >= 0) {
if (items[id].hash == 0) return -1;
if (strcmp(items[id].key, key) == 0)
return id;
id += 1;
search_distance -= 1;
if (id >= capacity) id = 0;
}
return -1;
}
T *get(const char* key) {
int32_t id = contains(key);
if (id == -1)
return nullptr;
return id == -1
? nullptr
: &items[id].value;
}
const T* get_or(const char* key, const T& default_value) {
int32_t id = contains(key);
return id == -1
? default_value
: &items[id].value;
}
void each (void (*e)(T&)) { for (int32_t i = 0; i < count; i++) if (items[i].hash != 0) e(items[i].value); }
void free () { for(int i=0;i<capacity;i+=1) {if (items[i].hash != 0) ARRAY_FREE(items[i].key); } ARRAY_FREE(items); *this = {}; }
bool remove (const char* key) { int32_t at = contains(key); if (at != -1) { if (items[at].hash != 0) { count--; } items[at].hash = 0; ARRAY_FREE(items[at].key); } return at != -1; }
void remove_at(const int32_t at) { if (items[at].hash != 0) { count--; } items[at].hash = 0; }
};
template <typename T>
int32_t array_t<T>::binary_search(const T &item) const {
int32_t l = 0, r = count - 1;
while (l <= r) {
int32_t mid = (l+r) / 2;
if (get(mid) < item) l = mid + 1;
else if (get(mid) > item) r = mid - 1;
else return mid;
}
return r < 0 ? r : -(r+2);
}
template <typename T>
void array_t<T>::resize(int32_t to_capacity) {
data = (T*)ARRAY_REALLOC(data, sizeof(T) * to_capacity);
capacity = to_capacity;
if (count > to_capacity)
count = to_capacity;
}
template <typename T>
void array_t<T>::free() {
ARRAY_FREE(data);
*this = {};
}
template <typename T>
array_t<T> array_t<T>::copy() const {
array_t<T> result = {
(T*)ARRAY_MALLOC(sizeof(T) * capacity),
count,
capacity
};
ARRAY_MEMCPY(result.data, data, sizeof(T) * count);
return result;
}
template <typename T>
void array_t<T>::remove(int32_t aAt) {
ARRAY_ASSERT(aAt < count);
ARRAY_MEMMOVE(&data[aAt], &data[aAt+1], (count - (aAt + 1))*sizeof(T));
count -= 1;
}
template <typename T>
void array_t<T>::insert(int32_t aAt, const T &item) {
ARRAY_ASSERT(aAt <= count);
if (count+1 > capacity)
resize(capacity<1?1:capacity*2);
ARRAY_MEMMOVE(&data[aAt+1], &data[aAt], (count-aAt)*sizeof(T));
ARRAY_MEMCPY (&data[aAt], &item, sizeof(T));
count += 1;
}
template <typename T>
void array_t<T>::reverse() {
for(int32_t i=0; i<count/2; i+=1) {
T tmp = this->get(i);
this->set(i, this->get(count - i - 1));
this->set(count - i - 1, tmp);
}
}
template <typename D, typename T>
inline static array_view_t<D> array_view_create(const T *data, int32_t count, D T::*key) {
return array_view_t<D>{data, count, sizeof(T), (int32_t)&((T*)nullptr->*key)};
}
template <typename D, typename T>
inline static array_view_t<D> array_view_create(const array_t<T> &src, D T::*key) {
return array_view_t<D>{src.data, src.count, sizeof(T), (int32_t)&((T*)nullptr->*key)};
}
template <typename T>
inline static array_view_t<T> array_view_create(const array_t<T> &src) {
return array_view_t<T>{src.data, src.count, sizeof(T), 0};
}
template <typename T>
T *array_view_t<T>::copy_deinterlace() const {
T *result = (T*)ARRAY_MALLOC(sizeof(T) * count);
for (int32_t i=0; i<count; i++) {
result[i] = this->get(i);
}
return result;
}