結果

問題 No.3030 ミラー・ラビン素数判定法のテスト
ユーザー meowricatormeowricator
提出日時 2020-05-14 22:58:04
言語 C++17
(gcc 12.3.0 + boost 1.83.0)
結果
RE  
実行時間 -
コード長 21,959 bytes
コンパイル時間 2,794 ms
コンパイル使用メモリ 141,880 KB
実行使用メモリ 10,752 KB
最終ジャッジ日時 2024-04-29 13:57:06
合計ジャッジ時間 14,817 ms
ジャッジサーバーID
(参考情報)
judge5 / judge4
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2 ms
5,248 KB
testcase_01 AC 2 ms
5,376 KB
testcase_02 AC 23 ms
5,376 KB
testcase_03 AC 121 ms
5,376 KB
testcase_04 RE -
testcase_05 TLE -
testcase_06 -- -
testcase_07 -- -
testcase_08 -- -
testcase_09 -- -
権限があれば一括ダウンロードができます

ソースコード

diff #

// https://yukicoder.me/problems/no/3030

#include <iostream>
#include <string>
#include <cmath>
#include <type_traits>
#include <cassert>
#include <iomanip>
#include <vector>
#include <complex>
#include <random>

using i64 = int_fast64_t;
using u64 = uint_fast64_t;
using i32 = int_fast32_t;
using u32 = uint_fast32_t;

namespace mlib {

struct constexpr_string {
  constexpr constexpr_string(const char *str): str(str) {}

  const char *str;

  constexpr bool strcmp_begin(const constexpr_string st) const {
	return st.str[0] == '\x00' || (this->str[0] != '\x00' && this->str[0] == st.str[0] && constexpr_string(this->str + 1).strcmp_begin(st.str + 1));
  }
};

template <typename T>
constexpr bool is_type_in_root_namespace(const constexpr_string name) {
	return constexpr_string(__PRETTY_FUNCTION__ + (sizeof("constexpr bool mlib::is_type_in_root_namespace(mlib::constexpr_string) [with T = ") - 1)).strcmp_begin(name);
}

}

namespace mlib {

constexpr double pi = std::acos(-1);

using std::pow;
template<typename T, typename U, std::enable_if_t<is_type_in_root_namespace<T>("mlib"), std::nullptr_t> = nullptr>
T pow(T x, U n) {
	T res = 1;
	T a = x;
	while(n != 0) {
		if(n % 2 == 1) res *= a;
		a *= a;
		n /= 2;
	}
	return res;
}

template<typename T>
T mod_pow(const T &x, const T &n, const T &mod) {
	T res = 1;
	T a = x;
	T ll = n;
	while(ll != 0) {
		if(ll % 2 == 1) res = mod_mul(res, a, mod);
		a = mod_mul(a, a, mod);
		ll /= 2;
	}
	return res;
}

// @mod needs prime.
template <typename T>
T mod_inv(T x, T mod) {
	assert(mod > 0);
	if(mod == 1) return 0;
	return mod_pow(x, mod - 2, mod);
}

template <typename T>
T mod_add(T lhs, T rhs, T mod) {
	assert(mod > 0);
	lhs %= mod, rhs %= mod;
	lhs += rhs;
	if(lhs >= mod) lhs -= mod;
	return lhs;
}

template <typename T>
T mod_sub(T lhs, T rhs, T mod) {
	assert(mod > 0);
	lhs %= mod, rhs %= mod;
	if(lhs < rhs) lhs += mod;
	lhs -= rhs;
	return lhs;
}

template <typename T>
T mod_mul(T lhs, T rhs, T mod) {
	assert(mod > 0);
	return ((lhs % mod) * (rhs % mod)) % mod;
}

// @mod needs prime.
template <typename T>
T mod_div(T lhs, T rhs, T mod) {
	assert(mod > 0);
	assert(rhs > 0);
	lhs %= mod, rhs %= mod;
	return mod_mul(lhs, mod_inv(rhs, mod), mod);
}

}

namespace mlib {

class FFT {
public:
	static std::vector<std::complex<double>> fft(std::vector<std::complex<double>> &f);
	static std::vector<std::complex<double>> ifft(std::vector<std::complex<double>> &f);
};

}

using namespace mlib;

