(u8)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Display)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
pub enum EthVersion {
    /// The `eth` protocol version 66.
    Eth66 = 66,
    /// The `eth` protocol version 67.
    Eth67 = 67,
    /// The `eth` protocol version 68.
    Eth68 = 68,
    /// The `eth` protocol version 69.
    Eth69 = 69,
    /// The `eth` protocol version 70.
    Eth70 = 70,
    /// The `eth` protocol version 71.
    Eth71 = 71,
    /// The `eth` protocol version 72.
    Eth72 = 72,
}

impl EthVersion {
    /// The latest known eth version
    pub const LATEST: Self = Self::Eth69;

    /// All known eth versions
    pub const ALL_VERSIONS: &'static [Self] = &[Self::Eth69, Self::Eth68, Self::Eth67, Self::Eth66];

    /// Returns true if the version is eth/66
    pub const fn is_eth66(&self) -> bool {
        matches!(self, Self::Eth66)
    }

    /// Returns true if the version is eth/67
    pub const fn is_eth67(&self) -> bool {
        matches!(self, Self::Eth67)
    }

    /// Returns true if the version is eth/68
    pub const fn is_eth68(&self) -> bool {
        matches!(self, Self::Eth68)
    }

    /// Returns true if the version carries eth/68 transaction announcement metadata.
    pub const fn has_eth68_metadata(&self) -> bool {
        matches!(self, Self::Eth68 | Self::Eth69 | Self::Eth70 | Self::Eth71 | Self::Eth72)
    }

    /// Returns true if the version is eth/69
    pub const fn is_eth69(&self) -> bool {
        matches!(self, Self::Eth69)
    }

    /// Returns true if the version is eth/70
    pub const fn is_eth70(&self) -> bool {
        matches!(self, Self::Eth70)
    }

    /// Returns true if the version is eth/71
    pub const fn is_eth71(&self) -> bool {
        matches!(self, Self::Eth71)
    }

    /// Returns true if the version is eth/72
    pub const fn is_eth72(&self) -> bool {
        matches!(self, Self::Eth72)
    }

    /// Returns true if the version is eth/69 or newer.
    pub const fn is_eth69_or_newer(&self) -> bool {
        matches!(self, Self::Eth69 | Self::Eth70 | Self::Eth71 | Self::Eth72)
    }
}

/// RLP encodes `EthVersion` as a single byte (66-71).
impl Encodable for EthVersion {
    fn encode(&self, out: &mut dyn BufMut) {
        (*self as u8).encode(out)
    }

    fn length(&self) -> usize {
        (*self as u8).length()
    }
}

/// RLP decodes a single byte into `EthVersion`.
/// Returns error if byte is not a valid version (66-71).
impl Decodable for EthVersion {
    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
        let version = u8::decode(buf)?;
        Self::try_from(version).map_err(|_| RlpError::Custom("invalid eth version"))
    }
}

/// Allow for converting from a `&str` to an `EthVersion`.
///
/// # Example
/// ```
/// use reth_eth_wire_types::EthVersion;
///
/// let version = EthVersion::try_from("67").unwrap();
/// assert_eq!(version, EthVersion::Eth67);
/// ```
impl TryFrom<&str> for EthVersion {
    type Error = ParseVersionError;

    #[inline]
    fn try_from(s: &str) -> Result<Self, Self::Error> {
        match s {
            "66" => Ok(Self::Eth66),
            "67" => Ok(Self::Eth67),
            "68" => Ok(Self::Eth68),
            "69" => Ok(Self::Eth69),
            "70" => Ok(Self::Eth70),
            "71" => Ok(Self::Eth71),
            "72" => Ok(Self::Eth72),
            _ => Err(ParseVersionError(s.to_string())),
        }
    }
}

/// Allow for converting from a u8 to an `EthVersion`.
///
/// # Example
/// ```
/// use reth_eth_wire_types::EthVersion;
///
/// let version = EthVersion::try_from(67).unwrap();
/// assert_eq!(version, EthVersion::Eth67);
/// ```
impl TryFrom<u8> for EthVersion {
    type Error = ParseVersionError;

    #[inline]
    fn try_from(u: u8) -> Result<Self, Self::Error> {
        match u {
            66 => Ok(Self::Eth66),
            67 => Ok(Self::Eth67),
            68 => Ok(Self::Eth68),
            69 => Ok(Self::Eth69),
            70 => Ok(Self::Eth70),
            71 => Ok(Self::Eth71),
            72 => Ok(Self::Eth72),
            _ => Err(ParseVersionError(u.to_string())),
        }
    }
}

impl FromStr for EthVersion {
    type Err = ParseVersionError;

    #[inline]
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Self::try_from(s)
    }
}

impl From<EthVersion> for u8 {
    #[inline]
    fn from(v: EthVersion) -> Self {
        v as Self
    }
}

impl From<EthVersion> for &'static str {
    #[inline]
    fn from(v: EthVersion) -> &'static str {
        match v {
            EthVersion::Eth66 => "66",
            EthVersion::Eth67 => "67",
            EthVersion::Eth68 => "68",
            EthVersion::Eth69 => "69",
            EthVersion::Eth70 => "70",
            EthVersion::Eth71 => "71",
            EthVersion::Eth72 => "72",
        }
    }
}

/// `RLPx` `p2p` protocol version
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
#[add_arbitrary_tests(rlp)]
pub enum ProtocolVersion {
    /// `p2p` version 4
    V4 = 4,
    /// `p2p` version 5
    #[default]
    V5 = 5,
}

impl fmt::Display for ProtocolVersion {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "v{}", *self as u8)
    }
}

impl Encodable for ProtocolVersion {
    fn encode(&self, out: &mut dyn BufMut) {
        (*self as u8).encode(out)
    }
    fn length(&self) -> usize {
        // the version should be a single byte
        (*self as u8).length()
    }
}

impl Decodable for ProtocolVersion {
    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
        let version = u8::decode(buf)?;
        match version {
            4 => Ok(Self::V4),
            5 => Ok(Self::V5),
            _ => Err(RlpError::Custom("unknown p2p protocol version")),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::EthVersion;
    use alloy_rlp::{Decodable, Encodable, Error as RlpError};
    use bytes::BytesMut;

    #[test]
    fn test_eth_version_try_from_str() {
        assert_eq!(EthVersion::Eth66, EthVersion::try_from("66").unwrap());
        assert_eq!(EthVersion::Eth67, EthVersion::try_from("67").unwrap());
        assert_eq!(EthVersion::Eth68, EthVersion::try_from("68").unwrap());
        assert_eq!(EthVersion::Eth69, EthVersion::try_from("69").unwrap());
        assert_eq!(EthVersion::Eth70, EthVersion::try_from("70").unwrap());
        assert_eq!(EthVersion::Eth71, EthVersion::try_from("71").unwrap());
        assert_eq!(EthVersion::Eth72, EthVersion::try_from("72").unwrap());
    }

    #[test]
    fn test_eth_version_from_str() {
        assert_eq!(EthVersion::Eth66, "66".parse().unwrap());
        assert_eq!(EthVersion::Eth67, "67".parse().unwrap());
        assert_eq!(EthVersion::Eth68, "68".parse().unwrap());
        assert_eq!(EthVersion::Eth69, "69".parse().unwrap());
        assert_eq!(EthVersion::Eth70, "70".parse().unwrap());
        assert_eq!(EthVersion::Eth71, "71".parse().unwrap());
        assert_eq!(EthVersion::Eth72, "72".parse().unwrap());
    }

    #[test]
    fn test_has_eth68_metadata() {
        assert!(!EthVersion::Eth66.has_eth68_metadata());
        assert!(!EthVersion::Eth67.has_eth68_metadata());
        assert!(EthVersion::Eth68.has_eth68_metadata());
        assert!(EthVersion::Eth69.has_eth68_metadata());
        assert!(EthVersion::Eth70.has_eth68_metadata());
        assert!(EthVersion::Eth71.has_eth68_metadata());
        assert!(EthVersion::Eth72.has_eth68_metadata());
    }

    #[test]
    fn test_eth_version_rlp_encode()

{
        let versions = [
            EthVersion::Eth66,
            EthVersion::Eth67,
            EthVersion::Eth68,
            EthVersion::Eth69,
            EthVersion::Eth70,
            EthVersion::Eth71,
            EthVersion::Eth72,
        ];

        for version in versions {
            let mut encoded = BytesMut::new();
            version.encode(&mut encoded);

            assert_eq!(encoded.len(), 1);
            assert_eq!(encoded[0], version as u8);
        }
    }


      match v {
            EthVersion::Eth66 => "66",
            EthVersion::Eth67 => "67",
            EthVersion::Eth68 => "68",
            EthVersion::Eth69 => "69",
            EthVersion::Eth70 => "70",
            EthVersion::Eth71 => "71",
            EthVersion::Eth72 => "72",
        }
    }
}

/// `RLPx` `p2p` protocol version
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
#[add_arbitrary_tests(rlp)]
pub enum ProtocolVersion {
    /// `p2p` version 4
    V4 = 4,
    /// `p2p` version 5
    #[default]
    V5 = 5,
}

