#![feature(test)]
extern crate rand_core;
extern crate paste;

use rand_core::RngCore;

pub mod base;

//has a function which computes the PRF (basically a kind of keyed hash function)
//do_prf_with_obj will also include some data associated to the PRF itself in addition to the
//randomly generated key. This is useful when you want to use some deterministic data in addition
//to or instead of the random data in the key, for instance, XMSS level information.
pub trait WotsPrf<const K_BYTES: usize,const N_BYTES: usize> { 
    fn do_prf(key: &[u8;K_BYTES],value: &mut [u8;N_BYTES]);
    fn do_prf_with_obj(obj: &Self, key: &[u8;K_BYTES],value: &mut [u8;N_BYTES]);
}

//has a function which spawns an RNG seeded from the given seed.
//mk_rng_with_obj will also include some data associated with the PRNG itself, which can be used to
//pass deterministic data (e.g. XMSS level information) to the PRNG.
pub trait WotsRng<const PRNG_BYTES: usize> {
    type R: RngCore;
    fn mk_rng(seed: &[u8; PRNG_BYTES]) -> Self::R;
    fn mk_rng_with_obj(obj: &Self, seed: &[u8; PRNG_BYTES]) -> Self::R;
}

//helper macro to define new wots+ instances all at once
#[macro_export]
macro_rules! mk_wotsplus_instance {
    ($name:ident, $m_bytes:literal, $n_bytes:literal, $w_exp:literal, $prf_type:ident, $k_bytes:literal, $rng_type:ident, $prng_bytes:literal) => {
        paste! {
            pub type [< Signer_ $name >] = SignerBase<$w_exp,$m_bytes,$n_bytes,$k_bytes,$prng_bytes,{ getW($w_exp) }, { getL1($w_exp,$m_bytes) }, { getL2($w_exp,$m_bytes) }, { getL($w_exp,$m_bytes) },$prf_type,$rng_type>;
        }
        paste! {
            pub type [< Verifier_ $name >] = VerifierBase<$w_exp,$m_bytes,$n_bytes,$k_bytes,$prng_bytes, { getW($w_exp) }, { getL1($w_exp,$m_bytes) }, { getL2($w_exp,$m_bytes) }, { getL($w_exp,$m_bytes) },$prf_type,$rng_type>;
        }
    }
}

#[cfg(feature = "blake3_chacha")]
pub mod blake3_chacha {
    extern crate blake3;
	extern crate rand_chacha;
	use rand_chacha::ChaCha12Rng;
    use blake3::{keyed_hash,hash};
    use paste::paste;

    use crate::{WotsPrf,WotsRng,mk_wotsplus_instance};
    use crate::base::*;

	#[derive(Clone)]
	pub struct CHACHA12RNG;
	
	impl WotsRng<32> for CHACHA12RNG {
	    type R = ChaCha12Rng;
	    fn mk_rng(seed: &[u8;32]) -> Self::R {
	        ChaCha12Rng::from_seed(*seed)
	    }
	    fn mk_rng_with_obj(obj: &Self, seed: &[u8;32]) -> Self::R {
	        ChaCha12Rng::from_seed(*seed)
	    }
	}

    #[derive(Clone)]
    pub struct BLAKE3PRF {
        key: [u8; 32]
    }

    impl WotsPrf<32,32> for BLAKE3PRF {
        fn do_prf(key: &[u8;32], value: &mut [u8;32]) {
            let h = keyed_hash(key,value);
            value.copy_from_slice(h.as_bytes());
        }
        fn do_prf_with_obj(obj: &Self, key: &[u8;32], value: &mut [u8;32]) { //no different from do_prf
            let h = keyed_hash(key,value);
            value.copy_from_slice(h.as_bytes());
        }
    }


    impl WotsPrf<0,32> for BLAKE3PRF {
        fn do_prf(key: &[u8;0], value: &mut [u8;32]) { //plain hash; THIS IS INSECURE!
            let h = hash(value);
            value.copy_from_slice(h.as_bytes());
        }
        fn do_prf_with_obj(obj: &Self, key: &[u8;0], value: &mut [u8;32]) { //PRF with fixed K
            let h = keyed_hash(&obj.key,value);
            value.copy_from_slice(h.as_bytes());
        }
    }