std::vector<std::complex<double>> FFT::fft(std::vector<std::complex<double>> &f) {
	u32 n = f.size();
	if(n <= 1) return f;
 
	std::vector<std::complex<double>> res = f;
	
	// Bit rev
	u32 s = 0;
	while((u32)(1 << s) < n) s++;
	for(u32 i = 0; i < n; i++) {
		u32 j = 0;
		for(u32 k = 0; k < s; k++) j |= (i >> k & 1) << (s - 1 - k);
		if(i < j) std::swap(res[i], res[j]);
	}
 
	double ang = -1.0 * pi / (2.0 * n);
	std::complex<double> zeta1, zeta3, x0, x1, x3;
	u32 m, mm, rev, k1, k2, k3;
 
	for(m = 4; m <= n; m <<= 1) {
		mm = m >> 2;
		for(u32 j = mm; j >= 1; j >>= 2) {
			for(u32 k = mm - j; k < mm - (j >> 1); k++) {
				k1 = k + mm;
				k2 = k1 + mm;
				k3 = k2 + mm;
				x1 = res[k] - res[k1];
				res[k] += res[k1];
				x3 = res[k3] - res[k2];
				res[k2] += res[k3];
				res[k1] = std::complex<double>(x1.real() - x3.imag(), x1.imag() + x3.real());	//res[k1] = x1 + x3 * ui;
				res[k3] = std::complex<double>(x1.real() + x3.imag(), x1.imag() - x3.real());	// res[k3] = x1 - x3 * ui;
			}
		}
		if(m == n) continue;
 
		rev = n >> 1;
		zeta1 = std::cos(ang * rev);
		for(u32 j = mm; j >= 1; j >>= 2) {
			for(u32 k = m + mm - j; k < m + mm - (j >> 1); k++) {
				k1 = k + mm;
				k2 = k1 + mm;
				k3 = k2 + mm;
				x1 = res[k] - res[k1];
				res[k] += res[k1];
				x3 = res[k3] - res[k2];
				res[k2] += res[k3];
				x0 = std::complex<double>(x1.real() - x3.imag(), x1.imag() + x3.real());	// x0 = x1 + x3 * ui; 
				x0 = std::complex<double>(x0.real() + x0.imag(), x0.imag() - x0.real());	// x0 *= 1.0 - ui;
				res[k1] = zeta1 * x0;
				x0 = std::complex<double>(x1.real() + x3.imag(), x1.imag() - x3.real());	// x0 = x1 - x3 * ui;
				x0 = std::complex<double>(-x0.real() + x0.imag(), -x0.imag() - x0.real());	// x0 *= -1.0 - ui;
				res[k3] = zeta1 * x0;
			}
		}
 
		for(u32 i = 2 * m; i < n; i += m) {
			for(u32 j = n >> 1; j > (rev ^= j); j >>= 1);
			zeta1 = std::polar(1.0, ang * rev);
			zeta3 = std::polar(1.0, 3.0 * ang * rev);
			for(u32 j = mm; j >= 1; j >>= 2) {
				for(u32 k = i + mm - j; k < i + mm - (j >> 1); k++) {
					k1 = k + mm;
					k2 = k1 + mm;
					k3 = k2 + mm;
					x1 = res[k] - res[k1];
					res[k] += res[k1];
					x3 = res[k3] - res[k2];
					res[k2] += res[k3];
					x0 = std::complex<double>(x1.real() - x3.imag(), x1.imag() + x3.real()); // x0 = x1 + x3 * ui;
					res[k1] = zeta1 * x0;
					x0 = std::complex<double>(x1.real() + x3.imag(), x1.imag() - x3.real()); // x0 = x1 - x3 * ui;
					res[k3] = zeta3 * x0;
				}
			}
		}
	}
 
	mm = n >> 1;
	for(u32 j = mm; j >= 1; j >>= 2) {
		for(u32 k = mm - j; k < mm - (j >> 1); k++) {
			k1 = mm + k;
			x0 = res[k] - res[k1];
			res[k] += res[k1];
			res[k1] = x0;
		}
	}
 
	return res;
}
 
