A number wheel has:

 A name which is an uppercase letter.
 A set of ordered values which are either numbers or names.

A number is generated/yielded from a named wheel by:

1. Starting at the first value of the named wheel and advancing through subsequent values and wrapping around to the first value to form a "wheel":
1.a If the value is a number, yield it.
1.b If the value is a name, yield the next value from the named wheel
1.c Advance the position of this wheel.
Given the wheel

 A: 1 2 3
the number 1 is first generated, then 2, then 3, 1, 2, 3, 1, ...

Note: When more than one wheel is defined as a set of intersecting wheels then the
first named wheel is assumed to be the one that values are generated from.

Examples

Given the wheels:

   A: 1 B 2
   B: 3 4

The series of numbers generated starts:

   1, 3, 2, 1, 4, 2, 1, 3, 2, 1, 4, 2, 1, 3, 2...

The intersections of number wheels can be more complex, (and might loop forever),
and wheels may be multiply connected. 


Note: If a named wheel is referenced more than
once by one or many other wheels, then there is only one position of the wheel
that is advanced by each and all references to it.

E.g.

 A:  1 D D
 D:  6 7 8
 Generates:
   1 6 7 1 8 6 1 7 8 1 6 7 1 8 6 1 7 8 1 6 ...    

Task

Generate and show the first twenty terms of the sequence of numbers generated
from these groups:

   Intersecting Number Wheel group:
     A:  1 2 3
   
   Intersecting Number Wheel group:
     A:  1 B 2
     B:  3 4
   
   Intersecting Number Wheel group:
     A:  1 D D
     D:  6 7 8
   
   Intersecting Number Wheel group:
     A:  1 B C
     B:  3 4
     C:  5 B

Show your output here, on this page.



BEGIN
    # a number wheel element                                                  #
    MODE NWELEMENT = UNION( CHAR # wheel name #, INT # wheel value # );
    # a number wheel                                                          #
    MODE NW = STRUCT( CHAR name, REF INT position, FLEX[ 1 : 0 ]NWELEMENT values );
    # get the next value from a number wheel in an array of number wheels     #
    # note: invalid wheel names will cause subscript range errors             #
    OP   NEXT = ( []NW wheels )INT:
         BEGIN
            INT  result;
            BOOL found := FALSE;
            INT  w     := LWB wheels; # start with the first wheel            #
            WHILE NOT found DO
                IF position OF wheels[ w ] > UPB values OF wheels[ w ] THEN
                    # passed the end of the wheel, go back to the start       #
                    position OF wheels[ w ] := LWB values OF wheels[ w ]
                FI;
                NWELEMENT e = ( values OF wheels[ w ] )[ position OF wheels[ w ] ];
                position OF wheels[ w ] +:= 1;
                CASE e
                  IN ( INT  n ): BEGIN result := n; found := TRUE END
                   , ( CHAR c ): BEGIN
                                     w := LWB wheels;
                                     WHILE name OF wheels[ w ] /= c DO w +:= 1 OD
                                 END
                ESAC
            OD;
            result
         END # NEXT # ;
    # prints the first n values from an array of wheels                       #
    PROC show = ( INT n, []NW wheels )VOID:
         BEGIN
            print( ( "First ", whole( n, 0 ), " values from the Intersecting Number Wheels:" ) );
            FOR i FROM LWB wheels TO UPB wheels DO
                print( ( newline, "    ", name OF wheels[ i ], ":" ) );
                FOR v FROM LWB values OF wheels[ i ] TO UPB values OF wheels[ i ] DO
                    CASE ( values OF wheels[ i ] )[ v ]
                      IN ( INT  n ): print( ( " ", whole( n, 0 ) ) )
                       , ( CHAR c ): print( ( " ", c ) )
                    ESAC
                OD
            OD;
            print( ( newline, "        " ) );
            FOR i TO n DO print( ( " ", whole( NEXT wheels, 0 ) ) ) OD;
            print( ( newline, newline ) )
         END # show # ;
    # show some wheels in action                                              #
    show( 20, ( NW( "A", LOC INT := 1, (  1,   2,   3  ) ) ) );
    show( 20, ( NW( "A", LOC INT := 1, (  1,  "B",  2  ) )
              , NW( "B", LOC INT := 1, (  3,   4       ) ) ) );
    show( 20, ( NW( "A", LOC INT := 1, (  1,  "D", "D" ) )
              , NW( "D", LOC INT := 1, (  6,   7,   8  ) ) ) );
    show( 20, ( NW( "A", LOC INT := 1, (  1,  "B", "C" ) )
              , NW( "B", LOC INT := 1, (  3,   4       ) )
              , NW( "C", LOC INT := 1, (  5,  "B"      ) ) ) )
END