SequenceGenerator Class
Definition
- Assembly
- Bodu.Core.dll
- Package
- Bodu.Core 1.0.1
Provides static factory methods that produce lazily evaluated IEnumerable<T> sequences without
materializing the underlying collection - both general-purpose shapes (Range, NextWhile,
Factory) and a catalogue of well-known mathematical sequences (Fibonacci, Farey, Leibniz, look-and-say, and
Thue-Morse).
public static class SequenceGenerator
- Inheritance
-
SequenceGenerator
- Inherited Members
Examples
// A counted descending range.
foreach (int n in SequenceGenerator.Range(start: 10, stop: 0, step: -2))
Console.WriteLine(n); // 10, 8, 6, 4, 2
// A stateful generator producing powers of two while the value fits in a positive int.
IEnumerable<int> powers = SequenceGenerator.NextWhile(
initialValue: 1,
conditionHandler: value => value > 0,
resultSelector: prev => prev * 2);
// Fibonacci numbers up to 100 - the value bound, not a fixed count.
foreach (long fib in SequenceGenerator.Fibonacci(min: 0, max: 100))
Console.WriteLine(fib); // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
Remarks
The factory surface mirrors the conventions of Enumerable and complements it with sequence shapes that LINQ does not provide directly. Every member returns a deferred sequence that produces elements only as the consumer iterates; nothing is allocated up front for the result set itself.
The general-purpose primitives cover numeric and value projections, stateful generation, and enumerator adaptation.
Range overloads accept either an inclusive start / exclusive stop pair (with an inferred step direction) or
an explicit step, and operate over int and long domains. For a fixed-length
single-value feed, use Repeat<TResult>(TResult, int). NextWhile drives
a state-machine-style generator from an initial state and a transition delegate, terminating when the supplied
predicate is no longer satisfied. Factory wraps a delegate-returned IEnumerator<T> so callers
can adapt non-collection iteration sources to the LINQ pipeline.
The mathematical catalogue is intentionally narrow - it covers reference sequences that appear repeatedly in
numerical recipes, algorithm exercises, and educational material, but it is not a general-purpose recurrence
framework. Most of these generators take inclusive bounds over the value space (min, max) rather than
element counts, while the Farey and look-and-say overloads accept an order or count parameter where bounds are not
meaningful. Refer to each member's documentation for the exact bounding rule.
All sequences returned by this type are single-pass with respect to side effects in their generator delegate:
re-enumerating the returned IEnumerable<T> invokes the supplied delegates again. Callers that require
a stable, replayable view should materialize the sequence via ToArray or ToList.
Methods
Factory<TResult>(Func<IEnumerator<TResult>>)
Wraps a user-supplied enumerator factory in an IEnumerable<T> so it can participate in LINQ pipelines.
public static IEnumerable<TResult> Factory<TResult>(Func<IEnumerator<TResult>> enumeratorFactory)
Parameters
enumeratorFactoryFunc<IEnumerator<TResult>>A delegate that returns a fresh IEnumerator<T> on each call. Must not be null. The delegate is invoked once per
foreach/GetEnumeratorcall, so it must produce an independent enumerator each time to allow re-enumeration.
Returns
- IEnumerable<TResult>
An IEnumerable<T> that defers all work to
enumeratorFactoryat iteration time.
Type Parameters
TResultThe element type produced by the supplied enumerator.
Examples
// Adapt an external pull-style API into an IEnumerable<T> pipeline.
IEnumerable<int> randomBytes = SequenceGenerator.Factory(() =>
{
var rng = new Random(42);
return Enumerable.Range(0, 4).Select(_ => rng.Next(0, 256)).GetEnumerator();
});
foreach (int b in randomBytes)
Console.Write($"{b} "); // => 79 235 64 230 (a deterministic run with seed 42)
Remarks
This is the preferred bridge from imperative or external enumerator implementations into a deferred,
LINQ-friendly sequence. Reach for it when an existing component exposes a GetEnumerator-shaped method but
does not implement IEnumerable<T>, or when the iteration logic is simpler to express by hand than
via the other SequenceGenerator overloads.
Argument validation runs eagerly; iteration itself is fully deferred. The wrapper is finite or infinite
according to the supplied enumerator and is re-enumerable only if enumeratorFactory returns
a new, independent enumerator on every call.
Allocations are limited to a single wrapper instance plus whatever the supplied enumerator allocates per iteration.
Exceptions
- ArgumentNullException
Thrown when
enumeratorFactoryis null.
Farey(int)
Yields the Farey sequence Fn as ordered (numerator, denominator) pairs in lowest terms.
public static IEnumerable<(int Numerator, int Denominator)> Farey(int order)
Parameters
orderintThe order
nof the Farey sequence. Must be at least1.
Returns
- IEnumerable<(int Numerator, int Denominator)>
A lazily evaluated, finite sequence of tuples
(Numerator, Denominator)covering every reduced fractiona/bwith0 ≤ a ≤ b ≤andordergcd(a, b) = 1, emitted in strict ascending order from0/1to1/1.
Examples
foreach (var (num, den) in SequenceGenerator.Farey(5))
Console.Write($"{num}/{den} "); // => 0/1 1/5 1/4 1/3 2/5 1/2 3/5 2/3 3/4 4/5 1/1
Remarks
Reach for this generator instead of building Farey sequences with nested loops or LINQ filtering on
Range(int, int): it uses the standard mediant recurrence and runs in time
linear in the size of Fn, avoiding the cost of computing GCDs for rejected fractions.
Both endpoints are inclusive: the sequence always begins at (0, 1) and ends at (1, 1). The pairs
are emitted in strictly increasing rational order so consumers can rely on monotonic ordering without
re-sorting.
Iteration is deferred and allocates only the iterator state. The implementation is deterministic and thread-safe in the usual sense: each enumerator carries its own state.
Exceptions
- ArgumentOutOfRangeException
Thrown when
orderis less than1.
Fibonacci(long, long)
Yields Fibonacci numbers that fall within the half-open interval
[.min, max)
public static IEnumerable<long> Fibonacci(long min, long max)
Parameters
minlongThe inclusive lower bound. Fibonacci numbers strictly less than this value are skipped. Must be non-negative.
maxlongThe exclusive upper bound. Iteration stops as soon as the next Fibonacci number is greater than or equal to this value. Must be non-negative and not less than
min.
Returns
- IEnumerable<long>
A lazily evaluated, finite sequence of long Fibonacci numbers in ascending order.
Examples
foreach (long f in SequenceGenerator.Fibonacci(10, 1000))
Console.Write($"{f} "); // => 13 21 34 55 89 144 233 377 610 987
var none = SequenceGenerator.Fibonacci(50, 54).ToArray(); // => empty (no Fibonacci numbers between 50 and 54).
Remarks
Use this generator when only the Fibonacci numbers within a known numeric window are required - building the full sequence up to MaxValue and filtering after the fact wastes iterations. The half-open interval matches the convention used elsewhere in SequenceGenerator, so two consecutive ranges can be stitched together without overlap or gaps.
Lower bound is inclusive, upper bound is exclusive. If no Fibonacci number lies in the requested range the
result is an empty sequence. If the next Fibonacci number would overflow long the iterator
terminates cleanly rather than throwing. The traditional seed values 0 and 1 are emitted only when
the window includes them.
Iteration is deferred and deterministic; allocation is limited to the iterator state.
Exceptions
- ArgumentOutOfRangeException
Thrown when
minormaxis negative.- ArgumentException
Thrown when
minis greater thanmax.
Leibniz(double, double)
Yields terms of the Leibniz series F(n) = (-1)n / (2n + 1) whose absolute magnitudes lie
within the requested half-open interval.
public static IEnumerable<double> Leibniz(double min, double max)
Parameters
mindoubleThe inclusive lower bound on the absolute value of emitted terms. Because term magnitudes strictly decrease, iteration ends as soon as
|F(n)| <- no later term can re-enter the window. Must be non-negative; a value ofmin0produces an unbounded sequence (bound consumption withTake).maxdoubleThe exclusive upper bound on the absolute value of emitted terms. Iteration stops as soon as
|F(n)| ≥. Must be non-negative and not less thanmaxmin.
Returns
- IEnumerable<double>
A lazily evaluated sequence of double values drawn from the Leibniz series in their original (signed) alternating order. The sequence is finite whenever
minis greater than zero.
Examples
// Approximate π by summing the first hundred terms whose magnitude is at least 1e-3.
double partial = 0;
foreach (double term in SequenceGenerator.Leibniz(1e-3, 1.1).Take(100))
partial += term;
double pi = partial * 4; // => pi ≈ 3.139... (slow convergence, expected)
Remarks
The Leibniz series is the alternating series whose partial sums converge to π/4. Use this generator when
illustrating convergence behavior, when computing rough approximations to π by summing terms and multiplying by
4, or when demonstrating how slowly such an alternating series converges.
The window is applied to absolute magnitudes so that the alternating sign is preserved in the output. Both
bounds are terminating: as soon as a term's magnitude reaches max or falls below
min, iteration ends. Setting min to 0 emits every term below
max without terminating.
Because the magnitude sequence 1, 1/3, 1/5, … starts at one and is monotonically decreasing, the upper
bound only ever gates the first term - a max of 1 or less terminates immediately with
an empty sequence - and once a term drops below min no later term can return to the window,
so ending iteration there is what keeps the sequence finite. The iterator is deferred, deterministic, and
allocates only its own state.
Exceptions
- ArgumentOutOfRangeException
Thrown when
minormaxis negative.- ArgumentException
Thrown when
minis greater thanmax.
LookAndSay(int)
Yields successive terms of Conway's Look-and-Say sequence, starting from the seed term "1".
public static IEnumerable<string> LookAndSay(int count)
Parameters
countintThe total number of terms to emit, including the seed. Must be at least
1.
Returns
- IEnumerable<string>
A lazily evaluated, finite sequence of string values where the first element is
"1"and each subsequent element is the run-length-encoded description of the digits of the previous element.
Examples
foreach (string term in SequenceGenerator.LookAndSay(6))
Console.WriteLine(term);
// => 1
// => 11
// => 21
// => 1211
// => 111221
// => 312211
Remarks
Use this generator when demonstrating self-describing integer sequences, prototyping digit-run encoders, or as a
worked example of how a single seed expands into Conway's "audioactive" sequence. The seed is fixed at
"1"; if a different seed is required, take the first term as a starting string and apply the run-length
transform manually.
Each term grows in length roughly by Conway's constant (≈ 1.303577…) per iteration, so the strings
produced for large count values grow quickly and may dominate allocation cost. Iteration is
deferred - argument validation runs on first MoveNext - and a single StringBuilder
instance is reused per term.
The result is deterministic (the seed is fixed) and the iterator carries no shared mutable state.
Exceptions
- ArgumentOutOfRangeException
Thrown when
countis less than1.
NextWhile<TResult>(TResult, Func<TResult, bool>, Func<TResult, int, TResult>)
Yields a value-driven sequence whose successor function additionally receives the zero-based index of the current element.
public static IEnumerable<TResult> NextWhile<TResult>(TResult initialValue, Func<TResult, bool> conditionHandler, Func<TResult, int, TResult> resultSelector)
Parameters
initialValueTResultThe seed value emitted as the first element of the sequence.
conditionHandlerFunc<TResult, bool>A predicate evaluated against the current value before each emission. Must not be null.
resultSelectorFunc<TResult, int, TResult>A function that derives the next value from the current value and its zero-based index. The index passed in for the very first call to the selector is
0. Must not be null.
Returns
- IEnumerable<TResult>
A lazily evaluated sequence terminating on the first iteration where
conditionHandlerreturns false.
Type Parameters
TResultThe element type produced by the sequence.
Examples
// Triangular numbers up to 100: 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91.
var triangular = SequenceGenerator.NextWhile(0, v => v <= 100, (v, i) => v + (i + 1));
Remarks
Choose this overload over
NextWhile<TResult>(TResult, Func<TResult, bool>, Func<TResult, TResult>) when the transformation
depends on its position - for example, accumulating sums of i, generating polynomial terms, or scaling by
a power of the current index.
The index increments after the selector returns, so the seed is index 0, the value passed to the first
selector call is index 0, and the value emitted next sits at index 1. Iteration is deferred and
the index is held internally; it cannot overflow a single iteration but will wrap to MinValue
after MaxValue elements.
Exceptions
- ArgumentNullException
Thrown when
conditionHandlerorresultSelectoris null.
NextWhile<TResult>(TResult, Func<TResult, bool>, Func<TResult, TResult>)
Yields a value-driven sequence by repeatedly applying a successor function as long as a predicate continues to hold.
public static IEnumerable<TResult> NextWhile<TResult>(TResult initialValue, Func<TResult, bool> conditionHandler, Func<TResult, TResult> resultSelector)
Parameters
initialValueTResultThe seed value emitted as the first element of the sequence.
conditionHandlerFunc<TResult, bool>A predicate evaluated against the current value before each emission. Must not be null.
resultSelectorFunc<TResult, TResult>A function that derives the next value from the current value. Must not be null.
Returns
- IEnumerable<TResult>
A lazily evaluated sequence terminating on the first iteration where
conditionHandlerreturns false.
Type Parameters
TResultThe element type produced by the sequence.
Examples
// Halving sequence - terminates when the value drops to zero.
var halves = SequenceGenerator.NextWhile(64, v => v > 0, v => v / 2); // => 64, 32, 16, 8, 4, 2, 1
// Empty result when the seed already fails the predicate.
var none = SequenceGenerator.NextWhile(0, v => v > 0, v => v - 1); // => (empty)
Remarks
Use this overload when the next value depends only on the current one - classic recurrence relations such as
x ↦ 2·x or x ↦ x / 2. Reach for the indexed overload when the position matters, and the
state-based overload when more than one variable must be tracked across iterations.
The condition is checked before each yield, so the sequence terminates as soon as the predicate fails -
including on the very first element, which produces an empty sequence. Behavior is fully deterministic provided
conditionHandler and resultSelector are themselves pure.
The sequence may be infinite if the predicate never fails; bound it with Take or TakeWhile when an
upper limit is required. Iteration is deferred and allocates only the iterator state and a single boxed state
slot.
Exceptions
- ArgumentNullException
Thrown when
conditionHandlerorresultSelectoris null.
NextWhile<TState, TResult>(TState, Func<TState, bool>, Func<TState, TState>, Func<TState, TResult>)
Yields a sequence projected from a custom state object that is advanced on each iteration.
public static IEnumerable<TResult> NextWhile<TState, TResult>(TState initialState, Func<TState, bool> conditionHandler, Func<TState, TState> iterateFunction, Func<TState, TResult> resultSelector)
Parameters
initialStateTStateThe initial state used as the iteration seed.
conditionHandlerFunc<TState, bool>A predicate evaluated against the current state before each emission. Must not be null.
iterateFunctionFunc<TState, TState>A function that produces the next state from the current state. Must not be null.
resultSelectorFunc<TState, TResult>A function that projects the current state into the emitted element. Must not be null.
Returns
- IEnumerable<TResult>
A lazily evaluated sequence whose elements are produced by projecting each state visited by the iteration.
Type Parameters
TStateThe shape of the iteration state.
TResultThe element type produced by the sequence.
Examples
// Fibonacci numbers below 100, tracked through a (prev, curr) state record.
var fib = SequenceGenerator.NextWhile(
initialState: (Prev: 0, Curr: 1),
conditionHandler: s => s.Curr < 100,
iterateFunction: s => (s.Curr, s.Prev + s.Curr),
resultSelector: s => s.Curr); // => 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
Remarks
This is the most general NextWhile form. Use it when each iteration needs to track several variables -
for example, a pair of counters, a running accumulator, or a parser state - that cannot be folded into a single
value.
The state object is opaque to SequenceGenerator: if TState is mutable the
caller is responsible for the mutation discipline, and if it is a struct each call to
iterateFunction may need to return an updated copy. Iteration is deferred and produces no
allocations beyond the iterator state itself.
As with the other overloads, the predicate is evaluated before each emission, the sequence may be infinite if it never fails, and behavior is deterministic provided the supplied delegates are pure with respect to the state.
Exceptions
- ArgumentNullException
Thrown when
conditionHandler,iterateFunction, orresultSelectoris null.
Range(int, int)
Yields consecutive integers from start through stop, walking in
whichever direction is implied by the two endpoints.
public static IEnumerable<int> Range(int start, int stop)
Parameters
startintThe first value emitted by the sequence.
stopintThe final value emitted by the sequence; this value is included in the output when reached exactly.
Returns
- IEnumerable<int>
A lazily evaluated, finite sequence of int values inclusive of both endpoints.
Examples
foreach (int n in SequenceGenerator.Range(10, 14))
Console.Write($"{n} "); // => 10 11 12 13 14
foreach (int n in SequenceGenerator.Range(3, -2))
Console.Write($"{n} "); // => 3 2 1 0 -1 -2
Remarks
Prefer this overload over Range(int, int) when the natural way to express
the range is "from a to b" rather than "n values starting at a", or when a
descending sequence is required - Enumerable.Range only walks forwards and rejects negative counts.
The step direction is chosen automatically: ascending when start is less than
stop, descending otherwise. When start equals stop a
single-element sequence is returned.
Both endpoints are inclusive. The result is deferred - no values are produced until the sequence is enumerated - and the underlying iterator allocates only the per-enumeration state object.
Range(int, int, int)
Yields integers between start and stop inclusive, advancing by
step at each iteration.
public static IEnumerable<int> Range(int start, int stop, int step)
Parameters
startintThe first value emitted by the sequence.
stopintThe endpoint at which iteration terminates; included in the output when reached exactly.
stepintThe signed delta applied between successive values. Positive values produce an ascending sequence, negative values produce a descending sequence, and zero produces an unbounded sequence that yields
startforever.
Returns
- IEnumerable<int>
A lazily evaluated sequence of int values; finite when
stepis non-zero, otherwise infinite.
Examples
foreach (int n in SequenceGenerator.Range(0, 20, 5))
Console.Write($"{n} "); // => 0 5 10 15 20
foreach (int n in SequenceGenerator.Range(10, 1, -3))
Console.Write($"{n} "); // => 10 7 4 1
// Step of zero yields an unbounded sequence - bound it with Take.
var heartbeat = SequenceGenerator.Range(42, 0, 0).Take(3); // => 42, 42, 42
Remarks
Use this overload when the caller needs explicit control over the stride, including descending ranges and
non-unit steps that Range(int, int) cannot express. The two-argument
Range(int, int) is more convenient when a step of +1 or -1 is sufficient.
Iteration is bounded by stop: the inclusive endpoint is yielded only when
step divides the interval evenly. If the next value would overflow
MaxValue or MinValue the sequence terminates cleanly rather than throwing.
When step is 0 the method returns an infinite sequence - callers must compose it with
an operator such as Take or TakeWhile to bound enumeration.
The result is deferred and allocation-light; no array or list is materialized.
Range(long, int)
Yields a fixed number of consecutive 64-bit integers beginning at start.
public static IEnumerable<long> Range(long start, int count)
Parameters
startlongThe first value emitted by the sequence.
countintThe number of values to produce. Must be non-negative.
Returns
- IEnumerable<long>
A lazily evaluated, finite sequence of long values containing exactly
countelements.
Examples
foreach (long n in SequenceGenerator.Range(1_000_000_000_000L, 4))
Console.Write($"{n} "); // => 1000000000000 1000000000001 1000000000002 1000000000003
var empty = SequenceGenerator.Range(0L, 0).ToArray(); // => empty.Length == 0
Remarks
Prefer this overload when the desired output is a count-bounded run of contiguous values, particularly for ranges that exceed the addressable region of Range(int, int) (which is restricted to int).
A count of 0 produces an empty sequence. The endpoint is exclusive in the sense that
the final value emitted is .
start + count - 1
Overflow is rejected up-front by the argument validation, so the iterator never throws mid-enumeration. Iteration is deferred, deterministic, and allocation-free apart from the iterator state.
Exceptions
- ArgumentOutOfRangeException
Thrown when
countis negative, or when the inclusive range[would overflow MaxValue.start,start+count- 1]
ThueMorse(int)
Yields the first count terms of the Thue-Morse sequence as a stream of 0s and
1s.
public static IEnumerable<int> ThueMorse(int count)
Parameters
countintThe number of terms to emit. Must be non-negative.
Returns
- IEnumerable<int>
A lazily evaluated, finite sequence of int values, each equal to
0or1, where elementnis the parity of the number of set bits in the binary representation ofn.
Examples
var prefix = SequenceGenerator.ThueMorse(16).ToArray(); // => [0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0]
// Convenient as the schedule for a fair turn-taking algorithm:
// player A goes on 0, player B goes on 1.
Remarks
Use this generator to drive cube-free sequence demonstrations, fair-division algorithms, or anywhere a
deterministic but non-periodic binary feed is useful. Compared to building the sequence by repeated bitwise
complement and concatenation, this implementation uses a per-index popcount reduction so it can produce a
single index without first materializing the preceding n bits.
A count of 0 returns an empty sequence. The sequence is finite and deterministic -
the same input always produces the same output. Iteration is deferred and carries no allocations beyond the
iterator state.
Exceptions
- ArgumentOutOfRangeException
Thrown when
countis negative.
Applies to
| Product | Versions |
|---|---|
| .NET | 8, 10 |