//! Macro for benchmarking a Substrate runtime. A fork of `frame-benchmarking` pallet.

#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "std")]
mod tests;

#[cfg(feature = "std")]
pub use frame_benchmarking::Analysis;
pub use frame_benchmarking::{
	benchmarking, BenchmarkBatch, BenchmarkParameter, BenchmarkResults, Benchmarking, BenchmarkingSetup,
};
pub use paste;
#[doc(hidden)]
pub use sp_io::storage::root as storage_root;
pub use sp_runtime::traits::{Dispatchable, One, Zero};

/// Construct pallet benchmarks for weighing dispatchables.
///
/// Works around the idea of complexity parameters, named by a single letter (which is usually
/// upper cased in complexity notation but is lower-cased for use in this macro).
///
/// Complexity parameters ("parameters") have a range which is a `u32` pair. Every time a benchmark
/// is prepared and run, this parameter takes a concrete value within the range. There is an
/// associated instancing block, which is a single expression that is evaluated during
/// preparation. It may use `?` (`i.e. `return Err(...)`) to bail with a string error. Here's a
/// few examples:
///
/// ```ignore
/// // These two are equivalent:
/// let x in 0 .. 10;
/// let x in 0 .. 10 => ();
/// // This one calls a setup function and might return an error (which would be terminal).
/// let y in 0 .. 10 => setup(y)?;
/// // This one uses a code block to do lots of stuff:
/// let z in 0 .. 10 => {
///   let a = z * z / 5;
///   let b = do_something(a)?;
///   combine_into(z, b);
/// }
/// ```
///
/// Note that due to parsing restrictions, if the `from` expression is not a single token (i.e. a
/// literal or constant), then it must be parenthesised.
///
/// The macro allows for a number of "arms", each representing an individual benchmark. Using the
/// simple syntax, the associated dispatchable function maps 1:1 with the benchmark and the name of
/// the benchmark is the same as that of the associated function. However, extended syntax allows
/// for arbitrary expresions to be evaluated in a benchmark (including for example,
/// `on_initialize`).
///
/// The macro allows for common parameters whose ranges and instancing expressions may be drawn upon
/// (or not) by each arm. Syntax is available to allow for only the range to be drawn upon if
/// desired, allowing an alternative instancing expression to be given.
///
/// Note that the ranges are *inclusive* on both sides. This is in contrast to ranges in Rust which
/// are left-inclusive right-exclusive.
///
/// Each arm may also have a block of code which is run prior to any instancing and a block of code
/// which is run afterwards. All code blocks may draw upon the specific value of each parameter
/// at any time. Local variables are shared between the two pre- and post- code blocks, but do not
/// leak from the interior of any instancing expressions.
///
/// Any common parameters that are unused in an arm do not have their instancing expressions
/// evaluated.
///
/// Example:
/// ```ignore
/// use path_to_node_runtime::MyRuntime;
/// use path_to_account_id::AccountId;
/// use frame_benchmarking::account;
/// use orml_benchmarking::runtime_benchmarks;
///
/// runtime_benchmarks! {
///   // The constructed runtime struct, and the pallet to benchmark.
///   { MyRuntime, my_pallet }
///
///   // common parameter; just one for this example.
///   // will be `1`, `MAX_LENGTH` or any value inbetween
///   _ {
///     let l in 1 .. MAX_LENGTH => initialize_l(l);
///   }
///
///   // first dispatchable: foo; this is a user dispatchable and operates on a `u8` vector of
///   // size `l`, which we allow to be initialized as usual.
///   foo {
///     let caller = account::<AccountId>(b"caller", 0, benchmarks_seed);
///     let l = ...;
///   }: _(Origin::Signed(caller), vec![0u8; l])
///
///   // second dispatchable: bar; this is a root dispatchable and accepts a `u8` vector of size
///   // `l`. We don't want it pre-initialized like before so we override using the `=> ()` notation.
///   // In this case, we explicitly name the call using `bar` instead of `_`.
///   bar {
///     let l = _ .. _ => ();
///   }: bar(Origin::Root, vec![0u8; l])
///
///   // third dispatchable: baz; this is a user dispatchable. It isn't dependent on length like the
///   // other two but has its own complexity `c` that needs setting up. It uses `caller` (in the
///   // pre-instancing block) within the code block. This is only allowed in the param instancers
///   // of arms. Instancers of common params cannot optimistically draw upon hypothetical variables
///   // that the arm's pre-instancing code block might have declared.
///   baz1 {
///     let caller = account::<AccountId>(b"caller", 0, benchmarks_seed);
///     let c = 0 .. 10 => setup_c(&caller, c);
///   }: baz(Origin::Signed(caller))
///
///   // this is a second benchmark of the baz dispatchable with a different setup.
///   baz2 {
///     let caller = account::<AccountId>(b"caller", 0, benchmarks_seed);
///     let c = 0 .. 10 => setup_c_in_some_other_way(&caller, c);
///   }: baz(Origin::Signed(caller))
///
///   // this is benchmarking some code that is not a dispatchable.
///   populate_a_set {
///     let x in 0 .. 10_000;
///     let mut m = Vec::<u32>::new();
///     for i in 0..x {
///       m.insert(i);
///     }
///   }: { m.into_iter().collect::<BTreeSet>() }
/// }
/// ```
///
/// Test functions are automatically generated for each benchmark and are accessible to you when you
/// run `cargo test`. All tests are named `test_benchmark_<benchmark_name>`, expect you to pass them
/// the Runtime Trait, and run them in a test externalities environment. The test function runs your
/// benchmark just like a regular benchmark, but only testing at the lowest and highest values for
/// each component. The function will return `Ok(())` if the benchmarks return no errors.
///
/// You can optionally add a `verify` code block at the end of a benchmark to test any final state
/// of your benchmark in a unit test. For example:
///
/// ```ignore
/// sort_vector {
/// 	let x in 1 .. 10000;
/// 	let mut m = Vec::<u32>::new();
/// 	for i in (0..x).rev() {
/// 		m.push(i);
/// 	}
/// }: {
/// 	m.sort();
/// } verify {
/// 	ensure!(m[0] == 0, "You forgot to sort!")
/// }
/// ```
///
/// These `verify` blocks will not execute when running your actual benchmarks!
///
/// You can construct benchmark tests like so:
///
/// ```ignore
/// #[test]
/// fn test_benchmarks() {
///   new_test_ext().execute_with(|| {
///     assert_ok!(test_benchmark_dummy());
///     assert_err!(test_benchmark_other_name(), "Bad origin");
///     assert_ok!(test_benchmark_sort_vector());
///     assert_err!(test_benchmark_broken_benchmark(), "You forgot to sort!");
///   });
/// }
/// ```
#[macro_export]
macro_rules! runtime_benchmarks {
	(
		{ $runtime:ident, $pallet:ident }
		_ {
			$(
				let $common:ident in $common_from:tt .. $common_to:expr => $common_instancer:expr;
			)*
		}
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter!(
			NO_INSTANCE
			$runtime
			$pallet
			{ $( { $common , $common_from , $common_to , $common_instancer } )* }
			( )
			$( $rest )*
		);
	}
}

