/*
 * SPDX-FileCopyrightText: 2023 Inria
 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
 *
 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
 */

#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
#![cfg_attr(
    all(feature = "aarch64_prefetch", target_arch = "aarch64"),
    feature(stdarch_aarch64_prefetch)
)]
#![cfg_attr(feature = "iter_advance_by", feature(iter_advance_by))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(unconditional_recursion)]
#![allow(clippy::duplicated_attributes)]
#![allow(clippy::len_without_is_empty)]
#![allow(clippy::type_complexity)]
#![allow(clippy::too_many_arguments)]

pub mod array;
pub mod bal_paren;
pub mod bits;
pub mod dict;
pub mod func;
pub mod list;
pub mod rank_sel;
pub mod traits;
pub mod utils;

#[cfg(feature = "cli")]
pub mod cli;

#[cfg(feature = "fuzz")]
pub mod fuzz;

/// Imports the most common items.
///
/// Note that [`bit_field_slice`] and [`indexed_dict`] are not included in
/// the prelude, as they may cause ambiguities in some contexts.
///
/// [`bit_field_slice`]: crate::traits::bit_field_slice
/// [`indexed_dict`]: crate::traits::indexed_dict
pub mod prelude {
    pub use crate::array::*;
    pub use crate::bal_paren::*;
    pub use crate::bit_field_vec;
    pub use crate::bit_vec;
    pub use crate::bits::*;
    pub use crate::dict::*;
    pub use crate::func::*;
    pub use crate::list::*;
    pub use crate::rank_sel::*;
    pub use crate::rank_small;
    pub use crate::traits::bit_field_slice;
    pub use crate::traits::bit_vec_ops;
    pub use crate::traits::indexed_dict;
    pub use crate::traits::{bit_vec_ops::BitLength, iter::*, rank_sel::*};
}

#[ambassador::delegatable_trait_remote]
pub(crate) trait Index<Idx> {
    type Output;
    fn index(&self, index: Idx) -> &Self::Output;
}

/// Parallel iterators performing very fast operations, such as [zeroing a
/// bit] vector, should pass this argument
/// to
/// [IndexedParallelIterator::with_min_len].
///
/// [zeroing a bit]: crate::traits::BitVecOpsMut::reset
/// [IndexedParallelIterator::with_min_len]: `rayon::iter::IndexedParallelIterator::with_min_len`
pub const RAYON_MIN_LEN: usize = 100_000;

macro_rules! panic_if_out_of_bounds {
    ($index: expr, $len: expr) => {
        if $index >= $len {
            panic!("Index out of bounds: {} >= {}", $index, $len)
        }
    };
}

pub(crate) use panic_if_out_of_bounds;

macro_rules! panic_if_value {
    ($value: expr, $mask: expr, $bit_width: expr) => {
        if $value & $mask != $value {
            panic!("Value {} does not fit in {} bits", $value, $bit_width);
        }
    };
}
pub(crate) use panic_if_value;

macro_rules! debug_assert_bounds {
    ($index: expr, $len: expr) => {
        debug_assert!(
            $index < $len || ($index == 0 && $len == 0),
            "Index out of bounds: {} >= {}",
            $index,
            $len
        );
    };
}

pub(crate) use debug_assert_bounds;

/// Initializes the `env_logger` logger with a custom format including
/// timestamps with elapsed time since initialization.
pub fn init_env_logger() -> anyhow::Result<()> {
    use jiff::{
        SpanRound,
        fmt::friendly::{Designator, Spacing, SpanPrinter},
    };
    use std::io::Write;

    let mut builder =
        env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"));

    let start = std::time::Instant::now();
    let printer = SpanPrinter::new()
        .spacing(Spacing::None)
        .designator(Designator::Compact);
    let span_round = SpanRound::new()
        .largest(jiff::Unit::Day)
        .smallest(jiff::Unit::Millisecond)
        .days_are_24_hours();

    builder.format(move |buf, record| {
        let Ok(ts) = jiff::Timestamp::try_from(std::time::SystemTime::now()) else {
            return Err(std::io::Error::other("Failed to get timestamp"));
        };
        let style = buf.default_level_style(record.level());
        let elapsed = start.elapsed();
        let span = jiff::Span::new()
            .seconds(elapsed.as_secs() as i64)
            .milliseconds(elapsed.subsec_millis() as i64);
        let span = span.round(span_round).expect("Failed to round span");
        writeln!(
            buf,
            "{} {} {style}{}{style:#} [{:?}] {} - {}",
            ts.strftime("%F %T%.3f"),
            printer.span_to_string(&span),
            record.level(),
            std::thread::current().id(),
            record.target(),
            record.args()
        )
    });
    builder.init();
    Ok(())
}
