#ifndef CLASS_MODINT
#define CLASS_MODINT

#include <cassert>
#include <cstdint>

template <std::uint32_t mod>
class modint {
private:
	std::uint32_t n;
public:
	modint() noexcept : n(0) {};
	modint(std::int64_t n_) noexcept : n((n_ >= 0 ? n_ : mod - (-n_) % mod) % mod) {};
	static constexpr std::uint32_t get_mod() noexcept { return mod; }
	std::uint32_t get() const noexcept { return n; }
	bool operator==(const modint& m) const noexcept { return n == m.n; }
	bool operator!=(const modint& m) const noexcept { return n != m.n; }
	modint& operator+=(const modint& m) noexcept { n += m.n; n = (n < mod ? n : n - mod); return *this; }
	modint& operator-=(const modint& m) noexcept { n += mod - m.n; n = (n < mod ? n : n - mod); return *this; }
	modint& operator*=(const modint& m) noexcept { n = std::uint64_t(n) * m.n % mod; return *this; }
	modint operator+(const modint& m) const noexcept { return modint(*this) += m; }
	modint operator-(const modint& m) const noexcept { return modint(*this) -= m; }
	modint operator*(const modint& m) const noexcept { return modint(*this) *= m; }
	modint inv() const {
		assert(n != 0);
		return pow(mod - 2);
	}
	modint pow(std::uint64_t b) const noexcept {
		modint ans = 1, m = modint(*this);
		while (b) {
			if (b & 1) ans *= m;
			m *= m;
			b >>= 1;
		}
		return ans;
	}
};

#endif // CLASS_MODINT

#ifndef CLASS_POLYNOMIAL_NTT
#define CLASS_POLYNOMIAL_NTT

#include <vector>
#include <algorithm>

template<std::uint32_t mod, std::uint32_t depth, std::uint32_t primroot>
class polynomial_ntt {
public:
	using modulo = modint<mod>;
	static void fourier_transform(std::vector<modulo> &v, bool inverse) {
		std::size_t s = v.size();
		for (std::size_t i = 0, j = 1; j < s - 1; ++j) {
			for (std::size_t k = s >> 1; k > (i ^= k); k >>= 1);
			if (i < j) std::swap(v[i], v[j]);
		}
		std::size_t sc = 0, sz = 1;
		while (sz < s) sz *= 2, ++sc;
		modulo root = modulo(primroot).pow((mod - 1) >> sc);
		std::vector<modulo> pw(s + 1); pw[0] = 1;
		for (std::size_t i = 1; i <= s; i++) pw[i] = pw[i - 1] * root;
		std::size_t qs = s;
		for (std::size_t b = 1; b < s; b <<= 1) {
			qs >>= 1;
			for (std::size_t i = 0; i < s; i += b * 2) {
				for (std::size_t j = i; j < i + b; ++j) {
					modulo delta = pw[(inverse ? b * 2 - j + i : j - i) * qs] * v[j + b];
					v[j + b] = v[j] - delta;
					v[j] += delta;
				}
			}
		}
		if (!inverse) return;
		modulo powinv = modulo((mod + 1) / 2).pow(sc);
		for (std::size_t i = 0; i < s; ++i) {
			v[i] = v[i] * powinv;
		}
	}
	static std::vector<modulo> convolve(std::vector<modulo> v1, std::vector<modulo> v2) {
		if(v1.empty() || v2.empty()) {
			return std::vector<modulo>();
		}
		if(v1.size() < v2.size()) {
			swap(v1, v2);
		}
		std::size_t fsize = v1.size() + v2.size() - 1;
		std::size_t step = v2.size();
		while(step & (step - 1)) {
			step += step & (-step); // step to become minimum 2^k >= v2.size()
		}
		v1.resize((v1.size() + step - 1) / step * step); // change v1.size() to multiple of step
		v2.resize(step * 2);
		std::vector<modulo> ans(fsize);
		std::vector<modulo> pv1(step * 2);
		fourier_transform(v2, false);
		for(std::size_t i = 0; i < v1.size(); i += step) {
			std::copy(v1.begin() + i, v1.begin() + i + step, pv1.begin());
			std::fill(pv1.begin() + step, pv1.end(), modulo(0));
			fourier_transform(pv1, false);
			for(std::size_t j = 0; j < step * 2; ++j) {
				pv1[j] *= v2[j];
			}
			fourier_transform(pv1, true);
			for(std::size_t j = 0; j < step * 2 && i + j < fsize; ++j) {
				ans[j + i] += pv1[j];
			}
		}
		return ans;
	}
};

