use std::cmp::Ordering;
use std::cmp;
use std::cmp::min;
use std::collections::BTreeMap;
use std::process;
use std::cmp::Ord;
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::VecDeque;
use std::collections::BTreeSet;
use std::mem;
use std::collections::BinaryHeap;
use std::hash::{Hash, Hasher};

pub struct Scanner<R> {
    stdin: R,
}

impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .stdin
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r')
            .take_while(|&b| b != b' ' && b != b'\n' && b != b'\r')
            .collect::<Vec<_>>();
        std::str::from_utf8(&buf).unwrap()
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

pub trait BinarySearch<T> {
    fn lower_bound(&self, x:&T) -> usize;
    fn upper_bound(&self, x:&T) -> usize;
}

impl<T: Ord> BinarySearch<T> for VecDeque<T>{
    fn lower_bound(&self, x: &T) -> usize {
        let mut low = 0;
        let mut high = self.len();

        while low != high {
            let mid = (low + high) / 2;
            match self[mid].cmp(x) {
                Ordering::Less => {
                    low = mid + 1;
                }
                Ordering::Equal | Ordering::Greater => {
                    high = mid;
                }
            }
        }
        low
    }

    fn upper_bound(&self, x: &T) -> usize {
        let mut low = 0;
        let mut high = self.len();

        while low != high {
            let mid = (low + high) / 2;
            match self[mid].cmp(x) {
                Ordering::Less | Ordering::Equal => {
                    low = mid + 1;
                }
                Ordering::Greater => {
                    high = mid;
                }
            }
        }
        low
    }
}
impl<T: Ord> BinarySearch<T> for [T]{
    fn lower_bound(&self, x: &T) -> usize {
        let mut low = 0;
        let mut high = self.len();

        while low != high {
            let mid = (low + high) / 2;
            match self[mid].cmp(x) {
                Ordering::Less => {
                    low = mid + 1;
                }
                Ordering::Equal | Ordering::Greater => {
                    high = mid;
                }
            }
        }
        low
    }

    fn upper_bound(&self, x: &T) -> usize {
        let mut low = 0;
        let mut high = self.len();

        while low != high {
            let mid = (low + high) / 2;
            match self[mid].cmp(x) {
                Ordering::Less | Ordering::Equal => {
                    low = mid + 1;
                }
                Ordering::Greater => {
                    high = mid;
                }
            }
        }
        low
    }
}

//10進数で表すとnをx進数で表す
fn base_number(x:usize, n:usize)->Vec<usize>{
    let mut x = x;
    let mut n = n;
    let mut r = 1;
    let mut q = x;
    let mut res = vec![];
    while(q>=n){
        r = q%n;
        res.push(r);
        q = q/n;
    }
    r = q%n;
    res.push(r);
    res.reverse();
    return res
}

//最大公約数
fn gcd(a:usize, b:usize)->usize{
    if b==0{return a;}
    return gcd(b, a%b);
}
//拡張GCD
//ax+by=1となる整数x,yを求める
fn extgcd(a:i64, b:i64, x:&mut i64, y:&mut i64) -> i64{
    let mut d = a;
    if b != 0{
        d = extgcd(b, a%b, y, x);
        *y -= (a/b)*(*x);
    }
    else{
        *x = 1;
        *y = 0;
    }
    return d;
}
//最小公倍数
fn lcm(a:usize, b:usize)->usize{
    return (b/gcd(a, b))*a;
}
//逆元
fn modinv(a:usize, M:usize)->usize{
    let mut b = M as i64;
    let mut u = 1 as i64;
    let mut v = 0 as i64;
    let mut a = a as i64;
    let mut m = M as i64;
    while(b>0){
        let mut t = a/b;
        a -= t*b;
        mem::swap(&mut a, &mut b);
        u-=t*v;
        mem::swap(&mut u, &mut v);
    }
    u%=m;
    if u<0{u+=m;}
    return u as usize;

}
//連立合同方程式
//A * X = B (MOD M) ->(b, m)  x=b(mod m)

//座標圧縮２次元 [1, W]
fn linear_congruence(A:&Vec<i64>, B:&Vec<i64>, M:&Vec<i64>) -> (i64, i64){
    let mut x = 0;
    let mut m = 1;
    for i in 0..A.len(){
        let mut a = A[i]*m;
        let mut b = B[i]-A[i]*x;
        let mut d = gcd(M[i] as usize, a as usize) as i64;
        if (b%d != 0){
            return (0, -1);
        }
        let mut t= b/d*(modinv( (a/d) as  usize, (M[i]/d) as  usize) as i64)%(M[i]/d);
        x = x+m*t;
        m*=M[i]/d;
    }
    return (x%m, m);

}

//座標圧縮２次元 [1, W]
fn compress_2d(x1:&mut Vec<i64>, x2:&mut Vec<i64>, w:i64) -> Vec<i64>{
    let mut xs = vec![];
    for i in 0..x1.len(){
        for d in -1..2{
            let mut tx1 = x1[i]+d;
            let mut tx2 = x2[i]+d;
            if (1<=tx1 && tx1 <= w){
                xs.push(tx1);
            }
            if (1<=tx2 && tx2 <= w){
                xs.push(tx2);
            }
        }
    }
    let mut h = HashSet::new();
    for i in 0..xs.len(){
        h.insert(xs[i]);
    }
    let mut xs:Vec<i64> = h.into_iter().collect();
    xs.sort();
    return xs;
}

//セグメント木
struct segment_tree<I, Op>{
    n: usize,
    dat: Vec<I>,
    op:Op,
    e:I,
}
impl<I, Op> segment_tree<I, Op>

    where Op: Fn(I, I) -> I, I:Copy{

        pub fn new(n_:usize, op: Op, e:I)->Self{
            let mut n = 1;
            while(n<n_){n*=2;}
            segment_tree{n: n, dat:vec![e; 2*n-1], op:op, e:e}
        }
        pub fn update(&mut self, k:usize, a:I){
            let mut k = k;
            k += self.n-1;
            self.dat[k] = a;
            while(k>0){
                k = (k-1)/2;
                self.dat[k] = (self.op)(self.dat[k*2 + 1], self.dat[k*2+2]);
            }
        }

        pub fn query_sub(&self, a:usize, b:usize, k:usize, l:usize, r:usize) -> I{
            if r<=a || b<=l{return self.e;}
            if a<=l && r<=b{return self.dat[k];}
            else{
                let mut vl = self.query_sub(a, b, k*2+1, l, (l+r)/2);
                let mut vr = self.query_sub(a, b, k*2+2, (l+r)/2, r);
                return (self.op)(vl, vr);
            }
        }
        pub fn query(&self, a:usize, b:usize)->I{
            return self.query_sub(a, b, 0, 0, self.n);
        }
    }

//遅延評価セグメント木
// composition(a, b) -> b(a(x))
struct lazy_segment_tree<S, F, op, mapping, composition>{
    n: usize,
    op:op,
    mapping:mapping,
    composition:composition,
    s:S,
    f:F,
    dat: Vec<S>,
    lazy:Vec<F>,

}
impl<S, F, op, mapping, composition> lazy_segment_tree<S, F, op, mapping, composition>

    where op:Fn(S, S)->S, mapping:Fn(F, S)->S, composition:Fn(F, F)->F, S:Copy, F:Copy, S:PartialEq, F:PartialEq{

        pub fn new(n_:usize, s:S, f:F, op:op, mapping:mapping, composition:composition)->Self{
            let mut n = 1;
            while(n<n_){n*=2;}
            lazy_segment_tree{n:n,
                              op:op, mapping:mapping,
                              composition:composition,
                              s:s,
                              f:f,
                              dat:vec![],
                              lazy:vec![],
            }
        }
        pub fn build(&mut self, initial:&Vec<S>){
            for i in 0..self.n*2-1{
                self.dat.push(self.s.clone());
                self.lazy.push(self.f.clone());
            }
            for i in 0..initial.len(){
                self.dat[i+self.n-1] = initial[i];
            }
            for i in (0..self.n-1).rev(){
                self.dat[i] = (self.op)(self.dat[i*2+1], self.dat[i*2+2]);
            }
        }
        pub fn eval(&mut self, k:usize){
            if self.lazy[k] == self.f{
                return;
            }
            if k*2+1 < self.n*2-1{
                self.lazy[k*2+1] = (self.composition)(self.lazy[k*2+1], self.lazy[k]);
                self.lazy[k*2+2] = (self.composition)(self.lazy[k*2+2], self.lazy[k]);
            }
            self.dat[k] = (self.mapping)(self.lazy[k], self.dat[k]);
            self.lazy[k] = self.f;
        }
        pub fn update_sub(&mut self, a:usize, b:usize, x:F, k:usize, l:usize, r:usize)->S{
            self.eval(k);
            if r<=a || b<=l{
                return self.dat[k];
            }
            if a<=l && r<= b{
                self.lazy[k] = (self.composition)(self.lazy[k], x);
                return (self.mapping)(self.lazy[k], self.dat[k]);
            }
            let mut vr = self.update_sub(a, b, x, k*2+1, l, (l+r)/2);
            let mut vl = self.update_sub(a, b, x, k*2+2, (l+r)/2, r);
            self.dat[k] = (self.op)(vr, vl);
            return self.dat[k];
        }
        pub fn update(&mut self, a:usize, b:usize, x:F){
            self.update_sub(a, b, x, 0, 0, self.n);
        }
        pub fn query_sub(&mut self, a:usize, b:usize, k:usize, l:usize, r:usize)->S{
            self.eval(k);
            if r<= a || b <= l{
                return self.s;
            }
            else if a<= l && r<= b{
                return self.dat[k];
            }
            else{
                 let mut vl = self.query_sub(a, b, k*2+1, l, (l+r)/2);
                 let mut vr = self.query_sub(a, b, k*2+2, (l+r)/2, r);
                 return (self.op)(vl, vr);
            }
        }
        pub fn query(&mut self, a:usize, b:usize)->S{
            return self.query_sub(a, b, 0, 0, self.n);
        }

    }

//ダイクストラ法
fn dijkstra(s:usize, g:&Vec<Vec<(usize, i64)>>)->Vec<i64>{
    let mut dist = vec![INF;g.len()];
    dist[s] = 0;
    let mut h = BinaryHeap::new();
    h.push((0, s));
    while(h.len() != 0){
        let mut t = h.pop().unwrap();
        let mut d = t.0 * -1;
        let mut v = t.1;
        if dist[v]<d{
            continue;
        }
        for i in 0..g[v].len(){
            let n = g[v][i].0;
            let dd = g[v][i].1;
            if dist[n]>d+dd{
                dist[n] = d+dd;
                h.push((dist[n]*-1, n));
            }
        }
    }
    return dist;
}
fn dijkstra2(s:usize, g:&Vec<Vec<(i64, usize, usize)>>)->Vec<i64>{
    let mut dist = vec![INF;g.len()];
    dist[s] = 0;
    let mut h = BinaryHeap::new();
    h.push((0, s));
    while(h.len() != 0){
        let mut t = h.pop().unwrap();
        let mut d = t.0 * -1;
        let mut v = t.1;
        if dist[v]<d{
            continue;
        }
        for i in 0..g[v].len(){
            let n = g[v][i].1;
            let dd = g[v][i].0;
            if dist[n]>d+dd{
                dist[n] = d+dd;
                h.push((dist[n]*-1, n));
            }
        }
    }
    return dist;
}
fn dijkstra3(st:(usize, usize), go:(usize, usize), h:usize, w:usize, map:&Vec<Vec<char>>)->Vec<Vec<i64>>{
    let mut d = vec![vec![INF;w];h];
    d[st.0][st.1] = 0;
    let mut hh = BinaryHeap::new();
    hh.push((0, st));
    let mut dx = vec![0, 0, 1, -1];
    let mut dy = vec![1, -1, 0, 0];

    while(hh.len() != 0){
        let mut tt = hh.pop().unwrap();
        let mut dist = tt.0 as i64 * -1;
        let mut x = (tt.1).0;
        let mut y = (tt.1).1;
        if d[x][y]<dist{
            continue;
        }
        for i in 0..4{
            let mut nx = x as i64+dx[i];
            let mut ny = y as i64+dy[i];
            if 0<=nx && (nx<h as i64) && 0<= ny && ny<w as i64{
                let mut nx = nx as usize;
                let mut ny = ny as usize;
                if map[nx][ny] == '#'{
                    continue;
                }
                if d[nx][ny]>dist+1{
                    d[nx][ny]=dist+1;
                    hh.push((-1*(dist+1),(nx, ny)));
                }

            }
        }
    }
    return d;
}
//２次元和 [ (x, y), (x, y) )
//sum[i+1][j+1] = ((sum[i+1][j]+sum[i][j+1]+MODu-sum[i][j])%MODu+dp[i+1][j+1])%MODu;
/*for (int i = 0; i < H; i++) {

             for (int j = 0; j < W; j++) {
                 imos[i][j + 1] += imos[i][j];
             }
         }

         for (int j = 0; j < W; j++) {
             for (int i = 0; i < H; i++) {
                 imos[i + 1][j] += imos[i][j];
             }
         }

        sums[yy1][xx1]+=1;
        sums[yy2][xx2]+=1;
        sums[yy1][xx2]-=1;
        sums[yy2][xx1]-=1;
    }
    for i in 0..ys.len() {
        for j in 0..xs.len(){
            sums[i][j + 1] += sums[i][j];
            }
        }

    for j in 0..xs.len(){
        for i in 0..ys.len(){
            sums[i + 1][j] += sums[i][j];
        }
    }
*/
//3次元和 [ (x, y, z), (x, y, z) )
/*
//let mut v = vec![X1, Y1, Z1, X2, Y2, Z2];
        let mut si = vec![1i64, -1i64];
        for i in 0..2{
            for j in 0..2{
                for k in 0..2{
                    let mut X = v[0+i*3];
                    let mut Y = v[1+j*3];
                    let mut Z = v[2+k*3];
                    sum[X][Y][Z]+=si[i]*si[j]*si[k];
                }
            }
        }
    }
    for i in 0..xs.len()-1{
        for j in 0..ys.len(){
            for k in 0..zs.len(){
                sum[i+1][j][k] += sum[i][j][k];
            }
        }
    }

    for i in 0..xs.len(){
        for j in 0..ys.len()-1{
            for k in 0..zs.len(){
                sum[i][j+1][k] += sum[i][j][k];
            }
        }
    }

    for i in 0..xs.len(){
        for j in 0..ys.len(){
            for k in 0..zs.len()-1{
                sum[i][j][k+1] += sum[i][j][k];
            }
        }
    }
*/

