Browse Source

Use depth-first scan for eliminating partial solutions instead of breadth-first

This reduces the peak number of lists in-memory from 2^k to k, and enables the
solver to eliminate most duplicates before it has instantiated the full set of
leaves.
pull/145/head
Jack Grigg 8 years ago
parent
commit
0a66f01304
  1. 82
      src/crypto/equihash.cpp

82
src/crypto/equihash.cpp

@ -19,6 +19,8 @@
#include <iostream>
#include <stdexcept>
#include <boost/optional.hpp>
template<unsigned int N, unsigned int K>
int Equihash<N,K>::InitialiseState(eh_HashState& base_state)
{
@ -433,50 +435,62 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) };
int invalidCount = 0;
for (eh_trunc* partialSoln : partialSolns) {
// 1) Generate first list of possibilities
size_t hashLen = N/8;
size_t lenIndices = sizeof(eh_index);
std::vector<std::vector<FullStepRow<FinalFullWidth>>> X;
X.reserve(soln_size);
size_t hashLen;
size_t lenIndices;
std::vector<boost::optional<std::vector<FullStepRow<FinalFullWidth>>>> X;
X.reserve(K+1);
// 3) Repeat steps 1 and 2 for each partial index
for (eh_index i = 0; i < soln_size; i++) {
std::vector<FullStepRow<FinalFullWidth>> ic;
ic.reserve(recreate_size);
// 1) Generate first list of possibilities
std::vector<FullStepRow<FinalFullWidth>> icv;
icv.reserve(recreate_size);
for (eh_index j = 0; j < recreate_size; j++) {
eh_index newIndex { UntruncateIndex(partialSoln[i], j, CollisionBitLength + 1) };
ic.emplace_back(N, base_state, newIndex);
icv.emplace_back(N, base_state, newIndex);
}
X.push_back(ic);
}
// 3) Repeat step 2 for each level of the tree
for (int r = 0; X.size() > 1; r++) {
std::vector<std::vector<FullStepRow<FinalFullWidth>>> Xc;
Xc.reserve(X.size()/2);
boost::optional<std::vector<FullStepRow<FinalFullWidth>>> ic = icv;
// 2a) For each pair of lists:
for (int v = 0; v < X.size(); v += 2) {
// 2b) Merge the lists
std::vector<FullStepRow<FinalFullWidth>> ic(X[v]);
ic.reserve(X[v].size() + X[v+1].size());
ic.insert(ic.end(), X[v+1].begin(), X[v+1].end());
std::sort(ic.begin(), ic.end(), CompareSR(hashLen));
CollideBranches(ic, hashLen, lenIndices, CollisionByteLength, CollisionBitLength + 1, partialSoln[(1<<r)*v], partialSoln[(1<<r)*(v+1)]);
// 2v) Check if this has become an invalid solution
if (ic.size() == 0)
goto invalidsolution;
Xc.push_back(ic);
hashLen = N/8;
lenIndices = sizeof(eh_index);
size_t rti = i;
for (size_t r = 0; r <= K; r++) {
// 2b) Until we are at the top of a subtree:
if (r < X.size()) {
if (X[r]) {
// 2c) Merge the lists
ic->reserve(ic->size() + X[r]->size());
ic->insert(ic->end(), X[r]->begin(), X[r]->end());
std::sort(ic->begin(), ic->end(), CompareSR(hashLen));
size_t lti = rti-(1<<r);
CollideBranches(*ic, hashLen, lenIndices,
CollisionByteLength,
CollisionBitLength + 1,
partialSoln[lti], partialSoln[rti]);
// 2d) Check if this has become an invalid solution
if (ic->size() == 0)
goto invalidsolution;
X[r] = boost::none;
hashLen -= CollisionByteLength;
lenIndices *= 2;
rti = lti;
} else {
X[r] = *ic;
break;
}
} else {
X.push_back(ic);
break;
}
}
X = Xc;
hashLen -= CollisionByteLength;
lenIndices *= 2;
}
// We are at the top of the tree
assert(X.size() == 1);
for (FullStepRow<FinalFullWidth> row : X[0]) {
assert(X.size() == K+1);
for (FullStepRow<FinalFullWidth> row : *X[K]) {
solns.insert(row.GetIndices(hashLen, lenIndices));
}
goto deletesolution;

Loading…
Cancel
Save