1#ifndef JNGEN_INCLUDE_RNDS_INL_H2#error File "rnds_inl.h" must not be included directly.3#include "../rnds.h" // for completion emgine4#endif56namespace jngen {78namespace detail {910int popcount(long long x) {11 int res = 0;12 while (x) {13 ++res;14 x &= x-1;15 }16 return res;17}1819int trailingZeroes(long long x) {20 int res = 0;21 ENSURE(x != 0);22 while (!(x&1)) {23 ++res;24 x >>= 1;25 }26 return res;27}2829std::string parseAllowedChars(std::string pattern) {30 std::string result;31 pattern += "\0\0";32 for (size_t i = 0; i < pattern.length(); ++i) {33 if (pattern[i] == '-') {34 result += '-';35 } else if (pattern[i+1] == '-' && pattern[i+2] != '\0') {36 for (char c = pattern[i]; c <= pattern[i+2]; ++c) {37 result += c;38 }39 i += 2;40 } else {41 result += pattern[i];42 }43 }44 std::sort(result.begin(), result.end());45 return result;46}4748std::vector<std::string> extendAntiHash(49 const std::vector<std::string>& chars,50 HashBase base,51 int count)52{53 ENSURE(count == 2, "Count != 2 is not supported (yet)");5455 size_t baseLength = chars[0].size();56 for (const auto& s: chars) {57 ensure(s.size() == baseLength);58 }5960 long long mod = base.first;61 long long p = base.second;6263 long long pPower = 1;64 for (size_t i = 0; i != baseLength; ++i) {65 pPower = (pPower * p) % mod;66 }6768 std::vector<long long> charHashes;69 for (const auto& s: chars) {70 long long hash = 0;71 for (char c: s) {72 hash = (hash * p + c) % mod;73 }74 charHashes.push_back(hash);75 }7677 auto computeHash = [&charHashes, mod, pPower](const std::vector<int>& a) {78 long long hash = 0;79 for (int x: a) {80 hash = (hash * pPower + charHashes[x]) % mod;81 }82 return hash;83 };8485 // This bounds were achieved empirically and should be justified.86 int needForMatch;87 if (count == 2) {88 needForMatch = 5 * pow(double(mod), 0.5);89 } else {90 ENSURE(false, "Only count = 2 is supported yet");91 }9293 int length = 2;94 double wordCount = pow(double(chars.size()), double(length));9596 while (true) {97 ++length;98 wordCount *= chars.size();99 if (wordCount < needForMatch) {100 continue;101 }102103 std::vector<std::pair<long long, Array>> words;104 std::map<long long, int> hashCount;105 std::set<Array> used;106107 for (int i = 0; i < needForMatch; ++i) {108 Array w = Array::random(length, chars.size());109 if (used.count(w)) {110 --i;111 continue;112 }113 used.insert(w);114 long long hash = computeHash(w);115 words.emplace_back(hash, w);116 if (++hashCount[hash] == count) {117 std::vector<std::string> result;118 for (const auto& kv: words) {119 if (kv.first == hash) {120 std::string word;121 for (int c: kv.second) {122 word += chars[c];123 }124 result.push_back(word);125 }126 }127 return result;128 }129 }130 }131}132133StringPair minimalAntiHashTest(134 std::vector<HashBase> bases,135 const std::string allowedChars)136{137 for (auto base: bases) {138 ensure(base.first >= 0, "0 < MOD must hold");139 ensure(140 base.first <= (long long)(2e9),141 "Modules larger than 2'000'000'000 are not supported yet");142 ensure(143 0 < base.second && base.second < base.first,144 "0 <= P < MOD must hold");145 }146147 std::vector<int> counts;148 if (bases.size() == 1) {149 counts = {2};150 } else if (bases.size() == 2) {151 counts = {2, 2};152 } else {153 counts.assign(bases.size(), 2);154 }155156 std::vector<std::string> cur;157 for (char c: allowedChars) {158 cur.emplace_back(1, c);159 }160161 for (size_t i = 0; i != bases.size(); ++i) {162 cur = extendAntiHash(cur, bases[i], counts[i]);163 ensure(static_cast<int>(cur.size()) == counts[i],164 "Cannot generate long enough pair with same hash");165 }166167 return {cur[0], cur[1]};168}169170} // namespace detail171172173std::string StringRandom::random(int len, const std::string& alphabet) {174 checkLargeParameter(len);175 std::string chars = detail::parseAllowedChars(alphabet);176 std::string res;177 res.reserve(len);178 for (int i = 0; i < len; ++i) {179 res += choice(chars);180 }181 return res;182}183184std::string StringRandom::thueMorse(int len, char first, char second) {185 ensure(len >= 0);186 checkLargeParameter(len);187 std::string res(len, ' ');188 for (int i = 0; i < len; ++i) {189 res[i] = detail::popcount(i)%2 == 0 ? first : second;190 }191 return res;192}193194std::string StringRandom::abacaba(int len, char first) {195 ensure(len >= 0);196 checkLargeParameter(len);197 std::string res(len, ' ');198 for (int i = 0; i < len; ++i) {199 res[i] = first + detail::trailingZeroes(~i);200 }201 return res;202}203204StringPair StringRandom::antiHash(205 const std::vector<HashBase>& bases,206 const std::string& alphabet,207 int length)208{209 checkLargeParameter(length);210 std::string allowedChars = detail::parseAllowedChars(alphabet);211 StringPair result = detail::minimalAntiHashTest(bases, allowedChars);212213 if (length == -1) {214 return result;215 }216217 ensure(218 static_cast<int>(result.first.length()) <= length,219 "Cannot generate enough long anti-hash test");220221 int extraLength = length - result.first.length();222 int leftSize = rnd.next(0, extraLength);223224 std::string left = rnd.next(format("[%s]{%d}", alphabet.c_str(), leftSize));225 std::string right =226 rnd.next(format("[%s]{%d}", alphabet.c_str(), extraLength - leftSize));227228 return {229 left + result.first + right,230 left + result.second + right231 };232}233234} // namespace jngen