Prime engine: generation, primality, factorization I've just written my first C# class, so I'm looking for some feedback before I go and write more code. The goal of the code is to provide a Singleton-like class to deal with common operations on the set of prime numbers that I will need to complete projecteuler.net problems. 

I'm interested in feedback for all aspects of the implementation (short of the actual prime generation algorithms, the 6*i +/- 1 method was used solely for its simplicity). 

I'm also interested in feedback about how the interfaces are split up. I split these up into slices of functionality in an attempt to allow future implementation changes. I am planning to use DI (e.g. SimpleInjector) to bind the singleton instance to each of the interfaces, but I'm seriously starting to doubt this pattern.

I'm less interested in feedback about the selections of methods, I just chose a relatively minimal subset of methods that I know that I will need at some point - more methods will probably be added as needed.

Interfaces

public interface IPrimeGenerator
{
    IEnumerable&lt;long&gt; PrimesUntilValue(long value);
    IEnumerable&lt;long&gt; PrimesUntilCount(int count);
}

public interface IPrimeChecker
{
    bool IsPrime(long value);
}

public interface IPrimeFactorizer
{
    IEnumerable&lt;long&gt; PrimeFactors(long value);
    IEnumerable&lt;long&gt; UniquePrimeFactors(long value);
}


Implementation

public class PrimeEngine: IPrimeGenerator, IPrimeChecker, IPrimeFactorizer
{
    private readonly ICollection&lt;long&gt; _primeCollection;
    private long _indexFactor;
    private long _maxChecked;

    public PrimeEngine()
    {
        _primeCollection = new Collection&lt;long&gt; {2, 3};
        _indexFactor = 1;
        _maxChecked = 3;
    }

    private void CheckNextPossiblePrimeDoublet()
    {
        var low = 6 * _indexFactor - 1;
        var high = low + 2;

        if (IsPrime(low))
        {
            _primeCollection.Add(low);
        }

        if (IsPrime(high))
        {
            _primeCollection.Add(high);
        }

        _indexFactor += 1;
        _maxChecked = high;
    }

    private IEnumerable&lt;long&gt; GetPossibleSmallerPrimeFactors(long value)
    {
        FillPrimesUntilRoot(value);
        return PrimesUntilRoot(value);
    }

    public bool IsPrime(long value)
    {
        var primePool = GetPossibleSmallerPrimeFactors(value);
        return primePool.All(prime =&gt; value % prime != 0);
    }

    private void FillPrimesUntilValue(long value)
    {
        while (_maxChecked &lt; value)
        {
            CheckNextPossiblePrimeDoublet();
        }
    }

    private void FillPrimesUntilCount(int count)
    {
        while (_primeCollection.Count &lt; count)
        {
            CheckNextPossiblePrimeDoublet();
        }
    }

    private static long FloorOfRoot(long value)
    {
        return (long) Math.Floor(Math.Sqrt(value));
    }

    private void FillPrimesUntilRoot(long value)
    {
        FillPrimesUntilValue(FloorOfRoot(value));
    }

    public IEnumerable&lt;long&gt; PrimesUntilValue(long value)
    {
        FillPrimesUntilValue(value);
        return _primeCollection.TakeWhile(prime =&gt; prime &lt;= value);
    }

    public IEnumerable&lt;long&gt; PrimesUntilCount(int count)
    {
        FillPrimesUntilCount(count);
        return _primeCollection.Take(count);
    }

    public IEnumerable&lt;long&gt; PrimesUntilRoot(long value)
    {
        return PrimesUntilValue(FloorOfRoot(value));
    }

    public IEnumerable&lt;long&gt; PrimeFactors(long value)
    {
        FillPrimesUntilRoot(value);
        foreach (var prime in PrimesUntilRoot(value))
        {
            if (prime &gt; value) break;

            while (value % prime == 0)
            {
                yield return prime;
                value /= prime;
            }
        }

        if (value != 1)
        {
            yield return value;
        }
    }

    public IEnumerable&lt;long&gt; UniquePrimeFactors(long value)
    {
        return PrimeFactors(value).Distinct();
    }
}
This is excellent code for a beginner. It's neat, the methods are short, etc. However, there are a couple things I'm concerned with.

1) Your engine has a lot of state. It looks like this is mostly related to the generation of the next prime sequences, and any found primes are cached, so it shouldn't be too bad. However, you have to test that calling PrimesUntilValue followed by UniquePrimeFactors, for example, doesn't generate "duplicate" values or skip any.

2) Given that you are using an approximation for primes, your code is structured neatly. However, do note that actually generating the sequence might get a bit messier than this and don't expect to be able to swap it out neatly. Given that your class's state is solely used for generating the next prime and that all previously found values are cached, it shouldn't be an issue.

3) This code could lead to massive memory use in an application. Since you are already caching the values and all, you should consider making it a static class and making the cached collection a thread-safe hashmap. Then you have one instance of the prime engine and can do things like PrimeEngine.PrimesUntilRoot, etc. The hashmap would prevent duplicate primes from being entered into the collection.