impl fmt::Display for ProtocolVersion {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "v{}", *self as u8)
    }
}

impl Encodable for ProtocolVersion {
    fn encode(&self, out: &mut dyn BufMut) {
        (*self as u8).encode(out)
    }
    fn length(&self) -> usize {
        // the version should be a single byte
        (*self as u8).length()
    }
}

impl Decodable for ProtocolVersion {
    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
        let version = u8::decode(buf)?;
        match version {
            4 => Ok(Self::V4),
            5 => Ok(Self::V5),
            _ => Err(RlpError::Custom("unknown p2p protocol version")),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::EthVersion;
    use alloy_rlp::{Decodable, Encodable, Error as RlpError};
    use bytes::BytesMut;

    #[test]
    fn test_eth_version_try_from_str() {
        assert_eq!(EthVersion::Eth66, EthVersion::try_from("66").unwrap());
        assert_eq!(EthVersion::Eth67, EthVersion::try_from("67").unwrap());
        assert_eq!(EthVersion::Eth68, EthVersion::try_from("68").unwrap());
        assert_eq!(EthVersion::Eth69, EthVersion::try_from("69").unwrap());
        assert_eq!(EthVersion::Eth70, EthVersion::try_from("70").unwrap());
        assert_eq!(EthVersion::Eth71, EthVersion::try_from("71").unwrap());
        assert_eq!(EthVersion::Eth72, EthVersion::try_from("72").unwrap());
    }

    #[test]
    fn test_eth_version_from_str() {
        assert_eq!(EthVersion::Eth66, "66".parse().unwrap());
        assert_eq!(EthVersion::Eth67, "67".parse().unwrap());
        assert_eq!(EthVersion::Eth68, "68".parse().unwrap());
        assert_eq!(EthVersion::Eth69, "69".parse().unwrap());
        assert_eq!(EthVersion::Eth70, "70".parse().unwrap());
        assert_eq!(EthVersion::Eth71, "71".parse().unwrap());
        assert_eq!(EthVersion::Eth72, "72".parse().unwrap());
    }

    #[test]
    fn test_has_eth68_metadata() {
        assert!(!EthVersion::Eth66.has_eth68_metadata());
        assert!(!EthVersion::Eth67.has_eth68_metadata());
        assert!(EthVersion::Eth68.has_eth68_metadata());
        assert!(EthVersion::Eth69.has_eth68_metadata());
        assert!(EthVersion::Eth70.has_eth68_metadata());
        assert!(EthVersion::Eth71.has_eth68_metadata());
        assert!(EthVersion::Eth72.has_eth68_metadata());
    }

    #[test]
    fn test_eth_version_rlp_encode() {
        let versions = [
            EthVersion::Eth66,
            EthVersion::Eth67,
            EthVersion::Eth68,
            EthVersion::Eth69,
            EthVersion::Eth70,
            EthVersion::Eth71,
            EthVersion::Eth72,
        ];

        for version in versions {
            let mut encoded = BytesMut::new();
            version.encode(&mut encoded);

            assert_eq!(encoded.len(), 1);
            assert_eq!(encoded[0], version as u8);
        }
    }
    #[test]

    fn test_eth_version_rlp_decode() {
        let test_cases = [
            (66_u8, Ok(EthVersion::Eth66)),
            (67_u8, Ok(EthVersion::Eth67)),
            (68_u8, Ok(EthVersion::Eth68)),
            (69_u8, Ok(EthVersion::Eth69)),
            (70_u8, Ok(EthVersion::Eth70)),
            (71_u8, Ok(EthVersion::Eth71)),
            (72_u8, Ok(EthVersion::Eth72)),
            (65_u8, Err(RlpError::Custom("invalid eth version"))),
        ];

        for (input, expected) in test_cases {
            let mut encoded = BytesMut::new();
            input.encode(&mut encoded);

            let mut slice = encoded.as_ref();
            let result = EthVersion::decode(&mut slice);
            assert_eq!(result, expected);
        }
    }


          EthVersion::Eth66 => "66",
            EthVersion::Eth67 => "67",
            EthVersion::Eth68 => "68",
            EthVersion::Eth69 => "69",
            EthVersion::Eth70 => "70",
            EthVersion::Eth71 => "71",
            EthVersion::Eth72 => "72",
        }
    }
}

/// `RLPx` `p2p` protocol version
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
#[add_arbitrary_tests(rlp)]
pub enum ProtocolVersion {
    /// `p2p` version 4
    V4 = 4,
    /// `p2p` version 5
    #[default]
    V5 = 5,
}

impl fmt::Display for ProtocolVersion {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "v{}", *self as u8)
    }
}

impl Encodable for ProtocolVersion {
    fn encode(&self, out: &mut dyn BufMut) {
        (*self as u8).encode(out)
    }
    fn length(&self) -> usize {
        // the version should be a single byte
        (*self as u8).length()
    }
}

