#![doc = include_str!("../doc/order.md")]
use crate::{
	index::{
		BitEnd,
		BitIdx,
		BitMask,
		BitPos,
		BitSel,
	},
	mem::{
		bits_of,
		BitRegister,
	},
};
#[doc = include_str!("../doc/order/BitOrder.md")]
pub unsafe trait BitOrder: 'static {
	fn at<R>(index: BitIdx<R>) -> BitPos<R>
	where R: BitRegister;
	#[inline]
	fn select<R>(index: BitIdx<R>) -> BitSel<R>
	where R: BitRegister {
		Self::at::<R>(index).select()
	}
	#[inline]
	fn mask<R>(
		from: impl Into<Option<BitIdx<R>>>,
		upto: impl Into<Option<BitEnd<R>>>,
	) -> BitMask<R>
	where
		R: BitRegister,
	{
		let (from, upto) = match (from.into(), upto.into()) {
			(None, None) => return BitMask::ALL,
			(Some(from), None) => (from, BitEnd::MAX),
			(None, Some(upto)) => (BitIdx::MIN, upto),
			(Some(from), Some(upto)) => (from, upto),
		};
		from.range(upto).map(Self::select::<R>).sum()
	}
}
#[doc = include_str!("../doc/order/Lsb0.md")]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Lsb0;
#[doc = include_str!("../doc/order/Msb0.md")]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Msb0;
unsafe impl BitOrder for Lsb0 {
	#[inline]
	fn at<R>(index: BitIdx<R>) -> BitPos<R>
	where R: BitRegister {
		unsafe { BitPos::new_unchecked(index.into_inner()) }
	}
	#[inline]
	fn select<R>(index: BitIdx<R>) -> BitSel<R>
	where R: BitRegister {
		unsafe { BitSel::new_unchecked(R::ONE << index.into_inner()) }
	}
	#[inline]
	fn mask<R>(
		from: impl Into<Option<BitIdx<R>>>,
		upto: impl Into<Option<BitEnd<R>>>,
	) -> BitMask<R>
	where
		R: BitRegister,
	{
		let from = from.into().unwrap_or(BitIdx::MIN).into_inner();
		let upto = upto.into().unwrap_or(BitEnd::MAX).into_inner();
		debug_assert!(
			from <= upto,
			"Ranges must run from low index ({}) to high ({})",
			from,
			upto,
		);
		let ct = upto - from;
		if ct == bits_of::<R>() as u8 {
			return BitMask::ALL;
		}
		BitMask::new(!(R::ALL << ct) << from)
	}
}
unsafe impl BitOrder for Msb0 {
	#[inline]
	fn at<R>(index: BitIdx<R>) -> BitPos<R>
	where R: BitRegister {
		unsafe { BitPos::new_unchecked(R::MASK - index.into_inner()) }
	}
	#[inline]
	fn select<R>(index: BitIdx<R>) -> BitSel<R>
	where R: BitRegister {
		let msbit: R = R::ONE << R::MASK;
		unsafe { BitSel::new_unchecked(msbit >> index.into_inner()) }
	}
	#[inline]
	fn mask<R>(
		from: impl Into<Option<BitIdx<R>>>,
		upto: impl Into<Option<BitEnd<R>>>,
	) -> BitMask<R>
	where
		R: BitRegister,
	{
		let from = from.into().unwrap_or(BitIdx::MIN).into_inner();
		let upto = upto.into().unwrap_or(BitEnd::MAX).into_inner();
		debug_assert!(
			from <= upto,
			"ranges must run from low index ({}) to high ({})",
			from,
			upto,
		);
		let ct = upto - from;
		if ct == bits_of::<R>() as u8 {
			return BitMask::ALL;
		}
		BitMask::new(!(R::ALL >> ct) >> from)
	}
}
#[cfg(target_endian = "little")]
#[doc = include_str!("../doc/order/LocalBits.md")]
pub use self::Lsb0 as LocalBits;
#[cfg(target_endian = "big")]
#[doc = include_str!("../doc/order/LocalBits.md")]
pub use self::Msb0 as LocalBits;
#[cfg(not(any(target_endian = "big", target_endian = "little")))]
compile_fail!(
	"This architecture is not supported! Please consider filing an issue"
);
#[inline]
#[cfg(not(tarpaulin_include))]
#[doc = include_str!("../doc/order/verify.md")]
pub fn verify<O>(verbose: bool)
where O: BitOrder {
	verify_for_type::<u8, O>(verbose);
	verify_for_type::<u16, O>(verbose);
	verify_for_type::<u32, O>(verbose);
	verify_for_type::<usize, O>(verbose);
	#[cfg(target_pointer_width = "64")]
	verify_for_type::<u64, O>(verbose);
}
#[cfg(miri)]
pub fn verify_for_type<R, O>(_: bool)
where
	R: BitRegister,
	O: BitOrder,
{
}
#[cfg(not(miri))]
#[doc = include_str!("../doc/order/verify_for_type.md")]
pub fn verify_for_type<R, O>(verbose: bool)
where
	R: BitRegister,
	O: BitOrder,
{
	use core::any::type_name;
	let mut accum = BitMask::<R>::ZERO;
	let ord_name = type_name::<O>();
	let reg_name = type_name::<R>();
	for n in 0 .. bits_of::<R>() as u8 {
		let idx = unsafe { BitIdx::<R>::new_unchecked(n) };
		let pos = O::at::<R>(idx);
		if verbose {
			#[cfg(feature = "std")]
			println!(
				"`<{} as BitOrder>::at::<{}>({})` produces {}",
				ord_name,
				reg_name,
				n,
				pos.into_inner(),
			);
		}
		assert!(
			pos.into_inner() < bits_of::<R>() as u8,
			"Error when verifying the implementation of `BitOrder` for `{}`: \
			 Index {} produces a bit position ({}) that exceeds the type width \
			 {}",
			ord_name,
			n,
			pos.into_inner(),
			bits_of::<R>(),
		);
		let sel = O::select::<R>(idx);
		if verbose {
			#[cfg(feature = "std")]
			println!(
				"`<{} as BitOrder>::select::<{}>({})` produces {:b}",
				ord_name, reg_name, n, sel,
			);
		}
		assert_eq!(
			sel.into_inner().count_ones(),
			1,
			"Error when verifying the implementation of `BitOrder` for `{}`: \
			 Index {} produces a bit selector ({:b}) that is not a one-hot mask",
			ord_name,
			n,
			sel,
		);
		let shl = pos.select();
		assert_eq!(
			sel,
			shl,
			"Error when verifying the implementation of `BitOrder` for `{}`: \
			 Index {} produces a bit selector ({:b}) that is not equal to `1 \
			 << {}` ({:b})",
			ord_name,
			n,
			sel,
			pos.into_inner(),
			shl,
		);
		assert!(
			!accum.test(sel),
			"Error when verifying the implementation of `BitOrder` for `{}`: \
			 Index {} produces a bit position ({}) that has already been \
			 produced by a prior index",
			ord_name,
			n,
			pos.into_inner(),
		);
		accum.insert(sel);
		if verbose {
			#[cfg(feature = "std")]
			println!(
				"`<{} as BitOrder>::at::<{}>({})` accumulates  {:b}",
				ord_name, reg_name, n, accum,
			);
		}
	}
	assert_eq!(
		accum,
		BitMask::ALL,
		"Error when verifying the implementation of `BitOrder` for `{}`: The \
		 bit positions marked with a `0` here were never produced from an \
		 index, despite all possible indices being passed in for translation: \
		 {:b}",
		ord_name,
		accum,
	);
	for from in BitIdx::<R>::range_all() {
		for upto in BitEnd::<R>::range_from(from) {
			let mask = O::mask(from, upto);
			let check = from
				.range(upto)
				.map(O::at)
				.map(BitPos::select)
				.sum::<BitMask<R>>();
			assert_eq!(
				mask,
				check,
				"Error when verifying the implementation of `BitOrder` for \
				 `{o}`: `{o}::mask::<{m}>({f}, {u})` produced {bad:b}, but \
				 expected {good:b}",
				o = ord_name,
				m = reg_name,
				f = from,
				u = upto,
				bad = mask,
				good = check,
			);
		}
	}
}
#[cfg(test)]
pub struct HiLo;
#[cfg(test)]
unsafe impl BitOrder for HiLo {
	fn at<R>(index: BitIdx<R>) -> BitPos<R>
	where R: BitRegister {
		BitPos::new(index.into_inner() ^ 4).unwrap()
	}
}
#[cfg(test)]
mod tests {
	use super::*;
	#[test]
	fn default_impl() {
		assert_eq!(Lsb0::mask(None, None), BitMask::<u8>::ALL);
		assert_eq!(Msb0::mask(None, None), BitMask::<u8>::ALL);
		assert_eq!(HiLo::mask(None, None), BitMask::<u8>::ALL);
		assert_eq!(
			HiLo::mask(None, BitEnd::<u8>::new(3).unwrap()),
			BitMask::new(0b0111_0000),
		);
		assert_eq!(
			HiLo::mask(BitIdx::<u8>::new(3).unwrap(), None),
			BitMask::new(0b1000_1111),
		);
	}
	mod lsb0 {
		use super::*;
		#[test]
		fn verify_u8() {
			verify_for_type::<u8, Lsb0>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(not(tarpaulin))]
		fn verify_u16() {
			verify_for_type::<u16, Lsb0>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(not(tarpaulin))]
		fn verify_u32() {
			verify_for_type::<u32, Lsb0>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(all(target_pointer_width = "64", not(tarpaulin)))]
		fn verify_u64() {
			verify_for_type::<u64, Lsb0>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(not(tarpaulin))]
		fn verify_usize() {
			verify_for_type::<usize, Lsb0>(cfg!(feature = "verbose"));
		}
	}
	mod msb0 {
		use super::*;
		#[test]
		fn verify_u8() {
			verify_for_type::<u8, Msb0>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(not(tarpaulin))]
		fn verify_u16() {
			verify_for_type::<u16, Msb0>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(not(tarpaulin))]
		fn verify_u32() {
			verify_for_type::<u32, Msb0>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(all(target_pointer_width = "64", not(tarpaulin)))]
		fn verify_u64() {
			verify_for_type::<u64, Msb0>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(not(tarpaulin))]
		fn verify_usize() {
			verify_for_type::<usize, Msb0>(cfg!(feature = "verbose"));
		}
	}
	mod hilo {
		use super::*;
		#[test]
		fn verify_u8() {
			verify_for_type::<u8, HiLo>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(not(tarpaulin))]
		fn verify_u16() {
			verify_for_type::<u16, HiLo>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(not(tarpaulin))]
		fn verify_u32() {
			verify_for_type::<u32, HiLo>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(all(target_pointer_width = "64", not(tarpaulin)))]
		fn verify_u64() {
			verify_for_type::<u64, HiLo>(cfg!(feature = "verbose"));
		}
		#[test]
		#[cfg(not(tarpaulin))]
		fn verify_usize() {
			verify_for_type::<usize, HiLo>(cfg!(feature = "verbose"));
		}
	}
}