I previously posted a thread asking for a general review of one of my first projects related to programming (C++ Prime Number Library) and received very good help and new perspectives. After this, I decided to incorporate my newly acquired knowledge of templates into the same project. I want to know if this implementation follows good standards or if I am making big mistakes regarding the template implementation or the source code in general.
inpalprime.hpp:
#ifndef inpalprime_hpp
#define inpalprime_hpp
#include <vector>
#include <string>
namespace inpal
{
template <class T> class prime
{
public:
static T max_prime(T n);
static T count_primes(T n);
static double prime_density(double h);
static bool prime_test(T p);
static bool twin_test(T p);
static bool cousin_test(T p);
static bool sexy_test(T p);
static T max_palprime(T n);
static T max_factor(T f);
static T count_factors(T f);
private:
static std::vector<bool> prime_sieve(T m);
static std::vector<T> factorizer(T f);
static bool pal_test(T n);
};
}
#endif /* inpalprime_hpp */
inpalprime.cpp:
#include "inpalprime.hpp"
#include <cmath>
#include <vector>
#include <string>
#include <algorithm>
template <class T> T inpal::prime<T>::max_prime(T n)
{
auto primes = prime_sieve(n);
auto it = std::find(primes.rbegin(), primes.rend(), true);
return primes.size()-std::distance(primes.rbegin(), it)-1;
}
template <class T> T inpal::prime<T>::count_primes(T n)
{
auto primes = prime_sieve(n);
return std::count(primes.begin(), primes.end(), true);
}
template <class T> double inpal::prime<T>::prime_density(double h)
{
return count_primes(h)/h;
}
template <class T> bool inpal::prime<T>::prime_test(T p)
{
return p == max_prime(p);
}
template <class T> bool inpal::prime<T>::twin_test(T p)
{
auto primes = prime_sieve(p+2);
return p!=2 && primes[primes.size()-3] && (primes[primes.size()-1] || primes[primes.size()-5]);
}
template <class T> bool inpal::prime<T>::cousin_test(T p)
{
auto primes = prime_sieve(p+4);
return p!=2 && primes[primes.size()-5] && (primes[primes.size()-1] || primes[primes.size()-9]);
}
template <class T> bool inpal::prime<T>::sexy_test(T p)
{
auto primes = prime_sieve(p+6);
return (p!=2 && p!=3) && primes[primes.size()-7] && (primes[primes.size()-1] || primes[primes.size()-13]);
}
template <class T> T inpal::prime<T>::max_palprime(T n)
{
auto primes = prime_sieve(n);
for(std::size_t i=n; i>=2; --i) if(primes[i] && pal_test(i)) return i;
return 2;
}
template <class T> T inpal::prime<T>::max_factor(T f)
{
return factorizer(f).back();
}
template <class T> T inpal::prime<T>::count_factors(T f)
{
return factorizer(f).size();
}
template <class T> std::vector<bool> inpal::prime<T>::prime_sieve(T m)
{
std::vector<bool> p_test(m+1, false);
//defines square root of m
T root = ceil(sqrt(m));
//sieve axioms
for(T x=1; x<=root; x++)
{
for(T y=1; y<=root; y++)
{
T i= (4*x*x)+(y*y);
if (i<=m && (i%12==1 || i%12==5))
{
p_test[i].flip();
}
i=(3*x*x)+(y*y);
if(i<=m && i%12==7)
{
p_test[i].flip();
}
i=(3*x*x)-(y*y);
if(x>y && i<=m && i%12==11)
{
p_test[i].flip();
}
}
}
//marks 2,3,5 and 7 as prime numbers
p_test[2]=p_test[3]=p_test[5]=p_test[7]=true;
//marks all multiples of primes as non primes
for(T r=5; r<=root; r++)
{
if(p_test[r])
{
for(T j=r*r; j<=m; j+=r*r)
{
p_test[j]=false;
}
}
}
return p_test;
}
template <class T> std::vector<T> inpal::prime<T>::factorizer(T f)
{
std::vector<T> p_fac;
T p = 2;
//trial division
while(p<=f)
{
while(f%p==0)
{
p_fac.push_back(p);
f=f/p;
}
p += p==2 ? 1 : 2;
}
return p_fac;
}
template <class T> bool inpal::prime<T>::pal_test(T n)
{
//converts n to a string
std::string rev = std::to_string(n);
//checks if the reverse of rev is equal to rev
if(std::equal(rev.begin(), rev.begin()+rev.size()/2, rev.rbegin()))
{
return true;
}
return false;
}
template class inpal::prime<unsigned long>;
template class inpal::prime<unsigned long long>;
template class inpal::prime<long>;
template class inpal::prime<long long>;