use machine_prime::{is_prime, is_prime_wc};

//use machine_prime::{is_prime_128, is_prime_wc_128};

const fn pi(x: u64) -> u64 {
    let mut inc = 0u64;
    let mut picount = 0u64;
    loop {
        inc += 1;
        if inc == x {
            return picount;
        }
        if is_prime(inc) {
            picount += 1;
        }
    }
}

const fn prng(mut x: u64) -> u64{
    x ^= x.wrapping_shr(12);
    x ^= x.wrapping_shl(25);
    x ^= x.wrapping_shr(27);
    x.wrapping_mul(0x2545F4914F6CDD1D)
}

fn sequential() -> u64{

    const DELTA: u64 = 10_000_000;
    const MAX: u64 = u64::MAX;
    
    let mut count = 0;
    for i in MAX - DELTA..MAX {
        if is_prime(i) {
            count += 1;
        }
    }
    count
}

fn ssmr() -> u64{
  const DELTA : u64 = 100_000_000;
  const MAX: u64 = 1u64<<47;
  let mut count = 0;
    for i in MAX - DELTA..MAX {
        if is_prime(i) {
            count += 1;
        }
    }
    count
}

fn pseudorandom() -> u64{
   const ITERATIONS : u64 = 100_000_000;
   let mut val = u64::MAX;
   let mut count = 0u64;
   for _ in 0..ITERATIONS{
    val=prng(val);
    if is_prime(val){
      count+=1;
    }
   }
   count
}

fn strong() -> u64{
   const ITERATIONS : u64 = 10_000_000;
   const PRIME : u64 = u64::MAX-58;
   let mut count = 0u64;
   
   for _ in 0..ITERATIONS{
     if is_prime_wc(PRIME){
       count+=1;
     }
   }
   count
}

/*
fn strong128() -> u64{

   const ITERATIONS : u64 = 1_000_000;
   // Prime with Frobenius index of 5, the hard general case
   const QFT_5 : u128 = u128::MAX-158;//340282366920938463463374607431768211283;//u128::MAX-158;
   // Prime with index of -1, resulting in a particularly fast Gaussian exponentiation
   //const QFT_GAUSSIAN : u128 = 340282366920938463463374607431768211283;
   // Prime with index of 2, using a slightly faster exponentiation routine
   //const QFT_2 : u128 = 340282366920938463463374607431768211181;
   
   let mut count = 0u64;
   
   for _ in 0..ITERATIONS{
     if is_prime_wc_128(QFT_5){
       count+=1;
     }
   }
   count
}

fn sequential128() -> u64{

    const DELTA: u128 = 10_000_000;
    const MAX: u128 = u128::MAX;
    
    let mut count = 0;
    for i in MAX - DELTA..MAX {
        if is_prime_128(i) {
            count += 1;
        }
    }
    count
}
*/
fn mont_one(n: u64) -> u64{
  // #[inline]
//pub const fn one_mont_128(n: u128) -> u128 {
     //n.wrapping_neg() % n
     (u64::MAX%n).wrapping_add(1)
    //(u128::MAX % n).wrapping_add(1)
//}
}

pub const fn two_mont(one: u64, n: u64) -> u64 {
   // let two = one.wrapping_add(one);
    let two = one*2;    // Cannot overflow
     if two > n {
//        return two.wrapping_sub(n);
        return two - n;
     }
     two
 }

fn mont_one_bench() -> u64{
   const ITERATIONS : u64 = 100_000_000;
   let mut val = u64::MAX;
   let mut count = 0u64;
   for _ in 0..ITERATIONS{
    val=prng(val);
    if two_mont(mont_one(val),val)&1==1{
      count+=1;
    }
   }
   count
}
/**/

fn timer<F: Fn()-> u64>(func: F){
    let mut max = 0;
    
    let mut min = u64::MAX;
    
    let mut acc = 0u64;
    
    const ITERATIONS : usize = 10;
    
     for _ in 0..ITERATIONS{
        let start = std::time::Instant::now();
        let res = func(); 
        let stop = start.elapsed().as_millis() as u64;
        
        if stop < min{
            min = stop;
        }
        
        if stop > max{
           max = stop;
        }
        acc+=stop;    
        }
        println!("min: {} max: {} mean: {}",min as f64/1000f64,max as f64/1000f64,acc  as f64/(ITERATIONS*1000) as f64);
}

fn filter_composite_utf8(x: &str){
   use std::io::{BufRead,Read};
   let f = std::fs::File::open(x).unwrap();
   let mut r = std::io::BufReader::new(f);
   
   for i in r.lines(){
     match i.unwrap().parse::<u64>(){
      Ok(num) => assert!(!is_prime(num)),
      Err(mess) => println!("{}",mess),
     }
   }
}
/*
fn filter_composite_utf8_128(x: &str){
   use std::io::{BufRead,Read};
   let f = std::fs::File::open(x).unwrap();
   let mut r = std::io::BufReader::new(f);
   
   for i in r.lines(){
     match i.unwrap().parse::<u128>(){
      Ok(num) => assert!(!is_prime_128(num)),
      Err(mess) => println!("{}",mess),
     }
   }
}
*/
// Benchmarking the average in the strongest interval
// Real implementation will likely be faster


fn main() {

   /* 
    let start = std::time::Instant::now();
    const PRIME_10K: u64 = pi(10_000);
    let stop = start.elapsed();
    println!(
        "{} primes under 10,000 calculated at compile-time {:?}",
        PRIME_10K, stop
    );
    
    println!("{}",sequential());
    */
  timer(sequential);
  //timer(strong);
  timer(pseudorandom);
  timer(ssmr);
  
  //timer(sequential128);
  //timer(strong128);
  
  /**/
  //println!("{}",sequential128());
  //  filter_composite_utf8("/home/jasory/Projects/factor/sprp2.txt");
   
   //filter_composite_utf8_128("/home/jasory/carmichael/sortedpseudoprimes");
  //timer(mont_one_bench)
  //timer(pseudorandom);
  
}