impl Decodable for ProtocolVersion {
    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
        let version = u8::decode(buf)?;
        match version {
            4 => Ok(Self::V4),
            5 => Ok(Self::V5),
            _ => Err(RlpError::Custom("unknown p2p protocol version")),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::EthVersion;
    use alloy_rlp::{Decodable, Encodable, Error as RlpError};
    use bytes::BytesMut;

    #[test]
    fn test_eth_version_try_from_str() {
        assert_eq!(EthVersion::Eth66, EthVersion::try_from("66").unwrap());
        assert_eq!(EthVersion::Eth67, EthVersion::try_from("67").unwrap());
        assert_eq!(EthVersion::Eth68, EthVersion::try_from("68").unwrap());
        assert_eq!(EthVersion::Eth69, EthVersion::try_from("69").unwrap());
        assert_eq!(EthVersion::Eth70, EthVersion::try_from("70").unwrap());
        assert_eq!(EthVersion::Eth71, EthVersion::try_from("71").unwrap());
        assert_eq!(EthVersion::Eth72, EthVersion::try_from("72").unwrap());
    }

    #[test]
    fn test_eth_version_from_str() {
        assert_eq!(EthVersion::Eth66, "66".parse().unwrap());
        assert_eq!(EthVersion::Eth67, "67".parse().unwrap());
        assert_eq!(EthVersion::Eth68, "68".parse().unwrap());
        assert_eq!(EthVersion::Eth69, "69".parse().unwrap());
        assert_eq!(EthVersion::Eth70, "70".parse().unwrap());
        assert_eq!(EthVersion::Eth71, "71".parse().unwrap());
        assert_eq!(EthVersion::Eth72, "72".parse().unwrap());
    }

    #[test]
    fn test_has_eth68_metadata() {
        assert!(!EthVersion::Eth66.has_eth68_metadata());
        assert!(!EthVersion::Eth67.has_eth68_metadata());
        assert!(EthVersion::Eth68.has_eth68_metadata());
        assert!(EthVersion::Eth69.has_eth68_metadata());
        assert!(EthVersion::Eth70.has_eth68_metadata());
        assert!(EthVersion::Eth71.has_eth68_metadata());
        assert!(EthVersion::Eth72.has_eth68_metadata());
    }

    #[test]
    fn test_eth_version_rlp_encode() {
        let versions = [
            EthVersion::Eth66,
            EthVersion::Eth67,
            EthVersion::Eth68,
            EthVersion::Eth69,
            EthVersion::Eth70,
            EthVersion::Eth71,
            EthVersion::Eth72,
        ];

        for version in versions {
            let mut encoded = BytesMut::new();
            version.encode(&mut encoded);

            assert_eq!(encoded.len(), 1);
            assert_eq!(encoded[0], version as u8);
        }
    }
    #[test]
    fn test_eth_version_rlp_decode()

{
        let test_cases = [
            (66_u8, Ok(EthVersion::Eth66)),
            (67_u8, Ok(EthVersion::Eth67)),
            (68_u8, Ok(EthVersion::Eth68)),
            (69_u8, Ok(EthVersion::Eth69)),
            (70_u8, Ok(EthVersion::Eth70)),
            (71_u8, Ok(EthVersion::Eth71)),
            (72_u8, Ok(EthVersion::Eth72)),
            (65_u8, Err(RlpError::Custom("invalid eth version"))),
        ];

        for (input, expected) in test_cases {
            let mut encoded = BytesMut::new();
            input.encode(&mut encoded);

            let mut slice = encoded.as_ref();
            let result = EthVersion::decode(&mut slice);
            assert_eq!(result, expected);
        }
    }


use alloy_primitives::BlockNumber;

#[derive(Debug, Default)]
pub(crate) struct PipelineProgress {
    /// Block number reached by the stage.
    pub(crate) block_number: Option<BlockNumber>,
    /// The maximum block number achieved by any stage during the execution of the pipeline.
    pub(crate) maximum_block_number: Option<BlockNumber>,
    /// The minimum block number achieved by any stage during the execution of the pipeline.
    pub(crate) minimum_block_number: Option<BlockNumber>,
}

impl PipelineProgress {
    pub(crate) fn update(&mut self, block_number: BlockNumber) {
        self.block_number = Some(block_number);
        self.minimum_block_number = opt::min(self.minimum_block_number, block_number);
        self.maximum_block_number = opt::max(self.maximum_block_number, block_number);
    }

    /// Get next control flow step
    pub(crate) const fn next_ctrl(&self) -> ControlFlow {
        match self.block_number {
            Some(block_number) => ControlFlow::Continue { block_number },
            None => ControlFlow::NoProgress { block_number: None },
        }
    }
}


#[derive(Debug, Default)]
pub(crate) struct PipelineProgress {
    /// Block number reached by the stage.
    pub(crate) block_number: Option<BlockNumber>,
    /// The maximum block number achieved by any stage during the execution of the pipeline.
    pub(crate) maximum_block_number: Option<BlockNumber>,
    /// The minimum block number achieved by any stage during the execution of the pipeline.
    pub(crate) minimum_block_number: Option<BlockNumber>,
}

impl PipelineProgress {
    pub(crate) fn update(&mut self, block_number: BlockNumber) {
        self.block_number = Some(block_number);
        self.minimum_block_number = opt::min(self.minimum_block_number, block_number);
        self.maximum_block_number = opt::max(self.maximum_block_number, block_number);
    }

    /// Get next control flow step
    pub(crate) const fn next_ctrl(&self) -> ControlFlow {
        match self.block_number {
            Some(block_number) => ControlFlow::Continue { block_number },
            None => ControlFlow::NoProgress { block_number: None },
        }
    }
}


mod job;
mod stream;
#[cfg(test)]
mod test_utils;

pub use factory::BackfillJobFactory;
pub use job::{BackfillJob, SingleBlockBackfillJob};
pub use stream::StreamBackfillJob;


#[cfg(test)]
mod test_utils;

pub use factory::BackfillJobFactory;
pub use job::{BackfillJob, SingleBlockBackfillJob};
pub use stream::StreamBackfillJob;


pub use factory::BackfillJobFactory;
pub use job::{BackfillJob, SingleBlockBackfillJob};
pub use stream::StreamBackfillJob;


// A helper function for getting the largest prefix of the sub-trie which contains a particular
// target, based on its `min_len`.
//
// A target will only match nodes which share the target's prefix, where the target's prefix is
// the first `min_len` nibbles of its key. E.g. a target with `key` 0xabcd and `min_len` 2 will
// only match nodes with prefix 0xab.
//
// In general the target will only match within the sub-trie whose prefix is identical to the
// target's. However there is an exception:
//
// Given a trie with a node at 0xabc, there must be a branch at 0xab. A target with prefix 0xabc
// needs to match that node, but the branch at 0xab must be constructed order to know the node
// is at that path. Therefore the sub-trie prefix is the target prefix with a nibble truncated.
//
// For a target with an empty prefix (`min_len` of 0) we still use an empty sub-trie prefix;
// this will still construct the branch at the root node (if there is one). Targets with
// `min_len` of both 0 and 1 will therefore construct the root node, but only those with
// `min_len` of 0 will retain it.
#[inline]
pub(crate) fn sub_trie_prefix(target: &ProofV2Target) -> Nibbles {
    let mut sub_trie_prefix = target.key_nibbles;
    sub_trie_prefix.truncate(target.min_len.saturating_sub(1) as usize);
    sub_trie_prefix
}

// A helper function which returns the first path following a sub-trie in lexicographical order.
#[inline]
fn sub_trie_upper_bound(sub_trie_prefix: &Nibbles) -> Option<Nibbles> {
    sub_trie_prefix.next_without_prefix()
}

/// Describes a set of targets which all apply to a single sub-trie, ie a section of the overall
/// trie whose nodes all share a prefix.
pub(crate) struct SubTrieTargets<'a> {
    /// The prefix which all nodes in the sub-trie share. This is also the first node in the trie
    /// in lexicographic order.
    pub(crate) prefix: Nibbles,
    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
    /// lexicographically.
    pub(crate) targets: &'a [ProofV2Target],
    /// Will be true if at least one target in the set has a zero `min_len`.
    ///
    /// If this is true then `prefix.is_empty()`, though not necessarily vice-versa.
    pub(crate) retain_root: bool,
}

impl<'a> SubTrieTargets<'a> {
    // A helper function which returns the first path following a sub-trie in lexicographical order.
    #[inline]
    pub(crate) fn upper_bound(&self) -> Option<Nibbles> {
        sub_trie_upper_bound(&self.prefix)
    }
}

/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
/// chunked by the sub-tries they apply to within the overall trie.
pub(crate) fn iter_sub_trie_targets(
    targets: &mut [ProofV2Target],
) -> impl Iterator<Item = SubTrieTargets<'_>> {
    // First sort by the sub-trie prefix of each target, falling back to the `min_len` in cases
    // where the sub-trie prefixes are equal (to differentiate targets which match the root node and
    // those which don't).
    targets.sort_unstable_by(|a, b| {
        sub_trie_prefix(a).cmp(&sub_trie_prefix(b)).then_with(|| a.min_len.cmp(&b.min_len))
    });

    // We now chunk targets, such that each chunk contains all targets belonging to the same
    // sub-trie. We are taking advantage of the following properties:
    //
    // - The first target in the chunk has the shortest sub-trie prefix (see previous sorting step).
    //
    // - The upper bound of the first target in the chunk's sub-trie will therefore be the upper
    //   bound of the whole chunk.
    //      - For example, given a chunk with sub-trie prefixes [0x2, 0x2f, 0x2fa], the upper bounds
    //        will be [0x3, 0x3, 0x2fb]. Note that no target could match a trie node with path equal
    //        to or greater than 0x3.
    //
    // - If a target's sub-trie's prefix does not lie within the bounds of the current chunk, then
    //   that target must be the first target of the next chunk, lying in a separate sub-trie.
    //      - Example: given sub-trie prefixes of [0x2, 0x2fa, 0x4c, 0x4ce, 0x4e], we would end up
    //        with the following chunks:
    //          - [0x2, 0x2fa] w/ upper bound 0x3
    //          - [0x4c 0x4ce] w/ upper bound 0x4d
    //          - [0x4e]       w/ upper bound 0x4f
    let mut upper_bound = targets.first().and_then(|t| sub_trie_upper_bound(&sub_trie_prefix(t)));
    let target_chunks = targets.chunk_by_mut(move |_, next| {
        if let Some(some_upper_bound) = upper_bound {
            let prefix = sub_trie_prefix(next);
            let same_chunk = prefix < some_upper_bound;
            if !same_chunk {
                upper_bound = sub_trie_upper_bound(&prefix);
            }
            same_chunk
        } else {
            true
        }
    });

    // Map the chunks to the return type. Within each chunk we want targets to be sorted by their
    // key, as that will be the order they are checked by the `ProofCalculator`.
    target_chunks.map(move |targets| {
        let prefix = sub_trie_prefix(&targets[0]);
        let retain_root = targets[0].min_len == 0;
        targets.sort_unstable_by_key(|target| target.key_nibbles);
        SubTrieTargets { prefix, targets, retain_root }
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::B256;

    #[test]
    fn test_iter_sub_trie_targets() {
        // Helper to create nibbles from hex string (each character is a nibble)
        let nibbles = |hex: &str| -> Nibbles {
            if hex.is_empty() {
                return Nibbles::new();
            }
            format!("0x{}", hex).parse().expect("valid nibbles hex string")
        };

        // Test cases: (input_targets, expected_output)
        // Expected output format: Vec<(exp_prefix_hex, Vec<key_hex>)>
        let test_cases = vec![
            // Case 1: Empty targets
            (vec![], vec![]),
            // Case 2: Single target without min_len
            (
                vec![ProofV2Target::new(B256::repeat_byte(0x20))],
                vec![(
                    "",
                    vec!["2020202020202020202020202020202020202020202020202020202020202020"],
                )],
            ),
            // Case 3: Multiple targets in same sub-trie (no min_len)
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)),
                    ProofV2Target::new(B256::repeat_byte(0x21)),
                ],
                vec![(
                    "",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2121212121212121212121212121212121212121212121212121212121212121",
                    ],
                )],
            ),
            // Case 4: Multiple targets in different sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    ("2", vec!["2020202020202020202020202020202020202020202020202020202020202020"]),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 5: Three targets, two in same sub-trie, one separate
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2


use reth_trie_common::{Nibbles, ProofV2Target};

// A helper function for getting the largest prefix of the sub-trie which contains a particular
// target, based on its `min_len`.
//
// A target will only match nodes which share the target's prefix, where the target's prefix is
// the first `min_len` nibbles of its key. E.g. a target with `key` 0xabcd and `min_len` 2 will
// only match nodes with prefix 0xab.
//
// In general the target will only match within the sub-trie whose prefix is identical to the
// target's. However there is an exception:
//
// Given a trie with a node at 0xabc, there must be a branch at 0xab. A target with prefix 0xabc
// needs to match that node, but the branch at 0xab must be constructed order to know the node
// is at that path. Therefore the sub-trie prefix is the target prefix with a nibble truncated.
//
// For a target with an empty prefix (`min_len` of 0) we still use an empty sub-trie prefix;
// this will still construct the branch at the root node (if there is one). Targets with
// `min_len` of both 0 and 1 will therefore construct the root node, but only those with
// `min_len` of 0 will retain it.
#[inline]