//階乗
fn invs(max:usize)->(Vec<usize>, Vec<usize>){
    let mut fac = vec![0;max+1];
    let mut ifac = vec![0;max+1];
    fac[0] = 1;
    ifac[0] = 1;
    for i in 0..max{

        fac[i+1] = fac[i] * (i+1)%MODu;
        ifac[i+1] = ifac[i] * modpow(i+1, MODu - 2)%MODu;
    }
    (fac, ifac)
}
//コンビネーション
fn comb(a:usize, b:usize,  fac:&Vec<usize>, ifac:&Vec<usize>)->usize{
        let mut a = a;
        let mut b = b;
        if a == 0 && b == 0{return 1;}
        if a<b || a<0{return 0;}
        let mut tmp = ifac[a-b]*ifac[b]%MODu;
        return tmp * fac[a]%MODu;
}
fn nHr(n:usize, r:usize, fac:&Vec<usize>, ifac:&Vec<usize>)->usize{
    return comb(n+r-1, r, fac, ifac);
}
//
fn modpow(x:usize, n:usize) -> usize{
        let mut ans = 1;
        let mut n = n as usize;
        let mut x = x;
        while(n != 0){
            if (n&1 == 1){ans = ans*x%MODu;}
            x = x*x%MODu;
            n = n>>1;
        }
        ans
}
fn modpow2(x:i64, n:i64, m:i32) -> i64{
        let mut ans = 1;
        let mut n = n as i64;
        let mut x = x;
        while(n != 0){
            if (n&1 == 1){ans = ans*x%m as i64;}
            x = x*x%m as i64;
            n = n>>1;
        }
        ans
}

//ユニオンファインド木
struct union_find{
    par:Vec<usize>,
    rank:Vec<usize>,
    size:Vec<usize>,
    size_edge:Vec<usize>,
}
impl union_find{
    fn new(n:usize) -> union_find{
        let mut par = vec![0;n];
        for i in 0..n{
            par[i] = i;
        }
        union_find{
            par:par,
            rank:vec![0;n],
            size:vec![1;n],
            size_edge:vec![0;n],
        }
    }
    fn find(&mut self, x:usize) ->usize{
        if(self.par[x] == x){
            x
        }
        else{
            let p = self.par[x];
            let res = self.find(p);
            self.par[x] = res;
            res
        }
    }
    fn same(&mut self, a:usize, b:usize)->bool{
        self.find(a) == self.find(b)
    }
    fn unite(&mut self, a:usize, b:usize){
        let x = self.find(a);
        let y = self.find(b);
        if x != y{
            if (self.rank[x] < self.rank[y]){
                self.par[x] = y;
                self.size[y] += self.size[x];
                self.size_edge[y] += self.size_edge[x];
                self.size_edge[y] += 1;
            }
            else{
                self.par[y] = x;
                self.size[x] += self.size[y];
                self.size_edge[x] += self.size_edge[y];
                self.size_edge[x] += 1;
                if(self.rank[x] == self.rank[y]){ self.rank[x]+=1;}
            }

        }
        else{
            self.size_edge[x] += 1;
        }
    }
    fn check_size(&mut self, a:usize) -> usize{
        let x = self.find(a);
        let s = self.size[x];
        s
    }
}
//偏角ソート 反時計周り
fn norm(x:f64, y:f64)->f64{
    (x*x + y*y).sqrt()
}
fn rad(x:f64, y:f64)->f64{
    if y>=0.0{
        (x /norm(x, y)).acos()
    }
    else{
        2.0*PI - (x/norm(x,y)).acos()
    }
}
fn acos(v1:Vec<f64>, v2:Vec<f64>)->f64{
    ((v1[0]*v2[0] + v1[1]*v2[1])/(norm(v1[0], v1[1])*norm(v2[0], v2[1]))).acos()
}

fn argument_sort(xy:&mut Vec<Vec<f64>>){
    xy.sort_by(|a, b| rad(a[0], a[1]).partial_cmp(&(rad(b[0], b[1]))).unwrap());
}
fn argument_sort2(xy:&mut Vec<(f64, f64, usize)>){
    xy.sort_by(|a, b| rad(a.0, a.1).partial_cmp(&(rad(b.0, b.1))).unwrap());
}

//行列の掛け算 A*B
//
fn matmul(A:&Vec<Vec<i64>>, B:&Vec<Vec<i64>>) -> Vec<Vec<i64>>{
    let mut C = vec![vec![0;B[0].len()];A.len()];
    for i in 0..A.len(){
        for k in 0..B.len(){
            for j in 0..B[0].len(){
                C[i][j] += A[i][k]*B[k][j];
                C[i][j] %= MOD;
            }
        }
    }
    return C;
}
fn divisor(n:usize) -> Vec<usize>{
    let mut res:Vec<usize> = Vec::new();
    for i in 1..n+1{
        if i*i>n{break;}
        if n%i == 0{
            res.push(i);
            if i != n/i{
                res.push(n/i);
            }
        }
    }
    res
}

//xに10のpow乗を掛けて小数部分を消す
fn f64_trans(x:Vec<char>, pow:usize)->i64{
    let mut w = x.len();
    let mut a = vec![];
    let mut b = vec![];
    let mut f = false;
    let mut ism = false;
    if x[0] == '-'{
        ism = true;
    }
    for i in 0..x.len(){
        if x[i] == '-'{
            continue;
        }
        if x[i] == '.'{
            w = i;
            f = true;
        }
        else if f{
            b.push(x[i]);
        }
        else{
            a.push(x[i]);
        }
    }
        for i in 0..b.len(){
            a.push(b[i]);
        }
        for j in 0..pow-b.len(){
            a.push('0');
        }
    let mut now = 1;
    let mut res = 0;
    for i in (0..a.len()).rev(){
        res += now*(a[i] as i64 - '0' as i64);
        now*=10;
    }
    if ism{
        res*=-1;
    }
    return res;
}

//ローリングハッシュ
struct rolling_hash{
    base1:usize,
    base2:usize,
    mod1:usize,
    mod2:usize,
    hash1:Vec<usize>,
    hash2:Vec<usize>,
    power1:Vec<usize>,
    power2:Vec<usize>
}
impl rolling_hash{
    fn new(S:&Vec<usize>)->rolling_hash{
        let mut n = S.len();
        let mut base1 = 1007;
        let mut base2 = 2009;
        let mut mod1 = 1000000007;
        let mut mod2 = 1000000009;
        let mut hash1 = vec![0;n+1];
        let mut hash2 = vec![0;n+1];
        let mut power1 = vec![1;n+1];
        let mut power2 = vec![1;n+1];

        for i in 0..n{
            hash1[i+1] = (hash1[i] * base1 + S[i])%mod1;
            hash2[i+1] = (hash2[i] * base2 + S[i])%mod2;
            power1[i+1] = (power1[i] * base1)%mod1;
            power2[i+1] = (power2[i] * base2)%mod2;
        }

        return rolling_hash{
            hash1:hash1,
            hash2:hash2,
            power1:power1,
            power2:power2,
            base1:base1,
            base2:base2,
            mod1:mod1,
            mod2:mod2
        }
    }
    // [l, r)
    fn get(&self, l:usize, r:usize)->(usize, usize){
        let mut res1 = self.hash1[r] as i64 - self.hash1[l] as i64 * self.power1[r-l] as i64%self.mod1 as i64;
        if (res1<0) {res1+= self.mod1 as i64;}
        let mut res2 = self.hash2[r] as i64 - self.hash2[l] as i64 * self.power2[r-l] as i64%self.mod2 as i64;
        if (res2<0) {res2+= self.mod2 as i64;}
        return (res1 as usize, res2 as usize);
    }
}

/*
#[macro_use]
extern crate lazy_static;
lazy_static!{
    static ref all:Vec<usize>={
        let n_ = 200001;
        let mut a = vec![0;n_+1];
        for i in 1..n_+1{
            a[i] = (a[i-1]*10%MODu+1)%MODu;
        }
        a
    };
}
*/
//中国剰余定理
fn safe_mod(x:i64, m:i64)->i64{
    let mut x = x;
    let mut m = m;
    x%=m;
    if x<0{
        x+=m;
    }
    return x;
}
fn inv_gcd(a:i64, b:i64)->(i64, i64){
    let mut a = a;
    let mut b = b;
    a = safe_mod(a, b);
    if (a == 0){
        return (b, 0);
    }

    let mut s = b;
    let mut t = a;
    let mut m0 = 0;
    let mut m1 = 1;

    while (t != 0) {
        let mut  u = s / t;
        s -= t * u;
        m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b

        // [3]:
        // (s - t * u) * |m1| + t * |m0 - m1 * u|
        // <= s * |m1| - t * u * |m1| + t * (|m0| + |m1| * u)
        // = s * |m1| + t * |m0| <= b

        let mut tmp = s;
        s = t;
        t = tmp;
        tmp = m0;
        m0 = m1;
        m1 = tmp;
    }
    // by [3]: |m0| <= b/g
    // by g != b: |m0| < b/g
    if (m0 < 0){
        m0 += b / s;
    }
    return (s, m0);
}
fn floor_sum(n: i64, m: i64, mut a: i64, mut b: i64) -> i64 {
        let mut ans = 0;
        if a >= m {
            ans += (n - 1) * n * (a / m) / 2;
            a %= m;
        }
        if b >= m {
            ans += n * (b / m);
            b %= m;
        }

        let y_max = (a * n + b) / m;
        let x_max = y_max * m - b;
        if y_max == 0 {
            return ans;
        }
        ans += (n - (x_max + a - 1) / a) * y_max;
        ans += floor_sum(y_max, a, m, (a - x_max % a) % a);
        ans
}
fn crt(r: &Vec<i64>, m: &Vec<i64>) -> (i64, i64) {
        assert_eq!(r.len(), m.len());
        let (mut r0, mut m0) = (0, 1);
        for (&(mut ri), &(mut mi)) in r.iter().zip(m.iter()) {
            assert!(1 <= mi);
            ri = safe_mod(ri, mi);
            if m0 < mi {
                mem::swap(&mut r0, &mut ri);
                mem::swap(&mut m0, &mut mi);
            }
            if m0 % mi == 0 {
                if r0 % mi != ri {
                    return (0, 0);
                }
                continue;
            }

            let (g, im) = inv_gcd(m0, mi);
            let u1 = mi / g;
            if (ri - r0) % g != 0 {
                return (0, 0);
            }
            let x = (ri - r0) / g % u1 * im % u1;

            r0 += x * m0;
            m0 *= u1; // -> lcm(m0, mi)
            if r0 < 0 {
                r0 += m0
            };
        }

        (r0, m0)
}
//畳み込み計算

fn mul_mod(a: u32, b: u32, m: u32, im: u64) -> u32 {
    let mut z = a as u64;
    z *= b as u64;
    let x = (((z as u128) * (im as u128)) >> 64) as u64;
    let mut v = z.wrapping_sub(x.wrapping_mul(m as u64)) as u32;
    if m <= v {
        v = v.wrapping_add(m);
    }
    v
}

fn inv_gcd2(a: i64, b: i64) -> (i64, i64) {
    let a = safe_mod(a, b);
    if a == 0 {
        return (b, 0);
    }

    let mut s = b;
    let mut t = a;
    let mut m0 = 0;
    let mut m1 = 1;

    while t != 0 {
        let u = s / t;
        s -= t * u;
        m0 -= m1 * u; // |m1 * u| <= |m1| * s <= b

        mem::swap(&mut s, &mut t);
        mem::swap(&mut m0, &mut m1);
    }
    if m0 < 0 {
        m0 += b / s;
    }
    (s, m0)
}

use std::{
    cell::RefCell,
    convert::{Infallible, TryInto as _},
    fmt,
    iter::{Product, Sum},
    marker::PhantomData,
    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
    str::FromStr,
    sync::atomic::{self, AtomicU32, AtomicU64},
    thread::LocalKey,
};

pub type ModInt1000000007 = StaticModInt<Mod1000000007>;
pub type ModInt998244353 = StaticModInt<Mod998244353>;

#[derive(Copy, Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct StaticModInt<M> {
    val: u32,
    phantom: PhantomData<fn() -> M>,
}

impl<M: Modulus> StaticModInt<M> {
    #[inline(always)]
    pub fn modulus() -> u32 {
        M::VALUE
    }

    #[inline]
    pub fn new<T: RemEuclidU32>(val: T) -> Self {
        Self::raw(val.rem_euclid_u32(M::VALUE))
    }

    #[inline]
    pub fn raw(val: u32) -> Self {
        Self {
            val,
            phantom: PhantomData,
        }
    }

    #[inline]
    pub fn val(self) -> u32 {
        self.val
    }

    #[inline]
    pub fn pow(self, n: u64) -> Self {
        <Self as ModIntBase>::pow(self, n)
    }

    #[inline]
    pub fn inv(self) -> Self {
        if M::HINT_VALUE_IS_PRIME {
            if self.val() == 0 {
                panic!("attempt to divide by zero");
            }
            self.pow((M::VALUE - 2).into())
        } else {
            Self::inv_for_non_prime_modulus(self)
        }
    }
}

impl<M: Modulus> ModIntBase for StaticModInt<M> {
    #[inline(always)]
    fn modulus() -> u32 {
        Self::modulus()
    }

    #[inline]
    fn raw(val: u32) -> Self {
        Self::raw(val)
    }

    #[inline]
    fn val(self) -> u32 {
        self.val()
    }

    #[inline]
    fn inv(self) -> Self {
        self.inv()
    }
}

