use std::{
    alloc::{self, handle_alloc_error, Layout},
    fmt, mem,
    ptr::NonNull,
};
use light_utils::{bigint::bigint_to_be_bytes_array, UtilsError};
use num_bigint::{BigUint, ToBigUint};
use num_traits::{Bounded, CheckedAdd, CheckedSub, FromBytes, ToPrimitive, Unsigned};
use thiserror::Error;
pub mod zero_copy;
#[derive(Debug, Error)]
pub enum HashSetError {
    #[error("The hash set is full, cannot add any new elements")]
    Full,
    #[error("The provided element is already in the hash set")]
    ElementAlreadyExists,
    #[error("The provided element doesn't exist in the hash set")]
    ElementDoesNotExist,
    #[error("The hash set is empty")]
    Empty,
    #[error("Could not convert the index from/to usize")]
    UsizeConv,
    #[error("Integer overflow")]
    IntegerOverflow,
    #[error("Invalid buffer size, expected {0}, got {1}")]
    BufferSize(usize, usize),
    #[error("Utils: big integer conversion error")]
    Utils(#[from] UtilsError),
}
#[cfg(feature = "solana")]
impl From<HashSetError> for u32 {
    fn from(e: HashSetError) -> u32 {
        match e {
            HashSetError::Full => 6001,
            HashSetError::ElementAlreadyExists => 6002,
            HashSetError::ElementDoesNotExist => 6003,
            HashSetError::Empty => 6004,
            HashSetError::UsizeConv => 6005,
            HashSetError::IntegerOverflow => 6006,
            HashSetError::BufferSize(_, _) => 6007,
            HashSetError::Utils(e) => e.into(),
        }
    }
}
#[cfg(feature = "solana")]
impl From<HashSetError> for solana_program::program_error::ProgramError {
    fn from(e: HashSetError) -> Self {
        solana_program::program_error::ProgramError::Custom(e.into())
    }
}
pub fn find_next_prime(mut n: f64) -> f64 {
    n = n.round();
    if n <= 2.0 {
        return 2.0;
    } else if n <= 3.0 {
        return 3.0;
    }
    let remainder = n % 6.0;
    if remainder != 0.0 {
        n = n + 6.0 - remainder;
        let candidate = n - 1.0;
        if is_prime(candidate) {
            return candidate;
        }
    }
    loop {
        let candidate = n + 1.0;
        if is_prime(candidate) {
            return candidate;
        }
        let candidate = n + 5.0;
        if is_prime(candidate) {
            return candidate;
        }
        n += 6.0;
    }
}
pub fn is_prime(n: f64) -> bool {
    if n <= 1.0 {
        return false;
    }
    if n <= 3.0 {
        return true;
    }
    if n % 2.0 == 0.0 || n % 3.0 == 0.0 {
        return false;
    }
    let mut i = 5.0;
    while i * i <= n {
        if n % i == 0.0 || n % (i + 2.0) == 0.0 {
            return false;
        }
        i += 6.0;
    }
    true
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct HashSetCell {
    value: [u8; 32],
    sequence_number: Option<usize>,
}
impl HashSetCell {
    pub fn value_bytes(&self) -> [u8; 32] {
        self.value
    }
    pub fn value_biguint(&self) -> BigUint {
        BigUint::from_bytes_be(self.value.as_slice())
    }
    pub fn sequence_number(&self) -> Option<usize> {
        self.sequence_number
    }
}
#[derive(Debug)]
pub struct HashSet<I>
where
    I: Bounded
        + CheckedAdd
        + CheckedSub
        + Clone
        + Copy
        + fmt::Display
        + From<u8>
        + PartialEq
        + PartialOrd
        + ToBigUint
        + TryFrom<u64>
        + TryFrom<usize>
        + Unsigned,
    usize: TryFrom<I>,
    <usize as TryFrom<I>>::Error: fmt::Debug,
{
    pub capacity_indices: usize,
    pub capacity_values: usize,
    pub sequence_threshold: usize,
    pub next_value_index: *mut usize,
    indices: NonNull<Option<I>>,
    values: NonNull<Option<HashSetCell>>,
}
impl<I> HashSet<I>
where
    I: Bounded
        + CheckedAdd
        + CheckedSub
        + Clone
        + Copy
        + fmt::Display
        + From<u8>
        + PartialEq
        + PartialOrd
        + ToBigUint
        + TryFrom<u64>
        + TryFrom<usize>
        + Unsigned,
    u64: TryFrom<I>,
    usize: TryFrom<I>,
    <usize as TryFrom<I>>::Error: fmt::Debug,
{
    pub fn non_dyn_fields_size() -> usize {
        mem::size_of::<usize>()
        + mem::size_of::<usize>()
        + mem::size_of::<usize>()
    }
    pub fn size_in_account(
        capacity_indices: usize,
        capacity_values: usize,
    ) -> Result<usize, HashSetError> {
        let dyn_fields_size = Self::non_dyn_fields_size();
        let next_value_index_size = mem::size_of::<usize>();
        let indices_size_unaligned = mem::size_of::<Option<I>>() * capacity_indices;
        let indices_size = indices_size_unaligned + mem::align_of::<usize>()
            - (indices_size_unaligned % mem::align_of::<usize>());
        let values_size_unaligned = mem::size_of::<Option<HashSetCell>>() * capacity_values;
        let values_size = values_size_unaligned + mem::align_of::<usize>()
            - (values_size_unaligned % mem::align_of::<usize>());
        Ok(dyn_fields_size + next_value_index_size + indices_size + values_size)
    }
    pub fn capacity_indices(
        capacity_elements: usize,
        load_factor: f64,
    ) -> Result<f64, HashSetError> {
        let capacity_elements =
            u32::try_from(capacity_elements).map_err(|_| HashSetError::IntegerOverflow)?;
        let minimum = f64::from(capacity_elements) / load_factor;
        Ok(find_next_prime(minimum))
    }
    pub fn new(
        capacity_indices: usize,
        capacity_values: usize,
        sequence_threshold: usize,
    ) -> Result<Self, HashSetError> {
        let layout = Layout::new::<usize>();
        let next_value_index = unsafe { alloc::alloc(layout) as *mut usize };
        if next_value_index.is_null() {
            handle_alloc_error(layout);
        }
        unsafe {
            *next_value_index = 0;
        }
        let layout = Layout::array::<Option<I>>(capacity_indices).unwrap();
        let indices_ptr = unsafe { alloc::alloc(layout) as *mut Option<I> };
        if indices_ptr.is_null() {
            handle_alloc_error(layout);
        }
        let indices = NonNull::new(indices_ptr).unwrap();
        for i in 0..capacity_indices {
            unsafe {
                std::ptr::write(indices_ptr.add(i), None);
            }
        }
        let layout = Layout::array::<Option<HashSetCell>>(capacity_values).unwrap();
        let values_ptr = unsafe { alloc::alloc(layout) as *mut Option<HashSetCell> };
        if values_ptr.is_null() {
            handle_alloc_error(layout);
        }
        let values = NonNull::new(values_ptr).unwrap();
        for i in 0..capacity_values {
            unsafe {
                std::ptr::write(values_ptr.add(i), None);
            }
        }
        Ok(HashSet {
            next_value_index,
            sequence_threshold,
            capacity_indices,
            capacity_values,
            indices,
            values,
        })
    }
    pub unsafe fn from_bytes_copy(bytes: &mut [u8]) -> Result<Self, HashSetError> {
        if bytes.len() < Self::non_dyn_fields_size() {
            return Err(HashSetError::BufferSize(
                Self::non_dyn_fields_size(),
                bytes.len(),
            ));
        }
        let capacity_indices = usize::from_ne_bytes(bytes[0..8].try_into().unwrap());
        let capacity_values = usize::from_ne_bytes(bytes[8..16].try_into().unwrap());
        let sequence_threshold = usize::from_ne_bytes(bytes[16..24].try_into().unwrap());
        let expected_size = Self::size_in_account(capacity_indices, capacity_values)?;
        if bytes.len() != expected_size {
            return Err(HashSetError::BufferSize(expected_size, bytes.len()));
        }
        let next_value_index_layout = Layout::new::<usize>();
        let next_value_index = unsafe { alloc::alloc(next_value_index_layout) as *mut usize };
        if next_value_index.is_null() {
            handle_alloc_error(next_value_index_layout);
        }
        unsafe {
            *next_value_index = usize::from_ne_bytes(bytes[24..32].try_into().unwrap());
        }
        let indices_layout = Layout::array::<Option<I>>(capacity_indices).unwrap();
        let indices_dst_ptr = unsafe { alloc::alloc(indices_layout) as *mut Option<I> };
        if indices_dst_ptr.is_null() {
            handle_alloc_error(indices_layout);
        }
        let indices = NonNull::new(indices_dst_ptr).unwrap();
        let indices_size = indices_layout.size() + mem::align_of::<usize>()
            - (indices_layout.size() % mem::size_of::<usize>());
        for i in 0..capacity_indices {
            std::ptr::write(indices_dst_ptr.add(i), None);
        }
        let offset = Self::non_dyn_fields_size() + mem::size_of::<usize>();
        let indices_src_ptr = bytes.as_ptr().add(offset) as *const Option<I>;
        std::ptr::copy(indices_src_ptr, indices_dst_ptr, capacity_indices);
        let values_layout = Layout::array::<Option<HashSetCell>>(capacity_values).unwrap();
        let values_dst_ptr = unsafe { alloc::alloc(values_layout) as *mut Option<HashSetCell> };
        if values_dst_ptr.is_null() {
            handle_alloc_error(values_layout);
        }
        let values = NonNull::new(values_dst_ptr).unwrap();
        for i in 0..capacity_values {
            std::ptr::write(values_dst_ptr.add(i), None);
        }
        let offset = offset + indices_size;
        let values_src_ptr = bytes.as_ptr().add(offset) as *const Option<HashSetCell>;
        std::ptr::copy(values_src_ptr, values_dst_ptr, capacity_values);
        Ok(Self {
            capacity_indices,
            capacity_values,
            next_value_index,
            sequence_threshold,
            indices,
            values,
        })
    }
    fn insert_into_occupied_cell(
        &mut self,
        value_index: usize,
        value: &BigUint,
        current_sequence_number: usize,
    ) -> Result<bool, HashSetError> {
        let value_bucket = unsafe { &mut *self.values.as_ptr().add(value_index) };
        match value_bucket {
            Some(value_bucket) => {
                if let Some(element_sequence_number) = value_bucket.sequence_number {
                    if current_sequence_number >= element_sequence_number {
                        *value_bucket = HashSetCell {
                            value: bigint_to_be_bytes_array(value)?,
                            sequence_number: None,
                        };
                        return Ok(true);
                    }
                }
                if &BigUint::from_be_bytes(value_bucket.value.as_slice()) == value {
                    return Err(HashSetError::ElementAlreadyExists);
                }
            }
            None => unreachable!(),
        }
        Ok(false)
    }
    pub fn insert(
        &mut self,
        value: &BigUint,
        current_sequence_number: usize,
    ) -> Result<(), HashSetError> {
        for i in 0..self.capacity_values {
            let probe_index = (value.clone() + i.to_biguint().unwrap() * i.to_biguint().unwrap())
                % self.capacity_values.to_biguint().unwrap();
            let probe_index = probe_index.to_usize().unwrap();
            let index_bucket = unsafe { &mut *self.indices.as_ptr().add(probe_index) };
            match index_bucket {
                Some(value_index) => {
                    let value_index =
                        usize::try_from(*value_index).map_err(|_| HashSetError::UsizeConv)?;
                    if self.insert_into_occupied_cell(
                        value_index,
                        value,
                        current_sequence_number,
                    )? {
                        return Ok(());
                    }
                }
                None => {
                    let value_bucket =
                        unsafe { &mut *self.values.as_ptr().add(*self.next_value_index) };
                    *index_bucket = Some(
                        I::try_from(unsafe { *self.next_value_index })
                            .map_err(|_| HashSetError::IntegerOverflow)?,
                    );
                    *value_bucket = Some(HashSetCell {
                        value: bigint_to_be_bytes_array(value)?,
                        sequence_number: None,
                    });
                    unsafe {
                        *self.next_value_index =
                            if *self.next_value_index < self.capacity_values - 1 {
                                *self.next_value_index + 1
                            } else {
                                0
                            };
                    }
                    return Ok(());
                }
            }
        }
        Err(HashSetError::Full)
    }
    pub fn find_element(
        &self,
        value: &BigUint,
        current_sequence_number: Option<usize>,
    ) -> Result<Option<(&mut HashSetCell, I)>, HashSetError> {
        for i in 0..self.capacity_values {
            let probe_index = (value.clone() + i.to_biguint().unwrap() * i.to_biguint().unwrap())
                % self.capacity_values.to_biguint().unwrap();
            let probe_index = probe_index.to_usize().unwrap();
            let index_bucket = unsafe { &*self.indices.as_ptr().add(probe_index) };
            match index_bucket {
                Some(value_index) => {
                    let value_bucket = self.by_value_index(
                        usize::try_from(*value_index).map_err(|_| HashSetError::UsizeConv)?,
                        current_sequence_number,
                    );
                    if let Some(value_bucket) = value_bucket {
                        if &value_bucket.value_biguint() == value {
                            return Ok(Some((value_bucket, *value_index)));
                        }
                    }
                }
                None => {
                    return Ok(None);
                }
            }
        }
        Ok(None)
    }
    pub fn first(
        &self,
        current_sequence_number: usize,
    ) -> Result<Option<&mut HashSetCell>, HashSetError> {
        for i in 0..self.capacity_values {
            let value_bucket = unsafe { &mut *self.values.as_ptr().add(i) };
            if let Some(value_bucket) = value_bucket {
                if let Some(element_sequence_number) = value_bucket.sequence_number {
                    if current_sequence_number < element_sequence_number {
                        return Ok(Some(value_bucket));
                    }
                } else {
                    return Ok(Some(value_bucket));
                }
            }
        }
        Ok(None)
    }
    pub fn first_no_seq(&self) -> Result<Option<(HashSetCell, u16)>, HashSetError> {
        for i in 0..self.capacity_values {
            let value_bucket = unsafe { &mut *self.values.as_ptr().add(i) };
            if let Some(value_bucket) = value_bucket {
                if value_bucket.sequence_number.is_none() {
                    return Ok(Some((*value_bucket, i as u16)));
                }
            }
        }
        Ok(None)
    }
    pub fn by_value_index(
        &self,
        value_index: usize,
        current_sequence_number: Option<usize>,
    ) -> Option<&mut HashSetCell> {
        let value_bucket = unsafe { &mut *self.values.as_ptr().add(value_index) };
        if let Some(value_bucket) = value_bucket {
            match current_sequence_number {
                Some(current_sequence_number) => {
                    match value_bucket.sequence_number {
                        Some(element_sequence_number) => {
                            if current_sequence_number < element_sequence_number {
                                return Some(value_bucket);
                            }
                        }
                        None => return Some(value_bucket),
                    }
                    if let Some(element_sequence_number) = value_bucket.sequence_number {
                        if current_sequence_number < element_sequence_number {
                            return Some(value_bucket);
                        }
                    }
                }
                None => {
                    if value_bucket.sequence_number.is_none() {
                        return Some(value_bucket);
                    }
                }
            }
        }
        None
    }
    pub fn contains(&self, value: &BigUint, sequence_number: usize) -> Result<bool, HashSetError> {
        let element = self.find_element(value, Some(sequence_number))?;
        Ok(element.is_some())
    }
    pub fn mark_with_sequence_number(
        &mut self,
        value: &BigUint,
        sequence_number: usize,
    ) -> Result<(), HashSetError> {
        let element = self.find_element(value, None)?;
        match element {
            Some((element, _)) => {
                element.sequence_number = Some(sequence_number + self.sequence_threshold);
                Ok(())
            }
            None => Err(HashSetError::ElementDoesNotExist),
        }
    }
    pub fn iter(&self) -> HashSetIterator<I> {
        HashSetIterator {
            hash_set: self,
            current: 0,
        }
    }
}
impl<I> Drop for HashSet<I>
where
    I: Bounded
        + CheckedAdd
        + CheckedSub
        + Clone
        + Copy
        + fmt::Display
        + From<u8>
        + PartialEq
        + PartialOrd
        + ToBigUint
        + TryFrom<u64>
        + TryFrom<usize>
        + Unsigned,
    usize: TryFrom<I>,
    <usize as TryFrom<I>>::Error: fmt::Debug,
{
    fn drop(&mut self) {
        unsafe {
            let layout = Layout::new::<usize>();
            alloc::dealloc(self.next_value_index as *mut u8, layout);
            let layout = Layout::array::<Option<I>>(self.capacity_indices).unwrap();
            alloc::dealloc(self.indices.as_ptr() as *mut u8, layout);
            let layout = Layout::array::<Option<HashSetCell>>(self.capacity_values).unwrap();
            alloc::dealloc(self.values.as_ptr() as *mut u8, layout);
        }
    }
}
pub struct HashSetIterator<'a, I>
where
    I: Bounded
        + CheckedAdd
        + CheckedSub
        + Clone
        + Copy
        + fmt::Display
        + From<u8>
        + PartialEq
        + PartialOrd
        + ToBigUint
        + TryFrom<u64>
        + TryFrom<usize>
        + Unsigned,
    usize: TryFrom<I>,
    <usize as TryFrom<I>>::Error: fmt::Debug,
{
    hash_set: &'a HashSet<I>,
    current: usize,
}
impl<'a, I> Iterator for HashSetIterator<'a, I>
where
    I: Bounded
        + CheckedAdd
        + CheckedSub
        + Clone
        + Copy
        + fmt::Display
        + From<u8>
        + PartialEq
        + PartialOrd
        + ToBigUint
        + TryFrom<u64>
        + TryFrom<usize>
        + Unsigned,
    usize: TryFrom<I>,
    <usize as TryFrom<I>>::Error: fmt::Debug,
{
    type Item = (usize, &'a HashSetCell);
    fn next(&mut self) -> Option<Self::Item> {
        if self.current < self.hash_set.capacity_values {
            let element_index = self.current;
            let element = unsafe { &*self.hash_set.values.as_ptr().add(element_index) };
            self.current += 1;
            element.as_ref().map(|element| (element_index, element))
        } else {
            None
        }
    }
}
#[cfg(test)]
mod test {
    use ark_bn254::Fr;
    use ark_ff::UniformRand;
    use rand::thread_rng;
    use super::*;
    #[test]
    fn test_find_next_prime() {
        assert_eq!(find_next_prime(0.0), 2.0);
        assert_eq!(find_next_prime(2.0), 2.0);
        assert_eq!(find_next_prime(3.0), 3.0);
        assert_eq!(find_next_prime(4.0), 5.0);
        assert_eq!(find_next_prime(10.0), 11.0);
        assert_eq!(find_next_prime(28.0), 29.0);
        assert_eq!(find_next_prime(100.0), 101.0);
        assert_eq!(find_next_prime(1000.0), 1009.0);
        assert_eq!(find_next_prime(102.0), 103.0);
        assert_eq!(find_next_prime(105.0), 107.0);
        assert_eq!(find_next_prime(7900.0), 7901.0);
        assert_eq!(find_next_prime(7907.0), 7907.0);
    }
    #[test]
    fn test_capacity_cells() {
        assert_eq!(HashSet::<u16>::capacity_indices(256, 0.5).unwrap(), 521.0);
        assert_eq!(HashSet::<u16>::capacity_indices(4800, 0.7).unwrap(), 6857.0);
    }
    #[test]
    fn test_hash_set_manual() {
        let mut hs = HashSet::<u16>::new(521, 256, 4).unwrap();
        let element_1_1 = 1.to_biguint().unwrap();
        hs.insert(&element_1_1, 0).unwrap();
        hs.mark_with_sequence_number(&element_1_1, 1).unwrap();
        assert_eq!(hs.contains(&element_1_1, 1).unwrap(), true);
        assert!(matches!(
            hs.insert(&element_1_1, 1),
            Err(HashSetError::ElementAlreadyExists)
        ));
        let element_2_3 = 3.to_biguint().unwrap();
        let element_2_6 = 6.to_biguint().unwrap();
        let element_2_8 = 8.to_biguint().unwrap();
        let element_2_9 = 9.to_biguint().unwrap();
        hs.insert(&element_2_3, 1).unwrap();
        hs.insert(&element_2_6, 1).unwrap();
        hs.insert(&element_2_8, 1).unwrap();
        hs.insert(&element_2_9, 1).unwrap();
        assert_eq!(hs.contains(&element_2_3, 2).unwrap(), true);
        assert_eq!(hs.contains(&element_2_6, 2).unwrap(), true);
        assert_eq!(hs.contains(&element_2_8, 2).unwrap(), true);
        assert_eq!(hs.contains(&element_2_9, 2).unwrap(), true);
        hs.mark_with_sequence_number(&element_2_3, 2).unwrap();
        hs.mark_with_sequence_number(&element_2_6, 2).unwrap();
        hs.mark_with_sequence_number(&element_2_8, 2).unwrap();
        hs.mark_with_sequence_number(&element_2_9, 2).unwrap();
        assert!(matches!(
            hs.insert(&element_2_3, 2),
            Err(HashSetError::ElementAlreadyExists)
        ));
        assert!(matches!(
            hs.insert(&element_2_6, 2),
            Err(HashSetError::ElementAlreadyExists)
        ));
        assert!(matches!(
            hs.insert(&element_2_8, 2),
            Err(HashSetError::ElementAlreadyExists)
        ));
        assert!(matches!(
            hs.insert(&element_2_9, 2),
            Err(HashSetError::ElementAlreadyExists)
        ));
        let element_3_11 = 11.to_biguint().unwrap();
        let element_3_13 = 13.to_biguint().unwrap();
        let element_3_21 = 21.to_biguint().unwrap();
        let element_3_29 = 29.to_biguint().unwrap();
        hs.insert(&element_3_11, 2).unwrap();
        hs.insert(&element_3_13, 2).unwrap();
        hs.insert(&element_3_21, 2).unwrap();
        hs.insert(&element_3_29, 2).unwrap();
        assert_eq!(hs.contains(&element_3_11, 3).unwrap(), true);
        assert_eq!(hs.contains(&element_3_13, 3).unwrap(), true);
        assert_eq!(hs.contains(&element_3_21, 3).unwrap(), true);
        assert_eq!(hs.contains(&element_3_29, 3).unwrap(), true);
        hs.mark_with_sequence_number(&element_3_11, 3).unwrap();
        hs.mark_with_sequence_number(&element_3_13, 3).unwrap();
        hs.mark_with_sequence_number(&element_3_21, 3).unwrap();
        hs.mark_with_sequence_number(&element_3_29, 3).unwrap();
        assert!(matches!(
            hs.insert(&element_3_11, 3),
            Err(HashSetError::ElementAlreadyExists)
        ));
        assert!(matches!(
            hs.insert(&element_3_13, 3),
            Err(HashSetError::ElementAlreadyExists)
        ));
        assert!(matches!(
            hs.insert(&element_3_21, 3),
            Err(HashSetError::ElementAlreadyExists)
        ));
        assert!(matches!(
            hs.insert(&element_3_29, 3),
            Err(HashSetError::ElementAlreadyExists)
        ));
        let element_4_93 = 93.to_biguint().unwrap();
        let element_4_65 = 64.to_biguint().unwrap();
        let element_4_72 = 72.to_biguint().unwrap();
        let element_4_15 = 15.to_biguint().unwrap();
        hs.insert(&element_4_93, 3).unwrap();
        hs.insert(&element_4_65, 3).unwrap();
        hs.insert(&element_4_72, 3).unwrap();
        hs.insert(&element_4_15, 3).unwrap();
        assert_eq!(hs.contains(&element_4_93, 4).unwrap(), true);
        assert_eq!(hs.contains(&element_4_65, 4).unwrap(), true);
        assert_eq!(hs.contains(&element_4_72, 4).unwrap(), true);
        assert_eq!(hs.contains(&element_4_15, 4).unwrap(), true);
        hs.mark_with_sequence_number(&element_4_93, 4).unwrap();
        hs.mark_with_sequence_number(&element_4_65, 4).unwrap();
        hs.mark_with_sequence_number(&element_4_72, 4).unwrap();
        hs.mark_with_sequence_number(&element_4_15, 4).unwrap();
        assert!(matches!(
            hs.insert(&element_1_1, 4),
            Err(HashSetError::ElementAlreadyExists)
        ));
        assert!(matches!(
            hs.insert(&element_2_3, 5),
            Err(HashSetError::ElementAlreadyExists)
        ));
        assert!(matches!(
            hs.insert(&element_2_6, 5),
            Err(HashSetError::ElementAlreadyExists)
        ));
        assert!(matches!(
            hs.insert(&element_2_8, 5),
            Err(HashSetError::ElementAlreadyExists)
        ));
        assert!(matches!(
            hs.insert(&element_2_9, 5),
            Err(HashSetError::ElementAlreadyExists)
        ));
        hs.insert(&element_1_1, 5).unwrap();
        hs.insert(&element_2_3, 6).unwrap();
        hs.insert(&element_2_6, 6).unwrap();
        hs.insert(&element_2_8, 6).unwrap();
        hs.insert(&element_2_9, 6).unwrap();
    }
    #[test]
    fn test_hash_set_random() {
        let mut hs = HashSet::<u16>::new(6857, 4800, 2400).unwrap();
        assert_eq!(hs.first(0).unwrap(), None);
        let mut rng = thread_rng();
        let nullifiers: [BigUint; 2400] =
            std::array::from_fn(|_| BigUint::from(Fr::rand(&mut rng)));
        for (seq, nullifier) in nullifiers.iter().enumerate() {
            assert_eq!(hs.contains(&nullifier, seq).unwrap(), false);
            hs.insert(&nullifier, seq as usize).unwrap();
            assert_eq!(hs.contains(&nullifier, seq).unwrap(), true);
            assert_eq!(
                hs.find_element(&nullifier, Some(seq))
                    .unwrap()
                    .unwrap()
                    .0
                    .clone(),
                HashSetCell {
                    value: bigint_to_be_bytes_array(&nullifier).unwrap(),
                    sequence_number: None,
                }
            );
            hs.mark_with_sequence_number(&nullifier, seq).unwrap();
            assert_eq!(
                hs.find_element(&nullifier, Some(seq))
                    .unwrap()
                    .unwrap()
                    .0
                    .clone(),
                HashSetCell {
                    value: bigint_to_be_bytes_array(&nullifier).unwrap(),
                    sequence_number: Some(2400 + seq)
                }
            );
            assert!(matches!(
                hs.insert(&nullifier, seq as usize + 1),
                Err(HashSetError::ElementAlreadyExists),
            ));
        }
        for (i, element) in hs.iter() {
            assert_eq!(element.value_biguint(), nullifiers[i]);
        }
        for seq in 0..2399 {
            assert_eq!(
                hs.first(seq).unwrap().unwrap().value_biguint(),
                nullifiers[0]
            );
        }
        for (seq, nullifier) in nullifiers.iter().enumerate() {
            assert_eq!(
                &hs.first(2399 + seq).unwrap().unwrap().value_biguint(),
                nullifier
            );
        }
        for (seq, nullifier) in nullifiers.iter().enumerate() {
            hs.insert(&nullifier, 2400 + seq as usize).unwrap();
        }
    }
}