Infinite and countdown counters I'm refactoring my scheduler and for one of its triggers I need an endless counter that would automatically restart. Since this is a simple and very common task I don't want to ever have to implement it again so I encapsulated it in such a way that it hopefully can be reused everywhere else.



The counter is represented by an enumerable interface with a couple or properties that provide information about the counter and methods allowing to interact with it.

public interface IInfiniteCounter : IEnumerable&lt;(int Value, InfiniteCounterState State)&gt;
{
    int Min { get; }

    int Max { get; }

    int Length { get; }

    int Current { get; }

    (int Value, InfiniteCounterState State) Next();

    void Reset();
}


It uses an enum to inform the caller about the type of the value. It returns First each time the counter restarted, Intermediate for values inbetween and Last for the last value.

public enum InfiniteCounterState
{
    First,
    Intermediate,
    Last,
}




I have one implementation of the interface. The InfiniteCounter that is a forward running counter:

public class InfiniteCounter : IInfiniteCounter
{
    private int _current;

    public InfiniteCounter(int min, int max)
    {
        Min = min;
        Max = max;
    }

    public InfiniteCounter(int max) : this(0, max) { }

    public int Min { get; }

    public int Max { get; }

    public int Length =&gt; Max - Min;

    public int Current =&gt; _current + Min;

    private bool IsLast =&gt; _current == Length;

    public (int Value, InfiniteCounterState State) Next()
    {
        if (IsLast)
        {
            Reset();
        }

        return
        (
            Current,
            _current++ == 0
                ? InfiniteCounterState.First
                : IsLast
                    ? InfiniteCounterState.Last
                    : InfiniteCounterState.Intermediate
        );
    }

    public void Reset()
    {
        _current = 0;
    }

    public IEnumerator&lt;(int Value, InfiniteCounterState State)&gt; GetEnumerator()
    {
        while (true)
        {
            yield return Next();
        }
    }

    IEnumerator IEnumerable.GetEnumerator() =&gt; GetEnumerator();
}


In order to have it running backwards I created two extensions. One that is an extension for the IInfiniteCounter

public static class InfiniteCounterExtensions
{
    public static IEnumerable&lt;(int Value, InfiniteCounterState State)&gt; Countdown(this IInfiniteCounter counter)
    {
        return counter.Select(x =&gt; (x.Value.Flip(counter.Min, counter.Max), x.State));
    }
}


and one that is flipping the value:

public static class IndexMath
{
    public static int Flip(this int value, int min, int max)
    {
        return (-(value - max + 1) % (max - min)) + min;
    }
}


Examples:

void Main()
{
    var take = 4;
    new InfiniteCounter(3).Take(take).Dump("0-2");
    new InfiniteCounter(3).Countdown().Take(take).Dump("2-0");
    new InfiniteCounter(2, 5).Take(take).Dump("2-4");
    new InfiniteCounter(2, 5).Countdown().Take(take).Dump("4-2");

    0.Flip(0, 3).Dump();
    1.Flip(0, 3).Dump();
}




What do you think about it? It should be convenient to use and every piece of it should be testable. (Argument checking will be added later)
public interface IInfiniteCounter : IEnumerable&lt;(int Value, InfiniteCounterState State)&gt;



vs


    int Current { get; }

    (int Value, InfiniteCounterState State) Next();

    void Reset();



The interface itself looks a lot more like IEnumerator&lt;&gt; than IEnumerable&lt;&gt;, so IMO implementing IEnumerable&lt;&gt; is misleading and dangerous. The docs for IEnumerable&lt;&gt; don't say either way whether or not it's possible to have two active enumerators at the same time, but I can't think of any implementations in the standard library for which it isn't possible, and I've probably written a lot of code which assumes that it is possible.




    int Min { get; }

    int Max { get; }

    int Length { get; }



I can safely assume that Min is inclusive, but I absolutely need a comment to tell me whether Max is or not, and it would be helpful to have a comment indicating the standard implementation of Length. Also, should Length be uint both because it's guaranteed to be non-negative and to avoid overflow in edge cases?




public static class IndexMath
{
    public static int Flip(this int value, int min, int max)
    {
        return (-(value - max + 1) % (max - min)) + min;
    }
}



I don't understand why this method uses %. For me the obvious interpretation of the name (and the use case) is max - (value - min). If there's some trickery going on to avoid or handle overflow, it needs explanatory comments.