    #[macro_export]
    macro_rules! mk_blake3_chacha_wotsplus_instance {
        ($m_bytes:literal, $w_exp:literal, $name:ident) {
            mk_wotsplus_instance!($name,$m_bytes,32,$w_exp, BLAKE3PRF, 32, CHACHA12RNG, 32)
        }
    }
}

#[cfg(test)]
mod test {
    extern crate blake3;
    extern crate rand;
    extern crate rand_chacha;
    use blake3::{keyed_hash,hash};
    use rand::prelude::*;
    use rand_chacha::ChaCha12Rng;
    use crate::{WotsPrf,WotsRng,mk_wotsplus_instance};
    use crate::base::*;
    use paste::paste;

    #[derive(Clone)]
    struct BLAKE3PRF {
        key: [u8;32]
    }

    impl WotsPrf<32,32> for BLAKE3PRF {
        fn do_prf(key: &[u8;32], value: &mut [u8;32]) {
            let h = keyed_hash(key,value);
            value.copy_from_slice(h.as_bytes());
        }
        fn do_prf_with_obj(obj: &Self, key: &[u8;32], value: &mut [u8;32]) {
            let h = keyed_hash(&obj.key,value);
            value.copy_from_slice(h.as_bytes());
        }
    }

    #[derive(Clone)]
    struct CHACHA12RNG;

    impl WotsRng<32> for CHACHA12RNG {
        type R = ChaCha12Rng;
        fn mk_rng(seed: &[u8;32]) -> Self::R {
            ChaCha12Rng::from_seed(*seed)
        }
        fn mk_rng_with_obj(obj: &Self, seed: &[u8;32]) -> Self::R {
            ChaCha12Rng::from_seed(*seed)
        }
    }

    #[derive(Clone)]
    struct CONSTRNG;

    impl WotsRng<0> for CONSTRNG {
        type R = ChaCha12Rng;
        fn mk_rng(seed: &[u8;0]) -> Self::R {
            ChaCha12Rng::seed_from_u64(0)
        }
        fn mk_rng_with_obj(obj: &Self, seed: &[u8;0]) -> Self::R {
            ChaCha12Rng::seed_from_u64(0)
        }
    }

    #[derive(Clone)]
    struct CONSTPRF;

    impl WotsPrf<0,32> for CONSTPRF { 
        fn do_prf(key: &[u8;0], value: &mut [u8; 32]) {
            let h = hash(value);
            value.copy_from_slice(h.as_bytes());
        }
        fn do_prf_with_obj(obj: &Self, key: &[u8;0], value: &mut [u8; 32]) {
            let h = hash(value);
            value.copy_from_slice(h.as_bytes());
        }
    }

    mk_wotsplus_instance!(BLAKE3_ChaCha12_2,32,32,1,BLAKE3PRF,32,CHACHA12RNG,32);
    mk_wotsplus_instance!(BLAKE3_ChaCha12_4,32,32,2,BLAKE3PRF,32,CHACHA12RNG,32);
    mk_wotsplus_instance!(BLAKE3_ChaCha12_8,32,32,3,BLAKE3PRF,32,CHACHA12RNG,32);
    mk_wotsplus_instance!(BLAKE3_ChaCha12_16,32,32,4,BLAKE3PRF,32,CHACHA12RNG,32);
    mk_wotsplus_instance!(BLAKE3_ChaCha12_32,32,32,5,BLAKE3PRF,32,CHACHA12RNG,32);
    mk_wotsplus_instance!(BLAKE3_ChaCha12_64,32,32,6,BLAKE3PRF,32,CHACHA12RNG,32);
    mk_wotsplus_instance!(BLAKE3_ChaCha12_128,32,32,7,BLAKE3PRF,32,CHACHA12RNG,32);
    mk_wotsplus_instance!(BLAKE3_ChaCha12_256,32,32,8,BLAKE3PRF,32,CHACHA12RNG,32);

    mk_wotsplus_instance!(CONST_16,32,32,4,CONSTPRF,0,CONSTRNG,0);

