// عَجَبًا لأَمْرِ المُؤْمِنِ، إنَّ أمْرَهُ كُلَّهُ خَيْرٌ
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
const char el = '\n';
const int N = 646646, M = 998244353, DIGITS = 12;
// fast power
ll fp(ll b, ll p) {
if (!p) return 1;
ll res = fp(b, p >> 1);
return res * res % M * (p & 1 ? b % M : 1) % M;
}
// mp1[x] = the x-th frequency permutation
// mp2[x] = the order of the frequency permutation that is x in base-10
int mp1[N], mp2[1 << (10 + DIGITS)];
// converts the frequency permutation into one base-10 compressed number
int val(vector<int> &f) {
int x = DIGITS, res = 0;
for (int &i: f) {
res <<= i + 1;
res |= (1 << i) - 1;
x -= i;
}
res = (res << (x + 1)) | ((1 << x) - 1);
return res;
}
// converts back the base-10 compression into frequency permutation
vector<int> freq(int c) {
vector<int> f(10);
if (!c) return f;
int x = 0;
for (int i = 10 + DIGITS - 1; i >= 0; i--) {
if ((c >> i & 1) == 0) {
x++;
} else if (x < 10) f[x]++;
}
return f;
}
// factorial, inverse factorial, ones[i] = 11...1 i times
ll fact[15], ones[15], inv[15];
// dp definitions
string n;
ll dp[DIGITS + 1][N][2];
int vis[DIGITS + 1][N][2], id;
// dp calculation
ll f(int i, int c, bool ls) {
auto &ans = dp[i][c][ls];
auto &vid = vis[i][c][ls];
if ((ls && vid) || vid == id) return ans;
vid = id, ans = 0;
// get the frequency from the base-10 compression
auto ff = freq(mp1[c]);
// base case calculation
if (i == DIGITS) {
int cnt = 0;
for (int x = 0; x < 10; x++)
ans += x * ff[x], cnt += ff[x];
ans = ans % M * ones[cnt] % M * fact[cnt - 1] % M;
for (auto &x: ff)
ans = ans * inv[x] % M;
return ans;
}
// dp transitions
for (int x = 0; x <= (ls ? 9 : n[i] - '0'); x++) {
// updating frequency
if (c || x) ff[x]++;
// compressing frequency
int cc = mp2[val(ff)];
// doing transition
ans = (ans + f(i + 1, cc, ls || x < n[i] - '0')) % M;
// undoing frequency update
if (c || x) ff[x]--;
}
return ans;
}
// helper function to know the needed N for this DIGITS, e.g. 10^12 -> 646646
int getN() {
int c = 0;
string p = string(10, '0') + string(DIGITS, '1');
while (next_permutation(p.begin(), p.end())) c++;
return c;
}
signed main() {
cin.tie(0)->sync_with_stdio(0);
#if Mosaab
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
// return cout << getN(), 0;
// filling factorial and inverses
fact[0] = 1;
for (int i = 1; i < 15; i++) {
fact[i] = fact[i - 1] * i % M;
ones[i] = (ones[i - 1] * 10 + 1) % M;
}
inv[14] = fp(fact[14], M - 2);
for (int i = 14; i > 0; i--) {
inv[i - 1] = inv[i] * i % M;
}
// maping all possible frequencies into compressed base-10 form and vice versa
string p = string(10, '0') + string(DIGITS, '1');
for (int i = 0; i < N; i++) {
int x = bitset<DIGITS + 10>(p).to_ulong();
mp1[i] = x, mp2[x] = i;
next_permutation(p.begin(), p.end());
}
// processing test cases in terms of increasing l,r value
int t;
cin >> t;
vector<pair<ll, ll> > a(t);
map<ll, ll> ans;
for (auto &[l,r]: a) {
cin >> l >> r;
ans[r], ans[l - 1];
}
for (auto &[i,j]: ans) {
n = to_string(i);
if (n.size() < DIGITS)
n = string(DIGITS - n.size(), '0') + n;
id++, j = f(0, 0, 0);
}
for (auto &[l,r]: a) {
cout << (ans[r] - ans[l - 1] + M) % M << el;
}
}
Ly8g2LnZjtis2Y7YqNmL2Kcg2YTYo9mO2YXZktix2ZAg2KfZhNmF2Y/YpNmS2YXZkNmG2ZDYjCDYpdmG2ZHZjiDYo9mF2ZLYsdmO2YfZjyDZg9mP2YTZkdmO2YfZjyDYrtmO2YrZktix2YwKI2luY2x1ZGUgPGJpdHMvc3RkYysrLmg+CnVzaW5nIG5hbWVzcGFjZSBzdGQ7CnVzaW5nIGxsID0gbG9uZyBsb25nOwp1c2luZyBsZCA9IGxvbmcgZG91YmxlOwpjb25zdCBjaGFyIGVsID0gJ1xuJzsKIApjb25zdCBpbnQgTiA9IDY0NjY0NiwgTSA9IDk5ODI0NDM1MywgRElHSVRTID0gMTI7CiAKLy8gZmFzdCBwb3dlcgpsbCBmcChsbCBiLCBsbCBwKSB7CglpZiAoIXApIHJldHVybiAxOwoJbGwgcmVzID0gZnAoYiwgcCA+PiAxKTsKCXJldHVybiByZXMgKiByZXMgJSBNICogKHAgJiAxID8gYiAlIE0gOiAxKSAlIE07Cn0KIAovLyBtcDFbeF0gPSB0aGUgeC10aCBmcmVxdWVuY3kgcGVybXV0YXRpb24KLy8gbXAyW3hdID0gdGhlIG9yZGVyIG9mIHRoZSBmcmVxdWVuY3kgcGVybXV0YXRpb24gdGhhdCBpcyB4IGluIGJhc2UtMTAKaW50IG1wMVtOXSwgbXAyWzEgPDwgKDEwICsgRElHSVRTKV07CiAKLy8gY29udmVydHMgdGhlIGZyZXF1ZW5jeSBwZXJtdXRhdGlvbiBpbnRvIG9uZSBiYXNlLTEwIGNvbXByZXNzZWQgbnVtYmVyCmludCB2YWwodmVjdG9yPGludD4gJmYpIHsKCWludCB4ID0gRElHSVRTLCByZXMgPSAwOwoJZm9yIChpbnQgJmk6IGYpIHsKCQlyZXMgPDw9IGkgKyAxOwoJCXJlcyB8PSAoMSA8PCBpKSAtIDE7CgkJeCAtPSBpOwoJfQoJcmVzID0gKHJlcyA8PCAoeCArIDEpKSB8ICgoMSA8PCB4KSAtIDEpOwoJcmV0dXJuIHJlczsKfQoKLy8gY29udmVydHMgYmFjayB0aGUgYmFzZS0xMCBjb21wcmVzc2lvbiBpbnRvIGZyZXF1ZW5jeSBwZXJtdXRhdGlvbgp2ZWN0b3I8aW50PiBmcmVxKGludCBjKSB7Cgl2ZWN0b3I8aW50PiBmKDEwKTsKCWlmICghYykgcmV0dXJuIGY7CglpbnQgeCA9IDA7Cglmb3IgKGludCBpID0gMTAgKyBESUdJVFMgLSAxOyBpID49IDA7IGktLSkgewoJCWlmICgoYyA+PiBpICYgMSkgPT0gMCkgewoJCQl4Kys7CgkJfSBlbHNlIGlmICh4IDwgMTApIGZbeF0rKzsKCX0KCXJldHVybiBmOwp9CiAKLy8gZmFjdG9yaWFsLCBpbnZlcnNlIGZhY3RvcmlhbCwgb25lc1tpXSA9IDExLi4uMSBpIHRpbWVzCmxsIGZhY3RbMTVdLCBvbmVzWzE1XSwgaW52WzE1XTsKLy8gZHAgZGVmaW5pdGlvbnMKc3RyaW5nIG47CmxsIGRwW0RJR0lUUyArIDFdW05dWzJdOwppbnQgdmlzW0RJR0lUUyArIDFdW05dWzJdLCBpZDsKIAovLyBkcCBjYWxjdWxhdGlvbgpsbCBmKGludCBpLCBpbnQgYywgYm9vbCBscykgewoJYXV0byAmYW5zID0gZHBbaV1bY11bbHNdOwoJYXV0byAmdmlkID0gdmlzW2ldW2NdW2xzXTsKCWlmICgobHMgJiYgdmlkKSB8fCB2aWQgPT0gaWQpIHJldHVybiBhbnM7Cgl2aWQgPSBpZCwgYW5zID0gMDsKCgkvLyBnZXQgdGhlIGZyZXF1ZW5jeSBmcm9tIHRoZSBiYXNlLTEwIGNvbXByZXNzaW9uCglhdXRvIGZmID0gZnJlcShtcDFbY10pOwoKCS8vIGJhc2UgY2FzZSBjYWxjdWxhdGlvbgoJaWYgKGkgPT0gRElHSVRTKSB7CgkJaW50IGNudCA9IDA7CgkJZm9yIChpbnQgeCA9IDA7IHggPCAxMDsgeCsrKQoJCQlhbnMgKz0geCAqIGZmW3hdLCBjbnQgKz0gZmZbeF07CgkJYW5zID0gYW5zICUgTSAqIG9uZXNbY250XSAlIE0gKiBmYWN0W2NudCAtIDFdICUgTTsKCQlmb3IgKGF1dG8gJng6IGZmKQoJCQlhbnMgPSBhbnMgKiBpbnZbeF0gJSBNOwoJCXJldHVybiBhbnM7Cgl9CgoJLy8gZHAgdHJhbnNpdGlvbnMKCWZvciAoaW50IHggPSAwOyB4IDw9IChscyA/IDkgOiBuW2ldIC0gJzAnKTsgeCsrKSB7CgkJLy8gdXBkYXRpbmcgZnJlcXVlbmN5CgkJaWYgKGMgfHwgeCkgZmZbeF0rKzsKCQkvLyBjb21wcmVzc2luZyBmcmVxdWVuY3kKCQlpbnQgY2MgPSBtcDJbdmFsKGZmKV07CgkJLy8gZG9pbmcgdHJhbnNpdGlvbgoJCWFucyA9IChhbnMgKyBmKGkgKyAxLCBjYywgbHMgfHwgeCA8IG5baV0gLSAnMCcpKSAlIE07CgkJLy8gdW5kb2luZyBmcmVxdWVuY3kgdXBkYXRlCgkJaWYgKGMgfHwgeCkgZmZbeF0tLTsKCX0KCXJldHVybiBhbnM7Cn0KIAovLyBoZWxwZXIgZnVuY3Rpb24gdG8ga25vdyB0aGUgbmVlZGVkIE4gZm9yIHRoaXMgRElHSVRTLCBlLmcuIDEwXjEyIC0+IDY0NjY0NgppbnQgZ2V0TigpIHsKCWludCBjID0gMDsKCXN0cmluZyBwID0gc3RyaW5nKDEwLCAnMCcpICsgc3RyaW5nKERJR0lUUywgJzEnKTsKCXdoaWxlIChuZXh0X3Blcm11dGF0aW9uKHAuYmVnaW4oKSwgcC5lbmQoKSkpIGMrKzsKCXJldHVybiBjOwp9CiAKc2lnbmVkIG1haW4oKSB7CgljaW4udGllKDApLT5zeW5jX3dpdGhfc3RkaW8oMCk7CiNpZiBNb3NhYWIKCWZyZW9wZW4oImlucHV0LnR4dCIsICJyIiwgc3RkaW4pOwoJZnJlb3Blbigib3V0cHV0LnR4dCIsICJ3Iiwgc3Rkb3V0KTsKI2VuZGlmCiAKCS8vIHJldHVybiBjb3V0IDw8IGdldE4oKSwgMDsKIAoJLy8gZmlsbGluZyBmYWN0b3JpYWwgYW5kIGludmVyc2VzCglmYWN0WzBdID0gMTsKCWZvciAoaW50IGkgPSAxOyBpIDwgMTU7IGkrKykgewoJCWZhY3RbaV0gPSBmYWN0W2kgLSAxXSAqIGkgJSBNOwoJCW9uZXNbaV0gPSAob25lc1tpIC0gMV0gKiAxMCArIDEpICUgTTsKCX0KCWludlsxNF0gPSBmcChmYWN0WzE0XSwgTSAtIDIpOwoJZm9yIChpbnQgaSA9IDE0OyBpID4gMDsgaS0tKSB7CgkJaW52W2kgLSAxXSA9IGludltpXSAqIGkgJSBNOwoJfQogCgkvLyBtYXBpbmcgYWxsIHBvc3NpYmxlIGZyZXF1ZW5jaWVzIGludG8gY29tcHJlc3NlZCBiYXNlLTEwIGZvcm0gYW5kIHZpY2UgdmVyc2EKCXN0cmluZyBwID0gc3RyaW5nKDEwLCAnMCcpICsgc3RyaW5nKERJR0lUUywgJzEnKTsKCWZvciAoaW50IGkgPSAwOyBpIDwgTjsgaSsrKSB7CgkJaW50IHggPSBiaXRzZXQ8RElHSVRTICsgMTA+KHApLnRvX3Vsb25nKCk7CgkJbXAxW2ldID0geCwgbXAyW3hdID0gaTsKCQluZXh0X3Blcm11dGF0aW9uKHAuYmVnaW4oKSwgcC5lbmQoKSk7Cgl9CgkKCS8vIHByb2Nlc3NpbmcgdGVzdCBjYXNlcyBpbiB0ZXJtcyBvZiBpbmNyZWFzaW5nIGwsciB2YWx1ZQoJaW50IHQ7CgljaW4gPj4gdDsKCXZlY3RvcjxwYWlyPGxsLCBsbD4gPiBhKHQpOwoJbWFwPGxsLCBsbD4gYW5zOwoJZm9yIChhdXRvICZbbCxyXTogYSkgewoJCWNpbiA+PiBsID4+IHI7CgkJYW5zW3JdLCBhbnNbbCAtIDFdOwoJfQogCglmb3IgKGF1dG8gJltpLGpdOiBhbnMpIHsKCQluID0gdG9fc3RyaW5nKGkpOwoJCWlmIChuLnNpemUoKSA8IERJR0lUUykKCQkJbiA9IHN0cmluZyhESUdJVFMgLSBuLnNpemUoKSwgJzAnKSArIG47CgkJaWQrKywgaiA9IGYoMCwgMCwgMCk7Cgl9CiAKCWZvciAoYXV0byAmW2wscl06IGEpIHsKCQljb3V0IDw8IChhbnNbcl0gLSBhbnNbbCAtIDFdICsgTSkgJSBNIDw8IGVsOwoJfQp9Cg==