pub trait Modulus: 'static + Copy + Eq {
    const VALUE: u32;
    const HINT_VALUE_IS_PRIME: bool;

    fn butterfly_cache() -> &'static LocalKey<RefCell<Option<ButterflyCache<Self>>>>;
}

#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub enum Mod1000000007 {}

impl Modulus for Mod1000000007 {
    const VALUE: u32 = 1_000_000_007;
    const HINT_VALUE_IS_PRIME: bool = true;

    fn butterfly_cache() -> &'static LocalKey<RefCell<Option<ButterflyCache<Self>>>> {
        thread_local! {
            static BUTTERFLY_CACHE: RefCell<Option<ButterflyCache<Mod1000000007>>> = RefCell::default();
        }
        &BUTTERFLY_CACHE
    }
}

#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub enum Mod998244353 {}

impl Modulus for Mod998244353 {
    const VALUE: u32 = 998_244_353;
    const HINT_VALUE_IS_PRIME: bool = true;

    fn butterfly_cache() -> &'static LocalKey<RefCell<Option<ButterflyCache<Self>>>> {
        thread_local! {
            static BUTTERFLY_CACHE: RefCell<Option<ButterflyCache<Mod998244353>>> = RefCell::default();
        }
        &BUTTERFLY_CACHE
    }
}

pub struct ButterflyCache<M> {
    pub(crate) sum_e: Vec<StaticModInt<M>>,
    pub(crate) sum_ie: Vec<StaticModInt<M>>,
}

pub trait ModIntBase:
    Default
    + FromStr
    + From<i8>
    + From<i16>
    + From<i32>
    + From<i64>
    + From<i128>
    + From<isize>
    + From<u8>
    + From<u16>
    + From<u32>
    + From<u64>
    + From<u128>
    + From<usize>
    + Copy
    + Eq
    + Hash
    + fmt::Display
    + fmt::Debug
    + Neg<Output = Self>
    + Add<Output = Self>
    + Sub<Output = Self>
    + Mul<Output = Self>
    + Div<Output = Self>
    + AddAssign
    + SubAssign
    + MulAssign
    + DivAssign
{
    fn modulus() -> u32;

    fn raw(val: u32) -> Self;

    fn val(self) -> u32;

    fn inv(self) -> Self;

    #[inline]
    fn new<T: RemEuclidU32>(val: T) -> Self {
        Self::raw(val.rem_euclid_u32(Self::modulus()))
    }

    #[inline]
    fn pow(self, mut n: u64) -> Self {
        let mut x = self;
        let mut r = Self::raw(1);
        while n > 0 {
            if n & 1 == 1 {
                r *= x;
            }
            x *= x;
            n >>= 1;
        }
        r
    }
}

pub trait RemEuclidU32 {
    /// Calculates `self` _mod_ `modulus` losslessly.
    fn rem_euclid_u32(self, modulus: u32) -> u32;
}

macro_rules! impl_rem_euclid_u32_for_small_signed {
    ($($ty:tt),*) => {
        $(
            impl RemEuclidU32 for $ty {
                #[inline]
                fn rem_euclid_u32(self, modulus: u32) -> u32 {
                    (self as i64).rem_euclid(i64::from(modulus)) as _
                }
            }
        )*
    }
}

impl_rem_euclid_u32_for_small_signed!(i8, i16, i32, i64, isize);

impl RemEuclidU32 for i128 {
    #[inline]
    fn rem_euclid_u32(self, modulus: u32) -> u32 {
        self.rem_euclid(i128::from(modulus)) as _
    }
}

macro_rules! impl_rem_euclid_u32_for_small_unsigned {
    ($($ty:tt),*) => {
        $(
            impl RemEuclidU32 for $ty {
                #[inline]
                fn rem_euclid_u32(self, modulus: u32) -> u32 {
                    self as u32 % modulus
                }
            }
        )*
    }
}

macro_rules! impl_rem_euclid_u32_for_large_unsigned {
    ($($ty:tt),*) => {
        $(
            impl RemEuclidU32 for $ty {
                #[inline]
                fn rem_euclid_u32(self, modulus: u32) -> u32 {
                    (self % (modulus as $ty)) as _
                }
            }
        )*
    }
}

impl_rem_euclid_u32_for_small_unsigned!(u8, u16, u32);
impl_rem_euclid_u32_for_large_unsigned!(u64, u128);

#[cfg(target_pointer_width = "32")]
impl_rem_euclid_u32_for_small_unsigned!(usize);

#[cfg(target_pointer_width = "64")]
impl_rem_euclid_u32_for_large_unsigned!(usize);

trait InternalImplementations: ModIntBase {
    #[inline]
    fn inv_for_non_prime_modulus(this: Self) -> Self {
        let (gcd, x) = inv_gcd2(this.val().into(), Self::modulus().into());
        if gcd != 1 {
            panic!("the multiplicative inverse does not exist");
        }
        Self::new(x)
    }

    #[inline]
    fn default_impl() -> Self {
        Self::raw(0)
    }

    #[inline]
    fn from_str_impl(s: &str) -> Result<Self, Infallible> {
        Ok(s.parse::<i64>()
            .map(Self::new)
            .unwrap_or_else(|_| todo!("parsing as an arbitrary precision integer?")))
    }

    #[inline]
    fn hash_impl(this: &Self, state: &mut impl Hasher) {
        this.val().hash(state)
    }

    #[inline]
    fn display_impl(this: &Self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&this.val(), f)
    }

    #[inline]
    fn debug_impl(this: &Self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&this.val(), f)
    }

    #[inline]
    fn neg_impl(this: Self) -> Self {
        Self::sub_impl(Self::raw(0), this)
    }

    #[inline]
    fn add_impl(lhs: Self, rhs: Self) -> Self {
        let modulus = Self::modulus();
        let mut val = lhs.val() + rhs.val();
        if val >= modulus {
            val -= modulus;
        }
        Self::raw(val)
    }

    #[inline]
    fn sub_impl(lhs: Self, rhs: Self) -> Self {
        let modulus = Self::modulus();
        let mut val = lhs.val().wrapping_sub(rhs.val());
        if val >= modulus {
            val = val.wrapping_add(modulus)
        }
        Self::raw(val)
    }

    fn mul_impl(lhs: Self, rhs: Self) -> Self;

    #[inline]
    fn div_impl(lhs: Self, rhs: Self) -> Self {
        Self::mul_impl(lhs, rhs.inv())
    }
}

impl<M: Modulus> InternalImplementations for StaticModInt<M> {
    #[inline]
    fn mul_impl(lhs: Self, rhs: Self) -> Self {
        Self::raw((u64::from(lhs.val()) * u64::from(rhs.val()) % u64::from(M::VALUE)) as u32)
    }
}

macro_rules! impl_basic_traits {
    () => {};
    (impl <$generic_param:ident : $generic_param_bound:tt> _ for $self:ty; $($rest:tt)*) => {
        impl <$generic_param: $generic_param_bound> Default for $self {
            #[inline]
            fn default() -> Self {
                Self::default_impl()
            }
        }

        impl <$generic_param: $generic_param_bound> FromStr for $self {
            type Err = Infallible;

            #[inline]
            fn from_str(s: &str) -> Result<Self, Infallible> {
                Self::from_str_impl(s)
            }
        }

        impl<$generic_param: $generic_param_bound, V: RemEuclidU32> From<V> for $self {
            #[inline]
            fn from(from: V) -> Self {
                Self::new(from)
            }
        }

        #[allow(clippy::derive_hash_xor_eq)]
        impl<$generic_param: $generic_param_bound> Hash for $self {
            #[inline]
            fn hash<H: Hasher>(&self, state: &mut H) {
                Self::hash_impl(self, state)
            }
        }

        impl<$generic_param: $generic_param_bound> fmt::Display for $self {
            #[inline]
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                Self::display_impl(self, f)
            }
        }

        impl<$generic_param: $generic_param_bound> fmt::Debug for $self {
            #[inline]
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                Self::debug_impl(self, f)
            }
        }

        impl<$generic_param: $generic_param_bound> Neg for $self {
            type Output = $self;

            #[inline]
            fn neg(self) -> $self {
                Self::neg_impl(self)
            }
        }

        impl<$generic_param: $generic_param_bound> Neg for &'_ $self {
            type Output = $self;

            #[inline]
            fn neg(self) -> $self {
                <$self>::neg_impl(*self)
            }
        }

        impl_basic_traits!($($rest)*);
    };
}

impl_basic_traits! {
    impl <M: Modulus> _ for StaticModInt<M> ;
}

macro_rules! impl_bin_ops {
    () => {};
    (for<$($generic_param:ident : $generic_param_bound:tt),*> <$lhs_ty:ty> ~ <$rhs_ty:ty> -> $output:ty { { $lhs_body:expr } ~ { $rhs_body:expr } } $($rest:tt)*) => {
        impl <$($generic_param: $generic_param_bound),*> Add<$rhs_ty> for $lhs_ty {
            type Output = $output;

            #[inline]
            fn add(self, rhs: $rhs_ty) -> $output {
                <$output>::add_impl(apply($lhs_body, self), apply($rhs_body, rhs))
            }
        }

        impl <$($generic_param: $generic_param_bound),*> Sub<$rhs_ty> for $lhs_ty {
            type Output = $output;

            #[inline]
            fn sub(self, rhs: $rhs_ty) -> $output {
                <$output>::sub_impl(apply($lhs_body, self), apply($rhs_body, rhs))
            }
        }

        impl <$($generic_param: $generic_param_bound),*> Mul<$rhs_ty> for $lhs_ty {
            type Output = $output;

            #[inline]
            fn mul(self, rhs: $rhs_ty) -> $output {
                <$output>::mul_impl(apply($lhs_body, self), apply($rhs_body, rhs))
            }
        }

        impl <$($generic_param: $generic_param_bound),*> Div<$rhs_ty> for $lhs_ty {
            type Output = $output;

            #[inline]
            fn div(self, rhs: $rhs_ty) -> $output {
                <$output>::div_impl(apply($lhs_body, self), apply($rhs_body, rhs))
            }
        }

        impl_bin_ops!($($rest)*);
    };
}

macro_rules! impl_assign_ops {
    () => {};
    (for<$($generic_param:ident : $generic_param_bound:tt),*> <$lhs_ty:ty> ~= <$rhs_ty:ty> { _ ~= { $rhs_body:expr } } $($rest:tt)*) => {
        impl <$($generic_param: $generic_param_bound),*> AddAssign<$rhs_ty> for $lhs_ty {
            #[inline]
            fn add_assign(&mut self, rhs: $rhs_ty) {
                *self = *self + apply($rhs_body, rhs);
            }
        }

        impl <$($generic_param: $generic_param_bound),*> SubAssign<$rhs_ty> for $lhs_ty {
            #[inline]
            fn sub_assign(&mut self, rhs: $rhs_ty) {
                *self = *self - apply($rhs_body, rhs);
            }
        }

        impl <$($generic_param: $generic_param_bound),*> MulAssign<$rhs_ty> for $lhs_ty {
            #[inline]
            fn mul_assign(&mut self, rhs: $rhs_ty) {
                *self = *self * apply($rhs_body, rhs);
            }
        }

        impl <$($generic_param: $generic_param_bound),*> DivAssign<$rhs_ty> for $lhs_ty {
            #[inline]
            fn div_assign(&mut self, rhs: $rhs_ty) {
                *self = *self / apply($rhs_body, rhs);
            }
        }

        impl_assign_ops!($($rest)*);
    };
}

#[inline]
fn apply<F: FnOnce(X) -> O, X, O>(f: F, x: X) -> O {
    f(x)
}

impl_bin_ops! {
    for<M: Modulus> <StaticModInt<M>     > ~ <StaticModInt<M>     > -> StaticModInt<M>  { { |x| x  } ~ { |x| x  } }
    for<M: Modulus> <StaticModInt<M>     > ~ <&'_ StaticModInt<M> > -> StaticModInt<M>  { { |x| x  } ~ { |&x| x } }
    for<M: Modulus> <&'_ StaticModInt<M> > ~ <StaticModInt<M>     > -> StaticModInt<M>  { { |&x| x } ~ { |x| x  } }
    for<M: Modulus> <&'_ StaticModInt<M> > ~ <&'_ StaticModInt<M> > -> StaticModInt<M>  { { |&x| x } ~ { |&x| x } }

    for<M: Modulus, T: RemEuclidU32> <StaticModInt<M>     > ~ <T> -> StaticModInt<M>  { { |x| x  } ~ { StaticModInt::<M>::new } }
}

impl_assign_ops! {
    for<M: Modulus> <StaticModInt<M> > ~= <StaticModInt<M>     > { _ ~= { |x| x  } }
    for<M: Modulus> <StaticModInt<M> > ~= <&'_ StaticModInt<M> > { _ ~= { |&x| x } }

    for<M: Modulus, T: RemEuclidU32> <StaticModInt<M> > ~= <T> { _ ~= { StaticModInt::<M>::new } }
}

macro_rules! impl_folding {
    () => {};
    (impl<$generic_param:ident : $generic_param_bound:tt> $trait:ident<_> for $self:ty { fn $method:ident(_) -> _ { _($unit:expr, $op:expr) } } $($rest:tt)*) => {
        impl<$generic_param: $generic_param_bound> $trait<Self> for $self {
            #[inline]
            fn $method<S>(iter: S) -> Self
            where
                S: Iterator<Item = Self>,
            {
                iter.fold($unit, $op)
            }
        }

        impl<'a, $generic_param: $generic_param_bound> $trait<&'a Self> for $self {
            #[inline]
            fn $method<S>(iter: S) -> Self
            where
                S: Iterator<Item = &'a Self>,
            {
                iter.fold($unit, $op)
            }
        }

        impl_folding!($($rest)*);
    };
}

impl_folding! {
    impl<M: Modulus> Sum<_>     for StaticModInt<M>  { fn sum(_)     -> _ { _(Self::raw(0), Add::add) } }
    impl<M: Modulus> Product<_> for StaticModInt<M>  { fn product(_) -> _ { _(Self::raw(1), Mul::mul) } }
}