#endif // CLASS_POLYNOMIAL_NTT

#ifndef CLASS_BASICINT
#define CLASS_BASICINT

#include <vector>
#include <cassert>
#include <cstdint>
#include <algorithm>

template<std::uint32_t base>
class basic_int {
	// 2 <= base < max(base_type) / 2 = 2^30
protected:
	using base_type = std::uint32_t; // base type
	using base_type_d = std::uint64_t; // double of base type
	std::size_t capacity;
	std::vector<base_type> digit;
	void resize(std::size_t capacity_) {
		if(capacity != capacity_) {
			assert(capacity_ >= 1);
			capacity = capacity_;
			digit.resize(capacity_);
		}
	}
	void minimize() {
		std::size_t d = digits();
		while(d & (d - 1)) {
			d += d & (-d);
		}
		resize(d);
	}
	std::size_t digits() const {
		for(std::size_t i = capacity - 1; i >= 1; --i) {
			if(digit[i] != 0) {
				return i + 1;
			}
		}
		return 1;
	}
public:
	// ----- #1. Costructors ----- //
	explicit basic_int() : capacity(1), digit(1, base_type(0)) {};
	explicit basic_int(std::vector<base_type> digit_) : digit(digit_) {
		assert(!digit.empty());
		for(base_type i : digit) {
			assert(0 <= i && i < base);
		}
		capacity = 1;
		while(capacity < digit.size()) {
			capacity <<= 1;
		}
		digit.resize(capacity, 0);
	};

	// ----- #2. Comparing Operators ----- //
	bool operator==(const basic_int &b) const noexcept {
		return digit == b.digit;
	}
	bool operator!=(const basic_int &b) const noexcept {
		return digit != b.digit;
	}
	bool operator<(const basic_int &b) const noexcept {
		if(capacity != b.capacity) {
			return capacity < b.capacity;
		}
		for(std::size_t i = capacity - 1; i >= 1; --i) {
			if(digit[i] != b.digit[i]) {
				return digit[i] < b.digit[i];
			}
		}
		return digit[0] < b.digit[0];
	}
	bool operator>(const basic_int &b) const noexcept {
		if(capacity != b.capacity) {
			return capacity > b.capacity;
		}
		for(std::size_t i = capacity - 1; i >= 1; --i) {
			if(digit[i] != b.digit[i]) {
				return digit[i] > b.digit[i];
			}
		}
		return digit[0] > b.digit[0];
	}
	bool operator<=(const basic_int &b) const noexcept {
		return basic_int(*this) < b || basic_int(*this) == b;
	}
	bool operator>=(const basic_int &b) const noexcept {
		return basic_int(*this) > b || basic_int(*this) == b;
	}

	// ----- #3. Shifting ----- //
	basic_int& operator<<=(std::size_t s) noexcept {
		std::size_t d = digits();
		if(d + s > capacity) {
			std::size_t nxtcap = d + s;
			while(nxtcap & (-nxtcap)) {
				nxtcap += nxtcap & (-nxtcap);
			}
			resize(nxtcap);
		}
		for(std::size_t i = d; i >= s; --i) {
			digit[i] = digit[i - s];
		}
		std::fill(digit.begin(), digit.begin() + s, 0);
		return *this;
	}
	basic_int& operator>>=(std::size_t s) noexcept {
		std::size_t d = digits();
		if(d <= s) {
			(*this) = basic_int();
		}
		else {
			for(std::size_t i = 0; i < d - s; ++i) {
				digit[i] = digit[i + s];
			}
			std::fill(digit.begin() + d - s, digit.begin() + d, 0);
			minimize();
		}
		return *this;
	}
	
