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.



/*REXX program  expresses numbers  from  intersecting number wheels  (or wheel sets).   */
@.=                                              /*initialize array to hold the wheels. */
parse arg lim @.1                                /*obtain optional arguments from the CL*/
if lim='' | lim=","  then lim= 20                /*Not specified?  Then use the default.*/
if @.1='' | @.1=","  then do;  @.1= ' A:  1 2 3 '
                               @.2= ' A:  1 B 2,    B:  3 4 '
                               @.3= ' A:  1 D D,    D:  6 7 8 '
                               @.4= ' A:  1 B C,    B:  3 4,    C:  5 B '
                          end
       do i=1  while @.i\='';  call run          /*construct wheel set and "execute" it.*/
       end   /*i*/
exit 0                                           /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
error: procedure; say;  say;    say '***error***'   arg(1);          say;   say;   exit 12
isLet: procedure; parse arg y;  return datatype(y, 'M') & length(y)==1   /*is a letter? */
isNum: procedure; parse arg y;  return datatype(y, 'N')                  /*is a number? */
/*──────────────────────────────────────────────────────────────────────────────────────*/
run: @wn= 'wheel name';     first=;      @noColon= "wheel name not followed by a colon:"
     @gn= 'gear name' ;     gear.=;      say copies("═", 79)
     say 'building wheel group for: '    @.i;    wheels= space(@.i);        upper wheels
        do #=1  while wheels\='';  parse var wheels  w gears "," wheels;    L= length(w)
        if L==2  then do;  !.#= left(w, 1)       /*obtain the one─character gear name.  */
                           if right(w, 1)\==':'  then call error @noColon  w
                           if \isLet(!.#)        then call error @wn "not a letter:"  w
                      end
                 else call error "first token isn't a"   @wn':'     w
        if #==1  then first= !.1                 /*Is this is the 1st wheel set?  Use it*/
        if first==''  then call error "no wheel name was specified."
        n= !.#                                   /*obtain the name of the 1st wheel set.*/
        gear.n.0= 1                              /*initialize default 1st gear position.*/
        say '   setting gear.name:'     n     "    gears=" gears
           do g=1  for words(gears);         _= word(gears, g)
           if isNum(_)  |  isLet(_)  then do;  gear.n.g= _;  iterate;  end
           call error  @gn  "isn't a number or a gear name:"  _
           end   /*g*/
        end      /*#*/
    say;                  say center(' running the wheel named '  first" ", 79, '─');   $=
        do dummy=0  by 0  until words($)==lim;           n= first
        z= gear.n.0;               x= gear.n.z;          z= z + 1
        gear.n.0= z;      if gear.n.z==''  then gear.n.0= 1
        if isNum(x)  then do;     $= $ x;    iterate;    end   /*found a number, use it.*/
        xx= x                                    /*different gear, keep switching 'til X*/
           do forever;            nn= xx
           if gear.nn.0==''  then call error "a gear is using an unknown gear name:"  x
           zz= gear.nn.0;         xx= gear.nn.zz
           zz= zz + 1;   gear.nn.0= zz;   if gear.nn.zz==''  then gear.nn.0= 1
           if isNum(xx)  then do;  $= $ xx;  iterate dummy;  end
           end   /*forever*/                     /* [↑]  found a number,  now use FIRST.*/
        end      /*dummy*/                       /*"DUMMY"  is needed for the  ITERATE. */
     say '('lim "results): "  strip($);      say;          say;          return