/*
    A Rust implementation of a base64 encoder and decoder.
    Written from scratch.
*/

// The charset and padding used for en- and decoding.
const CHARSET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const PADDING: char = '=';

/*
    Combines the two provided bytes into an u16,
    and collects 6 bits from it using an AND mask:

    Example:
    Bytes: X and Y
    (Bits of those bytes will be signified using the names of their byte)
    Offset: 4

    `combined` = 0bXXXXXXXXYYYYYYYY
    AND mask:
    0b1111110000000000 >> offset (4) = 0b0000111111000000
    `combined` with mask applied:
    0b0000XXYYYY000000
    Shift the value right by (16 bit number) - (6 bit mask) - (4 offset) = 6:
    0b0000000000XXYYYY
    And then turn it into an u8:
    0b00XXYYYY (Return value)
*/
fn collect_six_bits(from: (u8, u8), offset: u8) -> u8 {
    let combined: u16 = ((from.0 as u16) << 8) | (from.1 as u16);
    ((combined & (0b1111110000000000u16 >> offset)) >> (10 - offset)) as u8
}

pub fn base64_encode(data: &Vec<u8>) -> String {
    let mut bits_encoded = 0usize;
    let mut encoded_string = String::new();
    // Using modulo twice to prevent an underflow, Wolfram|Alpha says this is optimal
    let padding_needed = ((6 - (data.len() * 8) % 6) / 2) % 3;
    loop {
        let lower_byte_index_to_encode = bits_encoded / 8usize; // Integer division
        if lower_byte_index_to_encode == data.len() {
            break;
        }
        let lower_byte_to_encode = data[lower_byte_index_to_encode];
        let upper_byte_to_encode = if (lower_byte_index_to_encode + 1) == data.len() {
            0u8 // Padding
        } else {
            data[lower_byte_index_to_encode + 1]
        };
        let bytes_to_encode = (lower_byte_to_encode, upper_byte_to_encode);
        let offset: u8 = (bits_encoded % 8) as u8;
        encoded_string.push(CHARSET[collect_six_bits(bytes_to_encode, offset) as usize] as char);
        bits_encoded += 6;
    }
    for _ in 0..padding_needed {
        encoded_string.push(PADDING);
    }
    encoded_string
}

/*
    Performs the exact inverse of the above description of `base64_encode`
*/
pub fn base64_decode(data: &str) -> Result<Vec<u8>, (&str, u8)> {
    let mut collected_bits = 0;
    let mut byte_buffer = 0u16;
    let mut databytes = data.bytes();
    let mut outputbytes = Vec::<u8>::new();
    'decodeloop: loop {
        while collected_bits < 8 {
            if let Some(nextbyte) = databytes.next() {
                // Finds the first occurence of the latest byte
                if let Some(idx) = CHARSET.iter().position(|&x| x == nextbyte) {
                    byte_buffer |= ((idx & 0b00111111) as u16) << (10 - collected_bits);
                    collected_bits += 6;
                } else if nextbyte == (PADDING as u8) {
                    collected_bits -= 2; // Padding only comes at the end so this works
                } else {
                    return Err((
                        "Failed to decode base64: Expected byte from charset, found invalid byte.",
                        nextbyte,
                    ));
                }
            } else {
                break 'decodeloop;
            }
        }
        outputbytes.push(((0b1111111100000000 & byte_buffer) >> 8) as u8);
        byte_buffer &= 0b0000000011111111;
        byte_buffer <<= 8;
        collected_bits -= 8;
    }
    if collected_bits != 0 {
        return Err(("Failed to decode base64: Invalid padding.", collected_bits));
    }
    Ok(outputbytes)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn pregenerated_random_bytes_encode() {
        macro_rules! test_encode {
            ($left: expr, $right: expr) => {
                assert_eq!(base64_encode(&$left.to_vec()), $right);
            };
        }
        test_encode!(
            b"\xd31\xc9\x87D\xfe\xaa\xb3\xff\xef\x8c\x0eoD",
            "0zHJh0T+qrP/74wOb0Q="
        );
        test_encode!(
            b"\x9f\x0e8\xbc\xf5\xd0-\xb4.\xd4\xf0?\x8f\xe7\t{.\xff/6\xcbTY!\xae9\x82",
            "nw44vPXQLbQu1PA/j+cJey7/LzbLVFkhrjmC"
        );
        test_encode!(b"\x7f3\x15\x1a\xd3\xf91\x9bS\xa44=", "fzMVGtP5MZtTpDQ9");
        test_encode!(
            b"7:\xf5\xd1[\xbfV/P\x18\x03\x00\xdc\xcd\xa1\xecG",
            "Nzr10Vu/Vi9QGAMA3M2h7Ec="
        );
        test_encode!(
            b"\xc3\xc9\x18={\xc4\x08\x97wN\xda\x81\x84?\x94\xe6\x9e",
            "w8kYPXvECJd3TtqBhD+U5p4="
        );
        test_encode!(
            b"\x8cJ\xf8e\x13\r\x8fw\xa8\xe6G\xce\x93c*\xe7M\xb6\xd7",
            "jEr4ZRMNj3eo5kfOk2Mq50221w=="
        );
        test_encode!(
            b"\xde\xc4~\xb2}\xb1\x14F.~\xa1z|s\x90\x8dd\x9b\x04\x81\xf2\x92{",
            "3sR+sn2xFEYufqF6fHOQjWSbBIHykns="
        );
        test_encode!(
            b"\xf0y\t\x14\xd161n\x03e\xed\x0e\x05\xdf\xc1\xb9\xda",
            "8HkJFNE2MW4DZe0OBd/Budo="
        );
        test_encode!(
            b"*.\x8e\x1d@\x1ac\xdd;\x9a\xcc \x0c\xc2KI",
            "Ki6OHUAaY907mswgDMJLSQ=="
        );
        test_encode!(b"\xd6\x829\x82\xbc\x00\xc9\xfe\x03", "1oI5grwAyf4D");
        test_encode!(
            b"\r\xf2\xb4\xd4\xa1g\x8fhl\xaa@\x98\x00\xda\x95",
            "DfK01KFnj2hsqkCYANqV"
        );
        test_encode!(
            b"\x1a\xfaV\x1a\xc2e\xc0\xad\xef|\x07\xcf\xa9\xb7O",
            "GvpWGsJlwK3vfAfPqbdP"
        );
        test_encode!(b"\xc20{_\x81\xac", "wjB7X4Gs");
        test_encode!(
            b"B\xa85\xac\xe9\x0ev-\x8bT\xb3|\xde",
            "Qqg1rOkOdi2LVLN83g=="
        );
        test_encode!(
            b"\x05\xe0\xeeSs\xfdY9\x0b7\x84\xfc-\xec",
            "BeDuU3P9WTkLN4T8Lew="
        );
        test_encode!(
            b"Qj\x92\xfa?\xa5\xe3_[\xde\x82\x97{$\xb2\xf9\xd5\x98\x0cy\x15\xe4R\x8d",
            "UWqS+j+l419b3oKXeySy+dWYDHkV5FKN"
        );
        test_encode!(b"\x853\xe0\xc0\x1d\xc1", "hTPgwB3B");
        test_encode!(b"}2\xd0\x13m\x8d\x8f#\x9c\xf5,\xc7", "fTLQE22NjyOc9SzH");
    }

    #[test]
    fn pregenerated_random_bytes_decode() {
        macro_rules! test_decode {
            ($left: expr, $right: expr) => {
                assert_eq!(
                    base64_decode(&String::from($left)).unwrap(),
                    $right.to_vec()
                );
            };
        }
        test_decode!(
            "0zHJh0T+qrP/74wOb0Q=",
            b"\xd31\xc9\x87D\xfe\xaa\xb3\xff\xef\x8c\x0eoD"
        );
        test_decode!(
            "nw44vPXQLbQu1PA/j+cJey7/LzbLVFkhrjmC",
            b"\x9f\x0e8\xbc\xf5\xd0-\xb4.\xd4\xf0?\x8f\xe7\t{.\xff/6\xcbTY!\xae9\x82"
        );
        test_decode!("fzMVGtP5MZtTpDQ9", b"\x7f3\x15\x1a\xd3\xf91\x9bS\xa44=");
        test_decode!(
            "Nzr10Vu/Vi9QGAMA3M2h7Ec=",
            b"7:\xf5\xd1[\xbfV/P\x18\x03\x00\xdc\xcd\xa1\xecG"
        );
        test_decode!(
            "w8kYPXvECJd3TtqBhD+U5p4=",
            b"\xc3\xc9\x18={\xc4\x08\x97wN\xda\x81\x84?\x94\xe6\x9e"
        );
        test_decode!(
            "jEr4ZRMNj3eo5kfOk2Mq50221w==",
            b"\x8cJ\xf8e\x13\r\x8fw\xa8\xe6G\xce\x93c*\xe7M\xb6\xd7"
        );
        test_decode!(
            "3sR+sn2xFEYufqF6fHOQjWSbBIHykns=",
            b"\xde\xc4~\xb2}\xb1\x14F.~\xa1z|s\x90\x8dd\x9b\x04\x81\xf2\x92{"
        );
        test_decode!(
            "8HkJFNE2MW4DZe0OBd/Budo=",
            b"\xf0y\t\x14\xd161n\x03e\xed\x0e\x05\xdf\xc1\xb9\xda"
        );
        test_decode!(
            "Ki6OHUAaY907mswgDMJLSQ==",
            b"*.\x8e\x1d@\x1ac\xdd;\x9a\xcc \x0c\xc2KI"
        );
        test_decode!("1oI5grwAyf4D", b"\xd6\x829\x82\xbc\x00\xc9\xfe\x03");
        test_decode!(
            "DfK01KFnj2hsqkCYANqV",
            b"\r\xf2\xb4\xd4\xa1g\x8fhl\xaa@\x98\x00\xda\x95"
        );
        test_decode!(
            "GvpWGsJlwK3vfAfPqbdP",
            b"\x1a\xfaV\x1a\xc2e\xc0\xad\xef|\x07\xcf\xa9\xb7O"
        );
        test_decode!("wjB7X4Gs", b"\xc20{_\x81\xac");
        test_decode!(
            "Qqg1rOkOdi2LVLN83g==",
            b"B\xa85\xac\xe9\x0ev-\x8bT\xb3|\xde"
        );
        test_decode!(
            "BeDuU3P9WTkLN4T8Lew=",
            b"\x05\xe0\xeeSs\xfdY9\x0b7\x84\xfc-\xec"
        );
        test_decode!(
            "UWqS+j+l419b3oKXeySy+dWYDHkV5FKN",
            b"Qj\x92\xfa?\xa5\xe3_[\xde\x82\x97{$\xb2\xf9\xd5\x98\x0cy\x15\xe4R\x8d"
        );
        test_decode!("hTPgwB3B", b"\x853\xe0\xc0\x1d\xc1");
        test_decode!("fTLQE22NjyOc9SzH", b"}2\xd0\x13m\x8d\x8f#\x9c\xf5,\xc7");
    }

    #[test]
    fn encode_decode() {
        macro_rules! test_e_d {
            ($text: expr) => {
                assert_eq!(
                    base64_decode(&base64_encode(&$text.to_vec())).unwrap(),
                    $text
                );
            };
        }
        test_e_d!(b"green");
        test_e_d!(b"The quick brown fox jumped over the lazy dog.");
        test_e_d!(b"Lorem Ipsum sit dolor amet.");
        test_e_d!(b"0");
        test_e_d!(b"01");
        test_e_d!(b"012");
        test_e_d!(b"0123");
        test_e_d!(b"0123456789");
    }

    #[test]
    fn decode_encode() {
        macro_rules! test_d_e {
            ($data: expr) => {
                assert_eq!(
                    base64_encode(&base64_decode(&String::from($data)).unwrap()),
                    String::from($data)
                );
            };
        }
        test_d_e!("TG9uZyBsaXZlIGVhc3RlciBlZ2dzIDop");
        test_d_e!("SGFwcHkgSGFja3RvYmVyZmVzdCE=");
        test_d_e!("PVRoZSBBbGdvcml0aG1zPQ==");
    }
}


//! Caesar Cipher
//! Based on cipher_crypt::caesar
//!
//! # Algorithm
//!
//! Rotate each ascii character by shift. The most basic example is ROT 13, which rotates 'a' to
//! 'n'. This implementation does not rotate unicode characters.