#[macro_export]
macro_rules! runtime_benchmarks_instance {
	(
		{ $runtime:ident, $pallet:ident }
		_ {
			$(
				let $common:ident in $common_from:tt .. $common_to:expr => $common_instancer:expr;
			)*
		}
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter!(
			INSTANCE
			$runtime
			$pallet
			{ $( { $common , $common_from , $common_to , $common_instancer } )* }
			( )
			$( $rest )*
		);
	}
}

#[macro_export]
#[allow(missing_docs)]
macro_rules! benchmarks_iter {
	// mutation arm:
	(
		$instance:ident
		$runtime:ident
		$pallet:ident
		{ $( $common:tt )* }
		( $( $names:ident )* )
		$name:ident { $( $code:tt )* }: _ ( $origin:expr $( , $arg:expr )* )
		verify $postcode:block
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter! {
			$instance
			$runtime
			$pallet
			{ $( $common )* }
			( $( $names )* )
			$name { $( $code )* }: $name ( $origin $( , $arg )* )
			verify $postcode
			$( $rest )*
		}
	};
	// no instance mutation arm:
	(
		NO_INSTANCE
		$runtime:ident
		$pallet:ident
		{ $( $common:tt )* }
		( $( $names:ident )* )
		$name:ident { $( $code:tt )* }: $dispatch:ident ( $origin:expr $( , $arg:expr )* )
		verify $postcode:block
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter! {
			NO_INSTANCE
			$runtime
			$pallet
			{ $( $common )* }
			( $( $names )* )
			$name { $( $code )* }: {
				<$pallet::Call<$runtime> as $crate::Dispatchable>::dispatch($pallet::Call::<$runtime>::$dispatch($($arg),*), $origin.into())?;
			}
			verify $postcode
			$( $rest )*
		}
	};
	// instance mutation arm:
	(
		INSTANCE
		$runtime:ident
		$pallet:ident
		{ $( $common:tt )* }
		( $( $names:ident )* )
		$name:ident { $( $code:tt )* }: $dispatch:ident ( $origin:expr $( , $arg:expr )* )
		verify $postcode:block
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter! {
			INSTANCE
			$runtime
			$pallet
			{ $( $common )* }
			( $( $names )* )
			$name { $( $code )* }: {
				<$pallet::Call<$runtime, I> as $crate::Dispatchable>::dispatch($pallet::Call::<$runtime, I>::$dispatch($($arg),*), $origin.into())?;
			}
			verify $postcode
			$( $rest )*
		}
	};
	// iteration arm:
	(
		$instance:ident
		$runtime:ident
		$pallet:ident
		{ $( $common:tt )* }
		( $( $names:ident )* )
		$name:ident { $( $code:tt )* }: $eval:block
		verify $postcode:block
		$( $rest:tt )*
	) => {
		$crate::benchmark_backend! {
			$instance
			$runtime
			$pallet
			$name
			{ $( $common )* }
			{ }
			{ $eval }
			{ $( $code )* }
			$postcode
		}
		$crate::benchmarks_iter!(
			$instance
			$runtime
			$pallet
			{ $( $common )* }
			( $( $names )* $name )
			$( $rest )*
		);
	};
	// iteration-exit arm
	( $instance:ident $runtime:ident $pallet:ident { $( $common:tt )* } ( $( $names:ident )* ) ) => {
		$crate::selected_benchmark!( $instance $runtime $pallet $( $names ),* );
		$crate::impl_benchmark!( $instance $runtime $pallet $( $names ),* );
		#[cfg(test)]
		$crate::impl_benchmark_tests!( $instance $runtime $pallet $( $names ),* );
	};
	// add verify block to _() format
	(
		$instance:ident
		$runtime:ident
		$pallet:ident
		{ $( $common:tt )* }
		( $( $names:ident )* )
		$name:ident { $( $code:tt )* }: _ ( $origin:expr $( , $arg:expr )* )
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter! {
			$instance
			$runtime
			$pallet
			{ $( $common )* }
			( $( $names )* )
			$name { $( $code )* }: _ ( $origin $( , $arg )* )
			verify { }
			$( $rest )*
		}
	};
	// add verify block to name() format
	(
		$instance:ident
		$runtime:ident
		$pallet:ident
		{ $( $common:tt )* }
		( $( $names:ident )* )
		$name:ident { $( $code:tt )* }: $dispatch:ident ( $origin:expr $( , $arg:expr )* )
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter! {
			$instance
			$runtime
			$pallet
			{ $( $common )* }
			( $( $names )* )
			$name { $( $code )* }: $dispatch ( $origin $( , $arg )* )
			verify { }
			$( $rest )*
		}
	};
	// add verify block to {} format
	(
		$instance:ident
		$runtime:ident
		$pallet:ident
		{ $( $common:tt )* }
		( $( $names:ident )* )
		$name:ident { $( $code:tt )* }: $eval:block
		$( $rest:tt )*
	) => {
		$crate::benchmarks_iter!(
			$instance
			$runtime
			$pallet
			{ $( $common )* }
			( $( $names )* )
			$name { $( $code )* }: $eval
			verify { }
			$( $rest )*
		);
	};
}

