#include <stdio.h>
#include "libfastmint.h"

int minter_ansi_standard_2_test(void)
{
	/* This minter runs on any hardware */
	return 1;
}

/* Define low-level primitives in terms of operations */
#define S(n,X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) )
#define XOR(a,b) ( (a) ^ (b) )
#define AND(a,b) ( (a) & (b) )
#define ANDNOT(a,b) ( (a) & ~(b) )
#define OR(a,b) ( (a) | (b) )
#define ADD(a,b) ( (a) + (b) )

#define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) )
/* #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) */
/* #define F1( B, C, D, F, G ) ( \
	F = AND(B,C), \
	G = ANDNOT(D,B), \
	OR(F,G) ) */
#define F2( B, C, D ) ( (B) ^ (C) ^ (D) )
/* #define F2( B, C, D, F, G ) ( \
	F = XOR(B,C), \
	XOR(F,D) ) */
/* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (B) & (D) ) */
#define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) )
/* #define F3( B, C, D, F, G ) ( \
	F = OR(C,D), \
	G = AND(C,D), \
	F = AND(B,F), \
	OR(F,G) ) */
#define F4( B, C, D ) ( (B) ^ (C) ^ (D) )
/* #define F4(B,C,D,F,G) F2(B,C,D,F,G) */

#define K1 0x5A827999  /* constant used for rounds 0..19 */
#define K2 0x6ED9EBA1  /* constant used for rounds 20..39 */
#define K3 0x8F1BBCDC  /* constant used for rounds 40..59 */
#define K4 0xCA62C1D6  /* constant used for rounds 60..79 */

/* #define Wmod(W,t) ((t) < 16 ? (W)[t] : (W)[((t) & 0xF) + 16]) */
#define Wmod(W,t) ((W)[t])
#define Wf(W,t) (Wmod(W,t) = S(1, Wmod(W,t-16) ^ Wmod(W,t-14) ^ Wmod(W,t-8) ^ Wmod(W,t-3)))
/*#define Wf(W,t,F,G) ( \
	F = XOR((W)[t-16], (W)[t-14]), \
	G = XOR((W)[t-8], (W)[t-3]), \
	F = XOR(F,G), \
	(W)[t] = S(1,F) ) */

#define Wfly(W,t) ( (t) < 16 ? (W)[t] : Wf(W,t) )

/* #define ROUND(t,A,B,C,D,E,F,G,Func,K,W) \
	E = ADD(E,K); \
	F = S(5,A); \
	E = ADD(F,E); \
	E = ADD((W)[t],E); \
	F = Func(B,C,D,F,G); \
	E = ADD(F,E); \
	B = S(30,B);
	*/

#define ROUND(u,t,A,B,C,D,E,Func,K,W) \
	E += S(5,A) + Func(B,C,D) + ((u) ? Wfly(W,t) : (W)[t]) + K; \
	B = S(30,B);