	// ----- #4. Addition ----- //
	basic_int& operator+=(const basic_int &b) {
		resize(std::max(capacity, b.capacity));
		bool carry = false;
		for(std::uint32_t i = 0; i < b.capacity; ++i) {
			digit[i] += b.digit[i] + (carry ? 1 : 0);
			carry = (digit[i] >= base);
			if(carry) digit[i] -= base;
		}
		for(std::uint32_t i = b.capacity; i < capacity; ++i) {
			digit[i] += (carry ? 1 : 0);
			carry = (digit[i] >= base);
			if(carry) digit[i] -= base;
		}
		if(carry) {
			resize(capacity << 1);
			digit[capacity >> 1] = 1;
		}
		return *this;
	}
	
	// ----- #5. Subtraction ----- //
	basic_int& operator-=(const basic_int &b) {
		bool carry = false;
		for(std::uint32_t i = 0; i < b.capacity; ++i) {
			digit[i] += base - b.digit[i] - (carry ? 1 : 0);
			carry = (digit[i] < base);
			if(!carry) digit[i] -= base;
		}
		for(std::uint32_t i = b.capacity; i < capacity; ++i) {
			digit[i] += base - (carry ? 1 : 0);
			carry = (digit[i] < base);
			if(!carry) digit[i] -= base;
		}
		// assert(!carry) // noexcept
		minimize();
		return *this;
	}

	// ----- #6. Multiplication ----- //
	basic_int& operator*=(const basic_int &b) {
		using ntt1 = polynomial_ntt<469762049, 26, 3>;
		using ntt2 = polynomial_ntt<167772161, 25, 3>;
		const ntt2::modulo magic_mult = ntt2::modulo(ntt1::modulo::get_mod()).inv();
		std::size_t nxtcap = std::max(capacity, b.capacity) * 2;
		std::vector<ntt1::modulo> nxtdigit1 = ntt1::convolve(std::vector<ntt1::modulo>(digit.begin(), digit.end()), std::vector<ntt1::modulo>(b.digit.begin(), b.digit.end())); nxtdigit1.resize(nxtcap);
		std::vector<ntt2::modulo> nxtdigit2 = ntt2::convolve(std::vector<ntt2::modulo>(digit.begin(), digit.end()), std::vector<ntt2::modulo>(b.digit.begin(), b.digit.end())); nxtdigit2.resize(nxtcap);
		capacity = nxtcap;
		digit = std::vector<std::uint32_t>(capacity, 0);
		std::uint64_t carry = 0;
		for(std::uint32_t i = 0; i < capacity; ++i) {
			std::uint64_t nxtdigit = std::uint64_t(ntt1::modulo::get_mod()) * ((nxtdigit2[i] - nxtdigit1[i].get()) * magic_mult).get() + nxtdigit1[i].get();
			nxtdigit += carry;
			carry = nxtdigit / base;
			digit[i] = nxtdigit - carry * base;
		}
		minimize();
		return *this;
	}
};

#endif // CLASS_BASICINT

#ifndef CLASS_BIGINT
#define CLASS_BIGINT

#include <string>
#include <iostream>

constexpr std::size_t base_digit = 1;
constexpr std::uint32_t base_number = 10;