std::vector<std::complex<double>> FFT::ifft(std::vector<std::complex<double>> &f) {
	u32 n = f.size();
	if(n <= 1) return f;
 
	std::vector<std::complex<double>> res = f;
	
	// Bit rev
	u32 s = 0;
	while((u32)(1 << s) < n) s++;
	for(u32 i = 0; i < n; i++) {
		u32 j = 0;
		for(u32 k = 0; k < s; k++) j |= (i >> k & 1) << (s - 1 - k);
		if(i < j) std::swap(res[i], res[j]);
	}
 
	double ang = 1.0 * pi / (2.0 * n);
	std::complex<double> zeta1, zeta3, x0, x1, x3;
	u32 m, mm, rev, k1, k2, k3;
 
	for(m = 4; m <= n; m <<= 1) {
		mm = m >> 2;
		for(u32 j = mm; j >= 1; j >>= 2) {
			for(u32 k = mm - j; k < mm - (j >> 1); k++) {
				k1 = k + mm;
				k2 = k1 + mm;
				k3 = k2 + mm;
				x1 = res[k] - res[k1];
				res[k] += res[k1];
				x3 = res[k3] - res[k2];
				res[k2] += res[k3];
				res[k1] = std::complex<double>(x1.real() + x3.imag(), x1.imag() - x3.real());	// res[k1] = x1 - x3 * ui;
				res[k3] = std::complex<double>(x1.real() - x3.imag(), x1.imag() + x3.real());	// res[k3] = x1 + x3 * ui;
			}
		}
		if(m == n) continue;
 
		rev = n >> 1;
		zeta1 = std::cos(ang * rev);
		for(u32 j = mm; j >= 1; j >>= 2) {
			for(u32 k = m + mm - j; k < m + mm - (j >> 1); k++) {
				k1 = k + mm;
				k2 = k1 + mm;
				k3 = k2 + mm;
				x1 = res[k] - res[k1];
				res[k] += res[k1];
				x3 = res[k3] - res[k2];
				res[k2] += res[k3];
				x0 = std::complex<double>(x1.real() + x3.imag(), x1.imag() - x3.real());	// x0 = x1 - x3 * ui;
				x0 = std::complex<double>(x0.real() - x0.imag(), x0.imag() + x0.real());	// x0 *= 1.0 + ui;
				res[k1] = zeta1 * x0;
				x0 = std::complex<double>(x1.real() - x3.imag(), x1.imag() + x3.real());	// x0 = x1 + x3 * ui;
				x0 = std::complex<double>(-x0.real() - x0.imag(), x0.real() + x0.imag());	// x0 *= -1.0 + ui;
				res[k3] = zeta1 * x0;
			}
		}
 
		for(u32 i = 2 * m; i < n; i += m) {
			for(u32 j = n >> 1; j > (rev ^= j); j >>= 1);
			zeta1 = std::polar(1.0, ang * rev);
			zeta3 = std::polar(1.0, 3.0 * ang * rev);
			for(u32 j = mm; j >= 1; j >>= 2) {
				for(u32 k = i + mm - j; k < i + mm - (j >> 1); k++) {
					k1 = k + mm;
					k2 = k1 + mm;
					k3 = k2 + mm;
					x1 = res[k] - res[k1];
					res[k] += res[k1];
					x3 = res[k3] - res[k2];
					res[k2] += res[k3];
					x0 = std::complex<double>(x1.real() + x3.imag(), x1.imag() - x3.real());	// x0 = x1 - x3 * ui;
					res[k1] = zeta1 * x0;
					x0 = std::complex<double>(x1.real() - x3.imag(), x1.imag() + x3.real());	// x0 = x1 + x3 * ui;
					res[k3] = zeta3 * x0;
				}
			}
		}
	}
 
	mm = n >> 1;
	for(u32 j = mm; j >= 1; j >>= 2) {
		for(u32 k = mm - j; k < mm - (j >> 1); k++) {
			k1 = k + mm;
			x0 = res[k] - res[k1];
			res[k] += res[k1];
			res[k1] = x0;
		}
	}
 
	return res;
}

namespace mlib {

// number of base digits
constexpr u64 MAX_DIGITNUM = 5;
// radix base
constexpr u64 RADIX_BASE = 10;
// radix
constexpr u64 RADIX = std::pow(RADIX_BASE, MAX_DIGITNUM);

// sign
using SIGN = bool;
constexpr bool PLUS = true;
constexpr bool MINUS = false;

using DIGITS = std::vector<u64>;

class Int {
private:
	// 12345678 => digits = {78, 56, 34, 12}
	DIGITS digits;
	SIGN sign;

	void assignment(const Int &x) noexcept;
	void to_zero() noexcept;
	void to_radix_power(u64 n) noexcept;
	void push_up(u64 num) noexcept;	// link as upper digit

