#pragma GCC optimize ("Ofast")
#define MAX_FINGER_PRINT_SIZE (256)
using uint64 = unsigned long long;
static int tc,first,number[5],sortcase[120][5];
static int n,m,time,mindiff;
static char mFullImage[3][MAX_FINGER_PRINT_SIZE][MAX_FINGER_PRINT_SIZE],mImage[5][MAX_FINGER_PRINT_SIZE/2][MAX_FINGER_PRINT_SIZE/2];
//one of (ONE EIGHT SIXTYFOUR) set 1, others set 0
#define ONE 0
#define EIGHT 0
#define SIXTYFOUR 1
#if EIGHT
static uint64 mSrcImage[3][MAX_FINGER_PRINT_SIZE][MAX_FINGER_PRINT_SIZE/8];
static uint64 mTestImage[MAX_FINGER_PRINT_SIZE/2][MAX_FINGER_PRINT_SIZE/16];
#endif
#if SIXTYFOUR
static uint64 mSrcImage[3][MAX_FINGER_PRINT_SIZE/8][MAX_FINGER_PRINT_SIZE/8];
static uint64 mTestImage[MAX_FINGER_PRINT_SIZE/16][MAX_FINGER_PRINT_SIZE/16];
#endif
static inline void swap(int& lhs,int &rhs)
{
if(&lhs == &rhs) return;
lhs ^= rhs;
rhs ^= lhs;
lhs ^= rhs;
}
static void backtrack(int n)
{
if(n == 5)
{
for(int i = 0;i< 5;i++) sortcase[tc][i] = number[i];
tc++;
return;
}
for(int i = n;i< 5;i++)
{
swap(number[n],number[i]);
backtrack(n+1);
swap(number[n],number[i]);
}
}
static int getmindiff()
{
if(n == 16) return 64 - 61;
if(n == 32) return 256 - 244;
if(n == 64) return 1024 - 973;
if(n == 128) return 4096 - 3892;
if(n == 256) return 16384 - 15565;
}
static bool check(int k,int y,int x)
{
int j,i,t1,t2;
t1 = sortcase[k][4];
t2 = sortcase[k][0];
for(j=y;j<m;j++) for(i=x;i<m;i++) if(mImage[t1][j-y][i-x] != mImage[t2][j][i]) return false;
t2 = sortcase[k][1];
for(j=y;j<m;j++) for(i=m;i<x+m;i++) if(mImage[t1][j-y][i-x] != mImage[t2][j][i-m]) return false;
t2 = sortcase[k][2];
for(j=m;j<y+m;j++) for(i=x;i<m;i++) if(mImage[t1][j-y][i-x] != mImage[t2][j-m][i]) return false;
t2 = sortcase[k][3];
for(j=m;j<y+m;j++) for(i=m;i<x+m;i++) if(mImage[t1][j-y][i-x] != mImage[t2][j-m][i-m]) return false;
return true;
}
static uint64 square2uint64(char mImage[][MAX_FINGER_PRINT_SIZE],int y,int x)
{
uint64 val = 0;
for(int j=y;j<y+8;j++)
{
for(int i=x;i<x+8;i++)
{
val = (val << 1) | mImage[j][i];
}
}
return val;
}
static int getdiff(uint64 lhs,uint64 rhs)
{
uint64 i = lhs^rhs;
int j = 0 ;
while(i != 0)
{
++j;
i = i&(i-1);
}
return j;
}
#if ONE
static bool test(int k,int y,int x,char mImage[][MAX_FINGER_PRINT_SIZE])
{
int c = 0;
for(int j=0;j<m;j++)
{
for(int i=0;i<m;i++)
{
if(mImage[j][i] != mFullImage[k][y+j][x+i]) c++;
if(c > mindiff) return false;
}
}
return true;
}
#endif
#if EIGHT
static bool test(int k,int y,int x)
{
int c = 0;
for(int j=0;j<m;j++)
{
for(int i=0;i<m/8;i++)
{
c += getdiff(mTestImage[j][i], mSrcImage[k][y+j][x+i]);
if(c > mindiff) return false;
}
}
return true;
}
#endif
#if SIXTYFOUR
static bool test(int k,int y,int x)
{
int c = 0;
for(int j=0;j<m/8;j++)
{
for(int i=0;i<m/8;i++)
{
c += getdiff(mTestImage[j][i], mSrcImage[k][y+j][x+i]);
if(c > mindiff) return false;
}
}
return true;
}
#endif
static void fillimage(int k)
{
int y,x,t;
t = sortcase[k][0];
for(y=0;y<m;y++) for(x=0;x<m;x++) mFullImage[2][x][n-y-1] = mFullImage[1][n-x-1][y] = mFullImage[0][y][x] = mImage[t][y][x];
t = sortcase[k][1];
for(y=0;y<m;y++) for(x=m;x<n;x++) mFullImage[2][x][n-y-1] = mFullImage[1][n-x-1][y] = mFullImage[0][y][x] = mImage[t][y][x-m];
t = sortcase[k][2];
for(y=m;y<n;y++) for(x=0;x<m;x++) mFullImage[2][x][n-y-1] = mFullImage[1][n-x-1][y] = mFullImage[0][y][x] = mImage[t][y-m][x];
t = sortcase[k][3];
for(y=m;y<n;y++) for(x=m;x<n;x++) mFullImage[2][x][n-y-1] = mFullImage[1][n-x-1][y] = mFullImage[0][y][x] = mImage[t][y-m][x-m];
#if EIGHT
for(t=0;t<3;t++) for(y=0;y<n;y++) for(x=0;x<n/8;x++) mSrcImage[t][y][x] = *(reinterpret_cast<uint64*>(&mFullImage[t][y][x*8]));
#endif
#if SIXTYFOUR
for(t=0;t<3;t++) for(y=0;y<n/8;y++) for(x=0;x<n/8;x++) mSrcImage[t][y][x] = square2uint64(mFullImage[t],y*8,x*8);
#endif
}
static void getfullimage()
{
for(int k=0;k<tc;k++) for(int y=1;y<m;y++) for(int x=1;x<m;x++) if(check(k,y,x)) return fillimage(k);
}
void init(int mFingerPrintSize)
{
n = mFingerPrintSize;
m = mFingerPrintSize/2;
mindiff = getmindiff();
time = 0;
if(first == 0)
{
for(int i=0;i<5;i++) number[i] = i;
backtrack(0);
first = 1;
}
}
void send_register_image(char mImage[][MAX_FINGER_PRINT_SIZE])
{
for(int j=0;j<m;j++) for(int i=0;i<m;i++) ::mImage[time][j][i] = mImage[j][i];
if(++time == 5) getfullimage();
}
int compare_fingerprint(char mImage[][MAX_FINGER_PRINT_SIZE])
{
int y,x,k;
#if ONE
for(k=0;k<3;k++) for(y=0;y<=m;y+=8) for(x=0;x<=m;x+=8) if(test(k,y,x,mImage)) return 1;
#endif
#if EIGHT
for(y=0;y<m;y++) for(x=0;x<m/8;x++) mTestImage[y][x] = *(reinterpret_cast<uint64*>(&mImage[y][x*8]));
for(k=0;k<3;k++) for(y=0;y<=m;y+=8) for(x=0;x<=m/8;x++) if(test(k,y,x)) return 1;
#endif
#if SIXTYFOUR
for(y=0;y<m/8;y++) for(x=0;x<m/8;x++) mTestImage[y][x] = square2uint64(mImage,y*8,x*8);
for(k=0;k<3;k++) for(y=0;y<=m/8;y++) for(x=0;x<=m/8;x++) if(test(k,y,x)) return 1;
#endif
return 0;
}