/// Caesar cipher to rotate cipher text by shift and return an owned String.
pub fn caesar(cipher: &str, shift: u8) -> String {
    cipher
        .chars()
        .map(|c| {
            if c.is_ascii_alphabetic() {
                let first = if c.is_ascii_lowercase() { b'a' } else { b'A' };
                // modulo the distance to keep character range
                (first + (c as u8 + shift - first) % 26) as char
            } else {
                c
            }
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn empty() {
        assert_eq!(caesar("", 13), "");
    }

    #[test]
    fn caesar_rot_13() {
        assert_eq!(caesar("rust", 13), "ehfg");
    }

    #[test]
    fn caesar_unicode() {
        assert_eq!(caesar("attack at dawn æ”»", 5), "fyyfhp fy ifbs æ”»");
    }
}


macro_rules! quarter_round {
    ($a:expr,$b:expr,$c:expr,$d:expr) => {
        $a = $a.wrapping_add($b);
        $d = ($d ^ $a).rotate_left(16);
        $c = $c.wrapping_add($d);
        $b = ($b ^ $c).rotate_left(12);
        $a = $a.wrapping_add($b);
        $d = ($d ^ $a).rotate_left(8);
        $c = $c.wrapping_add($d);
        $b = ($b ^ $c).rotate_left(7);
    };
}

#[allow(dead_code)]
// "expand 32-byte k", written in little-endian order
pub const C: [u32; 4] = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574];

/// ChaCha20 implementation based on RFC8439
///
/// ChaCha20 is a stream cipher developed independently by Daniel J. Bernstein.\
/// To use it, the `chacha20` function should be called with appropriate
/// parameters and the output of the function should be XORed with plain text.
///
/// `chacha20` function takes as input an array of 16 32-bit integers (512 bits)
/// of which 128 bits is the constant 'expand 32-byte k', 256 bits is the key,
/// and 128 bits are nonce and counter. According to RFC8439, the nonce should
/// be 96 bits long, which leaves 32 bits for the counter. Given that the block
/// length is 512 bits, this leaves enough counter values to encrypt 256GB of
/// data.
///
/// The 16 input numbers can be thought of as the elements of a 4x4 matrix like
/// the one bellow, on which we do the main operations of the cipher.
///
/// ```text
/// +----+----+----+----+
/// | 00 | 01 | 02 | 03 |
/// +----+----+----+----+
/// | 04 | 05 | 06 | 07 |
/// +----+----+----+----+
/// | 08 | 09 | 10 | 11 |
/// +----+----+----+----+
/// | 12 | 13 | 14 | 15 |
/// +----+----+----+----+
/// ```
///
/// As per the diagram bellow, `input[0, 1, 2, 3]` are the constants mentioned
/// above, `input[4..=11]` is filled with the key, and `input[6..=9]` should be
/// filled with nonce and counter values. The output of the function is stored
/// in `output` variable and can be XORed with the plain text to produce the
/// cipher text.
///
/// ```text
/// +------+------+------+------+
/// |      |      |      |      |
/// | C[0] | C[1] | C[2] | C[3] |
/// |      |      |      |      |
/// +------+------+------+------+
/// |      |      |      |      |
/// | key0 | key1 | key2 | key3 |
/// |      |      |      |      |
/// +------+------+------+------+
/// |      |      |      |      |
/// | key4 | key5 | key6 | key7 |
/// |      |      |      |      |
/// +------+------+------+------+
/// |      |      |      |      |
/// | ctr0 | no.0 | no.1 | no.2 |
/// |      |      |      |      |
/// +------+------+------+------+
/// ```
///
/// Note that the constants, the key, and the nonce should be written in
/// little-endian order, meaning that for example if the key is 01:02:03:04
/// (in hex), it corresponds to the integer `0x04030201`. It is important to
/// know that the hex value of the counter is meaningless, and only its integer
/// value matters, and it should start with (for example) `0x00000000`, and then
/// `0x00000001` and so on until `0xffffffff`. Keep in mind that as soon as we get
/// from bytes to words, we stop caring about their representation in memory,
/// and we only need the math to be correct.
///
/// The output of the function can be used without any change, as long as the
/// plain text has the same endianness. For example if the plain text is
/// "hello world", and the first word of the output is `0x01020304`, then the
/// first byte of plain text ('h') should be XORed with the least-significant
/// byte of `0x01020304`, which is `0x04`.
pub fn chacha20(input: &[u32; 16], output: &mut [u32; 16]) {
    output.copy_from_slice(&input[..]);
    for _ in 0..10 {
        // Odd round (column round)
        quarter_round!(output[0], output[4], output[8], output[12]); //  column 1
        quarter_round!(output[1], output[5], output[9], output[13]); //  column 2
        quarter_round!(output[2], output[6], output[10], output[14]); // column 3
        quarter_round!(output[3], output[7], output[11], output[15]); // column 4

        // Even round (diagonal round)
        quarter_round!(output[0], output[5], output[10], output[15]); // diag 1
        quarter_round!(output[1], output[6], output[11], output[12]); // diag 2
        quarter_round!(output[2], output[7], output[8], output[13]); //  diag 3
        quarter_round!(output[3], output[4], output[9], output[14]); //  diag 4
    }
    for (a, &b) in output.iter_mut().zip(input.iter()) {
        *a = a.wrapping_add(b);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fmt::Write;

    fn output_hex(inp: &[u32; 16]) -> String {
        let mut res = String::new();
        res.reserve(512 / 4);
        for &x in inp {
            write!(&mut res, "{x:08x}").unwrap();
        }
        res
    }

    #[test]
    // test vector 1
    fn basic_tv1() {
        let mut inp = [0u32; 16];
        let mut out = [0u32; 16];
        inp[0] = C[0];
        inp[1] = C[1];
        inp[2] = C[2];
        inp[3] = C[3];
        inp[4] = 0x03020100; // The key is 00:01:02:..:1f (hex)
        inp[5] = 0x07060504;
        inp[6] = 0x0b0a0908;
        inp[7] = 0x0f0e0d0c;
        inp[8] = 0x13121110;
        inp[9] = 0x17161514;
        inp[10] = 0x1b1a1918;
        inp[11] = 0x1f1e1d1c;
        inp[12] = 0x00000001; // The value of counter is 1 (an integer). Nonce:
        inp[13] = 0x09000000; // 00:00:00:09
        inp[14] = 0x4a000000; // 00:00:00:4a
        inp[15] = 0x00000000; // 00:00:00:00
        chacha20(&inp, &mut out);
        assert_eq!(
            output_hex(&out),
            concat!(
                "e4e7f11015593bd11fdd0f50c47120a3c7f4d1c70368c0339aaa22044e6cd4c3",
                "466482d209aa9f0705d7c214a2028bd9d19c12b5b94e16dee883d0cb4e3c50a2"
            )
        );
    }
}


// Based on the TheAlgorithms/Python
// RFC 3526 - More Modular Exponential (MODP) Diffie-Hellman groups for
// Internet Key Exchange (IKE) https://tools.ietf.org/html/rfc3526

use lazy_static;
use num_bigint::BigUint;
use num_traits::{Num, Zero};
use std::{
    collections::HashMap,
    time::{SystemTime, UNIX_EPOCH},
};

// Using lazy static to initialize statics that require code to be executed at runtime.
lazy_static! {
    // A map of predefined prime numbers for different bit lengths, as specified in RFC 3526
    static ref PRIMES: HashMap<u8, BigUint> = {
        let mut m:HashMap<u8, BigUint> = HashMap::new();
        m.insert(
            // 1536-bit
            5,
            BigUint::parse_bytes(
                b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\
                    29024E088A67CC74020BBEA63B139B22514A08798E3404DD\
                    EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\
                    E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\
                    EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\
                    C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\
                    83655D23DCA3AD961C62F356208552BB9ED529077096966D\
                    670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF",
                16
            ).unwrap()
        );
        m.insert(
            // 2048-bit
            14,
            BigUint::parse_bytes(
                b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\
                29024E088A67CC74020BBEA63B139B22514A08798E3404DD\
                EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\
                E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\
                EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\
                C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\
                83655D23DCA3AD961C62F356208552BB9ED529077096966D\
                670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B\
                E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9\
                DE2BCBF6955817183995497CEA956AE515D2261898FA0510\
                15728E5A8AACAA68FFFFFFFFFFFFFFFF",
                16
            ).unwrap()
        );

    m.insert(
    // 3072-bit
    15,
    BigUint::parse_bytes(
            b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\
            29024E088A67CC74020BBEA63B139B22514A08798E3404DD\
            EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\
            E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\
            EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\
            C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\
            83655D23DCA3AD961C62F356208552BB9ED529077096966D\
            670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B\
            E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9\
            DE2BCBF6955817183995497CEA956AE515D2261898FA0510\
            15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64\
            ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7\
            ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B\
            F12FFA06D98A0864D87602733EC86A64521F2B18177B200C\
            BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31\
            43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF",
            16
        ).unwrap()
    );
    m.insert(
    // 4096-bit
    16,
    BigUint::parse_bytes(
            b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\
            29024E088A67CC74020BBEA63B139B22514A08798E3404DD\
            EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\
            E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\
            EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\
            C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\
            83655D23DCA3AD961C62F356208552BB9ED529077096966D\
            670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B\
            E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9\
            DE2BCBF6955817183995497CEA956AE515D2261898FA0510\
            15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64\
            ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7\
            ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B\
            F12FFA06D98A0864D87602733EC86A64521F2B18177B200C\
            BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31\
            43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7\
            88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA\
            2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6\
            287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED\
            1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9\
            93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199\
            FFFFFFFFFFFFFFFF",
            16
        ).unwrap()
    );
    m.insert(
    // 6144-bit
    17,
     BigUint::parse_bytes(
            b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\
             8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\
             302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\
             A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\
             49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\
             FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\
             670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\
             180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\
             3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\
             04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\
             B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\
             1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\
             BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\
             E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\
             99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\
             04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\
             233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\
             D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492\
             36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406\
             AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918\
             DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151\
             2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03\
             F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F\
             BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA\
             CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B\
             B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632\
             387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E\
             6DCC4024FFFFFFFFFFFFFFFF",
            16
        ).unwrap()
    );

    m.insert(
    // 8192-bit
    18,
    BigUint::parse_bytes(
             b"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\
               29024E088A67CC74020BBEA63B139B22514A08798E3404DD\
               EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\
               E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\
               EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\
               C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\
               83655D23DCA3AD961C62F356208552BB9ED529077096966D\
               670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B\
               E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9\
               DE2BCBF6955817183995497CEA956AE515D2261898FA0510\
               15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64\
               ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7\
               ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B\
               F12FFA06D98A0864D87602733EC86A64521F2B18177B200C\
               BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31\
               43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7\
               88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA\
               2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6\
               287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED\
               1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9\
               93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492\
               36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD\
               F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831\
               179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B\
               DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF\
               5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6\
               D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3\
               23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA\
               CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328\
               06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C\
               DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE\
               12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4\
               38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300\
               741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568\
               3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9\
               22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B\
               4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A\
               062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36\
               4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1\
               B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92\
               4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47\
               9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71\
               60C980DD98EDD3DFFFFFFFFFFFFFFFFF",
            16
        ).unwrap()
    );
    m
    };
}

/// Generating random number, should use num_bigint::RandomBits if possible.
fn rand() -> usize {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .subsec_nanos() as usize
}

pub struct DiffieHellman {
    prime: BigUint,
    private_key: BigUint,
    public_key: BigUint,
    generator: u8,
}

impl DiffieHellman {
    // Diffie-Hellman key exchange algorithm is based on the following mathematical concepts:

    //  - A large prime number p (known as the prime modulus) is chosen and shared by both parties.

    //  - A base number g (known as the generator) is chosen and shared by both parties.

    //  - Each party generates a private key a or b (which are secret and only known to that party) and calculates a corresponding public key A or B using the following formulas:
    //          - A = g^a mod p
    //          - B = g^b mod p

    //  - Each party then exchanges their public keys with each other.

    //  - Each party then calculates the shared secret key s using the following formula:
    //          - s = B^a mod p or s = A^b mod p

    // Both parties now have the same shared secret key s which can be used for encryption or authentication.

    pub fn new(group: Option<u8>) -> Self {
        let mut _group: u8 = 14;
        if let Some(x) = group {
            _group = x;
        }

        if !PRIMES.contains_key(&_group) {
            panic!("group not in primes")
        }

        // generate private key
        let private_key: BigUint = BigUint::from(rand());

        Self {
            prime: PRIMES[&_group].clone(),
            private_key,
            generator: 2, // the generator is 2 for all the primes if this would not be the case it can be added to hashmap
            public_key: BigUint::default(),
        }
    }

    /// get private key as hexadecimal String
    pub fn get_private_key(&self) -> String {
        self.private_key.to_str_radix(16)
    }

    /// Generate public key A = g**a mod p
    pub fn generate_public_key(&mut self) -> String {
        self.public_key = BigUint::from(self.generator).modpow(&self.private_key, &self.prime);
        self.public_key.to_str_radix(16)
    }

    pub fn is_valid_public_key(&self, key_str: &str) -> bool {
        // the unwrap_or_else will make sure it is false, because 2 <= 0 and therefor False is returned
        let key = BigUint::from_str_radix(key_str, 16)
            .unwrap_or_else(|_| BigUint::parse_bytes(b"0", 16).unwrap());

        // Check if the other public key is valid based on NIST SP800-56
        if BigUint::from(2_u8) <= key
            && key <= &self.prime - BigUint::from(2_u8)
            && !key
                .modpow(
                    &((&self.prime - BigUint::from(1_u8)) / BigUint::from(2_u8)),
                    &self.prime,
                )
                .is_zero()
        {
            return true;
        }
        false
    }

    /// Generate the shared key
    pub fn generate_shared_key(self, other_key_str: &str) -> Option<String> {
        let other_key = BigUint::from_str_radix(other_key_str, 16)
            .unwrap_or_else(|_| BigUint::parse_bytes(b"0", 16).unwrap());
        if !self.is_valid_public_key(&other_key.to_str_radix(16)) {
            return None;
        }

        let shared_key = other_key.modpow(&self.private_key, &self.prime);
        Some(shared_key.to_str_radix(16))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn verify_invalid_pub_key() {
        let diffie = DiffieHellman::new(Some(14));
        assert!(!diffie.is_valid_public_key("0000"));
    }

    #[test]
    fn verify_valid_pub_key() {
        let diffie = DiffieHellman::new(Some(14));
        assert!(diffie.is_valid_public_key("EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"));
    }

    #[test]
    fn verify_invalid_pub_key_same_as_prime() {
        let diffie = DiffieHellman::new(Some(14));
        assert!(!diffie.is_valid_public_key(
            "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1\
        29024E088A67CC74020BBEA63B139B22514A08798E3404DD\
        EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245\
        E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED\
        EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D\
        C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F\
        83655D23DCA3AD961C62F356208552BB9ED529077096966D\
        670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B\
        E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9\
        DE2BCBF6955817183995497CEA956AE515D2261898FA0510\
        15728E5A8AACAA68FFFFFFFFFFFFFFFF"
        ));
    }

    #[test]
    fn verify_key_exchange() {
        let mut alice = DiffieHellman::new(Some(16));
        let mut bob = DiffieHellman::new(Some(16));

        // Private key not used, showed for illustrative purpose
        let _alice_private = alice.get_private_key();
        let alice_public = alice.generate_public_key();

        // Private key not used, showed for illustrative purpose
        let _bob_private = bob.get_private_key();
        let bob_public = bob.generate_public_key();

        // generating shared key using the struct implemenations
        let alice_shared = alice.generate_shared_key(bob_public.as_str()).unwrap();
        let bob_shared = bob.generate_shared_key(alice_public.as_str()).unwrap();
        assert_eq!(alice_shared, bob_shared);
    }
}


pub trait Hasher<const DIGEST_BYTES: usize> {
    /// return a new instance with default parameters
    fn new_default() -> Self;

    /// Add new data
    fn update(&mut self, data: &[u8]);

    /// Returns the hash of current data. If it is necessary does finalization
    /// work on the instance, thus it may no longer make sense to do `update`
    /// after calling this.
    fn get_hash(&mut self) -> [u8; DIGEST_BYTES];
}

/// HMAC based on RFC2104, applicable to many cryptographic hash functions
pub struct HMAC<const KEY_BYTES: usize, const DIGEST_BYTES: usize, H: Hasher<DIGEST_BYTES>> {
    pub inner_internal_state: H,
    pub outer_internal_state: H,
}

impl<const KEY_BYTES: usize, const DIGEST_BYTES: usize, H: Hasher<DIGEST_BYTES>>
    HMAC<KEY_BYTES, DIGEST_BYTES, H>
{
    pub fn new_default() -> Self {
        HMAC {
            inner_internal_state: H::new_default(),
            outer_internal_state: H::new_default(),
        }
    }

    /// Note that `key` must be no longer than `KEY_BYTES`. According to RFC,
    /// if it is so, you should replace it with its hash. We do not do this
    /// automatically due to fear of `DIGEST_BYTES` not being the same as
    /// `KEY_BYTES` or even being longer than it
    pub fn add_key(&mut self, key: &[u8]) -> Result<(), &'static str> {
        match key.len().cmp(&KEY_BYTES) {
            std::cmp::Ordering::Less | std::cmp::Ordering::Equal => {
                let mut tmp_key = [0u8; KEY_BYTES];
                for (d, s) in tmp_key.iter_mut().zip(key.iter()) {
                    *d = *s;
                }
                // key ^ 0x363636.. should be used as inner key
                for b in tmp_key.iter_mut() {
                    *b ^= 0x36;
                }
                self.inner_internal_state.update(&tmp_key);
                // key ^ 0x5c5c5c.. should be used as outer key, but the key is
                // already XORed with 0x363636.. , so it must now be XORed with
                // 0x6a6a6a..
                for b in tmp_key.iter_mut() {
                    *b ^= 0x6a;
                }
                self.outer_internal_state.update(&tmp_key);
                Ok(())
            }
            _ => Err("Key is longer than `KEY_BYTES`."),
        }
    }

    pub fn update(&mut self, data: &[u8]) {
        self.inner_internal_state.update(data);
    }

    pub fn finalize(&mut self) -> [u8; DIGEST_BYTES] {
        self.outer_internal_state
            .update(&self.inner_internal_state.get_hash());
        self.outer_internal_state.get_hash()
    }
}

#[cfg(test)]
mod tests {
    use super::super::sha256::tests::get_hash_string;
    use super::super::SHA256;
    use super::HMAC;

    #[test]
    fn sha256_basic() {
        // To test this, use the following command on linux:
        // echo -n "Hello World" | openssl sha256 -hex -mac HMAC -macopt hexkey:"deadbeef"
        let mut hmac: HMAC<64, 32, SHA256> = HMAC::new_default();
        hmac.add_key(&[0xde, 0xad, 0xbe, 0xef]).unwrap();
        hmac.update(b"Hello World");
        let hash = hmac.finalize();
        assert_eq!(
            get_hash_string(&hash),
            "f585fc4536e8e7f378437465b65b6c2eb79036409b18a7d28b6d4c46d3a156f8"
        );
    }
}


mod aes;
mod another_rot13;
mod base64;
mod blake2b;
mod caesar;
mod chacha;
mod diffie_hellman;
mod hashing_traits;
mod kerninghan;
mod morse_code;
mod polybius;
mod rail_fence;
mod rot13;
mod salsa;
mod sha256;
mod sha3;
mod tea;
mod theoretical_rot13;
mod transposition;
mod vigenere;
mod xor;
pub use self::aes::{aes_decrypt, aes_encrypt, AesKey};
pub use self::another_rot13::another_rot13;
pub use self::base64::{base64_decode, base64_encode};
pub use self::blake2b::blake2b;
pub use self::caesar::caesar;
pub use self::chacha::chacha20;
pub use self::diffie_hellman::DiffieHellman;
pub use self::hashing_traits::Hasher;
pub use self::hashing_traits::HMAC;
pub use self::kerninghan::kerninghan;
pub use self::morse_code::{decode, encode};
pub use self::polybius::{decode_ascii, encode_ascii};
pub use self::rail_fence::{rail_fence_decrypt, rail_fence_encrypt};
pub use self::rot13::rot13;
pub use self::salsa::salsa20;
pub use self::sha256::SHA256;
pub use self::sha3::{sha3_224, sha3_256, sha3_384, sha3_512};
pub use self::tea::{tea_decrypt, tea_encrypt};
pub use self::theoretical_rot13::theoretical_rot13;
pub use self::transposition::transposition;
pub use self::vigenere::vigenere;
pub use self::xor::xor;


// wiki: https://en.wikipedia.org/wiki/Rail_fence_cipher
pub fn rail_fence_encrypt(plain_text: &str, key: usize) -> String {
    let mut cipher = vec![Vec::new(); key];

    for (c, i) in plain_text.chars().zip(zigzag(key)) {
        cipher[i].push(c);
    }

    return cipher.iter().flatten().collect::<String>();
}

pub fn rail_fence_decrypt(cipher: &str, key: usize) -> String {
    let mut indices: Vec<_> = zigzag(key).zip(1..).take(cipher.len()).collect();
    indices.sort();

    let mut cipher_text: Vec<_> = cipher
        .chars()
        .zip(indices)
        .map(|(c, (_, i))| (i, c))
        .collect();

    cipher_text.sort();
    return cipher_text.iter().map(|(_, c)| c).collect();
}

fn zigzag(n: usize) -> impl Iterator<Item = usize> {
    (0..n - 1).chain((1..n).rev()).cycle()
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn rails_basic() {
        assert_eq!(rail_fence_encrypt("attack at once", 2), "atc toctaka ne");
        assert_eq!(rail_fence_decrypt("atc toctaka ne", 2), "attack at once");

        assert_eq!(rail_fence_encrypt("rust is cool", 3), "r cuti olsso");
        assert_eq!(rail_fence_decrypt("r cuti olsso", 3), "rust is cool");
    }
}


pub fn rot13(text: &str) -> String {
    let to_enc = text.to_uppercase();
    to_enc
        .chars()
        .map(|c| match c {
            'A'..='M' => ((c as u8) + 13) as char,
            'N'..='Z' => ((c as u8) - 13) as char,
            _ => c,
        })
        .collect()
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_single_letter() {
        assert_eq!("N", rot13("A"));
    }

    #[test]
    fn test_bunch_of_letters() {
        assert_eq!("NOP", rot13("ABC"));
    }

    #[test]
    fn test_non_ascii() {
        assert_eq!("ðŸ˜€NO", rot13("ðŸ˜€AB"));
    }

    #[test]
    fn test_twice() {
        assert_eq!("ABCD", rot13(&rot13("ABCD")));
    }
}


macro_rules! quarter_round {
    ($v1:expr,$v2:expr,$v3:expr,$v4:expr) => {
        $v2 ^= ($v1.wrapping_add($v4).rotate_left(7));
        $v3 ^= ($v2.wrapping_add($v1).rotate_left(9));
        $v4 ^= ($v3.wrapping_add($v2).rotate_left(13));
        $v1 ^= ($v4.wrapping_add($v3).rotate_left(18));
    };
}

/// This is a `Salsa20` implementation based on <https://en.wikipedia.org/wiki/Salsa20>\
/// `Salsa20` is a stream cipher developed by Daniel J. Bernstein.\
/// To use it, the `salsa20` function should be called with appropriate parameters and the
/// output of the function should be XORed with plain text.
///
/// `salsa20` function takes as input an array of 16 32-bit integers (512 bits)
/// of which 128 bits is the constant 'expand 32-byte k', 256 bits is the key,
/// and 128 bits are nonce and counter. It is up to the user to determine how
/// many bits each of nonce and counter take, but a default of 64 bits each
/// seems to be a sane choice.
///
/// The 16 input numbers can be thought of as the elements of a 4x4 matrix like
/// the one bellow, on which we do the main operations of the cipher.
///
/// ```text
/// +----+----+----+----+
/// | 00 | 01 | 02 | 03 |
/// +----+----+----+----+
/// | 04 | 05 | 06 | 07 |
/// +----+----+----+----+
/// | 08 | 09 | 10 | 11 |
/// +----+----+----+----+
/// | 12 | 13 | 14 | 15 |
/// +----+----+----+----+
/// ```
///
/// As per the diagram bellow, `input[0, 5, 10, 15]` are the constants mentioned
/// above, `input[1, 2, 3, 4, 11, 12, 13, 14]` is filled with the key, and
/// `input[6, 7, 8, 9]` should be filled with nonce and counter values. The output
/// of the function is stored in `output` variable and can be XORed with the
/// plain text to produce the cipher text.
///
/// ```text
/// +------+------+------+------+
/// |      |      |      |      |
/// | C[0] | key1 | key2 | key3 |
/// |      |      |      |      |
/// +------+------+------+------+
/// |      |      |      |      |
/// | key4 | C[1] | no1  | no2  |
/// |      |      |      |      |
/// +------+------+------+------+
/// |      |      |      |      |
/// | ctr1 | ctr2 | C[2] | key5 |
/// |      |      |      |      |
/// +------+------+------+------+
/// |      |      |      |      |
/// | key6 | key7 | key8 | C[3] |
/// |      |      |      |      |
/// +------+------+------+------+
/// ```
pub fn salsa20(input: &[u32; 16], output: &mut [u32; 16]) {
    output.copy_from_slice(&input[..]);
    for _ in 0..10 {
        // Odd round
        quarter_round!(output[0], output[4], output[8], output[12]); // column 1
        quarter_round!(output[5], output[9], output[13], output[1]); // column 2
        quarter_round!(output[10], output[14], output[2], output[6]); // column 3
        quarter_round!(output[15], output[3], output[7], output[11]); // column 4

        // Even round
        quarter_round!(output[0], output[1], output[2], output[3]); // row 1
        quarter_round!(output[5], output[6], output[7], output[4]); // row 2
        quarter_round!(output[10], output[11], output[8], output[9]); // row 3
        quarter_round!(output[15], output[12], output[13], output[14]); // row 4
    }
    for (a, &b) in output.iter_mut().zip(input.iter()) {
        *a = a.wrapping_add(b);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fmt::Write;

    const C: [u32; 4] = [0x65787061, 0x6e642033, 0x322d6279, 0x7465206b];

    fn output_hex(inp: &[u32; 16]) -> String {
        let mut res = String::new();
        res.reserve(512 / 4);
        for &x in inp {
            write!(&mut res, "{x:08x}").unwrap();
        }
        res
    }
    #[test]
    // test vector 1
    fn basic_tv1() {
        let mut inp = [0u32; 16];
        let mut out = [0u32; 16];
        inp[0] = C[0];
        inp[1] = 0x01020304; // 1, 2, 3, 4
        inp[2] = 0x05060708; // 5, 6, 7, 8, ...
        inp[3] = 0x090a0b0c;
        inp[4] = 0x0d0e0f10;
        inp[5] = C[1];
        inp[6] = 0x65666768; // 101, 102, 103, 104
        inp[7] = 0x696a6b6c; // 105, 106, 107, 108, ...
        inp[8] = 0x6d6e6f70;
        inp[9] = 0x71727374;
        inp[10] = C[2];
        inp[11] = 0xc9cacbcc; // 201, 202, 203, 204
        inp[12] = 0xcdcecfd0; // 205, 206, 207, 208, ...
        inp[13] = 0xd1d2d3d4;
        inp[14] = 0xd5d6d7d8;
        inp[15] = C[3];
        salsa20(&inp, &mut out);
        // Checked with wikipedia implementation, does not agree with
        // "https://cr.yp.to/snuffle/spec.pdf"
        assert_eq!(
            output_hex(&out),
            concat!(
                "de1d6f8d91dbf69d0db4b70c8b4320d236694432896d98b05aa7b76d5738ca13",
                "04e5a170c8e479af1542ed2f30f26ba57da20203cfe955c66f4cc7a06dd34359"
            )
        );
    }
}


/*!
 * SHA-2 256 bit implementation
 * This implementation is based on RFC6234
 * Keep in mind that the amount of data (in bits) processed should always be an
 * integer multiple of 8
 */

// The constants are tested to make sure they are correct
pub const H0: [u32; 8] = [
    0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
];

pub const K: [u32; 64] = [
    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
];

// The following functions are implemented according to page 10 of RFC6234
#[inline]
fn ch(x: u32, y: u32, z: u32) -> u32 {
    (x & y) ^ ((!x) & z)
}

#[inline]
fn maj(x: u32, y: u32, z: u32) -> u32 {
    (x & y) ^ (x & z) ^ (y & z)
}

#[inline]
fn bsig0(x: u32) -> u32 {
    x.rotate_right(2) ^ x.rotate_right(13) ^ x.rotate_right(22)
}

#[inline]
fn bsig1(x: u32) -> u32 {
    x.rotate_right(6) ^ x.rotate_right(11) ^ x.rotate_right(25)
}

#[inline]
fn ssig0(x: u32) -> u32 {
    x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3)
}

#[inline]
fn ssig1(x: u32) -> u32 {
    x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
}

pub struct SHA256 {
    /// The current block to be processed, 512 bits long
    buffer: [u32; 16],
    /// Length (bits) of the message, should always be a multiple of 8
    length: u64,
    /// The current hash value. Note: this value is invalid unless `finalize`
    /// is called
    pub h: [u32; 8],
    /// Message schedule
    w: [u32; 64],
    pub finalized: bool,
    // Temporary values:
    round: [u32; 8],
}

fn process_block(h: &mut [u32; 8], w: &mut [u32; 64], round: &mut [u32; 8], buf: &[u32; 16]) {
    // Prepare the message schedule:
    w[..buf.len()].copy_from_slice(&buf[..]);
    for i in buf.len()..w.len() {
        w[i] = ssig1(w[i - 2])
            .wrapping_add(w[i - 7])
            .wrapping_add(ssig0(w[i - 15]))
            .wrapping_add(w[i - 16]);
    }
    round.copy_from_slice(h);
    for i in 0..w.len() {
        let t1 = round[7]
            .wrapping_add(bsig1(round[4]))
            .wrapping_add(ch(round[4], round[5], round[6]))
            .wrapping_add(K[i])
            .wrapping_add(w[i]);
        let t2 = bsig0(round[0]).wrapping_add(maj(round[0], round[1], round[2]));
        round[7] = round[6];
        round[6] = round[5];
        round[5] = round[4];
        round[4] = round[3].wrapping_add(t1);
        round[3] = round[2];
        round[2] = round[1];
        round[1] = round[0];
        round[0] = t1.wrapping_add(t2);
    }
    for i in 0..h.len() {
        h[i] = h[i].wrapping_add(round[i]);
    }
}

impl SHA256 {
    pub fn new_default() -> Self {
        SHA256 {
            buffer: [0u32; 16],
            length: 0,
            h: H0,
            w: [0u32; 64],
            round: [0u32; 8],
            finalized: false,
        }
    }
    /// Note: buffer should be empty before calling this!
    pub fn process_block(&mut self, buf: &[u32; 16]) {
        process_block(&mut self.h, &mut self.w, &mut self.round, buf);
        self.length += 512;
    }

    pub fn update(&mut self, data: &[u8]) {
        if data.is_empty() {
            return;
        }
        let offset = (((32 - (self.length & 31)) & 31) >> 3) as usize;
        let mut buf_ind = ((self.length & 511) >> 5) as usize;
        for (i, &byte) in data.iter().enumerate().take(offset) {
            self.buffer[buf_ind] ^= (byte as u32) << ((offset - i - 1) << 3);
        }
        self.length += (data.len() as u64) << 3;
        if offset > data.len() {
            return;
        }
        if offset > 0 {
            buf_ind += 1;
        }
        if data.len() > 3 {
            for i in (offset..(data.len() - 3)).step_by(4) {
                if buf_ind & 16 == 16 {
                    process_block(&mut self.h, &mut self.w, &mut self.round, &self.buffer);
                    buf_ind = 0;
                }
                self.buffer[buf_ind] = ((data[i] as u32) << 24)
                    ^ ((data[i + 1] as u32) << 16)
                    ^ ((data[i + 2] as u32) << 8)
                    ^ data[i + 3] as u32;
                buf_ind += 1;
            }
        }
        if buf_ind & 16 == 16 {
            process_block(&mut self.h, &mut self.w, &mut self.round, &self.buffer);
            buf_ind = 0;
        }
        self.buffer[buf_ind] = 0;
        let rem_ind = offset + ((data.len() - offset) & !0b11);
        for (i, &byte) in data[rem_ind..].iter().enumerate() {
            self.buffer[buf_ind] ^= (byte as u32) << ((3 - i) << 3);
        }
    }

    pub fn get_hash(&mut self) -> [u8; 32] {
        // we should first add a `1` bit to the end of the buffer, then we will
        // add enough 0s so that the length becomes (512k + 448). After that we
        // will append the binary representation of length to the data
        if !self.finalized {
            self.finalized = true;
            let clen = (self.length + 8) & 511;
            let num_0 = match clen.cmp(&448) {
                std::cmp::Ordering::Greater => (448 + 512 - clen) >> 3,
                _ => (448 - clen) >> 3,
            };
            let mut padding: Vec<u8> = vec![0_u8; (num_0 + 9) as usize];
            let len = padding.len();
            padding[0] = 0x80;
            padding[len - 8] = (self.length >> 56) as u8;
            padding[len - 7] = (self.length >> 48) as u8;
            padding[len - 6] = (self.length >> 40) as u8;
            padding[len - 5] = (self.length >> 32) as u8;
            padding[len - 4] = (self.length >> 24) as u8;
            padding[len - 3] = (self.length >> 16) as u8;
            padding[len - 2] = (self.length >> 8) as u8;
            padding[len - 1] = self.length as u8;
            self.update(&padding);
        }
        assert_eq!(self.length & 511, 0);
        let mut result = [0u8; 32];
        for i in (0..32).step_by(4) {
            result[i] = (self.h[i >> 2] >> 24) as u8;
            result[i + 1] = (self.h[i >> 2] >> 16) as u8;
            result[i + 2] = (self.h[i >> 2] >> 8) as u8;
            result[i + 3] = self.h[i >> 2] as u8;
        }
        result
    }
}

impl super::Hasher<32> for SHA256 {
    fn new_default() -> Self {
        SHA256::new_default()
    }

    fn update(&mut self, data: &[u8]) {
        self.update(data);
    }

    fn get_hash(&mut self) -> [u8; 32] {
        self.get_hash()
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;
    use crate::math::LinearSieve;
    use std::fmt::Write;

    // Let's keep this utility function
    pub fn get_hash_string(hash: &[u8; 32]) -> String {
        let mut result = String::new();
        result.reserve(64);
        for &ch in hash {
            write!(&mut result, "{ch:02x}").unwrap();
        }
        result
    }

    #[test]
    fn test_constants() {
        let mut ls = LinearSieve::new();
        ls.prepare(311).unwrap();
        assert_eq!(64, ls.primes.len());
        assert_eq!(311, ls.primes[63]);

        let float_len = 52;
        let constant_len = 32;
        for (pos, &k) in K.iter().enumerate() {
            let a: f64 = ls.primes[pos] as f64;
            let bits = a.cbrt().to_bits();
            let exp = bits >> float_len; // The sign bit is already 0
                                         //(exp - 1023) can be bigger than 0, we must include more bits.
            let k_ref = ((bits & ((1_u64 << float_len) - 1))
                >> (float_len - constant_len + 1023 - exp)) as u32;
            assert_eq!(k, k_ref);
        }

        for (pos, &h) in H0.iter().enumerate() {
            let a: f64 = ls.primes[pos] as f64;
            let bits = a.sqrt().to_bits();
            let exp = bits >> float_len;
            let h_ref = ((bits & ((1_u64 << float_len) - 1))
                >> (float_len - constant_len + 1023 - exp)) as u32;
            assert_eq!(h, h_ref);
        }
    }

    // To test the hashes, you can use the following command on linux:
    // echo -n 'STRING' | sha256sum
    // the `-n` is because by default, echo adds a `\n` to its output

    #[test]
    fn empty() {
        let mut res = SHA256::new_default();
        assert_eq!(
            res.get_hash(),
            [
                0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
                0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
                0x78, 0x52, 0xb8, 0x55
            ]
        );
    }

    #[test]
    fn ascii() {
        let mut res = SHA256::new_default();
        res.update(b"The quick brown fox jumps over the lazy dog");
        assert_eq!(
            res.get_hash(),
            [
                0xD7, 0xA8, 0xFB, 0xB3, 0x07, 0xD7, 0x80, 0x94, 0x69, 0xCA, 0x9A, 0xBC, 0xB0, 0x08,
                0x2E, 0x4F, 0x8D, 0x56, 0x51, 0xE4, 0x6D, 0x3C, 0xDB, 0x76, 0x2D, 0x02, 0xD0, 0xBF,
                0x37, 0xC9, 0xE5, 0x92
            ]
        )
    }

    #[test]
    fn ascii_avalanche() {
        let mut res = SHA256::new_default();
        res.update(b"The quick brown fox jumps over the lazy dog.");
        assert_eq!(
            res.get_hash(),
            [
                0xEF, 0x53, 0x7F, 0x25, 0xC8, 0x95, 0xBF, 0xA7, 0x82, 0x52, 0x65, 0x29, 0xA9, 0xB6,
                0x3D, 0x97, 0xAA, 0x63, 0x15, 0x64, 0xD5, 0xD7, 0x89, 0xC2, 0xB7, 0x65, 0x44, 0x8C,
                0x86, 0x35, 0xFB, 0x6C
            ]
        );
        // Test if finalization is not repeated twice
        assert_eq!(
            res.get_hash(),
            [
                0xEF, 0x53, 0x7F, 0x25, 0xC8, 0x95, 0xBF, 0xA7, 0x82, 0x52, 0x65, 0x29, 0xA9, 0xB6,
                0x3D, 0x97, 0xAA, 0x63, 0x15, 0x64, 0xD5, 0xD7, 0x89, 0xC2, 0xB7, 0x65, 0x44, 0x8C,
                0x86, 0x35, 0xFB, 0x6C
            ]
        )
    }
    #[test]
    fn long_ascii() {
        let mut res = SHA256::new_default();
        let val = b"The quick brown fox jumps over the lazy dog.";
        for _ in 0..1000 {
            res.update(val);
        }
        let hash = res.get_hash();
        assert_eq!(
            &get_hash_string(&hash),
            "c264fca077807d391df72fadf39dd63be21f1823f65ca530c9637760eabfc18c"
        );
        let mut res = SHA256::new_default();
        let val = b"a";
        for _ in 0..999 {
            res.update(val);
        }
        let hash = res.get_hash();
        assert_eq!(
            &get_hash_string(&hash),
            "d9fe27f3d807a7c46467325f7189495e82b099ce2e14c5b16cc76697fa909f81"
        )
    }
    #[test]
    fn short_ascii() {
        let mut res = SHA256::new_default();
        let val = b"a";
        res.update(val);
        let hash = res.get_hash();
        assert_eq!(
            &get_hash_string(&hash),
            "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
        );
    }
}


/// Size of the state array in bits
const B: usize = 1600;

const W: usize = B / 25;
const L: usize = W.ilog2() as usize;

const U8BITS: usize = u8::BITS as usize;

// Macro for looping through the whole state array
macro_rules! iterate {
    ( $x:ident, $y:ident, $z:ident => $b:block ) => {
        for $y in 0..5 {
            for $x in 0..5 {
                for $z in 0..W {
                    $b
                }
            }
        }
    };
}

/// A function that produces a padding string such that the length of the padding + the length of
/// the string to be padded (2nd parameter) is divisible by the 1st parameter
type PadFn = fn(isize, isize) -> Vec<bool>;
type SpongeFn = fn(&[bool]) -> [bool; B];

type State = [[[bool; W]; 5]; 5];

fn state_new() -> State {
    [[[false; W]; 5]; 5]
}

fn state_fill(dest: &mut State, bits: &[bool]) {
    let mut i = 0usize;

    iterate!(x, y, z => {
        if i >= bits.len() { return; }
        dest[x][y][z] = bits[i];
        i += 1;
    });
}

fn state_copy(dest: &mut State, src: &State) {
    iterate!(x, y, z => {
        dest[x][y][z] = src[x][y][z];
    });
}

fn state_dump(state: &State) -> [bool; B] {
    let mut bits = [false; B];

    let mut i = 0usize;

    iterate!(x, y, z => {
        bits[i] = state[x][y][z];
        i += 1;
    });

    bits
}

/// XORs the state with the parities of two columns in the state array
fn theta(state: &mut State) {
    let mut c = [[false; W]; 5];
    let mut d = [[false; W]; 5];

    // Assign values of C[x,z]
    for x in 0..5 {
        for z in 0..W {
            c[x][z] = state[x][0][z];

            for y in 1..5 {
                c[x][z] ^= state[x][y][z];
            }
        }
    }

    // Assign values of D[x,z]
    for x in 0..5 {
        for z in 0..W {
            let x1 = (x as isize - 1).rem_euclid(5) as usize;
            let z2 = (z as isize - 1).rem_euclid(W as isize) as usize;

            d[x][z] = c[x1][z] ^ c[(x + 1) % 5][z2];
        }
    }

    // Xor values of D[x,z] into our state array
    iterate!(x, y, z => {
        state[x][y][z] ^= d[x][z];
    });
}

/// Rotates each lane by an offset depending of the x and y indeces
fn rho(state: &mut State) {
    let mut new_state = state_new();

    for z in 0..W {
        new_state[0][0][z] = state[0][0][z];
    }

    let mut x = 1;
    let mut y = 0;

    for t in 0..=23isize {
        for z in 0..W {
            let z_offset: isize = ((t + 1) * (t + 2)) / 2;
            let new_z = (z as isize - z_offset).rem_euclid(W as isize) as usize;

            new_state[x][y][z] = state[x][y][new_z];
        }

        let old_y = y;
        y = ((2 * x) + (3 * y)) % 5;
        x = old_y;
    }

    state_copy(state, &new_state);
}

/// Rearrange the positions of the lanes of the state array
fn pi(state: &mut State) {
    let mut new_state = state_new();

    iterate!(x, y, z => {
        new_state[x][y][z] = state[(x + (3 * y)) % 5][x][z];
    });

    state_copy(state, &new_state);
}

fn chi(state: &mut State) {
    let mut new_state = state_new();

    iterate!(x, y, z => {
        new_state[x][y][z] = state[x][y][z] ^ ((state[(x + 1) % 5][y][z] ^ true) & state[(x + 2) % 5][y][z]);
    });

    state_copy(state, &new_state);
}

/// Calculates the round constant depending on what the round number is
fn rc(t: u8) -> bool {
    let mut b1: u16;
    let mut b2: u16;
    let mut r: u16 = 0x80; // tread r as an array of bits

    //if t % 0xFF == 0 { return true; }

    for _i in 0..(t % 255) {
        b1 = r >> 8;
        b2 = r & 1;
        r |= (b1 ^ b2) << 8;

        b1 = (r >> 4) & 1;
        r &= 0x1EF; // clear r[4]
        r |= (b1 ^ b2) << 4;

        b1 = (r >> 3) & 1;
        r &= 0x1F7; // clear r[3]
        r |= (b1 ^ b2) << 3;

        b1 = (r >> 2) & 1;
        r &= 0x1FB; // clear r[2]
        r |= (b1 ^ b2) << 2;

        r >>= 1;
    }

    (r >> 7) != 0
}

/// Applies the round constant to the first lane of the state array
fn iota(state: &mut State, i_r: u8) {
    let mut rc_arr = [false; W];

    for j in 0..=L {
        rc_arr[(1 << j) - 1] = rc((j as u8) + (7 * i_r));
    }

    for (z, bit) in rc_arr.iter().enumerate() {
        state[0][0][z] ^= *bit;
    }
}

fn rnd(state: &mut State, i_r: u8) {
    theta(state);
    rho(state);
    pi(state);
    chi(state);
    iota(state, i_r);
}

fn keccak_f(bits: &[bool]) -> [bool; B] {
    let n_r = 12 + (2 * L);

    let mut state = state_new();
    state_fill(&mut state, bits);

    for i_r in 0..n_r {
        rnd(&mut state, i_r as u8);
    }

    state_dump(&state)
}

fn pad101(x: isize, m: isize) -> Vec<bool> {
    let mut j = -m - 2;

    while j < 0 {
        j += x;
    }

    j %= x;

    let mut ret = vec![false; (j as usize) + 2];
    *ret.first_mut().unwrap() = true;
    *ret.last_mut().unwrap() = true;

    ret
}

/// Sponge construction is a method of compression needing 1) a function on fixed-length bit
/// strings( here we use keccak_f), 2) a padding function (pad10*1), and 3) a rate. The input and
/// output of this method can be arbitrarily long
fn sponge(f: SpongeFn, pad: PadFn, r: usize, n: &[bool], d: usize) -> Vec<bool> {
    let mut p = Vec::from(n);
    p.append(&mut pad(r as isize, n.len() as isize));

    assert!(r < B);

    let mut s = [false; B];
    for chunk in p.chunks(r) {
        for (s_i, c_i) in s.iter_mut().zip(chunk) {
            *s_i ^= c_i;
        }

        s = f(&s);
    }

    let mut z = Vec::<bool>::new();
    while z.len() < d {
        z.extend(&s);

        s = f(&s);
    }

    z.truncate(d);
    z
}

fn keccak(c: usize, n: &[bool], d: usize) -> Vec<bool> {
    sponge(keccak_f, pad101, B - c, n, d)
}

fn h2b(h: &[u8], n: usize) -> Vec<bool> {
    let mut bits = Vec::with_capacity(h.len() * U8BITS);

    for byte in h {
        for i in 0..u8::BITS {
            let mask: u8 = 1 << i;

            bits.push((byte & mask) != 0);
        }
    }

    assert!(bits.len() == h.len() * U8BITS);

    bits.truncate(n);
    bits
}

fn b2h(s: &[bool]) -> Vec<u8> {
    let m = if s.len() % U8BITS != 0 {
        (s.len() / 8) + 1
    } else {
        s.len() / 8
    };
    let mut bytes = vec![0u8; m];

    for (i, bit) in s.iter().enumerate() {
        let byte_index = i / U8BITS;
        let mask = (*bit as u8) << (i % U8BITS);

        bytes[byte_index] |= mask;
    }

    bytes
}
/// Macro to implement all sha3 hash functions as they only differ in digest size
macro_rules! sha3 {
    ($name:ident, $n:literal) => {
        pub fn $name(m: &[u8]) -> [u8; ($n / U8BITS)] {
            let mut temp = h2b(m, m.len() * U8BITS);
            temp.append(&mut vec![false, true]);

            temp = keccak($n * 2, &temp, $n);

            let mut ret = [0u8; ($n / U8BITS)];

            let temp = b2h(&temp);
            assert!(temp.len() == $n / U8BITS);

            for (i, byte) in temp.iter().enumerate() {
                ret[i] = *byte;
            }

            ret
        }
    };
}

sha3!(sha3_224, 224);
sha3!(sha3_256, 256);
sha3!(sha3_384, 384);
sha3!(sha3_512, 512);

#[cfg(test)]
mod tests {
    use super::*;

    macro_rules! digest_test {
        ($fname:ident, $hash:ident, $size:literal, $message:expr, $expected:expr) => {
            #[test]
            fn $fname() {
                let digest = $hash(&$message);

                let expected: [u8; $size / U8BITS] = $expected;

                assert_eq!(digest, expected);
            }
        };
    }

    digest_test!(
        sha3_224_0,
        sha3_224,
        224,
        [0; 0],
        [
            0x6b, 0x4e, 0x03, 0x42, 0x36, 0x67, 0xdb, 0xb7, 0x3b, 0x6e, 0x15, 0x45, 0x4f, 0x0e,
            0xb1, 0xab, 0xd4, 0x59, 0x7f, 0x9a, 0x1b, 0x07, 0x8e, 0x3f, 0x5b, 0x5a, 0x6b, 0xc7,
        ]
    );

    digest_test!(
        sha3_224_8,
        sha3_224,
        224,
        [1u8],
        [
            0x48, 0x82, 0x86, 0xd9, 0xd3, 0x27, 0x16, 0xe5, 0x88, 0x1e, 0xa1, 0xee, 0x51, 0xf3,
            0x6d, 0x36, 0x60, 0xd7, 0x0f, 0x0d, 0xb0, 0x3b, 0x3f, 0x61, 0x2c, 0xe9, 0xed, 0xa4,
        ]
    );

    // Done on large input to verify sponge function is working properly
    digest_test!(
        sha3_224_2312,
        sha3_224,
        224,
        [
            0x31, 0xc8, 0x2d, 0x71, 0x78, 0x5b, 0x7c, 0xa6, 0xb6, 0x51, 0xcb, 0x6c, 0x8c, 0x9a,
            0xd5, 0xe2, 0xac, 0xeb, 0x0b, 0x06, 0x33, 0xc0, 0x88, 0xd3, 0x3a, 0xa2, 0x47, 0xad,
            0xa7, 0xa5, 0x94, 0xff, 0x49, 0x36, 0xc0, 0x23, 0x25, 0x13, 0x19, 0x82, 0x0a, 0x9b,
            0x19, 0xfc, 0x6c, 0x48, 0xde, 0x8a, 0x6f, 0x7a, 0xda, 0x21, 0x41, 0x76, 0xcc, 0xda,
            0xad, 0xae, 0xef, 0x51, 0xed, 0x43, 0x71, 0x4a, 0xc0, 0xc8, 0x26, 0x9b, 0xbd, 0x49,
            0x7e, 0x46, 0xe7, 0x8b, 0xb5, 0xe5, 0x81, 0x96, 0x49, 0x4b, 0x24, 0x71, 0xb1, 0x68,
            0x0e, 0x2d, 0x4c, 0x6d, 0xbd, 0x24, 0x98, 0x31, 0xbd, 0x83, 0xa4, 0xd3, 0xbe, 0x06,
            0xc8, 0xa2, 0xe9, 0x03, 0x93, 0x39, 0x74, 0xaa, 0x05, 0xee, 0x74, 0x8b, 0xfe, 0x6e,
            0xf3, 0x59, 0xf7, 0xa1, 0x43, 0xed, 0xf0, 0xd4, 0x91, 0x8d, 0xa9, 0x16, 0xbd, 0x6f,
            0x15, 0xe2, 0x6a, 0x79, 0x0c, 0xff, 0x51, 0x4b, 0x40, 0xa5, 0xda, 0x7f, 0x72, 0xe1,
            0xed, 0x2f, 0xe6, 0x3a, 0x05, 0xb8, 0x14, 0x95, 0x87, 0xbe, 0xa0, 0x56, 0x53, 0x71,
            0x8c, 0xc8, 0x98, 0x0e, 0xad, 0xbf, 0xec, 0xa8, 0x5b, 0x7c, 0x9c, 0x28, 0x6d, 0xd0,
            0x40, 0x93, 0x65, 0x85, 0x93, 0x8b, 0xe7, 0xf9, 0x82, 0x19, 0x70, 0x0c, 0x83, 0xa9,
            0x44, 0x3c, 0x28, 0x56, 0xa8, 0x0f, 0xf4, 0x68, 0x52, 0xb2, 0x6d, 0x1b, 0x1e, 0xdf,
            0x72, 0xa3, 0x02, 0x03, 0xcf, 0x6c, 0x44, 0xa1, 0x0f, 0xa6, 0xea, 0xf1, 0x92, 0x01,
            0x73, 0xce, 0xdf, 0xb5, 0xc4, 0xcf, 0x3a, 0xc6, 0x65, 0xb3, 0x7a, 0x86, 0xed, 0x02,
            0x15, 0x5b, 0xbb, 0xf1, 0x7d, 0xc2, 0xe7, 0x86, 0xaf, 0x94, 0x78, 0xfe, 0x08, 0x89,
            0xd8, 0x6c, 0x5b, 0xfa, 0x85, 0xa2, 0x42, 0xeb, 0x08, 0x54, 0xb1, 0x48, 0x2b, 0x7b,
            0xd1, 0x6f, 0x67, 0xf8, 0x0b, 0xef, 0x9c, 0x7a, 0x62, 0x8f, 0x05, 0xa1, 0x07, 0x93,
            0x6a, 0x64, 0x27, 0x3a, 0x97, 0xb0, 0x08, 0x8b, 0x0e, 0x51, 0x54, 0x51, 0xf9, 0x16,
            0xb5, 0x65, 0x62, 0x30, 0xa1, 0x2b, 0xa6, 0xdc, 0x78
        ],
        [
            0xaa, 0xb2, 0x3c, 0x9e, 0x7f, 0xb9, 0xd7, 0xda, 0xce, 0xfd, 0xfd, 0x0b, 0x1a, 0xe8,
            0x5a, 0xb1, 0x37, 0x4a, 0xbf, 0xf7, 0xc4, 0xe3, 0xf7, 0x55, 0x6e, 0xca, 0xe4, 0x12
        ]
    );

    digest_test!(
        sha3_256_0,
        sha3_256,
        256,
        [0; 0],
        [
            0xa7, 0xff, 0xc6, 0xf8, 0xbf, 0x1e, 0xd7, 0x66, 0x51, 0xc1, 0x47, 0x56, 0xa0, 0x61,
            0xd6, 0x62, 0xf5, 0x80, 0xff, 0x4d, 0xe4, 0x3b, 0x49, 0xfa, 0x82, 0xd8, 0x0a, 0x4b,
            0x80, 0xf8, 0x43, 0x4a,
        ]
    );

    digest_test!(
        sha3_256_8,
        sha3_256,
        256,
        [0xe9u8],
        [
            0xf0, 0xd0, 0x4d, 0xd1, 0xe6, 0xcf, 0xc2, 0x9a, 0x44, 0x60, 0xd5, 0x21, 0x79, 0x68,
            0x52, 0xf2, 0x5d, 0x9e, 0xf8, 0xd2, 0x8b, 0x44, 0xee, 0x91, 0xff, 0x5b, 0x75, 0x9d,
            0x72, 0xc1, 0xe6, 0xd6,
        ]
    );

    digest_test!(
        sha3_256_2184,
        sha3_256,
        256,
        [
            0xb1, 0xca, 0xa3, 0x96, 0x77, 0x1a, 0x09, 0xa1, 0xdb, 0x9b, 0xc2, 0x05, 0x43, 0xe9,
            0x88, 0xe3, 0x59, 0xd4, 0x7c, 0x2a, 0x61, 0x64, 0x17, 0xbb, 0xca, 0x1b, 0x62, 0xcb,
            0x02, 0x79, 0x6a, 0x88, 0x8f, 0xc6, 0xee, 0xff, 0x5c, 0x0b, 0x5c, 0x3d, 0x50, 0x62,
            0xfc, 0xb4, 0x25, 0x6f, 0x6a, 0xe1, 0x78, 0x2f, 0x49, 0x2c, 0x1c, 0xf0, 0x36, 0x10,
            0xb4, 0xa1, 0xfb, 0x7b, 0x81, 0x4c, 0x05, 0x78, 0x78, 0xe1, 0x19, 0x0b, 0x98, 0x35,
            0x42, 0x5c, 0x7a, 0x4a, 0x0e, 0x18, 0x2a, 0xd1, 0xf9, 0x15, 0x35, 0xed, 0x2a, 0x35,
            0x03, 0x3a, 0x5d, 0x8c, 0x67, 0x0e, 0x21, 0xc5, 0x75, 0xff, 0x43, 0xc1, 0x94, 0xa5,
            0x8a, 0x82, 0xd4, 0xa1, 0xa4, 0x48, 0x81, 0xdd, 0x61, 0xf9, 0xf8, 0x16, 0x1f, 0xc6,
            0xb9, 0x98, 0x86, 0x0c, 0xbe, 0x49, 0x75, 0x78, 0x0b, 0xe9, 0x3b, 0x6f, 0x87, 0x98,
            0x0b, 0xad, 0x0a, 0x99, 0xaa, 0x2c, 0xb7, 0x55, 0x6b, 0x47, 0x8c, 0xa3, 0x5d, 0x1f,
            0x37, 0x46, 0xc3, 0x3e, 0x2b, 0xb7, 0xc4, 0x7a, 0xf4, 0x26, 0x64, 0x1c, 0xc7, 0xbb,
            0xb3, 0x42, 0x5e, 0x21, 0x44, 0x82, 0x03, 0x45, 0xe1, 0xd0, 0xea, 0x5b, 0x7d, 0xa2,
            0xc3, 0x23, 0x6a, 0x52, 0x90, 0x6a, 0xcd, 0xc3, 0xb4, 0xd3, 0x4e, 0x47, 0x4d, 0xd7,
            0x14, 0xc0, 0xc4, 0x0b, 0xf0, 0x06, 0xa3, 0xa1, 0xd8, 0x89, 0xa6, 0x32, 0x98, 0x38,
            0x14, 0xbb, 0xc4, 0xa1, 0x4f, 0xe5, 0xf1, 0x59, 0xaa, 0x89, 0x24, 0x9e, 0x7c, 0x73,
            0x8b, 0x3b, 0x73, 0x66, 0x6b, 0xac, 0x2a, 0x61, 0x5a, 0x83, 0xfd, 0x21, 0xae, 0x0a,
            0x1c, 0xe7, 0x35, 0x2a, 0xde, 0x7b, 0x27, 0x8b, 0x58, 0x71, 0x58, 0xfd, 0x2f, 0xab,
            0xb2, 0x17, 0xaa, 0x1f, 0xe3, 0x1d, 0x0b, 0xda, 0x53, 0x27, 0x20, 0x45, 0x59, 0x80,
            0x15, 0xa8, 0xae, 0x4d, 0x8c, 0xec, 0x22, 0x6f, 0xef, 0xa5, 0x8d, 0xaa, 0x05, 0x50,
            0x09, 0x06, 0xc4, 0xd8, 0x5e, 0x75, 0x67
        ],
        [
            0xcb, 0x56, 0x48, 0xa1, 0xd6, 0x1c, 0x6c, 0x5b, 0xda, 0xcd, 0x96, 0xf8, 0x1c, 0x95,
            0x91, 0xde, 0xbc, 0x39, 0x50, 0xdc, 0xf6, 0x58, 0x14, 0x5b, 0x8d, 0x99, 0x65, 0x70,
            0xba, 0x88, 0x1a, 0x05
        ]
    );

    digest_test!(
        sha3_384_0,
        sha3_384,
        384,
        [0; 0],
        [
            0x0c, 0x63, 0xa7, 0x5b, 0x84, 0x5e, 0x4f, 0x7d, 0x01, 0x10, 0x7d, 0x85, 0x2e, 0x4c,
            0x24, 0x85, 0xc5, 0x1a, 0x50, 0xaa, 0xaa, 0x94, 0xfc, 0x61, 0x99, 0x5e, 0x71, 0xbb,
            0xee, 0x98, 0x3a, 0x2a, 0xc3, 0x71, 0x38, 0x31, 0x26, 0x4a, 0xdb, 0x47, 0xfb, 0x6b,
            0xd1, 0xe0, 0x58, 0xd5, 0xf0, 0x04,
        ]
    );

    digest_test!(
        sha3_384_8,
        sha3_384,
        384,
        [0x80u8],
        [
            0x75, 0x41, 0x38, 0x48, 0x52, 0xe1, 0x0f, 0xf1, 0x0d, 0x5f, 0xb6, 0xa7, 0x21, 0x3a,
            0x4a, 0x6c, 0x15, 0xcc, 0xc8, 0x6d, 0x8b, 0xc1, 0x06, 0x8a, 0xc0, 0x4f, 0x69, 0x27,
            0x71, 0x42, 0x94, 0x4f, 0x4e, 0xe5, 0x0d, 0x91, 0xfd, 0xc5, 0x65, 0x53, 0xdb, 0x06,
            0xb2, 0xf5, 0x03, 0x9c, 0x8a, 0xb7,
        ]
    );

    digest_test!(
        sha3_384_2512,
        sha3_384,
        384,
        [
            0x03, 0x5a, 0xdc, 0xb6, 0x39, 0xe5, 0xf2, 0x8b, 0xb5, 0xc8, 0x86, 0x58, 0xf4, 0x5c,
            0x1c, 0xe0, 0xbe, 0x16, 0xe7, 0xda, 0xfe, 0x08, 0x3b, 0x98, 0xd0, 0xab, 0x45, 0xe8,
            0xdc, 0xdb, 0xfa, 0x38, 0xe3, 0x23, 0x4d, 0xfd, 0x97, 0x3b, 0xa5, 0x55, 0xb0, 0xcf,
            0x8e, 0xea, 0x3c, 0x82, 0xae, 0x1a, 0x36, 0x33, 0xfc, 0x56, 0x5b, 0x7f, 0x2c, 0xc8,
            0x39, 0x87, 0x6d, 0x39, 0x89, 0xf3, 0x57, 0x31, 0xbe, 0x37, 0x1f, 0x60, 0xde, 0x14,
            0x0e, 0x3c, 0x91, 0x62, 0x31, 0xec, 0x78, 0x0e, 0x51, 0x65, 0xbf, 0x5f, 0x25, 0xd3,
            0xf6, 0x7d, 0xc7, 0x3a, 0x1c, 0x33, 0x65, 0x5d, 0xfd, 0xf4, 0x39, 0xdf, 0xbf, 0x1c,
            0xbb, 0xa8, 0xb7, 0x79, 0x15, 0x8a, 0x81, 0x0a, 0xd7, 0x24, 0x4f, 0x06, 0xec, 0x07,
            0x81, 0x20, 0xcd, 0x18, 0x76, 0x0a, 0xf4, 0x36, 0xa2, 0x38, 0x94, 0x1c, 0xe1, 0xe6,
            0x87, 0x88, 0x0b, 0x5c, 0x87, 0x9d, 0xc9, 0x71, 0xa2, 0x85, 0xa7, 0x4e, 0xe8, 0x5c,
            0x6a, 0x74, 0x67, 0x49, 0xa3, 0x01, 0x59, 0xee, 0x84, 0x2e, 0x9b, 0x03, 0xf3, 0x1d,
            0x61, 0x3d, 0xdd, 0xd2, 0x29, 0x75, 0xcd, 0x7f, 0xed, 0x06, 0xbd, 0x04, 0x9d, 0x77,
            0x2c, 0xb6, 0xcc, 0x5a, 0x70, 0x5f, 0xaa, 0x73, 0x4e, 0x87, 0x32, 0x1d, 0xc8, 0xf2,
            0xa4, 0xea, 0x36, 0x6a, 0x36, 0x8a, 0x98, 0xbf, 0x06, 0xee, 0x2b, 0x0b, 0x54, 0xac,
            0x3a, 0x3a, 0xee, 0xa6, 0x37, 0xca, 0xeb, 0xe7, 0x0a, 0xd0, 0x9c, 0xcd, 0xa9, 0x3c,
            0xc0, 0x6d, 0xe9, 0x5d, 0xf7, 0x33, 0x94, 0xa8, 0x7a, 0xc9, 0xbb, 0xb5, 0x08, 0x3a,
            0x4d, 0x8a, 0x24, 0x58, 0xe9, 0x1c, 0x7d, 0x5b, 0xf1, 0x13, 0xae, 0xca, 0xe0, 0xce,
            0x27, 0x9f, 0xdd, 0xa7, 0x6b, 0xa6, 0x90, 0x78, 0x7d, 0x26, 0x34, 0x5e, 0x94, 0xc3,
            0xed, 0xbc, 0x16, 0xa3, 0x5c, 0x83, 0xc4, 0xd0, 0x71, 0xb1, 0x32, 0xdd, 0x81, 0x18,
            0x7b, 0xcd, 0x99, 0x61, 0x32, 0x30, 0x11, 0x50, 0x9c, 0x8f, 0x64, 0x4a, 0x1c, 0x0a,
            0x3f, 0x14, 0xee, 0x40, 0xd7, 0xdd, 0x18, 0x6f, 0x80, 0x7f, 0x9e, 0xdc, 0x7c, 0x02,
            0xf6, 0x76, 0x10, 0x61, 0xbb, 0xb6, 0xdd, 0x91, 0xa6, 0xc9, 0x6e, 0xc0, 0xb9, 0xf1,
            0x0e, 0xdb, 0xbd, 0x29, 0xdc, 0x52
        ],
        [
            0x02, 0x53, 0x5d, 0x86, 0xcc, 0x75, 0x18, 0x48, 0x4a, 0x2a, 0x23, 0x8c, 0x92, 0x1b,
            0x73, 0x9b, 0x17, 0x04, 0xa5, 0x03, 0x70, 0xa2, 0x92, 0x4a, 0xbf, 0x39, 0x95, 0x8c,
            0x59, 0x76, 0xe6, 0x58, 0xdc, 0x5e, 0x87, 0x44, 0x00, 0x63, 0x11, 0x24, 0x59, 0xbd,
            0xdb, 0x40, 0x30, 0x8b, 0x1c, 0x70
        ]
    );

    digest_test!(
        sha3_512_0,
        sha3_512,
        512,
        [0u8; 0],
        [
            0xa6, 0x9f, 0x73, 0xcc, 0xa2, 0x3a, 0x9a, 0xc5, 0xc8, 0xb5, 0x67, 0xdc, 0x18, 0x5a,
            0x75, 0x6e, 0x97, 0xc9, 0x82, 0x16, 0x4f, 0xe2, 0x58, 0x59, 0xe0, 0xd1, 0xdc, 0xc1,
            0x47, 0x5c, 0x80, 0xa6, 0x15, 0xb2, 0x12, 0x3a, 0xf1, 0xf5, 0xf9, 0x4c, 0x11, 0xe3,
            0xe9, 0x40, 0x2c, 0x3a, 0xc5, 0x58, 0xf5, 0x00, 0x19, 0x9d, 0x95, 0xb6, 0xd3, 0xe3,
            0x01, 0x75, 0x85, 0x86, 0x28, 0x1d, 0xcd, 0x26,
        ]
    );

    digest_test!(
        sha3_512_8,
        sha3_512,
        512,
        [0xe5u8],
        [
            0x15, 0x02, 0x40, 0xba, 0xf9, 0x5f, 0xb3, 0x6f, 0x8c, 0xcb, 0x87, 0xa1, 0x9a, 0x41,
            0x76, 0x7e, 0x7a, 0xed, 0x95, 0x12, 0x50, 0x75, 0xa2, 0xb2, 0xdb, 0xba, 0x6e, 0x56,
            0x5e, 0x1c, 0xe8, 0x57, 0x5f, 0x2b, 0x04, 0x2b, 0x62, 0xe2, 0x9a, 0x04, 0xe9, 0x44,
            0x03, 0x14, 0xa8, 0x21, 0xc6, 0x22, 0x41, 0x82, 0x96, 0x4d, 0x8b, 0x55, 0x7b, 0x16,
            0xa4, 0x92, 0xb3, 0x80, 0x6f, 0x4c, 0x39, 0xc1
        ]
    );

    digest_test!(
        sha3_512_4080,
        sha3_512,
        512,
        [
            0x43, 0x02, 0x56, 0x15, 0x52, 0x1d, 0x66, 0xfe, 0x8e, 0xc3, 0xa3, 0xf8, 0xcc, 0xc5,
            0xab, 0xfa, 0xb8, 0x70, 0xa4, 0x62, 0xc6, 0xb3, 0xd1, 0x39, 0x6b, 0x84, 0x62, 0xb9,
            0x8c, 0x7f, 0x91, 0x0c, 0x37, 0xd0, 0xea, 0x57, 0x91, 0x54, 0xea, 0xf7, 0x0f, 0xfb,
            0xcc, 0x0b, 0xe9, 0x71, 0xa0, 0x32, 0xcc, 0xfd, 0x9d, 0x96, 0xd0, 0xa9, 0xb8, 0x29,
            0xa9, 0xa3, 0x76, 0x2e, 0x21, 0xe3, 0xfe, 0xfc, 0xc6, 0x0e, 0x72, 0xfe, 0xdf, 0x9a,
            0x7f, 0xff, 0xa5, 0x34, 0x33, 0xa4, 0xb0, 0x5e, 0x0f, 0x3a, 0xb0, 0x5d, 0x5e, 0xb2,
            0x5d, 0x52, 0xc5, 0xea, 0xb1, 0xa7, 0x1a, 0x2f, 0x54, 0xac, 0x79, 0xff, 0x58, 0x82,
            0x95, 0x13, 0x26, 0x39, 0x4d, 0x9d, 0xb8, 0x35, 0x80, 0xce, 0x09, 0xd6, 0x21, 0x9b,
            0xca, 0x58, 0x8e, 0xc1, 0x57, 0xf7, 0x1d, 0x06, 0xe9, 0x57, 0xf8, 0xc2, 0x0d, 0x24,
            0x2c, 0x9f, 0x55, 0xf5, 0xfc, 0x9d, 0x4d, 0x77, 0x7b, 0x59, 0xb0, 0xc7, 0x5a, 0x8e,
            0xdc, 0x1f, 0xfe, 0xdc, 0x84, 0xb5, 0xd5, 0xc8, 0xa5, 0xe0, 0xeb, 0x05, 0xbb, 0x7d,
            0xb8, 0xf2, 0x34, 0x91, 0x3d, 0x63, 0x25, 0x30, 0x4f, 0xa4, 0x3c, 0x9d, 0x32, 0xbb,
            0xf6, 0xb2, 0x69, 0xee, 0x11, 0x82, 0xcd, 0x85, 0x45, 0x3e, 0xdd, 0xd1, 0x2f, 0x55,
            0x55, 0x6d, 0x8e, 0xdf, 0x02, 0xc4, 0xb1, 0x3c, 0xd4, 0xd3, 0x30, 0xf8, 0x35, 0x31,
            0xdb, 0xf2, 0x99, 0x4c, 0xf0, 0xbe, 0x56, 0xf5, 0x91, 0x47, 0xb7, 0x1f, 0x74, 0xb9,
            0x4b, 0xe3, 0xdd, 0x9e, 0x83, 0xc8, 0xc9, 0x47, 0x7c, 0x42, 0x6c, 0x6d, 0x1a, 0x78,
            0xde, 0x18, 0x56, 0x4a, 0x12, 0xc0, 0xd9, 0x93, 0x07, 0xb2, 0xc9, 0xab, 0x42, 0xb6,
            0xe3, 0x31, 0x7b, 0xef, 0xca, 0x07, 0x97, 0x02, 0x9e, 0x9d, 0xd6, 0x7b, 0xd1, 0x73,
            0x4e, 0x6c, 0x36, 0xd9, 0x98, 0x56, 0x5b, 0xfa, 0xc9, 0x4d, 0x19, 0x18, 0xa3, 0x58,
            0x69, 0x19, 0x0d, 0x17, 0x79, 0x43, 0xc1, 0xa8, 0x00, 0x44, 0x45, 0xca, 0xce, 0x75,
            0x1c, 0x43, 0xa7, 0x5f, 0x3d, 0x80, 0x51, 0x7f, 0xc4, 0x7c, 0xec, 0x46, 0xe8, 0xe3,
            0x82, 0x64, 0x2d, 0x76, 0xdf, 0x46, 0xda, 0xb1, 0xa3, 0xdd, 0xae, 0xab, 0x95, 0xa2,
            0xcf, 0x3f, 0x3a, 0xd7, 0x03, 0x69, 0xa7, 0x0f, 0x22, 0xf2, 0x93, 0xf0, 0xcc, 0x50,
            0xb0, 0x38, 0x57, 0xc8, 0x3c, 0xfe, 0x0b, 0xd5, 0xd2, 0x3b, 0x92, 0xcd, 0x87, 0x88,
            0xaa, 0xc2, 0x32, 0x29, 0x1d, 0xa6, 0x0b, 0x4b, 0xf3, 0xb3, 0x78, 0x8a, 0xe6, 0x0a,
            0x23, 0xb6, 0x16, 0x9b, 0x50, 0xd7, 0xfe, 0x44, 0x6e, 0x6e, 0xa7, 0x3d, 0xeb, 0xfe,
            0x1b, 0xb3, 0x4d, 0xcb, 0x1d, 0xb3, 0x7f, 0xe2, 0x17, 0x4a, 0x68, 0x59, 0x54, 0xeb,
            0xc2, 0xd8, 0x6f, 0x10, 0x2a, 0x59, 0x0c, 0x24, 0x73, 0x2b, 0xc5, 0xa1, 0x40, 0x3d,
            0x68, 0x76, 0xd2, 0x99, 0x5f, 0xab, 0x1e, 0x2f, 0x6f, 0x47, 0x23, 0xd4, 0xa6, 0x72,
            0x7a, 0x8a, 0x8e, 0xd7, 0x2f, 0x02, 0xa7, 0x4c, 0xcf, 0x5f, 0x14, 0xb5, 0xc2, 0x3d,
            0x95, 0x25, 0xdb, 0xf2, 0xb5, 0x47, 0x2e, 0x13, 0x45, 0xfd, 0x22, 0x3b, 0x08, 0x46,
            0xc7, 0x07, 0xb0, 0x65, 0x69, 0x65, 0x09, 0x40, 0x65, 0x0f, 0x75, 0x06, 0x3b, 0x52,
            0x98, 0x14, 0xe5, 0x14, 0x54, 0x1a, 0x67, 0x15, 0xf8, 0x79, 0xa8, 0x75, 0xb4, 0xf0,
            0x80, 0x77, 0x51, 0x78, 0x12, 0x84, 0x1e, 0x6c, 0x5c, 0x73, 0x2e, 0xed, 0x0c, 0x07,
            0xc0, 0x85, 0x95, 0xb9, 0xff, 0x0a, 0x83, 0xb8, 0xec, 0xc6, 0x0b, 0x2f, 0x98, 0xd4,
            0xe7, 0xc6, 0x96, 0xcd, 0x61, 0x6b, 0xb0, 0xa5, 0xad, 0x52, 0xd9, 0xcf, 0x7b, 0x3a,
            0x63, 0xa8, 0xcd, 0xf3, 0x72, 0x12
        ],
        [
            0xe7, 0xba, 0x73, 0x40, 0x7a, 0xa4, 0x56, 0xae, 0xce, 0x21, 0x10, 0x77, 0xd9, 0x20,
            0x87, 0xd5, 0xcd, 0x28, 0x3e, 0x38, 0x68, 0xd2, 0x84, 0xe0, 0x7e, 0xd1, 0x24, 0xb2,
            0x7c, 0xbc, 0x66, 0x4a, 0x6a, 0x47, 0x5a, 0x8d, 0x7b, 0x4c, 0xf6, 0xa8, 0xa4, 0x92,
            0x7e, 0xe0, 0x59, 0xa2, 0x62, 0x6a, 0x4f, 0x98, 0x39, 0x23, 0x36, 0x01, 0x45, 0xb2,
            0x65, 0xeb, 0xfd, 0x4f, 0x5b, 0x3c, 0x44, 0xfd
        ]
    );
}


use std::num::Wrapping as W;

struct TeaContext {
    key0: u64,
    key1: u64,
}

impl TeaContext {
    pub fn new(key: &[u64; 2]) -> TeaContext {
        TeaContext {
            key0: key[0],
            key1: key[1],
        }
    }

    pub fn encrypt_block(&self, block: u64) -> u64 {
        let (mut b0, mut b1) = divide_u64(block);
        let (k0, k1) = divide_u64(self.key0);
        let (k2, k3) = divide_u64(self.key1);
        let mut sum = W(0u32);

        for _ in 0..32 {
            sum += W(0x9E3779B9);
            b0 += ((b1 << 4) + k0) ^ (b1 + sum) ^ ((b1 >> 5) + k1);
            b1 += ((b0 << 4) + k2) ^ (b0 + sum) ^ ((b0 >> 5) + k3);
        }

        ((b1.0 as u64) << 32) | b0.0 as u64
    }

    pub fn decrypt_block(&self, block: u64) -> u64 {
        let (mut b0, mut b1) = divide_u64(block);
        let (k0, k1) = divide_u64(self.key0);
        let (k2, k3) = divide_u64(self.key1);
        let mut sum = W(0xC6EF3720u32);

        for _ in 0..32 {
            b1 -= ((b0 << 4) + k2) ^ (b0 + sum) ^ ((b0 >> 5) + k3);
            b0 -= ((b1 << 4) + k0) ^ (b1 + sum) ^ ((b1 >> 5) + k1);
            sum -= W(0x9E3779B9);
        }

        ((b1.0 as u64) << 32) | b0.0 as u64
    }
}

#[inline]
fn divide_u64(n: u64) -> (W<u32>, W<u32>) {
    (W(n as u32), W((n >> 32) as u32))
}

pub fn tea_encrypt(plain: &[u8], key: &[u8]) -> Vec<u8> {
    let tea = TeaContext::new(&[to_block(&key[..8]), to_block(&key[8..16])]);
    let mut result: Vec<u8> = Vec::new();

    for i in (0..plain.len()).step_by(8) {
        let block = to_block(&plain[i..i + 8]);
        result.extend(from_block(tea.encrypt_block(block)).iter());
    }

    result
}

pub fn tea_decrypt(cipher: &[u8], key: &[u8]) -> Vec<u8> {
    let tea = TeaContext::new(&[to_block(&key[..8]), to_block(&key[8..16])]);
    let mut result: Vec<u8> = Vec::new();

    for i in (0..cipher.len()).step_by(8) {
        let block = to_block(&cipher[i..i + 8]);
        result.extend(from_block(tea.decrypt_block(block)).iter());
    }

    result
}

#[inline]
fn to_block(data: &[u8]) -> u64 {
    data[0] as u64
        | (data[1] as u64) << 8
        | (data[2] as u64) << 16
        | (data[3] as u64) << 24
        | (data[4] as u64) << 32
        | (data[5] as u64) << 40
        | (data[6] as u64) << 48
        | (data[7] as u64) << 56
}

fn from_block(block: u64) -> [u8; 8] {
    [
        block as u8,
        (block >> 8) as u8,
        (block >> 16) as u8,
        (block >> 24) as u8,
        (block >> 32) as u8,
        (block >> 40) as u8,
        (block >> 48) as u8,
        (block >> 56) as u8,
    ]
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_block_convert() {
        assert_eq!(
            to_block(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
            0xefcdab8967452301
        );

        assert_eq!(
            from_block(0xefcdab8967452301),
            [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]
        );
    }

    #[test]
    fn test_tea_encrypt() {
        assert_eq!(
            tea_encrypt(
                &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
                &[
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00, 0x00, 0x00
                ]
            ),
            [0x0A, 0x3A, 0xEA, 0x41, 0x40, 0xA9, 0xBA, 0x94]
        );
    }

    #[test]
    fn test_tea_encdec() {
        let plain = &[0x1b, 0xcc, 0xd4, 0x31, 0xa0, 0xf6, 0x8a, 0x55];
        let key = &[
            0x20, 0x45, 0x08, 0x10, 0xb0, 0x23, 0xe2, 0x17, 0xc3, 0x81, 0xd6, 0xf2, 0xee, 0x00,
            0xa4, 0x8a,
        ];
        let cipher = tea_encrypt(plain, key);

        assert_eq!(tea_decrypt(&cipher[..], key), plain);
    }
}


// in theory rot-13 only affects the lowercase characters in a cipher
pub fn theoretical_rot13(text: &str) -> String {
    let mut pos: u8 = 0;
    let mut npos: u8 = 0;
    text.to_owned()
        .chars()
        .map(|mut c| {
            if c.is_ascii_lowercase() {
                // ((c as u8) + 13) as char
                pos = c as u8 - b'a';
                npos = (pos + 13) % 26;
                c = (npos + b'a') as char;
                c
            } else {
                c
            }
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_single_letter() {
        assert_eq!("n", theoretical_rot13("a"));
    }
    #[test]
    fn test_bunch_of_letters() {
        assert_eq!("nop op", theoretical_rot13("abc bc"));
    }

    #[test]
    fn test_non_ascii() {
        assert_eq!("ðŸ˜€ab", theoretical_rot13("ðŸ˜€no"));
    }

    #[test]
    fn test_twice() {
        assert_eq!("abcd", theoretical_rot13(&theoretical_rot13("abcd")));
    }
}


//! Transposition Cipher
//!
//! The Transposition Cipher is a method of encryption by which a message is shifted
//! according to a regular system, so that the ciphertext is a rearrangement of the
//! original message. The most commonly referred to Transposition Cipher is the
//! COLUMNAR TRANSPOSITION cipher, which is demonstrated below.

use std::ops::Range;

/// Encrypts or decrypts a message, using multiple keys. The
/// encryption is based on the columnar transposition method.
pub fn transposition(decrypt_mode: bool, msg: &str, key: &str) -> String {
    let key_uppercase: String = key.to_uppercase();
    let mut cipher_msg: String = msg.to_string();

    let keys: Vec<&str> = match decrypt_mode {
        false => key_uppercase.split_whitespace().collect(),
        true => key_uppercase.split_whitespace().rev().collect(),
    };

    for cipher_key in keys.iter() {
        let mut key_order: Vec<usize> = Vec::new();
        let mut counter: u8 = 0;

        // Removes any non-alphabet characters from 'msg'
        cipher_msg = cipher_msg
            .to_uppercase()
            .chars()
            .filter(|&c| c.is_ascii_alphabetic())
            .collect();

        // Determines the sequence of the columns, as dictated by the
        // alphabetical order of the keyword's letters
        let mut key_ascii: Vec<(usize, u8)> =
            cipher_key.bytes().enumerate().collect::<Vec<(usize, u8)>>();

        key_ascii.sort_by_key(|&(_, key)| key);

        key_ascii.iter_mut().for_each(|(_, key)| {
            *key = counter;
            counter += 1;
        });

        key_ascii.sort_by_key(|&(index, _)| index);

        key_ascii
            .into_iter()
            .for_each(|(_, key)| key_order.push(key.into()));

        // Determines whether to encrypt or decrypt the message,
        // and returns the result
        cipher_msg = match decrypt_mode {
            false => encrypt(cipher_msg, key_order),
            true => decrypt(cipher_msg, key_order),
        };
    }

    cipher_msg
}

/// Performs the columnar transposition encryption
fn encrypt(mut msg: String, key_order: Vec<usize>) -> String {
    let mut encrypted_msg: String = String::from("");
    let mut encrypted_vec: Vec<String> = Vec::new();

    let msg_len: usize = msg.len();
    let key_len: usize = key_order.len();

    let mut msg_index: usize = msg_len;
    let mut key_index: usize = key_len;

    // Loop each column, pushing it to a Vec<T>
    while !msg.is_empty() {
        let mut chars: String = String::from("");
        let mut index: usize = 0;
        key_index -= 1;

        // Loop every nth character, determined by key length, to create a column
        while index < msg_index {
            let ch: char = msg.remove(index);
            chars.push(ch);

            index += key_index;
            msg_index -= 1;
        }

        encrypted_vec.push(chars);
    }

    // Concatenate the columns into a string, determined by the
    // alphabetical order of the keyword's characters
    let mut indexed_vec: Vec<(usize, &String)> = Vec::new();
    let mut indexed_msg: String = String::from("");
    let mut counter: usize = 0;

    key_order.into_iter().for_each(|key_index| {
        indexed_vec.push((key_index, &encrypted_vec[counter]));
        counter += 1;
    });

    indexed_vec.sort();

    indexed_vec.into_iter().for_each(|(_, column)| {
        indexed_msg.push_str(column);
    });

    // Split the message by a space every nth character, determined by
    // 'message length divided by keyword length' to the next highest integer.
    let msg_div: usize = (msg_len as f32 / key_len as f32).ceil() as usize;
    let mut counter: usize = 0;

    indexed_msg.chars().for_each(|c| {
        encrypted_msg.push(c);
        counter += 1;
        if counter == msg_div {
            encrypted_msg.push(' ');
            counter = 0;
        }
    });

    encrypted_msg.trim_end().to_string()
}

/// Performs the columnar transposition decryption
fn decrypt(mut msg: String, key_order: Vec<usize>) -> String {
    let mut decrypted_msg: String = String::from("");
    let mut decrypted_vec: Vec<String> = Vec::new();
    let mut indexed_vec: Vec<(usize, String)> = Vec::new();

    let msg_len: usize = msg.len();
    let key_len: usize = key_order.len();

    // Split the message into columns, determined by 'message length divided by keyword length'.
    // Some columns are larger by '+1', where the prior calculation leaves a remainder.
    let split_size: usize = (msg_len as f64 / key_len as f64) as usize;
    let msg_mod: usize = msg_len % key_len;
    let mut counter: usize = msg_mod;

    let mut key_split: Vec<usize> = key_order.clone();
    let (split_large, split_small) = key_split.split_at_mut(msg_mod);

    split_large.sort_unstable();
    split_small.sort_unstable();

    split_large.iter_mut().rev().for_each(|key_index| {
        counter -= 1;
        let range: Range<usize> =
            ((*key_index * split_size) + counter)..(((*key_index + 1) * split_size) + counter + 1);

        let slice: String = msg[range.clone()].to_string();
        indexed_vec.push((*key_index, slice));

        msg.replace_range(range, "");
    });

    split_small.iter_mut().for_each(|key_index| {
        let (slice, rest_of_msg) = msg.split_at(split_size);
        indexed_vec.push((*key_index, (slice.to_string())));
        msg = rest_of_msg.to_string();
    });

    indexed_vec.sort();

    key_order.into_iter().for_each(|key| {
        if let Some((_, column)) = indexed_vec.iter().find(|(key_index, _)| key_index == &key) {
            decrypted_vec.push(column.to_string());
        }
    });

    // Concatenate the columns into a string, determined by the
    // alphabetical order of the keyword's characters
    for _ in 0..split_size {
        decrypted_vec.iter_mut().for_each(|column| {
            decrypted_msg.push(column.remove(0));
        })
    }

    if !decrypted_vec.is_empty() {
        decrypted_vec.into_iter().for_each(|chars| {
            decrypted_msg.push_str(&chars);
        })
    }

    decrypted_msg
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn encryption() {
        assert_eq!(
            transposition(
                false,
                "The quick brown fox jumps over the lazy dog",
                "Archive",
            ),
            "TKOOL ERJEZ CFSEG QOURY UWMTD HBXVA INPHO"
        );

        assert_eq!(
            transposition(
                false,
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.,/;'[]{}:|_+=-`~() ",
                "Tenacious"
            ),
            "DMVENW ENWFOX BKTCLU FOXGPY CLUDMV GPYHQZ IRAJSA JSBKTH QZIR"
        );

        assert_eq!(
            transposition(false, "WE ARE DISCOVERED. FLEE AT ONCE.", "ZEBRAS"),
            "EVLNA CDTES EAROF ODEEC WIREE"
        );
    }

    #[test]
    fn decryption() {
        assert_eq!(
            transposition(true, "TKOOL ERJEZ CFSEG QOURY UWMTD HBXVA INPHO", "Archive"),
            "THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG"
        );

        assert_eq!(
            transposition(
                true,
                "DMVENW ENWFOX BKTCLU FOXGPY CLUDMV GPYHQZ IRAJSA JSBKTH QZIR",
                "Tenacious"
            ),
            "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
        );

        assert_eq!(
            transposition(true, "EVLNA CDTES EAROF ODEEC WIREE", "ZEBRAS"),
            "WEAREDISCOVEREDFLEEATONCE"
        );
    }

    #[test]
    fn double_encryption() {
        assert_eq!(
            transposition(
                false,
                "The quick brown fox jumps over the lazy dog",
                "Archive Snow"
            ),
            "KEZEUWHAH ORCGRMBIO TLESOUDVP OJFQYTXN"
        );

        assert_eq!(
            transposition(
                false,
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.,/;'[]{}:|_+=-`~() ",
                "Tenacious Drink"
            ),
            "DWOCXLGZSKI VNBUPDYRJHN FTOCVQJBZEW KFYMHASQMEX LGUPIATR"
        );

        assert_eq!(
            transposition(false, "WE ARE DISCOVERED. FLEE AT ONCE.", "ZEBRAS STRIPE"),
            "CAEEN SOIAE DRLEF WEDRE EVTOC"
        );
    }

    #[test]
    fn double_decryption() {
        assert_eq!(
            transposition(
                true,
                "KEZEUWHAH ORCGRMBIO TLESOUDVP OJFQYTXN",
                "Archive Snow"
            ),
            "THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG"
        );

        assert_eq!(
            transposition(
                true,
                "DWOCXLGZSKI VNBUPDYRJHN FTOCVQJBZEW KFYMHASQMEX LGUPIATR",
                "Tenacious Drink",
            ),
            "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
        );

        assert_eq!(
            transposition(true, "CAEEN SOIAE DRLEF WEDRE EVTOC", "ZEBRAS STRIPE"),
            "WEAREDISCOVEREDFLEEATONCE"
        );
    }
}


pub fn xor_bytes(text: &[u8], key: u8) -> Vec<u8> {
    text.iter().map(|c| c ^ key).collect()
}

pub fn xor(text: &str, key: u8) -> Vec<u8> {
    xor_bytes(text.as_bytes(), key)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_simple() {
        let test_string = "test string";
        let ciphered_text = xor(test_string, 32);
        assert_eq!(test_string.as_bytes(), xor_bytes(&ciphered_text, 32));
    }

    #[test]
    fn test_every_alphabet_with_space() {
        let test_string = "The quick brown fox jumps over the lazy dog";
        let ciphered_text = xor(test_string, 64);
        assert_eq!(test_string.as_bytes(), xor_bytes(&ciphered_text, 64));
    }

    #[test]
    fn test_multi_byte() {
        let test_string = "æ—¥æœ¬èªž";
        let key = 42;
        let ciphered_text = xor(test_string, key);
        assert_eq!(test_string.as_bytes(), xor_bytes(&ciphered_text, key));
    }

    #[test]
    fn test_zero_byte() {
        let test_string = "The quick brown fox jumps over the lazy dog";
        let key = b' ';
        let ciphered_text = xor(test_string, key);
        assert_eq!(test_string.as_bytes(), xor_bytes(&ciphered_text, key));
    }

    #[test]
    fn test_invalid_byte() {
        let test_string = "The quick brown fox jumps over the lazy dog";
        let key = !0;
        let ciphered_text = xor(test_string, key);
        assert_eq!(test_string.as_bytes(), xor_bytes(&ciphered_text, key));
    }
}


mod run_length_encoding;

pub use self::run_length_encoding::{run_length_decode, run_length_encode};


// https://en.wikipedia.org/wiki/Run-length_encoding

pub fn run_length_encode(text: &str) -> Vec<(char, i32)> {
    let mut count = 1;
    let mut encoded: Vec<(char, i32)> = vec![];

    for (i, c) in text.chars().enumerate() {
        if i + 1 < text.len() && c == text.chars().nth(i + 1).unwrap() {
            count += 1;
        } else {
            encoded.push((c, count));
            count = 1;
        }
    }

    encoded
}

pub fn run_length_decode(encoded: &[(char, i32)]) -> String {
    let res = encoded
        .iter()
        .map(|x| (x.0).to_string().repeat(x.1 as usize))
        .collect::<String>();

    res
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_run_length_decode() {
        let res = run_length_decode(&[('A', 0)]);
        assert_eq!(res, "");
        let res = run_length_decode(&[('B', 1)]);
        assert_eq!(res, "B");
        let res = run_length_decode(&[('A', 5), ('z', 3), ('B', 1)]);
        assert_eq!(res, "AAAAAzzzB");
    }

    #[test]
    fn test_run_length_encode() {
        let res = run_length_encode("");
        assert_eq!(res, []);

        let res = run_length_encode("A");
        assert_eq!(res, [('A', 1)]);

        let res = run_length_encode("AA");
        assert_eq!(res, [('A', 2)]);

        let res = run_length_encode("AAAABBBCCDAA");
        assert_eq!(res, [('A', 4), ('B', 3), ('C', 2), ('D', 1), ('A', 2)]);

        let res = run_length_encode("Rust-Trends");
        assert_eq!(
            res,
            [
                ('R', 1),
                ('u', 1),
                ('s', 1),
                ('t', 1),
                ('-', 1),
                ('T', 1),
                ('r', 1),
                ('e', 1),
                ('n', 1),
                ('d', 1),
                ('s', 1)
            ]
        );
    }
}


pub fn decimal_to_binary(base_num: u64) -> String {
    let mut num = base_num;
    let mut binary_num = String::new();
    loop {
        let bit = (num % 2).to_string();
        binary_num.push_str(&bit);
        num /= 2;
        if num == 0 {
            break;
        }
    }

    let bits = binary_num.chars();
    bits.rev().collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn converting_decimal_to_binary() {
        assert_eq!(decimal_to_binary(542), "1000011110");
        assert_eq!(decimal_to_binary(92), "1011100");
    }
}


mod binary_to_decimal;
mod decimal_to_binary;
pub use self::binary_to_decimal::binary_to_decimal;
pub use self::decimal_to_binary::decimal_to_binary;