	// unsigned operations	
	// absolute peer arithmetics
	// The argument does not have to be non-negative.
	void add(const Int &x) noexcept;	
	SIGN sub(const Int &x) noexcept;	// automatically subtracted from the larger one
	void mul(const Int &x) noexcept;	
	void div_short(const Int &x, bool rem = false) noexcept;
	void div(const Int &x, bool rem = false) noexcept;
	bool is_equal(const Int &x) const noexcept;	
	bool less_than(const Int &x, bool equal = false) const noexcept;	
	bool greater_than(const Int &x, bool equal = false) const noexcept;

	// absolute
	Int abs() const noexcept;	

	// signed operations (arithmetic)
	void a_add(const Int &x) noexcept;	
	void a_sub(const Int &x) noexcept; 
	void a_mul(const Int &x) noexcept;	
	void a_div(const Int &x) noexcept;
	void a_mod(const Int &x) noexcept;
	bool a_is_equal(const Int &x) const noexcept;	
	bool a_less_than(const Int &x, bool equal = false) const noexcept;	
	bool a_greater_than(const Int &x, bool equal = false) const noexcept;

public:
	// default constructor
	Int();

	// integer constructor
	template<typename T, std::enable_if_t<std::is_integral<T>::value, std::nullptr_t> = nullptr>
	Int(T n): Int(std::to_string(n)) {
	}

	Int(const DIGITS &digits);
	
	// std::string constructor
	Int(const std::string &x);

	size_t size() const noexcept;
	u64 at(i64 n) const noexcept;
	u64 most_sig() const noexcept;

	void resize(size_t size) noexcept;
	void normalize() noexcept;	// remove meaningless 0 from the top digit
	void align() noexcept;		// adjusted so that each digit does not exceed RADIX

	/* operators */
	// io
	friend std::ostream &operator <<(std::ostream &os, Int &x) noexcept {
		if(x.sign == MINUS && x != 0) os << "-";
		os << x.digits.at(x.size() - 1);
		for(size_t i = x.size() - 1; i --> 0;) os << std::setw(MAX_DIGITNUM) << std::setfill('0') << x.digits.at(i);
		return os;
	}

	friend std::ostream &operator <<(std::ostream &os, Int &&x) noexcept {
		if(x.sign == MINUS && x != 0) os << "-";
		os << x.digits.at(x.size() - 1);
		for(size_t i = x.size() - 1; i --> 0;) os << std::setw(MAX_DIGITNUM) << std::setfill('0') << x.digits.at(i);
		return os;
	}

	friend std::istream &operator >>(std::istream &is, Int &x) noexcept {
		std::string s;
		is >> s;
		x = Int(s);
		return is;
	}

	// arithmetic
	Int &operator +=(const Int &x) noexcept {
		a_add(x);
		return *this;
	}

	Int &operator -=(const Int &x) noexcept {
		a_sub(x);
		return *this;
	}

	Int &operator *=(const Int &x) noexcept {
		a_mul(x);
		return *this;
	}

	Int &operator /=(const Int &x) noexcept {
		a_div(x);
		return *this;
	}

	Int &operator %=(const Int &x) noexcept {
		a_mod(x);
		return *this;
	}

	friend Int operator +(Int lhs, const Int &rhs) noexcept {
		lhs += rhs;
		return lhs;
	}

	friend Int operator -(Int lhs, const Int &rhs) noexcept {
		lhs -= rhs;
		return lhs;
	}

	friend Int operator *(Int lhs, const Int &rhs) noexcept {
		lhs *= rhs;
		return lhs;
	}

	friend Int operator /(Int lhs, const Int &rhs) noexcept {
		lhs /= rhs;
		return lhs;
	}

	friend Int operator %(Int lhs, const Int &rhs) noexcept {
		lhs %= rhs;
		return lhs;
	}

	// unary
	Int &operator ++() noexcept {
		operator +=(1);
		return *this;
	}

	Int operator ++(int) noexcept {
		Int x = *this;
		operator +=(1);
		return x; 
	}

	Int &operator --() noexcept {
		operator -=(1);
		return *this;
	}

	Int operator --(int) noexcept {
		Int x = *this;
		operator -=(1);
		return x;
	}

	Int operator +() const noexcept {
		return *this;
	}

	Int operator -() const noexcept {
		Int x = *this;
		x.sign = !x.sign;
		return x;
	}

	// comparison
	friend bool operator ==(const Int &lhs, const Int &rhs) noexcept {
		return lhs.a_is_equal(rhs);
	}