    #[test]
    //test that the formatting is sane
    fn formatting_compatibility() {
        assert!(Signer_BLAKE3_ChaCha12_2::KEYPAIRLEN == std::mem::size_of::<Signer_BLAKE3_ChaCha12_2>());
        assert!(Verifier_BLAKE3_ChaCha12_2::PKLEN == std::mem::size_of::<Verifier_BLAKE3_ChaCha12_2>());
        assert!(Signer_BLAKE3_ChaCha12_2::PKLEN == Verifier_BLAKE3_ChaCha12_2::PKLEN);
        assert!(Signer_BLAKE3_ChaCha12_2::SIGLEN == Verifier_BLAKE3_ChaCha12_2::SIGLEN);
        assert!(Signer_BLAKE3_ChaCha12_4::KEYPAIRLEN == std::mem::size_of::<Signer_BLAKE3_ChaCha12_4>());
        assert!(Verifier_BLAKE3_ChaCha12_4::PKLEN == std::mem::size_of::<Verifier_BLAKE3_ChaCha12_4>());
        assert!(Signer_BLAKE3_ChaCha12_4::PKLEN == Verifier_BLAKE3_ChaCha12_4::PKLEN);
        assert!(Signer_BLAKE3_ChaCha12_4::SIGLEN == Verifier_BLAKE3_ChaCha12_4::SIGLEN);
        assert!(Signer_BLAKE3_ChaCha12_8::KEYPAIRLEN == std::mem::size_of::<Signer_BLAKE3_ChaCha12_8>());
        assert!(Verifier_BLAKE3_ChaCha12_8::PKLEN == std::mem::size_of::<Verifier_BLAKE3_ChaCha12_8>());
        assert!(Signer_BLAKE3_ChaCha12_8::PKLEN == Verifier_BLAKE3_ChaCha12_8::PKLEN);
        assert!(Signer_BLAKE3_ChaCha12_8::SIGLEN == Verifier_BLAKE3_ChaCha12_8::SIGLEN);
        assert!(Signer_BLAKE3_ChaCha12_16::KEYPAIRLEN == std::mem::size_of::<Signer_BLAKE3_ChaCha12_16>());
        assert!(Verifier_BLAKE3_ChaCha12_16::PKLEN == std::mem::size_of::<Verifier_BLAKE3_ChaCha12_16>());
        assert!(Signer_BLAKE3_ChaCha12_16::PKLEN == Verifier_BLAKE3_ChaCha12_16::PKLEN);
        assert!(Signer_BLAKE3_ChaCha12_16::SIGLEN == Verifier_BLAKE3_ChaCha12_16::SIGLEN);
        assert!(Signer_BLAKE3_ChaCha12_32::KEYPAIRLEN == std::mem::size_of::<Signer_BLAKE3_ChaCha12_32>());
        assert!(Verifier_BLAKE3_ChaCha12_32::PKLEN == std::mem::size_of::<Verifier_BLAKE3_ChaCha12_32>());
        assert!(Signer_BLAKE3_ChaCha12_32::PKLEN == Verifier_BLAKE3_ChaCha12_32::PKLEN);
        assert!(Signer_BLAKE3_ChaCha12_32::SIGLEN == Verifier_BLAKE3_ChaCha12_32::SIGLEN);
        assert!(Signer_BLAKE3_ChaCha12_64::KEYPAIRLEN == std::mem::size_of::<Signer_BLAKE3_ChaCha12_64>());
        assert!(Verifier_BLAKE3_ChaCha12_64::PKLEN == std::mem::size_of::<Verifier_BLAKE3_ChaCha12_64>());
        assert!(Signer_BLAKE3_ChaCha12_64::PKLEN == Verifier_BLAKE3_ChaCha12_64::PKLEN);
        assert!(Signer_BLAKE3_ChaCha12_64::SIGLEN == Verifier_BLAKE3_ChaCha12_64::SIGLEN);
        assert!(Signer_BLAKE3_ChaCha12_128::KEYPAIRLEN == std::mem::size_of::<Signer_BLAKE3_ChaCha12_128>());
        assert!(Verifier_BLAKE3_ChaCha12_128::PKLEN == std::mem::size_of::<Verifier_BLAKE3_ChaCha12_128>());
        assert!(Signer_BLAKE3_ChaCha12_128::PKLEN == Verifier_BLAKE3_ChaCha12_128::PKLEN);
        assert!(Signer_BLAKE3_ChaCha12_128::SIGLEN == Verifier_BLAKE3_ChaCha12_128::SIGLEN);
        assert!(Signer_BLAKE3_ChaCha12_256::KEYPAIRLEN == std::mem::size_of::<Signer_BLAKE3_ChaCha12_256>());
        assert!(Verifier_BLAKE3_ChaCha12_256::PKLEN == std::mem::size_of::<Verifier_BLAKE3_ChaCha12_256>());
        assert!(Signer_BLAKE3_ChaCha12_256::PKLEN == Verifier_BLAKE3_ChaCha12_256::PKLEN);
        assert!(Signer_BLAKE3_ChaCha12_256::SIGLEN == Verifier_BLAKE3_ChaCha12_256::SIGLEN);
        assert!(Signer_CONST_16::KEYPAIRLEN == std::mem::size_of::<Signer_CONST_16>());
        assert!(Verifier_CONST_16::PKLEN == std::mem::size_of::<Verifier_CONST_16>());
        assert!(Signer_CONST_16::PKLEN == Verifier_CONST_16::PKLEN);
        assert!(Signer_CONST_16::SIGLEN == Verifier_CONST_16::SIGLEN);
    }

