/****************************************************
    PrimeCPE.hpp

    This program calculates the number of prime numbers found in a range.  
    It compares the use of a BitMap class I wrote vs that of a vector to
    track the number of prime numbers found in the given range.

****************************************************/


#ifndef PRIMECPE_HEADER
#define PRIMECPE_HEADER

#include <stdexcept>

#include "CPE.hpp"

//everything stuck in header for convenience, NOT considered 'good practice'!

class BitMap{
    unsigned int m_intHowHigh2Go, m_intEntries, m_intBitsPerEntry;
    unsigned long *m_lptrBitMask;
public:
    BitMap(unsigned int HowHighToLook){
        m_intHowHigh2Go = HowHighToLook;
        m_intBitsPerEntry = sizeof(unsigned long) * 8;
        m_intEntries = m_intHowHigh2Go / m_intBitsPerEntry;
        m_intEntries++;
        m_lptrBitMask = new unsigned long[m_intEntries];;
        for (unsigned int i=0; i<m_intEntries; i++)
            m_lptrBitMask[i] = 0;
    };
    ~BitMap(){
        delete m_lptrBitMask;;
    };
    inline void setBit(unsigned int bit2set, bool set){//if set = true, bit = 1
        if (bit2set > m_intHowHigh2Go)
            throw std::runtime_error("Trying to set a bit that is out of range");
        int entry = bit2set / m_intBitsPerEntry;
        int offset = bit2set % m_intBitsPerEntry;
        unsigned long setBit = 1;
        setBit <<= offset - 1;
        if (set){
            m_lptrBitMask[entry] |= setBit;
        }else{
            m_lptrBitMask[entry] &= ~setBit;
        }
    };
    inline bool getBit(unsigned int bit2get){//return true if 1
        if (bit2get > m_intHowHigh2Go)
            throw std::runtime_error("Trying to get a bit that is out of range");
        int entry = bit2get / m_intBitsPerEntry;
        int offset = bit2get % m_intBitsPerEntry;
        int check = 1;
        check <<= offset-1;
        if (m_lptrBitMask[entry] & check) return true;
        return false;
    };
};

class PrimeCPE : CCPE{
    class Args{
    public:
        unsigned short max;
        bool doBitMap;
    };

    static int primeBitMap(unsigned short Max2Check){
        int i, j;
        BitMap myBitMap(Max2Check);
        bool isPrime;

        //set 2 manually, less logic this way
        myBitMap.setBit(2, true);
        for (i=1; i<=Max2Check; i++){
            isPrime = true;
            if (i%2 == 0) continue;//no need to check even numbers
            for (j=2; j<i; j++){
                if (myBitMap.getBit(j)){
                    if (i%j == 0){
                        isPrime = false;
                        break;
                    }
                }
            }
            if (isPrime)
                myBitMap.setBit(i, true);
        }

        //count prime numbers:\n";
        for (i=2, j=0; i<Max2Check; i++){
            if (myBitMap.getBit(i)) j++;
        }
        return j;
    };

    static int primeVector(unsigned short Max2Check){
        int i, j;
        std::vector < bool > myVect(Max2Check);
        bool isPrime;

        for (i=0; i<Max2Check; i++) myVect[i] = false;

        //set 2 manually, less logic this way
        myVect[2] = true;
        for (i=1; i<=Max2Check; i++){
            isPrime = true;
            if (i%2 == 0) continue;//no need to check even numbers
            for (j=2; j<i; j++){
                if (myVect[j]){
                    if (i%j == 0){
                        isPrime = false;
                        break;
                    }
                }
            }
            if (isPrime)
               myVect[i] = true;
        }

        //count prime numbers:\n";
        for (i=2, j=0; i<Max2Check; i++){
            if (myVect[i]) j++;
        }
        return j;
    };

    static double factLoop(double val){
        double tmp = val;
        while (val > 1){
            val -= 1;
            tmp *= val;
        }
        return tmp;
    };

    //    function to run the tests on MUST ALWAYS RETURN A VOID *
    //    AND MUST ALWAYS TAKE A VOID * AS AN ARG
    static void * dummy(void *args){
        static int tmp;
        Args *myArgs = (Args *) args;
        if (myArgs->doBitMap)
            tmp = primeBitMap(myArgs->max);
        else
            tmp = primeVector(myArgs->max);
        return (void*)&tmp;
    };
public:
    PrimeCPE(unsigned short max){//ensure that we don't max out RAM (on a modern machine, anyway)
        void *retVal;
        Args args;

        args.max = max;
        args.doBitMap = true;
        retVal = test_it(dummy, (void *)&args, 3);
        std::cout << "Prime count via BitMap: " << *((int*)retVal) << std::endl;
        std::cout << "Overhead for BitMap: " << GetMinOverhead() << std::endl;
        std::cout << "CPE for BitMap: " << GetMinExecution() << std::endl;

        args.doBitMap = false;
        retVal = test_it(dummy, (void *)&args, 3);
        std::cout << "\nPrime count via vector: " << *((int*)retVal) << std::endl;
        std::cout << "Overhead for vector: " << GetMinOverhead() << std::endl;
        std::cout << "CPE for vector: " << GetMinExecution() << std::endl;
    };
};

#endif //PRIMECPE_HEADER


