#include <bits/stdc++.h>
using namespace std;
// Miller-Rabin Primality Test
bool millerRabin(long long n, long long a) {
if (n <= 1) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
// Write n-1 as d * 2^r
long long d = n - 1;
int r = 0;
while (d % 2 == 0) {
d /= 2;
r++;
}
// Modular exponentiation: a^d mod n
auto modPow = [](long long base, long long exp, long long mod) {
long long result = 1;
base %= mod;
while (exp > 0) {
if (exp & 1) result = (__int128)result * base % mod;
base = (__int128)base * base % mod;
exp >>= 1;
}
return result;
};
long long x = modPow(a, d, n);
if (x == 1 || x == n - 1) return true;
for (int i = 0; i < r - 1; i++) {
x = (__int128)x * x % n;
if (x == n - 1) return true;
}
return false;
}
bool isPrime(long long n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
// Check small primes first
vector<int> smallPrimes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
for (int p : smallPrimes) {
if (n == p) return true;
if (n % p == 0) return false;
}
// Miller-Rabin with deterministic witnesses for n < 3,317,044,064,679,887,385,961,981
vector<long long> witnesses = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
for (long long a : witnesses) {
if (a >= n) continue;
if (!millerRabin(n, a)) return false;
}
return true;
}
// Method 1: Simple backward iteration (Recommended for most cases)
long long largestPrimeSimple(long long n) {
if (n <= 1) return -1;
if (n == 2) return 2;
// Start from n (or n-1 if n is even)
if (n % 2 == 0) n--;
for (long long i = n; i >= 2; i -= 2) {
if (isPrime(i)) return i;
}
return 2; // Should never reach here
}
// Method 2: Optimized with gap estimation
long long largestPrimeOptimized(long long n) {
if (n <= 1) return -1;
if (n == 2) return 2;
// For small numbers, use simple method
if (n <= 1000) return largestPrimeSimple(n);
// Estimate maximum gap (Bertrand's postulate: gap < n for large n)
// In practice, gaps are much smaller: O(log n)
long long maxGap = min(n, (long long)(log(n) * log(n) * 10));
long long start = (n % 2 == 0) ? n - 1 : n;
for (long long i = start; i >= max(2LL, n - maxGap); i -= 2) {
if (isPrime(i)) return i;
}
// Fallback to simple method if not found in estimated range
return largestPrimeSimple(n - maxGap);
}
// Method 3: Using precomputed small primes for trial division
vector<int> smallPrimes;
void generateSmallPrimes() {
const int LIMIT = 100000;
vector<bool> sieve(LIMIT + 1, true);
sieve[0] = sieve[1] = false;
for (int i = 2; i * i <= LIMIT; i++) {
if (sieve[i]) {
for (int j = i * i; j <= LIMIT; j += i) {
sieve[j] = false;
}
}
}
for (int i = 2; i <= LIMIT; i++) {
if (sieve[i]) smallPrimes.push_back(i);
}
}
bool isPrimeFast(long long n) {
if (n <= 1) return false;
if (n <= smallPrimes.back()) {
return binary_search(smallPrimes.begin(), smallPrimes.end(), n);
}
// Trial division with small primes
for (int p : smallPrimes) {
if ((long long)p * p > n) break;
if (n % p == 0) return false;
}
// Miller-Rabin for larger numbers
return isPrime(n);
}
long long largestPrimeFast(long long n) {
if (smallPrimes.empty()) generateSmallPrimes();
if (n <= 1) return -1;
if (n == 2) return 2;
if (n % 2 == 0) n--;
for (long long i = n; i >= 2; i -= 2) {
if (isPrimeFast(i)) return i;
}
return 2;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
// Test cases
vector<long long> testCases = {
10, 100, 1000, 10000, 100000, 1000000,
436273036, 436273290, 436273036
};
cout << "Testing largest prime finder:\n";
cout << "N\t\tLargest Prime <= N\n";
cout << "--------------------------------\n";
for (long long n : testCases) {
long long result = largestPrimeSimple(n);
cout << n << "\t\t" << result << "\n";
}
// Interactive mode
cout << "\nEnter numbers to find largest prime (0 to exit):\n";
long long n;
while (cin >> n && n != 0) {
if (n < 2 || n > 1000000000) {
cout << "Invalid input. Range: 2 <= n <= 10^9\n";
continue;
}
auto start = chrono::high_resolution_clock::now();
long long result = largestPrimeSimple(n);
auto end = chrono::high_resolution_clock::now();
auto duration = chrono::duration_cast<chrono::microseconds>(end - start);
cout << "Largest prime <= " << n << " is: " << result;
cout << " (Time: " << duration.count() << " microseconds)\n";
}
return 0;
}
/*
Time Complexity:
- Average case: O(log n) iterations × O(log³ n) per primality test = O(log⁴ n)
- Worst case: O(√n) for very unlucky gaps
Space Complexity: O(1) for basic version, O(√n) for optimized with small primes
For n = 10^9:
- Expected iterations: ~23 (average prime gap)
- Time per test: ~1ms
- Total time: Usually < 50ms
*/