//convolution.ts
//

fn primitive_root(m: i32) -> i32 {
    match m {
        2 => return 1,
        167_772_161 => return 3,
        469_762_049 => return 3,
        754_974_721 => return 11,
        998_244_353 => return 3,
        _ => {}
    }

    let mut divs = [0; 20];
    divs[0] = 2;
    let mut cnt = 1;
    let mut x = (m - 1) / 2;
    while x % 2 == 0 {
        x /= 2;
    }
    for i in (3..std::i32::MAX).step_by(2) {
        if i as i64 * i as i64 > x as i64 {
            break;
        }
        if x % i == 0 {
            divs[cnt] = i;
            cnt += 1;
            while x % i == 0 {
                x /= i;
            }
        }
    }
    if x > 1 {
        divs[cnt] = x;
        cnt += 1;
    }
    let mut g = 2;
    loop {
        if (0..cnt).all(|i| modpow2(g, ((m - 1) / divs[i]) as i64, m) != 1) {
            break g as i32;
        }
        g += 1;
    }
}

fn ceil_pow2(n: u32) -> u32 {
    32 - n.saturating_sub(1).leading_zeros()
}

macro_rules! modulus {
    ($($name:ident),*) => {
        $(
            #[derive(Copy, Clone, Eq, PartialEq)]
            enum $name {}

            impl Modulus for $name {
                const VALUE: u32 = $name as _;
                const HINT_VALUE_IS_PRIME: bool = true;

                fn butterfly_cache() -> &'static ::std::thread::LocalKey<::std::cell::RefCell<::std::option::Option<ButterflyCache<Self>>>> {
                    thread_local! {
                        static BUTTERFLY_CACHE: ::std::cell::RefCell<::std::option::Option<ButterflyCache<$name>>> = ::std::default::Default::default();
                    }
                    &BUTTERFLY_CACHE
                }
            }
        )*
    };
}

#[allow(clippy::many_single_char_names)]
pub fn convolution<M>(a: &[StaticModInt<M>], b: &[StaticModInt<M>]) -> Vec<StaticModInt<M>>
where
    M: Modulus,
{
    if a.is_empty() || b.is_empty() {
        return vec![];
    }
    let (n, m) = (a.len(), b.len());

    if cmp::min(n, m) <= 60 {
        let (n, m, a, b) = if n < m { (m, n, b, a) } else { (n, m, a, b) };
        let mut ans = vec![StaticModInt::new(0); n + m - 1];
        for i in 0..n {
            for j in 0..m {
                ans[i + j] += a[i] * b[j];
            }
        }
        return ans;
    }

    let (mut a, mut b) = (a.to_owned(), b.to_owned());
    let z = 1 << ceil_pow2((n + m - 1) as _);
    a.resize(z, StaticModInt::raw(0));
    butterfly(&mut a);
    b.resize(z, StaticModInt::raw(0));
    butterfly(&mut b);
    for (a, b) in a.iter_mut().zip(&b) {
        *a *= b;
    }
    butterfly_inv(&mut a);
    a.resize(n + m - 1, StaticModInt::raw(0));
    let iz = StaticModInt::new(z).inv();
    for a in &mut a {
        *a *= iz;
    }
    a
}

pub fn convolution_raw<T, M>(a: &[T], b: &[T]) -> Vec<T>
where
    T: RemEuclidU32 + TryFrom<u32> + Clone,
    T::Error: fmt::Debug,
    M: Modulus,
{
    let a = a.iter().cloned().map(Into::into).collect::<Vec<_>>();
    let b = b.iter().cloned().map(Into::into).collect::<Vec<_>>();
    convolution::<M>(&a, &b)
        .into_iter()
        .map(|z| {
            z.val()
                .try_into()
                .expect("the numeric type is smaller than the modulus")
        })
        .collect()
}

#[allow(clippy::many_single_char_names)]
fn butterfly<M: Modulus>(a: &mut [StaticModInt<M>]) {
    let n = a.len();
    let h = ceil_pow2(n as u32);

    M::butterfly_cache().with(|cache| {
        let mut cache = cache.borrow_mut();
        let ButterflyCache { sum_e, .. } = cache.get_or_insert_with(prepare);
        for ph in 1..=h {
            let w = 1 << (ph - 1);
            let p = 1 << (h - ph);
            let mut now = StaticModInt::<M>::new(1);
            for s in 0..w {
                let offset = s << (h - ph + 1);
                for i in 0..p {
                    let l = a[i + offset];
                    let r = a[i + offset + p] * now;
                    a[i + offset] = l + r;
                    a[i + offset + p] = l - r;
                }
                now *= sum_e[(!s).trailing_zeros() as usize];
            }
        }
    });
}

#[allow(clippy::many_single_char_names)]
fn butterfly_inv<M: Modulus>(a: &mut [StaticModInt<M>]) {
    let n = a.len();
    let h = ceil_pow2(n as u32);

    M::butterfly_cache().with(|cache| {
        let mut cache = cache.borrow_mut();
        let ButterflyCache { sum_ie, .. } = cache.get_or_insert_with(prepare);
        for ph in (1..=h).rev() {
            let w = 1 << (ph - 1);
            let p = 1 << (h - ph);
            let mut inow = StaticModInt::<M>::new(1);
            for s in 0..w {
                let offset = s << (h - ph + 1);
                for i in 0..p {
                    let l = a[i + offset];
                    let r = a[i + offset + p];
                    a[i + offset] = l + r;
                    a[i + offset + p] = StaticModInt::new(M::VALUE + l.val() - r.val()) * inow;
                }
                inow *= sum_ie[(!s).trailing_zeros() as usize];
            }
        }
    });
}

fn prepare<M: Modulus>() -> ButterflyCache<M> {
    let g = StaticModInt::<M>::raw(primitive_root(M::VALUE as i32) as u32);
    let mut es = [StaticModInt::<M>::raw(0); 30]; // es[i]^(2^(2+i)) == 1
    let mut ies = [StaticModInt::<M>::raw(0); 30];
    let cnt2 = (M::VALUE - 1).trailing_zeros() as usize;
    let mut e = g.pow(((M::VALUE - 1) >> cnt2).into());
    let mut ie = e.inv();
    for i in (2..=cnt2).rev() {
        es[i - 2] = e;
        ies[i - 2] = ie;
        e *= e;
        ie *= ie;
    }
    let sum_e = es
        .iter()
        .scan(StaticModInt::new(1), |acc, e| {
            *acc *= e;
            Some(*acc)
        })
        .collect();
    let sum_ie = ies
        .iter()
        .scan(StaticModInt::new(1), |acc, ie| {
            *acc *= ie;
            Some(*acc)
        })
        .collect();
    ButterflyCache { sum_e, sum_ie }
}

use std::{
    convert::{TryFrom, TryInto as _},
};

#[allow(clippy::many_single_char_names)]
pub fn convolution_i64(a: &[i64], b: &[i64]) -> Vec<i64> {
    const M1: u64 = 754_974_721; // 2^24
    const M2: u64 = 167_772_161; // 2^25
    const M3: u64 = 469_762_049; // 2^26
    const M2M3: u64 = M2 * M3;
    const M1M3: u64 = M1 * M3;
    const M1M2: u64 = M1 * M2;
    const M1M2M3: u64 = M1M2.wrapping_mul(M3);

    modulus!(M1, M2, M3);

    if a.is_empty() || b.is_empty() {
        return vec![];
    }

    let (_, i1) = inv_gcd(M2M3 as _, M1 as _);
    let (_, i2) = inv_gcd(M1M3 as _, M2 as _);
    let (_, i3) = inv_gcd(M1M2 as _, M3 as _);

    let c1 = convolution_raw::<i64, M1>(a, b);
    let c2 = convolution_raw::<i64, M2>(a, b);
    let c3 = convolution_raw::<i64, M3>(a, b);

    c1.into_iter()
        .zip(c2)
        .zip(c3)
        .map(|((c1, c2), c3)| {
            const OFFSET: &[u64] = &[0, 0, M1M2M3, 2 * M1M2M3, 3 * M1M2M3];

            let mut x = [(c1, i1, M1, M2M3), (c2, i2, M2, M1M3), (c3, i3, M3, M1M2)]
                .iter()
                .map(|&(c, i, m1, m2)| c.wrapping_mul(i).rem_euclid(m1 as _).wrapping_mul(m2 as _))
                .fold(0, i64::wrapping_add);

            let mut diff = c1 - safe_mod(x, M1 as _);
            if diff < 0 {
                diff += M1 as i64;
            }
            x = x.wrapping_sub(OFFSET[diff.rem_euclid(5) as usize] as _);
            x
        })
        .collect()
}

//suffix array
struct suffix_array{
    n:usize,
    S:Vec<i64>,
    k:usize,
    sa:Vec<i64>,
    rank:Vec<i64>,
    tmp:Vec<i64>,
    lcp:Vec<i64>,
}

impl suffix_array{
    fn compare_sa(&mut self, i:&i64, j:&i64)-> std::cmp::Ordering{
        match self.rank[*i as usize].cmp(&(self.rank[*j as usize])){
            std::cmp::Ordering::Less => std::cmp::Ordering::Less,
            std::cmp::Ordering::Greater => std::cmp::Ordering::Greater,
            std::cmp::Ordering::Equal => {
                let mut ri =  {if (*i as usize+self.k)<=self.n{self.rank[*i as usize+self.k]}else{-1}};
                let mut rj =  {if (*j as usize+self.k)<=self.n{self.rank[*j as usize+self.k]}else{-1}};
                match ri.cmp(&rj){
                    std::cmp::Ordering::Less => std::cmp::Ordering::Less,
                    std::cmp::Ordering::Greater => std::cmp::Ordering::Greater,
                    std::cmp::Ordering::Equal => std::cmp::Ordering::Greater,
                }

            }
        }
    }

    fn new(S:&Vec<char>)->suffix_array{
        let mut n = S.len();
        let mut s = vec![];
        for i in 0..n{
            s.push(S[i] as i64);
        }
        let mut sa = vec![];
        let mut rank = vec![0;n+1];
        let mut tmp = vec![0;n+1];
        let mut lcp = vec![];
        return suffix_array{lcp:lcp, n:n, S:s, sa:sa, rank:rank, tmp:tmp, k:0};
    }
    fn build(&mut self){
        let mut sa = vec![0;self.n+1];
        for i in 0..self.n+1{
            sa[i] = i as i64;
            self.rank[i] = {if i<self.n{self.S[i]}else{-1}};
        }
        self.k = 1;
        while(true){
            if self.k>self.n{
                break;
            }
            sa.sort_by(|a, b| self.compare_sa(a, b));
            self.tmp[sa[0] as usize] = 0;
            for i in 1..self.n+1{
                let res = self.compare_sa(&(sa[i-1]), &(sa[i]));
                let mut zo = 0;
                if res == Ordering::Greater{
                    zo = 0;
                }
                else{
                    zo = 1;
                }
                self.tmp[sa[i]  as usize] = self.tmp[sa[i-1] as usize] + zo;
            }
            mem::swap(&mut self.rank, &mut self.tmp);
            self.k*=2;
        }
        self.sa = sa;
    }
    fn contain(&self, T:&Vec<i64>)->bool{
        let mut a = 0;
        let mut b = self.S.len();
        let mut t = T.as_slice();
        while(b-a>1){
            let mut c=  (a+b)/2;
            if &(self.S[ (self.sa[c] as usize)..(self.sa[c] as usize + T.len()) ]) < t{
                a = c;
            }
            else{
                b = c;
            }
        }
        return &(self.S[self.sa[b] as usize ..self.sa[b] as usize+T.len()]) == T.as_slice();
    }

    fn construct_lcp(&mut self){
        let mut n = self.S.len();
        for i in 0..n+1{
            self.rank[self.sa[i] as usize] = i as i64;
        }
        let mut h = 0;
        self.lcp = vec![0;n+1];
        self.lcp[0] = 0;
        for i in 0..n{
            let mut j = self.sa[self.rank[i] as usize -1];
            if h>0{
                h-=1;
            }
            while(true){
                if( (j as i64+h as i64) <n as i64) && ( (i as i64+h as i64)<n as i64){
                }
                else{
                    break;
                }
                if self.S[j as usize+h] != self.S[i as usize+h]{
                    break;
                }

                h+=1;
            }
            self.lcp[self.rank[i] as usize-1] = h as i64;
        }
    }
    fn compare_pat(&self, x:usize, pat:&Vec<i64>, size:usize)->i64{
        for i in 0..size{
            let a = pat[i];
            if x+i>=self.S.len(){
                return -1;
            }
            let b = self.S[x+i];
            if a<b{
                return -1;
            }
            else if a>b{
                return 1;
            }
        }
        return 0;
    }

    fn search_pattern_sub(&self, pat:&Vec<i64>, pat_size:usize)->i64{
        let mut low= 0;
        let mut high = self.S.len()-1;
        while low<=high{
            let mut mid =(low+high)/2;
            let mut r = self.compare_pat(self.sa[mid] as usize, pat, pat_size);
            if r==0{
                return mid as i64;
            }
            else if r>0{
                low = mid+1;
            }
            else{
                high = mid-1;
            }
        }
        return -1;
    }
    fn search_pat(&self, pat:&Vec<i64>)->i64{
        let mut pat_size = pat.len();
        let mut x = self.search_pattern_sub(pat, pat_size);
        if x >= 0{
            return self.sa[x as usize];
        }
        return -1;
    }
    fn search_pattern_all(&self, pat:&Vec<i64>)->Vec<i64>{
        let mut pat_size = pat.len();
        let mut x = self.search_pattern_sub(pat, pat_size);
        if x<0{
            return vec![];
        }
        let mut s = x-1;
        while s>=0 && self.compare_pat(self.sa[s as usize] as usize, pat, pat_size) == 0 {
            s -=1;
        }
        let mut e = x+1;
        while (e as usize) < self.S.len() && self.compare_pat(self.sa[e as usize] as usize, pat, pat_size) ==0{
            e+=1;
        }
        return self.sa[s as usize+1..e as usize].to_vec();
    }

}