#[macro_export]
#[allow(missing_docs)]
macro_rules! benchmark_backend {
	// parsing arms
	($instance:ident $runtime:ident $pallet:ident $name:ident {
		$( $common:tt )*
	} {
		$( PRE { $( $pre_parsed:tt )* } )*
	} { $eval:block } {
			let $pre_id:tt : $pre_ty:ty = $pre_ex:expr;
			$( $rest:tt )*
	} $postcode:block) => {
		$crate::benchmark_backend! {
			$instance $runtime $pallet $name { $( $common )* } {
				$( PRE { $( $pre_parsed )* } )*
				PRE { $pre_id , $pre_ty , $pre_ex }
			} { $eval } { $( $rest )* } $postcode
		}
	};
	($instance:ident $runtime:ident $pallet:ident $name:ident {
		$( $common:tt )*
	} {
		$( $parsed:tt )*
	} { $eval:block } {
		let $param:ident in ( $param_from:expr ) .. $param_to:expr => $param_instancer:expr;
		$( $rest:tt )*
	} $postcode:block) => {
		$crate::benchmark_backend! {
			$instance $runtime $pallet $name { $( $common )* } {
				$( $parsed )*
				PARAM { $param , $param_from , $param_to , $param_instancer }
			} { $eval } { $( $rest )* } $postcode
		}
	};
	// mutation arm to look after defaulting to a common param
	($instance:ident $runtime:ident $pallet:ident $name:ident {
		$( { $common:ident , $common_from:tt , $common_to:expr , $common_instancer:expr } )*
	} {
		$( $parsed:tt )*
	} { $eval:block } {
		let $param:ident in ...;
		$( $rest:tt )*
	} $postcode:block) => {
		$crate::benchmark_backend! {
			$instance $runtime $pallet $name {
				$( { $common , $common_from , $common_to , $common_instancer } )*
			} {
				$( $parsed )*
			} { $eval } {
				let $param
					in ({ $( let $common = $common_from; )* $param })
					.. ({ $( let $common = $common_to; )* $param })
					=> ({ $( let $common = || -> Result<(), &'static str> { $common_instancer ; Ok(()) }; )* $param()? });
				$( $rest )*
			} $postcode
		}
	};
	// mutation arm to look after defaulting only the range to common param
	($instance:ident $runtime:ident $pallet:ident $name:ident {
		$( { $common:ident , $common_from:tt , $common_to:expr , $common_instancer:expr } )*
	} {
		$( $parsed:tt )*
	} { $eval:block } {
		let $param:ident in _ .. _ => $param_instancer:expr ;
		$( $rest:tt )*
	} $postcode:block) => {
		$crate::benchmark_backend! {
			$instance $runtime $pallet $name {
				$( { $common , $common_from , $common_to , $common_instancer } )*
			} {
				$( $parsed )*
			} { $eval } {
				let $param
					in ({ $( let $common = $common_from; )* $param })
					.. ({ $( let $common = $common_to; )* $param })
					=> $param_instancer ;
				$( $rest )*
			} $postcode
		}
	};
	// mutation arm to look after a single tt for param_from.
	($instance:ident $runtime:ident $pallet:ident $name:ident {
		$( $common:tt )*
	} {
		$( $parsed:tt )*
	} { $eval:block } {
		let $param:ident in $param_from:tt .. $param_to:expr => $param_instancer:expr ;
		$( $rest:tt )*
	} $postcode:block) => {
		$crate::benchmark_backend! {
			$instance $runtime $pallet $name { $( $common )* } { $( $parsed )* } { $eval } {
				let $param in ( $param_from ) .. $param_to => $param_instancer;
				$( $rest )*
			} $postcode
		}
	};
	// mutation arm to look after the default tail of `=> ()`
	($instance:ident $runtime:ident $pallet:ident $name:ident {
		$( $common:tt )*
	} {
		$( $parsed:tt )*
	} { $eval:block } {
		let $param:ident in $param_from:tt .. $param_to:expr;
		$( $rest:tt )*
	} $postcode:block) => {
		$crate::benchmark_backend! {
			$instance $runtime $pallet $name { $( $common )* } { $( $parsed )* } { $eval } {
				let $param in $param_from .. $param_to => ();
				$( $rest )*
			} $postcode
		}
	};
	// mutation arm to look after `let _ =`
	($instance:ident $runtime:ident $pallet:ident $name:ident {
		$( $common:tt )*
	} {
		$( $parsed:tt )*
	} { $eval:block } {
		let $pre_id:tt = $pre_ex:expr;
		$( $rest:tt )*
	} $postcode:block) => {
		$crate::benchmark_backend! {
			$instance $runtime $pallet $name { $( $common )* } { $( $parsed )* } { $eval } {
				let $pre_id : _ = $pre_ex;
				$( $rest )*
			} $postcode
		}
	};
	// no instance actioning arm
	(NO_INSTANCE $runtime:ident $pallet:ident $name:ident {
		$( { $common:ident , $common_from:tt , $common_to:expr , $common_instancer:expr } )*
	} {
		$( PRE { $pre_id:tt , $pre_ty:ty , $pre_ex:expr } )*
		$( PARAM { $param:ident , $param_from:expr , $param_to:expr , $param_instancer:expr } )*
	} { $eval:block } { $( $post:tt )* } $postcode:block) => {
		#[allow(non_camel_case_types)]
		struct $name;
		#[allow(unused_variables)]
		impl $crate::BenchmarkingSetup<$runtime> for $name {
			fn components(&self) -> Vec<($crate::BenchmarkParameter, u32, u32)> {
				vec! [
					$(
						($crate::BenchmarkParameter::$param, $param_from, $param_to)
					),*
				]
			}

			fn instance(&self, components: &[($crate::BenchmarkParameter, u32)])
				-> Result<Box<dyn FnOnce() -> Result<(), &'static str>>, &'static str>
			{
				$(
					let $common = $common_from;
				)*
				$(
					// Prepare instance
					let $param = components.iter().find(|&c| c.0 == $crate::BenchmarkParameter::$param).unwrap().1;
				)*
				$(
					let $pre_id : $pre_ty = $pre_ex;
				)*
				$( $param_instancer ; )*
				$( $post )*

				Ok(Box::new(move || -> Result<(), &'static str> { $eval; Ok(()) }))
			}

			fn verify(&self, components: &[($crate::BenchmarkParameter, u32)])
				-> Result<Box<dyn FnOnce() -> Result<(), &'static str>>, &'static str>
			{
				$(
					let $common = $common_from;
				)*
				$(
					// Prepare instance
					let $param = components.iter().find(|&c| c.0 == $crate::BenchmarkParameter::$param).unwrap().1;
				)*
				$(
					let $pre_id : $pre_ty = $pre_ex;
				)*
				$( $param_instancer ; )*
				$( $post )*

				Ok(Box::new(move || -> Result<(), &'static str> { $eval; $postcode; Ok(()) }))
			}
		}
	};
	// instance actioning arm
	(INSTANCE $runtime:ident $pallet:ident $name:ident {
		$( { $common:ident , $common_from:tt , $common_to:expr , $common_instancer:expr } )*
	} {
		$( PRE { $pre_id:tt , $pre_ty:ty , $pre_ex:expr } )*
		$( PARAM { $param:ident , $param_from:expr , $param_to:expr , $param_instancer:expr } )*
	} { $eval:block } { $( $post:tt )* } $postcode:block) => {
		#[allow(non_camel_case_types)]
		struct $name;
		#[allow(unused_variables)]
		impl<I: Instance> $crate::BenchmarkingSetupInstance<$runtime, I> for $name {
			fn components(&self) -> Vec<($crate::BenchmarkParameter, u32, u32)> {
				vec! [
					$(
						($crate::BenchmarkParameter::$param, $param_from, $param_to)
					),*
				]
			}

			fn instance(&self, components: &[($crate::BenchmarkParameter, u32)])
				-> Result<Box<dyn FnOnce() -> Result<(), &'static str>>, &'static str>
			{
				$(
					let $common = $common_from;
				)*
				$(
					// Prepare instance
					let $param = components.iter().find(|&c| c.0 == $crate::BenchmarkParameter::$param).unwrap().1;
				)*
				$(
					let $pre_id : $pre_ty = $pre_ex;
				)*
				$( $param_instancer ; )*
				$( $post )*

				Ok(Box::new(move || -> Result<(), &'static str> { $eval; Ok(()) }))
			}

			fn verify(&self, components: &[($crate::BenchmarkParameter, u32)])
				-> Result<Box<dyn FnOnce() -> Result<(), &'static str>>, &'static str>
			{
				$(
					let $common = $common_from;
				)*
				$(
					// Prepare instance
					let $param = components.iter().find(|&c| c.0 == $crate::BenchmarkParameter::$param).unwrap().1;
				)*
				$(
					let $pre_id : $pre_ty = $pre_ex;
				)*
				$( $param_instancer ; )*
				$( $post )*

				Ok(Box::new(move || -> Result<(), &'static str> { $eval; $postcode; Ok(()) }))
			}
		}
	}
}

/// Creates a `SelectedBenchmark` enum implementing `BenchmarkingSetup`.
///
/// Every variant must implement [`BenchmarkingSetup`].
///
/// ```nocompile
///
/// struct Transfer;
/// impl BenchmarkingSetup for Transfer { ... }
///
/// struct SetBalance;
/// impl BenchmarkingSetup for SetBalance { ... }
///
/// selected_benchmark!(Transfer, SetBalance);
/// ```
#[macro_export]
macro_rules! selected_benchmark {
	(
		NO_INSTANCE $runtime:ident $pallet:ident $( $bench:ident ),*
	) => {
		// The list of available benchmarks for this pallet.
		#[allow(non_camel_case_types)]
		enum SelectedBenchmark {
			$( $bench, )*
		}

		// Allow us to select a benchmark from the list of available benchmarks.
		impl $crate::BenchmarkingSetup<$runtime> for SelectedBenchmark {
			fn components(&self) -> Vec<($crate::BenchmarkParameter, u32, u32)> {
				match self {
					$( Self::$bench => <$bench as $crate::BenchmarkingSetup<$runtime>>::components(&$bench), )*
				}
			}

			fn instance(&self, components: &[($crate::BenchmarkParameter, u32)])
				-> Result<Box<dyn FnOnce() -> Result<(), &'static str>>, &'static str>
			{
				match self {
					$( Self::$bench => <$bench as $crate::BenchmarkingSetup<$runtime>>::instance(&$bench, components), )*
				}
			}

			fn verify(&self, components: &[($crate::BenchmarkParameter, u32)])
				-> Result<Box<dyn FnOnce() -> Result<(), &'static str>>, &'static str>
			{
				match self {
					$( Self::$bench => <$bench as $crate::BenchmarkingSetup<$runtime>>::verify(&$bench, components), )*
				}
			}
		}
	};
	(
		INSTANCE $runtime:ident $pallet:ident $( $bench:ident ),*
	) => {
		// The list of available benchmarks for this pallet.
		#[allow(non_camel_case_types)]
		enum SelectedBenchmark {
			$( $bench, )*
		}

		// Allow us to select a benchmark from the list of available benchmarks.
		impl<I: Instance> $crate::BenchmarkingSetupInstance<$runtime, I> for SelectedBenchmark {
			fn components(&self) -> Vec<($crate::BenchmarkParameter, u32, u32)> {
				match self {
					$( Self::$bench => <$bench as $crate::BenchmarkingSetupInstance<$runtime, I>>::components(&$bench), )*
				}
			}

			fn instance(&self, components: &[($crate::BenchmarkParameter, u32)])
				-> Result<Box<dyn FnOnce() -> Result<(), &'static str>>, &'static str>
			{
				match self {
					$( Self::$bench => <$bench as $crate::BenchmarkingSetupInstance<$runtime, I>>::instance(&$bench, components), )*
				}
			}

			fn verify(&self, components: &[($crate::BenchmarkParameter, u32)])
				-> Result<Box<dyn FnOnce() -> Result<(), &'static str>>, &'static str>
			{
				match self {
					$( Self::$bench => <$bench as $crate::BenchmarkingSetupInstance<$runtime, I>>::verify(&$bench, components), )*
				}
			}
		}
	}
}

#[macro_export]
macro_rules! impl_benchmark {
	(
		NO_INSTANCE $runtime:ident $pallet:ident $( $name:ident ),*
	) => {
		pub struct Benchmark;

		impl $crate::Benchmarking<$crate::BenchmarkResults> for Benchmark {
			fn benchmarks() -> Vec<&'static [u8]> {
				vec![ $( stringify!($name).as_ref() ),* ]
			}

			fn run_benchmark(
				extrinsic: &[u8],
				lowest_range_values: &[u32],
				highest_range_values: &[u32],
				steps: &[u32],
				repeat: u32,
			) -> Result<Vec<$crate::BenchmarkResults>, &'static str> {
				// Map the input to the selected benchmark.
				let extrinsic = sp_std::str::from_utf8(extrinsic)
					.map_err(|_| "`extrinsic` is not a valid utf8 string!")?;
				let selected_benchmark = match extrinsic {
					$( stringify!($name) => SelectedBenchmark::$name, )*
					_ => return Err("Could not find extrinsic."),
				};

				// Warm up the DB
				$crate::benchmarking::commit_db();
				$crate::benchmarking::wipe_db();

				let components = <SelectedBenchmark as $crate::BenchmarkingSetup<$runtime>>::components(&selected_benchmark);
				let mut results: Vec<$crate::BenchmarkResults> = Vec::new();

				// Default number of steps for a component.
				let mut prev_steps = 10;

				// Select the component we will be benchmarking. Each component will be benchmarked.
				for (idx, (name, low, high)) in components.iter().enumerate() {
					// Get the number of steps for this component.
					let steps = steps.get(idx).cloned().unwrap_or(prev_steps);
					prev_steps = steps;

					// Skip this loop if steps is zero
					if steps == 0 { continue }

					let lowest = lowest_range_values.get(idx).cloned().unwrap_or(*low);
					let highest = highest_range_values.get(idx).cloned().unwrap_or(*high);

					let diff = highest - lowest;

					// Create up to `STEPS` steps for that component between high and low.
					let step_size = (diff / steps).max(1);
					let num_of_steps = diff / step_size + 1;

					for s in 0..num_of_steps {
						// This is the value we will be testing for component `name`
						let component_value = lowest + step_size * s;

						// Select the max value for all the other components.
						let c: Vec<($crate::BenchmarkParameter, u32)> = components.iter()
							.enumerate()
							.map(|(idx, (n, _, h))|
								if n == name {
									(*n, component_value)
								} else {
									(*n, *highest_range_values.get(idx).unwrap_or(h))
								}
							)
							.collect();

						// Run the benchmark `repeat` times.
						for _ in 0..repeat {
							// Set up the externalities environment for the setup we want to benchmark.
							let closure_to_benchmark = <SelectedBenchmark as $crate::BenchmarkingSetup<$runtime>>::instance(&selected_benchmark, &c)?;

							// Set the block number to at least 1 so events are deposited.
							if $crate::Zero::is_zero(&frame_system::Module::<$runtime>::block_number()) {
								frame_system::Module::<$runtime>::set_block_number(1u8.into());
							}

							// Commit the externalities to the database, flushing the DB cache.
							// This will enable worst case scenario for reading from the database.
							$crate::benchmarking::commit_db();

							// Time the extrinsic logic.
							frame_support::debug::trace!(target: "benchmark", "Start Benchmark: {:?} {:?}", name, component_value);
							let start_extrinsic = $crate::benchmarking::current_time();
							closure_to_benchmark()?;
							let finish_extrinsic = $crate::benchmarking::current_time();
							let elapsed_extrinsic = finish_extrinsic - start_extrinsic;
							frame_support::debug::trace!(target: "benchmark", "End Benchmark: {} ns", elapsed_extrinsic);

							// Time the storage root recalculation.
							let start_storage_root = $crate::benchmarking::current_time();
							$crate::storage_root();
							let finish_storage_root = $crate::benchmarking::current_time();
							let elapsed_storage_root = finish_storage_root - start_storage_root;

							results.push((c.clone(), elapsed_extrinsic, elapsed_storage_root));

							// Wipe the DB back to the genesis state.
							$crate::benchmarking::wipe_db();
						}
					}
				}
				return Ok(results);
			}
		}
	};
	(
		INSTANCE $runtime:ident $pallet:ident $( $name:ident ),*
	) => {
		pub struct Benchmark<I>($crate::sp_std::marker::PhantomData(I));

		impl<I: Instance> $crate::Benchmarking<$crate::BenchmarkResults> for Benchmark<I> {
			fn benchmarks() -> Vec<&'static [u8]> {
				vec![ $( stringify!($name).as_ref() ),* ]
			}

			fn run_benchmark(
				extrinsic: &[u8],
				lowest_range_values: &[u32],
				highest_range_values: &[u32],
				steps: &[u32],
				repeat: u32,
			) -> Result<Vec<$crate::BenchmarkResults>, &'static str> {
				// Map the input to the selected benchmark.
				let extrinsic = sp_std::str::from_utf8(extrinsic)
					.map_err(|_| "`extrinsic` is not a valid utf8 string!")?;
				let selected_benchmark = match extrinsic {
					$( stringify!($name) => SelectedBenchmark::$name, )*
					_ => return Err("Could not find extrinsic."),
				};

				// Warm up the DB
				$crate::benchmarking::commit_db();
				$crate::benchmarking::wipe_db();

				let components = <SelectedBenchmark as $crate::BenchmarkingSetupInstance<$runtime, I>>::components(&selected_benchmark);
				let mut results: Vec<$crate::BenchmarkResults> = Vec::new();

				// Default number of steps for a component.
				let mut prev_steps = 10;

				// Select the component we will be benchmarking. Each component will be benchmarked.
				for (idx, (name, low, high)) in components.iter().enumerate() {
					// Get the number of steps for this component.
					let steps = steps.get(idx).cloned().unwrap_or(prev_steps);
					prev_steps = steps;

					// Skip this loop if steps is zero
					if steps == 0 { continue }

					let lowest = lowest_range_values.get(idx).cloned().unwrap_or(*low);
					let highest = highest_range_values.get(idx).cloned().unwrap_or(*high);

					let diff = highest - lowest;

					// Create up to `STEPS` steps for that component between high and low.
					let step_size = (diff / steps).max(1);
					let num_of_steps = diff / step_size + 1;

					for s in 0..num_of_steps {
						// This is the value we will be testing for component `name`
						let component_value = lowest + step_size * s;

						// Select the max value for all the other components.
						let c: Vec<($crate::BenchmarkParameter, u32)> = components.iter()
							.enumerate()
							.map(|(idx, (n, _, h))|
								if n == name {
									(*n, component_value)
								} else {
									(*n, *highest_range_values.get(idx).unwrap_or(h))
								}
							)
							.collect();

						// Run the benchmark `repeat` times.
						for _ in 0..repeat {
							// Set up the externalities environment for the setup we want to benchmark.
							let closure_to_benchmark = <SelectedBenchmark as $crate::BenchmarkingSetupInstance<$runtime, I>>::instance(&selected_benchmark, &c)?;

							// Set the block number to at least 1 so events are deposited.
							if $crate::Zero::is_zero(&frame_system::Module::<$runtime>::block_number()) {
								frame_system::Module::<$runtime>::set_block_number($crate::One::one());
							}

							// Commit the externalities to the database, flushing the DB cache.
							// This will enable worst case scenario for reading from the database.
							$crate::benchmarking::commit_db();

							// Time the extrinsic logic.
							frame_support::debug::trace!(target: "benchmark", "Start Benchmark: {:?} {:?}", name, component_value);
							let start_extrinsic = $crate::benchmarking::current_time();
							closure_to_benchmark()?;
							let finish_extrinsic = $crate::benchmarking::current_time();
							let elapsed_extrinsic = finish_extrinsic - start_extrinsic;
							frame_support::debug::trace!(target: "benchmark", "End Benchmark: {} ns", elapsed_extrinsic);

							// Time the storage root recalculation.
							let start_storage_root = $crate::benchmarking::current_time();
							$crate::storage_root();
							let finish_storage_root = $crate::benchmarking::current_time();
							let elapsed_storage_root = finish_storage_root - start_storage_root;

							results.push((c.clone(), elapsed_extrinsic, elapsed_storage_root));

							// Wipe the DB back to the genesis state.
							$crate::benchmarking::wipe_db();
						}
					}
				}
				return Ok(results);
			}
		}
	}
}

// This creates unit tests from the main benchmark macro.
// They run the benchmark using the `high` and `low` value for each component
// and ensure that everything completes successfully.
#[macro_export]
macro_rules! impl_benchmark_tests {
	(
		NO_INSTANCE
		$runtime:ident
		$pallet:ident
		$( $name:ident ),*
	) => {
		$(
			$crate::paste::item! {
				fn [<test_benchmark_ $name>] () -> Result<(), &'static str>
				{
					let selected_benchmark = SelectedBenchmark::$name;
					let components = <SelectedBenchmark as $crate::BenchmarkingSetup<$runtime>>::components(&selected_benchmark);

					for (_, (name, low, high)) in components.iter().enumerate() {
						// Test only the low and high value, assuming values in the middle won't break
						for component_value in vec![low, high] {
							// Select the max value for all the other components.
							let c: Vec<($crate::BenchmarkParameter, u32)> = components.iter()
								.enumerate()
								.map(|(_, (n, _, h))|
									if n == name {
										(*n, *component_value)
									} else {
										(*n, *h)
									}
								)
								.collect();

							// Set up the verification state
							let closure_to_verify = <SelectedBenchmark as $crate::BenchmarkingSetup<$runtime>>::verify(&selected_benchmark, &c)?;

							// Set the block number to at least 1 so events are deposited.
							if $crate::Zero::is_zero(&frame_system::Module::<$runtime>::block_number()) {
								frame_system::Module::<$runtime>::set_block_number($crate::One::one());
							}

							// Run verification
							closure_to_verify()?;

							// Reset the state
							$crate::benchmarking::wipe_db();
						}
					}
					Ok(())
				}
			}
		)*
	};
	(
		INSTANCE
		$runtime:ident
		$pallet:ident
		$( $name:ident ),*
	) => {
		$(
			$crate::paste::item! {
				fn [<test_benchmark_ $name>] () -> Result<(), &'static str>
				{
					let selected_benchmark = SelectedBenchmark::$name;
					let components = <SelectedBenchmark as $crate::BenchmarkingSetupInstance<$runtime, _>>::components(&selected_benchmark);

					for (_, (name, low, high)) in components.iter().enumerate() {
						// Test only the low and high value, assuming values in the middle won't break
						for component_value in vec![low, high] {
							// Select the max value for all the other components.
							let c: Vec<($crate::BenchmarkParameter, u32)> = components.iter()
								.enumerate()
								.map(|(_, (n, _, h))|
									if n == name {
										(*n, *component_value)
									} else {
										(*n, *h)
									}
								)
								.collect();

							// Set up the verification state
							let closure_to_verify = <SelectedBenchmark as $crate::BenchmarkingSetupInstance<$runtime, _>>::verify(&selected_benchmark, &c)?;

							// Set the block number to at least 1 so events are deposited.
							if $crate::Zero::is_zero(&frame_system::Module::<$runtime>::block_number()) {
								frame_system::Module::<$runtime>::set_block_number(1.into());
							}

							// Run verification
							closure_to_verify()?;

							// Reset the state
							$crate::benchmarking::wipe_db();
						}
					}
					Ok(())
				}
			}
		)*
	};
}

/// This macro adds pallet benchmarks to a `Vec<BenchmarkBatch>` object.
///
/// First create an object that holds in the input parameters for the benchmark:
///
/// ```ignore
/// let params = (&pallet, &benchmark, &lowest_range_values, &highest_range_values, &steps, repeat);
/// ```
///
/// Then define a mutable local variable to hold your `BenchmarkBatch` object:
///
/// ```ignore
/// let mut batches = Vec::<BenchmarkBatch>::new();
/// ````
///
/// Then add the pallets you want to benchmark to this object, including the string
/// you want to use target a particular pallet:
///
/// ```ignore
/// add_benchmark!(params, batches, b"balances", Balances);
/// add_benchmark!(params, batches, b"identity", Identity);
/// add_benchmark!(params, batches, b"session", SessionBench::<Runtime>);
/// ...
/// ```
///
/// At the end of `dispatch_benchmark`, you should return this batches object.
#[macro_export]
macro_rules! add_benchmark {
	( $params:ident, $batches:ident, $name:literal, $( $location:tt )* ) => (
		let (pallet, benchmark, lowest_range_values, highest_range_values, steps, repeat) = $params;
		if &pallet[..] == &$name[..] || &pallet[..] == &b"*"[..] {
			if &pallet[..] == &b"*"[..] || &benchmark[..] == &b"*"[..] {
				for benchmark in $( $location )*::Benchmark::benchmarks().into_iter() {
					$batches.push($crate::BenchmarkBatch {
						results: $( $location )*::Benchmark::run_benchmark(
							benchmark,
							&lowest_range_values[..],
							&highest_range_values[..],
							&steps[..],
							repeat,
						)?,
						pallet: $name.to_vec(),
						benchmark: benchmark.to_vec(),
					});
				}
			} else {
				$batches.push($crate::BenchmarkBatch {
					results: $( $location )*::Benchmark::run_benchmark(
						&benchmark[..],
						&lowest_range_values[..],
						&highest_range_values[..],
						&steps[..],
						repeat,
					)?,
					pallet: $name.to_vec(),
					benchmark: benchmark.clone(),
				});
			}
		}
	)
}