#include <assert.h>
#include <stdio.h>
#include "common.h"
#include "large.h"
#define MASK32 0xFFFFFFFF
INTERNAL void zint_large_load_str_u64(large_uint *t, const unsigned char *s, const int length) {
uint64_t val = 0;
const unsigned char *const se = s + length;
for (; s < se && z_isdigit(*s); s++) {
val *= 10;
val += *s - '0';
}
t->lo = val;
t->hi = 0;
}
INTERNAL void zint_large_add(large_uint *t, const large_uint *s) {
t->lo += s->lo;
t->hi += s->hi + (t->lo < s->lo);
}
INTERNAL void zint_large_add_u64(large_uint *t, const uint64_t s) {
t->lo += s;
if (t->lo < s) {
t->hi++;
}
}
INTERNAL void zint_large_sub_u64(large_uint *t, const uint64_t s) {
uint64_t r = t->lo - s;
if (r > t->lo) {
t->hi--;
}
t->lo = r;
}
INTERNAL void zint_large_mul_u64(large_uint *t, const uint64_t s) {
uint64_t thi = t->hi;
uint64_t tlo0 = t->lo & MASK32;
uint64_t tlo1 = t->lo >> 32;
uint64_t s0 = s & MASK32;
uint64_t s1 = s >> 32;
uint64_t tmp = s0 * tlo0;
uint64_t p00 = tmp & MASK32;
uint64_t k10;
tmp = (s1 * tlo0) + (tmp >> 32);
k10 = tmp >> 32;
tmp = (s0 * tlo1) + (tmp & MASK32);
t->lo = (tmp << 32) + p00;
t->hi = (s1 * tlo1) + k10 + (tmp >> 32) + thi * s;
}
static int clz_u64(uint64_t x) {
uint64_t n = 64, y;
y = x >> 32; if (y) { n -= 32; x = y; }
y = x >> 16; if (y) { n -= 16; x = y; }
y = x >> 8; if (y) { n -= 8; x = y; }
y = x >> 4; if (y) { n -= 4; x = y; }
y = x >> 2; if (y) { n -= 2; x = y; }
y = x >> 1; if (y) { n -= 1; x = y; }
return (int) (n - x);
}
#ifdef ZINT_TEST
INTERNAL int zint_test_clz_u64(uint64_t x) {
return clz_u64(x);
}
#endif
INTERNAL uint64_t zint_large_div_u64(large_uint *t, uint64_t v) {
const uint64_t b = 0x100000000;
uint64_t qhi = 0;
uint64_t tnhi, tnlo, tnlo1, tnlo0, vn1, vn0;
uint64_t rnhilo1;
uint64_t qhat1, qhat0;
uint64_t rhat;
uint64_t tmp;
int norm_shift;
if (v < b) {
qhi = t->hi / v;
tmp = ((t->hi - qhi * v) << 32) + (t->lo >> 32);
qhat1 = tmp / v;
tmp = ((tmp - qhat1 * v) << 32) + (t->lo & MASK32);
qhat0 = tmp / v;
t->lo = (qhat1 << 32) | qhat0;
t->hi = qhi;
return tmp - qhat0 * v;
}
if (t->hi >= v) {
qhi = t->hi / v;
t->hi %= v;
}
norm_shift = clz_u64(v);
v <<= norm_shift;
vn1 = v >> 32;
vn0 = v & MASK32;
if (norm_shift > 0) {
tnhi = (t->hi << norm_shift) | (t->lo >> (64 - norm_shift));
tnlo = t->lo << norm_shift;
} else {
tnhi = t->hi;
tnlo = t->lo;
}
tnlo1 = tnlo >> 32;
tnlo0 = tnlo & MASK32;
assert(vn1 != 0);
qhat1 = tnhi / vn1;
rhat = tnhi % vn1;
for (tmp = qhat1 * vn0; qhat1 >= b || tmp > (rhat << 32) + tnlo1; tmp -= vn0) {
--qhat1;
rhat += vn1;
if (rhat >= b) {
break;
}
}
rnhilo1 = (tnhi << 32) + tnlo1 - (qhat1 * v);
qhat0 = rnhilo1 / vn1;
rhat = rnhilo1 % vn1;
for (tmp = qhat0 * vn0; qhat0 >= b || tmp > (rhat << 32) + tnlo0; tmp -= vn0) {
--qhat0;
rhat += vn1;
if (rhat >= b) {
break;
}
}
t->lo = (qhat1 << 32) | qhat0;
t->hi = qhi;
return ((rnhilo1 << 32) + tnlo0 - (qhat0 * v)) >> norm_shift;
}
INTERNAL void zint_large_unset_bit(large_uint *t, const int bit) {
if (bit < 64) {
t->lo &= ~(((uint64_t) 1) << bit);
} else if (bit < 128) {
t->hi &= ~(((uint64_t) 1) << (bit - 64));
}
}
INTERNAL void zint_large_uint_array(const large_uint *t, unsigned int *uint_array, const int size, int bits) {
int i, j;
uint64_t mask;
if (bits <= 0) {
bits = 8;
} else if (bits > 32) {
bits = 32;
}
mask = ~(((uint64_t) -1) << bits);
for (i = 0, j = 0; i < size && j < 64; i++, j += bits) {
uint_array[size - 1 - i] = (unsigned int) ((t->lo >> j) & mask);
}
if (i < size) {
if (j != 64) {
j -= 64;
uint_array[size - i] = (unsigned int) (((t->hi & ~((((uint64_t) -1) << j))) << (bits - j))
| (t->lo >> (64 - (bits - j)) & mask));
} else {
j = 0;
}
for (; i < size && j < 64; i++, j += bits) {
uint_array[size - 1 - i] = (unsigned int) ((t->hi >> j) & mask);
}
if (i < size) {
memset(uint_array, 0, sizeof(unsigned int) * (size - i));
}
}
}
INTERNAL void zint_large_uchar_array(const large_uint *t, unsigned char *uchar_array, const int size, int bits) {
int i;
unsigned int *uint_array = (unsigned int *) z_alloca(sizeof(unsigned int) * (size ? size : 1));
zint_large_uint_array(t, uint_array, size, bits);
for (i = 0; i < size; i++) {
uchar_array[i] = (unsigned char) uint_array[i];
}
}
INTERNAL char *zint_large_dump(const large_uint *t, char *buf) {
unsigned int tlo1 = (unsigned int) (zint_large_lo(t) >> 32);
unsigned int tlo0 = (unsigned int) (zint_large_lo(t) & MASK32);
unsigned int thi1 = (unsigned int) (zint_large_hi(t) >> 32);
unsigned int thi0 = (unsigned int) (zint_large_hi(t) & MASK32);
if (thi1) {
sprintf(buf, "0x%X%08X%08X%08X", thi1, thi0, tlo1, tlo0);
} else if (thi0) {
sprintf(buf, "0x%X%08X%08X", thi0, tlo1, tlo0);
} else if (tlo1) {
sprintf(buf, "0x%X%08X", tlo1, tlo0);
} else {
sprintf(buf, "0x%X", tlo0);
}
return buf;
}
INTERNAL void zint_large_print(const large_uint *t) {
char buf[35];
puts(zint_large_dump(t, buf));
}