//素因数分解
fn prime_factor(n:usize)->HashMap<usize, usize>{
    let mut res = HashMap::new();
    let mut n = n;
    for i in 2..n{
        if i*i>n{break;}
        while(n%i==0){
            *res.entry(i).or_insert(0)+=1;
            n/=i;
        }
    }
    if n != 1{
        res.insert(n, 1);
    }
    res
}

//素因数分解　クエリ
//let mut spf = smallest_prime_factor(10000);
//let mut f = factorization(x, &sfp);
fn smallest_prime_factor(n:usize)->Vec<usize>{
    let mut spf = vec![0;n+1];
    for i in 0..n+1{
        spf[i] = i;
    }
    for i in 2..INFu{
        if i*i>n{
            break;
        }
        if spf[i] == i{
            let mut j = i*i;
            while(j<=n){
                if spf[j] == j{
                    spf[j] = i;
                }
                j+=i;
            }
        }
    }

    return spf;
}
fn factorization(x:usize, spf:&Vec<usize>)->Vec<usize>{
    let mut res = vec![];
    let mut x = x;
    while(x != 1){
        res.push(spf[x]);
        x/=spf[x];
    }
    res.sort();
    return res;
}

fn usize_multiply_is_overflow(a:usize, b:usize)->bool{
    if a<std::usize::MAX/b{
        return false;
    }
    else{
        return true;
    }
}

fn sieve(n:usize) -> (Vec<bool>, Vec<usize>){
    let mut p:usize = 0;
    let mut is_prime = vec![false; n+1];
    let mut prime = Vec::new();
    for i in 0..n+1{
        is_prime[i] = true;
    }
    is_prime[0] = false;
    is_prime[1] = false;
    for i in 2..n+1{
        if is_prime[i]{
            prime.push(i as usize);
            let mut j = 2*i;
            while(j<=n){
                is_prime[j] = false;
                j+=i;
            }
        }
    }
    (is_prime, prime)

}

fn transpose<T>(v: Vec<Vec<T>>) -> Vec<Vec<T>>
where
    T: Clone,
{
    assert!(!v.is_empty());
    (0..v[0].len())
        .map(|i| v.iter().map(|inner| inner[i].clone()).collect::<Vec<T>>())
        .collect()
}
use std::ops::Bound::Included;
use std::iter::{once, repeat};

fn solve(){
    let sssss = std::io::stdin();
    let mut sc = Scanner { stdin: sssss.lock() };
    //let mut test:usize = sc.read();
    let test = 1;
    for _ in 0..test{
        let mut n:usize = sc.read();
        let mut spf = smallest_prime_factor(2*1000000+10);
        let mut res = 1;
        let mut s = 1;
        let mut dp = vec![0;n+1];
        dp[1] = 1;
        for i in 2..n+1{
            let mut f = factorization(i, &spf);
            let mut h = HashMap::new();
            for j in &f{
                *h.entry(*j).or_insert(0)+=1;
            }

            let mut ret = 1;
            for (j, k) in &h{
                ret*= (*k + 1);

            }
            dp[i] = (ret + s)%MOD;
            s+=dp[i];
            s%=MOD;
        }
        println!("{}", dp[n]);

    }

}

fn main(){
    solve();
}

const PI:f64 = std::f64::consts::PI;
pub static MOD:i64    = 998244353;
pub static MODu:usize = 1000000007;
pub static MODi32:i32 = 998244353;
pub static eps:f64 = 1e-6;
const INF: i64 = 1 << 60;
const INFu:usize = 1<<30;


use std::io::{self, Write};

const MOD: i64 = 998244353i64;

fn div(n: usize) -> Vec<i64> {
    let mut ans: Vec<i64> = vec![0; n + 1];
    for i in 1..=n {
        for j in (i..=n).step_by(i) {
            ans[j] += 1;
        }
    }
    ans
}

fn main() {
    let (stdin, stdout) = (io::stdin(), io::stdout());
    let mut sc = cf_scanner::Scanner::new(stdin.lock());
    let mut out = io::BufWriter::new(stdout.lock());

    let n: usize = sc.next();
    let div: Vec<i64> = div(n);
    let mut dp: Vec<i64> = vec![0; n + 1];
    let mut ans: Vec<i64> = vec![0; n + 1];
    for i in 1..=n {
        ans[i] = (div[i] + dp[i - 1]) % MOD;
        dp[i] = (ans[i] + dp[i - 1]) % MOD;
    }
    writeln!(out, "{}", ans[n]).unwrap();
}

#[allow(dead_code)]
mod cf_scanner {
    use std::io;
    use std::str::{FromStr, SplitAsciiWhitespace};

    pub struct Scanner<R> {
        reader: R,
        buf_str: Vec<u8>,
        buf_iter: SplitAsciiWhitespace<'static>,
    }

    impl<R: io::BufRead> Scanner<R> {
        pub fn new(reader: R) -> Scanner<R> {
            Scanner {
                reader,
                buf_str: Vec::new(),
                buf_iter: "".split_ascii_whitespace(),
            }
        }

        pub fn next<T: FromStr>(&mut self) -> T {
            loop {
                if let Some(token) = self.buf_iter.next() {
                    return token.parse().ok().expect("Failed parse");
                }
                self.buf_str.clear();
                self.reader
                    .read_until(b'\n', &mut self.buf_str)
                    .expect("Failed read");
                self.buf_iter = unsafe {
                    let slice = std::str::from_utf8_unchecked(&self.buf_str);
                    std::mem::transmute(slice.split_ascii_whitespace())
                }
            }
        }

        pub fn next_vec<T: FromStr>(&mut self, n: usize) -> Vec<T> {
            (0..n).map(|_| self.next()).collect()
        }

        pub fn next_str(&mut self) -> Vec<char> {
            self.next::<String>().chars().collect()
        }
    }
}


use crate::lib::stdio::*;
use crate::lib::gf::Gf99 as Int;

fn main() {
    scan!(n: usize);

    let mut lpf: Vec<_> = (0..=n).collect();
    for i in 2..=n {
        if lpf[i] == i {
            for j in (i..=n).step_by(i) {
                lpf[j] = lpf[j].min(i);
            }
        }
    }
    let d = |mut n: usize| {
        let mut now = 0;
        let mut cnt = 0;
        let mut ans = 1;
        while n != 1 {
            if now != lpf[n] {
                ans *= cnt + 1;
                cnt = 0;
                now = lpf[n];
            }
            cnt += 1;
            n /= now;
        }
        ans *= cnt + 1;
        ans
    };
    let mut sum = Int::one();
    let mut now = Int::zero();
    for n in 1..=n {
        now = sum + d(n) - 1;
        sum += now;
    }
    prtln!(now);
}

pub mod lib {

pub mod gf {

// modular arithmetics

use crate::lib::zo::ZeroOne;
use std::marker::PhantomData;
use std::{cmp, fmt, iter, ops::*, str};

pub trait Mod: Default + Clone + Copy {
    const P: u32;
    const K: u32; // -1 / P mod 2^32
    const R2: u32; // 2^64 mod P
}

// montgomery reduction (x -> x / 2^32 mod P)
fn reduce<M: Mod>(x: u64) -> u32 {
    let s = M::K.wrapping_mul(x as u32);
    ((x + s as u64 * M::P as u64) >> 32) as u32
}

#[macro_export]
macro_rules! def_prime {
    ($name:ident, $modu:expr, $k:expr) => {
        #[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
        pub struct $name;
        impl Mod for $name {
            const P: u32 = $modu;
            const K: u32 = $k;
            const R2: u32 = ((1_u128 << 64) % $modu) as u32;
        }
    };
}

def_prime!(ModA, 1_000_000_007, 2_226_617_417);
def_prime!(ModB, 998_244_353, 998_244_351);
def_prime!(ModC, 1_012_924_417, 1_012_924_415);
def_prime!(ModD, 924_844_033, 924_844_031);

#[repr(transparent)]
#[derive(Default, Clone, Copy)]
pub struct Gf<M: Mod> {
    val: u32,
    _m: PhantomData<*const M>,
}

pub type GfA = Gf<ModA>;
pub type GfB = Gf<ModB>;
pub type GfC = Gf<ModC>;
pub type GfD = Gf<ModD>;
pub type Gf17 = GfA;
pub type Gf99 = GfB;

impl<M: Mod> Gf<M> {
    pub const P: u32 = M::P;
    pub fn zero() -> Self { ZeroOne::zero() }
    pub fn one() -> Self { ZeroOne::one() }
    pub fn new(val: u32) -> Self { val.into() }
    pub fn is_zero(&self) -> bool { *self == Self::zero() }
    fn from_raw(val: u32) -> Self { Gf { val, _m: PhantomData } }
    pub fn value(self) -> u32 {
        let v = reduce::<M>(self.val as u64);
        if v >= M::P { v - M::P } else { v }
    }
    pub fn pow(mut self, mut k: u64) -> Self {
        if self.val == 0 && k == 0 { return Self::new(1); }
        k %= (M::P - 1) as u64;
        let mut res = Self::one();
        while !k.is_zero() {
            if k % 2 != 0 { res *= self; }
            self *= self; k >>= 1;
        }
        res
    }
    pub fn inv(self) -> Self {
        let (mut a, mut b, mut u, mut v) = (M::P as i32, self.value() as i32, 0, 1);
        while b != 0 {
            let t = a / b;
            a -= t * b; u -= t * v;
            std::mem::swap(&mut a, &mut b); std::mem::swap(&mut u, &mut v);
        }
        debug_assert_eq!(a, 1);
        if u < 0 { debug_assert_eq!(v, M::P as i32); u += v; }
        Self::new(u as u32)
    }
}
impl<M: Mod> From<u32> for Gf<M> {
    fn from(x: u32) -> Self { Gf::from_raw(reduce::<M>(x as u64 * M::R2 as u64)) }
}
macro_rules! impl_from_int {
    ($($t:ty),*) => { $(
        impl<M: Mod> From<$t> for Gf<M> {
            fn from(x: $t) -> Self {
                Gf::from_raw(reduce::<M>(x.rem_euclid(M::P as _) as u64 * M::R2 as u64))
            }
        }
    )* };
}
impl_from_int!(u64, usize, i32, i64, isize);
impl<M: Mod> cmp::PartialEq for Gf<M> {
    fn eq(&self, other: &Self) -> bool {
        let val = |obj: &Gf<M>| {
            if obj.val >= M::P { obj.val - M::P } else { obj.val }
        };
        val(self) == val(other)
    }
}
impl<M: Mod> cmp::Eq for Gf<M> {}
impl<M: Mod, T: Into<Gf<M>>> AddAssign<T> for Gf<M> {
    fn add_assign(&mut self, rhs: T) {
        self.val += rhs.into().val;
        if self.val >= M::P * 2 { self.val -= M::P * 2; }
    }
}
impl<M: Mod, T: Into<Gf<M>>> SubAssign<T> for Gf<M> {
    fn sub_assign(&mut self, rhs: T) {
        let rhs = rhs.into();
        if self.val < rhs.val { self.val += M::P * 2; }
        self.val -= rhs.val;
    }
}
impl<M: Mod, T: Into<Gf<M>>> MulAssign<T> for Gf<M> {
    fn mul_assign(&mut self, rhs: T) {
        self.val = reduce::<M>(self.val as u64 * rhs.into().val as u64);
    }
}
impl<M: Mod, T: Into<Gf<M>>> DivAssign<T> for Gf<M> {
    fn div_assign(&mut self, rhs: T) { *self *= rhs.into().inv(); }
}
macro_rules! impl_binop {
    ($(($Op:ident, $op:ident, $OpAssign:ident, $op_assign:ident)),*) => { $(
        impl<M: Mod, T: Into<Gf<M>>> $Op<T> for Gf<M> {
            type Output = Self;
            fn $op(mut self, rhs: T) -> Self { self.$op_assign(rhs); self }
        }
    )* };
}
impl_binop!(
    (Add, add, AddAssign, add_assign),
    (Sub, sub, SubAssign, sub_assign),
    (Mul, mul, MulAssign, mul_assign),
    (Div, div, DivAssign, div_assign)
);
impl<M: Mod> Neg for Gf<M> {
    type Output = Self;
    fn neg(self) -> Self { Gf::from_raw(M::P * 2 - self.val) }
}
impl<M: Mod> iter::Sum for Gf<M> {
    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { iter.fold(Self::zero(), |b, x| b + x) }
}
impl<M: Mod> iter::Product for Gf<M> {
    fn product<I: Iterator<Item = Self>>(iter: I) -> Self { iter.fold(Self::one(), |b, x| b * x) }
}
impl<M: Mod> fmt::Debug for Gf<M> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value().fmt(f) }
}
impl<M: Mod> fmt::Display for Gf<M> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value().fmt(f) }
}
impl<M: Mod> str::FromStr for Gf<M> {
    type Err = <u32 as str::FromStr>::Err;
    fn from_str(s: &str) -> Result<Self, Self::Err> { u32::from_str(s).map(Self::new) }
}
impl<M: Mod> ZeroOne for Gf<M> {
    fn zero() -> Self { Self { val: 0, _m: PhantomData } }
    fn one() -> Self { Self { val: M::P.wrapping_neg() % M::P, _m: PhantomData } }
}

}  // mod gf

pub mod stdio {

pub use crate::prtln;
pub use crate::scan;
pub use crate::parse;
pub use  std::io::Write;
use std::io::{stdout, BufWriter, StdoutLock};

pub fn stdout_buf() -> BufWriter<StdoutLock<'static>> {
    let out = Box::leak(Box::new(stdout()));
    BufWriter::new(out.lock())
}

