# -*- coding: utf-8 -*-
"""
Created on Sun Sep 27 15:16:37 2020

@author: jbobowsk
"""

# This script calls the module 'modAdv.py'  which contains the function 
# 'primes_adv()' to find all of the prime numbers between 'a'
# and 'b'.  It's an "advanced" version of the function 'primes()'.  There's
# nothing different about how we're calling the function.  It's just that
# what's executed within the actual function is slightly more sophisticated
# than what's in 'primes()'.

# 'primes_adv()' maintains a file of the sequential prime numbers that it
# has previously found.  Then, if a user asks for prime numbers that it has
# previously found, it just consults the list and tells the user the
# numbers they want to know without doing any searching.  Only if the user
# has asked for prime numbers that it has never found before, does it
# actually implement a search.

# In addition, 'primes_adv()' uses this list of prime numbers to
# intelligently search for new prime numbers. To check if n is a prime
# number, it is only necessary to check if it is a factor of any of the prime
# numbers less than n.  For example, we don't need to check if 27 is a
# factor of n.  If it is, then 3 is also a factor of n and we would have
# already determined that n is not prime.  By eliminating as many factors
# from our search as possible, we can greatly enhance the efficiency of our
# search for new prime numbers.

# As of Sept. 28, 2020, all of the prime number between 1 and about 1.317e9 
# have been found and sequentially listed by 'primes_adv()'.  So far, the 
# function has sequentially listed 66,051,364 prime numbers.

# Note that this is no where close to the largest prime number ever found.
# As I type this, the largest know prime number is 2^(57,885,161) - 1.  A
# number that is a ridiculous 17,425,170 digits long.  However, people do
# not know the sequential list of primes up to this number.

# Note that, after creating this script, I read that people use much faster
# sieve programs to find sequential prime numbers.

# The function that this script calls is named 'primes_adv(a, b)'. It finds all
# of the prime numbers between input integers a and b.  The code saved in
# 'modAdv.py' is shown below without any explanations.  If you open the
# file 'modAdv.py', you will find some additional comments that help
# explain the purpose of the various lines of code.

# To run this function, you will need to have the files 'modAdv.py',
# 'primes_max.txt', and 'primes_list.txt' in the same directory as the
# script that calls 'primes_adv()'.  'primes_list.txt' is inside
# 'primes_list.zip' which can be downloaded from my website.  With a list
# all prime numbers less than 1.317e9.  When unzipped, 'primes_list.txt' is 
# about 650 Mb.

#def primes_adv(a, b):
#     piN = []
#     if a % 1 == 0 and b % 1 == 0 and a > 0 and b > 0 and b > a:
#         import numpy as np 
#         import pandas as pd
#         # with open('primes_list.txt', 'r') as f:
#         #     lineList = f.readlines()
#         # with open("primes_list.txt", "ab") as f:
#         #        if len(lineList[-1].split('\n')) == 1:
#         #             f.write(b"\n")
#         primeMax = int(np.loadtxt("primes_max.txt"))
#         primeNums = pd.read_csv("./primes_list.txt", delimiter="\n", header = None).values[:, 0]
#         if b <= primeMax:
#             i = 0
#             while i <= len(primeNums) - 1 and primeNums[i] <= b:
#                 if primeNums[i] >= a:
#                     piN = piN + [primeNums[i]]
#                 i += 1
#             f = np.array(piN) 
#             n = len(f) 
#         elif b > primeMax:
#              for i in range(primeMax + 1, int(b) + 1):
#                  cnt = 0
#                  if i != 1 and i % 2 == 1 and sum(map(int, str(i))) % 3 != 0\
#                  and i % 10 != 5 or i == 2 or i == 3 or i == 5:
#                      j = 3
#                      while primeNums[j] < i/primeNums[j - 1] and cnt == 0:
#                          if i % primeNums[j] == 0: 
#                              cnt = 1
#                              primeMax = i
#                          j += 1
#                      if cnt == 0:
#                          primeMax = i
#                          primeNums = np.append(primeNums, i)
#                          np.savetxt('primes_max.txt', np.array([primeMax]), fmt='%i', delimiter='\n')
#                          with open("primes_list.txt", "ab") as f:
#                              np.savetxt(f, np.array([i]), fmt='%i')
#                  else:
#                      primeMax = i
#                      # np.savetxt('primes_max.txt', np.array([primeMax]), fmt='%i', delimiter='\n')
#                      # We could update primes_max.txt, but I commented out the line above to save time (i.e. not necessary to constantly update primes_max.txt.
#              np.savetxt('primes_max.txt', np.array([primeMax]), fmt='%i', delimiter='\n')
#              i = 0
#              while i <= len(primeNums) - 1 and primeNums[i] <= b:
#                  if primeNums[i] >= a:
#                      piN = piN + [primeNums[i]]
#                  i += 1
#              f = np.array(piN) 
#              n = len(f) 
#     else:
#         f = 'ERROR: a and b in primes(a, b) must be positive integers with b > a.'
#         n = -1 
#     return f, n                 

from datetime import datetime
print(datetime.now())
n_min = 20e6
n_max = 21e6;
import modAdv
xx, x = modAdv.primes_adv(n_min, n_max)
print(datetime.now())
print('A list of the', x, 'sequential prime numbers between', int(n_min),\
      'and', int(n_max))