	friend bool operator !=(const Int &lhs, const Int &rhs) noexcept {
		return !lhs.a_is_equal(rhs);
	}

	friend bool operator <(const Int &lhs, const Int &rhs) noexcept {
		return lhs.a_less_than(rhs);
	}

	friend bool operator <=(const Int &lhs, const Int &rhs) noexcept {
		return lhs.a_less_than(rhs, true);
	}

	friend bool operator >(const Int &lhs, const Int &rhs) noexcept {
		return lhs.a_greater_than(rhs);
	}

	friend bool operator >=(const Int &lhs, const Int &rhs) noexcept {
		return lhs.a_greater_than(rhs, true);
	}
};

inline Int Int::abs() const noexcept {
	if(this->sign == PLUS) return *this;

	Int res = *this;
	res.sign = PLUS;
	return res;
}

inline void Int::resize(size_t size) noexcept {
	this->digits.resize(size, 0);
}

inline void Int::normalize() noexcept {
	while(size() > 0) {
		if(this->digits.back() == 0) this->digits.pop_back();
		else break;
	}

	if(size() == 0) to_zero();
}

inline u64 Int::at(i64 n) const noexcept {
	return this->digits.at(n);
}

inline void Int::assignment(const Int &x) noexcept {
	this->digits = x.digits;
	this->sign = x.sign;
}

inline void Int::to_zero() noexcept {
	std::vector<u64>().swap(this->digits);
	push_up(0);
}

inline void Int::to_radix_power(u64 n) noexcept {
	if(n == 0) {
		assignment(1);
		return;
	}

	to_zero();
	this->digits.reserve(n);
	for(u64 i = 0; i < n -1; i++) push_up(0);
	push_up(1);
}

inline void Int::push_up(u64 num) noexcept {
	this->digits.emplace_back(num);
}

inline size_t Int::size() const noexcept {
	return this->digits.size();
}

inline u64 Int::most_sig() const noexcept {
	return this->digits.back();
}

inline void Int::align() noexcept {
	for(size_t i = 0; i < this->digits.size() - 1; i++) {
		if(this->digits.at(i) >= RADIX) {
			this->digits.at(i + 1) += this->digits.at(i) / RADIX;
			this->digits.at(i) %= RADIX;
		}
	}

	if(this->digits.back() >= RADIX) {
		this->push_up(this->digits.back() / RADIX);
		this->digits.at(this->digits.size() - 2) %= RADIX;
	}
}

}

using namespace mlib;

Int::Int(): Int("0") {
}

Int::Int(const DIGITS &digits) {
	this->sign = PLUS;
	this->digits = digits;
}

Int::Int(const std::string &x) {
	std::string num = x;

	this->sign = PLUS;
	if(num.size() != 0) {
		if(num.front() == '+') num.erase(num.begin());
		else if(num.front() == '-') {
			num.erase(num.begin());
			this->sign = MINUS;
		}
	}

	if(num.size() == 0) num = "0";

	while(num.size() > MAX_DIGITNUM) {
		push_up(std::stoull(num.substr(num.size() - MAX_DIGITNUM, MAX_DIGITNUM)));
		for(size_t i = 0; i < MAX_DIGITNUM; i++) num.pop_back();
	}
	push_up(std::stoull(num));
}

void Int::add(const Int &x) noexcept {
	if(x == 0) return;

	if(size() < x.size()) resize(x.size());

	u64 buf, carry = 0;
	u64 rd;
	for(size_t i = 0; i < size(); i++) {
		rd = (i < x.size())? x.digits.at(i) : 0;
		buf = this->digits.at(i) + rd + carry;
		carry = buf / RADIX;
		this->digits.at(i) = buf % RADIX;
	}

	if(carry != 0) push_up(carry);
	normalize();
}