#[macro_export]
macro_rules! prtln {
    (@ $dst:expr, iter=$v:expr) => { $crate::prtln!(@ $dst, iter=$v, sep=" "); };
    (@ $dst:expr, iter=$v:expr, sep=$sep:expr) => { {
        let mut iter = $v.into_iter();
        if let Some(expr) = iter.next() {
            std::write!($dst, "{}", expr).unwrap();
            for v in iter { std::write!($dst, "{}{}", $sep, v).unwrap(); }
        }
        $crate::prtln!(@ $dst, "");
    } };
    (@ $dst:expr, bytes=$v:expr) => {
        $crate::prtln!(@ $dst, std::str::from_utf8(&$v).unwrap());
    };
    (@ $dst:expr, YesNo=$v:expr) => { $crate::prtln!(@ $dst, if $v { "Yes" } else { "No" }); };
    (@ $dst:expr, YESNO=$v:expr) => { $crate::prtln!(@ $dst, if $v { "YES" } else { "NO" }); };
    (@ $dst:expr, $v:expr, no eol) => { std::write!($dst, "{}", $v).unwrap(); };
    (@ $dst:expr, $v:expr) => { std::writeln!($dst, "{}", $v).unwrap(); };
    (@ $dst:expr, $v:expr, $($t:tt)*) => { {
        std::write!($dst, "{} ", $v).unwrap();
        $crate::prtln!(@ $dst, $($t)*);
    } };
    (new $var:ident $(,)?) => { let mut $var = stdout_buf(); };
    (new $var:ident, $($t:tt)*) => {
        $crate::prtln!(new $var);
        $crate::prtln!(to $var, $($t)*);
    };
    (to $var:ident, $($t:tt)*) => { {
        $crate::prtln!(@ $var, $($t)*);
    } };
    ($($t:tt)*) => { {
        $crate::prtln!(new __prtln, $($t)*);
        std::mem::drop(__prtln);
    } };
}

#[macro_export]
macro_rules! scan {
    (@ $iter:expr $(,)?) => {};
    (@ $iter:expr, mut $var:ident : $t:tt $($r:tt)*) => {
        #[allow(non_snake_case)]
        let mut $var = $crate::parse!($iter.into_iter(), $t);
        $crate::scan!(@ $iter $($r)*)
    };
    (@ $iter:expr, $var:ident : $t:tt $($r:tt)*) => {
        #[allow(non_snake_case)]
        let $var = $crate::parse!($iter.into_iter(), $t);
        $crate::scan!(@ $iter $($r)*)
    };
    (@ $iter:expr, $pat:pat in $t:tt $($r:tt)*) => {
        let $pat = $crate::parse!($iter.into_iter(), $t);
        $crate::scan!(@ $iter $($r)*)
    };
    (from $s:expr, $($r:tt)*) => { $crate::scan!(@ $s, $($r)*); };
    (new $var:ident, $($r:tt)*) => {
        let mut __input = String::new();
        std::io::Read::read_to_string(&mut std::io::stdin(), &mut __input).unwrap();
        let $var = &mut __input.split_ascii_whitespace();
        $crate::scan!(@ $var, $($r)*);
    };
    ($($r:tt)*) => { $crate::scan!(new __scan, $($r)*); };
}

#[macro_export]
macro_rules! parse {
    ($iter:expr, ( $($t:tt),* )) => { ( $($crate::parse!($iter, $t)),* ) };
    ($iter:expr, [ $t:tt ; $len:expr ]) => {
        (0..$len).map(|_| $crate::parse!($iter, $t)).collect::<Vec<_>>()
    };
    ($iter:expr, bytes) => { $iter.next().expect("no input").as_bytes() };
    ($iter:expr, [u8]) => { $iter.next().expect("no input").as_bytes().to_vec() };
    ($iter:expr, [char]) => { $iter.next().expect("no input").chars().collect::<Vec<_>>() };
    ($iter:expr, usize1) => { $crate::parse!($iter, usize) - 1 };
    (@graph $iter:expr, $n:expr, $m:expr) => { {
        let mut graph = vec![Vec::new(); $n];
        for _ in 0..$m {
            let (a, b) = $crate::parse!($iter, (usize1, usize1));
            graph[a].push(b);
            graph[b].push(a);
        }
        ($n, graph)
    } };
    ($iter:expr, graph) => { {
        let (n, m) = $crate::parse!($iter, (usize, usize));
        $crate::parse!(@graph $iter, n, m)
    } };
    ($iter:expr, tree) => { {
        let n = $crate::parse!($iter, usize);
        $crate::parse!(@graph $iter, n, n - 1)
    } };
    ($iter:expr, $t:ty) => { $iter.next().expect("no input").parse::<$t>().expect("parse error") };
    ($iter:expr) => { $iter.next().expect("no input").parse().expect("parse error") };
}

}  // mod stdio

pub mod zo {

pub trait ZeroOne: Copy + PartialEq {
    fn zero() -> Self;
    fn one() -> Self;
    fn is_zero(self) -> bool { self == Self::zero() }
}

macro_rules! impl_zo {
    ($($t:ty),*) => { $(
        impl ZeroOne for $t {
            fn zero() -> Self { 0 as $t }
            fn one() -> Self { 1 as $t }
        }
    )* };
}

impl_zo!(i32, i64, i128, isize, u32, u64, u128, usize, f32, f64);

}  // mod zo

}  // mod lib


use std::io::Write;

// ---------- begin scannner ----------
#[allow(dead_code)]
mod scanner {
    use std::str::FromStr;
    pub struct Scanner<'a> {
        it: std::str::SplitWhitespace<'a>,
    }
    impl<'a> Scanner<'a> {
        pub fn new(s: &'a String) -> Scanner<'a> {
            Scanner {
                it: s.split_whitespace(),
            }
        }
        pub fn next<T: FromStr>(&mut self) -> T {
            self.it.next().unwrap().parse::<T>().ok().unwrap()
        }
        pub fn next_bytes(&mut self) -> Vec<u8> {
            self.it.next().unwrap().bytes().collect()
        }
        pub fn next_chars(&mut self) -> Vec<char> {
            self.it.next().unwrap().chars().collect()
        }
        pub fn next_vec<T: FromStr>(&mut self, len: usize) -> Vec<T> {
            (0..len).map(|_| self.next()).collect()
        }
    }
}
// ---------- end scannner ----------

fn main() {
    use std::io::Read;
    let mut s = String::new();
    std::io::stdin().read_to_string(&mut s).unwrap();
    let mut sc = scanner::Scanner::new(&s);
    let out = std::io::stdout();
    let mut out = std::io::BufWriter::new(out.lock());
    run(&mut sc, &mut out);
}

const MOD:i32 = 998244353;

fn solve(n: usize) -> i32 {
    let mut ans = vec![0i32; n + 1];
    let mut sum = 0i32;
    let mut presum = 0i32;
    for i in 1..n+1 {
        let mut j = i;
        while j <= n {
            ans[j] += 1;
            j+=i;
        }
        // println!("ans[{}]: {}, sum: {}, presum: {}", i, ans[i], sum, presum);
        if ans[i] + presum > MOD {
            sum = ans[i] + presum - MOD;
            presum += ans[i] + presum - MOD;
        } else {
            sum = ans[i] + presum;
            presum += ans[i] + presum;
        }
        if presum > MOD {
            presum -= MOD
        }
    }
    return sum
}

fn run<W: Write>(sc: &mut scanner::Scanner, out: &mut std::io::BufWriter<W>) {
    let n: usize = sc.next();
    writeln!(out, "{}", solve(n));
}


use std::io::{prelude::*, stdin, stdout, BufReader, BufWriter};

fn main() {
    let std_in = stdin();
    let in_lock = std_in.lock();
    let input = BufReader::new(in_lock);

    let std_out = stdout();
    let out_lock = std_out.lock();
    let mut output = BufWriter::new(out_lock);

    let mut lines = input.lines().map(|r| r.unwrap());

    let n = lines.next().unwrap().parse::<usize>().unwrap();

    let mut c: Vec<_> = (0..=n).collect();
    for p in (2..).take_while(|&p| p * p <= n) {
        if c[p] != p {
            continue;
        }

        for i in (1..).take_while(|&i| p * i <= n) {
            c[p * i] = p;
        }
    }

    let ndivisors = |mut n: usize| -> u64 {
        let mut r = 1;
        while n > 1 {
            let p: usize = c[n];
            let mut q = 0;
            while n > 1 && c[n] == p {
                n /= p;
                q += 1;
            }

            r *= q + 1;
        }

        r
    };

    let mut t0 = 0;
    let mut t1 = 1;

    const P: u64 = 998244353;

    for i in 2..=n {
        t0 = t1;
        t1 = (2 * t1 + ndivisors(i)) % P;
    }

    let ans = if t0 <= t1 { t1 - t0 } else { (t1 + P) - t0};

    writeln!(&mut output, "{}", ans).unwrap();
}


use std::io;
use std::io::Write;

/**************************************************

    START OF TEMPLATE CODE

 *************************************************/
#[allow(unused_macros)]
macro_rules! dbg {
    ($first_val:expr, $($val:expr),+ $(,)?) => {
        eprint!("[{}:{}] {} = {:?}",
                    file!(), line!(), stringify!($first_val), &$first_val);
        ($(eprint!(", {} = {:?}", stringify!($val), &$val)),+,);
        eprintln!();
    };
    ($first_val:expr) => {
        eprintln!("[{}:{}] {} = {:?}",
                    file!(), line!(), stringify!($first_val), &$first_val);
    };
}

enum InputSource {
    Stdin,
    FromFile(Vec<String>),
}

struct Scanner {
    buffer: Vec<String>,
    input_source: InputSource,
}

impl Scanner {
    #[allow(dead_code)]
    fn new() -> Self {
        Self { buffer: vec![], input_source: InputSource::Stdin }
    }

    #[allow(dead_code)]
    fn new_file(filename: &str) -> Self {
        let file = std::fs::read_to_string(filename).unwrap();
        let mut lines: Vec<String> = file.lines().map(|line| String::from(line)).collect();
        lines.reverse();
        Self { buffer: vec![], input_source: InputSource::FromFile(lines) }
    }

    #[allow(dead_code)]
    fn i64(&mut self) -> i64 {
        self.next::<i64>()
    }

    #[allow(dead_code)]
    fn i32(&mut self) -> i32 {
        self.next::<i32>()
    }

    #[allow(dead_code)]
    fn usize(&mut self) -> usize {
        self.next::<usize>()
    }

    #[allow(dead_code)]
    fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.next::<T>()).collect()
    }

    fn parse_next_line(&mut self) -> bool {
        let mut input = String::new();
        match &mut self.input_source {
            | InputSource::Stdin => {
                if std::io::stdin().read_line(&mut input).expect("Failed read") == 0 {
                    return false;
                }
            }
            | InputSource::FromFile(lines) => {
                match lines.pop() {
                    Some(line) => input = line,
                    None => return false,
                }
            }
        }

        self.buffer = input.split_whitespace().rev().map(String::from).collect();
        return true;
    }

    fn next<T: std::str::FromStr>(&mut self) -> T {
        loop {
            if let Some(token) = self.buffer.pop() {
                return token.parse().ok().expect("Failed parse");
            }

            self.parse_next_line();
        }
    }

    #[allow(dead_code)]
    fn has_more_elements(&mut self) -> bool {
        loop {
            if !self.buffer.is_empty() {
                return true;
            }
            if !self.parse_next_line() {
                return false;
            }
        }
    }

    #[allow(dead_code)]
    fn string(&mut self) -> Vec<u8> {
        self.next::<String>().into_bytes()
    }
}

mod modulo {
    const MODULO: i32 = 998244353;

    #[derive(Copy, Clone, Eq, PartialEq)]
    pub struct Mod(i32);

    impl Mod {
        #[allow(unused)]
        pub const ZERO: Self = Self(0);

        #[allow(unused)]
        pub const ONE: Self = Self(1);

        fn rev_rec(a: i32, m: i32) -> i32 {
            if a == 1 {
                return a;
            }
            return ((1 - Self::rev_rec(m % a, a) as i64 * m as i64) / a as i64 + m as i64) as i32;
        }

        #[allow(dead_code)]
        fn inv(self) -> Mod {
            Mod(Self::rev_rec(self.0, MODULO))
        }

        #[allow(dead_code)]
        pub fn new(mut x: i32) -> Self {
            if x < 0 {
                x += MODULO;
            } else if x >= MODULO {
                x -= MODULO;
            }
            assert!(0 <= x && x < MODULO);
            Self(x)
        }
    }

    impl std::fmt::Display for Mod {
        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
            write!(f, "{}", self.0)
        }
    }

    impl std::fmt::Debug for Mod {
        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
            const MAX: usize = 100;
            if self.0 <= MAX as i32 {
                write!(f, "{}", self.0)
            } else if self.0 >= MODULO - MAX as i32 {
                write!(f, "-{}", MODULO - self.0)
            } else {
                for denum in 1..MAX {
                    for num in 1..MAX {
                        if Mod(num as i32) / Mod(denum as i32) == *self {
                            return write!(f, "{}/{}", num, denum);
                        }
                    }
                }
                write!(f, "(?? {} ??)", self.0)
            }
        }
    }

    impl std::ops::Add for Mod {
        type Output = Mod;

        fn add(self, rhs: Self) -> Self::Output {
            let res = self.0 + rhs.0;
            if res >= MODULO {
                Mod(res - MODULO)
            } else {
                Mod(res)
            }
        }
    }

    impl std::ops::AddAssign for Mod {
        fn add_assign(&mut self, rhs: Self) {
            self.0 += rhs.0;
            if self.0 >= MODULO {
                self.0 -= MODULO;
            }
        }
    }

    impl std::ops::Sub for Mod {
        type Output = Mod;

        fn sub(self, rhs: Self) -> Self::Output {
            let res = self.0 - rhs.0;
            if res < 0 {
                Mod(res + MODULO)
            } else {
                Mod(res)
            }
        }
    }

    impl std::ops::SubAssign for Mod {
        fn sub_assign(&mut self, rhs: Self) {
            self.0 -= rhs.0;
            if self.0 < 0 {
                self.0 += MODULO;
            }
        }
    }

    impl std::ops::Mul for Mod {
        type Output = Mod;

        fn mul(self, rhs: Self) -> Self::Output {
            let res = (self.0 as i64) * (rhs.0 as i64) % (MODULO as i64);
            Mod(res as i32)
        }
    }

    impl std::ops::MulAssign for Mod {
        fn mul_assign(&mut self, rhs: Self) {
            self.0 = ((self.0 as i64) * (rhs.0 as i64) % (MODULO as i64)) as i32;
        }
    }

    impl std::ops::Div for Mod {
        type Output = Mod;

        fn div(self, rhs: Self) -> Self::Output {
            let rhs_inv = rhs.inv();
            self * rhs_inv
        }
    }

    impl std::ops::DivAssign for Mod {
        fn div_assign(&mut self, rhs: Self) {
            *self *= rhs.inv();
        }
    }
}