pub(crate) fn sub_trie_prefix(target: &ProofV2Target) -> Nibbles {
    let mut sub_trie_prefix = target.key_nibbles;
    sub_trie_prefix.truncate(target.min_len.saturating_sub(1) as usize);
    sub_trie_prefix
}

// A helper function which returns the first path following a sub-trie in lexicographical order.
#[inline]
fn sub_trie_upper_bound(sub_trie_prefix: &Nibbles) -> Option<Nibbles> {
    sub_trie_prefix.next_without_prefix()
}

/// Describes a set of targets which all apply to a single sub-trie, ie a section of the overall
/// trie whose nodes all share a prefix.
pub(crate) struct SubTrieTargets<'a> {
    /// The prefix which all nodes in the sub-trie share. This is also the first node in the trie
    /// in lexicographic order.
    pub(crate) prefix: Nibbles,
    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
    /// lexicographically.
    pub(crate) targets: &'a [ProofV2Target],
    /// Will be true if at least one target in the set has a zero `min_len`.
    ///
    /// If this is true then `prefix.is_empty()`, though not necessarily vice-versa.
    pub(crate) retain_root: bool,
}

impl<'a> SubTrieTargets<'a> {
    // A helper function which returns the first path following a sub-trie in lexicographical order.
    #[inline]
    pub(crate) fn upper_bound(&self) -> Option<Nibbles> {
        sub_trie_upper_bound(&self.prefix)
    }
}

/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
/// chunked by the sub-tries they apply to within the overall trie.
pub(crate) fn iter_sub_trie_targets(
    targets: &mut [ProofV2Target],
) -> impl Iterator<Item = SubTrieTargets<'_>> {
    // First sort by the sub-trie prefix of each target, falling back to the `min_len` in cases
    // where the sub-trie prefixes are equal (to differentiate targets which match the root node and
    // those which don't).
    targets.sort_unstable_by(|a, b| {
        sub_trie_prefix(a).cmp(&sub_trie_prefix(b)).then_with(|| a.min_len.cmp(&b.min_len))
    });

    // We now chunk targets, such that each chunk contains all targets belonging to the same
    // sub-trie. We are taking advantage of the following properties:
    //
    // - The first target in the chunk has the shortest sub-trie prefix (see previous sorting step).
    //
    // - The upper bound of the first target in the chunk's sub-trie will therefore be the upper
    //   bound of the whole chunk.
    //      - For example, given a chunk with sub-trie prefixes [0x2, 0x2f, 0x2fa], the upper bounds
    //        will be [0x3, 0x3, 0x2fb]. Note that no target could match a trie node with path equal
    //        to or greater than 0x3.
    //
    // - If a target's sub-trie's prefix does not lie within the bounds of the current chunk, then
    //   that target must be the first target of the next chunk, lying in a separate sub-trie.
    //      - Example: given sub-trie prefixes of [0x2, 0x2fa, 0x4c, 0x4ce, 0x4e], we would end up
    //        with the following chunks:
    //          - [0x2, 0x2fa] w/ upper bound 0x3
    //          - [0x4c 0x4ce] w/ upper bound 0x4d
    //          - [0x4e]       w/ upper bound 0x4f
    let mut upper_bound = targets.first().and_then(|t| sub_trie_upper_bound(&sub_trie_prefix(t)));
    let target_chunks = targets.chunk_by_mut(move |_, next| {
        if let Some(some_upper_bound) = upper_bound {
            let prefix = sub_trie_prefix(next);
            let same_chunk = prefix < some_upper_bound;
            if !same_chunk {
                upper_bound = sub_trie_upper_bound(&prefix);
            }
            same_chunk
        } else {
            true
        }
    });

    // Map the chunks to the return type. Within each chunk we want targets to be sorted by their
    // key, as that will be the order they are checked by the `ProofCalculator`.
    target_chunks.map(move |targets| {
        let prefix = sub_trie_prefix(&targets[0]);
        let retain_root = targets[0].min_len == 0;
        targets.sort_unstable_by_key(|target| target.key_nibbles);
        SubTrieTargets { prefix, targets, retain_root }
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::B256;

    #[test]
    fn test_iter_sub_trie_targets() {
        // Helper to create nibbles from hex string (each character is a nibble)
        let nibbles = |hex: &str| -> Nibbles {
            if hex.is_empty() {
                return Nibbles::new();
            }
            format!("0x{}", hex).parse().expect("valid nibbles hex string")
        };

        // Test cases: (input_targets, expected_output)
        // Expected output format: Vec<(exp_prefix_hex, Vec<key_hex>)>
        let test_cases = vec![
            // Case 1: Empty targets
            (vec![], vec![]),
            // Case 2: Single target without min_len
            (
                vec![ProofV2Target::new(B256::repeat_byte(0x20))],
                vec![(
                    "",
                    vec!["2020202020202020202020202020202020202020202020202020202020202020"],
                )],
            ),
            // Case 3: Multiple targets in same sub-trie (no min_len)
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)),
                    ProofV2Target::new(B256::repeat_byte(0x21)),
                ],
                vec![(
                    "",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2121212121212121212121212121212121212121212121212121212121212121",
                    ],
                )],
            ),
            // Case 4: Multiple targets in different sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    ("2", vec!["2020202020202020202020202020202020202020202020202020202020202020"]),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 5: Three targets, two in same sub-trie, one separate
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                        ],
                    ),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 6: Targets with different min_len values in same sub-trie
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(3),
                ],
                vec![(
                    "2",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                    ],
                )],
            ),
            // Case 7: More complex chunking with multiple sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(3),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(4),


use reth_trie_common::{Nibbles, ProofV2Target};

// A helper function for getting the largest prefix of the sub-trie which contains a particular
// target, based on its `min_len`.
//
// A target will only match nodes which share the target's prefix, where the target's prefix is
// the first `min_len` nibbles of its key. E.g. a target with `key` 0xabcd and `min_len` 2 will
// only match nodes with prefix 0xab.
//
// In general the target will only match within the sub-trie whose prefix is identical to the
// target's. However there is an exception:
//
// Given a trie with a node at 0xabc, there must be a branch at 0xab. A target with prefix 0xabc
// needs to match that node, but the branch at 0xab must be constructed order to know the node
// is at that path. Therefore the sub-trie prefix is the target prefix with a nibble truncated.
//
// For a target with an empty prefix (`min_len` of 0) we still use an empty sub-trie prefix;
// this will still construct the branch at the root node (if there is one). Targets with
// `min_len` of both 0 and 1 will therefore construct the root node, but only those with
// `min_len` of 0 will retain it.
#[inline]
pub(crate) fn sub_trie_prefix(target: &ProofV2Target) -> Nibbles

{
    let mut sub_trie_prefix = target.key_nibbles;
    sub_trie_prefix.truncate(target.min_len.saturating_sub(1) as usize);
    sub_trie_prefix
}

// A helper function which returns the first path following a sub-trie in lexicographical order.
#[inline]
fn sub_trie_upper_bound(sub_trie_prefix: &Nibbles) -> Option<Nibbles> {
    sub_trie_prefix.next_without_prefix()
}

/// Describes a set of targets which all apply to a single sub-trie, ie a section of the overall
/// trie whose nodes all share a prefix.
pub(crate) struct SubTrieTargets<'a> {
    /// The prefix which all nodes in the sub-trie share. This is also the first node in the trie
    /// in lexicographic order.
    pub(crate) prefix: Nibbles,
    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
    /// lexicographically.
    pub(crate) targets: &'a [ProofV2Target],
    /// Will be true if at least one target in the set has a zero `min_len`.
    ///
    /// If this is true then `prefix.is_empty()`, though not necessarily vice-versa.
    pub(crate) retain_root: bool,
}

impl<'a> SubTrieTargets<'a> {
    // A helper function which returns the first path following a sub-trie in lexicographical order.
    #[inline]
    pub(crate) fn upper_bound(&self) -> Option<Nibbles> {
        sub_trie_upper_bound(&self.prefix)
    }
}

