using System.Linq.Expressions;
namespace StoneRed.LogicSimulator.Simulation;
///
/// A synchronous cycle-based circuit simulator that evaluates all gates every cycle.
/// Provides deterministic timing and predictable evaluation order (gate 0 to N).
///
///
/// This simulator evaluates all gates synchronously each step, making it simpler and more
/// predictable than . It's ideal for synchronous digital
/// designs where deterministic timing is important. Less efficient for sparse circuits
/// but has uniform performance characteristics.
///
public sealed class CycleCircuitSimulator : SimulatorBase
{
private int[] nextInputMasks = [];
private Action computeOutputs = (_, _, _) => { };
private int[] previousOutputMasks = [];
///
/// Ensures internal storage arrays are properly sized for the current gate count.
///
protected override void EnsureStorage()
{
base.EnsureStorage();
if (nextInputMasks.Length != gateKinds.Count)
{
nextInputMasks = new int[gateKinds.Count];
previousOutputMasks = new int[gateKinds.Count];
}
}
///
/// Resets the circuit to initial state, clearing all input and output buffers.
///
public override void Reset()
{
base.Reset();
Array.Clear(nextInputMasks);
Array.Clear(previousOutputMasks);
}
///
/// Executes one simulation cycle by evaluating all gates synchronously,
/// then propagating outputs to inputs for the next cycle.
///
public override void Step()
{
EnsureCompiled();
if (!initialized)
{
Reset();
}
(outputMasks, previousOutputMasks) = (previousOutputMasks, outputMasks);
computeOutputs(inputMasks, outputMasks, sourceStates);
PropagateAndSwap();
if (hasAnyWatchers)
{
NotifyAllWatchers(previousOutputMasks);
}
}
private void PropagateAndSwap()
{
Array.Clear(nextInputMasks);
for (int fromGate = 0; fromGate < gateKinds.Count; fromGate++)
{
if ((outputMasks[fromGate] & 1) == 0)
{
continue;
}
int start = edgeStart[fromGate];
int end = edgeStart[fromGate + 1];
for (int e = start; e < end; e++)
{
nextInputMasks[edgeToGate[e]] |= 1 << edgeToInputBit[e];
}
}
(inputMasks, nextInputMasks) = (nextInputMasks, inputMasks);
}
private bool PropagateAndSwapDetectChange()
{
Array.Clear(nextInputMasks);
for (int fromGate = 0; fromGate < gateKinds.Count; fromGate++)
{
if ((outputMasks[fromGate] & 1) == 0)
{
continue;
}
int start = edgeStart[fromGate];
int end = edgeStart[fromGate + 1];
for (int e = start; e < end; e++)
{
nextInputMasks[edgeToGate[e]] |= 1 << edgeToInputBit[e];
}
}
bool changed = false;
for (int i = 0; i < inputMasks.Length; i++)
{
if (inputMasks[i] != nextInputMasks[i]) { changed = true; break; }
}
(inputMasks, nextInputMasks) = (nextInputMasks, inputMasks);
return changed;
}
///
/// Runs the simulation until inputs stabilize (no changes between cycles) or maxSteps is reached.
///
/// Maximum number of cycles to execute.
/// Output parameter containing the number of cycles executed.
/// True if the circuit stabilized; false if maxSteps was exceeded.
public override bool TryRunUntilStable(int maxSteps, out int steps)
{
steps = 0;
if (maxSteps <= 0)
{
return false;
}
EnsureCompiled();
if (!initialized)
{
Reset();
}
bool changed = true;
while (changed && steps < maxSteps)
{
steps++;
(outputMasks, previousOutputMasks) = (previousOutputMasks, outputMasks);
computeOutputs(inputMasks, outputMasks, sourceStates);
changed = PropagateAndSwapDetectChange();
if (hasAnyWatchers)
{
NotifyAllWatchers(previousOutputMasks);
}
}
return !changed;
}
///
/// Compiles a single evaluator that computes outputs for all gates in one call.
/// All gate logic is combined into a single compiled lambda expression.
///
protected override void CompileEngine()
{
ParameterExpression inputsParam = Expression.Parameter(typeof(int[]), "inputs");
ParameterExpression outputsParam = Expression.Parameter(typeof(int[]), "outputs");
ParameterExpression sourcesParam = Expression.Parameter(typeof(int[]), "sources");
ConstantExpression lutDataConst = Expression.Constant(lutData);
List body = [];
for (int i = 0; i < gateKinds.Count; i++)
{
ConstantExpression indexExpr = Expression.Constant(i);
BinaryExpression inMask = Expression.ArrayIndex(inputsParam, indexExpr);
Expression logicExpr = GenerateGateLogic(i, inMask, sourcesParam, indexExpr, lutDataConst);
body.Add(Expression.Assign(Expression.ArrayAccess(outputsParam, indexExpr), logicExpr));
}
computeOutputs = Expression.Lambda>(Expression.Block(body), inputsParam, outputsParam, sourcesParam).Compile();
}
///
/// Creates a new instance of CycleCircuitSimulator for internal use (e.g., LUT computation).
///
/// A new CycleCircuitSimulator instance.
protected override SimulatorBase CreateInternalSimulator()
{
return new CycleCircuitSimulator();
}
}