static Word32 f(int t, Word32 x, Word32 y, Word32 z)
        {
            // This function is used in Sha-1
            // should have 0 <= t <= 79

            if (t >= 0 && t <= 19)
            {
                return(Ch(x, y, z));
            }
            else if (t >= 20 && t <= 39)
            {
                return(Parity(x, y, z));
            }
            else if (t >= 40 && t <= 59)
            {
                return(Maj(x, y, z));
            }
            else if (t >= 60 && t <= 79)
            {
                return(Parity(x, y, z));
            }
            else
            {
                throw new ArgumentException("ERROR: t is out of bounds");
            }
        }
        static byte[] Sha256Algorithm(byte[] plaintext, Word32[] H0, int numberBits)
        {
            Block512[] blocks = ConvertPaddedTextToBlock512Array(PadPlainText512(plaintext));

            // Define the hash variables and set their initial values.
            Word32[] H = H0;

            for (int i = 0; i < blocks.Length; i++)
            {
                Word32[] W = CreateMessageScheduleSha256(blocks[i]);

                // Set the working variables a,...,h to the current hash values.
                Word32 a = H[0];
                Word32 b = H[1];
                Word32 c = H[2];
                Word32 d = H[3];
                Word32 e = H[4];
                Word32 f = H[5];
                Word32 g = H[6];
                Word32 h = H[7];

                for (int t = 0; t < 64; t++)
                {
                    Word32 T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[t] + W[t];
                    Word32 T2 = Sigma0_256(a) + Maj(a, b, c);
                    h = g;
                    g = f;
                    f = e;
                    e = d + T1;
                    d = c;
                    c = b;
                    b = a;
                    a = T1 + T2;
                }

                // Update the current value of the hash H after processing block i.
                H[0] += a;
                H[1] += b;
                H[2] += c;
                H[3] += d;
                H[4] += e;
                H[5] += f;
                H[6] += g;
                H[7] += h;
            }

            // Concatenate all the Word32 Hash Values
            byte[] hash = ShaUtilities.Word32ArrayToByteArray(H);

            // The number of bytes in the final output hash
            int numberBytes = numberBits / 8;

            byte[] truncatedHash = new byte[numberBytes];
            Array.Copy(hash, truncatedHash, numberBytes);

            return(truncatedHash);
        }
        public static Word32 ByteArrayToWord32(byte[] B, int startIndex)
        {
            // We assume: 0 <= startIndex < B. Length, and startIndex + 4 <= B.Length

            Word32 c      = 256;
            Word32 output = 0;

            for (int i = startIndex; i < startIndex + 4; i++)
            {
                output = output * c + (Word32)B[i];
            }

            return(output);
        }
        public static Word32[] ByteArrayToWord32Array(byte[] B)
        {
            // We assume B is not null, is not empty and number elements is divisible by 4
            int numberBytes = B.Length;
            int n           = numberBytes / 4; // 4 bytes for each Word32

            Word32[] word32Array = new Word32[n];

            for (int i = 0; i < n; i++)
            {
                word32Array[i] = ByteArrayToWord32(B, 4 * i);
            }

            return(word32Array);
        }
        static byte[] Sha1Algorithm(byte[] plaintext)
        {
            Block512[] blocks = ConvertPaddedTextToBlock512Array(PadPlainText512(plaintext));

            // Define the hash variable and set its initial values.
            Word32[] H = new Word32[5];
            H0Sha1.CopyTo(H, 0);

            for (int i = 0; i < blocks.Length; i++)
            {
                Word32[] W = CreateMessageScheduleSha1(blocks[i]);

                // Set the working variables a,...,e to the current hash values.
                Word32 a = H[0];
                Word32 b = H[1];
                Word32 c = H[2];
                Word32 d = H[3];
                Word32 e = H[4];

                for (int t = 0; t < 80; t++)
                {
                    Word32 T = RotL(5, a) + f(t, b, c, d) + e + K1[t] + W[t];
                    e = d;
                    d = c;
                    c = RotL(30, b);
                    b = a;
                    a = T;
                }

                // Update the current value of the hash H after processing block i.
                H[0] += a;
                H[1] += b;
                H[2] += c;
                H[3] += d;
                H[4] += e;
            }

            // Concatenating the final 5 hash words H[0],...,H[4] gives the digest.
            // Since each H[i] is 4 bytes, the digest is 5 * 4 = 20 bytes = 160 bits.
            return(ShaUtilities.Word32ArrayToByteArray(H));
        }
        static Word32[] CreateMessageScheduleSha1(Block512 block)
        {
            // The message schedule.
            Word32[] W = new Word32[80];

            // Prepare the message schedule W.
            // The first 16 words in W are the same as the words of the block.
            // The remaining 80-16 = 64 words in W are functions of the previously defined words.
            for (int t = 0; t < 80; t++)
            {
                if (t < 16)
                {
                    W[t] = block.words[t];
                }
                else
                {
                    W[t] = RotL(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);
                }
            }

            return(W);
        }
        static Word32[] CreateMessageScheduleSha256(Block512 block)
        {
            // The message schedule.
            Word32[] W = new Word32[64];

            // Prepare the message schedule W.
            // The first 16 words in W are the same as the words of the block.
            // The remaining 64-16 = 48 words in W are functions of the previously defined words.
            for (int t = 0; t < 64; t++)
            {
                if (t < 16)
                {
                    W[t] = block.words[t];
                }
                else
                {
                    W[t] = sigma1_256(W[t - 2]) + W[t - 7] + sigma0_256(W[t - 15]) + W[t - 16];
                }
            }

            return(W);
        }
 // Returns an array of 4 bytes.
 public static byte[] Word32ToByteArray(Word32 x)
 {
     byte[] b = BitConverter.GetBytes(x);
     Array.Reverse(b);
     return(b);
 }
 static Word32 sigma1_256(Word32 x)
 {
     return(RotR(17, x) ^ RotR(19, x) ^ ShR(10, x));
 }
 static Word32 sigma0_256(Word32 x)
 {
     return(RotR(7, x) ^ RotR(18, x) ^ ShR(3, x));
 }
 static Word32 Sigma1_256(Word32 x)
 {
     return(RotR(6, x) ^ RotR(11, x) ^ RotR(25, x));
 }
 static Word32 Sigma0_256(Word32 x)
 {
     return(RotR(2, x) ^ RotR(13, x) ^ RotR(22, x));
 }
 static Word32 Maj(Word32 x, Word32 y, Word32 z)
 {
     return((x & y) ^ (x & z) ^ (y & z));
 }
 static Word32 Ch(Word32 x, Word32 y, Word32 z)
 {
     return((x & y) ^ (~x & z));
 }
 static Word32 RotL(int n, Word32 x)
 {
     // should have 0 <= n < 32
     return((x << n) | (x >> 32 - n));
 }
        // Most of these functions have a Word32 version and a Word64 version.
        // Sometimes they are the same (Ch, Maj,..) but sometimes different (Sigma0_256, Sigma0_512).
        // We do not need a RotL or Parity function for Word64 since they are only used in Sha-1.

        static Word32 ShR(int n, Word32 x)
        {
            // should have 0 <= n < 32
            return(x >> n);
        }
 static Word32 Parity(Word32 x, Word32 y, Word32 z)
 {
     return(x ^ y ^ z);
 }
 public static string Word32ToHexString(Word32 x)
 {
     return(ByteArrayToHexString(Word32ToByteArray(x)));
 }