context: using System;
  
  using Nequeo.Cryptography.Key.Crypto.Parameters;
  using Nequeo.Cryptography.Key.Math;
  using Nequeo.Cryptography.Key.Security;
  
  namespace Nequeo.Cryptography.Key.Crypto.Generators
  {
  	/**
  	 * generate suitable parameters for GOST3410.
  	 */
  	public class Gost3410ParametersGenerator
  	{
  		private int             size;
  		private int             typeproc;
  		private SecureRandom    init_random;
  
  		/**
  		 * initialise the key generator.
  		 *
  		 * @param size size of the key
  		 * @param typeProcedure type procedure A,B = 1;  A',B' - else
  		 * @param random random byte source.
  		 */
  		public void Init(
  			int             size,
  			int             typeProcedure,
  			SecureRandom    random)
  		{
  			this.size = size;
  			this.typeproc = typeProcedure;
  			this.init_random = random;
  		}
  
  		//Procedure A
  		private int procedure_A(int x0, int c,  BigInteger[] pq, int size)
  		{
  			//Verify and perform condition: 0<x<2^16; 0<c<2^16; c - odd.
  			while(x0<0 || x0>65536)
  			{
  				x0 = init_random.NextInt()/32768;
  			}
  
  			while((c<0 || c>65536) || (c/2==0))
  			{
  				c = init_random.NextInt()/32768 + 1;
  			}
  
  			BigInteger C = BigInteger.ValueOf(c);
  			BigInteger constA16 = BigInteger.ValueOf(19381);
  
  			//step1
  			BigInteger[] y = new BigInteger[1]; // begin length = 1
  			y[0] = BigInteger.ValueOf(x0);
  
  			//step 2
  			int[] t = new int[1]; // t - orders; begin length = 1
  			t[0] = size;
  			int s = 0;
  			for (int i=0; t[i]>=17; i++)
  			{
  				// extension array t
  				int[] tmp_t = new int[t.Length + 1];             ///////////////
  					Array.Copy(t,0,tmp_t,0,t.Length);          //  extension
  				t = new int[tmp_t.Length];                       //  array t
  				Array.Copy(tmp_t, 0, t, 0, tmp_t.Length);  ///////////////
  
  				t[i+1] = t[i]/2;
  				s = i+1;
  			}
  
  			//step3
  			BigInteger[] p = new BigInteger[s+1];
  			p[s] = new BigInteger("8003",16); //set min prime number length 16 bit
  
  			int m = s-1;  //step4
  
  			for (int i=0; i<s; i++)
  			{
  				int rm = t[m]/16;  //step5
  
  			step6: for(;;)
  				   {
  					   //step 6
  					   BigInteger[] tmp_y = new BigInteger[y.Length];  ////////////////
  					   Array.Copy(y,0,tmp_y,0,y.Length);         //  extension
  					   y = new BigInteger[rm+1];                       //  array y
  					   Array.Copy(tmp_y,0,y,0,tmp_y.Length);     ////////////////
  
  					   for (int j=0; j<rm; j++)
  					   {
  						   y[j+1] = (y[j].Multiply(constA16).Add(C)).Mod(BigInteger.Two.Pow(16));
  					   }
  
  					   //step 7
  					   BigInteger Ym = BigInteger.Zero;
  					   for (int j=0; j<rm; j++)
  					   {
  						   Ym = Ym.Add(y[j].ShiftLeft(16*j));
  					   }
  
  					   y[0] = y[rm]; //step 8
  
  					   //step 9
  					   BigInteger N = BigInteger.One.ShiftLeft(t[m]-1).Divide(p[m+1]).Add(
  						   Ym.ShiftLeft(t[m]-1).Divide(p[m+1].ShiftLeft(16*rm)));
  
  					   if (N.TestBit(0))
  					   {
  						   N = N.Add(BigInteger.One);
  					   }
  
  					   //step 10
  
  						for(;;)
  						{
  							//step 11
  							BigInteger NByLastP = N.Multiply(p[m+1]);
  
  							if (NByLastP.BitLength > t[m])
  							{
  								goto step6; //step 12
  							}
  
  							p[m] = NByLastP.Add(BigInteger.One);
  
  							//step13
  							if (BigInteger.Two.ModPow(NByLastP, p[m]).CompareTo(BigInteger.One) == 0
  								&& BigInteger.Two.ModPow(N, p[m]).CompareTo(BigInteger.One) != 0)
  							{
  								break;
  							}
  
  							N = N.Add(BigInteger.Two);
  						}
  
  					   if (--m < 0)
  					   {
  						   pq[0] = p[0];
  						   pq[1] = p[1];
  						   return y[0].IntValue; //return for procedure B step 2
  					   }
  
  					   break; //step 14
  				   }
  			}
  			return y[0].IntValue;
  		}
  
  		//Procedure A'
  		private long procedure_Aa(long x0, long c, BigInteger[] pq, int size)
  		{
  			//Verify and perform condition: 0<x<2^32; 0<c<2^32; c - odd.
  			while(x0<0 || x0>4294967296L)
  			{
  				x0 = init_random.NextInt()*2;
  			}
  
  			while((c<0 || c>4294967296L) || (c/2==0))
  			{
  				c = init_random.NextInt()*2+1;
  			}
  
  			BigInteger C = BigInteger.ValueOf(c);
  			BigInteger constA32 = BigInteger.ValueOf(97781173);
  
  			//step1
  			BigInteger[] y = new BigInteger[1]; // begin length = 1
  			y[0] = BigInteger.ValueOf(x0);
  
  			//step 2
  			int[] t = new int[1]; // t - orders; begin length = 1
  			t[0] = size;
  			int s = 0;
  			for (int i=0; t[i]>=33; i++)
  			{
  				// extension array t
  				int[] tmp_t = new int[t.Length + 1];             ///////////////
  					Array.Copy(t,0,tmp_t,0,t.Length);          //  extension
  				t = new int[tmp_t.Length];                       //  array t
  				Array.Copy(tmp_t, 0, t, 0, tmp_t.Length);  ///////////////
  
  				t[i+1] = t[i]/2;
  				s = i+1;
  			}
  
  			//step3
  			BigInteger[] p = new BigInteger[s+1];
  			p[s] = new BigInteger("8000000B",16); //set min prime number length 32 bit
  
  			int m = s-1;  //step4
  
  			for (int i=0; i<s; i++)
  			{
  				int rm = t[m]/32;  //step5
  
  			step6: for(;;)
  				   {
  					   //step 6
  					   BigInteger[] tmp_y = new BigInteger[y.Length];  ////////////////
  						   Array.Copy(y,0,tmp_y,0,y.Length);         //  extension
  					   y = new BigInteger[rm+1];                       //  array y
  					   Array.Copy(tmp_y,0,y,0,tmp_y.Length);     ////////////////
  
  					   for (int j=0; j<rm; j++)
  					   {
  						   y[j+1] = (y[j].Multiply(constA32).Add(C)).Mod(BigInteger.Two.Pow(32));
  					   }
  
  					   //step 7
  					   BigInteger Ym = BigInteger.Zero;
  					   for (int j=0; j<rm; j++)
  					   {
  						   Ym = Ym.Add(y[j].ShiftLeft(32*j));
  					   }
  
  					   y[0] = y[rm]; //step 8
  
  					   //step 9
  					   BigInteger N = BigInteger.One.ShiftLeft(t[m]-1).Divide(p[m+1]).Add(
  						   Ym.ShiftLeft(t[m]-1).Divide(p[m+1].ShiftLeft(32*rm)));
  
  					   if (N.TestBit(0))
  					   {
  						   N = N.Add(BigInteger.One);
  					   }
  
  					   //step 10
  
  						for(;;)
  						{
  							//step 11
  							BigInteger NByLastP = N.Multiply(p[m+1]);
  
  							if (NByLastP.BitLength > t[m])
  							{
  								goto step6; //step 12
  							}
  
  							p[m] = NByLastP.Add(BigInteger.One);
  
  							//step13
  							if (BigInteger.Two.ModPow(NByLastP, p[m]).CompareTo(BigInteger.One) == 0
  								&& BigInteger.Two.ModPow(N, p[m]).CompareTo(BigInteger.One) != 0)
  							{
  								break;
  							}
  
  							N = N.Add(BigInteger.Two);
  						}
  
  					   if (--m < 0)
  					   {
  						   pq[0] = p[0];
  						   pq[1] = p[1];
  						   return y[0].LongValue; //return for procedure B' step 2
  					   }
  
  					   break; //step 14
  				   }
  			}
  			return y[0].LongValue;
  		}
  
  		//Procedure B
  		private void procedure_B(int x0, int c, BigInteger[] pq)
  		{
  			//Verify and perform condition: 0<x<2^16; 0<c<2^16; c - odd.
  			while(x0<0 || x0>65536)
  			{
  				x0 = init_random.NextInt()/32768;
  			}
  
  			while((c<0 || c>65536) || (c/2==0))
  			{
  				c = init_random.NextInt()/32768 + 1;
  			}
  
  			BigInteger [] qp = new BigInteger[2];
  			BigInteger q = null, Q = null, p = null;
  			BigInteger C = BigInteger.ValueOf(c);
  			BigInteger constA16 = BigInteger.ValueOf(19381);
  
  			//step1
  			x0 = procedure_A(x0, c, qp, 256);
  			q = qp[0];
  
  			//step2
  			x0 = procedure_A(x0, c, qp, 512);
  			Q = qp[0];
  
  			BigInteger[] y = new BigInteger[65];
  			y[0] = BigInteger.ValueOf(x0);
  
  			const int tp = 1024;
  
  			BigInteger qQ = q.Multiply(Q);
  
  step3:
  			for(;;)
  			{
  				//step 3
  				for (int j=0; j<64; j++)
  				{
  					y[j+1] = (y[j].Multiply(constA16).Add(C)).Mod(BigInteger.Two.Pow(16));
  				}
  
  				//step 4
  				BigInteger Y = BigInteger.Zero;
  
  				for (int j=0; j<64; j++)
  				{
  					Y = Y.Add(y[j].ShiftLeft(16*j));
  				}
  
  				y[0] = y[64]; //step 5
  
  				//step 6
  				BigInteger N = BigInteger.One.ShiftLeft(tp-1).Divide(qQ).Add(
  					Y.ShiftLeft(tp-1).Divide(qQ.ShiftLeft(1024)));
  
  				if (N.TestBit(0))
  				{
  					N = N.Add(BigInteger.One);
  				}
  
  				//step 7
  
  				for(;;)
  				{
  					//step 11
  					BigInteger qQN = qQ.Multiply(N);
  
  					if (qQN.BitLength > tp)
  					{
  						goto step3; //step 9
  					}
  
  					p = qQN.Add(BigInteger.One);
  
  					//step10
  					if (BigInteger.Two.ModPow(qQN, p).CompareTo(BigInteger.One) == 0
  						&& BigInteger.Two.ModPow(q.Multiply(N), p).CompareTo(BigInteger.One) != 0)
  					{
  
gt: 						pq[0] = p;