use modulo::*;

/**************************************************

    END OF TEMPLATE CODE

 *************************************************/

pub fn main() {
    let stdout = io::stdout();
    let mut out = std::io::BufWriter::new(stdout.lock());
    let mut sc = Scanner::new();

    let n = sc.usize();
    let mut res = vec![Mod::ZERO; n + 1];
    for delta in 1..=n {
        for sum in (delta..=n).step_by(delta) {
            res[sum] += Mod::ONE;
        }
    }
    let mut sum = Mod::ZERO;
    for i in 0..=n {
        res[i] += sum;
        sum += res[i];
    }
    writeln!(out, "{}", res[n]).unwrap();
}


//spnauti-rusT {{{
#[allow(unused_imports)]use std::{io::*,collections::*,fmt::Debug,str::{self,*},cmp::*,ops::{self,*},iter::{self,*}};
macro_rules!l{
	($($v:ident),+:$t:ty=$e:expr)=>{$(let$v:$t=$e;)+};(mut $($v:ident),+ =$e:expr)=>{$(let mut$v=$e;)+};
	(mut $($v:ident),+:$t:ty=$e:expr)=>{$(let mut$v:$t=$e;)+};($($v:ident),+ =$e:expr)=>{$(let$v=$e;)+};}
macro_rules!v{([$d:expr]$($s:tt)+)=>{vec![v!($($s)+);$d]};([])=>{Vec::new()};([$e:expr])=>{Vec::with_capacity($e)};(=$e:expr)=>{$e};}
fn rio()->(Reader,BufWriter<Stdout>){(Reader::new(),BufWriter::new(stdout()))}
struct Reader{buf:Vec<u8>,pos:usize,q:StdinLock<'static>}/*'>}*/#[allow(dead_code)]impl Reader{
	fn new()->Self{let r=unsafe{&*Box::into_raw(Box::new(stdin()))};Self{q:r.lock(),buf:v!([]),pos:0}}
	fn next_line(&mut self)->bool{self.buf.clear();self.pos=0;self.q.read_until(b'\n',&mut self.buf).unwrap_or(0)>0}
	fn byte(&mut self)->Option<u8>{
		if self.pos==self.buf.len(){if!self.next_line(){return None;}}self.pos+=1;Some(self.buf[self.pos-1])}
	fn vb(&mut self)->Vec<u8>{let mut s=v!([10]);let mut f=false;while let Some(c)=self.byte(){
		if!c.is_ascii_whitespace(){s.push(c);f=true;}else if f{break;}}s}
	fn p<T:FromStr>(&mut self)->T where T::Err:Debug{let w=self.vb();str::from_utf8(w.as_ref()).unwrap().parse::<T>().unwrap()}
	fn u(&mut self) ->  usize { self.p() }
}
//----------}}}

// Field (mod prime) {{{
mod field {
	use std::ops::*;
	#[allow(dead_code)] pub const Z0: Field = Field(0);
	#[allow(dead_code)] pub const Z1: Field = Field(1);
	const P5: i32 = 998_244_353;
	const P6: i64 = P5 as i64;
	#[derive(Debug,Clone,Copy,PartialEq,Eq,PartialOrd,Ord,Default)] pub struct Field(i32);
	#[allow(dead_code)] impl Field {
		pub fn new<T: Into<i64>>(a: T) -> Self { Self(Self::cv(a)) }
		pub unsafe fn new_unchecked(a: i32) -> Self { Self(a) }
		fn hi(mut a: i32) -> i32 { if a >= P5 { a -= P5; } a }
		fn lo(mut a: i32) -> i32 { if a < 0 { a += P5; } a }
		fn cv<T: Into<i64>>(a: T) -> i32 { Self::lo((a.into() % P6) as i32) }
		pub fn inv(self) -> Self { self.pow(P5-2) }
		pub fn pow<T: Into<i64>>(self, b: T) -> Self {
			let b = b.into(); if b == 0 { Self(1) } else {
				let mut res = self.pow(b/2); res *= res; if b % 2 == 1 {res *= self;} res
			}
		}
	}
	impl Add for Field { type Output = Self; fn add(self, a: Self) -> Self::Output { Self(Self::hi(self.0 + a.0)) }}
	impl Sub for Field { type Output = Self; fn sub(self, a: Self) -> Self::Output { Self(Self::lo(self.0 - a.0)) }}
	impl Mul for Field { type Output = Self; fn mul(self, a: Self) -> Self::Output { Self(Self::cv(self.0 as i64 * a.0 as i64)) }}
	impl Div for Field { type Output = Self; fn div(self, a: Self) -> Self::Output { self * a.inv() }}
	impl Neg for Field { type Output = Self; fn neg(self) -> Self::Output { Self(Self::lo(-self.0)) }}
	impl AddAssign for Field { fn add_assign(&mut self, a: Self) { *self = *self + a; } }
	impl SubAssign for Field { fn sub_assign(&mut self, a: Self) { *self = *self - a; } }
	impl MulAssign for Field { fn mul_assign(&mut self, a: Self) { *self = *self * a; } }
	impl DivAssign for Field { fn div_assign(&mut self, a: Self) { *self = *self / a; } }
	use std::fmt::{Display, Formatter, Result};
	impl Display for Field { fn fmt(&self, f: &mut Formatter) -> Result { write!(f, "{}", self.0) } }
}
#[allow(unused_imports)] use field::{Field, Field as Z, Z0, Z1};
//}}}

fn main() {
	let (mut rin,mut rout) = rio();

	let n = rin.u();
	let mut div = v!([n+1] = 1);
	for i in 2..=n {
		if div[i] == 1 {
			div[i] = 2;
			let mut j = i+i;
			while j <= n {
				let mut c = 2;
				let mut x = j / i;
				while x % i == 0 {
					x /= i;
					c += 1;
				}
				div[j] *= c;
				j += i;
			}
		}
	}
	l!(mut dp,s = v!([n+1] = Z0));
	for i in 1..=n {
		dp[i] = Z::new(div[i]) + s[i-1];
		s[i] = dp[i] + s[i-1];
	}
	writeln!(rout, "{}", dp[n]).unwrap();
}


// ---------- begin ModInt ----------
// モンゴメリ乗算を用いる
// ほぼCodeforces用
// 注意
// new_unchecked は値xが 0 <= x < modulo であることを仮定
// ModInt の中身は正規化された値で持ってるので直接読んだり書いたりするとぶっ壊れる
// 奇素数のみ
mod modint {

    use std::marker::*;
    use std::ops::*;

    pub trait Modulo {
        fn modulo() -> u32;
        fn rem() -> u32;
        fn ini() -> u64;
        fn reduce(x: u64) -> u32 {
            assert!(x < (Self::modulo() as u64) << 32);
            let b = (x as u32 * Self::rem()) as u64;
            let t = x + b * Self::modulo() as u64;
            let mut c = (t >> 32) as u32;
            if c >= Self::modulo() {
                c -= Self::modulo();
            }
            c as u32
        }
    }

    #[allow(dead_code)]
    pub enum Mod1_000_000_007 {}

    impl Modulo for Mod1_000_000_007 {
        fn modulo() -> u32 {
            1_000_000_007
        }
        fn rem() -> u32 {
            2226617417
        }
        fn ini() -> u64 {
            582344008
        }
    }

    #[allow(dead_code)]
    pub enum Mod998_244_353 {}

    impl Modulo for Mod998_244_353 {
        fn modulo() -> u32 {
            998_244_353
        }
        fn rem() -> u32 {
            998244351
        }
        fn ini() -> u64 {
            932051910
        }
    }

    #[allow(dead_code)]
    pub fn generate_umekomi_modulo(p: u32) {
        assert!(p < (1 << 31) && p > 2 && p & 1 == 1 && (2u32..).take_while(|v| v * v <= p).all(|k| p % k != 0));
        let mut t = 1u32;
        let mut s = !p + 1;
        let mut n = !0u32 >> 2;
        while n > 0 {
            if n & 1 == 1 {
                t *= s;
            }
            s *= s;
            n >>= 1;
        }
        let mut ini = (1u64 << 32) % p as u64;
        ini = (ini << 32) % p as u64;
        assert!(t * p == !0);
        println!("pub enum Mod{} {{}}", p);
        println!("impl Modulo for Mod{} {{", p);
        println!("    fn modulo() -> u32 {{");
        println!("        {}", p);
        println!("    }}");
        println!("    fn rem() -> u32 {{");
        println!("        {}", t);
        println!("    }}");
        println!("    fn ini() -> u32 {{");
        println!("        {}", ini);
        println!("    }}");
        println!("}}");
        let mut f = vec![];
        let mut n = p - 1;
        for i in 2.. {
            if i * i > n {
                break;
            }
            if n % i == 0 {
                f.push(i);
                while n % i == 0 {
                    n /= i;
                }
            }
        }
        if n > 1 {
            f.push(n);
        }
        let mut order = 1;
        let mut n = p - 1;
        while n % 2 == 0 {
            n /= 2;
            order <<= 1;
        }
        let z = (2u64..).find(|z| {
            f.iter().all(|f| mod_pow(*z, ((p - 1) / *f) as u64, p as u64) != 1)
        }).unwrap();
        let zeta = mod_pow(z, ((p - 1) / order) as u64, p as u64);
        println!("impl transform::NTTFriendly for Mod{} {{", p);
        println!("    fn order() -> usize {{");
        println!("        {}", order);
        println!("    }}");
        println!("    fn zeta() -> u32 {{");
        println!("        {}", zeta);
        println!("    }}");
        println!("}}");
    }

    pub struct ModInt<T>(u32, PhantomData<T>);

    impl<T> Clone for ModInt<T> {
        fn clone(&self) -> Self {
            ModInt::build(self.0)
        }
    }

    impl<T> Copy for ModInt<T> {}

    impl<T: Modulo> Add for ModInt<T> {
        type Output = ModInt<T>;
        fn add(self, rhs: Self) -> Self::Output {
            let mut d = self.0 + rhs.0;
            if d >= T::modulo() {
                d -= T::modulo();
            }
            Self::build(d)
        }
    }

    impl<T: Modulo> AddAssign for ModInt<T> {
        fn add_assign(&mut self, rhs: Self) {
            *self = *self + rhs;
        }
    }

    impl<T: Modulo> Sub for ModInt<T> {
        type Output = ModInt<T>;
        fn sub(self, rhs: Self) -> Self::Output {
            let mut d = T::modulo() + self.0 - rhs.0;
            if d >= T::modulo() {
                d -= T::modulo();
            }
            Self::build(d)
        }
    }

    impl<T: Modulo> SubAssign for ModInt<T> {
        fn sub_assign(&mut self, rhs: Self) {
            *self = *self - rhs;
        }
    }

    impl<T: Modulo> Mul for ModInt<T> {
        type Output = ModInt<T>;
        fn mul(self, rhs: Self) -> Self::Output {
            Self::build(T::reduce(self.0 as u64 * rhs.0 as u64))
        }
    }

    impl<T: Modulo> MulAssign for ModInt<T> {
        fn mul_assign(&mut self, rhs: Self) {
            *self = *self * rhs;
        }
    }

    impl<T: Modulo> Neg for ModInt<T> {
        type Output = ModInt<T>;
        fn neg(self) -> Self::Output {
            if self.0 == 0 {
                Self::zero()
            } else {
                Self::build(T::modulo() - self.0)
            }
        }
    }

    impl<T: Modulo> std::fmt::Display for ModInt<T> {
        fn fmt<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result {
            write!(f, "{}", self.get())
        }
    }

    impl<T: Modulo> std::fmt::Debug for ModInt<T> {
        fn fmt<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result {
            write!(f, "{}", self.get())
        }
    }

    impl<T: Modulo> std::str::FromStr for ModInt<T> {
        type Err = std::num::ParseIntError;
        fn from_str(s: &str) -> Result<Self, Self::Err> {
            let val = s.parse::<u32>()?;
            Ok(ModInt::new(val))
        }
    }

    impl<T: Modulo> From<usize> for ModInt<T> {
        fn from(val: usize) -> ModInt<T> {
            ModInt::new_unchecked((val % T::modulo() as usize) as u32)
        }
    }

    impl<T: Modulo> From<u64> for ModInt<T> {
        fn from(val: u64) -> ModInt<T> {
            ModInt::new_unchecked((val % T::modulo() as u64) as u32)
        }
    }

    impl<T: Modulo> From<i64> for ModInt<T> {
        fn from(val: i64) -> ModInt<T> {
            let m = T::modulo() as i64;
            ModInt::new((val % m + m) as u32)
        }
    }

    #[allow(dead_code)]
    impl<T> ModInt<T> {
        fn build(d: u32) -> Self {
            ModInt(d, PhantomData)
        }
        pub fn zero() -> Self {
            Self::build(0)
        }
        pub fn is_zero(&self) -> bool {
            self.0 == 0
        }
    }

    #[allow(dead_code)]
    impl<T: Modulo> ModInt<T> {
        pub fn new_unchecked(d: u32) -> Self {
            Self::build(T::reduce(d as u64 * T::ini()))
        }
        pub fn new(d: u32) -> Self {
            Self::new_unchecked(d % T::modulo())
        }
        pub fn one() -> Self {
            Self::new_unchecked(1)
        }
        pub fn get(&self) -> u32 {
            T::reduce(self.0 as u64)
        }
        pub fn pow(&self, mut n: u64) -> Self {
            let mut t = Self::one();
            let mut s = *self;
            while n > 0 {
                if n & 1 == 1 {
                    t *= s;
                }
                s *= s;
                n >>= 1;
            }
            t
        }
        pub fn inv(&self) -> Self {
            assert!(!self.is_zero());
            self.pow((T::modulo() - 2) as u64)
        }
    }

