use std::io::*;
use std::str::FromStr;

#[allow(dead_code)]
fn read<T: FromStr>() -> T {
    let stdin = stdin();
    let stdin = stdin.lock();
    let token: String = stdin
        .bytes()
        .map(|c| c.expect("failed to read char") as char)
        .skip_while(|c| c.is_whitespace())
        .take_while(|c| !c.is_whitespace())
        .collect();
    token.parse().ok().expect("failed to parse token")
}

#[allow(dead_code)]
fn pow_mod(base: i64, power: i64, modulo: i64) -> i64 {
    let mut base = base;
    let mut power = power;
    let mut ans = 1;
    while power > 0 {
        if power & 1 == 1 {
            ans = ans * base % modulo;
        }
        power >>= 1;
        base = base * base % modulo;
    }
    ans
}

#[allow(dead_code)]
fn inv_mod(a: i64, modulo: i64) -> i64 {
    pow_mod(a, modulo - 2, modulo)
}

const MOD: i64 = 1e+9 as i64 + 7;
const FACT_MAX: usize = 1000000;
static mut FACTORIAL: [i64; FACT_MAX + 1] = [0; FACT_MAX + 1];
static mut RFACTORIAL: [i64; FACT_MAX + 1] = [0; FACT_MAX + 1];
fn init_fact() {
    unsafe {
        if FACTORIAL[0] == 0 {
            FACTORIAL[0] = 1;
            for i in 1..FACT_MAX + 1 {
                FACTORIAL[i] = (FACTORIAL[i - 1] * i as i64) % MOD;
            }
            RFACTORIAL[FACT_MAX] = inv_mod(FACTORIAL[FACT_MAX], MOD);
            for i in (0..FACT_MAX).rev() {
                RFACTORIAL[i] = RFACTORIAL[i + 1] * (i as i64 + 1) % MOD;
            }
        }
    }
}
#[allow(dead_code)]
fn fact(n: usize) -> i64 {
    init_fact();
    unsafe { FACTORIAL[n] }
}
#[allow(dead_code, unused_comparisons)]
fn combi(n: usize, m: usize) -> i64 {
    if n < m || m < 0 {
        return 0;
    }
    init_fact();
    unsafe { FACTORIAL[n] * RFACTORIAL[m] % MOD * RFACTORIAL[n - m] % MOD }
}

#[allow(dead_code)]
struct Eratosthnes {
    n: usize,           // incluesive
    bprime: Vec<bool>,  // self.bprime[5] = true
    primes: Vec<usize>, // self.primes[2] = 5
}
#[allow(dead_code)]
impl Eratosthnes {
    fn new(n: usize) -> Eratosthnes {
        let mut bprime = vec![true; n + 1];
        let mut primes = vec![];
        primes.reserve(n / 10 + 1000);
        bprime[0] = false;
        bprime[1] = false;
        for i in 2..n + 1 {
            if !bprime[i] {
                continue;
            }
            primes.push(i);
            if i * i > n {
                continue;
            }
            let mut j = i * i;
            while j <= n {
                bprime[j] = false;
                j += i;
            }
        }
        Eratosthnes {
            n: n,
            bprime: bprime,
            primes: primes,
        }
    }
}

// need Erathosthenes
fn prime_factorization(n: usize, era: &Eratosthnes) -> Vec<(usize, usize)> {
    assert!(1 < n && n <= era.n * era.n);
    let mut ret = vec![];
    let mut v = n;
    for &prime in era.primes.iter() {
        if v < prime * prime {
            break;
        }
        let mut cnt = 0;
        while v % prime == 0 {
            v /= prime;
            cnt += 1;
        }
        if cnt != 0 {
            ret.push((prime, cnt));
        }
    }
    if v != 1 {
        ret.push((v, 1));
    }
    return ret;
}

#[test]
fn test_prime_factorization() {
    let era = Eratosthnes::new(10);
    assert!(prime_factorization(2, &era) == vec![(2, 1)]);
    assert!(prime_factorization(4, &era) == vec![(2, 2)]);
    assert!(prime_factorization(12, &era) == vec![(2, 2), (3, 1)]);
    assert!(prime_factorization(31, &era) == vec![(31, 1)]);
}

fn main() {
    let n = read::<usize>();
    let m = read::<usize>();
    let era = Eratosthnes::new(200000);
    let factors = if m == 1 {
        vec![(1, 0)]
    } else {
        prime_factorization(m, &era)
    };
    let mut ans = 1;
    for &(_p, cnt) in factors.iter() {
        ans = (ans * combi(n + cnt - 1, cnt)) % MOD;
    }
    println!("{}", ans);
}


Measured memory_bytes: 19964