    #[test]
    fn doesnt_verify_bogus_message() {
        let mut seed_rng = rand::thread_rng();
        let real_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_16::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_16::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_16::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        my_signer.sign(&real_msg,&mut my_sig,None);
        let mut my_verifier = Verifier_BLAKE3_ChaCha12_16::from_formatted_pubkey(&my_pk).unwrap();
        for byte in 0..32 {
            for bit in 0..8 {
                let mut fake_msg = real_msg;
                fake_msg[byte] ^= 1 << bit;
                assert!(!my_verifier.verify_destructive(&fake_msg,&mut my_sig,None).unwrap());
            }
        }
    }

    #[test]
    fn sign_verify_const_version() {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        {
            let mut my_pk = [0u8; Signer_CONST_16::PKLEN];
            let mut my_sig = [0u8; Signer_CONST_16::SIGLEN];
            let mut my_signer = Signer_CONST_16::generate(&mut seed_rng,None);
            my_signer.format_public_key(&mut my_pk);
            my_signer.sign(&my_msg,&mut my_sig,None);
            let mut my_verifier = Verifier_CONST_16::from_formatted_pubkey(&my_pk).unwrap();
            assert!(my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap());
        };
    }

    #[test]
    fn sign_verify_with_offsets() {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        {
            let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_2::PKLEN];
            let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_2::SIGLEN];
            let mut my_signer = Signer_BLAKE3_ChaCha12_2::generate(&mut seed_rng,None);
            my_signer.format_public_key(&mut my_pk);
            my_signer.sign(&my_msg,&mut my_sig,None);
            let mut my_verifier = Verifier_BLAKE3_ChaCha12_2::from_formatted_pubkey(&my_pk).unwrap();
            assert!(my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap());
        };
        {
            let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_4::PKLEN];
            let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_4::SIGLEN];
            let mut my_signer = Signer_BLAKE3_ChaCha12_4::generate(&mut seed_rng,None);
            my_signer.format_public_key(&mut my_pk);
            my_signer.sign(&my_msg,&mut my_sig,None);
            let mut my_verifier = Verifier_BLAKE3_ChaCha12_4::from_formatted_pubkey(&my_pk).unwrap();
            assert!(my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap());
        };
        {
            let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_8::PKLEN];
            let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_8::SIGLEN];
            let mut my_signer = Signer_BLAKE3_ChaCha12_8::generate(&mut seed_rng,None);
            my_signer.format_public_key(&mut my_pk);
            my_signer.sign(&my_msg,&mut my_sig,None);
            let mut my_verifier = Verifier_BLAKE3_ChaCha12_8::from_formatted_pubkey(&my_pk).unwrap();
            assert!(my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap());
        };
        {
            let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_32::PKLEN];
            let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_32::SIGLEN];
            let mut my_signer = Signer_BLAKE3_ChaCha12_32::generate(&mut seed_rng,None);
            my_signer.format_public_key(&mut my_pk);
            my_signer.sign(&my_msg,&mut my_sig,None);
            let mut my_verifier = Verifier_BLAKE3_ChaCha12_32::from_formatted_pubkey(&my_pk).unwrap();
            assert!(my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap());
        };
        {
            let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_64::PKLEN];
            let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_64::SIGLEN];
            let mut my_signer = Signer_BLAKE3_ChaCha12_64::generate(&mut seed_rng,None);
            my_signer.format_public_key(&mut my_pk);
            my_signer.sign(&my_msg,&mut my_sig,None);
            let mut my_verifier = Verifier_BLAKE3_ChaCha12_64::from_formatted_pubkey(&my_pk).unwrap();
            assert!(my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap());
        };
        {
            let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_128::PKLEN];
            let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_128::SIGLEN];
            let mut my_signer = Signer_BLAKE3_ChaCha12_128::generate(&mut seed_rng,None);
            my_signer.format_public_key(&mut my_pk);
            my_signer.sign(&my_msg,&mut my_sig,None);
            let mut my_verifier = Verifier_BLAKE3_ChaCha12_128::from_formatted_pubkey(&my_pk).unwrap();
            assert!(my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap());
        };
        {
            let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_256::PKLEN];
            let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_256::SIGLEN];
            let mut my_signer = Signer_BLAKE3_ChaCha12_256::generate(&mut seed_rng,None);
            my_signer.format_public_key(&mut my_pk);
            my_signer.sign(&my_msg,&mut my_sig,None);
            let mut my_verifier = Verifier_BLAKE3_ChaCha12_256::from_formatted_pubkey(&my_pk).unwrap();
            assert!(my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap());
        };
    }
    
    //benchmarking stuff, requires nightly
    extern crate test;

    use test::Bencher;

    #[bench]
    fn gen_one_2(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_2::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_2::SIGLEN];
        let mut my_signer =  b.iter(|| {
            Signer_BLAKE3_ChaCha12_2::generate(&mut seed_rng,None)
        });
    }

    #[bench]
    fn sign_one_2(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_2::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_2::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_2::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        b.iter(|| {
            my_signer.sign(&my_msg,&mut my_sig,None);
            my_sig[0] = 1;
            my_sig
        });
    }

    #[bench]
    fn verify_one_2(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_2::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_2::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_2::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        my_signer.sign(&my_msg,&mut my_sig,None);
        let mut my_verifier = Verifier_BLAKE3_ChaCha12_2::from_formatted_pubkey(&my_pk).unwrap();
        b.iter(|| { my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap()});
    }
    
    #[bench]
    fn gen_one_4(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_4::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_4::SIGLEN];
        let mut my_signer =  b.iter(|| {
            Signer_BLAKE3_ChaCha12_4::generate(&mut seed_rng,None)
        });
    }

    #[bench]
    fn sign_one_4(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_4::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_4::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_4::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        b.iter(|| {
            my_signer.sign(&my_msg,&mut my_sig,None);
            my_sig[0] = 1;
            my_sig
        });
    }

    #[bench]
    fn verify_one_4(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_4::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_4::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_4::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        my_signer.sign(&my_msg,&mut my_sig,None);
        let mut my_verifier = Verifier_BLAKE3_ChaCha12_4::from_formatted_pubkey(&my_pk).unwrap();
        b.iter(|| { my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap()});
    }
    
    #[bench]
    fn gen_one_8(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_8::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_8::SIGLEN];
        let mut my_signer =  b.iter(|| {
            Signer_BLAKE3_ChaCha12_8::generate(&mut seed_rng,None)
        });
    }

    #[bench]
    fn sign_one_8(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_8::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_8::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_8::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        b.iter(|| {
            my_signer.sign(&my_msg,&mut my_sig,None);
            my_sig[0] = 1;
            my_sig
        });
    }

    #[bench]
    fn verify_one_8(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_8::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_8::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_8::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        my_signer.sign(&my_msg,&mut my_sig,None);
        let mut my_verifier = Verifier_BLAKE3_ChaCha12_8::from_formatted_pubkey(&my_pk).unwrap();
        b.iter(|| { my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap()});
    }
    
    #[bench]
    fn gen_one_16(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_16::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_16::SIGLEN];
        let mut my_signer =  b.iter(|| {
            Signer_BLAKE3_ChaCha12_16::generate(&mut seed_rng,None)
        });
    }

    #[bench]
    fn sign_one_16(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_16::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_16::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_16::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        b.iter(|| {
            my_signer.sign(&my_msg,&mut my_sig,None);
            my_sig[0] = 1;
            my_sig
        });
    }

    #[bench]
    fn verify_one_16(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_16::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_16::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_16::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        my_signer.sign(&my_msg,&mut my_sig,None);
        let mut my_verifier = Verifier_BLAKE3_ChaCha12_16::from_formatted_pubkey(&my_pk).unwrap();
        b.iter(|| { my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap()});
    }
    
    #[bench]
    fn gen_one_32(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_32::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_32::SIGLEN];
        let mut my_signer =  b.iter(|| {
            Signer_BLAKE3_ChaCha12_32::generate(&mut seed_rng,None)
        });
    }

    #[bench]
    fn sign_one_32(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_32::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_32::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_32::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        b.iter(|| {
            my_signer.sign(&my_msg,&mut my_sig,None);
            my_sig[0] = 1;
            my_sig
        });
    }

    #[bench]
    fn verify_one_32(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_32::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_32::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_32::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        my_signer.sign(&my_msg,&mut my_sig,None);
        let mut my_verifier = Verifier_BLAKE3_ChaCha12_32::from_formatted_pubkey(&my_pk).unwrap();
        b.iter(|| { my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap()});
    }
    
    #[bench]
    fn gen_one_64(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_64::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_64::SIGLEN];
        let mut my_signer =  b.iter(|| {
            Signer_BLAKE3_ChaCha12_64::generate(&mut seed_rng,None)
        });
    }

    #[bench]
    fn sign_one_64(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_64::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_64::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_64::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        b.iter(|| {
            my_signer.sign(&my_msg,&mut my_sig,None);
            my_sig[0] = 1;
            my_sig
        });
    }

    #[bench]
    fn verify_one_64(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_64::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_64::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_64::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        my_signer.sign(&my_msg,&mut my_sig,None);
        let mut my_verifier = Verifier_BLAKE3_ChaCha12_64::from_formatted_pubkey(&my_pk).unwrap();
        b.iter(|| { my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap()});
    }
    
    #[bench]
    fn gen_one_128(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_128::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_128::SIGLEN];
        let mut my_signer =  b.iter(|| {
            Signer_BLAKE3_ChaCha12_128::generate(&mut seed_rng,None)
        });
    }

    #[bench]
    fn sign_one_128(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_128::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_128::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_128::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        b.iter(|| {
            my_signer.sign(&my_msg,&mut my_sig,None);
            my_sig[0] = 1;
            my_sig
        });
    }

    #[bench]
    fn verify_one_128(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_128::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_128::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_128::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        my_signer.sign(&my_msg,&mut my_sig,None);
        let mut my_verifier = Verifier_BLAKE3_ChaCha12_128::from_formatted_pubkey(&my_pk).unwrap();
        b.iter(|| { my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap()});
    }
    
    #[bench]
    fn gen_one_256(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_256::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_256::SIGLEN];
        let mut my_signer =  b.iter(|| {
            Signer_BLAKE3_ChaCha12_256::generate(&mut seed_rng,None)
        });
    }

    #[bench]
    fn sign_one_256(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_256::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_256::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_256::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        b.iter(|| {
            my_signer.sign(&my_msg,&mut my_sig,None);
            my_sig[0] = 1;
            my_sig
        });
    }

    #[bench]
    fn verify_one_256(b: &mut Bencher) {
        let mut seed_rng = rand::thread_rng();
        let my_msg: [u8;32] = seed_rng.gen();
        let mut my_pk = [0u8; Signer_BLAKE3_ChaCha12_256::PKLEN];
        let mut my_sig = [0u8; Signer_BLAKE3_ChaCha12_256::SIGLEN];
        let mut my_signer = Signer_BLAKE3_ChaCha12_256::generate(&mut seed_rng,None);
        my_signer.format_public_key(&mut my_pk);
        my_signer.sign(&my_msg,&mut my_sig,None);
        let mut my_verifier = Verifier_BLAKE3_ChaCha12_256::from_formatted_pubkey(&my_pk).unwrap();
        b.iter(|| { my_verifier.verify_destructive(&my_msg,&mut my_sig,None).unwrap()});
    }
}
