2016-10-14 11:28:34 -07:00
|
|
|
// Equihash solver
|
2016-10-14 12:30:52 -07:00
|
|
|
// Copyright (c) 2016 John Tromp
|
2016-10-14 11:28:34 -07:00
|
|
|
|
|
|
|
// Fix N, K, such that n = N/(k+1) is integer
|
|
|
|
// Fix M = 2^{n+1} hashes each of length N bits,
|
|
|
|
// H_0, ... , H_{M-1}, generated fom (n+1)-bit indices.
|
|
|
|
// Problem: find binary tree on 2^K distinct indices,
|
|
|
|
// for which the exclusive-or of leaf hashes is all 0s.
|
|
|
|
// Additionally, it should satisfy the Wagner conditions:
|
|
|
|
// for each height i subtree, the exclusive-or
|
|
|
|
// of its 2^i corresponding hashes starts with i*n 0 bits,
|
|
|
|
// and for i>0 the leftmost leaf of its left subtree
|
|
|
|
// is less than the leftmost leaf of its right subtree
|
|
|
|
|
2016-10-20 09:06:27 -07:00
|
|
|
// The algorithm below solves this by maintaining the tree
|
2016-10-14 11:28:34 -07:00
|
|
|
// in a graph of K layers, each split into buckets
|
|
|
|
// with buckets indexed by the first n-RESTBITS bits following
|
|
|
|
// the i*n 0s, each bucket having 4 * 2^RESTBITS slots,
|
|
|
|
// twice the number of subtrees expected to land there.
|
|
|
|
|
|
|
|
#include "equi.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
2016-10-21 14:51:42 -07:00
|
|
|
#if defined __builtin_bswap32 && defined __LITTLE_ENDIAN
|
|
|
|
#undef htobe32
|
|
|
|
#define htobe32(x) __builtin_bswap32(x)
|
2016-10-23 07:55:10 -07:00
|
|
|
#elif defined __APPLE__
|
|
|
|
#undef htobe32
|
|
|
|
#define htobe32(x) OSSwapHostToBigInt32(x)
|
2016-10-21 14:51:42 -07:00
|
|
|
#endif
|
|
|
|
|
2016-10-17 11:52:30 -07:00
|
|
|
typedef uint16_t u16;
|
2016-10-14 11:28:34 -07:00
|
|
|
typedef uint64_t u64;
|
|
|
|
|
|
|
|
#ifdef ATOMIC
|
|
|
|
#include <atomic>
|
|
|
|
typedef std::atomic<u32> au32;
|
|
|
|
#else
|
|
|
|
typedef u32 au32;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef RESTBITS
|
2016-10-17 11:52:30 -07:00
|
|
|
#define RESTBITS 8
|
2016-10-14 11:28:34 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// 2_log of number of buckets
|
|
|
|
#define BUCKBITS (DIGITBITS-RESTBITS)
|
|
|
|
|
2016-10-18 19:07:54 -07:00
|
|
|
#ifndef SAVEMEM
|
|
|
|
#if RESTBITS == 4
|
|
|
|
// can't save memory in such small buckets
|
|
|
|
#define SAVEMEM 1
|
|
|
|
#elif RESTBITS >= 8
|
|
|
|
// take advantage of law of large numbers (sum of 2^8 random numbers)
|
|
|
|
// this reduces (200,9) memory to under 144MB, with negligible discarding
|
|
|
|
#define SAVEMEM 9/14
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2016-10-14 11:28:34 -07:00
|
|
|
// number of buckets
|
|
|
|
static const u32 NBUCKETS = 1<<BUCKBITS;
|
2016-10-20 16:19:19 -07:00
|
|
|
// bucket mask
|
|
|
|
static const u32 BUCKMASK = NBUCKETS-1;
|
2016-10-14 11:28:34 -07:00
|
|
|
// 2_log of number of slots per bucket
|
|
|
|
static const u32 SLOTBITS = RESTBITS+1+1;
|
2016-10-18 19:07:54 -07:00
|
|
|
static const u32 SLOTRANGE = 1<<SLOTBITS;
|
2016-10-19 08:53:24 -07:00
|
|
|
static const u32 SLOTMSB = 1<<(SLOTBITS-1);
|
2016-10-14 11:28:34 -07:00
|
|
|
// number of slots per bucket
|
2016-10-18 19:07:54 -07:00
|
|
|
static const u32 NSLOTS = SLOTRANGE * SAVEMEM;
|
2016-10-14 11:28:34 -07:00
|
|
|
// number of per-xhash slots
|
2016-10-18 14:47:36 -07:00
|
|
|
static const u32 XFULL = 16;
|
2016-10-14 11:28:34 -07:00
|
|
|
// SLOTBITS mask
|
2016-10-18 19:07:54 -07:00
|
|
|
static const u32 SLOTMASK = SLOTRANGE-1;
|
2016-10-14 11:28:34 -07:00
|
|
|
// number of possible values of xhash (rest of n) bits
|
|
|
|
static const u32 NRESTS = 1<<RESTBITS;
|
|
|
|
// number of blocks of hashes extracted from single 512 bit blake2b output
|
|
|
|
static const u32 NBLOCKS = (NHASHES+HASHESPERBLAKE-1)/HASHESPERBLAKE;
|
|
|
|
// nothing larger found in 100000 runs
|
|
|
|
static const u32 MAXSOLS = 8;
|
|
|
|
|
|
|
|
// tree node identifying its children as two different slots in
|
|
|
|
// a bucket on previous layer with the same rest bits (x-tra hash)
|
|
|
|
struct tree {
|
2016-10-19 08:53:24 -07:00
|
|
|
u32 bid_s0_s1; // manual bitfields
|
2016-10-14 11:28:34 -07:00
|
|
|
|
2016-10-19 08:53:24 -07:00
|
|
|
tree(const u32 idx) {
|
|
|
|
bid_s0_s1 = idx;
|
|
|
|
}
|
|
|
|
tree(const u32 bid, const u32 s0, const u32 s1) {
|
|
|
|
#ifdef SLOTDIFF
|
|
|
|
u32 ds10 = (s1 - s0) & SLOTMASK;
|
|
|
|
if (ds10 & SLOTMSB) {
|
|
|
|
bid_s0_s1 = (((bid << SLOTBITS) | s1) << (SLOTBITS-1)) | (SLOTMASK & ~ds10);
|
|
|
|
} else {
|
|
|
|
bid_s0_s1 = (((bid << SLOTBITS) | s0) << (SLOTBITS-1)) | (ds10 - 1);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
bid_s0_s1 = (((bid << SLOTBITS) | s0) << SLOTBITS) | s1;
|
|
|
|
#endif
|
|
|
|
}
|
2016-10-14 11:28:34 -07:00
|
|
|
u32 getindex() const {
|
2016-10-19 08:53:24 -07:00
|
|
|
return bid_s0_s1;
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
2016-10-19 08:53:24 -07:00
|
|
|
u32 bucketid() const {
|
2016-10-19 14:49:11 -07:00
|
|
|
#ifdef SLOTDIFF
|
|
|
|
return bid_s0_s1 >> (2 * SLOTBITS - 1);
|
|
|
|
#else
|
|
|
|
return bid_s0_s1 >> (2 * SLOTBITS);
|
|
|
|
#endif
|
2016-10-19 08:53:24 -07:00
|
|
|
}
|
|
|
|
u32 slotid0() const {
|
|
|
|
#ifdef SLOTDIFF
|
|
|
|
return (bid_s0_s1 >> (SLOTBITS-1)) & SLOTMASK;
|
|
|
|
#else
|
|
|
|
return (bid_s0_s1 >> SLOTBITS) & SLOTMASK;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
u32 slotid1() const {
|
|
|
|
#ifdef SLOTDIFF
|
|
|
|
return (slotid0() + 1 + (bid_s0_s1 & (SLOTMASK>>1))) & SLOTMASK;
|
|
|
|
#else
|
|
|
|
return bid_s0_s1 & SLOTMASK;
|
|
|
|
#endif
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-10-20 09:06:27 -07:00
|
|
|
union htunit {
|
|
|
|
tree tag;
|
2016-10-17 11:52:30 -07:00
|
|
|
u32 word;
|
2016-10-14 11:28:34 -07:00
|
|
|
uchar bytes[sizeof(u32)];
|
|
|
|
};
|
|
|
|
|
2016-10-17 11:52:30 -07:00
|
|
|
#define WORDS(bits) ((bits + 31) / 32)
|
|
|
|
#define HASHWORDS0 WORDS(WN - DIGITBITS + RESTBITS)
|
|
|
|
#define HASHWORDS1 WORDS(WN - 2*DIGITBITS + RESTBITS)
|
|
|
|
|
2016-10-20 09:06:27 -07:00
|
|
|
// A slot is up to HASHWORDS0 hash units followed by a tag
|
|
|
|
typedef htunit slot0[HASHWORDS0+1];
|
|
|
|
typedef htunit slot1[HASHWORDS1+1];
|
2016-10-14 11:28:34 -07:00
|
|
|
// a bucket is NSLOTS treenodes
|
2016-10-17 11:52:30 -07:00
|
|
|
typedef slot0 bucket0[NSLOTS];
|
|
|
|
typedef slot1 bucket1[NSLOTS];
|
2016-10-14 11:28:34 -07:00
|
|
|
// the N-bit hash consists of K+1 n-bit "digits"
|
|
|
|
// each of which corresponds to a layer of NBUCKETS buckets
|
2016-10-17 11:52:30 -07:00
|
|
|
typedef bucket0 digit0[NBUCKETS];
|
|
|
|
typedef bucket1 digit1[NBUCKETS];
|
2016-10-20 09:06:27 -07:00
|
|
|
typedef au32 bsizes[NBUCKETS];
|
|
|
|
|
|
|
|
u32 min(const u32 a, const u32 b) {
|
|
|
|
return a < b ? a : b;
|
|
|
|
}
|
2016-10-14 11:28:34 -07:00
|
|
|
|
|
|
|
// size (in bytes) of hash in round 0 <= r < WK
|
|
|
|
u32 hashsize(const u32 r) {
|
|
|
|
const u32 hashbits = WN - (r+1) * DIGITBITS + RESTBITS;
|
|
|
|
return (hashbits + 7) / 8;
|
|
|
|
}
|
|
|
|
|
2016-10-17 11:52:30 -07:00
|
|
|
u32 hashwords(u32 bytes) {
|
|
|
|
return (bytes + 3) / 4;
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// manages hash and tree data
|
|
|
|
struct htalloc {
|
2016-10-20 09:06:27 -07:00
|
|
|
bucket0 *heap0;
|
|
|
|
bucket1 *heap1;
|
2016-10-16 15:49:03 -07:00
|
|
|
u32 alloced;
|
2016-10-14 11:28:34 -07:00
|
|
|
htalloc() {
|
|
|
|
alloced = 0;
|
|
|
|
}
|
|
|
|
void alloctrees() {
|
2016-10-15 17:16:42 -07:00
|
|
|
// optimize xenoncat's fixed memory layout, avoiding any waste
|
2016-10-20 09:06:27 -07:00
|
|
|
// digit hashes tree hashes tree
|
|
|
|
// 0 A A A A A A 0 . . . . . .
|
|
|
|
// 1 A A A A A A 0 B B B B B 1
|
|
|
|
// 2 C C C C C 2 0 B B B B B 1
|
|
|
|
// 3 C C C C C 2 0 D D D D 3 1
|
|
|
|
// 4 E E E E 4 2 0 D D D D 3 1
|
|
|
|
// 5 E E E E 4 2 0 F F F 5 3 1
|
|
|
|
// 6 G G 6 . 4 2 0 F F F 5 3 1
|
|
|
|
// 7 G G 6 . 4 2 0 H H 7 5 3 1
|
|
|
|
// 8 I 8 6 . 4 2 0 H H 7 5 3 1
|
2016-10-15 17:16:42 -07:00
|
|
|
assert(DIGITBITS >= 16); // ensures hashes shorten by 1 unit every 2 digits
|
2016-10-20 09:06:27 -07:00
|
|
|
heap0 = (bucket0 *)alloc(NBUCKETS, sizeof(bucket0));
|
|
|
|
heap1 = (bucket1 *)alloc(NBUCKETS, sizeof(bucket1));
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
void dealloctrees() {
|
2016-10-17 11:52:30 -07:00
|
|
|
free(heap0);
|
|
|
|
free(heap1);
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
void *alloc(const u32 n, const u32 sz) {
|
|
|
|
void *mem = calloc(n, sz);
|
|
|
|
assert(mem);
|
2016-10-16 15:49:03 -07:00
|
|
|
alloced += n * sz;
|
2016-10-14 11:28:34 -07:00
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct equi {
|
|
|
|
blake2b_state blake_ctx;
|
|
|
|
htalloc hta;
|
2016-10-20 09:06:27 -07:00
|
|
|
bsizes *nslots;
|
2016-10-14 11:28:34 -07:00
|
|
|
proof *sols;
|
|
|
|
au32 nsols;
|
|
|
|
u32 nthreads;
|
|
|
|
u32 xfull;
|
|
|
|
u32 bfull;
|
2016-10-20 09:06:27 -07:00
|
|
|
u32 hfull;
|
2016-10-14 11:28:34 -07:00
|
|
|
pthread_barrier_t barry;
|
|
|
|
equi(const u32 n_threads) {
|
2016-10-20 09:06:27 -07:00
|
|
|
assert(sizeof(htunit) == 4);
|
2016-10-20 15:35:18 -07:00
|
|
|
assert(WK&1); // assumed in candidate() calling indices1()
|
2016-10-14 11:28:34 -07:00
|
|
|
nthreads = n_threads;
|
|
|
|
const int err = pthread_barrier_init(&barry, NULL, nthreads);
|
|
|
|
assert(!err);
|
|
|
|
hta.alloctrees();
|
2016-10-17 11:52:30 -07:00
|
|
|
nslots = (bsizes *)hta.alloc(2 * NBUCKETS, sizeof(au32));
|
|
|
|
sols = (proof *)hta.alloc(MAXSOLS, sizeof(proof));
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
~equi() {
|
2016-10-15 17:16:42 -07:00
|
|
|
hta.dealloctrees();
|
2016-10-14 11:28:34 -07:00
|
|
|
free(nslots);
|
|
|
|
free(sols);
|
|
|
|
}
|
2016-10-18 07:35:10 -07:00
|
|
|
void setnonce(const char *header, const u32 headerlen, const u32 nonce) {
|
|
|
|
setheader(&blake_ctx, header, headerlen, nonce);
|
2016-10-14 11:28:34 -07:00
|
|
|
memset(nslots, 0, NBUCKETS * sizeof(au32)); // only nslots[0] needs zeroing
|
2016-10-20 09:06:27 -07:00
|
|
|
nsols = xfull = bfull = hfull = 0;
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 getslot0(const u32 bucketi) {
|
2016-10-14 11:28:34 -07:00
|
|
|
#ifdef ATOMIC
|
2016-10-20 15:57:39 -07:00
|
|
|
return std::atomic_fetch_add_explicit(&nslots[0][bucketi], 1U, std::memory_order_relaxed);
|
2016-10-14 11:28:34 -07:00
|
|
|
#else
|
2016-10-20 15:57:39 -07:00
|
|
|
return nslots[0][bucketi]++;
|
2016-10-14 11:28:34 -07:00
|
|
|
#endif
|
|
|
|
}
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 getslot1(const u32 bucketi) {
|
|
|
|
#ifdef ATOMIC
|
|
|
|
return std::atomic_fetch_add_explicit(&nslots[1][bucketi], 1U, std::memory_order_relaxed);
|
|
|
|
#else
|
|
|
|
return nslots[1][bucketi]++;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
u32 getnslots0(const u32 bid) {
|
|
|
|
au32 &nslot = nslots[0][bid];
|
|
|
|
const u32 n = min(nslot, NSLOTS);
|
|
|
|
nslot = 0;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
u32 getnslots1(const u32 bid) {
|
|
|
|
au32 &nslot = nslots[1][bid];
|
2016-10-14 11:28:34 -07:00
|
|
|
const u32 n = min(nslot, NSLOTS);
|
|
|
|
nslot = 0;
|
|
|
|
return n;
|
|
|
|
}
|
2016-10-21 20:47:41 -07:00
|
|
|
#ifdef MERGESORT
|
2016-10-20 15:35:18 -07:00
|
|
|
// if merged != 0, mergesort indices and return true if dupe found
|
|
|
|
// if merged == 0, order indices as in Wagner condition
|
|
|
|
bool orderindices(u32 *indices, u32 size, u32 *merged) {
|
|
|
|
if (merged) {
|
|
|
|
u32 i = 0, j = 0, k;
|
|
|
|
for (k = 0; i<size && j<size; k++) {
|
|
|
|
if (indices[i] == indices[size+j]) return true;
|
|
|
|
merged[k] = indices[i] < indices[size+j] ? indices[i++] : indices[size+j++];
|
2016-10-17 11:52:30 -07:00
|
|
|
}
|
2016-10-20 15:35:18 -07:00
|
|
|
memcpy(merged+k, indices+i, (size-i) * sizeof(u32));
|
|
|
|
memcpy(indices, merged, (size+j) * sizeof(u32));
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
if (indices[0] > indices[size]) {
|
|
|
|
for (u32 i=0; i < size; i++) {
|
|
|
|
const u32 tmp = indices[i];
|
|
|
|
indices[i] = indices[size+i];
|
|
|
|
indices[size+i] = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2016-10-17 11:52:30 -07:00
|
|
|
}
|
|
|
|
}
|
2016-10-20 09:06:27 -07:00
|
|
|
// return true if dupe found
|
2016-10-20 15:35:18 -07:00
|
|
|
bool listindices0(u32 r, const tree t, u32 *indices, u32 *merged) {
|
2016-10-14 11:28:34 -07:00
|
|
|
if (r == 0) {
|
2016-10-20 15:35:18 -07:00
|
|
|
*indices = t.getindex();
|
2016-10-20 09:06:27 -07:00
|
|
|
return false;
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
2016-10-20 09:06:27 -07:00
|
|
|
const slot1 *buck = hta.heap1[t.bucketid()];
|
|
|
|
const u32 size = 1 << --r;
|
2016-10-14 11:28:34 -07:00
|
|
|
u32 *indices1 = indices + size;
|
2016-10-20 09:06:27 -07:00
|
|
|
u32 tagi = hashwords(hashsize(r));
|
2016-10-21 20:47:41 -07:00
|
|
|
return listindices1(r, buck[t.slotid0()][tagi].tag, indices, merged)
|
2016-10-20 15:35:18 -07:00
|
|
|
|| listindices1(r, buck[t.slotid1()][tagi].tag, indices1, merged)
|
|
|
|
|| orderindices(indices, size, merged);
|
2016-10-17 11:52:30 -07:00
|
|
|
}
|
2016-10-20 15:35:18 -07:00
|
|
|
bool listindices1(u32 r, const tree t, u32 *indices, u32 *merged) {
|
2016-10-20 09:06:27 -07:00
|
|
|
const slot0 *buck = hta.heap0[t.bucketid()];
|
|
|
|
const u32 size = 1 << --r;
|
2016-10-17 11:52:30 -07:00
|
|
|
u32 *indices1 = indices + size;
|
2016-10-20 09:06:27 -07:00
|
|
|
u32 tagi = hashwords(hashsize(r));
|
2016-10-21 20:47:41 -07:00
|
|
|
return listindices0(r, buck[t.slotid0()][tagi].tag, indices, merged)
|
2016-10-20 15:35:18 -07:00
|
|
|
|| listindices0(r, buck[t.slotid1()][tagi].tag, indices1, merged)
|
|
|
|
|| orderindices(indices, size, merged);
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
void candidate(const tree t) {
|
2016-10-20 15:35:18 -07:00
|
|
|
proof prf, merged;
|
|
|
|
if (listindices1(WK, t, prf, merged)) return;
|
2016-10-14 11:28:34 -07:00
|
|
|
#ifdef ATOMIC
|
|
|
|
u32 soli = std::atomic_fetch_add_explicit(&nsols, 1U, std::memory_order_relaxed);
|
|
|
|
#else
|
|
|
|
u32 soli = nsols++;
|
|
|
|
#endif
|
2016-10-20 15:35:18 -07:00
|
|
|
if (soli < MAXSOLS) listindices1(WK, t, sols[soli], 0);
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
2016-10-21 16:25:45 -07:00
|
|
|
#else
|
2016-10-21 20:47:41 -07:00
|
|
|
bool orderindices(u32 *indices, u32 size) {
|
2016-10-21 16:25:45 -07:00
|
|
|
if (indices[0] > indices[size]) {
|
|
|
|
for (u32 i=0; i < size; i++) {
|
|
|
|
const u32 tmp = indices[i];
|
|
|
|
indices[i] = indices[size+i];
|
|
|
|
indices[size+i] = tmp;
|
|
|
|
}
|
|
|
|
}
|
2016-10-21 20:47:41 -07:00
|
|
|
return false;
|
2016-10-21 16:25:45 -07:00
|
|
|
}
|
2016-10-21 20:47:41 -07:00
|
|
|
// if dupes != 0, list indices in arbitrary order and return true if dupe found
|
|
|
|
// if dupes == 0, order indices as in Wagner condition
|
|
|
|
bool listindices0(u32 r, const tree t, u32 *indices, u32 *dupes) {
|
2016-10-21 16:25:45 -07:00
|
|
|
if (r == 0) {
|
|
|
|
u32 idx = t.getindex();
|
|
|
|
if (dupes) {
|
|
|
|
u32 bin = idx & (PROOFSIZE-1);
|
2016-10-21 20:47:41 -07:00
|
|
|
if (idx == dupes[bin]) return true;
|
|
|
|
dupes[bin] = idx;
|
2016-10-21 16:25:45 -07:00
|
|
|
}
|
|
|
|
*indices = idx;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const slot1 *buck = hta.heap1[t.bucketid()];
|
|
|
|
const u32 size = 1 << --r;
|
|
|
|
u32 tagi = hashwords(hashsize(r));
|
2016-10-21 20:47:41 -07:00
|
|
|
return listindices1(r, buck[t.slotid0()][tagi].tag, indices, dupes)
|
|
|
|
|| listindices1(r, buck[t.slotid1()][tagi].tag, indices+size, dupes)
|
|
|
|
|| (!dupes && orderindices(indices, size));
|
2016-10-21 16:25:45 -07:00
|
|
|
}
|
2016-10-21 20:47:41 -07:00
|
|
|
bool listindices1(u32 r, const tree t, u32 *indices, u32 *dupes) {
|
2016-10-21 16:25:45 -07:00
|
|
|
const slot0 *buck = hta.heap0[t.bucketid()];
|
|
|
|
const u32 size = 1 << --r;
|
|
|
|
u32 tagi = hashwords(hashsize(r));
|
2016-10-21 20:47:41 -07:00
|
|
|
return listindices0(r, buck[t.slotid0()][tagi].tag, indices, dupes)
|
|
|
|
|| listindices0(r, buck[t.slotid1()][tagi].tag, indices+size, dupes)
|
|
|
|
|| (!dupes && orderindices(indices, size));
|
2016-10-21 16:25:45 -07:00
|
|
|
}
|
|
|
|
void candidate(const tree t) {
|
2016-10-21 20:47:41 -07:00
|
|
|
proof prf, dupes;
|
|
|
|
memset(dupes, 0xffff, sizeof(proof));
|
|
|
|
if (listindices1(WK, t, prf, dupes)) return; // assume WK odd
|
2016-10-21 16:25:45 -07:00
|
|
|
qsort(prf, PROOFSIZE, sizeof(u32), &compu32);
|
2016-10-21 20:47:41 -07:00
|
|
|
for (u32 i=1; i<PROOFSIZE; i++) if (prf[i] <= prf[i-1]) return;
|
2016-10-21 16:25:45 -07:00
|
|
|
#ifdef ATOMIC
|
|
|
|
u32 soli = std::atomic_fetch_add_explicit(&nsols, 1U, std::memory_order_relaxed);
|
|
|
|
#else
|
|
|
|
u32 soli = nsols++;
|
|
|
|
#endif
|
2016-10-21 20:47:41 -07:00
|
|
|
if (soli < MAXSOLS) listindices1(WK, t, sols[soli], 0); // assume WK odd
|
2016-10-21 16:25:45 -07:00
|
|
|
}
|
|
|
|
#endif
|
2016-10-14 11:28:34 -07:00
|
|
|
void showbsizes(u32 r) {
|
2016-10-20 09:06:27 -07:00
|
|
|
printf(" x%d b%d h%d\n", xfull, bfull, hfull);
|
|
|
|
xfull = bfull = hfull = 0;
|
2016-10-17 11:52:30 -07:00
|
|
|
#if defined(HIST) || defined(SPARK) || defined(LOGSPARK)
|
|
|
|
u32 binsizes[65];
|
|
|
|
memset(binsizes, 0, 65 * sizeof(u32));
|
2016-10-14 11:28:34 -07:00
|
|
|
for (u32 bucketid = 0; bucketid < NBUCKETS; bucketid++) {
|
2016-10-17 11:52:30 -07:00
|
|
|
u32 bsize = min(nslots[r&1][bucketid], NSLOTS) >> (SLOTBITS-6);
|
|
|
|
binsizes[bsize]++;
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
2016-10-17 11:52:30 -07:00
|
|
|
for (u32 i=0; i < 65; i++) {
|
2016-10-14 11:28:34 -07:00
|
|
|
#ifdef HIST
|
2016-10-17 11:52:30 -07:00
|
|
|
printf(" %d:%d", i, binsizes[i]);
|
|
|
|
#else
|
|
|
|
#ifdef SPARK
|
|
|
|
u32 sparks = binsizes[i] / SPARKSCALE;
|
2016-10-14 11:28:34 -07:00
|
|
|
#else
|
2016-10-17 11:52:30 -07:00
|
|
|
u32 sparks = 0;
|
|
|
|
for (u32 bs = binsizes[i]; bs; bs >>= 1) sparks++;
|
|
|
|
sparks = sparks * 7 / SPARKSCALE;
|
|
|
|
#endif
|
|
|
|
printf("\342\226%c", '\201' + sparks);
|
2016-10-14 11:28:34 -07:00
|
|
|
#endif
|
|
|
|
}
|
2016-10-16 15:49:03 -07:00
|
|
|
printf("\n");
|
2016-10-14 11:28:34 -07:00
|
|
|
#endif
|
2016-10-22 19:06:52 -07:00
|
|
|
printf("Digit %d", r+1);
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
struct htlayout {
|
|
|
|
htalloc hta;
|
2016-10-20 09:06:27 -07:00
|
|
|
u32 prevhtunits;
|
|
|
|
u32 nexthtunits;
|
2016-10-14 11:28:34 -07:00
|
|
|
u32 dunits;
|
|
|
|
u32 prevbo;
|
|
|
|
|
2016-10-20 09:06:27 -07:00
|
|
|
htlayout(equi *eq, u32 r): hta(eq->hta), prevhtunits(0), dunits(0) {
|
2016-10-14 11:28:34 -07:00
|
|
|
u32 nexthashbytes = hashsize(r);
|
2016-10-20 09:06:27 -07:00
|
|
|
nexthtunits = hashwords(nexthashbytes);
|
2016-10-14 11:28:34 -07:00
|
|
|
prevbo = 0;
|
|
|
|
if (r) {
|
|
|
|
u32 prevhashbytes = hashsize(r-1);
|
2016-10-20 09:06:27 -07:00
|
|
|
prevhtunits = hashwords(prevhashbytes);
|
|
|
|
prevbo = prevhtunits * sizeof(htunit) - prevhashbytes; // 0-3
|
|
|
|
dunits = prevhtunits - nexthtunits;
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
}
|
2016-10-20 09:06:27 -07:00
|
|
|
u32 getxhash0(const htunit* slot) const {
|
2016-10-17 11:52:30 -07:00
|
|
|
#if WN == 200 && RESTBITS == 4
|
2016-10-20 09:06:27 -07:00
|
|
|
return slot->bytes[prevbo] >> 4;
|
2016-10-17 11:52:30 -07:00
|
|
|
#elif WN == 200 && RESTBITS == 8
|
2016-10-20 09:06:27 -07:00
|
|
|
return (slot->bytes[prevbo] & 0xf) << 4 | slot->bytes[prevbo+1] >> 4;
|
2016-10-17 11:52:30 -07:00
|
|
|
#elif WN == 144 && RESTBITS == 4
|
2016-10-20 09:06:27 -07:00
|
|
|
return slot->bytes[prevbo] & 0xf;
|
2016-10-14 11:28:34 -07:00
|
|
|
#else
|
2016-10-17 11:52:30 -07:00
|
|
|
#error non implemented
|
2016-10-14 11:28:34 -07:00
|
|
|
#endif
|
|
|
|
}
|
2016-10-20 09:06:27 -07:00
|
|
|
u32 getxhash1(const htunit* slot) const {
|
2016-10-17 11:52:30 -07:00
|
|
|
#if WN == 200 && RESTBITS == 4
|
2016-10-20 09:06:27 -07:00
|
|
|
return slot->bytes[prevbo] & 0xf;
|
2016-10-17 11:52:30 -07:00
|
|
|
#elif WN == 200 && RESTBITS == 8
|
2016-10-20 09:06:27 -07:00
|
|
|
return slot->bytes[prevbo];
|
2016-10-17 11:52:30 -07:00
|
|
|
#elif WN == 144 && RESTBITS == 4
|
2016-10-20 09:06:27 -07:00
|
|
|
return slot->bytes[prevbo] & 0xf;
|
2016-10-17 11:52:30 -07:00
|
|
|
#else
|
|
|
|
#error non implemented
|
|
|
|
#endif
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
2016-10-20 09:06:27 -07:00
|
|
|
bool equal(const htunit *hash0, const htunit *hash1) const {
|
|
|
|
return hash0[prevhtunits-1].word == hash1[prevhtunits-1].word;
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct collisiondata {
|
|
|
|
#ifdef XBITMAP
|
2016-10-17 11:52:30 -07:00
|
|
|
#if NSLOTS > 64
|
|
|
|
#error cant use XBITMAP with more than 64 slots
|
|
|
|
#endif
|
2016-10-14 11:28:34 -07:00
|
|
|
u64 xhashmap[NRESTS];
|
|
|
|
u64 xmap;
|
|
|
|
#else
|
2016-10-17 11:52:30 -07:00
|
|
|
#if RESTBITS <= 6
|
2016-10-14 11:28:34 -07:00
|
|
|
typedef uchar xslot;
|
2016-10-17 11:52:30 -07:00
|
|
|
#else
|
|
|
|
typedef u16 xslot;
|
|
|
|
#endif
|
2016-10-14 11:28:34 -07:00
|
|
|
xslot nxhashslots[NRESTS];
|
|
|
|
xslot xhashslots[NRESTS][XFULL];
|
|
|
|
xslot *xx;
|
|
|
|
u32 n0;
|
|
|
|
u32 n1;
|
|
|
|
#endif
|
|
|
|
u32 s0;
|
|
|
|
|
|
|
|
void clear() {
|
|
|
|
#ifdef XBITMAP
|
|
|
|
memset(xhashmap, 0, NRESTS * sizeof(u64));
|
|
|
|
#else
|
|
|
|
memset(nxhashslots, 0, NRESTS * sizeof(xslot));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
bool addslot(u32 s1, u32 xh) {
|
|
|
|
#ifdef XBITMAP
|
|
|
|
xmap = xhashmap[xh];
|
|
|
|
xhashmap[xh] |= (u64)1 << s1;
|
|
|
|
s0 = -1;
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
n1 = (u32)nxhashslots[xh]++;
|
|
|
|
if (n1 >= XFULL)
|
|
|
|
return false;
|
|
|
|
xx = xhashslots[xh];
|
|
|
|
xx[n1] = s1;
|
|
|
|
n0 = 0;
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
bool nextcollision() const {
|
|
|
|
#ifdef XBITMAP
|
|
|
|
return xmap != 0;
|
|
|
|
#else
|
|
|
|
return n0 < n1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
u32 slot() {
|
|
|
|
#ifdef XBITMAP
|
|
|
|
const u32 ffs = __builtin_ffsll(xmap);
|
|
|
|
s0 += ffs; xmap >>= ffs;
|
|
|
|
return s0;
|
|
|
|
#else
|
|
|
|
return (u32)xx[n0++];
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void digit0(const u32 id) {
|
|
|
|
uchar hash[HASHOUT];
|
2016-10-22 19:06:52 -07:00
|
|
|
blake2b_state state0 = blake_ctx;
|
2016-10-14 11:28:34 -07:00
|
|
|
htlayout htl(this, 0);
|
|
|
|
const u32 hashbytes = hashsize(0);
|
|
|
|
for (u32 block = id; block < NBLOCKS; block += nthreads) {
|
2016-10-22 19:06:52 -07:00
|
|
|
blake2b_state state = state0;
|
2016-10-17 11:52:30 -07:00
|
|
|
u32 leb = htole32(block);
|
2016-10-14 11:28:34 -07:00
|
|
|
blake2b_update(&state, (uchar *)&leb, sizeof(u32));
|
|
|
|
blake2b_final(&state, hash, HASHOUT);
|
|
|
|
for (u32 i = 0; i<HASHESPERBLAKE; i++) {
|
|
|
|
const uchar *ph = hash + i * WN/8;
|
2016-10-22 19:06:52 -07:00
|
|
|
#if BUCKBITS == 12 && RESTBITS == 8
|
2016-10-17 11:52:30 -07:00
|
|
|
const u32 bucketid = ((u32)ph[0] << 4) | ph[1] >> 4;
|
2016-10-22 19:06:52 -07:00
|
|
|
#elif BUCKBITS == 16 && RESTBITS == 4
|
|
|
|
const u32 bucketid = ((u32)ph[0] << 8) | ph[1];
|
2016-10-14 11:28:34 -07:00
|
|
|
#elif BUCKBITS == 20 && RESTBITS == 4
|
|
|
|
const u32 bucketid = ((((u32)ph[0] << 8) | ph[1]) << 4) | ph[2] >> 4;
|
|
|
|
#elif BUCKBITS == 12 && RESTBITS == 4
|
|
|
|
const u32 bucketid = ((u32)ph[0] << 4) | ph[1] >> 4;
|
|
|
|
const u32 xhash = ph[1] & 0xf;
|
|
|
|
#else
|
|
|
|
#error not implemented
|
|
|
|
#endif
|
2016-10-20 15:57:39 -07:00
|
|
|
const u32 slot = getslot0(bucketid);
|
2016-10-14 11:28:34 -07:00
|
|
|
if (slot >= NSLOTS) {
|
|
|
|
bfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-20 09:06:27 -07:00
|
|
|
htunit *s = hta.heap0[bucketid][slot] + htl.nexthtunits;
|
|
|
|
memcpy(s->bytes-hashbytes, ph+WN/8-hashbytes, hashbytes);
|
|
|
|
s->tag = tree(block * HASHESPERBLAKE + i);
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-17 11:52:30 -07:00
|
|
|
void digitodd(const u32 r, const u32 id) {
|
2016-10-14 11:28:34 -07:00
|
|
|
htlayout htl(this, r);
|
|
|
|
collisiondata cd;
|
|
|
|
for (u32 bucketid=id; bucketid < NBUCKETS; bucketid += nthreads) {
|
|
|
|
cd.clear();
|
2016-10-20 09:06:27 -07:00
|
|
|
slot0 *buck = htl.hta.heap0[bucketid];
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 bsize = getnslots0(bucketid);
|
2016-10-14 11:28:34 -07:00
|
|
|
for (u32 s1 = 0; s1 < bsize; s1++) {
|
2016-10-20 09:06:27 -07:00
|
|
|
const htunit *slot1 = buck[s1];
|
|
|
|
if (!cd.addslot(s1, htl.getxhash0(slot1))) {
|
2016-10-14 11:28:34 -07:00
|
|
|
xfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (; cd.nextcollision(); ) {
|
|
|
|
const u32 s0 = cd.slot();
|
2016-10-20 09:06:27 -07:00
|
|
|
const htunit *slot0 = buck[s0];
|
|
|
|
if (htl.equal(slot0, slot1)) {
|
2016-10-14 11:28:34 -07:00
|
|
|
hfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
u32 xorbucketid;
|
2016-10-20 09:06:27 -07:00
|
|
|
const uchar *bytes0 = slot0->bytes, *bytes1 = slot1->bytes;
|
2016-10-17 11:52:30 -07:00
|
|
|
#if WN == 200 && BUCKBITS == 12 && RESTBITS == 8
|
|
|
|
xorbucketid = (((u32)(bytes0[htl.prevbo+1] ^ bytes1[htl.prevbo+1]) & 0xf) << 8)
|
|
|
|
| (bytes0[htl.prevbo+2] ^ bytes1[htl.prevbo+2]);
|
|
|
|
#elif WN == 144 && BUCKBITS == 20 && RESTBITS == 4
|
|
|
|
xorbucketid = ((((u32)(bytes0[htl.prevbo+1] ^ bytes1[htl.prevbo+1]) << 8)
|
|
|
|
| (bytes0[htl.prevbo+2] ^ bytes1[htl.prevbo+2])) << 4)
|
|
|
|
| (bytes0[htl.prevbo+3] ^ bytes1[htl.prevbo+3]) >> 4;
|
|
|
|
#elif WN == 96 && BUCKBITS == 12 && RESTBITS == 4
|
|
|
|
xorbucketid = ((u32)(bytes0[htl.prevbo+1] ^ bytes1[htl.prevbo+1]) << 4)
|
|
|
|
| (bytes0[htl.prevbo+2] ^ bytes1[htl.prevbo+2]) >> 4;
|
2016-10-16 16:17:11 -07:00
|
|
|
#else
|
2016-10-17 11:52:30 -07:00
|
|
|
#error not implemented
|
2016-10-16 16:17:11 -07:00
|
|
|
#endif
|
2016-10-20 15:57:39 -07:00
|
|
|
const u32 xorslot = getslot1(xorbucketid);
|
2016-10-17 11:52:30 -07:00
|
|
|
if (xorslot >= NSLOTS) {
|
|
|
|
bfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-20 09:06:27 -07:00
|
|
|
htunit *xs = htl.hta.heap1[xorbucketid][xorslot];
|
|
|
|
for (u32 i=htl.dunits; i < htl.prevhtunits; i++)
|
|
|
|
xs++->word = slot0[i].word ^ slot1[i].word;
|
|
|
|
xs->tag = tree(bucketid, s0, s1);
|
2016-10-17 11:52:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void digiteven(const u32 r, const u32 id) {
|
|
|
|
htlayout htl(this, r);
|
|
|
|
collisiondata cd;
|
|
|
|
for (u32 bucketid=id; bucketid < NBUCKETS; bucketid += nthreads) {
|
|
|
|
cd.clear();
|
2016-10-20 09:06:27 -07:00
|
|
|
slot1 *buck = htl.hta.heap1[bucketid];
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 bsize = getnslots1(bucketid);
|
2016-10-17 11:52:30 -07:00
|
|
|
for (u32 s1 = 0; s1 < bsize; s1++) {
|
2016-10-20 09:06:27 -07:00
|
|
|
const htunit *slot1 = buck[s1];
|
|
|
|
if (!cd.addslot(s1, htl.getxhash1(slot1))) {
|
2016-10-17 11:52:30 -07:00
|
|
|
xfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (; cd.nextcollision(); ) {
|
|
|
|
const u32 s0 = cd.slot();
|
2016-10-20 09:06:27 -07:00
|
|
|
const htunit *slot0 = buck[s0];
|
|
|
|
if (htl.equal(slot0, slot1)) {
|
2016-10-17 11:52:30 -07:00
|
|
|
hfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
u32 xorbucketid;
|
2016-10-20 09:06:27 -07:00
|
|
|
const uchar *bytes0 = slot0->bytes, *bytes1 = slot1->bytes;
|
2016-10-17 11:52:30 -07:00
|
|
|
#if WN == 200 && BUCKBITS == 12 && RESTBITS == 8
|
|
|
|
xorbucketid = ((u32)(bytes0[htl.prevbo+1] ^ bytes1[htl.prevbo+1]) << 4)
|
|
|
|
| (bytes0[htl.prevbo+2] ^ bytes1[htl.prevbo+2]) >> 4;
|
|
|
|
#elif WN == 144 && BUCKBITS == 20 && RESTBITS == 4
|
|
|
|
xorbucketid = ((((u32)(bytes0[htl.prevbo+1] ^ bytes1[htl.prevbo+1]) << 8)
|
|
|
|
| (bytes0[htl.prevbo+2] ^ bytes1[htl.prevbo+2])) << 4)
|
|
|
|
| (bytes0[htl.prevbo+3] ^ bytes1[htl.prevbo+3]) >> 4;
|
|
|
|
#elif WN == 96 && BUCKBITS == 12 && RESTBITS == 4
|
|
|
|
xorbucketid = ((u32)(bytes0[htl.prevbo+1] ^ bytes1[htl.prevbo+1]) << 4)
|
|
|
|
| (bytes0[htl.prevbo+2] ^ bytes1[htl.prevbo+2]) >> 4;
|
2016-10-14 11:28:34 -07:00
|
|
|
#else
|
|
|
|
#error not implemented
|
|
|
|
#endif
|
2016-10-20 15:57:39 -07:00
|
|
|
const u32 xorslot = getslot0(xorbucketid);
|
2016-10-14 11:28:34 -07:00
|
|
|
if (xorslot >= NSLOTS) {
|
|
|
|
bfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-20 09:06:27 -07:00
|
|
|
htunit *xs = htl.hta.heap0[xorbucketid][xorslot];
|
|
|
|
for (u32 i=htl.dunits; i < htl.prevhtunits; i++)
|
|
|
|
xs++->word = slot0[i].word ^ slot1[i].word;
|
|
|
|
xs->tag = tree(bucketid, s0, s1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void digit1(const u32 id) {
|
2016-10-20 15:35:18 -07:00
|
|
|
htalloc heaps = hta;
|
2016-10-20 09:06:27 -07:00
|
|
|
collisiondata cd;
|
|
|
|
for (u32 bucketid=id; bucketid < NBUCKETS; bucketid += nthreads) {
|
|
|
|
cd.clear();
|
2016-10-20 15:35:18 -07:00
|
|
|
slot0 *buck = heaps.heap0[bucketid];
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 bsize = getnslots0(bucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
for (u32 s1 = 0; s1 < bsize; s1++) {
|
|
|
|
const htunit *slot1 = buck[s1];
|
2016-10-21 14:51:42 -07:00
|
|
|
if (!cd.addslot(s1, htobe32(slot1->word) >> 20 & 0xff)) {
|
2016-10-20 09:06:27 -07:00
|
|
|
xfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (; cd.nextcollision(); ) {
|
|
|
|
const u32 s0 = cd.slot();
|
|
|
|
const htunit *slot0 = buck[s0];
|
2016-10-20 15:35:18 -07:00
|
|
|
if (slot0[5].word == slot1[5].word) {
|
2016-10-20 09:06:27 -07:00
|
|
|
hfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-21 14:51:42 -07:00
|
|
|
u32 xorbucketid = htobe32(slot0->word ^ slot1->word) >> 8 & BUCKMASK;
|
2016-10-20 15:57:39 -07:00
|
|
|
const u32 xorslot = getslot1(xorbucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
if (xorslot >= NSLOTS) {
|
|
|
|
bfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-20 15:35:18 -07:00
|
|
|
u64 *x = (u64 *)heaps.heap1[xorbucketid][xorslot];
|
|
|
|
u64 *x0 = (u64 *)slot0, *x1 = (u64 *)slot1;
|
|
|
|
*x++ = x0[0] ^ x1[0];
|
|
|
|
*x++ = x0[1] ^ x1[1];
|
|
|
|
*x++ = x0[2] ^ x1[2];
|
|
|
|
((htunit *)x)->tag = tree(bucketid, s0, s1);
|
2016-10-20 09:06:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void digit2(const u32 id) {
|
2016-10-20 15:35:18 -07:00
|
|
|
htalloc heaps = hta;
|
2016-10-20 09:06:27 -07:00
|
|
|
collisiondata cd;
|
|
|
|
for (u32 bucketid=id; bucketid < NBUCKETS; bucketid += nthreads) {
|
|
|
|
cd.clear();
|
2016-10-20 15:35:18 -07:00
|
|
|
slot1 *buck = heaps.heap1[bucketid];
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 bsize = getnslots1(bucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
for (u32 s1 = 0; s1 < bsize; s1++) {
|
|
|
|
const htunit *slot1 = buck[s1];
|
2016-10-20 15:35:18 -07:00
|
|
|
if (!cd.addslot(s1, slot1->bytes[3])) {
|
2016-10-20 09:06:27 -07:00
|
|
|
xfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (; cd.nextcollision(); ) {
|
|
|
|
const u32 s0 = cd.slot();
|
|
|
|
const htunit *slot0 = buck[s0];
|
2016-10-20 15:35:18 -07:00
|
|
|
if (slot0[5].word == slot1[5].word) {
|
2016-10-20 09:06:27 -07:00
|
|
|
hfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-21 14:51:42 -07:00
|
|
|
u32 xorbucketid = htobe32(slot0[1].word ^ slot1[1].word) >> 20;
|
2016-10-20 15:57:39 -07:00
|
|
|
const u32 xorslot = getslot0(xorbucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
if (xorslot >= NSLOTS) {
|
|
|
|
bfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-20 15:35:18 -07:00
|
|
|
htunit *xs = heaps.heap0[xorbucketid][xorslot];
|
2016-10-20 09:06:27 -07:00
|
|
|
xs++->word = slot0[1].word ^ slot1[1].word;
|
2016-10-20 15:35:18 -07:00
|
|
|
u64 *x = (u64 *)xs, *x0 = (u64 *)slot0, *x1 = (u64 *)slot1;
|
|
|
|
*x++ = x0[1] ^ x1[1];
|
|
|
|
*x++ = x0[2] ^ x1[2];
|
|
|
|
((htunit *)x)->tag = tree(bucketid, s0, s1);
|
2016-10-20 09:06:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void digit3(const u32 id) {
|
2016-10-20 15:35:18 -07:00
|
|
|
htalloc heaps = hta;
|
2016-10-20 09:06:27 -07:00
|
|
|
collisiondata cd;
|
|
|
|
for (u32 bucketid=id; bucketid < NBUCKETS; bucketid += nthreads) {
|
|
|
|
cd.clear();
|
2016-10-20 15:35:18 -07:00
|
|
|
slot0 *buck = heaps.heap0[bucketid];
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 bsize = getnslots0(bucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
for (u32 s1 = 0; s1 < bsize; s1++) {
|
|
|
|
const htunit *slot1 = buck[s1];
|
2016-10-21 14:51:42 -07:00
|
|
|
if (!cd.addslot(s1, htobe32(slot1->word) >> 12 & 0xff)) {
|
2016-10-20 09:06:27 -07:00
|
|
|
xfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (; cd.nextcollision(); ) {
|
|
|
|
const u32 s0 = cd.slot();
|
|
|
|
const htunit *slot0 = buck[s0];
|
2016-10-20 15:35:18 -07:00
|
|
|
if (slot0[4].word == slot1[4].word) {
|
2016-10-20 09:06:27 -07:00
|
|
|
hfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-21 14:51:42 -07:00
|
|
|
u32 xorbucketid = htobe32(slot0[0].word ^ slot1[0].word) & BUCKMASK;
|
2016-10-20 15:57:39 -07:00
|
|
|
const u32 xorslot = getslot1(xorbucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
if (xorslot >= NSLOTS) {
|
|
|
|
bfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-20 15:35:18 -07:00
|
|
|
u64 *x = (u64 *)heaps.heap1[xorbucketid][xorslot];
|
|
|
|
u64 *x0 = (u64 *)(slot0+1), *x1 = (u64 *)(slot1+1);
|
|
|
|
*x++ = x0[0] ^ x1[0];
|
|
|
|
*x++ = x0[1] ^ x1[1];
|
|
|
|
((htunit *)x)->tag = tree(bucketid, s0, s1);
|
2016-10-20 09:06:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void digit4(const u32 id) {
|
2016-10-20 15:35:18 -07:00
|
|
|
htalloc heaps = hta;
|
2016-10-20 09:06:27 -07:00
|
|
|
collisiondata cd;
|
|
|
|
for (u32 bucketid=id; bucketid < NBUCKETS; bucketid += nthreads) {
|
|
|
|
cd.clear();
|
2016-10-20 15:35:18 -07:00
|
|
|
slot1 *buck = heaps.heap1[bucketid];
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 bsize = getnslots1(bucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
for (u32 s1 = 0; s1 < bsize; s1++) {
|
|
|
|
const htunit *slot1 = buck[s1];
|
2016-10-20 15:35:18 -07:00
|
|
|
if (!cd.addslot(s1, slot1->bytes[0])) {
|
2016-10-20 09:06:27 -07:00
|
|
|
xfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (; cd.nextcollision(); ) {
|
|
|
|
const u32 s0 = cd.slot();
|
|
|
|
const htunit *slot0 = buck[s0];
|
2016-10-20 15:35:18 -07:00
|
|
|
if (slot0[3].word == slot1[3].word) {
|
2016-10-20 09:06:27 -07:00
|
|
|
hfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-21 14:51:42 -07:00
|
|
|
u32 xorbucketid = htobe32(slot0[0].word ^ slot1[0].word) >> 12 & BUCKMASK;
|
2016-10-20 15:57:39 -07:00
|
|
|
const u32 xorslot = getslot0(xorbucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
if (xorslot >= NSLOTS) {
|
|
|
|
bfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-20 15:35:18 -07:00
|
|
|
u64 *x = (u64 *)heaps.heap0[xorbucketid][xorslot];
|
|
|
|
u64 *x0 = (u64 *)slot0, *x1 = (u64 *)slot1;
|
|
|
|
*x++ = x0[0] ^ x1[0];
|
|
|
|
*x++ = x0[1] ^ x1[1];
|
|
|
|
((htunit *)x)->tag = tree(bucketid, s0, s1);
|
2016-10-20 09:06:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void digit5(const u32 id) {
|
2016-10-20 15:35:18 -07:00
|
|
|
htalloc heaps = hta;
|
2016-10-20 09:06:27 -07:00
|
|
|
collisiondata cd;
|
|
|
|
for (u32 bucketid=id; bucketid < NBUCKETS; bucketid += nthreads) {
|
|
|
|
cd.clear();
|
2016-10-20 15:35:18 -07:00
|
|
|
slot0 *buck = heaps.heap0[bucketid];
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 bsize = getnslots0(bucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
for (u32 s1 = 0; s1 < bsize; s1++) {
|
|
|
|
const htunit *slot1 = buck[s1];
|
2016-10-21 14:51:42 -07:00
|
|
|
if (!cd.addslot(s1, htobe32(slot1->word) >> 4 & 0xff)) {
|
2016-10-20 09:06:27 -07:00
|
|
|
xfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (; cd.nextcollision(); ) {
|
|
|
|
const u32 s0 = cd.slot();
|
|
|
|
const htunit *slot0 = buck[s0];
|
2016-10-20 15:35:18 -07:00
|
|
|
if (slot0[3].word == slot1[3].word) {
|
2016-10-20 09:06:27 -07:00
|
|
|
hfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-21 14:51:42 -07:00
|
|
|
u32 xor1 = slot0[1].word ^ slot1[1].word;
|
|
|
|
u32 xorbucketid = (((u32)(slot0->bytes[3] ^ slot1->bytes[3]) & 0xf)
|
|
|
|
<< 8) | (xor1 & 0xff);
|
2016-10-20 15:57:39 -07:00
|
|
|
const u32 xorslot = getslot1(xorbucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
if (xorslot >= NSLOTS) {
|
|
|
|
bfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-20 15:35:18 -07:00
|
|
|
htunit *xs = heaps.heap1[xorbucketid][xorslot];
|
2016-10-21 14:51:42 -07:00
|
|
|
xs++->word = xor1;
|
2016-10-20 15:35:18 -07:00
|
|
|
u64 *x = (u64 *)xs, *x0 = (u64 *)slot0, *x1 = (u64 *)slot1;
|
|
|
|
*x++ = x0[1] ^ x1[1];
|
|
|
|
((htunit *)x)->tag = tree(bucketid, s0, s1);
|
2016-10-20 09:06:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void digit6(const u32 id) {
|
2016-10-20 15:35:18 -07:00
|
|
|
htalloc heaps = hta;
|
2016-10-20 09:06:27 -07:00
|
|
|
collisiondata cd;
|
|
|
|
for (u32 bucketid=id; bucketid < NBUCKETS; bucketid += nthreads) {
|
|
|
|
cd.clear();
|
2016-10-20 15:35:18 -07:00
|
|
|
slot1 *buck = heaps.heap1[bucketid];
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 bsize = getnslots1(bucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
for (u32 s1 = 0; s1 < bsize; s1++) {
|
|
|
|
const htunit *slot1 = buck[s1];
|
2016-10-20 15:35:18 -07:00
|
|
|
if (!cd.addslot(s1, slot1->bytes[1])) {
|
2016-10-20 09:06:27 -07:00
|
|
|
xfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (; cd.nextcollision(); ) {
|
|
|
|
const u32 s0 = cd.slot();
|
|
|
|
const htunit *slot0 = buck[s0];
|
2016-10-20 15:35:18 -07:00
|
|
|
if (slot0[2].word == slot1[2].word) {
|
2016-10-20 09:06:27 -07:00
|
|
|
hfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-21 14:51:42 -07:00
|
|
|
u32 xorbucketid = htobe32(slot0[0].word ^ slot1[0].word) >> 4 & BUCKMASK;
|
2016-10-20 15:57:39 -07:00
|
|
|
const u32 xorslot = getslot0(xorbucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
if (xorslot >= NSLOTS) {
|
|
|
|
bfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-20 15:35:18 -07:00
|
|
|
htunit *xs = heaps.heap0[xorbucketid][xorslot];
|
2016-10-20 09:06:27 -07:00
|
|
|
xs++->word = slot0[0].word ^ slot1[0].word;
|
2016-10-20 15:35:18 -07:00
|
|
|
u64 *x = (u64 *)xs, *x0 = (u64 *)(slot0+1), *x1 = (u64 *)(slot1+1);
|
|
|
|
*x++ = x0[0] ^ x1[0];
|
|
|
|
((htunit *)x)->tag = tree(bucketid, s0, s1);
|
2016-10-20 09:06:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void digit7(const u32 id) {
|
2016-10-20 15:35:18 -07:00
|
|
|
htalloc heaps = hta;
|
2016-10-20 09:06:27 -07:00
|
|
|
collisiondata cd;
|
|
|
|
for (u32 bucketid=id; bucketid < NBUCKETS; bucketid += nthreads) {
|
|
|
|
cd.clear();
|
2016-10-20 15:35:18 -07:00
|
|
|
slot0 *buck = heaps.heap0[bucketid];
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 bsize = getnslots0(bucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
for (u32 s1 = 0; s1 < bsize; s1++) {
|
|
|
|
const htunit *slot1 = buck[s1];
|
2016-10-22 19:06:52 -07:00
|
|
|
if (!cd.addslot(s1, (slot1->bytes[3] & 0xf) << 4 | slot1->bytes[4] >> 4)) {
|
2016-10-20 09:06:27 -07:00
|
|
|
xfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (; cd.nextcollision(); ) {
|
|
|
|
const u32 s0 = cd.slot();
|
|
|
|
const htunit *slot0 = buck[s0];
|
2016-10-20 15:35:18 -07:00
|
|
|
if (slot0[2].word == slot1[2].word) {
|
2016-10-20 09:06:27 -07:00
|
|
|
hfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-21 14:51:42 -07:00
|
|
|
u32 xorbucketid = htobe32(slot0[1].word ^ slot1[1].word) >> 16 & BUCKMASK;
|
2016-10-20 15:57:39 -07:00
|
|
|
const u32 xorslot = getslot1(xorbucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
if (xorslot >= NSLOTS) {
|
|
|
|
bfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-20 15:35:18 -07:00
|
|
|
u64 *x = (u64 *)heaps.heap1[xorbucketid][xorslot];
|
|
|
|
u64 *x0 = (u64 *)(slot0+1), *x1 = (u64 *)(slot1+1);
|
|
|
|
*x++ = x0[0] ^ x1[0];
|
|
|
|
((htunit *)x)->tag = tree(bucketid, s0, s1);
|
2016-10-20 09:06:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void digit8(const u32 id) {
|
2016-10-20 15:35:18 -07:00
|
|
|
htalloc heaps = hta;
|
2016-10-20 09:06:27 -07:00
|
|
|
collisiondata cd;
|
|
|
|
for (u32 bucketid=id; bucketid < NBUCKETS; bucketid += nthreads) {
|
|
|
|
cd.clear();
|
2016-10-20 15:35:18 -07:00
|
|
|
slot1 *buck = heaps.heap1[bucketid];
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 bsize = getnslots1(bucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
for (u32 s1 = 0; s1 < bsize; s1++) {
|
|
|
|
const htunit *slot1 = buck[s1];
|
2016-10-20 15:35:18 -07:00
|
|
|
if (!cd.addslot(s1, slot1->bytes[2])) {
|
2016-10-20 09:06:27 -07:00
|
|
|
xfull++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (; cd.nextcollision(); ) {
|
|
|
|
const u32 s0 = cd.slot();
|
|
|
|
const htunit *slot0 = buck[s0];
|
2016-10-21 14:51:42 -07:00
|
|
|
u32 xor1 = slot0[1].word ^ slot1[1].word;
|
|
|
|
if (!xor1) {
|
2016-10-20 09:06:27 -07:00
|
|
|
hfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-21 14:51:42 -07:00
|
|
|
u32 xorbucketid = ((u32)(slot0->bytes[3] ^ slot1->bytes[3]) << 4)
|
|
|
|
| (xor1 >> 4 & 0xf);
|
2016-10-20 15:57:39 -07:00
|
|
|
const u32 xorslot = getslot0(xorbucketid);
|
2016-10-20 09:06:27 -07:00
|
|
|
if (xorslot >= NSLOTS) {
|
|
|
|
bfull++;
|
|
|
|
continue;
|
|
|
|
}
|
2016-10-20 15:35:18 -07:00
|
|
|
htunit *xs = heaps.heap0[xorbucketid][xorslot];
|
2016-10-21 14:51:42 -07:00
|
|
|
xs++->word = xor1;
|
2016-10-20 09:06:27 -07:00
|
|
|
xs->tag = tree(bucketid, s0, s1);
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void digitK(const u32 id) {
|
|
|
|
collisiondata cd;
|
|
|
|
htlayout htl(this, WK);
|
2016-10-20 09:06:27 -07:00
|
|
|
u32 nc = 0;
|
2016-10-14 11:28:34 -07:00
|
|
|
for (u32 bucketid = id; bucketid < NBUCKETS; bucketid += nthreads) {
|
|
|
|
cd.clear();
|
2016-10-20 09:06:27 -07:00
|
|
|
slot0 *buck = htl.hta.heap0[bucketid];
|
2016-10-20 15:57:39 -07:00
|
|
|
u32 bsize = getnslots0(bucketid);
|
2016-10-14 11:28:34 -07:00
|
|
|
for (u32 s1 = 0; s1 < bsize; s1++) {
|
2016-10-20 09:06:27 -07:00
|
|
|
const htunit *slot1 = buck[s1];
|
|
|
|
if (!cd.addslot(s1, htl.getxhash0(slot1))) // assume WK odd
|
2016-10-14 11:28:34 -07:00
|
|
|
continue;
|
|
|
|
for (; cd.nextcollision(); ) {
|
|
|
|
const u32 s0 = cd.slot();
|
2016-10-20 15:35:18 -07:00
|
|
|
if (htl.equal(buck[s0], slot1)) { // EASY OPTIMIZE
|
2016-10-20 09:06:27 -07:00
|
|
|
candidate(tree(bucketid, s0, s1));
|
|
|
|
nc++;
|
|
|
|
}
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-22 19:06:52 -07:00
|
|
|
printf(" %d candidates ", nc);
|
2016-10-14 11:28:34 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
u32 id;
|
|
|
|
pthread_t thread;
|
|
|
|
equi *eq;
|
|
|
|
} thread_ctx;
|
|
|
|
|
|
|
|
void barrier(pthread_barrier_t *barry) {
|
|
|
|
const int rc = pthread_barrier_wait(barry);
|
|
|
|
if (rc != 0 && rc != PTHREAD_BARRIER_SERIAL_THREAD) {
|
|
|
|
printf("Could not wait on barrier\n");
|
|
|
|
pthread_exit(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void *worker(void *vp) {
|
|
|
|
thread_ctx *tp = (thread_ctx *)vp;
|
|
|
|
equi *eq = tp->eq;
|
|
|
|
|
2016-10-22 19:06:52 -07:00
|
|
|
if (tp->id == 0) printf("Digit 0");
|
2016-10-14 11:28:34 -07:00
|
|
|
eq->digit0(tp->id);
|
|
|
|
barrier(&eq->barry);
|
2016-10-20 09:06:27 -07:00
|
|
|
if (tp->id == 0) eq->showbsizes(0);
|
2016-10-14 11:28:34 -07:00
|
|
|
barrier(&eq->barry);
|
2016-10-20 09:06:27 -07:00
|
|
|
#if WN == 200 && WK == 9 && RESTBITS == 8
|
|
|
|
eq->digit1(tp->id);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
if (tp->id == 0) eq->showbsizes(1);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
eq->digit2(tp->id);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
if (tp->id == 0) eq->showbsizes(2);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
eq->digit3(tp->id);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
if (tp->id == 0) eq->showbsizes(3);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
eq->digit4(tp->id);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
if (tp->id == 0) eq->showbsizes(4);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
eq->digit5(tp->id);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
if (tp->id == 0) eq->showbsizes(5);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
eq->digit6(tp->id);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
if (tp->id == 0) eq->showbsizes(6);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
eq->digit7(tp->id);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
if (tp->id == 0) eq->showbsizes(7);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
eq->digit8(tp->id);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
if (tp->id == 0) eq->showbsizes(8);
|
|
|
|
barrier(&eq->barry);
|
|
|
|
#else
|
2016-10-14 11:28:34 -07:00
|
|
|
for (u32 r = 1; r < WK; r++) {
|
2016-10-17 11:52:30 -07:00
|
|
|
r&1 ? eq->digitodd(r, tp->id) : eq->digiteven(r, tp->id);
|
2016-10-14 11:28:34 -07:00
|
|
|
barrier(&eq->barry);
|
2016-10-20 09:06:27 -07:00
|
|
|
if (tp->id == 0) eq->showbsizes(r);
|
2016-10-14 11:28:34 -07:00
|
|
|
barrier(&eq->barry);
|
|
|
|
}
|
2016-10-20 09:06:27 -07:00
|
|
|
#endif
|
2016-10-14 11:28:34 -07:00
|
|
|
eq->digitK(tp->id);
|
|
|
|
pthread_exit(NULL);
|
|
|
|
return 0;
|
|
|
|
}
|