#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(feature = "std")]
mod rstd {
	pub use std::vec::Vec;
	pub use std::cmp;
	pub use std::collections::{BTreeMap, VecDeque};
}
#[cfg(not(feature = "std"))]
mod rstd {
	pub use core::cmp;
	pub use alloc::collections::{BTreeMap, VecDeque};
	pub use alloc::vec::Vec;
}
use self::rstd::*;
pub use hash_db::Hasher;
pub trait TrieStream {
	
	fn new() -> Self;
	
	fn append_empty_data(&mut self);
	
	
	fn begin_branch(
		&mut self,
		maybe_key: Option<&[u8]>,
		maybe_value: Option<&[u8]>,
		has_children: impl Iterator<Item = bool>,
	);
	
	fn append_empty_child(&mut self) {}
	
	
	fn end_branch(&mut self, _value: Option<&[u8]>) {}
	
	fn append_leaf(&mut self, key: &[u8], value: &[u8]);
	
	fn append_extension(&mut self, key: &[u8]);
	
	fn append_substream<H: Hasher>(&mut self, other: Self);
	
	fn out(self) -> Vec<u8>;
}
fn shared_prefix_length<T: Eq>(first: &[T], second: &[T]) -> usize {
	first.iter()
		.zip(second.iter())
		.position(|(f, s)| f != s)
		.unwrap_or_else(|| cmp::min(first.len(), second.len()))
}
pub fn trie_root<H, S, I, A, B>(input: I) -> H::Out where
	I: IntoIterator<Item = (A, B)>,
	A: AsRef<[u8]> + Ord,
	B: AsRef<[u8]>,
	H: Hasher,
	S: TrieStream,
{
	trie_root_inner::<H, S, I, A, B>(input, false)
}
fn trie_root_inner<H, S, I, A, B>(input: I, no_extension: bool) -> H::Out where
	I: IntoIterator<Item = (A, B)>,
	A: AsRef<[u8]> + Ord,
	B: AsRef<[u8]>,
	H: Hasher,
	S: TrieStream,
{
	
	let input = input
		.into_iter()
		.collect::<BTreeMap<_, _>>();
	
	let mut nibbles = Vec::with_capacity(input.keys().map(|k| k.as_ref().len()).sum::<usize>() * 2);
	let mut lens = Vec::with_capacity(input.len() + 1);
	lens.push(0);
	for k in input.keys() {
		for &b in k.as_ref() {
			nibbles.push(b >> 4);
			nibbles.push(b & 0x0F);
		}
		lens.push(nibbles.len());
	}
	
	let input = input.into_iter().zip(lens.windows(2))
		.map(|((_, v), w)| (&nibbles[w[0]..w[1]], v))
		.collect::<Vec<_>>();
	let mut stream = S::new();
	build_trie::<H, S, _, _>(&input, 0, &mut stream, no_extension);
	H::hash(&stream.out())
}
pub fn trie_root_no_extension<H, S, I, A, B>(input: I) -> H::Out where
	I: IntoIterator<Item = (A, B)>,
	A: AsRef<[u8]> + Ord,
	B: AsRef<[u8]>,
	H: Hasher,
	S: TrieStream,
{
	trie_root_inner::<H, S, I, A, B>(input, true)
}
pub fn unhashed_trie<H, S, I, A, B>(input: I) -> Vec<u8> where
	I: IntoIterator<Item = (A, B)>,
	A: AsRef<[u8]> + Ord,
	B: AsRef<[u8]>,
	H: Hasher,
	S: TrieStream,
{
	unhashed_trie_inner::<H, S, I, A, B>(input, false)
}
fn unhashed_trie_inner<H, S, I, A, B>(input: I, no_extension: bool) -> Vec<u8> where
	I: IntoIterator<Item = (A, B)>,
	A: AsRef<[u8]> + Ord,
	B: AsRef<[u8]>,
	H: Hasher,
	S: TrieStream,
{
	
	let input = input
		.into_iter()
		.collect::<BTreeMap<_, _>>();
	let mut nibbles = Vec::with_capacity(input.keys().map(|k| k.as_ref().len()).sum::<usize>() * 2);
	let mut lens = Vec::with_capacity(input.len() + 1);
	lens.push(0);
	for k in input.keys() {
		for &b in k.as_ref() {
			nibbles.push(b >> 4);
			nibbles.push(b & 0x0F);
		}
		lens.push(nibbles.len());
	}
	
	let input = input.into_iter().zip(lens.windows(2))
		.map(|((_, v), w)| (&nibbles[w[0]..w[1]], v))
		.collect::<Vec<_>>();
	let mut stream = S::new();
	build_trie::<H, S, _, _>(&input, 0, &mut stream, no_extension);
	stream.out()
}
pub fn unhashed_trie_no_extension<H, S, I, A, B>(input: I) -> Vec<u8> where
	I: IntoIterator<Item = (A, B)>,
	A: AsRef<[u8]> + Ord,
	B: AsRef<[u8]>,
	H: Hasher,
	S: TrieStream,
{
	unhashed_trie_inner::<H, S, I, A, B>(input, true)
}
pub fn sec_trie_root<H, S, I, A, B>(input: I) -> H::Out where
	I: IntoIterator<Item = (A, B)>,
	A: AsRef<[u8]>,
	B: AsRef<[u8]>,
	H: Hasher,
	H::Out: Ord,
	S: TrieStream,
{
	trie_root::<H, S, _, _, _>(input.into_iter().map(|(k, v)| (H::hash(k.as_ref()), v)))
}
fn build_trie<H, S, A, B>(input: &[(A, B)], cursor: usize, stream: &mut S, no_extension: bool) where
	A: AsRef<[u8]>,
	B: AsRef<[u8]>,
	H: Hasher,
	S: TrieStream,
{
	match input.len() {
		
		0 => stream.append_empty_data(),
		
		1 => stream.append_leaf(&input[0].0.as_ref()[cursor..], &input[0].1.as_ref() ),
		
		
		_ => {
			let (key, value) = (&input[0].0.as_ref(), input[0].1.as_ref());
			
			
			
			let shared_nibble_count = input.iter().skip(1).fold(key.len(), |acc, &(ref k, _)| {
				cmp::min( shared_prefix_length(key, k.as_ref()), acc )
			});
			
			
			
			
			let (cursor, o_branch_slice) = if no_extension {
				if shared_nibble_count > cursor {
					(shared_nibble_count, Some(&key[cursor..shared_nibble_count]))
				} else {
					(cursor, Some(&key[0..0]))
				}
			} else if shared_nibble_count > cursor {
				stream.append_extension(&key[cursor..shared_nibble_count]);
				build_trie_trampoline::<H, _, _, _>(
					input,
					shared_nibble_count,
					stream,
					no_extension,
				);
				return;
			} else { (cursor, None) };
			
			
			
			
			let value = if cursor == key.len() { Some(value) } else { None };
			
			let mut shared_nibble_counts = [0usize; 16];
			{
				
				
				
				
				let mut begin = match value { None => 0, _ => 1 };
				for i in 0..16 {
					shared_nibble_counts[i] = input[begin..].iter()
						.take_while(|(k, _)| k.as_ref()[cursor] == i as u8)
						.count();
					begin += shared_nibble_counts[i];
				}
			}
			
			stream.begin_branch(o_branch_slice, value, shared_nibble_counts.iter().map(|&n| n > 0));
			
			
			let mut begin = match value { None => 0, _ => 1 };
			for &count in &shared_nibble_counts {
				if count > 0 {
					build_trie_trampoline::<H, S, _, _>(
						&input[begin..(begin + count)],
						cursor + 1,
						stream,
						no_extension,
					);
					begin += count;
				} else {
					stream.append_empty_child();
				}
			}
			stream.end_branch(value);
		}
	}
}
fn build_trie_trampoline<H, S, A, B>(
	input: &[(A, B)],
	cursor: usize,
	stream: &mut S,
	no_extension: bool,
) where
	A: AsRef<[u8]>,
	B: AsRef<[u8]>,
	H: Hasher,
	S: TrieStream,
{
	let mut substream = S::new();
	build_trie::<H, _, _, _>(input, cursor, &mut substream, no_extension);
	stream.append_substream::<H>(substream);
}