define_modint!(MInt; Modulus: i32 = 1000000007);

fn main() {
	let mut s = String::new();
	use std::io::Read;
	std::io::stdin().read_to_string(&mut s).unwrap();
	let mut s = s.split_whitespace();
	let n: u32 = s.next().unwrap().parse().unwrap();
	let m: i32 = s.next().unwrap().parse().unwrap();
	let fmod = math::number::modint::table::FactorialMod::<Modulus>::new(n as usize + 100);
	use num::One;
	let mut ans = MInt::one();
	for (_, e) in factorize(m) {
		ans = ans * fmod.combination(n - 1 + e, e);
	}
	println!("{}", ans);
}

pub fn factorize<T>(n: T) -> impl std::iter::Iterator<Item = (T, u32)>
where
	T: num::PrimInt,
{
	let mut n = n;
	let mut p = T::one() + T::one();
	std::iter::from_fn(move || {
		if n > T::one() {
			while p * p <= n {
				let mut e = 0;
				while n % p == T::zero() {
					n = n / p;
					e += 1;
				}
				if e > 0 {
					return Some((p, e));
				}
				p = p + T::one();
			}
			let m = n;
			n = T::one();
			Some((m, 1))
		} else {
			None
		}
	})
}

pub mod traits {
	pub mod consts {
		pub trait Constant: super::marker::Unsafe {
			type Type;
			fn value() -> Self::Type;
		}

		unsafe impl<T: LiteralConstant> super::marker::Unsafe for T {}

		pub trait LiteralConstant {
			type Type;
			const VALUE: Self::Type;
		}

		impl<T: LiteralConstant> Constant for T {
			type Type = <Self as LiteralConstant>::Type;
			fn value() -> Self::Type {
				Self::VALUE
			}
		}
	}

	pub mod marker {
		pub unsafe trait Unsafe {}
	}

	pub mod integer {
		pub trait SizeOrder<T> {
			type LargerType;
			type SmallerType;
		}

		pub trait IntoLarger {
			type LargerType;
		}

		macro_rules! impl_ {
			($i0:ty:$u0:ty, $($is:ty:$us:ty),+) => {
				impl_![imp $i0:$u0];
				impl_![imp $i0];
				impl_![imp $u0];
				$(impl_![imp $i0:$u0, $is:$us];)+
				impl_![$($is:$us),+];
			};

			($i:ty:$u:ty) => {
				impl_![imp $i:$u];
				impl_![imp $i];
				impl_![imp $u];
			};

			(imp $t:ty) => {
				impl_![imp $t | $t, $t:$t];
			};

			(imp $i:ty:$u:ty) => {
				impl_![imp $u | $i, $u:$i];
				impl_![imp $i | $u, $u:$i];
			};

			(imp $i0:ty:$u0:ty, $i1:ty:$u1:ty) => {
				impl_![imp $i1 | $i0, $i1:$i0];
				impl_![imp $u1 | $u0, $u1:$u0];
				impl_![imp $i1 | $u0, $i1:$u0];
				impl_![imp $u1 | $i0, $u1:$i0];
				impl_![imp $i0 | $i1, $i1:$i0];
				impl_![imp $u0 | $u1, $u1:$u0];
				impl_![imp $u0 | $i1, $i1:$u0];
				impl_![imp $i0 | $u1, $u1:$i0];
			};

			(imp $target:ty | $self:ty, $large:ty:$small:ty) => {
				impl SizeOrder<$target> for $self {
					type LargerType = $large;
					type SmallerType = $small;
				}
			};

			(lt ptr; $($is:ty:$us:ty),+) => {
				$(impl_![imp $is:$us, isize:usize];)+
			};
			(eq ptr; $($is:ty:$us:ty),+) => {
				impl_![isize:usize];
				$(
					impl_![imp $is | isize, $is:isize];
					impl_![imp $us | isize, $us:isize];
					impl_![imp $is | usize, usize:$is];
					impl_![imp $us | usize, $us:usize];
					impl_![imp isize | $is, $is:isize];
					impl_![imp isize | $us, $us:isize];
					impl_![imp usize | $is, usize:$is];
					impl_![imp usize | $us, $us:usize];
				)+
			};
			(gt ptr; $($is:ty:$us:ty),+) => {
				$(impl_![imp isize:usize, $is:$us];)+
			};
		}