/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
/// chunked by the sub-tries they apply to within the overall trie.
pub(crate) fn iter_sub_trie_targets(
    targets: &mut [ProofV2Target],
) -> impl Iterator<Item = SubTrieTargets<'_>> {
    // First sort by the sub-trie prefix of each target, falling back to the `min_len` in cases
    // where the sub-trie prefixes are equal (to differentiate targets which match the root node and
    // those which don't).
    targets.sort_unstable_by(|a, b| {
        sub_trie_prefix(a).cmp(&sub_trie_prefix(b)).then_with(|| a.min_len.cmp(&b.min_len))
    });

    // We now chunk targets, such that each chunk contains all targets belonging to the same
    // sub-trie. We are taking advantage of the following properties:
    //
    // - The first target in the chunk has the shortest sub-trie prefix (see previous sorting step).
    //
    // - The upper bound of the first target in the chunk's sub-trie will therefore be the upper
    //   bound of the whole chunk.
    //      - For example, given a chunk with sub-trie prefixes [0x2, 0x2f, 0x2fa], the upper bounds
    //        will be [0x3, 0x3, 0x2fb]. Note that no target could match a trie node with path equal
    //        to or greater than 0x3.
    //
    // - If a target's sub-trie's prefix does not lie within the bounds of the current chunk, then
    //   that target must be the first target of the next chunk, lying in a separate sub-trie.
    //      - Example: given sub-trie prefixes of [0x2, 0x2fa, 0x4c, 0x4ce, 0x4e], we would end up
    //        with the following chunks:
    //          - [0x2, 0x2fa] w/ upper bound 0x3
    //          - [0x4c 0x4ce] w/ upper bound 0x4d
    //          - [0x4e]       w/ upper bound 0x4f
    let mut upper_bound = targets.first().and_then(|t| sub_trie_upper_bound(&sub_trie_prefix(t)));
    let target_chunks = targets.chunk_by_mut(move |_, next| {
        if let Some(some_upper_bound) = upper_bound {
            let prefix = sub_trie_prefix(next);
            let same_chunk = prefix < some_upper_bound;
            if !same_chunk {
                upper_bound = sub_trie_upper_bound(&prefix);
            }
            same_chunk
        } else {
            true
        }
    });

    // Map the chunks to the return type. Within each chunk we want targets to be sorted by their
    // key, as that will be the order they are checked by the `ProofCalculator`.
    target_chunks.map(move |targets| {
        let prefix = sub_trie_prefix(&targets[0]);
        let retain_root = targets[0].min_len == 0;
        targets.sort_unstable_by_key(|target| target.key_nibbles);
        SubTrieTargets { prefix, targets, retain_root }
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::B256;

    #[test]
    fn test_iter_sub_trie_targets() {
        // Helper to create nibbles from hex string (each character is a nibble)
        let nibbles = |hex: &str| -> Nibbles {
            if hex.is_empty() {
                return Nibbles::new();
            }
            format!("0x{}", hex).parse().expect("valid nibbles hex string")
        };

        // Test cases: (input_targets, expected_output)
        // Expected output format: Vec<(exp_prefix_hex, Vec<key_hex>)>
        let test_cases = vec![
            // Case 1: Empty targets
            (vec![], vec![]),
            // Case 2: Single target without min_len
            (
                vec![ProofV2Target::new(B256::repeat_byte(0x20))],
                vec![(
                    "",
                    vec!["2020202020202020202020202020202020202020202020202020202020202020"],
                )],
            ),
            // Case 3: Multiple targets in same sub-trie (no min_len)
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)),
                    ProofV2Target::new(B256::repeat_byte(0x21)),
                ],
                vec![(
                    "",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2121212121212121212121212121212121212121212121212121212121212121",
                    ],
                )],
            ),
            // Case 4: Multiple targets in different sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    ("2", vec!["2020202020202020202020202020202020202020202020202020202020202020"]),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 5: Three targets, two in same sub-trie, one separate
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                        ],
                    ),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 6: Targets with different min_len values in same sub-trie
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(3),
                ],
                vec![(
                    "2",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                    ],
                )],
            ),
            // Case 7: More complex chunking with multiple sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(3),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(4),
                    ProofV2Target::n


use reth_trie_common::{Nibbles, ProofV2Target};

// A helper function for getting the largest prefix of the sub-trie which contains a particular
// target, based on its `min_len`.
//
// A target will only match nodes which share the target's prefix, where the target's prefix is
// the first `min_len` nibbles of its key. E.g. a target with `key` 0xabcd and `min_len` 2 will
// only match nodes with prefix 0xab.
//
// In general the target will only match within the sub-trie whose prefix is identical to the
// target's. However there is an exception:
//
// Given a trie with a node at 0xabc, there must be a branch at 0xab. A target with prefix 0xabc
// needs to match that node, but the branch at 0xab must be constructed order to know the node
// is at that path. Therefore the sub-trie prefix is the target prefix with a nibble truncated.
//
// For a target with an empty prefix (`min_len` of 0) we still use an empty sub-trie prefix;
// this will still construct the branch at the root node (if there is one). Targets with
// `min_len` of both 0 and 1 will therefore construct the root node, but only those with
// `min_len` of 0 will retain it.
#[inline]
pub(crate) fn sub_trie_prefix(target: &ProofV2Target) -> Nibbles {
    let mut sub_trie_prefix = target.key_nibbles;
    sub_trie_prefix.truncate(target.min_len.saturating_sub(1) as usize);
    sub_trie_prefix
}

// A helper function which returns the first path following a sub-trie in lexicographical order.
#[inline]

fn sub_trie_upper_bound(sub_trie_prefix: &Nibbles) -> Option<Nibbles> {
    sub_trie_prefix.next_without_prefix()
}

/// Describes a set of targets which all apply to a single sub-trie, ie a section of the overall
/// trie whose nodes all share a prefix.
pub(crate) struct SubTrieTargets<'a> {
    /// The prefix which all nodes in the sub-trie share. This is also the first node in the trie
    /// in lexicographic order.
    pub(crate) prefix: Nibbles,
    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
    /// lexicographically.
    pub(crate) targets: &'a [ProofV2Target],
    /// Will be true if at least one target in the set has a zero `min_len`.
    ///
    /// If this is true then `prefix.is_empty()`, though not necessarily vice-versa.
    pub(crate) retain_root: bool,
}

impl<'a> SubTrieTargets<'a> {
    // A helper function which returns the first path following a sub-trie in lexicographical order.
    #[inline]
    pub(crate) fn upper_bound(&self) -> Option<Nibbles> {
        sub_trie_upper_bound(&self.prefix)
    }
}

