#[cfg(feature = "specialize")]
use crate::HasherExt;
use core::hash::Hash;
use core::hash::Hasher;
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std as alloc;
#[cfg(feature = "specialize")]
use alloc::string::String;
#[cfg(feature = "specialize")]
use alloc::vec::Vec;
pub trait CallHasher {
    fn get_hash<H: Hasher>(value: &Self, hasher: H) -> u64;
}
#[cfg(not(feature = "specialize"))]
impl<T> CallHasher for T
where
    T: Hash + ?Sized,
{
    #[inline]
    fn get_hash<H: Hasher>(value: &T, mut hasher: H) -> u64 {
        value.hash(&mut hasher);
        hasher.finish()
    }
}
#[cfg(feature = "specialize")]
impl<T> CallHasher for T
where
    T: Hash + ?Sized,
{
    #[inline]
    default fn get_hash<H: Hasher>(value: &T, mut hasher: H) -> u64 {
        value.hash(&mut hasher);
        hasher.finish()
    }
}
macro_rules! call_hasher_impl {
    ($typ:ty) => {
        #[cfg(feature = "specialize")]
        impl CallHasher for $typ {
            #[inline]
            fn get_hash<H: Hasher>(value: &$typ, hasher: H) -> u64 {
                hasher.hash_u64(*value as u64)
            }
        }
        #[cfg(feature = "specialize")]
        impl CallHasher for &$typ {
            #[inline]
            fn get_hash<H: Hasher>(value: &&$typ, hasher: H) -> u64 {
                hasher.hash_u64(**value as u64)
            }
        }
        #[cfg(feature = "specialize")]
        impl CallHasher for &&$typ {
            #[inline]
            fn get_hash<H: Hasher>(value: &&&$typ, hasher: H) -> u64 {
                hasher.hash_u64(***value as u64)
            }
        }
    };
}
call_hasher_impl!(u8);
call_hasher_impl!(u16);
call_hasher_impl!(u32);
call_hasher_impl!(u64);
call_hasher_impl!(i8);
call_hasher_impl!(i16);
call_hasher_impl!(i32);
call_hasher_impl!(i64);
#[cfg(feature = "specialize")]
impl CallHasher for u128 {
    #[inline]
    fn get_hash<H: Hasher>(value: &u128, mut hasher: H) -> u64 {
        hasher.write_u128(*value);
        hasher.short_finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for &u128 {
    #[inline]
    fn get_hash<H: Hasher>(value: &&u128, mut hasher: H) -> u64 {
        hasher.write_u128(**value);
        hasher.short_finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for &&u128 {
    #[inline]
    fn get_hash<H: Hasher>(value: &&&u128, mut hasher: H) -> u64 {
        hasher.write_u128(***value);
        hasher.short_finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for i128 {
    #[inline]
    fn get_hash<H: Hasher>(value: &i128,  mut hasher: H) -> u64 {
        hasher.write_u128(*value as u128);
        hasher.short_finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for &i128 {
    #[inline]
    fn get_hash<H: Hasher>(value: &&i128,  mut hasher: H) -> u64 {
        hasher.write_u128(**value as u128);
        hasher.short_finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for &&i128 {
    #[inline]
    fn get_hash<H: Hasher>(value: &&&i128,  mut hasher: H) -> u64 {
        hasher.write_u128(***value as u128);
        hasher.short_finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for [u8] {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(value);
        hasher.finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for &[u8] {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(*value);
        hasher.finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for &&[u8] {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(**value);
        hasher.finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for Vec<u8> {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(value);
        hasher.finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for &Vec<u8> {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(*value);
        hasher.finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for &&Vec<u8> {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(**value);
        hasher.finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for str {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(value.as_bytes());
        hasher.finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for &str {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(value.as_bytes());
        hasher.finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for &&str {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(value.as_bytes());
        hasher.finish()
    }
}
#[cfg(feature = "specialize")]
impl CallHasher for &&&str {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(value.as_bytes());
        hasher.finish()
    }
}
#[cfg(all(feature = "specialize"))]
impl CallHasher for String {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(value.as_bytes());
        hasher.finish()
    }
}
#[cfg(all(feature = "specialize"))]
impl CallHasher for &String {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(value.as_bytes());
        hasher.finish()
    }
}
#[cfg(all(feature = "specialize"))]
impl CallHasher for &&String {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(value.as_bytes());
        hasher.finish()
    }
}
#[cfg(all(feature = "specialize"))]
impl CallHasher for &&&String {
    #[inline]
    fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
        hasher.write(value.as_bytes());
        hasher.finish()
    }
}
#[cfg(test)]
mod test {
    use super::*;
    use crate::*;
    #[test]
    #[cfg(feature = "specialize")]
    pub fn test_specialized_invoked() {
        let shortened = u64::get_hash(&0, AHasher::new_with_keys(1, 2));
        let mut hasher = AHasher::new_with_keys(1, 2);
        0_u64.hash(&mut hasher);
        assert_ne!(hasher.finish(), shortened);
    }
    
    #[test]
    pub fn test_input_processed() {
        let hasher = || AHasher::new_with_keys(3, 2);
        assert_ne!(0, u64::get_hash(&0, hasher()));
        assert_ne!(1, u64::get_hash(&0, hasher()));
        assert_ne!(2, u64::get_hash(&0, hasher()));
        assert_ne!(3, u64::get_hash(&0, hasher()));
        assert_ne!(4, u64::get_hash(&0, hasher()));
        assert_ne!(5, u64::get_hash(&0, hasher()));
        assert_ne!(0, u64::get_hash(&1, hasher()));
        assert_ne!(1, u64::get_hash(&1, hasher()));
        assert_ne!(2, u64::get_hash(&1, hasher()));
        assert_ne!(3, u64::get_hash(&1, hasher()));
        assert_ne!(4, u64::get_hash(&1, hasher()));
        assert_ne!(5, u64::get_hash(&1, hasher()));
        let xored = u64::get_hash(&0, hasher()) ^ u64::get_hash(&1, hasher());
        assert_ne!(0, xored);
        assert_ne!(1, xored);
        assert_ne!(2, xored);
        assert_ne!(3, xored);
        assert_ne!(4, xored);
        assert_ne!(5, xored);
    }
    #[test]
    pub fn test_ref_independent() {
        let hasher = || AHasher::new_with_keys(3, 2);
        assert_eq!(<&u8>::get_hash(&&1, hasher()), u8::get_hash(&1, hasher()));
        assert_eq!(<&u16>::get_hash(&&2, hasher()), u16::get_hash(&2, hasher()));
        assert_eq!(<&u32>::get_hash(&&3, hasher()), u32::get_hash(&3, hasher()));
        assert_eq!(<&u64>::get_hash(&&4, hasher()), u64::get_hash(&4, hasher()));
        assert_eq!(<&u128>::get_hash(&&5, hasher()), u128::get_hash(&5, hasher()));
        assert_eq!(<&str>::get_hash(&"test", hasher()), str::get_hash("test", hasher()));
        assert_eq!(<&str>::get_hash(&"test", hasher()), String::get_hash(&"test".to_string(), hasher()));
        #[cfg(feature = "specialize")]
        assert_eq!(<&str>::get_hash(&"test", hasher()), <[u8]>::get_hash("test".as_bytes(), hasher()));
        let hasher = || AHasher::new_with_keys(3, 2);
        assert_eq!(<&&u8>::get_hash(&&&1, hasher()), u8::get_hash(&1, hasher()));
        assert_eq!(<&&u16>::get_hash(&&&2, hasher()), u16::get_hash(&2, hasher()));
        assert_eq!(<&&u32>::get_hash(&&&3, hasher()), u32::get_hash(&3, hasher()));
        assert_eq!(<&&u64>::get_hash(&&&4, hasher()), u64::get_hash(&4, hasher()));
        assert_eq!(<&&u128>::get_hash(&&&5, hasher()), u128::get_hash(&5, hasher()));
        assert_eq!(<&&str>::get_hash(&&"test", hasher()), str::get_hash("test",hasher()));
        assert_eq!(<&&str>::get_hash(&&"test", hasher()), String::get_hash(&"test".to_string(), hasher()));
        #[cfg(feature = "specialize")]
        assert_eq!(<&&str>::get_hash(&&"test", hasher()), <[u8]>::get_hash(&"test".to_string().into_bytes(), hasher()));
    }
}