		impl_![i8: u8, i16: u16, i32: u32, i64: u64, i128: u128];
		#[cfg(target_pointer_width = "32")]
		mod size_order_for_ptr {
			use super::SizeOrder;
			impl_![lt ptr; i8: u8, i16: u16];
			impl_![eq ptr; i32: u32];
			impl_![gt ptr; i64: u64, i128: u128];
		}

		#[cfg(target_pointer_width = "64")]
		mod size_order_for_ptr {
			use super::SizeOrder;
			impl_![lt ptr; i8: u8, i16: u16, i32: u32];
			impl_![eq ptr; i64: u64];
			impl_![gt ptr; i128: u128];
		}

		macro_rules! impl_ {
			($t0:ty, $t1:ty, $($ts:ty),+) => {
				impl_![imp $t0, $t1];
				impl_![$t1, $($ts),+];
			};

			($t0:ty, $t1:ty) => {
				impl_![imp $t0, $t1];
			};

			(imp $t0:ty, $t1:ty) => {
				impl IntoLarger for $t0 {
					type LargerType = $t1;
				}
			};
		}

		impl_![i8, i16, i32, i64, i128];
		impl_![u8, u16, u32, u64, u128];
	}
}

pub mod math {
	pub mod number {
		pub mod modint {
			use crate::traits::consts::Constant;
			use crate::traits::integer::{IntoLarger, SizeOrder};
			use num::traits::{AsPrimitive, One, Zero};
			use std::ops::{Add, Div, Mul, Sub};

			pub struct ModInt<M>
			where
				M: Constant,
			{
				value: M::Type,
			}

			impl<T, U, M> From<T> for ModInt<M>
			where
				M: Constant,
				M::Type: num::Integer + num::Signed + SizeOrder<T, LargerType = U> + AsPrimitive<U>,
				T: num::Integer + SizeOrder<M::Type, LargerType = U> + AsPrimitive<U>,
				U: num::Integer + AsPrimitive<M::Type>,
			{
				fn from(src: T) -> Self {
					debug_assert!(M::value() >= M::Type::zero());
					let src: U = src.as_();
					let modulus: U = M::value().as_();
					let value = num::integer::mod_floor(src, modulus).as_();
					Self { value }
				}
			}

			impl<M> std::fmt::Display for ModInt<M>
			where
				M: Constant,
				M::Type: std::fmt::Display,
			{
				fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
					write!(f, "{}", self.value)
				}
			}

			impl<M> std::fmt::Debug for ModInt<M>
			where
				M: Constant,
				M::Type: std::fmt::Debug,
			{
				fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
					f.debug_tuple("ModInt").field(&self.value).finish()
				}
			}

			impl<M> Clone for ModInt<M>
			where
				M: Constant,
				M::Type: Clone,
			{
				fn clone(&self) -> Self {
					Self {
						value: self.value.clone(),
					}
				}
			}

			impl<M> Copy for ModInt<M>
			where
				M: Constant,
				M::Type: Copy,
			{
			}

			impl<M> Default for ModInt<M>
			where
				M: Constant,
				M::Type: Default,
			{
				fn default() -> Self {
					Self {
						value: M::Type::default(),
					}
				}
			}