/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
/// chunked by the sub-tries they apply to within the overall trie.
pub(crate) fn iter_sub_trie_targets(
    targets: &mut [ProofV2Target],
) -> impl Iterator<Item = SubTrieTargets<'_>> {
    // First sort by the sub-trie prefix of each target, falling back to the `min_len` in cases
    // where the sub-trie prefixes are equal (to differentiate targets which match the root node and
    // those which don't).
    targets.sort_unstable_by(|a, b| {
        sub_trie_prefix(a).cmp(&sub_trie_prefix(b)).then_with(|| a.min_len.cmp(&b.min_len))
    });

    // We now chunk targets, such that each chunk contains all targets belonging to the same
    // sub-trie. We are taking advantage of the following properties:
    //
    // - The first target in the chunk has the shortest sub-trie prefix (see previous sorting step).
    //
    // - The upper bound of the first target in the chunk's sub-trie will therefore be the upper
    //   bound of the whole chunk.
    //      - For example, given a chunk with sub-trie prefixes [0x2, 0x2f, 0x2fa], the upper bounds
    //        will be [0x3, 0x3, 0x2fb]. Note that no target could match a trie node with path equal
    //        to or greater than 0x3.
    //
    // - If a target's sub-trie's prefix does not lie within the bounds of the current chunk, then
    //   that target must be the first target of the next chunk, lying in a separate sub-trie.
    //      - Example: given sub-trie prefixes of [0x2, 0x2fa, 0x4c, 0x4ce, 0x4e], we would end up
    //        with the following chunks:
    //          - [0x2, 0x2fa] w/ upper bound 0x3
    //          - [0x4c 0x4ce] w/ upper bound 0x4d
    //          - [0x4e]       w/ upper bound 0x4f
    let mut upper_bound = targets.first().and_then(|t| sub_trie_upper_bound(&sub_trie_prefix(t)));
    let target_chunks = targets.chunk_by_mut(move |_, next| {
        if let Some(some_upper_bound) = upper_bound {
            let prefix = sub_trie_prefix(next);
            let same_chunk = prefix < some_upper_bound;
            if !same_chunk {
                upper_bound = sub_trie_upper_bound(&prefix);
            }
            same_chunk
        } else {
            true
        }
    });

    // Map the chunks to the return type. Within each chunk we want targets to be sorted by their
    // key, as that will be the order they are checked by the `ProofCalculator`.
    target_chunks.map(move |targets| {
        let prefix = sub_trie_prefix(&targets[0]);
        let retain_root = targets[0].min_len == 0;
        targets.sort_unstable_by_key(|target| target.key_nibbles);
        SubTrieTargets { prefix, targets, retain_root }
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::B256;

    #[test]
    fn test_iter_sub_trie_targets() {
        // Helper to create nibbles from hex string (each character is a nibble)
        let nibbles = |hex: &s


use reth_trie_common::{Nibbles, ProofV2Target};

// A helper function for getting the largest prefix of the sub-trie which contains a particular
// target, based on its `min_len`.
//
// A target will only match nodes which share the target's prefix, where the target's prefix is
// the first `min_len` nibbles of its key. E.g. a target with `key` 0xabcd and `min_len` 2 will
// only match nodes with prefix 0xab.
//
// In general the target will only match within the sub-trie whose prefix is identical to the
// target's. However there is an exception:
//
// Given a trie with a node at 0xabc, there must be a branch at 0xab. A target with prefix 0xabc
// needs to match that node, but the branch at 0xab must be constructed order to know the node
// is at that path. Therefore the sub-trie prefix is the target prefix with a nibble truncated.
//
// For a target with an empty prefix (`min_len` of 0) we still use an empty sub-trie prefix;
// this will still construct the branch at the root node (if there is one). Targets with
// `min_len` of both 0 and 1 will therefore construct the root node, but only those with
// `min_len` of 0 will retain it.
#[inline]
pub(crate) fn sub_trie_prefix(target: &ProofV2Target) -> Nibbles {
    let mut sub_trie_prefix = target.key_nibbles;
    sub_trie_prefix.truncate(target.min_len.saturating_sub(1) as usize);
    sub_trie_prefix
}

// A helper function which returns the first path following a sub-trie in lexicographical order.
#[inline]
fn sub_trie_upper_bound(sub_trie_prefix: &Nibbles) -> Option<Nibbles>

{
    sub_trie_prefix.next_without_prefix()
}

/// Describes a set of targets which all apply to a single sub-trie, ie a section of the overall
/// trie whose nodes all share a prefix.
pub(crate) struct SubTrieTargets<'a> {
    /// The prefix which all nodes in the sub-trie share. This is also the first node in the trie
    /// in lexicographic order.
    pub(crate) prefix: Nibbles,
    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
    /// lexicographically.
    pub(crate) targets: &'a [ProofV2Target],
    /// Will be true if at least one target in the set has a zero `min_len`.
    ///
    /// If this is true then `prefix.is_empty()`, though not necessarily vice-versa.
    pub(crate) retain_root: bool,
}

impl<'a> SubTrieTargets<'a> {
    // A helper function which returns the first path following a sub-trie in lexicographical order.
    #[inline]
    pub(crate) fn upper_bound(&self) -> Option<Nibbles> {
        sub_trie_upper_bound(&self.prefix)
    }
}

/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
/// chunked by the sub-tries they apply to within the overall trie.
pub(crate) fn iter_sub_trie_targets(
    targets: &mut [ProofV2Target],
) -> impl Iterator<Item = SubTrieTargets<'_>> {
    // First sort by the sub-trie prefix of each target, falling back to the `min_len` in cases
    // where the sub-trie prefixes are equal (to differentiate targets which match the root node and
    // those which don't).
    targets.sort_unstable_by(|a, b| {
        sub_trie_prefix(a).cmp(&sub_trie_prefix(b)).then_with(|| a.min_len.cmp(&b.min_len))
    });

    // We now chunk targets, such that each chunk contains all targets belonging to the same
    // sub-trie. We are taking advantage of the following properties:
    //
    // - The first target in the chunk has the shortest sub-trie prefix (see previous sorting step).
    //
    // - The upper bound of the first target in the chunk's sub-trie will therefore be the upper
    //   bound of the whole chunk.
    //      - For example, given a chunk with sub-trie prefixes [0x2, 0x2f, 0x2fa], the upper bounds
    //        will be [0x3, 0x3, 0x2fb]. Note that no target could match a trie node with path equal
    //        to or greater than 0x3.
    //
    // - If a target's sub-trie's prefix does not lie within the bounds of the current chunk, then
    //   that target must be the first target of the next chunk, lying in a separate sub-trie.
    //      - Example: given sub-trie prefixes of [0x2, 0x2fa, 0x4c, 0x4ce, 0x4e], we would end up
    //        with the following chunks:
    //          - [0x2, 0x2fa] w/ upper bound 0x3
    //          - [0x4c 0x4ce] w/ upper bound 0x4d
    //          - [0x4e]       w/ upper bound 0x4f
    let mut upper_bound = targets.first().and_then(|t| sub_trie_upper_bound(&sub_trie_prefix(t)));
    let target_chunks = targets.chunk_by_mut(move |_, next| {
        if let Some(some_upper_bound) = upper_bound {
            let prefix = sub_trie_prefix(next);
            let same_chunk = prefix < some_upper_bound;
            if !same_chunk {
                upper_bound = sub_trie_upper_bound(&prefix);
            }
            same_chunk
        } else {
            true
        }
    });

    // Map the chunks to the return type. Within each chunk we want targets to be sorted by their
    // key, as that will be the order they are checked by the `ProofCalculator`.
    target_chunks.map(move |targets| {
        let prefix = sub_trie_prefix(&targets[0]);
        let retain_root = targets[0].min_len == 0;
        targets.sort_unstable_by_key(|target| target.key_nibbles);
        SubTrieTargets { prefix, targets, retain_root }
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::B256;

    #[test]
    fn test_iter_sub_trie_targets() {
        // Helper to create nibbles from hex string (each character is a nibble)
        let nibbles = |hex: &str| -> Nibbles {
            if hex.is_empty() {
                return Nibbles::new();
            }
            format!("0x{}", hex).parse().expect("valid nibbles hex string")
        };

        // Test cases: (input_targets, expected_output)
        // Expected output format: Vec<(exp_prefix_hex, Vec<key_hex>)>
        let test_cases = vec![
            // Case 1: Empty targets
            (vec![], vec![]),
            // Case 2: Single target without min_len
            (
                vec![ProofV2Target::new(B256::repeat_byte(0x20))],
                vec![(
                    "",
                    vec!["2020202020202020202020202020202020202020202020202020202020202020"],
                )],
            ),
            // Case 3: Multiple targets in same sub-trie (no min_len)
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)),
                    ProofV2Target::new(B256::repeat_byte(0x21)),
                ],
                vec![(
                    "",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2121212121212121212121212121212121212121212121212121212121212121",
                    ],
                )],
            ),
            // Case 4: Multiple targets in different sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    ("2", vec!["2020202020202020202020202020202020202020202020202020202020202020"]),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 5: Three targets, two in same sub-trie, one separate
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                        ],
                    ),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 6: Targets with different min_len values in same sub-trie
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(3),
                ],
                vec![(
                    "2",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                    ],
                )],
            ),
            // Case 7: More complex chunking with multiple sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(3),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4e)).with_min_len(3),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",


use reth_trie_common::{Nibbles, ProofV2Target};

// A helper function for getting the largest prefix of the sub-trie which contains a particular
// target, based on its `min_len`.
//
// A target will only match nodes which share the target's prefix, where the target's prefix is
// the first `min_len` nibbles of its key. E.g. a target with `key` 0xabcd and `min_len` 2 will
// only match nodes with prefix 0xab.
//
// In general the target will only match within the sub-trie whose prefix is identical to the
// target's. However there is an exception:
//
// Given a trie with a node at 0xabc, there must be a branch at 0xab. A target with prefix 0xabc
// needs to match that node, but the branch at 0xab must be constructed order to know the node
// is at that path. Therefore the sub-trie prefix is the target prefix with a nibble truncated.
//
// For a target with an empty prefix (`min_len` of 0) we still use an empty sub-trie prefix;
// this will still construct the branch at the root node (if there is one). Targets with
// `min_len` of both 0 and 1 will therefore construct the root node, but only those with
// `min_len` of 0 will retain it.
#[inline]
pub(crate) fn sub_trie_prefix(target: &ProofV2Target) -> Nibbles {
    let mut sub_trie_prefix = target.key_nibbles;
    sub_trie_prefix.truncate(target.min_len.saturating_sub(1) as usize);
    sub_trie_prefix
}

// A helper function which returns the first path following a sub-trie in lexicographical order.
#[inline]
fn sub_trie_upper_bound(sub_trie_prefix: &Nibbles) -> Option<Nibbles> {
    sub_trie_prefix.next_without_prefix()
}

/// Describes a set of targets which all apply to a single sub-trie, ie a section of the overall
/// trie whose nodes all share a prefix.
pub(crate) struct SubTrieTargets<'a> {
    /// The prefix which all nodes in the sub-trie share. This is also the first node in the trie
    /// in lexicographic order.
    pub(crate) prefix: Nibbles,
    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
    /// lexicographically.
    pub(crate) targets: &'a [ProofV2Target],
    /// Will be true if at least one target in the set has a zero `min_len`.
    ///
    /// If this is true then `prefix.is_empty()`, though not necessarily vice-versa.
    pub(crate) retain_root: bool,
}

impl<'a> SubTrieTargets<'a> {
    // A helper function which returns the first path following a sub-trie in lexicographical order.
    #[inline]
    pub(crate) fn upper_bound(&self) -> Option<Nibbles> {
        sub_trie_upper_bound(&self.prefix)
    }
}

