-rw-r--r-- 4362 cryptattacktester-20231020/transition.cpp raw
#include <map>
#include <cassert>
#include "transition.h"
using namespace std;
// consider the following markov chain:
// state 0: search has not finished, and target has 0 errors within KK positions
// state 1: search has not finished, and target has 1 error within KK positions
// ...
// state W: search has not finished, and target has W errors within KK positions
// state W+1: have finished by finding target
// state W+2: have finished with systematic-form failure
// initial state is 0...W
// chance of state e: binomial(KK,e)*binomial(N-KK,W-e)/binomial(N,W)
vector<vector<bigfloat>> transition_zero(bigint W)
{
  vector<vector<bigfloat>> result;
  vector<bigfloat> zerorow;
  for (bigint v = 0;v <= W+2;++v) zerorow.push_back(0);
  for (bigint u = 0;u <= W+2;++u) result.push_back(zerorow);
  return result;
}
vector<vector<bigfloat>> transition_identity(bigint W)
{
  vector<vector<bigfloat>> result = transition_zero(W);
  for (bigint u = 0;u <= W+2;++u) result.at(u).at(u) = 1;
  return result;
}
static map<tuple<bigint,bigint,bigint,bigint,bigint>,vector<vector<bigfloat>>> transition_weightstep_cache;
// output[u][v] is chance that a type-3 exchange of X columns moves state u to state v
vector<vector<bigfloat>> transition_weightstep(bigint N,bigint KK,bigint W,bigint X)
{
  tuple<bigint,bigint,bigint,bigint,bigint> key = make_tuple(N,KK,W,X,bigfloat::get_default_precision());
  if (transition_weightstep_cache.count(key))
    return transition_weightstep_cache[key];
  vector<vector<bigfloat>> result = transition_identity(W);
  bigfloat denom = bigfloat(binomial(N-KK,X)*binomial(KK,X));
  for (bigint u = 0;u <= W;++u) {
    for (bigint v = 0;v <= W;++v) {
      bigint resultuv = 0;
      bigint d = u-v;
      if (d > X && d < -X) continue;
      for (bigint i = 0;i <= W-u;++i) {
        if (X-i < 0) continue;
        if (d+i < 0) continue;
        if (X-d-i < 0) continue;
        if (X-i > N-KK-W+u) continue;
        if (d+i > u) continue;
        if (X-d-i > KK-u) continue;
        resultuv += binomial(W-u,i)*binomial(N-KK-W+u,X-i)*binomial(u,d+i)*binomial(KK-u,X-d-i);
      }
      result.at(u).at(v) = bigfloat(resultuv)/denom;
    }
  }
  transition_weightstep_cache[key] = result;
  return result;
}
vector<vector<bigfloat>> transition_checksystematic(bigint W,bigint X,bigint Y)
{
  vector<vector<bigfloat>> result = transition_identity(W);
  bigfloat success;
  assert(X <= Y);
  // what's the chance that an XxY matrix has rank X?
  // 1/2^Y chance of first row failing
  // if it's ok: 1/2^(Y-1) chance of second row failing
  // ...
  // so overall chance (1-1/2^Y)(1-1/2^(Y-1))...(1-1/2^(Y-X+1))
  bigfloat log05 = -log1p(bigfloat(1));
  success = 1;
  for (bigint pos = Y;pos > Y-X;--pos)
    success *= -expm1(pos*log05);
  for (bigint u = 0;u <= W;++u) {
    result.at(u).at(u) = success;
    result.at(u).at(W+2) = 1-success;
  }
  return result;
}
// pr: conditional probability of finding target given P errors
vector<vector<bigfloat>> transition_found(bigint W,bigint P,bigfloat pr)
{
  vector<vector<bigfloat>> result = transition_identity(W);
  assert(P <= W);
  result.at(P).at(P) = 1-pr;
  result.at(P).at(W+1) = pr;
  return result;
}
vector<vector<bigfloat>> transition_multiply(bigint W,const vector<vector<bigfloat>> &T0,const vector<vector<bigfloat>> &T1)
{
  vector<vector<bigfloat>> result = transition_zero(W);
  for (bigint t = 0;t <= W+2;++t)
    for (bigint u = 0;u <= W+2;++u) {
      bigfloat T0tu = T0.at(t).at(u);
      if (T0tu.definitely_zero()) continue;
      for (bigint v = 0;v <= W+2;++v)
        result.at(t).at(v) += T0tu*T1.at(u).at(v);
    }
#ifdef TRANSITION_RENORMALIZE
  // renormalize for better floating-point stability
  for (bigint t = 0;t <= W+2;++t) {
    bigfloat x = 0;
    for (bigint u = 0;u <= W+2;++u)
      x += result.at(t).at(u);
    if (x <= 0) continue;
    x = 1/x;
    for (bigint u = 0;u <= W+2;++u)
      result.at(t).at(u) *= x;
  }
#endif
  return result;
}
vector<vector<bigfloat>> transition_power(bigint W,const vector<vector<bigfloat>> &T,bigint e)
{
  assert(e >= 0);
  if (e == 0) return transition_identity(W);
  if (e == 1) return T;
  vector<vector<bigfloat>> result = transition_power(W,T,e/2);
  result = transition_multiply(W,result,result);
  if ((e&1) != 0) result = transition_multiply(W,result,T);
  return result;
}