//myClasses/headers/mee/numberAlgorithms.hpp //Matthew Ellison // Created: 07-02-21 //Modified: 07-02-21 //This file contains declarations of functions I have created to manipulate numbers /* Copyright (C) 2021 Matthew Ellison This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #ifndef MEE_NUMBER_ALGORITHMS_HPP #define MEE_NUMBER_ALGORITHMS_HPP #include #include #include #include #include #include #include #include "Generator.hpp" namespace mee{ //This function determines whether the number passed into it is a prime template bool isPrime(T possiblePrime){ if(possiblePrime <= 3){ return possiblePrime > 1; } else if(((possiblePrime % 2) == 0) || ((possiblePrime % 3) == 0)){ return false; } for(T cnt = 5;(cnt * cnt) <= possiblePrime;cnt += 6){ if(((possiblePrime % cnt) == 0) || ((possiblePrime % (cnt + 2)) == 0)){ return false; } } return true; } //This is a function that returns all the primes <= goalNumber and returns a vector with those prime numbers template std::vector getPrimes(T goalNumber){ std::vector primes; bool foundFactor = false; //If the number is 1, 0, or a negative number return an empty vector if(goalNumber <= 1){ return primes; } else{ primes.push_back(2); } //We can now start at 3 and skip all of the even numbers for(T possiblePrime = 3;possiblePrime <= goalNumber;possiblePrime += 2){ //Step through every element in the current primes. If you don't find anything that divides it, it must be a prime itself uint64_t topPossibleFactor = ceil(sqrt(possiblePrime)); for(uint64_t cnt = 0;(cnt < primes.size()) && (primes.at(cnt) <= topPossibleFactor);++cnt){ if((possiblePrime % primes.at(cnt)) == 0){ foundFactor = true; break; } } //If you didn't find a factor then it must be prime if(!foundFactor){ primes.push_back(possiblePrime); } //If you did find a factor you need to reset the flag else{ foundFactor = false; } } std::sort(primes.begin(), primes.end()); return primes; } //This function returns a vector with a specific number of primes template std::vector getNumPrimes(T numberOfPrimes){ std::vector primes; primes.reserve(numberOfPrimes); //Saves cycles later bool foundFactor = false; //If the number is 1, 0, or a negative number return an empty vector if(numberOfPrimes <= 1){ return primes; } //Otherwise 2 is the first prime number else{ primes.push_back(2); } //Loop through every odd number starting at 3 until we find the requisite number of primes //Using possiblePrime >= 3 to make sure it doesn't loop back around in an overflow error and create an infinite loop for(T possiblePrime = 3;(primes.size() < numberOfPrimes) && (possiblePrime >= 3);possiblePrime += 2){ //Step through every element in the current primes. If you don't find anything that divides it, it must be a prime itself uint64_t topPossibleFactor = ceil(sqrt(possiblePrime)); for(uint64_t cnt = 0;(cnt < primes.size()) && (primes.at(cnt) <= topPossibleFactor);++cnt){ if((possiblePrime % primes.at(cnt)) == 0){ foundFactor = true; break; } } //If you didn't find a factor then it must be prime if(!foundFactor){ primes.push_back(possiblePrime); } //If you did find a factor you need to reset the flag else{ foundFactor = false; } } //The numbers should be in order, but sort them anyway just in case std::sort(primes.begin(), primes.end()); return primes; } //This function returns all prime factors of a number template std::vector getFactors(T goalNumber){ //Get all the prime numbers up to sqrt(number). If there is a prime < goalNumber it will have to be <= sqrt(goalNumber) std::vector primes = getPrimes((T)ceil(sqrt(goalNumber))); //Make sure you are getting a vector of the correct type std::vector factors; //Need to step through each prime and see if it is a factor of the number for(int cnt = 0;cnt < primes.size();){ if((goalNumber % primes[cnt]) == 0){ factors.push_back(primes[cnt]); goalNumber /= primes[cnt]; } else{ ++cnt; } } //If it didn't find any factors in the primes the number itself must be prime if(factors.size() == 0){ factors.push_back(goalNumber); goalNumber /= goalNumber; } ///Should add some kind of error throwing inc ase the number != 1 after searching for all prime factors return factors; } //This is a function that gets all the divisors of num and returns a vector containing the divisors template std::vector getDivisors(T num){ std::vector divisors; //Holds the number of divisors //Ensure the parameter is a valid number if(num <= 0){ return divisors; } else if(num == 1){ divisors.push_back(1); return divisors; } //You only need to check up to sqrt(num) T topPossibleDivisor = ceil(sqrt(num)); for(uint64_t possibleDivisor = 1;possibleDivisor <= topPossibleDivisor;++possibleDivisor){ //Check if the counter evenly divides the number //If it does the counter and the other number are both divisors if((num % possibleDivisor) == 0){ //We don't need to check if the number already exists because we are only checking numbers <= sqrt(num), so there can be no duplicates divisors.push_back(possibleDivisor); //We still need to account for sqrt(num) being a divisor if(possibleDivisor != topPossibleDivisor){ divisors.push_back(num / possibleDivisor); } //Take care of a few occations where a number was added twice if(divisors.at(divisors.size() - 1) == (possibleDivisor + 1)){ ++possibleDivisor; } } } //Sort the vector for neatness std::sort(divisors.begin(), divisors.end()); //Return the vector of divisors return divisors; } //These functions return the numth Fibonacci number template T getFib(const T num){ //Make sure the number is within bounds if(num <= 2){ return 1; } //Setup the variables T fib = 0; T tempNums[3]; tempNums[0] = tempNums[1] = 1; //Do the calculation unsigned int cnt; for(cnt = 2;(cnt < num) && (tempNums[(cnt - 1) % 3] >= tempNums[(cnt - 2) % 3]);++cnt){ tempNums[cnt % 3] = tempNums[(cnt + 1) % 3] + tempNums[(cnt + 2) % 3]; } fib = tempNums[(cnt - 1) % 3]; //Transfer the answer to permanent variable. -1 to account for the offset of starting at 0 return fib; } //This function returns a vector that includes all Fibonacci numbers <= num template std::vector getAllFib(const T num){ std::vector fibList; //Make sure the number is within bounds if(num <= 1){ fibList.push_back(1); return fibList; } else{ //Make sure to add the first 2 elements fibList.push_back(1); fibList.push_back(1); } //Setup the variables T fib = 0; T tempNums[3]; tempNums[0] = tempNums[1] = 1; //Do the calculation and add each number to the vector for(T cnt = 2;(tempNums[(cnt - 1) % 3] <= num) && (tempNums[(cnt - 1) % 3] >= tempNums[(cnt - 2) % 3]);++cnt){ tempNums[cnt % 3] = tempNums[(cnt + 1) % 3] + tempNums[(cnt + 2) % 3]; fibList.push_back(tempNums[cnt % 3]); } //If you triggered the exit statement you have one more element than you need fibList.pop_back(); //Return the vector that contains all of the Fibonacci numbers return fibList; } //This function converts a number to its binary equivalent template std::string toBin(T num){ //Convert the number to a binary string std::string fullString = std::bitset(num).to_string(); //Remove leading zeros int loc = 0; for(loc = 0;(loc < fullString.size()) && (fullString[loc] == '0');++loc); std::string trimmedString = fullString.substr(loc); if(trimmedString == ""){ trimmedString = "0"; } return trimmedString; } //Return the factorial of the number passed in template T factorial(T num){ T fact = 1; for(T cnt = 1;cnt <= num;++cnt){ fact *= cnt; } return fact; } //A generator for prime numbers template mee::Generator sieveOfEratosthenes(){ //Return 2 the first time, this lets us skip all even numbers later co_yield 2; int num = 0; //Dictionary to hold the primes we have already found std::unordered_map> dict; //Start checking for primes with the number 3 and skip all even numbers for(T possiblePrime = 3;true;possiblePrime += 2){ //If possiblePrime is in the dictionary it is a composite number if(dict.contains(possiblePrime)){ //Move each number to its next odd multiple for(T num : dict[possiblePrime]){ dict[possiblePrime + num + num].push_back(num); } //We no longer need this, free the memory dict.erase(possiblePrime); } //If possiblePrime is not in the dictionary it is a new prime number //Return it and mark its next multiple else{ co_yield possiblePrime; dict[possiblePrime * possiblePrime].push_back(possiblePrime); } } } //An alternate to sieveOfEratosthenes that uses map instead of unordered_map for greater compatibility but lower performance template mee::Generator sieveOfEratosthenesAlt(){ //Return 2 the first time, this lets us skip all even numbers later co_yield 2; int num = 0; //Dictionary to hold the primes we have already found std::map> dict; //Start checking for primes with the number 3 and skip all even numbers for(T possiblePrime = 3;true;possiblePrime += 2){ //If possiblePrime is in the dictionary it is a composite number if(dict.contains(possiblePrime)){ //Move each number to its next odd multiple for(T num : dict[possiblePrime]){ dict[possiblePrime + num + num].push_back(num); } //We no longer need this, free the memory dict.erase(possiblePrime); } //If possiblePrime is not in the dictionary it is a new prime number //Return it and mark its next multiple else{ co_yield possiblePrime; dict[possiblePrime * possiblePrime].push_back(possiblePrime); } } } } #endif //MEE_NUMBER_ALGORITHMS_HPP