class bigint : private basic_int<base_number> {
private:
	bool sign; // positive/zero = false, negative = true
	bool is_number(std::string str) {
		if(str.empty() || str == "-" || !(('0' <= str[0] && str[0] <= '9') || str[0] == '-')) {
			return false;
		}
		for(std::uint64_t i = 1; i < str.size(); ++i) {
			if(!('0' <= str[i] && str[i] <= '9')) {
				return false;
			}
		}
		return true;
	}
	std::vector<std::uint32_t> to_basic_expression(std::uint64_t x) {
		std::vector<std::uint32_t> res;
		do {
			res.push_back(x % base_number);
			x /= base_number;
		} while(x != 0);
		return res;
	}
public:
	// ----- #1. Constructors / String Functions ----- //
	bigint() : basic_int(), sign(false) {};
	bigint(std::int64_t x) : basic_int(to_basic_expression(x >= 0 ? x : -x)), sign(x < 0) {};
	bigint(const std::string& str) {
		assert(is_number(str));
		sign = (str[0] == '-');
		std::size_t initial = (sign ? 1 : 0);
		while(initial + 1 != str.size() && str[initial] == '0') {
			++initial;
		}
		std::size_t str_digits = str.size() - initial;
		capacity = 1;
		while(capacity < str_digits) {
			capacity *= 2;
		}
		digit = std::vector<std::uint32_t>(capacity, 0);
		for(std::size_t i = 0; i < str_digits - base_digit; i += base_digit) {
			digit[i] = std::stoi(str.substr(str.size() - i - base_digit, base_digit));
		}
		digit[(str_digits - 1) / base_digit] = std::stoi(str.substr(initial, (str.size() - (str_digits - 1) / base_digit * base_digit) - initial));
	}
	std::string to_string() const {
		std::size_t d = digits();
		std::string res = (sign ? "-" : "") + std::to_string(digit[d - 1]);
		for(std::size_t i = d - 2; i != std::size_t(-1); --i) {
			std::string digit_str = std::to_string(digit[i]);
			res += std::string(base_digit - digit_str.size(), '0') + digit_str;
		}
		return res;
	}

	// ----- #2. Operators ----- //
	bool operator==(const bigint& b) const noexcept { return sign == b.sign && basic_int::operator==(b); }
	bool operator!=(const bigint& b) const noexcept { return sign != b.sign || basic_int::operator!=(b); }
	bool operator<(const bigint& b) const noexcept { return sign != b.sign ? sign : basic_int::operator<(b); }
	bool operator>(const bigint& b) const noexcept { return sign != b.sign ? !sign : basic_int::operator>(b); }
	bool operator<=(const bigint& b) const noexcept { return sign != b.sign ? sign : basic_int::operator<=(b); }
	bool operator>=(const bigint& b) const noexcept { return sign != b.sign ? !sign : basic_int::operator>=(b); }
	bigint& operator+=(const bigint& b) {
		if(sign == b.sign) {
			basic_int::operator+=(b);
		}
		else if(basic_int(*this) >= basic_int(b)) {
			basic_int::operator-=(b);
			if(sign && basic_int(*this) == basic_int()) sign = false;
		}
		else {
			sign = !sign;
			(*this) = b - (*this);
		}
		return *this;
	}
	bigint& operator-=(const bigint& b) {
		if(sign != b.sign) {
			basic_int::operator+=(b);
		}
		else if(basic_int(*this) >= basic_int(b)) {
			basic_int::operator-=(b);
			if(sign && basic_int(*this) == basic_int()) sign = false;
		}
		else {
			sign = !sign;
			(*this) += b;
			sign = !sign;
		}
		return *this;
	}
	bigint& operator*=(const bigint& b) {
		basic_int::operator*=(b);
		if(b.sign) sign = !sign;
		if(sign && basic_int(*this) == basic_int()) sign = false;
		return *this;
	}
	bigint operator+(const bigint& b) const { return bigint(*this) += b; }
	bigint operator-(const bigint& b) const { return bigint(*this) -= b; }
	bigint operator*(const bigint& b) const { return bigint(*this) *= b; }
	friend std::istream& operator>>(std::istream& is, bigint& x) { std::string s; is >> s; x = bigint(s); return is; }
	friend std::ostream& operator<<(std::ostream& os, const bigint& x) { os << x.to_string(); return os; }
};

#endif // CLASS_BIGINT

#include <iostream>
int main() {
	bigint a, b;
	std::cin >> a >> b;
	std::cout << a * b << std::endl;
	return 0;
}


Measured memory_bytes: 3356