ICounter Based<T> Interface
Definition
Assembly: Numerics.NET (in Numerics.NET.dll) Version: 10.3.0
public interface ICounterBased<T>
where T : struct, new(), Object
Type Parameters
Remarks
Counter-based random number generators use a cryptographic-like structure where output is computed from a counter and key using a deterministic function. This design enables:
- Efficient parallel generation: Multiple threads can generate independent subsequences by using different counter ranges.
- Fast skip-ahead: Jumping to arbitrary positions in the sequence is O(1) by setting the counter directly.
- Block generation: Output is computed in fixed-size blocks, where each block corresponds to one counter value.
- Reproducibility: Setting the key and counter to specific values always produces the same output block.
Architecture:
Counter-based engines maintain three components:
- Key: A fixed value that parameterizes the generator (analogous to a cryptographic key). The key is set during seeding and typically remains constant.
- Counter: A value that is incremented for each block. The counter determines the current position in the output sequence.
- Block: A cached buffer holding the current output block computed from the current key/counter coordinates. This block is engine-owned and may be persisted as part of engine state.
Consistency Protocol:
Counter-based engines cache a materialized block that depends on the current (Key, Counter) coordinates. The engine is consistent when the cached block corresponds exactly to the current coordinates. The engine is inconsistent after coordinate mutations that do not rematerialize the block.
The engine must be consistent after: Seed(SeedSequence), LoadState(ReadOnlySpan<Byte>), and GenerateBlock().
The engine becomes inconsistent after: SetKey(ReadOnlySpan<T>), SetCounter(ReadOnlySpan<T>), and ResetCounter().
Invoking output methods (Output(Int32) or Fill(Int32, Span<T>)) while the engine is inconsistent results in undefined behavior. Callers must invoke GenerateBlock() after coordinate mutations before using output methods.
Block Generation:
The core operation is GenerateBlock(), which computes the output block for the current key/counter combination using the algorithm-specific permutation function (e.g., Philox rounds, Threefry rounds, ChaCha quarter-rounds). This operation does not advance the counter.
Common Counter-Based Engines:
- Philox (4x32, 4x64)
- Threefry (4x64)
- ChaCha (configurable rounds)
Example
// Create and seed a counter-based engine
var engine = new Philox4x32Engine();
engine.Seed(SeedSequences.SplitMix64(42));
// Engine is now consistent (block matches key/counter)
// Access values from the current block
uint value0 = engine.Output(0);
uint value1 = engine.Output(1);
// Jump ahead by 1000 blocks
engine.AdvanceBlock(1000);
// Set counter to a specific position
uint[] counter = new uint[4] { 0, 0, 0, 0 };
engine.SetCounter(counter);
// Engine is now inconsistent!
// Must regenerate block before output
engine.GenerateBlock();
// Engine is now consistent again
uint value = engine.Output(0);Properties
| Counter | Gets the number of words in the engine counter. |
| Key | Gets the number of words in the engine key. |
Methods
| Advance | Advances the counter by the specified number of blocks. |
| Generate | Computes the output block for the current key and counter. |
| Get | Copies the current counter into the provided destination span. |
| Get | Copies the current key into the provided destination span. |
| Reset | Resets the counter to all zeros. |
| Set | Sets the counter to the specified value. |
| Set | Sets the key to the specified value. |