% Jake Bobowski
% August 23, 2017
% Created using MATLAB R2014a

% A function that can be called to find all the prime numbers between the
% positive integers a and b, where b > a.

function f = primes_adv(a, b) % Start the function.
    fclose('all'); % Close all files in case the function was previously interupted and left a file open.
    if rem(a, 1) == 0 && rem(b, 1) == 0 && a > 0 && b > 0 && b > a % Check that a and b are positive integers with b > a.
        primeMax = dlmread('primes_max.txt'); % Read the value of the largest number that this function previously checked to see if it was a prime.
        primeNums = dlmread('primes_list.txt'); % Read the list of known sequential prime numbers.
        if b <= primeMax % If b is less than primeMax, then there's no need to search, just extract the needed the prime numbers from the list.
            i = 1;
            z = 1; % A counter for the list of needed primes.
            while i <= length(primeNums) && primeNums(i) <= b
                if primeNums(i) >= a
                    piN(z) = primeNums(i); % Add prime numbers to piN if the prime is greater than or equal to 'a' and less than our equal to 'b'.
                    z = z + 1; % Increment the counter variable.
                end
                i = i + 1;
            end
            f = piN; % Output the list of primes.
        elseif b > primeMax % If 'b' is larger than the largest previously found prime, then we have to do some searching.
            counter = 0; 
            fileID = fopen('primes_list.txt','a'); % Open the file 'primes_list.txt' so that we can appended lines to it as we find new prime numbers.
            fmt = '%d\n'; % Just formatting for the new numbers that will be addded to the file of prime numbers.
            for i = (primeMax + 1):b % Start the search from at the interger that is one greater than the largest prime number in our list of known primes.  Stop the search when we reach 'b'.
                cnt = 0; % Counter variable used to stop the while loop.
                if (i ~= 1 && mod(i, 2) == 1 && mod(sum(int2str(i)-48), 3) ~= 0) && mod(i, 10) ~= 5 || i == 2 || i == 3 || i == 5 % We only need to consider odd integers,
                    % integers in which the sum of the digits of the number is not divisible by 3, integers that are not divisible by 5, and the numbers 2, 3, and 5 (the first few primes).
                    j = 4; % Start from the fourth item is primes_list (2, 3, 5, 7) which is the number 7.  We don't have to check 2, 3, 5 because the if statement above has already done this.
                    % Note that, this means prime_max.txt must be at least 7 and primes_list.txt must at least contain 2, 3, 5, 7.
                    while primeNums(j) < i/primeNums(j - 1) && cnt == 0 % Execute the loop while primeNums(j) < i/primeNums(j - 1) and while cnt = 0 (i.e. no factors of i have been found).
                        % This is the line that makes this search more
                        % effieicent than that of 'primes.m'.  First, it only checks if the known prime numbers are factors of i.  
                        % Second, it cuts off the search once primeNums(j) < i/primeNums(j - 1). The reason for dividing i by primeNums(j - 1) is the following:
                        % If primeNums(j) is not a factor of i, then it's guaranteed that i will not have any factors larger than i/primeNums(j - 1).   
                        % Take 25, for example.  j = 3 is not a factor of 25, so any number greater than 25/3 = 8.33 cannot be a factor of 25.  
                        if mod(i, primeNums(j)) == 0 % Check if i/primeNums(j) has a remainder.  If it doesn't, then i is not prime and cnt set equal to one.
                            cnt = 1;
                            primeMax = i; % Increase the value of the largest number checked for "primeness".
                        end
                        j = j + 1; % Increment j for the next iteration of the while loop.
                    end
                    if cnt == 0 % If the while loop completes and cnt = 0, then i is prime.
                        primeMax = i; % Increase the value of the largest number checked for "primeness".
                        primeNums(length(primeNums) + 1) = i; % Add i to the list of prime numbers.
                        dlmwrite('primes_max.txt', primeMax, 'precision', 12, 'delimiter', '\t') % Update primes_max.txt
                        fprintf(fileID, fmt, i); % Update primes_list.txt
                    end
                else
                    primeMax = i; % If it's not necessary to check if i is prime (i.e. i is even or a factor of 3 or a factor of 5), then increase the value of the largest number checked for "primeness".
                    % dlmwrite('primes_max.txt', primeMax, 'precision', 12, 'delimiter', '\t') 
                    % 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.
                end
            end
            fclose(fileID); % Close the open file (primes_list.txt).
            dlmwrite('primes_max.txt', primeMax, 'precision', 12, 'delimiter', '\t') % The search has ended, so update primes_max.txt.
            i = 1;
            z = 1;
            while i <= length(primeNums) && primeNums(i) <= b % Now that the search is done, extract the needed the prime numbers from the updated list.
                if primeNums(i) >= a
                    piN(z) = primeNums(i);
                    z = z + 1;
                end
                i = i + 1;
            end
            f = piN;
        end
    else
        f = 'ERROR: a and b in primes(a, b) must be positive integers with b > a.'; % Error messgae if invalid inputs were entered.
    end
end