/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
/// chunked by the sub-tries they apply to within the overall trie.
pub(crate) fn iter_sub_trie_targets(
    targets: &mut [ProofV2Target],
) -> impl Iterator<Item = SubTrieTargets<'_>> {
    // First sort by the sub-trie prefix of each target, falling back to the `min_len` in cases
    // where the sub-trie prefixes are equal (to differentiate targets which match the root node and
    // those which don't).
    targets.sort_unstable_by(|a, b| {
        sub_trie_prefix(a).cmp(&sub_trie_prefix(b)).then_with(|| a.min_len.cmp(&b.min_len))
    });

    // We now chunk targets, such that each chunk contains all targets belonging to the same
    // sub-trie. We are taking advantage of the following properties:
    //
    // - The first target in the chunk has the shortest sub-trie prefix (see previous sorting step).
    //
    // - The upper bound of the first target in the chunk's sub-trie will therefore be the upper
    //   bound of the whole chunk.
    //      - For example, given a chunk with sub-trie prefixes [0x2, 0x2f, 0x2fa], the upper bounds
    //        will be [0x3, 0x3, 0x2fb]. Note that no target could match a trie node with path equal
    //        to or greater than 0x3.
    //
    // - If a target's sub-trie's prefix does not lie within the bounds of the current chunk, then
    //   that target must be the first target of the next chunk, lying in a separate sub-trie.
    //      - Example: given sub-trie prefixes of [0x2, 0x2fa, 0x4c, 0x4ce, 0x4e], we would end up
    //        with the following chunks:
    //          - [0x2, 0x2fa] w/ upper bound 0x3
    //          - [0x4c 0x4ce] w/ upper bound 0x4d
    //          - [0x4e]       w/ upper bound 0x4f
    let mut upper_bound = targets.first().and_then(|t| sub_trie_upper_bound(&sub_trie_prefix(t)));
    let target_chunks = targets.chunk_by_mut(move |_, next| {
        if let Some(some_upper_bound) = upper_bound {
            let prefix = sub_trie_prefix(next);
            let same_chunk = prefix < some_upper_bound;
            if !same_chunk {
                upper_bound = sub_trie_upper_bound(&prefix);
            }
            same_chunk
        } else {
            true
        }
    });

    // Map the chunks to the return type. Within each chunk we want targets to be sorted by their
    // key, as that will be the order they are checked by the `ProofCalculator`.
    target_chunks.map(move |targets| {
        let prefix = sub_trie_prefix(&targets[0]);
        let retain_root = targets[0].min_len == 0;
        targets.sort_unstable_by_key(|target| target.key_nibbles);
        SubTrieTargets { prefix, targets, retain_root }
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::B256;

    #[test]
    fn test_iter_sub_trie_targets() {
        // Helper to create nibbles from hex string (each character is a nibble)
        let nibbles = |hex: &str| -> Nibbles {
            if hex.is_empty() {
                return Nibbles::new();
            }
            format!("0x{}", hex).parse().expect("valid nibbles hex string")
        };

        // Test cases: (input_targets, expected_output)
        // Expected output format: Vec<(exp_prefix_hex, Vec<key_hex>)>
        let test_cases = vec![
            // Case 1: Empty targets
            (vec![], vec![]),
            // Case 2: Single target without min_len
            (
                vec![ProofV2Target::new(B256::repeat_byte(0x20))],
                vec![(
                    "",
                    vec!["2020202020202020202020202020202020202020202020202020202020202020"],
                )],
            ),
            // Case 3: Multiple targets in same sub-trie (no min_len)
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)),
                    ProofV2Target::new(B256::repeat_byte(0x21)),
                ],
                vec![(
                    "",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2121212121212121212121212121212121212121212121212121212121212121",
                    ],
                )],
            ),
            // Case 4: Multiple targets in different sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    ("2", vec!["2020202020202020202020202020202020202020202020202020202020202020"]),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 5: Three targets, two in same sub-trie, one separate
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                        ],
                    ),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 6: Targets with different min_len values in same sub-trie
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(3),
                ],
                vec![(
                    "2",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                    ],
                )],
            ),
            // Case 7: More complex chunking with multiple sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(3),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4e)).with_min_len(3),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                        ],
                    ),
                    (
                        "4c",
                        vec![
                            "4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c",
                            "4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4


use reth_trie_common::{Nibbles, ProofV2Target};

// A helper function for getting the largest prefix of the sub-trie which contains a particular
// target, based on its `min_len`.
//
// A target will only match nodes which share the target's prefix, where the target's prefix is
// the first `min_len` nibbles of its key. E.g. a target with `key` 0xabcd and `min_len` 2 will
// only match nodes with prefix 0xab.
//
// In general the target will only match within the sub-trie whose prefix is identical to the
// target's. However there is an exception:
//
// Given a trie with a node at 0xabc, there must be a branch at 0xab. A target with prefix 0xabc
// needs to match that node, but the branch at 0xab must be constructed order to know the node
// is at that path. Therefore the sub-trie prefix is the target prefix with a nibble truncated.
//
// For a target with an empty prefix (`min_len` of 0) we still use an empty sub-trie prefix;
// this will still construct the branch at the root node (if there is one). Targets with
// `min_len` of both 0 and 1 will therefore construct the root node, but only those with
// `min_len` of 0 will retain it.
#[inline]
pub(crate) fn sub_trie_prefix(target: &ProofV2Target) -> Nibbles {
    let mut sub_trie_prefix = target.key_nibbles;
    sub_trie_prefix.truncate(target.min_len.saturating_sub(1) as usize);
    sub_trie_prefix
}

// A helper function which returns the first path following a sub-trie in lexicographical order.
#[inline]
fn sub_trie_upper_bound(sub_trie_prefix: &Nibbles) -> Option<Nibbles> {
    sub_trie_prefix.next_without_prefix()
}

/// Describes a set of targets which all apply to a single sub-trie, ie a section of the overall
/// trie whose nodes all share a prefix.
pub(crate) struct SubTrieTargets<'a> {
    /// The prefix which all nodes in the sub-trie share. This is also the first node in the trie
    /// in lexicographic order.
    pub(crate) prefix: Nibbles,
    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
    /// lexicographically.
    pub(crate) targets: &'a [ProofV2Target],
    /// Will be true if at least one target in the set has a zero `min_len`.
    ///
    /// If this is true then `prefix.is_empty()`, though not necessarily vice-versa.
    pub(crate) retain_root: bool,
}

impl<'a> SubTrieTargets<'a> {
    // A helper function which returns the first path following a sub-trie in lexicographical order.
    #[inline]
    pub(crate) fn upper_bound(&self) -> Option<Nibbles> {
        sub_trie_upper_bound(&self.prefix)
    }
}

/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
/// chunked by the sub-tries they apply to within the overall trie.
pub(crate) fn iter_sub_trie_targets(
    targets: &mut [ProofV2Target],
) -> impl Iterator<Item = SubTrieTargets<'_>> {
    // First sort by the sub-trie prefix of each target, falling back to the `min_len` in cases
    // where the sub-trie prefixes are equal (to differentiate targets which match the root node and
    // those which don't).
    targets.sort_unstable_by(|a, b| {
        sub_trie_prefix(a).cmp(&sub_trie_prefix(b)).then_with(|| a.min_len.cmp(&b.min_len))
    });

    // We now chunk targets, such that each chunk contains all targets belonging to the same
    // sub-trie. We are taking advantage of the following properties:
    //
    // - The first target in the chunk has the shortest sub-trie prefix (see previous sorting step).
    //
    // - The upper bound of the first target in the chunk's sub-trie will therefore be the upper
    //   bound of the whole chunk.
    //      - For example, given a chunk with sub-trie prefixes [0x2, 0x2f, 0x2fa], the upper bounds
    //        will be [0x3, 0x3, 0x2fb]. Note that no target could match a trie node with path equal
    //        to or greater than 0x3.
    //
    // - If a target's sub-trie's prefix does not lie within the bounds of the current chunk, then
    //   that target must be the first target of the next chunk, lying in a separate sub-trie.
    //      - Example: given sub-trie prefixes of [0x2, 0x2fa, 0x4c, 0x4ce, 0x4e], we would end up
    //        with the following chunks:
    //          - [0x2, 0x2fa] w/ upper bound 0x3
    //          - [0x4c 0x4ce] w/ upper bound 0x4d
    //          - [0x4e]       w/ upper bound 0x4f
    let mut upper_bound = targets.first().and_then(|t| sub_trie_upper_bound(&sub_trie_prefix(t)));
    let target_chunks = targets.chunk_by_mut(move |_, next| {
        if let Some(some_upper_bound) = upper_bound {
            let prefix = sub_trie_prefix(next);
            let same_chunk = prefix < some_upper_bound;
            if !same_chunk {
                upper_bound = sub_trie_upper_bound(&prefix);
            }
            same_chunk
        } else {
            true
        }
    });

    // Map the chunks to the return type. Within each chunk we want targets to be sorted by their
    // key, as that will be the order they are checked by the `ProofCalculator`.
    target_chunks.map(move |targets| {
        let prefix = sub_trie_prefix(&targets[0]);
        let retain_root = targets[0].min_len == 0;
        targets.sort_unstable_by_key(|target| target.key_nibbles);
        SubTrieTargets { prefix, targets, retain_root }
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::B256;

    #[test]
    fn test_iter_sub_trie_targets() {
        // Helper to create nibbles from hex string (each character is a nibble)
        let nibbles = |hex: &str| -> Nibbles {
            if hex.is_empty() {
                return Nibbles::new();
            }
            format!("0x{}", hex).parse().expect("valid nibbles hex string")
        };

        // Test cases: (input_targets, expected_output)
        // Expected output format: Vec<(exp_prefix_hex, Vec<key_hex>)>
        let test_cases = vec![
            // Case 1: Empty targets
            (vec![], vec![]),
            // Case 2: Single target without min_len
            (
                vec![ProofV2Target::new(B256::repeat_byte(0x20))],
                vec![(
                    "",
                    vec!["2020202020202020202020202020202020202020202020202020202020202020"],
                )],
            ),
            // Case 3: Multiple targets in same sub-trie (no min_len)
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)),
                    ProofV2Target::new(B256::repeat_byte(0x21)),
                ],
                vec![(
                    "",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2121212121212121212121212121212121212121212121212121212121212121",
                    ],
                )],
            ),
            // Case 4: Multiple targets in different sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    ("2", vec!["2020202020202020202020202020202020202020202020202020202020202020"]),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 5: Three targets, two in same sub-trie, one separate
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                        ],
                    ),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 6: Targets with different min_len values in same sub-trie
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(3),
                ],
                vec![(
                    "2",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                    ],
                )],
            ),
            // Case 7: More complex chunking with multiple sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(3),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4e)).with_min_len(3),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                        ],
                    ),
                    (
                        "4c",
                        vec![
                            "4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c",
                            "4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c",
                        ],
                    ),
                    (
                        "4e",
                        vec!["4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e"],
                    ),
                ],
            ),
            // Case 8: Min-len 1 should result in zero-length sub-trie prefix
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(1),