			macro_rules! impl_std_ops {
				($trait:ident:$fn:ident for $type:ty) => {
					impl_std_ops!($type; $trait:$fn);
					impl_std_ops!($type; $trait:$fn<'a, &>);
					impl_std_ops!($type; <'a, &> $trait:$fn);
					impl_std_ops!($type; <'a, &> $trait:$fn<'b, &>);
				};

				($type:ty; $(<$lt_self:lifetime, $ref_self:tt>)? Add:$fn:ident $(<$lt_rhs:lifetime, $ref_rhs:tt>)?) => {
					impl<$($lt_self,)? $($lt_rhs, )?M> Add<$($ref_rhs$lt_rhs)?$type> for $($ref_self$lt_self)? $type
					where
						M: Constant,
						M::Type: Copy + num::Integer,
					{
						type Output = $type;
						fn $fn(self, rhs: $($ref_rhs$lt_rhs)?$type) -> Self::Output {
							let value = self.value.$fn(rhs.value);
							ModInt { value: if value >= M::value() { value - M::value() } else { value } }
						}
					}
				};

				($type:ty; $(<$lt_self:lifetime, $ref_self:tt>)? Sub:$fn:ident $(<$lt_rhs:lifetime, $ref_rhs:tt>)?) => {
					impl<$($lt_self,)? $($lt_rhs, )?M> Sub<$($ref_rhs$lt_rhs)?$type> for $($ref_self$lt_self)? $type
					where
						M: Constant,
						M::Type: Copy + num::Integer + num::Signed,
					{
						type Output = $type;
						fn $fn(self, rhs: $($ref_rhs$lt_rhs)?$type) -> Self::Output {
							let value = self.value.$fn(rhs.value);
							ModInt { value: if value < M::Type::zero() { value + M::value() } else { value } }
						}
					}
				};

				($type:ty; $(<$lt_self:lifetime, $ref_self:tt>)? Mul:$fn:ident $(<$lt_rhs:lifetime, $ref_rhs:tt>)?) => {
					impl<$($lt_self,)? $($lt_rhs, )?M> Mul<$($ref_rhs$lt_rhs)?$type> for $($ref_self$lt_self)? $type
					where
						M: Constant,
						M::Type: num::Integer + IntoLarger + AsPrimitive<<M::Type as IntoLarger>::LargerType>,
						<M::Type as IntoLarger>::LargerType: num::Integer + AsPrimitive<M::Type>,
					{
						type Output = $type;
						fn $fn(self, rhs: $($ref_rhs$lt_rhs)?$type) -> Self::Output {
							let modulus: <M::Type as IntoLarger>::LargerType = M::value().as_();
							let lhs: <M::Type as IntoLarger>::LargerType = self.value.as_();
							let rhs: <M::Type as IntoLarger>::LargerType = rhs.value.as_();
							let value: M::Type = num::integer::mod_floor(lhs.$fn(rhs), modulus).as_();
							ModInt { value }
						}
					}
				};

				($type:ty; $(<$lt_self:lifetime, $ref_self:tt>)? Div:$fn:ident $(<$lt_rhs:lifetime, $ref_rhs:tt>)?) => {
					impl<$($lt_self,)? $($lt_rhs, )?M> Div<$($ref_rhs$lt_rhs)?$type> for $($ref_self$lt_self)? $type
					where
						M: Constant,
						M::Type: Copy + num::Integer + num::Signed,
						$($ref_self$lt_self)?$type : Mul<$type, Output = $type>
					{
						type Output = $type;
						fn $fn(self, rhs: $($ref_rhs$lt_rhs)?$type) -> Self::Output {
							self * rhs.inverse()
						}
					}
				};
			}

			impl_std_ops!(Add:add for ModInt<M>);
			impl_std_ops!(Sub:sub for ModInt<M>);
			impl_std_ops!(Mul:mul for ModInt<M>);
			impl_std_ops!(Div:div for ModInt<M>);

			impl<M> std::ops::Neg for ModInt<M>
			where
				M: Constant,
				M::Type: num::Integer + num::Signed,
			{
				type Output = Self;
				fn neg(self) -> Self::Output {
					Self {
						value: if self.value == M::Type::zero() {
							M::Type::zero()
						} else {
							M::value() - self.value
						},
					}
				}
			}

			impl<M> Zero for ModInt<M>
			where
				M: Constant,
				M::Type: num::Zero + num::Integer,
				Self: Add<Output = Self>,
			{
				fn zero() -> Self {
					Self {
						value: M::Type::zero(),
					}
				}

				fn is_zero(&self) -> bool {
					self.value == M::Type::zero()
				}
			}

			impl<M, U> num::traits::Pow<U> for ModInt<M>
			where
				M: Constant,
				Self: Copy + One,
				U: num::Integer + num::Unsigned,
			{
				type Output = Self;
				fn pow(self, rhs: U) -> Self {
					let mut r = Self::one();
					let mut b = rhs;
					let mut a = self;
					while b > U::zero() {
						if b.is_odd() {
							r = r * a;
						}
						a = a * a;
						b = b / (U::one() + U::one());
					}
					r
				}
			}

			impl<M> One for ModInt<M>
			where
				M: Constant,
				M::Type: num::Integer,
				Self: Mul<Output = Self>,
			{
				fn one() -> Self {
					Self {
						value: M::Type::one(),
					}
				}
			}

			impl<M> ModInt<M>
			where
				M: Constant,
			{
				pub fn value(&self) -> &M::Type {
					&self.value
				}
			}

			impl<M> ModInt<M>
			where
				M: Constant,
				M::Type: Copy + num::Integer + num::Signed,
			{
				pub fn inverse(self) -> Self {
					let mut r = (M::value(), self.value);
					let mut s = (M::Type::zero(), M::Type::one());
					while r.1 != M::Type::zero() {
						let q = r.0 / r.1;
						let nr = r.0 - q * r.1;
						r = (r.1, nr);
						let ns = s.0 - q * s.1;
						s = (s.1, ns);
					}
					if r.0 > M::Type::one() {
						panic!();
					}
					Self {
						value: if s.0 < M::Type::zero() {
							s.0 + M::value()
						} else {
							s.0
						},
					}
				}

				pub fn pow<U>(self, mut exp: U) -> Self
				where
					Self: One,
					U: num::Integer + num::Unsigned,
				{
					let mut r = Self::one();
					let mut a = self;
					while exp > U::zero() {
						if exp.is_odd() {
							r = r * a;
						}
						a = a * a;
						exp = exp / (U::one() + U::one());
					}
					r
				}
			}

			#[macro_export]
			macro_rules! define_modint {
				($mint:ident; $modulus:ident: $type:ty = $expr:expr) => {
					type $mint = $crate::math::number::modint::ModInt<$modulus>;
					struct $modulus;
					impl $crate::traits::consts::LiteralConstant for $modulus {
						type Type = $type;
						const VALUE: $type = $expr;
					}
				};
			}

			#[macro_export]
			macro_rules! define_runtime_modint {
				($mint:ident; $modulus:ident with $mod:ident: $type:ty) => {
					type $mint = $crate::math::number::modint::ModInt<$modulus>;
					thread_local! { static $mod: std::cell::RefCell<$type> = std::cell::RefCell::new(0); }
					struct $modulus;
					impl $modulus {
						pub fn with<T, F: FnOnce() -> T>(modulus: $type, f: F) -> T {
							$mod.with(|m| {
								*m.borrow_mut() = modulus;
								let _m = m.borrow();
								f()
							})
						}
					}
					unsafe impl traits::marker::Unsafe for $modulus {}
					impl traits::consts::Constant for $modulus {
						type Type = $type;
						fn value() -> Self::Type {
							$mod.with(|m| *m.borrow())
						}
					}
				};
			}

			pub mod table {
				use super::ModInt;
				use crate::traits::{consts::Constant, integer::IntoLarger};
				use num::{One, Zero};

				pub struct FactorialMod<M>
				where
					M: Constant,
				{
					value: Vec<ModInt<M>>,
					inverse: Vec<ModInt<M>>,
				}

				impl<M> FactorialMod<M>
				where
					M: Constant,
					M::Type: num::Integer
						+ num::Signed
						+ IntoLarger
						+ num::traits::AsPrimitive<<M::Type as IntoLarger>::LargerType>,
					<M::Type as IntoLarger>::LargerType:
						num::Integer + num::traits::AsPrimitive<M::Type>,
					usize: Into<ModInt<M>>,
				{
					pub fn new(n: usize) -> Self {
						let mut value = Vec::with_capacity(n + 1);
						value.push(ModInt::<M>::one());

						for i in 1..=n {
							let next = i.into() * value.last().unwrap();
							value.push(next);
						}
						let mut inverse = vec![ModInt::zero(); n + 1];
						inverse[n] = value[n].inverse();
						for i in (0..n).rev() {
							let next = (i + 1).into() * inverse[i + 1];
							inverse[i] = next;
						}
						Self { value, inverse }
					}

					pub fn value<T>(&self, n: T) -> ModInt<M>
					where
						T: num::Integer + std::convert::TryInto<usize>,
						T::Error: std::fmt::Debug,
					{
						let n = n.try_into().unwrap();
						self.value[n]
					}

					pub fn inverse<T>(&self, n: T) -> ModInt<M>
					where
						T: num::Integer + std::convert::TryInto<usize>,
						T::Error: std::fmt::Debug,
					{
						let n = n.try_into().unwrap();
						self.inverse[n]
					}

					pub fn combination<T>(&self, n: T, r: T) -> ModInt<M>
					where
						T: num::Integer + std::convert::TryInto<usize>,
						T::Error: std::fmt::Debug,
					{
						if r > n || r < T::zero() {
							Zero::zero()
						} else {
							let n = n.try_into().unwrap();
							let r = r.try_into().unwrap();
							self.value[n] * self.inverse[r] * self.inverse[n - r]
						}
					}
				}
			}
		}
	}
}


Measured memory_bytes: 2792