#line 1 "verify/number-theory/LC_sum_of_totient_function.test.cpp"
#define PROBLEM "https://judge.yosupo.jp/problem/sum_of_totient_function"
#line 2 "template/template.hpp"
#include <bits/stdc++.h>
using namespace std ;
#line 2 "template/macro.hpp"
#define rep(i, a, b) for (int i = (a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b) - 1; i >= (a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())
#define YN(b) cout << ((b) ? "YES" : "NO") << "\n";
#define Yn(b) cout << ((b) ? "Yes" : "No") << "\n";
#define yn(b) cout << ((b) ? "yes" : "no") << "\n";
#line 6 "template/template.hpp"
#line 2 "template/util.hpp"
using uint = unsigned int ;
using ll = long long int ;
using ull = unsigned long long ;
using i128 = __int128_t ;
using u128 = __uint128_t ;
template < class T >
using priority_queue_asc = priority_queue < T , vector < T > , greater < T >> ;
template < class T , class S = T >
S SUM ( const vector < T >& a ) {
return accumulate ( ALL ( a ), S ( 0 ));
}
template < class T1 , class T2 >
inline bool chmin ( T1 & a , T2 b ) {
if ( a > b ) {
a = b ;
return true ;
}
return false ;
}
template < class T1 , class T2 >
inline bool chmax ( T1 & a , T2 b ) {
if ( a < b ) {
a = b ;
return true ;
}
return false ;
}
template < class T1 , class T2 >
inline bool chmin_opt ( optional < T1 >& a , T2 b ) {
if ( ! a || a > b ) {
a = b ;
return true ;
}
return false ;
}
template < class T1 , class T2 >
inline bool chmax_opt ( optional < T1 >& a , T2 b ) {
if ( ! a || a < b ) {
a = b ;
return true ;
}
return false ;
}
template < class T >
int popcnt ( T x ) {
return __builtin_popcountll ( x );
}
template < class T >
int topbit ( T x ) {
return ( x == 0 ? - 1 : 63 - __builtin_clzll ( x ));
}
template < class T >
int lowbit ( T x ) {
return ( x == 0 ? - 1 : __builtin_ctzll ( x ));
}
#line 8 "template/template.hpp"
#line 2 "template/inout.hpp"
struct Fast {
Fast () {
cin . tie ( nullptr );
ios_base :: sync_with_stdio ( false );
cout << fixed << setprecision ( 15 );
}
} fast ;
ostream & operator << ( ostream & os , __uint128_t x ) {
char buf [ 40 ];
size_t k = 0 ;
while ( x > 0 ) buf [ k ++ ] = ( char )( x % 10 + '0' ), x /= 10 ;
if ( k == 0 ) buf [ k ++ ] = '0' ;
while ( k ) os << buf [ -- k ];
return os ;
}
ostream & operator << ( ostream & os , __int128_t x ) {
return x < 0 ? ( os << '-' << ( __uint128_t )( - x )) : ( os << ( __uint128_t ) x );
}
template < class T , size_t N >
ostream & operator << ( ostream & os , const array < T , N >& a );
template < class T1 , class T2 >
istream & operator >> ( istream & is , pair < T1 , T2 >& p ) {
return is >> p . first >> p . second ;
}
template < class T1 , class T2 >
ostream & operator << ( ostream & os , const pair < T1 , T2 >& p ) {
return os << p . first << " " << p . second ;
}
template < class T >
istream & operator >> ( istream & is , vector < T >& a ) {
for ( auto & v : a ) is >> v ;
return is ;
}
template < class T >
ostream & operator << ( ostream & os , const vector < T >& a ) {
for ( auto it = a . begin (); it != a . end ();) {
os << * it ;
if ( ++ it != a . end ()) os << " " ;
}
return os ;
}
template < class T , size_t N >
ostream & operator << ( ostream & os , const array < T , N >& a ) {
for ( auto it = a . begin (); it != a . end ();) {
os << * it ;
if ( ++ it != a . end ()) os << " " ;
}
return os ;
}
template < class T >
ostream & operator << ( ostream & os , const set < T >& st ) {
os << "{" ;
for ( auto it = st . begin (); it != st . end ();) {
os << * it ;
if ( ++ it != st . end ()) os << "," ;
}
os << "}" ;
return os ;
}
template < class T1 , class T2 >
ostream & operator << ( ostream & os , const map < T1 , T2 >& mp ) {
os << "{" ;
for ( auto it = mp . begin (); it != mp . end ();) {
os << it -> first << ":" << it -> second ;
if ( ++ it != mp . end ()) os << "," ;
}
os << "}" ;
return os ;
}
void in () {}
template < typename T , class ... U >
void in ( T & t , U & ... u ) {
cin >> t ;
in ( u ...);
}
template < class ... T >
void in_zip ( int n , T & ... t ) {
assert ( n >= 0 && (( size ( t ) >= static_cast < size_t > ( n )) && ...));
for ( int i = 0 ; i < n ; i ++ ) in ( t [ i ]...);
}
void out () { cout << " \n " ; }
template < typename T , class ... U , char sep = ' ' >
void out ( const T & t , const U & ... u ) {
cout << t ;
if ( sizeof ...( u )) cout << sep ;
out ( u ...);
}
template < class T , class U >
void out_opt ( const optional < T >& opt , const U & fallback , ostream & os = cout ) {
if ( opt . has_value ())
os << opt . value ();
else
os << fallback ;
os << " \n " ;
}
template < class T , class U >
void out_opt ( const vector < optional < T >>& vec , const U & fallback , ostream & os = cout ) {
for ( auto it = vec . begin (); it != vec . end ();) {
if (( * it ). has_value ())
os << ( * it ). value ();
else
os << fallback ;
if ( ++ it != vec . end ()) os << " " ;
}
os << " \n " ;
}
namespace IO {
template < class T , class ... U >
T read ( U && ... u ) {
T t = T ( forward < U > ( u )...);
in ( t );
return t ;
}
namespace Graph {
vector < vector < int >> unweighted ( int n , int m , bool directed = false , int offset = 1 ) {
vector < vector < int >> g ( n );
for ( int i = 0 ; i < m ; i ++ ) {
int u , v ;
cin >> u >> v ;
u -= offset , v -= offset ;
g [ u ]. push_back ( v );
if ( ! directed ) g [ v ]. push_back ( u );
}
return g ;
}
template < class T >
vector < vector < pair < int , T >>> weighted ( int n , int m , bool directed = false , int offset = 1 ) {
vector < vector < pair < int , T >>> g ( n );
for ( int i = 0 ; i < m ; i ++ ) {
int u , v ;
T w ;
cin >> u >> v >> w ;
u -= offset , v -= offset ;
g [ u ]. push_back ({ v , w });
if ( ! directed ) g [ v ]. push_back ({ u , w });
}
return g ;
}
} // namespace Graph
namespace Tree {
vector < vector < int >> unweighted ( int n , bool directed = false , int offset = 1 ) {
return Graph :: unweighted ( n , n - 1 , directed , offset );
}
template < class T >
vector < vector < pair < int , T >>> weighted ( int n , bool directed = false , int offset = 1 ) {
return Graph :: weighted < T > ( n , n - 1 , directed , offset );
}
vector < vector < int >> rooted ( int n , bool to_root = true , bool to_leaf = true , int offset = 1 ) {
vector < vector < int >> g ( n );
for ( int i = 1 ; i < n ; i ++ ) {
int p ;
cin >> p ;
p -= offset ;
if ( to_root ) g [ i ]. push_back ( p );
if ( to_leaf ) g [ p ]. push_back ( i );
}
return g ;
}
} // namespace Tree
} // namespace IO
#line 10 "template/template.hpp"
#line 2 "template/debug.hpp"
#ifdef LOCAL
#define debug 1
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define debug 0
#define show(...) true
#endif
template < class T >
void _show ( int , T ) {
cerr << '\n' ;
}
template < class T1 , class T2 , class ... T3 >
void _show ( int i , const T1 & a , const T2 & b , const T3 & ... c ) {
for (; a [ i ] != ',' && a [ i ] != '\0' ; i ++ ) cerr << a [ i ];
cerr << ":" << b << " " ;
_show ( i + 1 , a , c ...);
}
#line 2 "math/util.hpp"
namespace Math {
template < class T >
T safe_mod ( T a , T b ) {
assert ( b != 0 );
if ( b < 0 ) a = - a , b = - b ;
a %= b ;
return a >= 0 ? a : a + b ;
}
template < class T >
T floor ( T a , T b ) {
assert ( b != 0 );
if ( b < 0 ) a = - a , b = - b ;
return a >= 0 ? a / b : ( a + 1 ) / b - 1 ;
}
template < class T >
T ceil ( T a , T b ) {
assert ( b != 0 );
if ( b < 0 ) a = - a , b = - b ;
return a > 0 ? ( a - 1 ) / b + 1 : a / b ;
}
long long isqrt ( long long n ) {
if ( n <= 0 ) return 0 ;
long long x = sqrt ( n );
while (( __int128 )( x + 1 ) * ( x + 1 ) <= n ) x ++ ;
while (( __int128 ) x * x > n ) x -- ;
return x ;
}
long long floor_root ( long long n , int k ) {
assert ( n >= 0 );
if ( n == 0 ) return 0 ;
assert ( k >= 1 );
if ( k == 1 ) return n ;
if ( k > 64 ) return 1 ;
long long x = round ( pow (( long double ) n , 1.0 L / k ));
auto check = [ & ]( long long a ) {
if ( a <= 0 ) return true ;
__int128_t p = 1 ;
for ( int i = 0 ; i < k ; ++ i )
if (( p *= a ) > n ) return false ;
return true ;
};
while ( check ( x + 1 )) x ++ ;
while ( ! check ( x )) x -- ;
return x ;
}
unsigned long long floor_root_unsigned ( unsigned long long n , int k ) {
assert ( k >= 1 );
if ( n <= 1 || k == 1 ) return n ;
if ( k >= 64 ) return 1 ;
int bits = ( 64 + k - 1 ) / k ;
unsigned long long ok = 1 , ng = min ( n , 1ULL << bits );
auto check = [ & ]( unsigned long long a ) {
__uint128_t p = 1 ;
for ( int i = 0 ; i < k ; i ++ ) {
p *= a ;
if ( p > n ) return false ;
}
return true ;
};
while ( ok + 1 < ng ) {
unsigned long long mid = ok + ( ng - ok ) / 2 ;
( check ( mid ) ? ok : ng ) = mid ;
}
return ok ;
}
// return g=gcd(a,b)
// a*x+b*y=g
// - b!=0 -> 0<=x<|b|/g
// - b=0 -> ax=g
template < class T >
T ext_gcd ( T a , T b , T & x , T & y ) {
T a0 = a , b0 = b ;
bool sgn_a = a < 0 , sgn_b = b < 0 ;
if ( sgn_a ) a = - a ;
if ( sgn_b ) b = - b ;
if ( b == 0 ) {
x = sgn_a ? - 1 : 1 ;
y = 0 ;
return a ;
}
T x00 = 1 , x01 = 0 , x10 = 0 , x11 = 1 ;
while ( b != 0 ) {
T q = a / b , r = a - b * q ;
x00 -= q * x01 ;
x10 -= q * x11 ;
swap ( x00 , x01 );
swap ( x10 , x11 );
a = b , b = r ;
}
x = x00 , y = x10 ;
if ( sgn_a ) x = - x ;
if ( sgn_b ) y = - y ;
if ( b0 != 0 ) {
a0 /= a , b0 /= a ;
if ( b0 < 0 ) a0 = - a0 , b0 = - b0 ;
T q = x >= 0 ? x / b0 : ( x + 1 ) / b0 - 1 ;
x -= b0 * q ;
y += a0 * q ;
}
return a ;
}
constexpr long long inv_mod ( long long x , long long m ) {
x %= m ;
if ( x < 0 ) x += m ;
long long a = m , b = x ;
long long y0 = 0 , y1 = 1 ;
while ( b > 0 ) {
long long q = a / b ;
swap ( a -= q * b , b );
swap ( y0 -= q * y1 , y1 );
}
if ( y0 < 0 ) y0 += m / a ;
return y0 ;
}
long long pow_mod ( long long x , long long n , long long m ) {
if ( m == 1 ) return 0 ;
x = ( x % m + m ) % m ;
long long y = 1 ;
while ( n ) {
if ( n & 1 ) y = y * x % m ;
x = x * x % m ;
n >>= 1 ;
}
return y ;
}
constexpr long long pow_mod_constexpr ( long long x , long long n , int m ) {
if ( m == 1 ) return 0 ;
unsigned int _m = ( unsigned int )( m );
unsigned long long r = 1 ;
unsigned long long y = x % m ;
if ( y >= m ) y += m ;
while ( n ) {
if ( n & 1 ) r = ( r * y ) % _m ;
y = ( y * y ) % _m ;
n >>= 1 ;
}
return r ;
}
constexpr bool is_prime_constexpr ( int n ) {
if ( n <= 1 ) return false ;
if ( n == 2 || n == 7 || n == 61 ) return true ;
if ( n % 2 == 0 ) return false ;
long long d = n - 1 ;
while ( d % 2 == 0 ) d /= 2 ;
constexpr long long bases [ 3 ] = { 2 , 7 , 61 };
for ( long long a : bases ) {
long long t = d ;
long long y = pow_mod_constexpr ( a , t , n );
while ( t != n - 1 && y != 1 && y != n - 1 ) {
y = y * y % n ;
t <<= 1 ;
}
if ( y != n - 1 && t % 2 == 0 ) {
return false ;
}
}
return true ;
}
template < int n >
constexpr bool is_prime = is_prime_constexpr ( n );
}; // namespace Math
#line 3 "modint/modint.hpp"
template < unsigned int m = 998244353 >
struct ModInt {
using mint = ModInt ;
static constexpr unsigned int get_mod () { return m ; }
static mint raw ( int v ) {
mint x ;
x . _v = v ;
return x ;
}
ModInt () : _v ( 0 ) {}
ModInt ( int64_t v ) {
long long x = ( long long )( v % ( long long )( umod ()));
if ( x < 0 ) x += umod ();
_v = ( unsigned int )( x );
}
unsigned int val () const { return _v ; }
mint & operator ++ () {
_v ++ ;
if ( _v == umod ()) _v = 0 ;
return * this ;
}
mint & operator -- () {
if ( _v == 0 ) _v = umod ();
_v -- ;
return * this ;
}
mint operator ++ ( int ) {
mint result = * this ;
++* this ;
return result ;
}
mint operator -- ( int ) {
mint result = * this ;
--* this ;
return result ;
}
mint & operator += ( const mint & rhs ) {
_v += rhs . _v ;
if ( _v >= umod ()) _v -= umod ();
return * this ;
}
mint & operator -= ( const mint & rhs ) {
_v -= rhs . _v ;
if ( _v >= umod ()) _v += umod ();
return * this ;
}
mint & operator *= ( const mint & rhs ) {
unsigned long long z = _v ;
z *= rhs . _v ;
_v = ( unsigned int )( z % umod ());
return * this ;
}
mint & operator /= ( const mint & rhs ) { return * this *= rhs . inv (); }
mint operator + () const { return * this ; }
mint operator - () const { return mint () - * this ; }
mint pow ( long long n ) const {
assert ( 0 <= n );
mint x = * this , r = 1 ;
while ( n ) {
if ( n & 1 ) r *= x ;
x *= x ;
n >>= 1 ;
}
return r ;
}
mint inv () const {
if ( is_prime ) {
assert ( _v );
return pow ( umod () - 2 );
} else {
auto inv = Math :: inv_mod ( _v , umod ());
return raw ( inv );
}
}
friend mint operator + ( const mint & lhs , const mint & rhs ) { return mint ( lhs ) += rhs ; }
friend mint operator - ( const mint & lhs , const mint & rhs ) { return mint ( lhs ) -= rhs ; }
friend mint operator * ( const mint & lhs , const mint & rhs ) { return mint ( lhs ) *= rhs ; }
friend mint operator / ( const mint & lhs , const mint & rhs ) { return mint ( lhs ) /= rhs ; }
friend bool operator == ( const mint & lhs , const mint & rhs ) { return lhs . _v == rhs . _v ; }
friend bool operator != ( const mint & lhs , const mint & rhs ) { return lhs . _v != rhs . _v ; }
friend istream & operator >> ( istream & is , mint & x ) {
int64_t v ;
is >> v ;
x = mint ( v );
return is ;
}
friend ostream & operator << ( ostream & os , const mint & x ) { return os << x . val (); }
private:
unsigned int _v ;
static constexpr unsigned int umod () { return m ; }
static constexpr bool is_prime = Math :: is_prime < m > ;
};
using ModInt998244353 = ModInt < 998244353 > ;
using ModInt1000000007 = ModInt < 1000000007 > ;
#line 5 "verify/number-theory/LC_sum_of_totient_function.test.cpp"
using mint = ModInt < 998244353 > ;
#line 2 "number-theory/totient-function.hpp"
#line 2 "number-theory/prime-sieve.hpp"
namespace PrimeSieve {
using ll = long long ;
vector < int > lpf ( int n ) {
assert ( n >= 0 );
vector < int > ret ( n + 1 );
for ( size_t i = 0 ; i < ret . size (); i ++ ) ret [ i ] = ( int ) i ;
for ( int p = 2 ; ( ll ) p * p <= n ; p ++ ) {
if ( ret [ p ] != p ) continue ;
for ( ll x = ( ll ) p * p ;; x += p ) {
if ( ret [ x ] == x ) ret [ x ] = p ;
if ( n - x < p ) break ;
}
}
return ret ;
}
vector < int > table ( int n ) {
assert ( n >= 0 );
vector < bool > composite ( n + 1 , false );
for ( int p = 2 ; ( ll ) p * p <= n ; p += ( p & 1 ) + 1 ) {
if ( composite [ p ]) continue ;
for ( ll x = ( ll ) p * p ;; x += p ) {
composite [ x ] = true ;
if ( n - x < p ) break ;
}
}
vector < int > ps ;
for ( int p = 2 ; p <= n ;) {
if ( ! composite [ p ]) ps . push_back ( p );
int step = ( p & 1 ) + 1 ;
if ( n - p < step ) break ;
p += step ;
}
return ps ;
}
vector < vector < pair < ll , int >>> factorize ( int n ) {
assert ( n >= 0 );
vector < vector < pair < ll , int >>> factors ( n + 1 );
auto lp = lpf ( n );
for ( int x = 2 ; x <= n ;) {
int y = x ;
while ( y > 1 ) {
int p = lp [ y ], e = 0 ;
while ( y % p == 0 ) y /= p , e ++ ;
factors [ x ]. emplace_back ( p , e );
}
if ( x == n ) break ;
x ++ ;
}
return factors ;
}
}; // namespace PrimeSieve
/**
* @brief 素数篩
* @docs docs/number-theory/prime-sieve.md
*/
#line 4 "number-theory/totient-function.hpp"
namespace TotientFunction {
vector < int > table ( int n ) {
vector < int > tot ( n + 1 , 1 );
tot [ 0 ] = 0 ;
auto lpf = PrimeSieve :: lpf ( n );
for ( int x = 2 ; x <= n ; x ++ ) {
int p = lpf [ x ];
if ( x / p % p == 0 )
tot [ x ] = tot [ x / p ] * p ;
else
tot [ x ] = tot [ x / p ] * ( p - 1 );
}
return tot ;
}
template < class T >
T sum ( long long n ) {
const int lim = 2000000 ;
if ( n < lim ) {
vector < int > phi ( n + 1 );
iota ( phi . begin (), phi . end (), 0 );
vector < bool > f ( n + 1 , false );
for ( int i = 2 ; i <= n ; i += ( i & 1 ) + 1 )
if ( ! f [ i ])
for ( int j = i ; j <= n ; j += i ) {
f [ j ] = true ;
phi [ j ] -= phi [ j ] / i ;
}
T ans = 0 ;
for ( auto v : phi ) ans += v ;
return ans ;
} else {
vector < T > sum_small ( lim ), sum_large ( n / lim + 1 );
iota ( sum_small . begin (), sum_small . end (), 0 );
vector < bool > f ( lim , false );
for ( int i = 2 ; i < lim ; i += ( i & 1 ) + 1 )
if ( ! f [ i ])
for ( int j = i ; j < lim ; j += i ) {
f [ j ] = true ;
sum_small [ j ] -= sum_small [ j ] / i ;
}
for ( int i = 2 ; i < lim ; i ++ ) sum_small [ i ] += sum_small [ i - 1 ];
const int sz = sum_large . size ();
for ( int k = sz - 1 ; k > 0 ; k -- ) {
int m = 2 ;
long long cur = n / k ;
sum_large [ k ] = ( T )(( cur + 1 ) / 2 ) * ( cur | 1 );
for (; cur / m != cur / ( m + 1 ) && m <= cur ; m ++ )
sum_large [ k ] -= k * m >= sz ? sum_small [ cur / m ] : sum_large [ k * m ];
for ( int arg = ( int )( cur / m ); arg >= 1 ; arg -- )
sum_large [ k ] -= ( T )( cur / arg - cur / ( arg + 1 )) * ( arg < lim ? sum_small [ arg ] : sum_large [ n / arg ]);
}
return sum_large [ 1 ];
}
}
}; // namespace TotientFunction
/**
* @brief Totient Function
* @docs docs/number-theory/totient-function.md
*/
#line 7 "verify/number-theory/LC_sum_of_totient_function.test.cpp"
int main () {
ll n ;
in ( n );
out ( TotientFunction :: sum < mint > ( n ));
}