package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func power(base, exp int) int {
res := 1
base %= 998244353
for exp > 0 {
if exp%2 == 1 {
res = (res * base) % 998244353
}
base = (base * base) % 998244353
exp /= 2
}
return res
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanWords)
if !scanner.Scan() {
return
}
m, _ := strconv.Atoi(scanner.Text())
const P = 998244353
const P_1 = 998244352
const MAXA = 100005
cnt := make([]bool, MAXA)
cnt_exp := make([]int, MAXA)
cnt_mod := make([]int, MAXA)
sum1 := make([]int, MAXA)
sum2 := make([]int, MAXA)
for i := 0; i < m; i++ {
scanner.Scan()
a, _ := strconv.Atoi(scanner.Text())
scanner.Scan()
f, _ := strconv.Atoi(scanner.Text())
cnt[a] = true
cnt_exp[a] = f % P_1
cnt_mod[a] = f % P
sum1[a] = (cnt_mod[a] * a) % P
sum2[a] = (cnt_mod[a] * ((a * a) % P)) % P
}
mu := make([]int, MAXA)
primes := make([]int, 0)
isPrime := make([]bool, MAXA)
for i := 2; i < MAXA; i++ {
isPrime[i] = true
}
mu[1] = 1
for i := 2; i < MAXA; i++ {
if isPrime[i] {
primes = append(primes, i)
mu[i] = -1
}
for _, p := range primes {
if i*p >= MAXA {
break
}
isPrime[i*p] = false
if i%p == 0 {
mu[i*p] = 0
break
} else {
mu[i*p] = -mu[i]
}
}
}
ans := 0
for d := 1; d < MAXA; d++ {
if mu[d] == 0 {
continue
}
k_exp := 0
k_mod := 0
s1 := 0
s2 := 0
has_elements := false
for j := d; j < MAXA; j += d {
if cnt[j] {
has_elements = true
k_exp = (k_exp + cnt_exp[j]) % P_1
k_mod = (k_mod + cnt_mod[j]) % P
s1 = (s1 + sum1[j]) % P
s2 = (s2 + sum2[j]) % P
}
}
if !has_elements {
continue
}
E := (k_exp - 3) % P_1
if E < 0 {
E += P_1
}
inv2 := power(2, E)
term1 := (k_mod - 2 + P) % P
term1 = (term1 * s2) % P
term2 := (k_mod * s1) % P
term2 = (term2 * s1) % P
sum := (term1 + term2) % P
sum = (sum * inv2) % P
if mu[d] == -1 {
ans = (ans - sum + P) % P
} else if mu[d] == 1 {
ans = (ans + sum) % P
}
}
fmt.Println(ans)
}