use reth_trie_common::{Nibbles, ProofV2Target};

// A helper function for getting the largest prefix of the sub-trie which contains a particular
// target, based on its `min_len`.
//
// A target will only match nodes which share the target's prefix, where the target's prefix is
// the first `min_len` nibbles of its key. E.g. a target with `key` 0xabcd and `min_len` 2 will
// only match nodes with prefix 0xab.
//
// In general the target will only match within the sub-trie whose prefix is identical to the
// target's. However there is an exception:
//
// Given a trie with a node at 0xabc, there must be a branch at 0xab. A target with prefix 0xabc
// needs to match that node, but the branch at 0xab must be constructed order to know the node
// is at that path. Therefore the sub-trie prefix is the target prefix with a nibble truncated.
//
// For a target with an empty prefix (`min_len` of 0) we still use an empty sub-trie prefix;
// this will still construct the branch at the root node (if there is one). Targets with
// `min_len` of both 0 and 1 will therefore construct the root node, but only those with
// `min_len` of 0 will retain it.
#[inline]
pub(crate) fn sub_trie_prefix(target: &ProofV2Target) -> Nibbles {
    let mut sub_trie_prefix = target.key_nibbles;
    sub_trie_prefix.truncate(target.min_len.saturating_sub(1) as usize);
    sub_trie_prefix
}

// A helper function which returns the first path following a sub-trie in lexicographical order.
#[inline]
fn sub_trie_upper_bound(sub_trie_prefix: &Nibbles) -> Option<Nibbles> {
    sub_trie_prefix.next_without_prefix()
}

/// Describes a set of targets which all apply to a single sub-trie, ie a section of the overall
/// trie whose nodes all share a prefix.
pub(crate) struct SubTrieTargets<'a> {
    /// The prefix which all nodes in the sub-trie share. This is also the first node in the trie
    /// in lexicographic order.
    pub(crate) prefix: Nibbles,
    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
    /// lexicographically.
    pub(crate) targets: &'a [ProofV2Target],
    /// Will be true if at least one target in the set has a zero `min_len`.
    ///
    /// If this is true then `prefix.is_empty()`, though not necessarily vice-versa.
    pub(crate) retain_root: bool,
}

impl<'a> SubTrieTargets<'a> {
    // A helper function which returns the first path following a sub-trie in lexicographical order.
    #[inline]

    pub(crate) fn upper_bound(&self) -> Option<Nibbles> {
        sub_trie_upper_bound(&self.prefix)
    }

}

/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
/// chunked by the sub-tries they apply to within the overall trie.
pub(crate) fn iter_sub_trie_targets(
    targets: &mut [ProofV2Target],
) -> impl Iterator<Item = SubTrieTargets<'_>> {
    // First sort by the sub-trie prefix of each target, falling back to the `min_len` in cases
    // where the sub-trie prefixes are equal (to differentiate targets which match the root node and
    // those which don't).
    targets.sort_unstable_by(|a, b| {
        sub_trie_prefix(a).cmp(&sub_trie_prefix(b)).then_with(|| a.min_len.cmp(&b.min_len))
    });

    // We now chunk targets, such that each chunk contains all targets belonging to the same
    // sub-trie. We are taking advantage of the following properties:
    //
    // - The first target in the chunk has the shortest sub-trie prefix (see previous sorting step).
    //
    // - The upper bound of the first target in the chunk's sub-trie will therefore be the upper
    //   bound of the whole chunk.
    //      - For example, given a chunk with sub-trie prefixes [0x2, 0x2f, 0x2fa], the upper bounds
    //        will be [0x3, 0x3, 0x2fb]. Note that no target could match a trie node with path equal
    //        to or greater than 0x3.
    //
    // - If a target's sub-trie's prefix does not lie within the bounds of the current chunk, then
    //   that target must be the first target of the next chunk, lying in a separate sub-trie.
    //      - Example: given sub-trie prefixes of [0x2, 0x2fa, 0x4c, 0x4ce, 0x4e], we would end up
    //        with the following chunks:
    //          - [0x2, 0x2fa] w/ upper bound 0x3
    //          - [0x4c 0x4ce] w/ upper bound 0x4d
    //          - [0x4e]       w/ upper bound 0x4f
    let mut upper_bound = targets.first().and_then(|t| sub_trie_upper_bound(&sub_trie_prefix(t)));
    let target_chunks = targets.chunk_by_mut(move |_, next| {
        if let Some(some_upper_bound) = upper_bound {
            let prefix = sub_trie_prefix(next);
            let same_chunk = prefix < some_upper_bound;
            if !same_chunk {
                upper_bound = sub_trie_upper_bound(&prefix);
            }
            same_chunk
        } else {
            true
        }
    });

    // Map the chunks to the return type. Within each chunk we want targets to be sorted by their
    // key, as that will be the order they are checked by the `ProofCalculator`.
    target_chunks.map(move |targets| {
        let prefix = sub_trie_prefix(&targets[0]);
        let retain_root = targets[0].min_len == 0;
        targets.sort_unstable_by_key(|target| target.key_nibbles);
        SubTrieTargets { prefix, targets, retain_root }
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::B256;

    #[test]
    fn test_iter_sub_trie_targets() {
        // Helper to create nibbles from hex string (each character is a nibble)
        let nibbles = |hex: &str| -> Nibbles {
            if hex.is_empty() {
                return Nibbles::new();
            }
            format!("0x{}", hex).parse().expect("valid nibbles hex string")
        };

        // Test cases: (input_targets, expected_output)
        // Expected output format: Vec<(exp_prefix_hex, Vec<key_hex>)>
        let test_cases = vec![
            // Case 1: Empty targets
            (vec![], vec![]),
            // Case 2: Single target without min_len
            (
                vec![ProofV2Target::new(B256::repeat_byte(0x20))],
                vec![(
                    "",
                    vec!["2020202020202020202020202020202020202020202020202020202020202020"],
                )],
            ),
            // Case 3: Multiple targets in same sub-trie (no min_len)
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)),
                    ProofV2Target::new(B256::repeat_byte(0x21)),
                ],
                vec![(
                    "",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2121212121212121212121212121212121212121212121212121212121212121",
                    ],
                )],
            ),
            // Case 4: Multiple targets in different sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    ("2", vec!["2020202020202020202020202020202020202020202020202020202020202020"]),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 5: Three targets, two in same sub-trie, one separate
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min_len(2),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                        ],
                    ),
                    ("4", vec!["4040404040404040404040404040404040404040404040404040404040404040"]),
                ],
            ),
            // Case 6: Targets with different min_len values in same sub-trie
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(3),
                ],
                vec![(
                    "2",
                    vec![
                        "2020202020202020202020202020202020202020202020202020202020202020",
                        "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                    ],
                )],
            ),
            // Case 7: More complex chunking with multiple sub-tries
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(2),
                    ProofV2Target::new(B256::repeat_byte(0x2f)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(3),
                    ProofV2Target::new(B256::repeat_byte(0x4c)).with_min_len(4),
                    ProofV2Target::new(B256::repeat_byte(0x4e)).with_min_len(3),
                ],
                vec![
                    (
                        "2",
                        vec![
                            "2020202020202020202020202020202020202020202020202020202020202020",
                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
                        ],
                    ),
                    (
                        "4c",
                        vec![
                            "4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c",
                            "4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c",
                        ],
                    ),
                    (
                        "4e",
                        vec!["4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e4e"],
                    ),
                ],
            ),
            // Case 8: Min-len 1 should result in zero-length sub-trie prefix
            (
                vec![
                    ProofV2Target::new(B256::repeat_byte(0x20)).with_min_len(1),
                    ProofV2Target::new(B256::repeat_byte(0x40)).with_min