#ifndef CPU_HCLOG
#define CPU_HCLOG
template <typename T>
static inline bool h_HCLOG(int& csize, byte in [CS], byte out [CS])
{
assert(std::is_unsigned<T>::value);
const int TB = sizeof(T) * 8; const int SC = 32; const int CB = (32 - __builtin_clz(sizeof(T))) + 3; assert((1 << CB) > TB);
assert((1 << (CB - 1)) <= TB);
assert(SC == sizeof(int) * 8);
T* in_t = (T*)in;
T* out_t = (T*)out;
const int size = csize / sizeof(T);
byte ln [SC];
int flags = 0;
int bits = 0;
int end = 0;
for (int i = 0; i < SC; i++) {
const int beg = end;
end = (i + 1) * size / SC;
T max_val1 = 0;
T max_val2 = 0;
for (int j = beg; j < end; j++) {
const T val1 = in_t[j];
const T val2 = (val1 << 1) ^ (((std::make_signed_t<T>)val1) >> (sizeof(T) * 8 - 1)); max_val1 = std::max(max_val1, val1);
max_val2 = std::max(max_val2, val2);
}
const T max_val = std::min(max_val1, max_val2);
if (max_val2 < max_val1) {
flags |= 1 << i;
}
int cnt = 0;
if (max_val != 0) {
cnt = (sizeof(T) == 8) ? (sizeof(unsigned long long) * 8 - __builtin_clzll((unsigned long long)max_val)) : (sizeof(unsigned int) * 8 - __builtin_clz((unsigned int)max_val));
}
bits += cnt * (end - beg);
ln[i] = cnt;
}
const int extra = csize % sizeof(T);
const int newsize = (SC + 16 + CB * SC + bits + 7) / 8;
if (newsize + extra >= CS) return false;
memset(out_t, 0, newsize);
*((int*)out) = flags;
out[4] = csize;
out[5] = csize >> 8;
int loc = SC + 16;
for (int i = 0; i < SC; i++) {
const T val = ln[i];
const int pos = loc / TB;
const int shift = loc % TB;
out_t[pos] |= val << shift;
if (TB - CB < shift) {
out_t[pos + 1] = val >> (TB - shift);
}
loc += CB;
}
end = 0;
for (int i = 0; i < SC; i++) {
const int logn = ln[i];
const int beg = end;
end = (i + 1) * size / SC;
const bool flag = flags & (1 << i);
for (int j = beg; j < end; j++) {
T val = in_t[j];
if (flag) {
val = (val << 1) ^ (((std::make_signed_t<T>)val) >> (sizeof(T) * 8 - 1)); }
const int pos = loc / TB;
const int shift = loc % TB;
out_t[pos] |= val << shift;
if (TB - logn < shift) {
out_t[pos + 1] = val >> (TB - shift);
}
loc += logn;
}
}
if constexpr (sizeof(T) > 1) {
for (int i = 0; i < extra; i++) out[newsize + i] = in[csize - extra + i];
}
csize = newsize + extra;
return true;
}
template <typename T>
static inline void h_iHCLOG(int& csize, byte in [CS], byte out [CS])
{
assert(std::is_unsigned<T>::value);
const int TB = sizeof(T) * 8; const int SC = 32; const int CB = (32 - __builtin_clz(sizeof(T))) + 3; assert((1 << CB) > TB);
assert((1 << (CB - 1)) <= TB);
T* in_t = (T*)in;
T* out_t = (T*)out;
const int flags = *((int*)in);
byte ln [SC];
int loc = SC + 16;
const T mask = ((1 << CB) - 1);
for (int i = 0; i < SC; i++) {
const int pos = loc / TB;
const int shift = loc % TB;
T res = in_t[pos] >> shift;
if (TB - CB < shift) {
res |= in_t[pos + 1] << (TB - shift);
}
ln[i] = res & mask;
loc += CB;
}
const int orig_csize = *((unsigned short*)&in[4]);
const int size = orig_csize / sizeof(T);
int end = 0;
for (int i = 0; i < SC; i++) {
const int logn = ln[i];
const int beg = end;
end = (i + 1) * size / SC;
const T mask = (sizeof(T) < 8) ? ((1ULL << logn) - 1) : ((logn == 64) ? (~0ULL) : ((1ULL << logn) - 1));
const bool flag = flags & (1 << i);
for (int j = beg; j < end; j++) {
const int pos = loc / TB;
const int shift = loc % TB;
T res = in_t[pos] >> shift;
if (TB - logn < shift) {
res |= in_t[pos + 1] << (TB - shift);
}
T val = res & mask;
if (flag) {
val = (val >> 1) ^ ((std::make_signed_t<T>)(val << (sizeof(T) * 8 - 1))) >> (sizeof(T) * 8 - 1); }
out_t[j] = val;
loc += logn;
}
}
if constexpr (sizeof(T) > 1) {
const int extra = orig_csize % sizeof(T);
for (int i = 0; i < extra; i++) out[orig_csize - extra + i] = in[csize - extra + i];
}
csize = orig_csize;
}
#endif