SIGN Int::sub(const Int &x) noexcept {
	if(is_equal(x)) {
		to_zero();
		return PLUS;
	}

	u32 borrow = 0;
	u64 rd;

	// *this - x
	if(greater_than(x)) {
		for(size_t i = 0; i < size(); i++) {
			rd = (i < x.size())? x.digits.at(i) : 0;
			if(this->digits.at(i) >= rd + borrow) {
				this->digits.at(i) = this->digits.at(i) - borrow - rd;
				borrow = 0;
			} else {
				this->digits.at(i) = this->digits.at(i) + RADIX - borrow - rd;
				borrow = 1;
			}
		}
		normalize();
		return PLUS;
	}

	// x - *this
	if(size() < x.size()) resize(x.size());
	for(size_t i = 0; i < x.size(); i ++) {
		rd = (i < size())? this->digits.at(i) : 0;
		if(x.digits.at(i) >= rd + borrow) {
			this->digits.at(i) = x.digits.at(i) - borrow - rd;
			borrow = 0;
		} else {
			this->digits.at(i) = x.digits.at(i) + RADIX - borrow - rd;
			borrow = 1;
		}
	}
	normalize();
	return MINUS;
}

void Int::mul(const Int &x) noexcept {
	if(x == 0) {
		to_zero();
		return;
	}

	// adjust the number of digits to 2^n
	size_t n = 1;
	while (n < size() + x.size()) n <<= 1;

	std::vector<std::complex<double>> a(n);
	for(size_t i = 0; i < n; i++) {
		a.at(i).real((i >= size()) ? 0 : this->digits.at(i));
		a.at(i).imag((i >= x.size()) ? 0 : x.digits.at(i));
	}

	a = FFT::fft(a);
	std::vector<std::complex<double>> res(n);
	for(size_t i = 0; i < n; i++) {
		res.at(i) = (a.at(i) + std::conj(a.at((n - i) % n))) * 0.5 * (a.at(i) - std::conj(a.at((n - i) % n))) * std::complex<double>(0, -0.5);
	}
		
	res = FFT::ifft(res);
	resize(n);
	for(size_t i = 0; i < n; i++) this->digits.at(i) = std::round(res.at(i).real() / static_cast<double>(n));

	normalize();
	align();
}

// short divisor
// x is absolute value
void Int::div_short(const Int &x, bool rem) noexcept {
	if(x < RADIX) {
		u64 dividend = this->digits.back();
		for(u64 i = this->digits.size(); i --> 0;) {
			this->digits.at(i) = dividend / x.at(0);
			if(i == 0) {
				if(rem) assignment(dividend % x.at(0));
				else normalize();
				return;
			}
			dividend = (dividend % x.at(0)) * RADIX + this->digits.at(i - 1);
		}
	}
	
	u64 radix_per_10 = RADIX / 10;
	Int dividend(this->digits.back() / radix_per_10);

	u64 qq, xx;
	Int rr;
	for(u64 i = this->digits.size(); i --> 0;) {
		xx = 0;
		for(u64 j = MAX_DIGITNUM; j --> 0;) {
			qq = 0;
			rr = dividend;
			while(rr >= x) {
				rr -= x;
				qq++;
			}
			xx += qq * std::pow(RADIX_BASE, j);
			
			if(i == 0 && j == 0) {
				this->digits.at(i) = xx;
				if(!rem) {
					this->digits.at(i) = xx;
					normalize();
				} else {
					assignment(rr);
				}
				return;
			}
			
			if(qq != 0) dividend = rr;
			dividend *= RADIX_BASE;
			if(j == 0) dividend += at(i - 1) / radix_per_10;
			else dividend += at(i) / static_cast<u64>(std::pow(RADIX_BASE, j - 1)) % 10;
		}
		this->digits.at(i) = xx;
	}
}


// Knuth D
void Int::div(const Int &x, bool rem) noexcept {
	assert(x != 0);
	if(less_than(x)) {
		if(!rem) to_zero();
		return;
	}

	if(is_equal(x)) {
		if(!rem) assignment(1);
		else assignment(0);
		return;
	}

	i64 s, t;
	s = size() - 1, t = x.size() - 1;
	if(t <= 1) {
		div_short(x.abs(), rem);
		return;
	}

	u64 k = RADIX / (x.digits.back() + 1);
	Int a = abs() * k;
	Int b = x.abs() * k;

	DIGITS q(a.size(), 0);

	i64 u;
	u64 qp, rp;
	u64 fi = 1;
	while(true) {
		s = a.size() - 1 + fi;
		a.push_up(0);
		u = (a.at(s) < b.at(t))? s - t - 1 : s - t;
		qp = (a.at(s) * RADIX + a.at(s - 1)) / b.at(t);
		rp = (a.at(s) * RADIX + a.at(s - 1)) % b.at(t);
		while(rp < RADIX && qp * b.at(t - 1) > RADIX * rp + a.at(s - 2)) {
			qp--;
			rp += b.at(t);
		}

		q.at(u) = qp;
		Int xx;
		xx.to_radix_power(u);
		xx *= b;
		a -= qp * xx;
		if(a < 0) {
			--q.at(u);
			a += xx;
		}

		fi = 0;
		if(u == 0) {
			if(!rem) {
				this->digits = q;
				normalize();
			} else {
				assignment(a / k);
			}
			return;
		}
	}
}

