## Control-flow actors

These actors provide control-flow constructs for other actors. They send messages to user-specified message groups as the results of their actions -- looping, conditional statements, random number generation.

To use the LoopActor, load control.so and create an actor of type LoopActor.

### LoopActor messages

The LoopActor periodically sends a single floating-point value to a message group. You can control three aspects of the loop: initialization; termination conditions; and step behavior (just like the three parts of a C-language for(;;) loop). You can also control the number of times to repeat the loop, and optionally "swing" back and forth through the loop.

A loop has three states: its data value, its elapsed time, and how many steps (iterations) it has taken so far. You can control the loop's execution with all three of these. Moreover, since these produce an overdetermined system when taken as a whole, you can specify which of these shall be constrained by the others (e.g., figure out what the data and time increments will be, given a data range of 0 to 10, a time duration of 20 seconds, and exactly 42 steps through the loop).

This is trickier than it looks. Watch out particularly that you terminate the loop in the way you intend to: when the data value reaches a limit, when a fixed number of steps have completed, or when a certain amount of time has elapsed. (If you don't do one of these things, the loop is infinite.)

The default loop "does nothing": it sends out a stream of zeros, unchanging, without termination. (It only does this once a message group has been set to receive the stream, and once a `SetNumLoops` message has activated it, though.)

In addition to the messages understood by all actors, the LoopActor understands the following messages:

- Loop Initialization

  SetDataStart `hActor x`
    Set the starting data value of the loop to `x`.

- Loop Stepping

  SetDataStep `hActor xDelta [time]`
    Each time through the loop, increment the data value by `xDelta` (default 0). If `time` is specified, linearly change the value of `xDelta` from its current value to the new value over the next `time` seconds.
  SetTimeStep `hActor tDelta [time]`
    Each time through the loop, pause for `tDelta` seconds (default 1). `tDelta` must be nonnegative. If `time` is specified, linearly change the value of `tDelta` from its current value to the new value over the next `time` seconds.

- Loop Termination - The loop will terminate if any one of these conditions holds (logical "or").

  SetDataLimit `hActor xLimit`
    Terminate loop when the data value `x` passes `xLimit`, considering the sign of SetDataStep. (If the argument of the last SetDataStep was positive, terminate when `x` exceeds `xLimit`; if negative, when `x` falls below `xLimit`.)
  SetNoDataLimit `hActor`
    Ignore any previous `SetDataLimit` messages when checking for loop termination. This is the default.
  SetTimeLimit `hActor tLimit`
    Terminate loop when `tLimit` seconds have elapsed since the loop started running (by sending a `SetNumLoops` message). If `tLimit` is negative (the default), ignore time limits.
  SetStepLimit `hActor stepLimit`
    Terminate loop after the `stepLimit`'th step through the loop. If `stepLimit` is negative (the default), ignore step limits.

- Constraint Definition - Of course, these may be inaccurate if values change during the loop's execution, e.g. as a result of gradual acceleration in `SetTimeStep` above.

  ComputeDataLimit `hActor`
  ComputeDataStep `hActor`
    Compute this value so the data reaches its limit at the same time as the first of (time, steps) reaches its limit.
  ComputeTimeLimit `hActor`
  ComputeTimeStep `hActor`
    Compute this value so the time reaches its limit at the same time as the first of (data, steps) reaches its limit.
  ComputeStepLimit `hActor`
    Compute this value so the step reaches its limit at the same time as the first of (data, time) reaches its limit.

- Miscellany

  SetMessageGroup `hActor messageGroupName`
    Assign a message group to send the periodic messages to. These messages will have two arguments ("`*0, *1`"), the current data value of the loop and the most recent value sent to SetUserFloat respectively.
  SetNumLoops `hActor numLoops`
    Run the loop for `numLoops` times. You'll commonly use a value of 1. (If the loop is currently running, it will restart from the beginning and do its thing `numLoops` times starting from now, irrespective of any previous SetNumLoops commands.)
    If `numLoops` is zero (the default), the loop will immediately halt and remain halted until a nonzero `SetNumLoops` message is received.
    If `numLoops` is negative, the loop will run indefinitely.
  SetSwing `hActor bool`
    Enable or disable Swing Mode. If `bool` is true (nonzero), each time the loop terminates, the start and limit data values are exchanged and the data step is negated, so the next execution of the loop is "reversed" with respect to the data. If SetNumLoops has a large value, this causes the data value to "swing" back and forth between its two extremes. The default value is false.
  Debug `hActor bool`
    Enable or disable Debug Mode. If `bool` is true (nonzero), the full state of the loop (start, end, step, and current values of data, time, and step; and swing and numloops values) is printed just after each step of the loop and just after each message received by the LoopActor.
  SetUserFloat `hActor x`
    Assign a new value to this variable, simply to pass it along to the message group named in `SetMessageGroup` as a second parameter.

### Examples

`LoadDSO debug.so;
pr = Create PrintfActor;
// This prints debugging messages.
LoadDSO msgGroup.so;
printme = Create MessageGroup;
AddMessage printme printf pr *0;
// Call this message group to print a number.
LoadDSO control.so;
loop = Create LoopActor;
SetMessageGroup loop printme;
// Attach mg to our loop actor, so our loop will print numbers.
`

Print 2, 4, 6, 8, one number per second (with debugging info):

  `Active loop 0;
  Debug loop 1;
  SetNumLoops loop 1;
  SetDataStart loop 2;
  SetDataStep loop 2;
  SetDataLimit loop 8;
  Active loop 1;
  sleep 15;`

Print 2, 4, 6, 8, one number per second, for one hour:

  `Active loop 0;
  SetNumLoops loop -1; // loop forever
  SetDataStart loop 2;
  SetDataStep loop 2;
  SetDataLimit loop 8;
  Active loop 1;
  sleep 3600;`

Print 2, 4, 6, 8, 6, 4, 2, ten numbers per second, for one minute:

  `Active loop 0;
  SetSwing loop 1; // back and forth
  SetTimeStep loop 0.1;
  SetNumLoops loop -1; // loop forever
  SetDataStart loop 2;
  SetDataStep loop 2;
  SetDataLimit loop 8;
  Active loop 1;
  sleep 60;`

Print increasing even numbers, ten per second, for one minute:

  `Active loop 0;
  SetSwing loop 1;
  SetTimeStep loop 0.1;
  SetTimeLimit loop 60; // this replaces SetDataLimit as termination condition
  SetNumLoops loop 1; // do this only once
  SetDataStart loop 2;
  SetDataStep loop 2;
  Active loop 1;
  sleep 65;`

Print "42" ten times:

  `Active loop 0;
  SetStepLimit loop 9; // "for (i=0; i<=9; i++)"
  SetDataStart loop 42;
  SetDataStep loop 0; // Don't change 42 to anything else (default)
  SetNumLoops loop 1;
  Active loop 1;
  sleep 60;`

Print "42" ten times (using 10 loops, instead of 10 steps through one loop):

  `Active loop 0;
  SetStepLimit loop 0; // "for (i=0; i<=0; i++)"
  SetDataStart loop 42;
  SetNumLoops loop 10; // Do all of the above ten times
  Active loop 1;
  sleep 60;`

Print "42" ten times (using 2 loops of 5 steps each):

  `Active loop 0;
  SetStepLimit loop 4; // "for (i=0; i<=4; i++)"
  SetDataStart loop 42;
  SetNumLoops loop 2; // Do it twice
  Active loop 1;
  sleep 60;`