#ifndef INCLUDED_BOBCAT_PRIMEFACTORS_
#define INCLUDED_BOBCAT_PRIMEFACTORS_

#include <iostream>
#include <vector>
#include <fstream>
#include <memory>

#include <bobcat/bigint>

namespace FBB
{

struct PrimeFactors
{
    struct PrimePower
    {
        BigInt prime;
        size_t power;
    };

    using Factors = std::vector<PrimePower>;

    private:
        using BigIntVector = std::vector<BigInt>;
        using ConstIterator = BigIntVector::const_iterator;

        class iterator
        {
            protected:
                enum InputMode
                {
                    PRIMES,
                    SENTINEL
                };
                int d_mode;

                BigIntVector *d_primes;
                ConstIterator d_iterator;
                BigInt d_lastPrime;
                BigIntVector d_sentinel;

            public:
                virtual ~iterator();
                iterator();
                iterator(BigIntVector &primes);     // 2

                iterator &operator++();                         //    opinc.f
                void setSentinel(BigInt const &sentinel);               // .f
                BigInt const &operator*() const;                //   opstar.f
                void nextPrime();                                       // .f
                BigInt const &lastPrime() const;                        // .f
                bool atSentinel() const;                                // .f

            protected:
                void checkInitialPrimes(BigIntVector const &primes) const;
                bool isComposite(BigInt const &candidate);

            private:
                virtual iterator &operatorPreInc();
                virtual void next();
        };

        class iteratorStream;

        BigInt d_value;
        BigInt d_last;

        std::shared_ptr<iterator> d_iterator;

        Factors d_factors;

    public:
        PrimeFactors(BigIntVector &primes);
        PrimeFactors(std::string const &name = "", size_t blockSize = 1000);
        PrimeFactors(PrimeFactors const &other) = delete;

        Factors const &factorize(BigInt const &value);

    private:
        void reduce(BigInt const &prime);
        void availablePrimes();
        void addPrimes();
};

} // FBB
#endif