#define ROUNDn(t,A,B,C,D,E,Func,K,W) \
	ROUND(0,t,A##1,B##1,C##1,D##1,E##1,Func,K,W##1); \
	ROUND(0,t,A##2,B##2,C##2,D##2,E##2,Func,K,W##2);

#define ROUNDu(t,A,B,C,D,E,Func,K,W) \
	ROUND(1,t,A##1,B##1,C##1,D##1,E##1,Func,K,W##1); \
	ROUND(1,t,A##2,B##2,C##2,D##2,E##2,Func,K,W##2);

#define ROUND5n( t, Func, K ) \
    ROUNDn( t + 0, A, B, C, D, E, Func, K, W );\
    ROUNDn( t + 1, E, A, B, C, D, Func, K, W );\
    ROUNDn( t + 2, D, E, A, B, C, Func, K, W );\
    ROUNDn( t + 3, C, D, E, A, B, Func, K, W );\
    ROUNDn( t + 4, B, C, D, E, A, Func, K, W );

#define ROUND5u( t, Func, K ) \
    ROUNDu( t + 0, A, B, C, D, E, Func, K, W );\
    ROUNDu( t + 1, E, A, B, C, D, Func, K, W );\
    ROUNDu( t + 2, D, E, A, B, C, Func, K, W );\
    ROUNDu( t + 3, C, D, E, A, B, Func, K, W );\
    ROUNDu( t + 4, B, C, D, E, A, Func, K, W );

unsigned long minter_ansi_standard_2(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS)
{
	MINTER_CALLBACK_VARS;
	unsigned long iters = 0 ;
	int n = 0, t = 0, gotBits = 0, maxBits = (bits > 16) ? 16 : bits;
	uInt32 bitMask1Low = 0 , bitMask1High = 0 , s = 0 ;
	uInt32 A = 0 , B = 0 , *W = NULL  ;
	/*register*/ uInt32 A1 = 0 , B1 = 0 , C1 = 0 , D1 = 0 , E1 = 0 ;
	/*register*/ uInt32 A2 = 0 , B2 = 0 , C2 = 0 , D2 = 0 , E2 = 0 ;
	uInt32 W1[80] = {0};
	uInt32 W2[80] = {0};
	uInt32 H[5] = {0}, pH[5] = {0};
	const char *p = encodeAlphabets[EncodeBase64];
	unsigned char *X1 = (unsigned char*) W1;
	unsigned char *X2 = (unsigned char*) W2;
	int addressMask = 0 ;
	static const int endTest = 3;
	unsigned char *output = (unsigned char*) block;
	
	if ( *best > 0 ) { maxBits = *best+1; }

	/* Work out whether we need to swap bytes during encoding */
	addressMask = ( *(char*)&endTest );
	
	/* Work out which bits to mask out for test */
	if(maxBits < 32) {
		if ( bits == 0 ) { bitMask1Low = 0; } else {
			bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1);
		}
		bitMask1High = 0;
	} else {
		bitMask1Low = ~0;
		bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1);
	}
	maxBits = 0;
	
	/* Copy block and IV to internal storage */
	for(t=0; t < 16; t++)
		W1[t] = W2[t] = GET_WORD(output + t*4);
	for(t=0; t < 5; t++)
		pH[t] = H[t] = IV[t];
	
	/* The Tight Loop - everything in here should be extra efficient */
	for(iters=0; iters < maxIter-2; iters += 2) {

		/* Encode iteration count into tail */
		X1[(tailIndex - 1) ^ addressMask] = p[((iters+0)      ) & 0x3f];
		X2[(tailIndex - 1) ^ addressMask] = p[((iters+1)      ) & 0x3f];
		if(!(iters & 0x3f)) {
			if ( iters >> 6 ) {
				X1[(tailIndex - 2) ^ addressMask] = X2[(tailIndex - 2) ^ addressMask] = p[((iters) >>  6) & 0x3f];
			}
			if ( iters >> 12 ) {
				X1[(tailIndex - 3) ^ addressMask] = X2[(tailIndex - 3) ^ addressMask] = p[((iters) >> 12) & 0x3f];
			}
			if ( iters >> 18 ) {
				X1[(tailIndex - 4) ^ addressMask] = X2[(tailIndex - 4) ^ addressMask] = p[((iters) >> 18) & 0x3f];
			}
			if ( iters >> 24 ) {
				X1[(tailIndex - 5) ^ addressMask] = X2[(tailIndex - 5) ^ addressMask] = p[((iters) >> 24) & 0x3f];
			}
			if ( iters >> 30 ) {
				X1[(tailIndex - 6) ^ addressMask] = X2[(tailIndex - 6) ^ addressMask] = p[((iters) >> 30) & 0x3f];
			}
		}

		/* Bypass shortcuts below on certain iterations */
		if((!(iters & 0xffffff)) && (tailIndex == 52 || tailIndex == 32)) {
			A1 = H[0];
			B1 = H[1];
			C1 = H[2];
			D1 = H[3];
			E1 = H[4];
			
			for(t=16; t < 32; t++) {
				Wf(W1,t);
				Wf(W2,t);
			}
			
	    ROUND(0, 0, A1, B1, C1, D1, E1, F1, K1, W1 );
	    ROUND(0, 1, E1, A1, B1, C1, D1, F1, K1, W1 );
	    ROUND(0, 2, D1, E1, A1, B1, C1, F1, K1, W1 );
	    ROUND(0, 3, C1, D1, E1, A1, B1, F1, K1, W1 );
	    ROUND(0, 4, B1, C1, D1, E1, A1, F1, K1, W1 );
	    ROUND(0, 5, A1, B1, C1, D1, E1, F1, K1, W1 );
	    ROUND(0, 6, E1, A1, B1, C1, D1, F1, K1, W1 );
			
			if(tailIndex == 52) {
		    ROUND(0, 7, D1, E1, A1, B1, C1, F1, K1, W1 );
		    ROUND(0, 8, C1, D1, E1, A1, B1, F1, K1, W1 );
		    ROUND(0, 9, B1, C1, D1, E1, A1, F1, K1, W1 );
		    ROUND(0,10, A1, B1, C1, D1, E1, F1, K1, W1 );
		    ROUND(0,11, E1, A1, B1, C1, D1, F1, K1, W1 );
			}
			
			pH[0] = A1;
			pH[1] = B1;
			pH[2] = C1;
			pH[3] = D1;
			pH[4] = E1;
		}

		/* Set up working variables */
		A1 = A2 = pH[0];
		B1 = B2 = pH[1];
		C1 = C2 = pH[2];
		D1 = D2 = pH[3];
		E1 = E2 = pH[4];
		
		/* Do the rounds */
		switch(tailIndex) {
			default:
		    ROUNDn( 0, A, B, C, D, E, F1, K1, W );
		    ROUNDn( 1, E, A, B, C, D, F1, K1, W );
		    ROUNDn( 2, D, E, A, B, C, F1, K1, W );
		    ROUNDn( 3, C, D, E, A, B, F1, K1, W );
		    ROUNDn( 4, B, C, D, E, A, F1, K1, W );
		    ROUNDn( 5, A, B, C, D, E, F1, K1, W );
		    ROUNDn( 6, E, A, B, C, D, F1, K1, W );
			case 32:
		    ROUNDn( 7, D, E, A, B, C, F1, K1, W );
		    ROUNDn( 8, C, D, E, A, B, F1, K1, W );
		    ROUNDn( 9, B, C, D, E, A, F1, K1, W );
		    ROUNDn(10, A, B, C, D, E, F1, K1, W );
		    ROUNDn(11, E, A, B, C, D, F1, K1, W );
			case 52:
		    ROUNDn(12, D, E, A, B, C, F1, K1, W );
		    ROUNDn(13, C, D, E, A, B, F1, K1, W );
		    ROUNDn(14, B, C, D, E, A, F1, K1, W );
		    ROUNDn(15, A, B, C, D, E, F1, K1, W );
		}

		if(tailIndex == 52) {
	    ROUNDn(16, E, A, B, C, D, F1, K1, W );
	    ROUNDn(17, D, E, A, B, C, F1, K1, W );
	    ROUNDn(18, C, D, E, A, B, F1, K1, W );
	    ROUNDn(19, B, C, D, E, A, F1, K1, W );
	    ROUNDu(20, A, B, C, D, E, F2, K2, W );
	    ROUNDn(21, E, A, B, C, D, F2, K2, W );
	    ROUNDn(22, D, E, A, B, C, F2, K2, W );
	    ROUNDu(23, C, D, E, A, B, F2, K2, W );
	    ROUNDn(24, B, C, D, E, A, F2, K2, W );
	    ROUNDn(25, A, B, C, D, E, F2, K2, W );
	    ROUNDu(26, E, A, B, C, D, F2, K2, W );
	    ROUNDn(27, D, E, A, B, C, F2, K2, W );
	    ROUNDu(28, C, D, E, A, B, F2, K2, W );
	    ROUNDu(29, B, C, D, E, A, F2, K2, W );
	    ROUNDn(30, A, B, C, D, E, F2, K2, W );
		} else if (tailIndex == 32) {
	    ROUNDn(16, E, A, B, C, D, F1, K1, W );
	    ROUNDn(17, D, E, A, B, C, F1, K1, W );
	    ROUNDn(18, C, D, E, A, B, F1, K1, W );
	    ROUNDn(19, B, C, D, E, A, F1, K1, W );
	    ROUNDn(20, A, B, C, D, E, F2, K2, W );
	    ROUNDu(21, E, A, B, C, D, F2, K2, W );
	    ROUNDn(22, D, E, A, B, C, F2, K2, W );
	    ROUNDu(23, C, D, E, A, B, F2, K2, W );
	    ROUNDu(24, B, C, D, E, A, F2, K2, W );
	    ROUNDn(25, A, B, C, D, E, F2, K2, W );
	    ROUNDu(26, E, A, B, C, D, F2, K2, W );
	    ROUNDu(27, D, E, A, B, C, F2, K2, W );
	    ROUNDn(28, C, D, E, A, B, F2, K2, W );
	    ROUNDu(29, B, C, D, E, A, F2, K2, W );
	    ROUNDu(30, A, B, C, D, E, F2, K2, W );
		} else {
	    ROUNDu(16, E, A, B, C, D, F1, K1, W );
	    ROUNDu(17, D, E, A, B, C, F1, K1, W );
	    ROUNDu(18, C, D, E, A, B, F1, K1, W );
	    ROUNDu(19, B, C, D, E, A, F1, K1, W );
	    ROUNDu(20, A, B, C, D, E, F2, K2, W );
	    ROUNDu(21, E, A, B, C, D, F2, K2, W );
	    ROUNDu(22, D, E, A, B, C, F2, K2, W );
	    ROUNDu(23, C, D, E, A, B, F2, K2, W );
	    ROUNDu(24, B, C, D, E, A, F2, K2, W );
	    ROUNDu(25, A, B, C, D, E, F2, K2, W );
	    ROUNDu(26, E, A, B, C, D, F2, K2, W );
	    ROUNDu(27, D, E, A, B, C, F2, K2, W );
	    ROUNDu(28, C, D, E, A, B, F2, K2, W );
	    ROUNDu(29, B, C, D, E, A, F2, K2, W );
	    ROUNDu(30, A, B, C, D, E, F2, K2, W );
	  }
	  
    ROUNDu(31, E, A, B, C, D, F2, K2, W );
    ROUNDu(32, D, E, A, B, C, F2, K2, W );
    ROUNDu(33, C, D, E, A, B, F2, K2, W );
    ROUNDu(34, B, C, D, E, A, F2, K2, W );
    ROUNDu(35, A, B, C, D, E, F2, K2, W );
    ROUNDu(36, E, A, B, C, D, F2, K2, W );
    ROUNDu(37, D, E, A, B, C, F2, K2, W );
    ROUNDu(38, C, D, E, A, B, F2, K2, W );
    ROUNDu(39, B, C, D, E, A, F2, K2, W );
	
		ROUND5u(40, F3, (K3) );
		ROUND5u(45, F3, (K3) );
		ROUND5u(50, F3, (K3) );
		ROUND5u(55, F3, (K3) );
	
		ROUND5u(60, F4, (K4) );
		ROUND5u(65, F4, (K4) );
		ROUND5u(70, F4, (K4) );
		ROUND5u(75, F4, (K4) );
		
		/* Mix in the IV again */
		A1 += H[0];
		B1 += H[1];

		A2 += H[0];
		B2 += H[1];
		
		/* Debugging! */
		if(0 && iters==0) {
			for(t=0; t < 80; t++) {
				X1 = (unsigned char*) (W1+t);
				X2 = (unsigned char*) (W2+t);
				printf("%2X %2X %2X %2X | %2X %2X %2X %2X\n",
					X1 [ 0 ] , X1 [ 1 ] , X1 [ 2 ] , X1 [ 3 ] , 
					X2 [ 0 ] , X2 [ 1 ] , X2 [ 2 ] , X2 [ 3 ] ) ;
			}
			
			X1 = (unsigned char*) W1;
			X2 = (unsigned char*) W2;
		}
		
		/* Quickly check which pipe contains the best result */
		if(A1 < A2)
			n = 0;
		else if(A1 > A2)
			n = 1;
		else if(B1 < B2)
			n = 0;
		else
			n = 1;
		
		switch(n) {
			case 0:
				A = A1;
				B = B1;
				W = W1;
				break;
			
			case 1:
				A = A2;
				B = B2;
				W = W2;
				break;
		}
		
		/* Is this the best bit count so far? */
		if(!(A & bitMask1Low) && !(B & bitMask1High)) {
			/* Count bits */
			gotBits = 0;
			if(A) {
				s = A;
				while(!(s & 0x80000000)) {
					s <<= 1;
					gotBits++;
				}
			} else {
				gotBits = 32;
				if(B) {
					s = B;
					while(!(s & 0x80000000)) {
						s <<= 1;
						gotBits++;
					}
				} else {
					gotBits = 64;
				}
			}
			
			if ( gotBits > *best ) { *best = gotBits; }
			/* Regenerate the bit mask */
			maxBits = gotBits+1;
			if(maxBits < 32) {
				bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1);
				bitMask1High = 0;
			} else {
				bitMask1Low = ~0;
				bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1);
			}

			/* Copy this result back to the block buffer */
			for(t=0; t < 16; t++)
				PUT_WORD(output + t*4, W[t]);
			
			/* Is it good enough to bail out? */
			if(gotBits >= bits) {
				return iters+2;
			}
		}
		
		if(0) return 0;
		MINTER_CALLBACK();
	}
	
	return iters+2;
}
