#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <list>
#include <iterator>
#include <cassert>
#include <numeric>
#include <functional>
#include <ctime>
#pragma warning(disable:4996) 

#include <iostream>
#include <iomanip>

using namespace std;
typedef long long ll;
#define all(x) (x).begin(),(x).end()
template<typename T1, typename T2> bool chmin(T1 &a, T2 b) { if (a <= b)return 0; a = b; return 1; }
template<typename T1, typename T2> bool chmax(T1 &a, T2 b) { if (a >= b)return 0; a = b; return 1; }
int dx[4] = { 0,1,0,-1 }, dy[4] = { 1,0,-1,0 };
long double eps = 1e-9;
long double pi = acos(-1);

template< int mod = 1000000007 >
struct ModInt {
    int x;
    ModInt() : x(0) {}
    ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
    ModInt &operator+=(const ModInt &p) {
        if ((x += p.x) >= mod) x -= mod;
        return *this;
    }
    ModInt &operator-=(const ModInt &p) {
        if ((x += mod - p.x) >= mod) x -= mod;
        return *this;
    }
    ModInt &operator*=(const ModInt &p) {
        x = (int)(1LL * x * p.x % mod);
        return *this;
    }
    ModInt &operator/=(const ModInt &p) {
        *this *= p.inverse();
        return *this;
    }

    ModInt operator-() const { return ModInt(-x); }
    ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }
    ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }
    ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }
    ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }
    bool operator==(const ModInt &p) const { return x == p.x; }
    bool operator!=(const ModInt &p) const { return x != p.x; }

    ModInt inverse() const {
        int a = x, b = mod, u = 1, v = 0, t;
        while (b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return ModInt(u);
    }

    ModInt pow(int64_t n) const {
        ModInt ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }

    friend ostream &operator<<(ostream &os, const ModInt &p) {
        return os << p.x;
    }

    friend istream &operator>>(istream &is, ModInt &a) {
        int64_t t;
        is >> t;
        a = ModInt< mod >(t);
        return (is);
    }

    static int get_mod() { return mod; }
};

// const int mod = 1000000007;
const int mod = 998244353;
using mint = ModInt< mod >;
const int MAX = 200005;

mint fac[MAX], finv[MAX], inv[MAX];

// テーブルを作る前処理
void COMinit() {
    fac[0] = fac[1] = 1;
    finv[0] = finv[1] = 1;
    inv[1] = 1;
    for (int i = 2; i < MAX; i++) {
        fac[i] = fac[i - 1] * i;
    }
    finv[MAX - 1] = fac[MAX - 1].inverse();
    for (int i = MAX - 2; i >= 1; i--) {
        finv[i] = finv[i + 1] * (i + 1);
        inv[i + 1] = fac[i] * finv[i + 1];
    }
}

// 二項係数計算
mint COM(int n, int k) {
    if (n < k) return 0;
    if (n < 0 || k < 0) return 0;
    return fac[n] * (finv[k] * finv[n - k]);
}

mint ans = 0;
ll n, m;
ll mp[200020][20];

mint dfs(ll now, ll cnt) {
    if (mp[now][cnt]>=0) {
        return mp[now][cnt];
    }
    mint ret = COM(n - 1, cnt - 1);
    for (ll i = 2; i <= m; i++) {
        if (now*i <= m)ret += dfs(now*i, cnt + 1);
        else break;
    }
    mp[now][cnt] = ret.x;
    return ret;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(20);

    cin >> n >> m;

    fill(mp[0], mp[200005], -1);

    COMinit();
    for (int i = 1; i <= m; i++) {
        ans += dfs(i, 1);
    }
    cout << ans << endl;
}

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll MOD = 1000000007;
const int M = 998244353;

int n,m;
vector<bool> num(210000,true);
vector<int> prime(210000,-1);

vector<ll> fac(210000); //n!(mod M)
vector<ll> ifac(210000); //k!^{M-2} (mod M)

inline ll mpow(ll x, ll n){ //x^n(mod M) ←普通にpow(x,n)では溢れてしまうため，随時mod計算
    ll ans = 1;
    while(n != 0){
        if(n&1) ans = ans*x % M;
        x = x*x % M;
        n = n >> 1;
    }
    return ans;
}

inline ll comb(int a, int b){ //aCbをmod計算
    if(a == 0 && b == 0)return 1;
    if(b == 0)return 1;
    if(a < b || a < 0)return 0;
    ll tmp = ifac[a-b]* ifac[b] % M;
    return tmp * fac[a] % M;
}

void Eratos(){
    num[0] = num[1] = false;
    int itr = 0;
    for(int i = 2;i < 210000;i++){
        if(num[i]){
            for(int j = 2*i;j <= 210000;j += i){
                num[j] = false;
            }
            prime[itr] = i;
            itr++;
        }
    }
}


ll solve(int n,int m){
    ll res = 0;
    ll add = 1;
    for(int j = 1;j <= m;j++){
        add = 1;
        int num = j;
        for(int i = 0;prime[i] != -1 && prime[i] <= num;i++){
            int cnt = 0;
            while(num % prime[i] == 0){
                cnt++;
                num /= prime[i];
            }
            // cout << j << sp << prime[i] << sp << cnt << sp << endl;
            // cout << "comb" << n-2+cnt << sp << cnt << sp << comb(n-2+cnt,cnt) << endl;
            add *= comb(n-2+cnt,cnt);
            add %= M;
        }
        //cout << add << endl;
        res += add*(m/j);
        res %= M;
    }
    return res;
}

int main(){
    cin >> n >> m;
    Eratos();
    fac[0] = 1;
    ifac[0] = 1;
    for(int i = 0;i <= 210000;i++){
        fac[i+1] = fac[i]*(i+1) % M; // n!(mod M)
        ifac[i+1] = ifac[i]*mpow(i+1, M-2) % M; // k!^{M-2} (mod M) ←累乗にmpowを採用
    }
    cout << solve(n,m) << endl;
}