    pub fn mod_pow(mut r: u64, mut n: u64, m: u64) -> u64 {
        let mut t = 1 % m;
        while n > 0 {
            if n & 1 == 1 {
                t = t * r % m;
            }
            r = r * r % m;
            n >>= 1;
        }
        t
    }
}
// ---------- end ModInt ----------
// ---------- begin Precalc ----------
mod precalc {
    use super::modint::*;
    #[allow(dead_code)]
    pub struct Precalc<T> {
        inv: Vec<ModInt<T>>,
        fact: Vec<ModInt<T>>,
        ifact: Vec<ModInt<T>>,
    }
    #[allow(dead_code)]
    impl<T: Modulo> Precalc<T> {
        pub fn new(n: usize) -> Precalc<T> {
            let mut inv = vec![ModInt::one(); n + 1];
            let mut fact = vec![ModInt::one(); n + 1];
            let mut ifact = vec![ModInt::one(); n + 1];
            for i in 2..(n + 1) {
                fact[i] = fact[i - 1] * ModInt::new_unchecked(i as u32);
            }
            ifact[n] = fact[n].inv();
            if n > 0 {
                inv[n] = ifact[n] * fact[n - 1];
            }
            for i in (1..n).rev() {
                ifact[i] = ifact[i + 1] * ModInt::new_unchecked((i + 1) as u32);
                inv[i] = ifact[i] * fact[i - 1];
            }
            Precalc {
                inv: inv,
                fact: fact,
                ifact: ifact,
            }
        }
        pub fn inv(&self, n: usize) -> ModInt<T> {
            assert!(n > 0);
            self.inv[n]
        }
        pub fn fact(&self, n: usize) -> ModInt<T> {
            self.fact[n]
        }
        pub fn ifact(&self, n: usize) -> ModInt<T> {
            self.ifact[n]
        }
        pub fn perm(&self, n: usize, k: usize) -> ModInt<T> {
            if k > n {
                return ModInt::zero();
            }
            self.fact[n] * self.ifact[n - k]
        }
        pub fn comb(&self, n: usize, k: usize) -> ModInt<T> {
            if k > n {
                return ModInt::zero();
            }
            self.fact[n] * self.ifact[k] * self.ifact[n - k]
        }
    }
}
// ---------- end Precalc ----------
// ---------- begin NTT ----------
#[allow(dead_code)]
mod transform {
    use super::modint::*;
    pub trait NTTFriendly: Modulo {
        fn order() -> usize;
        fn zeta() -> u32;
    }
    pub fn ntt<T: NTTFriendly>(f: &mut [ModInt<T>]) {
        let n = f.len();
        assert!(n.count_ones() == 1);
        assert!(n <= T::order());
        let len = n.trailing_zeros() as usize;
        let mut zeta = Vec::with_capacity(len);
        let mut r = ModInt::new_unchecked(T::zeta()).pow((T::order() >> len) as u64);
        for _ in 0..len {
            zeta.push(r);
            r = r * r;
        }
        let mut q = Vec::with_capacity(len / 2);
        for (k, &z) in zeta.iter().rev().enumerate().rev() {
            let m = 1 << k;
            q.clear();
            let mut r = ModInt::one();
            for _ in 0..m {
                q.push(r);
                r *= z;
            }
            for f in f.chunks_exact_mut(2 * m) {
                let (x, y) = f.split_at_mut(m);
                for ((x, y), q) in x.iter_mut().zip(y.iter_mut()).zip(q.iter()) {
                    let a = *x;
                    let b = *y;
                    *x = a + b;
                    *y = (a - b) * *q;
                }
            }
        }
    }
    pub fn intt<T: NTTFriendly>(f: &mut [ModInt<T>]) {
        let n = f.len();
        assert!(n.count_ones() == 1);
        assert!(n <= T::order());
        let len = n.trailing_zeros() as usize;
        let mut zeta = Vec::with_capacity(len);
        let mut r = ModInt::new_unchecked(T::zeta()).inv().pow((T::order() >> len) as u64);
        for _ in 0..len {
            zeta.push(r);
            r = r * r;
        }
        let mut q = Vec::with_capacity(len / 2);
        for (k, &z) in zeta.iter().rev().enumerate() {
            let m = 1 << k;
            q.clear();
            let mut r = ModInt::one();
            for _ in 0..m {
                q.push(r);
                r *= z;
            }
            for f in f.chunks_exact_mut(2 * m) {
                let (x, y) = f.split_at_mut(m);
                for ((x, y), q) in x.iter_mut().zip(y.iter_mut()).zip(q.iter()) {
                    let a = *x;
                    let b = *y * *q;
                    *x = a + b;
                    *y = a - b;
                }
            }
        }
        let ik = ModInt::new_unchecked((T::modulo() + 1) >> 1).pow(len as u64);
        for f in f.iter_mut() {
            *f *= ik;
        }
    }
    pub fn multiply<T: NTTFriendly>(a: &[ModInt<T>], b: &[ModInt<T>]) -> Vec<ModInt<T>> {
        if a.is_empty() || b.is_empty() {
            return vec![];
        }
        let n = a.len() + b.len() - 1;
        let k = n.next_power_of_two();
        assert!(k <= T::order());
        let mut f = Vec::with_capacity(k);
        let mut g = Vec::with_capacity(k);
        f.extend_from_slice(a);
        f.resize(k, ModInt::zero());
        ntt(&mut f);
        g.extend_from_slice(b);
        g.resize(k, ModInt::zero());
        ntt(&mut g);
        for (f, g) in f.iter_mut().zip(g.iter()) {
            *f *= *g;
        }
        intt(&mut f);
        f.truncate(n);
        f
    }
}
// ---------- end NTT ----------

use modint::*;

type M = ModInt<Mod998_244_353>;

impl transform::NTTFriendly for Mod998_244_353 {
    fn order() -> usize {
        8388608
    }
    fn zeta() -> u32 {
        15311432
    }
}

// ---------- begin input macro ----------
// reference: https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8
macro_rules! input {
    (source = $s:expr, $($r:tt)*) => {
        let mut iter = $s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
    ($($r:tt)*) => {
        let s = {
            use std::io::Read;
            let mut s = String::new();
            std::io::stdin().read_to_string(&mut s).unwrap();
            s
        };
        let mut iter = s.split_whitespace();
        input_inner!{iter, $($r)*}
    };
}

macro_rules! input_inner {
    ($iter:expr) => {};
    ($iter:expr, ) => {};
    ($iter:expr, $var:ident : $t:tt $($r:tt)*) => {
        let $var = read_value!($iter, $t);
        input_inner!{$iter $($r)*}
    };
}

macro_rules! read_value {
    ($iter:expr, ( $($t:tt),* )) => {
        ( $(read_value!($iter, $t)),* )
    };
    ($iter:expr, [ $t:tt ; $len:expr ]) => {
        (0..$len).map(|_| read_value!($iter, $t)).collect::<Vec<_>>()
    };
    ($iter:expr, chars) => {
        read_value!($iter, String).chars().collect::<Vec<char>>()
    };
    ($iter:expr, bytes) => {
        read_value!($iter, String).bytes().collect::<Vec<u8>>()
    };
    ($iter:expr, usize1) => {
        read_value!($iter, usize) - 1
    };
    ($iter:expr, $t:ty) => {
        $iter.next().unwrap().parse::<$t>().expect("Parse error")
    };
}
// ---------- end input macro ----------

fn run() {
    input! {
        n: usize,
    }
    let mut dp = vec![M::zero(); n + 1];
    for i in 1..=n {
        for j in 1..=(n / i) {
            dp[i * j] += M::one();
        }
    }
    let mut sum = M::zero();
    for i in 1..=n {
        dp[i] = dp[i] + sum;
        sum += dp[i];
    }
    println!("{}", dp[n]);
}

fn main() {
    run();
}


#[allow(unused_imports)]
use std::cmp::*;
#[allow(unused_imports)]
use std::collections::*;
use std::io::*;
#[allow(dead_code)]
fn getline() -> String {
    let mut ret = String::new();
    std::io::stdin().read_line(&mut ret).ok();
    return ret;
}
fn get_word() -> String {
    let mut stdin = std::io::stdin();
    let mut u8b: [u8; 1] = [0];
    loop {
        let mut buf: Vec<u8> = Vec::with_capacity(16);
        loop {
            let res = stdin.read(&mut u8b);
            if res.is_err() || res.ok().unwrap() == 0 || u8b[0] <= ' ' as u8 {
                break;
            } else {
                buf.push(u8b[0]);
            }
        }
        if buf.len() >= 1 {
            let ret = std::string::String::from_utf8(buf).unwrap();
            return ret;
        }
    }
}
fn parse<T: std::str::FromStr>(s: &str) -> T { s.parse::<T>().ok().unwrap() }

#[allow(dead_code)]
fn get<T: std::str::FromStr>() -> T { parse(&get_word()) }

fn main() {
    let n: i64 = get();
    let m: i64 = get();
    let nn = n as usize;
    let mm = m as usize;
    let k: i64 = get();
    let x: usize = get::<usize>() - 1;
    let y: usize = get::<usize>() - 1;
    let mut tbl = vec![vec![0; mm]; nn];
    let div = if n == 1 { m } else { 2 * (n - 1) * m };
    let ascension = k / div;
    let rem = k % div;
    for i in 0 .. nn {
        for j in 0 .. mm {
            tbl[i][j] = ascension * if i == 0 || i == nn - 1 { 1 } else { 2 };
        }
    }
    for i in 0 .. (rem as usize) {
        let p = i / mm;
        let q = i % mm;
        tbl[if p >= nn { 2 * nn - 2 - p } else { p }][q] += 1;
    }
    let mut ma = 0;
    let mut mi = tbl[0][0];
    for i in 0 .. nn {
        ma = max(ma, *tbl[i].iter().max().unwrap());
        mi = min(mi, *tbl[i].iter().min().unwrap());
    }
    println!("{} {} {}", ma, mi, tbl[x][y]);
}


use std::collections::HashMap;
use std::io::stdin;

fn read_int() -> i32 {
    let mut line = String::new();
    stdin().read_line(&mut line).unwrap();
    return line.trim().parse().unwrap();
}

fn read_ints() -> Vec<i32> {
    let mut line = String::new();
    stdin().read_line(&mut line).unwrap();
    return line
        .trim()
        .split(" ")
        .map(|x| x.parse::<i32>().unwrap())
        .collect();
}

fn uniq_nums(v: Vec<i32>) -> Vec<i32> {
    let mut dict = HashMap::new();
    for num in v {
        dict.insert(num, true);
    }

    let mut uniqs = Vec::<i32>::new();
    for key in dict.keys() {
        uniqs.push(*key);
    }
    uniqs.sort();
    return uniqs;
}

fn case_2(v: Vec<i32>) {
    assert_eq!(v.len(), 2);

    let mid = (v[0] + v[1]) / 2;
    if mid - v[0] == v[1] - mid {
        println!("{}", mid - v[0]);
    } else {
        println!("{}", v[1] - v[0]);
    }
}

fn case_3(v: Vec<i32>) {
    assert_eq!(v.len(), 3);
    if v[2] - v[1] == v[1] - v[0] {
        println!("{}", v[2] - v[1]);
    } else {
        println!("-1")
    }
}

fn main() {
    let n = read_int();
    let nums = read_ints();
    let mut uniqs = uniq_nums(nums);

    match uniqs.len() {
        1 => println!("0"),
        2 => case_2(uniqs),
        3 => case_3(uniqs),
        _ => println!("-1"),
    }
}


use std::io;
use std::cmp;

fn main() {
    let _ = read_one::<usize>();
    let mut an = read::<isize>();
    an.sort();
    an.dedup();

    if an.len() > 3 { println!("-1"); return; }
    if an.len() == 1 { println!("{}", 0); return; }
    if an.len() == 2 {
        if (an[1] - an[0]) % 2 == 0 {
            println!("{}", cmp::min((an[1] - an[0]) / 2, an[1] - an[0]));
        } else {
            println!("{}", an[1] - an[0]);
        }
        return;
    }
    if an.len() == 3 {
        if an[2] - an[1] == an[1] - an[0] {
            println!("{}", an[1] - an[0]);
        } else {
            println!("-1");
        }
    }
}

#[allow(dead_code)]
fn read<T>() -> Vec<T>
where T:
std::str::FromStr,
T::Err: std::fmt::Debug {

    let mut buf = String::new();
    io::stdin().read_line(&mut buf).unwrap();
    buf.split_whitespace()
        .map(|s| s.trim().parse().unwrap())
        .collect()
}

#[allow(dead_code)]
fn read_one<T>() -> T
where T:
std::str::FromStr,
T::Err: std::fmt::Debug {

    let mut buf = String::new();
    io::stdin().read_line(&mut buf).unwrap();
    buf.trim().parse().unwrap()
}


/**
 * https://codeforces.com/contest/1154/problem/B
 */
macro_rules! parse_line {
    ($($t: ty),+) => ({
        let mut a_str = String::new();
        std::io::stdin().read_line(&mut a_str).expect("read error");
        let mut a_iter = a_str.split_whitespace();
        (
            $(
            a_iter.next().unwrap().parse::<$t>().expect("parse error"),
            )+
        )
    })
}

pub fn main() {
    let _ = parse_line!(i32);
    let mut buffer = String::new();
    std::io::stdin().read_line(&mut buffer).expect("n not esist");
    let mut nums: Vec<i32> = buffer.split_whitespace().map(|x| x.parse().expect("error")).collect();
    nums.sort();
    nums.dedup();
    let res = match nums.len() {
        1 => 0,
        2 => {
            let d = nums[1] - nums[0];
            if d % 2 == 0 {
                d / 2
            } else {
                d
            }
        }
        3 => {
            if nums[2] - nums[1] == nums[1] - nums[0] {
                nums[2] - nums[1]
            } else {
                -1
            }
        }
        _ => -1,
    };
    println!("{}", res);
}