bool Int::is_equal(const Int &x) const noexcept {
	if(size() != x.size()) return false;

	for(size_t i = 0; i < size(); i++) {
		if(this->digits.at(i) != x.digits.at(i)) return false;
	}

	return true;
}

bool Int::less_than(const Int &x, bool equal) const noexcept {
	if(size() < x.size()) return true;
	if(size() > x.size()) return false;

	for(size_t i = size(); i --> 0;) {
		if(this->digits.at(i) < x.digits.at(i)) return true;
		if(this->digits.at(i) > x.digits.at(i)) return false;
	}

	// equal
	return equal;
}

bool Int::greater_than(const Int &x, bool equal) const noexcept {
	return !less_than(x, !equal);
}

void Int::a_add(const Int &x) noexcept {
	if(this->sign == x.sign) {
		add(x);
	} else {
		if(sub(x) == MINUS) this->sign = x.sign;
	}
}

void Int::a_sub(const Int &x) noexcept {
	a_add(-x);
}

void Int::a_mul(const Int &x) noexcept {
	mul(x);
	this->sign = !(this->sign ^ x.sign);
}

void Int::a_div(const Int &x) noexcept {
	div(x);
	this->sign = !(this->sign ^ x.sign);
}

void Int::a_mod(const Int &x) noexcept {
	div(x, true);
}

bool Int::a_is_equal(const Int &x) const noexcept {
	if(is_equal(0) && x.is_equal(0)) return true;
	if(this->sign != x.sign) return false;
	return is_equal(x);
}

bool Int::a_less_than(const Int &x, bool equal) const noexcept {
	// +, - zero
	if(is_equal(0)) {
		if(x.sign == PLUS) return less_than(x, equal);
		else return greater_than(x, equal);
	}
	
	// different sign
	if(!x.is_equal(0)) {
		if(this->sign == MINUS && x.sign == PLUS) return true;
		if(this->sign == PLUS && x.sign == MINUS) return false;
	}

	// same sign
	if(this->sign == PLUS) return less_than(x, equal);
	return greater_than(x, equal);
}

bool Int::a_greater_than(const Int &x, bool equal) const noexcept {
	return !a_less_than(x, !equal);
}

namespace mlib {
namespace random {

// return random Int value in [0, x)
Int randint(const Int &x);

}
}

mlib::Int mlib::random::randint(const mlib::Int &x) {
	assert(x.size() != 0);

	std::random_device seed;
	std::mt19937 mt(seed());
	std::uniform_int_distribution<> rand(0, mlib::RADIX - 1);
	mlib::DIGITS dig(x.size());
	
	for(u64 i = 0; i < dig.size() - 1; i++) dig[i] = rand(mt);	

	std::uniform_int_distribution<> rand2(0, x.most_sig() - 1);
	dig[dig.size() - 1] = rand2(mt);

	mlib::Int ret(dig);
	ret.normalize();
	return ret;
}

namespace mlib {

bool is_prime(const Int &n);

}

// Miller-Rabin algorithm
bool mlib::is_prime(const mlib::Int &n) {
	if(n == 2) return true;
	if(n == 1) return false;
	if(n % 2 == 0) return false;

	mlib::Int d = (n - 1) / 2;
	while(d % 2 == 0) d /= 2;

	for(u64 i = 0; i < 100; i++) {
		auto a = mlib::random::randint(n - 2) + 1;
		auto x = mlib::mod_pow(a, d, n);
		auto t = d;

		while(t != n - 1 && x != 1 && x != n - 1) {
			x = mlib::mod_pow(x, mlib::Int(2), n);
			t *= 2;
		}

		if(x != n - 1 && x % 2 == 0) return false;
	}

	return true;
}

int main() {
	u64 n;
	std::cin >> n;
	for(u64 i = 0; i < n; i++) {
		mlib::Int x;
		std::cin >> x;
		std::cout << x << " ";
		if(mlib::is_prime(x)) std::cout << 1 << std::endl;
		else std::cout << 0 << std::endl;
	}
	return 0;
}
0