#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/courses/library/6/NTL/1/NTL_2_F"
#include "../../cpp/template/template.cpp"
#include "../../cpp/data-structure/big-int.cpp"
int main(){
BigInt A,B;cin>>A>>B;
cout << A*B << endl;
}
#line 1 "cpp/z_test/aoj-NTL_2_F.test.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/courses/library/6/NTL/1/NTL_2_F"
#line 2 "cpp/template/template.cpp"
//yukicoder@cpp17
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <climits>
#include <cassert>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <map>
#include <random>
#include <bitset>
#include <complex>
#include <utility>
#include <numeric>
#include <functional>
using namespace std;
using ll = long long;
using P = pair<ll,ll>;
const ll MOD = 998244353;
const ll MODx = 1000000007;
const int INF = (1<<30)-1;
const ll LINF = (1LL<<62LL)-1;
const double EPS = (1e-10);
P ar4[4] = {{0,1},{0,-1},{1,0},{-1,0}};
P ar8[8] = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
template <typename T> vector<T> make_vector(size_t a, T b) { return vector<T>(a, b); }
template <typename... Ts> auto make_vector(size_t a, Ts... ts) { return vector<decltype(make_vector(ts...))>(a, make_vector(ts...)); }
/*
確認ポイント
cout << fixed << setprecision(n) << 小数計算//n桁の小数表記になる
計算量は変わらないが楽できるシリーズ
min(max)_element(iter,iter)で一番小さい(大きい)値のポインタが帰ってくる
count(iter,iter,int)でintがiterからiterの間にいくつあったかを取得できる
*/
/* comment outed because can cause bugs
__attribute__((constructor))
void initial() {
cin.tie(0);
ios::sync_with_stdio(false);
}
*/
#line 4 "cpp/z_test/aoj-NTL_2_F.test.cpp"
#line 1 "cpp/math/convolution.cpp"
// Ref: https://qiita.com/AngrySadEight/items/0dfde26060daaf6a2fda
#line 2 "cpp/math/binary-power-method.cpp"
template <typename T>
T uPow(T z,T n, T mod){
T ans = 1;
while(n != 0){
if(n%2){
ans*=z;
if(mod)ans%=mod;
}
n >>= 1;
z*=z;
if(mod)z%=mod;
}
return ans;
}
#line 2 "cpp/data-structure/mod-int/mod-int.cpp"
template <int mod>
struct ModInt{
int n;
ModInt():n(0){}
ModInt(long long n_):n(n_ >= 0 ? n_%mod : mod - ((-n_)%mod) ){}
ModInt(int n_):n(n_ >= 0 ? n_%mod : mod - ((-n_)%mod) ){}
ModInt &operator+=(const ModInt &p){
if((n+=p.n) >= mod)n-=mod;
return *this;
}
ModInt &operator-=(const ModInt &p){
n+=mod-p.n;
if(n >= mod)n-=mod;
return *this;
}
ModInt &operator*=(const ModInt &p){
n = (int) ((1LL*n*p.n)%mod);
return *this;
}
ModInt &operator/=(const ModInt &p){
*this *= p.inverse();
return *this;
}
ModInt operator-() const {return ModInt(-n);}
ModInt operator+(const ModInt &p) const {return ModInt(*this) += p;}
ModInt operator-(const ModInt &p) const {return ModInt(*this) -= p;}
ModInt operator*(const ModInt &p) const {return ModInt(*this) *= p;}
ModInt operator/(const ModInt &p) const {return ModInt(*this) /= p;}
bool operator==(const ModInt &p) const {return n==p.n;}
bool operator<(const ModInt &p) const {return n<p.n;}
bool operator>(const ModInt &p) const {return n>p.n;}
bool operator>=(const ModInt &p) const {return n>=p.n;}
bool operator<=(const ModInt &p) const {return n<=p.n;}
bool operator!=(const ModInt &p) const {return n!=p.n;}
ModInt inverse() const {
int a = n,b = mod,u = 1,v = 0;
while(b){
int t = a/b;
a -= t*b; swap(a,b);
u -= t*v; swap(u,v);
}
return ModInt(u);
}
ModInt pow(int64_t z) const {
ModInt ret(1),mul(n);
while(z > 0){
if(z & 1) ret *= mul;
mul *= mul;
z >>= 1;
}
return ret;
}
int getMod() const {
return mod;
}
friend ostream &operator<<(ostream &os, const ModInt &p){
return os << p.n;
}
friend istream &operator>>(istream &is, ModInt &a){
int64_t t;
is >> t;
a = ModInt<mod> ((long long)t);
return (is);
}
};
using mint = ModInt<MOD>;
#line 5 "cpp/math/convolution.cpp"
using namespace std;
template<typename MINT>
vector<MINT> ntt(vector<MINT> X, int depth, vector<MINT> root) {
long long n = X.size();
if(n == 1){
return X;
}else{
vector<MINT> val(0);
vector<MINT> even(0);
vector<MINT> odd(0);
for(int i = 0; n > i; i++){
if(i % 2 == 0)even.push_back(X[i]);
else odd.push_back(X[i]);
}
auto ntt_even = ntt(even, depth-1, root);
auto ntt_odd = ntt(odd, depth-1, root);
mint r = root[depth];
MINT now = 1;
for(int i = 0; n > i; i++){
val.push_back(ntt_even[i%(n/2)] + (now * ntt_odd[i%(n/2)]));
now *= r;
}
return val;
}
}
template<typename MINT> // 998244353 mod
vector<MINT> make_root(long long p){
vector<MINT> val(0);
mint r = uPow(3LL, 119LL, p);
for(int i = 0; 23 > i; i++){
val.push_back(r);
r *= r;
}
reverse(val.begin(), val.end());
return val;
}
template<typename MINT>
vector<MINT> make_invroot(vector<MINT> root){
vector<MINT> val(0);
for(int i = 0; 23 > i; i++){
val.push_back(root[i].inverse());
}
return val;
}
template<typename MINT>
vector<MINT> convolution(vector<MINT> A, vector<MINT> B){
long long p = A[0].getMod(); // each mod must be same
vector<MINT> root = make_root<MINT>(p);
vector<MINT> invroot = make_invroot<MINT>(root);
size_t size = (A.size()+B.size()-1);
int n = 1;
int log2_n = 0;
while(n < size){
n *= 2;
log2_n++;
}
while(A.size() < n)A.push_back(0);
while(B.size() < n)B.push_back(0);
// AとBのNTTを求める
auto nttA = ntt(A, log2_n-1, root);
auto nttB = ntt(B, log2_n-1, root);
vector<MINT> nttC(n);
for(int i = 0; n > i; i++){
nttC[i] = nttA[i]*nttB[i];
}
auto nC = ntt(nttC, log2_n-1, invroot);
vector<MINT> C(size);
for(int i = 0; size > i; i++){
C[i] = nC[i]/(mint)n;
}
return C;
}
#line 2 "cpp/data-structure/big-int.cpp"
struct BigInt{
string val;
BigInt():val("0"){}
BigInt(string s){
if(s == "0"){
val = "0";
return;
}
bool firstdigit = false;
for(int i = 0; s.size() > i; i++){
if('0' <= s[i] && s[i] <= '9'){
if(!firstdigit && s[i] == '0')continue;
firstdigit = true;
val.push_back(s[i]);
}else if(s[i] == '-' && val.empty()){
val.push_back(s[i]);
}else{
val = "0";
break;
}
}
}
BigInt(int n): val(to_string(n)){}
BigInt abs(BigInt p) const {
if(p.val[0] == '-'){
p.val = p.val.substr(1,p.val.size()-1);
}
return p;
}
int size(){
return (*this).val.length();
}
bool isZero(){
int i = 0;
if((*this).val[0] == '-')i = 1;
for(; (*this).size() > i; i++){
if((*this).val[i] != '0')return false;
}
return true;
}
BigInt SignTurn(BigInt p) const {
if(p.val[0] == '-'){
p.val = p.val.substr(1,p.val.size()-1);
}else{
p.val = "-" + p.val;
}
return p;
}
BigInt max(BigInt a,BigInt b) const {
if(a.val.size() > b.val.size()){
return a;
}else if(a.val.size() < b.val.size()){
return b;
}else{
for(int i = 0; a.val.size()> i; i++){
if(a.val[i] > b.val[i]){
return a;
}
if(a.val[i] < b.val[i]){
return b;
}
}
}
return a;
}
BigInt operator+(const BigInt &p) const {
BigInt z = *this;
BigInt k = p;
if(z.val[0] == '-' && p.val[0] == '-'){
return SignTurn(abs(z)+abs(k));//(-123)+(-124) -> -((123)+(124))
}else if(z.val[0] == '-'){
return k-abs(z);//(-123)+(124) -> (124)-(123)
}else if(p.val[0] == '-'){
return (z)-abs(k);//(123)+(-124) -> (123)-(124)
}else{
//123 + 1234
bool Mvup = false;
string ret = "";
if(z.val.size() < k.val.size()){
swap(z,k);
}
//val.size() > k.val.size();
for(int i = z.val.size()-1; 0 <= i; i--){
if(z.val.size()-k.val.size() > i){
//val[i]+Mvup
ret.push_back(((z.val[i]-'0'+(Mvup?1:0))%10)+'0');
Mvup = ((z.val[i]-'0'+(Mvup?1:0))>=10);
}else{
//val[i],k.val[i-(val.size()-k.val.size())]+Mvup
ret.push_back(((z.val[i]-'0'+k.val[i-(z.val.size()-k.val.size())]-'0'+(Mvup?1:0))%10)+'0');
Mvup = ((z.val[i]-'0'+k.val[i-(z.val.size()-k.val.size())]-'0'+(Mvup?1:0)) >= 10);
}
}
if(Mvup)ret.push_back('1');
reverse(ret.begin(),ret.end());
if(ret.empty())ret.push_back('0');
z.val = ret;
}
return z;
}
BigInt operator-(const BigInt &p) const {
BigInt z = *this;
BigInt k = p;
if(z.val[0] == '-' && k.val[0] == '-'){
return (z)+abs(k);//(-123)-(-123) -> (-123)+(123)
}else if(z.val[0] == '-'){
return (z)+SignTurn(k);//(-123)-(123) -> (-123)+(-123)
}else if(k.val[0] == '-'){
return abs(k)+(z);//(123)-(-123) -> (123)+(123)
}else{
string ret = "";
//z >= k
bool Mvdwn = false;
bool minus = false;
if(max((z),k) != z){
minus = true;
swap(z,k);
}
for(int i = z.val.size()-1; 0 <= i; i--){
if(z.val.size() - k.val.size() > i){
if(z.val[i]-'0' < (Mvdwn?1:0)){
ret.push_back(10+z.val[i]-(Mvdwn?1:0));
Mvdwn = true;
}else{
ret.push_back(z.val[i]-(Mvdwn?1:0));
Mvdwn = false;
}
}else{
if(z.val[i] < k.val[i-(z.val.size()-k.val.size())]+(Mvdwn?1:0)){
ret.push_back(10+z.val[i]-(k.val[i-(z.val.size()-k.val.size())]+(Mvdwn?1:0)) + '0');
Mvdwn = true;
}else{
ret.push_back(z.val[i]- (k.val[i-(z.val.size()-k.val.size())]+(Mvdwn?1:0)) + '0');
Mvdwn = false;
}
}
}
while(ret[ret.size()-1] == '0' && ret.size() > 1)ret.pop_back();
if(minus)ret.push_back('-');
if(ret.empty())ret.push_back('0');
reverse(ret.begin(),ret.end());
z.val = ret;
}
return z;
}
BigInt operator*(const BigInt &p) const {
BigInt z = *this;
BigInt k = p;
bool minus = false;
if((z.val[0] == '-') + (p.val[0] == '-') == 1)minus = true;
string Z = abs(z).val;
string K = abs(k).val;
vector<mint> Zz(Z.size());
for(int i = 0; Z.size() > i; i++){
Zz[i] = Z[Z.size()-i-1]-'0';
}
vector<mint> Kk(K.size());
for(int i = 0; K.size() > i; i++){
Kk[i] = K[K.size()-i-1]-'0';
}
auto Cc = convolution(Zz, Kk);
vector<int> C(Cc.size());
for(int i = 0; Cc.size() > i; i++){
C[i] = Cc[i].n;
}
for(int i = 0; C.size() > i; i++){
if(C[i] >= 10 && i+1 == C.size()){
C.push_back(C[i]/10);
}else if(C[i] >= 10){
C[i+1] += C[i]/10;
}
C[i] = C[i]%10;
}
C.push_back(0);
while(C.size() != 1 && C[C.size()-1] == 0)C.pop_back();
bool isZero = (C.size() == 1 && C[0] == 0);
string val;
if(minus && !isZero)val.push_back('-');
for(int i = 0; C.size() > i; i++){
val.push_back(C[C.size()-i-1]+'0');
}
BigInt ans = (BigInt)val;
return ans;
}
//Only N
BigInt operator/(const BigInt &p) const {
BigInt z = *this;
BigInt k = p;
assert(k != (BigInt)"0");
bool minus = false;
if((z.val[0] == '-') + (p.val[0] == '-') == 1)minus = true;
string Z = abs(z).val;
string K = abs(k).val;
BigInt rem = (BigInt)"0";
BigInt ans = (BigInt)"0";
// z / k
for(int i = 0; Z.size() > i; i++){
rem = rem*(BigInt)"10" + (BigInt)(Z[i]-'0');
int nw = 0;
while(rem >= K){
nw++;
rem -= K;
}
ans = ans*(BigInt)"10" + (BigInt)(nw);
}
if(minus && !ans.isZero())ans = SignTurn(ans);
return ans;
}
BigInt operator%(const BigInt &p) const {
BigInt z = *this;
BigInt k = p;
assert(k != (BigInt)"0");
bool minus = false;
if((z.val[0] == '-') + (p.val[0] == '-') == 1)minus = true;
string Z = abs(z).val;
string K = abs(k).val;
BigInt rem = (BigInt)"0";
BigInt ans = (BigInt)"0";
// z / k
for(int i = 0; Z.size() > i; i++){
rem = rem*(BigInt)"10" + (BigInt)(Z[i]-'0');
int nw = 0;
while(rem >= K){
nw++;
rem -= K;
}
ans = ans*(BigInt)"10" + (BigInt)(nw);
}
return rem;
}
BigInt operator+=(const BigInt &p){return *this = (*this) + p;}
BigInt operator-=(const BigInt &p){return *this = (*this) - p;}
BigInt operator*=(const BigInt &p){return *this = (*this) * p;}
BigInt operator/=(const BigInt &p){return *this = (*this) / p;}
BigInt operator%=(const BigInt &p){return *this = (*this) % p;}
bool operator==(const BigInt &p)const{return val == p.val;}
bool operator!=(const BigInt &p)const{return val != p.val;}
bool operator<(const BigInt &p)const{return val != max(*this, p).val;}
bool operator<=(const BigInt &p)const{return (*this) == p || (*this) < p;}
bool operator>(const BigInt &p)const{return p.val != max(*this, p).val;}
bool operator>=(const BigInt &p)const{return (*this) == p || (*this) > p;}
friend ostream &operator<<(ostream &os, const BigInt &p){
return os << p.val;
}
friend istream &operator>>(istream &is, BigInt &p){
string s;
cin>>s;
p = BigInt(s);
return (is);
}
};
#line 6 "cpp/z_test/aoj-NTL_2_F.test.cpp"
int main(){
BigInt A,B;cin>>A>>B;
cout << A*B << endl;
}