home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / s / sd-27.zip / sdmoves.c < prev    next >
C/C++ Source or Header  |  1992-11-05  |  72KB  |  1,695 lines

  1. /* SD -- square dance caller's helper.
  2.  
  3.     Copyright (C) 1990, 1991, 1992  William B. Ackerman.
  4.  
  5.     This program is free software; you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation; either version 1, or (at your option)
  8.     any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19.     This is for version 27. */
  20.  
  21. /* This defines the following functions:
  22.    canonicalize_rotation
  23.    reinstate_rotation
  24.    move
  25. */
  26.  
  27. #include "sd.h"
  28.  
  29.  
  30.  
  31.  
  32. extern void canonicalize_rotation(setup *result) {
  33.  
  34.    if (result->kind == s_1x1) {
  35.       (void) copy_rot(result, 0, result, 0, (result->rotation & 3) * 011);
  36.       result->rotation = 0;
  37.    }
  38.    else if ((result->kind == s4x4) ||
  39.             (result->kind == s2x2) ||
  40.             (result->kind == s_star) ||
  41.             (result->kind == s_bigblob) ||
  42.             (result->kind == s_c1phan) ||
  43.             (result->kind == s_hyperglass) ||
  44.             (result->kind == s_galaxy)) {
  45.       /* The setup has 4-way symmetry.  We can canonicalize it so the
  46.          result rotation is zero. */
  47.       int i, rot, rot11, delta, bigd, i0, i1, i2, i3, j0, j1, j2, j3;
  48.       personrec x0, x1, x2, x3;
  49.  
  50.       rot = result->rotation & 3;
  51.       if (rot == 0) return;
  52.       rot11 = rot * 011;
  53.       bigd = setup_limits[result->kind] + 1;
  54.       delta = bigd >> 2;
  55.  
  56.       i0 = 1;
  57.       i1 = i0 + delta;
  58.       i2 = i1 + delta;
  59.       i3 = i2 + delta;
  60.       j0 = (rot-4)*delta+1;
  61.       j1 = j0 + delta;
  62.       j2 = j1 + delta;
  63.       j3 = j2 + delta;
  64.       for (i=0; i<delta; i++) {
  65.          if ((--i0) < 0) i0 += bigd;
  66.          if ((--i1) < 0) i1 += bigd;
  67.          if ((--i2) < 0) i2 += bigd;
  68.          if ((--i3) < 0) i3 += bigd;
  69.          if ((--j0) < 0) j0 += bigd;
  70.          if ((--j1) < 0) j1 += bigd;
  71.          if ((--j2) < 0) j2 += bigd;
  72.          if ((--j3) < 0) j3 += bigd;
  73.          x0 = result->people[i0];
  74.          x1 = result->people[i1];
  75.          x2 = result->people[i2];
  76.          x3 = result->people[i3];
  77.          result->people[j0].id1 = rotperson(x0.id1, rot11);
  78.          result->people[j0].id2 = x0.id2;
  79.          result->people[j1].id1 = rotperson(x1.id1, rot11);
  80.          result->people[j1].id2 = x1.id2;
  81.          result->people[j2].id1 = rotperson(x2.id1, rot11);
  82.          result->people[j2].id2 = x2.id2;
  83.          result->people[j3].id1 = rotperson(x3.id1, rot11);
  84.          result->people[j3].id2 = x3.id2;
  85.       }
  86.  
  87.       result->rotation = 0;
  88.    }
  89.    else if (result->kind == s_1x3) {
  90.       if (result->rotation & 2) {
  91.  
  92.          /* Must turn this setup upside-down. */
  93.    
  94.          swap_people(result, 0, 2);
  95.          (void) copy_rot(result, 0, result, 0, 022);
  96.          (void) copy_rot(result, 1, result, 1, 022);
  97.          (void) copy_rot(result, 2, result, 2, 022);
  98.       }
  99.       result->rotation &= 1;
  100.    }
  101.    else if (((setup_limits[result->kind] & ~07776) == 1)) {
  102.       /* We have a setup of an even number of people.  We know how to canonicalize
  103.          this.  The resulting rotation should be 0 or 1. */
  104.  
  105.       if (result->rotation & 2) {
  106.  
  107.          /* Must turn this setup upside-down. */
  108.    
  109.          int i, offs;
  110.    
  111.          offs = (setup_limits[result->kind]+1) >> 1;     /* Half the setup size. */
  112.    
  113.          for (i=0; i<offs; i++) {
  114.             swap_people(result, i, i+offs);
  115.             (void) copy_rot(result, i, result, i, 022);
  116.             (void) copy_rot(result, i+offs, result, i+offs, 022);
  117.          }
  118.       }
  119.       result->rotation &= 1;
  120.    }
  121.    else
  122.       result->rotation &= 3;
  123. }
  124.  
  125.  
  126. extern void reinstate_rotation(setup *ss, setup *result) {
  127.  
  128.    int globalrotation;
  129.  
  130.    switch (ss->kind) {
  131.       case s_normal_concentric:
  132.          globalrotation = 0;
  133.          break;
  134.       default:
  135.          globalrotation = ss->rotation;
  136.    }
  137.    
  138.    switch (result->kind) {
  139.       case s_normal_concentric:
  140.          result->inner.srotation += globalrotation;
  141.          result->outer.srotation += globalrotation;
  142.          break;
  143.       case nothing:
  144.          break;
  145.       default:
  146.          result->rotation += globalrotation;
  147.          break;
  148.    }
  149.  
  150.    canonicalize_rotation(result);
  151. }
  152.  
  153.  
  154.  
  155. typedef struct gloop {
  156.     int x;                  /* This person's coordinates, calibrated so that a matrix */
  157.     int y;                  /*   position cooresponds to an increase by 4. */
  158.     long_boolean sel;       /* True if this person is selected.  (False if selectors not in use.) */
  159.     long_boolean done;      /* Used for loop control on each pass */
  160.     long_boolean realdone;  /* Used for loop control on each pass */
  161.     int boybit;             /* 1 if boy, 0 if not (might be neither). */
  162.     int girlbit;            /* 1 if girl, 0 if not (might be neither). */ 
  163.     int dir;                /* This person's initial facing direction, 0 to 3. */
  164.     int deltax;             /* How this person will move, relative to his own facing */
  165.     int deltay;             /*   direction, when call is finally executed. */
  166.     int deltarot;           /* How this person will turn. */
  167.     int rollinfo;           /* How this person's roll info will be set. */
  168.     struct gloop *nextse;   /* Points to next person south (dir even) or east (dir odd.) */
  169.     struct gloop *nextnw;   /* Points to next person north (dir even) or west (dir odd.) */
  170.     long_boolean tbstopse;  /* True if nextse/nextnw is zero because the next spot */
  171.     long_boolean tbstopnw;  /*   is occupied by a T-boned person (as opposed to being empty.) */
  172.     } matrix_rec;
  173.  
  174.  
  175.  
  176.  
  177. /* This function is internal. */
  178.  
  179. static void start_matrix_call(
  180.    setup *ss,
  181.    int *nump,
  182.    matrix_rec matrix_info[],
  183.    long_boolean use_selector,
  184.    setup *people)
  185.  
  186. {
  187.    int i;
  188.    coordrec *thingyptr;
  189.  
  190.    clear_people(people);
  191.    
  192.    thingyptr = setup_coords[ss->kind];
  193.    if (!thingyptr) fail("Can't do this in this setup.");
  194.    
  195.    if (setup_limits[ss->kind] < 0) fail("Can't do this in this setup.");        /* this is actually superfluous */
  196.    
  197.    *nump = 0;
  198.    for (i=0; i<=setup_limits[ss->kind]; i++) {
  199.       if (ss->people[i].id1) {
  200.          if (*nump == 8) fail("?????too many people????");
  201.          (void) copy_person(people, *nump, ss, i);
  202.          matrix_info[*nump].x = thingyptr->xca[i];
  203.          matrix_info[*nump].y = thingyptr->yca[i];
  204.  
  205.          matrix_info[*nump].done = FALSE;
  206.          matrix_info[*nump].realdone = FALSE;
  207.  
  208.          if (use_selector)
  209.             matrix_info[*nump].sel = selectp(people, *nump);
  210.          else
  211.             matrix_info[*nump].sel = FALSE;
  212.  
  213.          matrix_info[*nump].dir = people->people[*nump].id1 & 3;
  214.    
  215.          matrix_info[*nump].girlbit = (people->people[*nump].id2 & ID2_GIRL) ? 1 : 0;
  216.          matrix_info[*nump].boybit = (people->people[*nump].id2 & ID2_BOY) ? 1 : 0;
  217.          matrix_info[*nump].nextse = 0;
  218.          matrix_info[*nump].nextnw = 0;
  219.          matrix_info[*nump].deltax = 0;
  220.          matrix_info[*nump].deltay = 0;
  221.          matrix_info[*nump].deltarot = 0;
  222.          matrix_info[*nump].rollinfo = ROLLBITM;
  223.          matrix_info[*nump].tbstopse = FALSE;
  224.          matrix_info[*nump].tbstopnw = FALSE;
  225.  
  226.          (*nump)++;
  227.       }
  228.    }
  229. }
  230.  
  231.  
  232. /* This function is internal. */
  233.  
  234. static void finish_matrix_call(
  235.    matrix_rec matrix_info[],
  236.    int nump,
  237.    setup *people,
  238.    setup *result)
  239.  
  240. {
  241.    int i, place;
  242.    int xmax, xpar, ymax, ypar, signature, x, y, k;
  243.    coordrec *checkptr;
  244.  
  245.    xmax = xpar = ymax = ypar = signature = 0;
  246.  
  247.    for (i=0; i<nump; i++) {
  248.       people->people[i].id1 = (rotperson(people->people[i].id1, matrix_info[i].deltarot*011) & (~ROLL_MASK)) | matrix_info[i].rollinfo;
  249.  
  250.       /* If this person's position has low bit on, that means we consider his coordinates
  251.          not sufficiently well-defined that we will allow him to do any pressing or
  252.          trucking.  He is only allowed to turn.  That is, we will require deltax and
  253.          deltay to be zero.  An example of this situation is the points of a galaxy. */
  254.  
  255.       if (((matrix_info[i].x | matrix_info[i].y) & 1) && (matrix_info[i].deltax | matrix_info[i].deltay))
  256.          fail("Someone's ending position is not well defined.");
  257.  
  258.       switch (matrix_info[i].dir) {
  259.          case 0:
  260.             matrix_info[i].x += matrix_info[i].deltax;
  261.             matrix_info[i].y += matrix_info[i].deltay;
  262.             break;
  263.          case 1:
  264.             matrix_info[i].x += matrix_info[i].deltay;
  265.             matrix_info[i].y -= matrix_info[i].deltax;
  266.             break;
  267.          case 2:
  268.             matrix_info[i].x -= matrix_info[i].deltax;
  269.             matrix_info[i].y -= matrix_info[i].deltay;
  270.             break;
  271.          case 3:
  272.             matrix_info[i].x -= matrix_info[i].deltay;
  273.             matrix_info[i].y += matrix_info[i].deltax;
  274.             break;
  275.       }
  276.  
  277.       x = matrix_info[i].x;
  278.       y = matrix_info[i].y;
  279.  
  280.       /* Compute new max, parity, and signature info. */
  281.  
  282.       if ((x < 0) || ((x == 0) && (y < 0))) { x = -x; y = -y; }
  283.       signature |= 1 << ((31000 + 12*x - 11*y) % 31);
  284.       if (y < 0) y = -y;
  285.       /* Now x and y have both had absolute values taken. */
  286.       if (x > xmax) xmax = x;
  287.       if (y > ymax) ymax = y;
  288.       k = x | 4;
  289.       xpar |= (k & (~(k-1)));
  290.       k = y | 4;
  291.       ypar |= (k & (~(k-1)));
  292.    }
  293.  
  294.    ypar |= (xmax << 20) | (xpar << 16) | (ymax << 4);
  295.  
  296.    result->rotation = 0;
  297.  
  298.    if ((ypar == 0x00A20026) && ((signature & (~0x08008404)) == 0)) {
  299.       checkptr = setup_coords[s_rigger];
  300.       goto doit;
  301.    }
  302.    if ((ypar == 0x00770077) && ((signature & (~0x00418004)) == 0)) {
  303.       checkptr = setup_coords[s_galaxy];
  304.       goto doit;
  305.    }
  306.    else if ((ypar == 0x00A20026) && ((signature & (~0x01040420)) == 0)) {
  307.       checkptr = setup_coords[s_bone];
  308.       goto doit;
  309.    }
  310.    else if ((ypar == 0x00840026) && ((signature & (~0x04000308)) == 0)) {
  311.       checkptr = setup_coords[s_spindle];
  312.       goto doit;
  313.    }
  314.    else if ((ypar == 0x00A200A2) && ((signature & (~0x101CC4E6)) == 0)) {
  315.       checkptr = setup_coords[s_bigblob];
  316.       goto doit;
  317.    }
  318.    else if ((ypar == 0x00670055) && ((signature & (~0x01000420)) == 0)) {
  319.       checkptr = setup_coords[s_qtag];
  320.       goto doit;
  321.    }
  322.    else if ((ypar == 0x00550067) && ((signature & (~0x08410200)) == 0)) {
  323.       checkptr = setup_coords[s_qtag];
  324.       goto doitrot;
  325.    }
  326.    else if ((ypar == 0x00A60055) && ((signature & (~0x09000480)) == 0)) {
  327.       checkptr = setup_coords[s_3x1dmd];
  328.       goto doit;
  329.    }
  330.    /* If certain far out people are missing, xmax will be different, but we will
  331.        still need to go to a 3dmd. */
  332.    else if (((ypar == 0x00A70055) || (ypar == 0x00770055) || (ypar == 0x00730055)) && ((signature & (~0x29008480)) == 0)) {
  333.       checkptr = setup_coords[s_3dmd];
  334.       goto doit;
  335.    }
  336.    /* If certain far out people are missing, xmax will be different, but we will
  337.        still need to go to a 4dmd. */
  338.    else if (((ypar == 0x00E30055) || (ypar == 0x00B30055) || (ypar == 0x00A30055)) && ((signature & (~0x0940A422)) == 0)) {
  339.       checkptr = setup_coords[s_4dmd];
  340.       goto doit;
  341.    }
  342.    else if ((ypar == 0x00550057) && ((signature & (~0x20000620)) == 0)) {
  343.       checkptr = setup_coords[s_hrglass];
  344.       goto doit;
  345.    }
  346.    else if ((ypar == 0x00620044) && ((signature & (~0x11800C40)) == 0)) {
  347.       checkptr = setup_coords[s3x4];
  348.       goto doit;
  349.    }
  350.    else if ((ypar == 0x00440062) && ((signature & (~0x0C202300)) == 0)) {
  351.       checkptr = setup_coords[s3x4];
  352.       goto doitrot;
  353.    }
  354.    else if ((ypar == 0x00E20004) && ((signature & (~0x09002400)) == 0)) {
  355.       checkptr = setup_coords[s1x8];
  356.       goto doit;
  357.    }
  358.    else if ((ypar == 0x000400E2) && ((signature & (~0x08004202)) == 0)) {
  359.       checkptr = setup_coords[s1x8];
  360.       goto doitrot;
  361.    }
  362.    else if ((ypar == 0x01220004) && ((signature & (~0x49002400)) == 0)) {
  363.       checkptr = setup_coords[s1x10];
  364.       goto doit;
  365.    }
  366.    else if ((ypar == 0x01620004) && ((signature & (~0x49012400)) == 0)) {
  367.       checkptr = setup_coords[s1x12];
  368.       goto doit;
  369.    }
  370.    else if ((ypar == 0x01A20004) && ((signature & (~0x49012404)) == 0)) {
  371.       checkptr = setup_coords[s1x14];
  372.       goto doit;
  373.    }
  374.    else if ((ypar == 0x01E20004) && ((signature & (~0x49092404)) == 0)) {
  375.       checkptr = setup_coords[s1x16];
  376.       goto doit;
  377.    }
  378.    else if ((ypar == 0x00620022) && ((signature & (~0x00088006)) == 0)) {
  379.       checkptr = setup_coords[s2x4];
  380.       goto doit;
  381.    }
  382.    else if ((ypar == 0x00220062) && ((signature & (~0x10108004)) == 0)) {
  383.       checkptr = setup_coords[s2x4];
  384.       goto doitrot;
  385.    }
  386.    else if ((ypar == 0x00A20022) && ((signature & (~0x000C8026)) == 0)) {
  387.       checkptr = setup_coords[s2x6];
  388.       goto doit;
  389.    }
  390.    else if ((ypar == 0x002200A2) && ((signature & (~0x10108484)) == 0)) {
  391.       checkptr = setup_coords[s2x6];
  392.       goto doitrot;
  393.    }
  394.    else if ((ypar == 0x00E20022) && ((signature & (~0x004C8036)) == 0)) {
  395.       checkptr = setup_coords[s2x8];
  396.       goto doit;
  397.    }
  398.    else if ((ypar == 0x002200E2) && ((signature & (~0x12908484)) == 0)) {
  399.       checkptr = setup_coords[s2x8];
  400.       goto doitrot;
  401.    }
  402.    else if ((ypar == 0x00A20062) && ((signature & (~0x109CC067)) == 0)) {
  403.       checkptr = setup_coords[s4x6];
  404.       goto doit;
  405.    }
  406.    else if ((ypar == 0x006200A2) && ((signature & (~0x1918C4C6)) == 0)) {
  407.       checkptr = setup_coords[s4x6];
  408.       goto doitrot;
  409.    }
  410.    else if ((ypar == 0x00620062) && ((signature & (~0x1018C046)) == 0)) {
  411.       checkptr = setup_coords[s4x4];
  412.       goto doit;
  413.    }
  414.    /* **** These last ones are sort of a crock.  They are designed to make
  415.       matrix calls work in distorted or virtual setups in some circumstances
  416.       (i.e. if no one changes coordinates.)  However, they won't work in the
  417.       presence of unsymmetrical phantoms.  What we really should do is, if
  418.       the setup is virtual/distorted (or maybe the test should be if no one
  419.       moved) just force people back to the same setup they started in. */
  420.    else if ((ypar == 0x00220022) && ((signature & (~0x00008004)) == 0)) {
  421.       checkptr = setup_coords[s2x2];
  422.       goto doit;
  423.    }
  424.    else if ((ypar == 0x00620004) && ((signature & (~0x01000400)) == 0)) {
  425.       checkptr = setup_coords[s1x4];
  426.       goto doit;
  427.    }
  428.    else if ((ypar == 0x00220004) && ((signature & (~0x01000000)) == 0)) {
  429.       checkptr = setup_coords[s_1x2];
  430.       goto doit;
  431.    }
  432.  
  433.    fail("Can't handle this result matrix.");
  434.  
  435.    
  436.  
  437. doit:
  438.       result->kind = checkptr->result_kind;
  439.       for (i=0; i<nump; i++) {
  440.          place = checkptr->diagram[28 - ((matrix_info[i].y >> 2) << checkptr->xfactor) + (matrix_info[i].x >> 2)];
  441.          if (place < 0) fail("Person has moved into a grossly ill-defined location.");
  442.          if ((checkptr->xca[place] != matrix_info[i].x) || (checkptr->yca[place] != matrix_info[i].y))
  443.             fail("Person has moved into a slightly ill-defined location.");
  444.          install_person(result, place, people, i);
  445.          result->people[place].id1 &= ~STABLE_MASK;   /* For now, can't do fractional stable on this kind of call. */
  446.       }
  447.       
  448.       return;
  449.  
  450. doitrot:
  451.       result->kind = checkptr->result_kind;
  452.       result->rotation = 1;   
  453.       for (i=0; i<nump; i++) {
  454.          place = checkptr->diagram[27 - ((matrix_info[i].x >> 2) << checkptr->xfactor) - (matrix_info[i].y >> 2)];
  455.          if (place < 0) fail("Person has moved into a grossly ill-defined location.");
  456.          if ((checkptr->xca[place] != -matrix_info[i].y) || (checkptr->yca[place] != matrix_info[i].x))
  457.             fail("Person has moved into a slightly ill-defined location.");
  458.          install_rot(result, place, people, i, 033);
  459.          result->people[place].id1 &= ~STABLE_MASK;   /* For now, can't do fractional stable on this kind of call. */
  460.       }
  461.       
  462.       return;
  463. }
  464.  
  465.  
  466.  
  467. /* This function is internal. */
  468.  
  469. static void matrixmove(
  470.    setup *ss,
  471.    callspec_block *callspec,
  472.    setup *result)
  473.  
  474. {
  475.    int datum;
  476.    setup people;
  477.    matrix_rec matrix_info[9];
  478.    int i, nump, alldelta;
  479.  
  480.    alldelta = 0;
  481.  
  482.    start_matrix_call(ss, &nump, matrix_info, TRUE, &people);
  483.  
  484.    for (i=0; i<nump; i++) {
  485.       if (matrix_info[i].sel) {
  486.          /* This is legal if girlbit or boybit is on (in which case we use the appropriate datum)
  487.             or if the two data are identical so the sex doesn't matter. */
  488.          if ((matrix_info[i].girlbit | matrix_info[i].boybit) == 0 &&
  489.                   (callspec->stuff.matrix.stuff[0] != callspec->stuff.matrix.stuff[1]))
  490.             fail("Can't determine sex of this person.");
  491.  
  492.          datum = callspec->stuff.matrix.stuff[matrix_info[i].girlbit];
  493.          alldelta |= (  matrix_info[i].deltax = ( ((datum >> 7) & 0x1F) - 16) << 1  );
  494.          alldelta |= (  matrix_info[i].deltay = ( ((datum >> 2) & 0x1F) - 16) << 1  );
  495.          matrix_info[i].deltarot = datum & 03;
  496.          matrix_info[i].rollinfo = (datum >> 12) * ROLLBITR;
  497.       }
  498.    }
  499.  
  500.    if ((alldelta != 0) && (ss->setupflags & SETUPFLAG__DISTORTED))
  501.       fail("This call not allowed in distorted or virtual setup.");
  502.    
  503.    finish_matrix_call(matrix_info, nump, &people, result);
  504. }
  505.  
  506.  
  507. /* This function is internal. */
  508.  
  509. static void do_pair(
  510.    matrix_rec *ppp,        /* Selected person */
  511.    matrix_rec *qqq,        /* Unselected person */
  512.    callspec_block *callspec,
  513.    int flip,
  514.    int filter)             /* 1 to do N/S facers, 0 for E/W facers. */
  515. {
  516.    int base;
  517.    int datum;
  518.    int flags;
  519.  
  520.    flags = callspec->stuff.matrix.flags;
  521.  
  522.    if ((filter ^ ppp->dir) & 1) {
  523.       base = (ppp->dir & 2) ? 6 : 4;
  524.       if (!(flags & MTX_USE_SELECTOR)) base &= 3;
  525.       base ^= flip;
  526.  
  527.       /* This is legal if girlbit or boybit is on (in which case we use the appropriate datum)
  528.          or if the two data are identical so the sex doesn't matter. */
  529.       if ((ppp->girlbit | ppp->boybit) == 0 &&
  530.                (callspec->stuff.matrix.stuff[base] != callspec->stuff.matrix.stuff[base+1]))
  531.          fail("Can't determine sex of this person.");
  532.  
  533.       datum = callspec->stuff.matrix.stuff[base+ppp->girlbit];
  534.       if (datum == 0) fail("Can't do this call.");
  535.    
  536.       ppp->deltax = (((datum >> 7) & 0x1F) - 16) << 1;
  537.       ppp->deltay = (((datum >> 2) & 0x1F) - 16) << 1;
  538.       ppp->deltarot = datum & 3;
  539.       ppp->rollinfo = (datum >> 12) * ROLLBITR;
  540.       ppp->realdone = TRUE;
  541.    }
  542.    ppp->done = TRUE;
  543.  
  544.    if ((filter ^ qqq->dir) & 1) {
  545.       base = (qqq->dir & 2) ? 0 : 2;
  546.       if (flags & MTX_IGNORE_NONSELECTEES) base |= 4;
  547.       base ^= flip;
  548.  
  549.       /* This is legal if girlbit or boybit is on (in which case we use the appropriate datum)
  550.          or if the two data are identical so the sex doesn't matter. */
  551.       if ((qqq->girlbit | qqq->boybit) == 0 &&
  552.                (callspec->stuff.matrix.stuff[base] != callspec->stuff.matrix.stuff[base+1]))
  553.          fail("Can't determine sex of this person.");
  554.  
  555.       datum = callspec->stuff.matrix.stuff[base+qqq->girlbit];
  556.       if (datum == 0) fail("Can't do this call.");
  557.  
  558.       qqq->deltax = (((datum >> 7) & 0x1F) - 16) << 1;
  559.       qqq->deltay = (((datum >> 2) & 0x1F) - 16) << 1;
  560.       qqq->deltarot = datum & 3;
  561.       qqq->rollinfo = (datum >> 12) * ROLLBITR;
  562.       qqq->realdone = TRUE;
  563.    }
  564.    qqq->done = TRUE;
  565. }
  566.  
  567.  
  568. /* This function is internal. */
  569.  
  570. static void do_matrix_chains(
  571.    matrix_rec matrix_info[],
  572.    int nump,
  573.    callspec_block *callspec,
  574.    int filter)                        /* 1 for E/W chains, 0 for N/S chains. */
  575.  
  576. {
  577.    long_boolean another_round;
  578.    int i, j, flags;
  579.  
  580.    flags = callspec->stuff.matrix.flags;
  581.  
  582.    /* Find adjacency relationships, and fill in the "se"/"nw" pointers. */
  583.  
  584.    for (i=0; i<nump; i++) {
  585.       if ((flags & MTX_IGNORE_NONSELECTEES) && (!matrix_info[i].sel)) continue;
  586.       for (j=0; j<nump; j++) {
  587.          if ((flags & MTX_IGNORE_NONSELECTEES) && (!matrix_info[j].sel)) continue;
  588.          /* Find out if these people are adjacent in the right way. */
  589.  
  590.          if (    ( filter && matrix_info[j].x == matrix_info[i].x + 4 && matrix_info[j].y == matrix_info[i].y)
  591.             ||   (!filter && matrix_info[j].y == matrix_info[i].y - 4 && matrix_info[j].x == matrix_info[i].x)   ) {
  592.  
  593.             /* Now, if filter = 1, person j is just east of person i.
  594.                If filter = 0, person j is just south of person i. */
  595.  
  596.             if (flags & MTX_TBONE_IS_OK) {
  597.                matrix_info[i].nextse = &matrix_info[j];     /* Make the chain independently of facing direction. */
  598.                matrix_info[j].nextnw = &matrix_info[i];
  599.             }
  600.             else {
  601.                if ((matrix_info[i].dir ^ filter) & 1) {
  602.                   if ((matrix_info[i].dir ^ matrix_info[j].dir) & 1) {
  603.                      if (!(flags & MTX_STOP_AND_WARN_ON_TBONE)) fail("People are T-boned.");
  604.                      matrix_info[i].tbstopse = TRUE;
  605.                      matrix_info[j].tbstopnw = TRUE;
  606.                   }
  607.                   else {
  608.                      if ((flags & MTX_MUST_FACE_SAME_WAY) && (matrix_info[i].dir ^ matrix_info[j].dir))
  609.                         fail("Paired people must face the same way.");
  610.                      matrix_info[i].nextse = &matrix_info[j];
  611.                      matrix_info[j].nextnw = &matrix_info[i];
  612.                   }
  613.                }
  614.             }
  615.             break;
  616.          }
  617.       }
  618.    }
  619.  
  620.    /* Pick out pairs of people and move them. */
  621.  
  622.    another_round = TRUE;
  623.  
  624.    while (another_round) {
  625.       another_round = FALSE;
  626.  
  627.       for (i=0; i<nump; i++) {
  628.          if (!matrix_info[i].done) {
  629.  
  630.             /* This person might be ready to be paired up with someone. */
  631.  
  632.             if (matrix_info[i].nextse) {
  633.                if (matrix_info[i].nextnw)
  634.                   /* This person has neighbors on both sides.  Can't do anything yet. */
  635.                   ;
  636.                else {
  637.                   /* This person has a neighbor on south/east side only. */
  638.  
  639.                   if (matrix_info[i].tbstopnw) warn(warn__not_tbone_person);
  640.  
  641.                   if (   (!(flags & MTX_USE_SELECTOR))  ||  matrix_info[i].sel   ) {
  642.                      if ((!(flags & MTX_IGNORE_NONSELECTEES)) && matrix_info[i].nextse->sel) {
  643.                         fail("Two adjacent selected people.");
  644.                      }
  645.                      else {
  646.  
  647.                         /* Do this pair.  First, chop the pair off from anyone else. */
  648.  
  649.                         if (matrix_info[i].nextse->nextse) matrix_info[i].nextse->nextse->nextnw = 0;
  650.                         matrix_info[i].nextse->nextse = 0;
  651.                         another_round = TRUE;
  652.                         do_pair(&matrix_info[i], matrix_info[i].nextse, callspec, 0, filter);
  653.                      }
  654.                   }
  655.                }
  656.             }
  657.             else {
  658.                if (matrix_info[i].nextnw) {
  659.                   /* This person has a neighbor on north/west side only. */
  660.  
  661.                   if (matrix_info[i].tbstopse) warn(warn__not_tbone_person);
  662.  
  663.                   if (   (!(flags & MTX_USE_SELECTOR))  ||  matrix_info[i].sel   ) {
  664.                      if ((!(flags & MTX_IGNORE_NONSELECTEES)) && matrix_info[i].nextnw->sel) {
  665.                         fail("Two adjacent selected people.");
  666.                      }
  667.                      else {
  668.                         /* Do this pair.  First, chop the pair off from anyone else. */
  669.  
  670.                         if (matrix_info[i].nextnw->nextnw) matrix_info[i].nextnw->nextnw->nextse = 0;
  671.                         matrix_info[i].nextnw->nextnw = 0;
  672.                         another_round = TRUE;
  673.                         do_pair(&matrix_info[i], matrix_info[i].nextnw, callspec, 2, filter);
  674.                      }
  675.                   }
  676.                }
  677.                else {
  678.                   /* Person is alone.  If this is his lateral axis, mark him done and don't move him. */
  679.  
  680.                   if ((matrix_info[i].dir ^ filter) & 1) {
  681.                      if (matrix_info[i].tbstopse || matrix_info[i].tbstopnw) warn(warn__not_tbone_person);
  682.                      if (   (!(flags & MTX_USE_SELECTOR))  ||  matrix_info[i].sel   )
  683.                         fail("Person has no one to work with.");
  684.                      matrix_info[i].done = TRUE;
  685.                   }
  686.                }
  687.             }
  688.          }
  689.       }
  690.    }
  691. }
  692.  
  693.  
  694.  
  695.  
  696.  
  697.  
  698. /* This function is internal. */
  699.  
  700. static void partner_matrixmove(
  701.    setup *ss,
  702.    callspec_block *callspec,
  703.    setup *result)
  704.  
  705. {
  706.    int flags;
  707.    setup people;
  708.    matrix_rec matrix_info[9];
  709.    int i, nump;
  710.  
  711.    if (ss->setupflags & SETUPFLAG__DISTORTED)
  712.       fail("This call not allowed in distorted or virtual setup.");
  713.  
  714.    flags = callspec->stuff.matrix.flags;
  715.  
  716.    start_matrix_call(ss, &nump, matrix_info, flags & MTX_USE_SELECTOR, &people);
  717.  
  718.    /* Make the lateral chains first. */
  719.  
  720.    do_matrix_chains(matrix_info, nump, callspec, 1);
  721.  
  722.    /* Now clean off the pointers in preparation for the second pass. */
  723.  
  724.    for (i=0; i<nump; i++) {
  725.       matrix_info[i].done = FALSE;
  726.       matrix_info[i].nextse = 0;
  727.       matrix_info[i].nextnw = 0;
  728.       matrix_info[i].tbstopse = FALSE;
  729.       matrix_info[i].tbstopnw = FALSE;
  730.    }
  731.  
  732.    /* Vertical chains next. */
  733.  
  734.    do_matrix_chains(matrix_info, nump, callspec, 0);
  735.  
  736.    /* Scan for people who ought to have done something but didn't. */
  737.  
  738.    for (i=0; i<nump; i++) {
  739.       if (!matrix_info[i].realdone) {
  740.          if (   (!(flags & MTX_USE_SELECTOR))  ||  matrix_info[i].sel   ) {
  741.             fail("Person could not identify other person to work with.");
  742.          }
  743.       }
  744.    }
  745.  
  746.    finish_matrix_call(matrix_info, nump, &people, result);
  747. }
  748.  
  749.  
  750. /* This function is internal. */
  751.  
  752. static void rollmove(
  753.    setup *ss,
  754.    callspec_block *callspec,
  755.    setup *result)
  756.  
  757. {
  758.    int i, rot;
  759.  
  760.    if (setup_limits[ss->kind] < 0) fail("Can't roll in this setup.");
  761.    
  762.    result->kind = ss->kind;
  763.    result->rotation = ss->rotation;
  764.    
  765.    for (i=0; i<=setup_limits[ss->kind]; i++) {
  766.       if (ss->people[i].id1) {
  767.          rot = 0;
  768.          if (!(callspec->callflags & cflag__requires_selector) || selectp(ss, i)) {
  769.             switch (ss->people[i].id1 & ROLL_MASK) {
  770.                case ROLLBITL: rot = 033; break;
  771.                case ROLLBITM: break;
  772.                case ROLLBITR: rot = 011; break;
  773.                default: fail("Roll not supported after previous call.");
  774.             }
  775.          }
  776.          install_rot(result, i, ss, i, rot);
  777.          result->people[i].id1 &= ~STABLE_MASK;   /* For now, can't do fractional stable on this kind of call. */
  778.       }
  779.       else
  780.          clear_person(result, i);
  781.    }
  782. }
  783.  
  784.  
  785. /* Strip out those concepts that do not have the "dfm__xxx" flag set saying that they are to be
  786.    inherited to this part of the call.  BUT: the "dfm_inherit_left" (a.k.a. "FINAL__LEFT") flag controls
  787.    both "FINAL__REVERSE" and "FINAL__LEFT", turning the former into the latter.  This makes reverse
  788.    circle by, touch by, and clean sweep work. */
  789.  
  790. static final_set get_mods_for_subcall(final_set new_final_concepts, defmodset this_mod, int callflags)
  791. {
  792.    final_set retval;
  793.  
  794.    retval = new_final_concepts;
  795.  
  796.    /* If this subcall has "inherit_reverse" or "inherit_left" given, but the top-level call
  797.       doesn't permit the corresponding flag to be given, we should turn any "reverse" or
  798.       "left" modifier that was given into the other one, and cause that to be inherited.
  799.       This is what turns, for example, part 3 of "*REVERSE* clean sweep" into a "*LEFT* 1/2 tag". */
  800.  
  801.  
  802.    if (this_mod & ~callflags & (FINAL__REVERSE | FINAL__LEFT)) {
  803.       if (new_final_concepts & (FINAL__REVERSE | FINAL__LEFT))
  804.          retval |= (FINAL__REVERSE | FINAL__LEFT);
  805.    }
  806.  
  807.    retval &= ~(new_final_concepts & HERITABLE_FLAG_MASK & ~this_mod);
  808.    
  809.    return (retval);
  810. }
  811.  
  812.  
  813. static void move_with_real_call(
  814.    setup *ss,
  815.    parse_block *parseptr,
  816.    callspec_block *callspec,
  817.    final_set final_concepts,
  818.    long_boolean qtfudged,
  819.    setup *result)
  820.  
  821. {
  822.    int subcall_index;
  823.    final_set temp_concepts, conc1, conc2;
  824.    long_boolean qtf;
  825.    parse_block *cp1;
  826.    parse_block *cp2;
  827.    warning_info saved_warnings;
  828.    int tbonetest;
  829.    setup tempsetup;
  830.    final_set new_final_concepts;
  831.    callspec_block *call1, *call2;
  832.    calldef_schema the_schema;
  833.    long_boolean mirror;
  834.  
  835.    /* We have a genuine call.  Presumably all serious concepts have been disposed of
  836.       (that is, nothing interesting will be found in parseptr -- it might be
  837.       useful to check that someday) and we just have the callspec and the final
  838.       concepts.
  839.       The first thing we must do is check for a call whose schema is single (cross)
  840.       concentric.  If so, be sure the setup is divided into 1x4's or diamonds. */
  841.  
  842.    the_schema = callspec->schema;
  843.    if (the_schema == schema_maybe_single_concentric)
  844.       the_schema = (final_concepts & FINAL__SINGLE) ? schema_single_concentric : schema_concentric;
  845.    else if (the_schema == schema_maybe_matrix_conc_star)
  846.       the_schema = (final_concepts & FINAL__12_MATRIX) ? schema_conc_star12 : schema_conc_star;
  847.  
  848.    /* Do some quick error checking for visible fractions.  For now, either flag is acceptable.  Later, we will
  849.       distinguish between the "visible_fractions" and "first_part_visible" flags. */
  850.    if ((ss->setupflags & SETUPFLAG__FRACTIONALIZE_MASK) &&
  851.             (((the_schema != schema_sequential) && (the_schema != schema_split_sequential)) || (!(callspec->callflags & (cflag__visible_fractions | cflag__first_part_visible)))))
  852.       fail("This call can't be fractionalized.");
  853.  
  854.    switch (the_schema) {
  855.       case schema_single_concentric:
  856.       case schema_single_cross_concentric:
  857.          switch (ss->kind) {
  858.             case s2x4:
  859.                divided_setup_move(ss, parseptr, callspec, final_concepts, (*map_lists[s1x4][1])[MPKIND__SPLIT][1], phantest_ok, TRUE, result);
  860.                return;
  861.             case s1x8:
  862.                divided_setup_move(ss, parseptr, callspec, final_concepts, (*map_lists[s1x4][1])[MPKIND__SPLIT][0], phantest_ok, TRUE, result);
  863.                return;
  864.             case s_qtag:
  865.                divided_setup_move(ss, parseptr, callspec, final_concepts, (*map_lists[sdmd][1])[MPKIND__SPLIT][1], phantest_ok, TRUE, result);
  866.                return;
  867.             case s_ptpd:
  868.                divided_setup_move(ss, parseptr, callspec, final_concepts, (*map_lists[sdmd][1])[MPKIND__SPLIT][0], phantest_ok, TRUE, result);
  869.                return;
  870.          }
  871.    }
  872.  
  873.    /* If the "diamond" concept has been given and the call doesn't want it, we do
  874.       the "diamond single wheel" variety. */
  875.  
  876.    if (FINAL__DIAMOND & final_concepts & (~callspec->callflags))  {
  877.       /* If the call is sequentially or concentrically defined, the top level flag is required
  878.          before the diamond concept can be inherited.  Since that flag is off, it is an error. */
  879.       if (the_schema != schema_by_array)
  880.          fail("Can't do this call with the 'diamond' concept.");
  881.  
  882.       ss->setupflags |= SETUPFLAG__NO_EXPAND_MATRIX;
  883.  
  884.       switch (ss->kind) {
  885.          case sdmd:
  886.             divided_setup_move(ss, parseptr, callspec, final_concepts & ~FINAL__DIAMOND,
  887.                   (*map_lists[s_1x2][1])[MPKIND__DMD_STUFF][0], phantest_ok, TRUE, result);
  888.             return;
  889.          case s_qtag:
  890.             /* If in a qtag, perhaps we ought to divide into single diamonds and try again.
  891.                BUT: if "magic" or "interlocked" is also present, we don't.  We let basic_move deal with
  892.                it.  It will come back here after it has done what it needs to. */
  893.    
  894.             if ((final_concepts & (FINAL__MAGIC | FINAL__INTERLOCKED)) == 0) {
  895.                /* Divide into diamonds and try again.  Note that we do not clear the concept. */
  896.                divided_setup_move(ss, parseptr, callspec, final_concepts,
  897.                      (*map_lists[sdmd][1])[MPKIND__SPLIT][1], phantest_ok, FALSE, result);
  898.                return;
  899.             }
  900.             break;
  901.          case s_ptpd:
  902.             /* If in point-to-point diamonds, perhaps we ought to divide into single diamonds and try again.
  903.                BUT: if "magic" or "interlocked" is also present, we don't.  We let basic_move deal with
  904.                it.  It will come back here after it has done what it needs to. */
  905.    
  906.             if ((final_concepts & (FINAL__MAGIC | FINAL__INTERLOCKED)) == 0) {
  907.                /* Divide into diamonds and try again.  Note that we do not clear the concept. */
  908.                divided_setup_move(ss, parseptr, callspec, final_concepts,
  909.                      (*map_lists[sdmd][1])[MPKIND__SPLIT][0], phantest_ok, FALSE, result);
  910.                return;
  911.             }
  912.             break;
  913.          default: fail("Must have diamonds for this concept.");
  914.       }
  915.    }
  916.  
  917.    mirror = FALSE;
  918.  
  919.    /* It may be appropriate to step to a wave or rear back from one.
  920.       This is only legal if the flag forbidding same is off.
  921.       Furthermore, if certain modifiers have been given, we don't allow it. */
  922.  
  923.    if (final_concepts & (FINAL__MAGIC | FINAL__INTERLOCKED | FINAL__12_MATRIX | FINAL__FUNNY))
  924.       ss->setupflags |= SETUPFLAG__NO_STEP_TO_WAVE;
  925.  
  926.    /* But, alas, if fractionalization is on, we can't do it yet, because we don't
  927.       know whether we are starting at the beginning.  In the case of fractionalization,
  928.       we will do it later.  We already know that the call is sequentially defined. */
  929.  
  930.    if ((!(ss->setupflags & (SETUPFLAG__NO_STEP_TO_WAVE | SETUPFLAG__FRACTIONALIZE_MASK))) &&
  931.          (callspec->callflags & (cflag__rear_back_from_r_wave | cflag__rear_back_from_qtag | cflag__step_to_wave))) {
  932.  
  933.       ss->setupflags |= SETUPFLAG__NO_STEP_TO_WAVE;  /* Can only do it once. */
  934.  
  935.       if (final_concepts & FINAL__LEFT) {
  936.          mirror = TRUE;
  937.          mirror_this(ss);
  938.       }
  939.  
  940.       if (setup_limits[ss->kind] >= 0)          /* We don't understand absurd setups. */
  941.          touch_or_rear_back(ss, callspec->callflags);
  942.    }
  943.  
  944.    if (ss->setupflags & SETUPFLAG__DOING_ENDS) {
  945.       /* Check for special case of ends doing a call like "detour" which specifically
  946.          allows just the ends part to be done. */
  947.       ss->setupflags |= SETUPFLAG__NO_EXPAND_MATRIX;
  948.       if ((the_schema == schema_concentric || the_schema == schema_rev_checkpoint) &&
  949.             (dfm_endscando & callspec->stuff.conc.outerdef.modifiers)) {
  950.  
  951.          /* Copy the concentricity flags from the call definition into the setup.  All the fuss
  952.             in database.h about concentricity flags co-existing with setupflags refers
  953.             to this moment. */
  954.          ss->setupflags |= (callspec->stuff.conc.outerdef.modifiers & DFM_CONCENTRICITY_FLAG_MASK);
  955.  
  956.          callspec = base_calls[callspec->stuff.conc.outerdef.call_id];
  957.          the_schema = callspec->schema;
  958.          if (the_schema == schema_maybe_single_concentric)
  959.             the_schema = (final_concepts & FINAL__SINGLE) ? schema_single_concentric : schema_concentric;
  960.          else if (the_schema == schema_maybe_matrix_conc_star)
  961.             the_schema = (final_concepts & FINAL__12_MATRIX) ? schema_conc_star12 : schema_conc_star;
  962.       }
  963.    }
  964.  
  965.    /* Enforce the restriction that only tagging or scooting calls are allowed in certain contexts. */
  966.  
  967.    if ((final_concepts & FINAL__MUST_BE_TAG) && (!(cflag__is_tag_call & callspec->callflags)))
  968.       fail("Only a tagging call is allowed here.");
  969.    else if ((final_concepts & FINAL__MUST_BE_SCOOT) && (!(cflag__is_scoot_call & callspec->callflags)))
  970.       fail("Only a scoot/tag (chain thru) (and scatter) call is allowed here.");
  971.  
  972.    final_concepts &= ~(FINAL__MUST_BE_TAG | FINAL__MUST_BE_SCOOT);
  973.  
  974.    /* If the "split" concept has been given and this call uses that concept for a special
  975.       meaning (split square thru, split dixie style), set the special flag to determine that
  976.       action, and remove the split concept.  Why remove it?  So that "heads split catch grand
  977.       mix 3" will work.  If we are doing a "split catch", we don't really want to split the
  978.       setup into 2x2's that are isolated from each other. */
  979.  
  980.    if (final_concepts & FINAL__SPLIT) {
  981.       ss->setupflags |= SETUPFLAG__NO_EXPAND_MATRIX;
  982.       if (callspec->callflags & cflag__split_like_square_thru)
  983.          final_concepts = (final_concepts | FINAL__SPLIT_SQUARE_APPROVED) & (~FINAL__SPLIT);
  984.       else if (callspec->callflags & cflag__split_like_dixie_style)
  985.          final_concepts = (final_concepts | FINAL__SPLIT_DIXIE_APPROVED) & (~FINAL__SPLIT);
  986.    }
  987.  
  988.    /* NOTE: We may have mirror-reflected the setup.  "Mirror" is true if so.  We may need to undo this. */
  989.  
  990.    /* If this is the "split sequential" schema and we have not already done so,
  991.       cause splitting to take place. */
  992.  
  993.    if (the_schema == schema_split_sequential && !(final_concepts & FINAL__SPLIT_SEQ_DONE))
  994.       final_concepts |= FINAL__SPLIT | FINAL__SPLIT_SEQ_DONE;
  995.  
  996.    /* If the split concept is still present, do it. */
  997.  
  998.    if (final_concepts & FINAL__SPLIT) {
  999.       map_thing *split_map;
  1000.  
  1001.       final_concepts &= ~FINAL__SPLIT;
  1002.       ss->setupflags |= (SETUPFLAG__SAID_SPLIT | SETUPFLAG__NO_EXPAND_MATRIX);
  1003.  
  1004.       /* We can't handle the mirroring, so undo it. */
  1005.       if (mirror) { mirror_this(ss); mirror = FALSE; }
  1006.  
  1007.       if (ss->kind == s2x4) split_map = (*map_lists[s2x2][1])[MPKIND__SPLIT][0];
  1008.       else if (ss->kind == s1x8) split_map = (*map_lists[s1x4][1])[MPKIND__SPLIT][0];
  1009.       else if (ss->kind == s_ptpd) split_map = (*map_lists[sdmd][1])[MPKIND__SPLIT][0];
  1010.       else if (ss->kind == s_qtag) split_map = (*map_lists[sdmd][1])[MPKIND__SPLIT][1];
  1011.       else if (ss->kind == s2x2) {
  1012.          /* "Split" was given while we are already in a 2x2?  The only way that
  1013.             can be legal is if the word "split" was meant as a modifier for "split square thru"
  1014.             etc., rather than as a virtual-setup concept, or if the "split sequential" schema
  1015.             is in use.  In those cases, some "split approved" flag will still be on. */
  1016.  
  1017.          if (!(final_concepts & (FINAL__SPLIT_SQUARE_APPROVED | FINAL__SPLIT_DIXIE_APPROVED | FINAL__SPLIT_SEQ_DONE)))
  1018.             fail("Split concept is meaningless in a 2x2.");
  1019.  
  1020.          move(ss, parseptr, callspec, final_concepts, qtfudged, result);
  1021.          return;
  1022.       }
  1023.       else
  1024.          fail("Can't do split concept in this setup.");
  1025.  
  1026.       divided_setup_move(ss, parseptr, callspec, final_concepts, split_map, phantest_ok, TRUE, result);
  1027.       return;
  1028.    }
  1029.  
  1030.    tbonetest = 0;
  1031.    if (setup_limits[ss->kind] >= 0) {
  1032.       int j;
  1033.  
  1034.       for (j=0; j<=setup_limits[ss->kind]; j++) tbonetest |= ss->people[j].id1;
  1035.       if (!(tbonetest & 011)) {
  1036.          result->kind = nothing;
  1037.          return;
  1038.       }
  1039.    }
  1040.  
  1041.    /* We can't handle the mirroring unless the schema is by_array, so undo it. */
  1042.  
  1043.    if (the_schema != schema_by_array) {
  1044.       if (mirror) { mirror_this(ss); mirror = FALSE; }
  1045.    }
  1046.  
  1047.    switch (the_schema) {
  1048.       case schema_nothing:
  1049.          if (final_concepts) fail("Illegal concept for this call.");
  1050.          *result = *ss;
  1051.          result->setupflags = ((ss->setupflags & SETUPFLAG__ELONGATE_MASK) / SETUPFLAG__ELONGATE_BIT) * RESULTFLAG__ELONGATE_BIT;
  1052.          break;
  1053.       case schema_matrix:
  1054.          if (final_concepts) fail("Illegal concept for this call.");
  1055.          matrixmove(ss, callspec, result);
  1056.          reinstate_rotation(ss, result);
  1057.          break;
  1058.       case schema_partner_matrix:
  1059.          if (final_concepts) fail("Illegal concept for this call.");
  1060.          partner_matrixmove(ss, callspec, result);
  1061.          reinstate_rotation(ss, result);
  1062.          break;
  1063.       case schema_roll:
  1064.          if (final_concepts) fail("Illegal concept for this call.");
  1065.          rollmove(ss, callspec, result);
  1066.          break;
  1067.       case schema_by_array:
  1068.          /* Dispose of the "left" concept first -- it can only mean mirror.  If it is on,
  1069.             mirroring may already have taken place. */
  1070.  
  1071.          if (final_concepts & FINAL__LEFT) {
  1072.             if (!(callspec->callflags & cflag__left_means_mirror)) fail("Can't do this call 'left'.");
  1073.             if (!mirror) mirror_this(ss);
  1074.             mirror = TRUE;
  1075.             final_concepts &= ~FINAL__LEFT;
  1076.          }
  1077.  
  1078.          /* The "reverse" concept might mean mirror, or it might be genuine. */
  1079.  
  1080.          if ((final_concepts & FINAL__REVERSE) && (callspec->callflags & cflag__reverse_means_mirror)) {
  1081.             /* This "reverse" just means mirror. */
  1082.             if (mirror) fail("Can't do this call 'left' and 'reverse'.");
  1083.             if (!mirror) mirror_this(ss);
  1084.             mirror = TRUE;
  1085.             final_concepts &= ~FINAL__REVERSE;
  1086.          }
  1087.  
  1088.          /* If the "reverse" flag is still set in final_concepts, it means a genuine
  1089.             reverse as in reverse cut/flip the diamond or reverse change-O. */
  1090.  
  1091.          basic_move(ss, parseptr, callspec, final_concepts, tbonetest, qtfudged, result);
  1092.          break;
  1093.       default:
  1094.    
  1095.          /* Must be sequential or some form of concentric. */
  1096.    
  1097.          /* ***** This probably isn't right -- we should allow expansion on the first part, and then
  1098.             shut it off for later parts. */
  1099.          ss->setupflags |= SETUPFLAG__NO_EXPAND_MATRIX;
  1100.          new_final_concepts = final_concepts;
  1101.    
  1102.          /* We demand that the final concepts that remain be only those in the following list,
  1103.             which includes all of the "heritable" concepts. */
  1104.  
  1105.          if (new_final_concepts &
  1106.                ~(FINAL__SPLIT | HERITABLE_FLAG_MASK | FINAL__SPLIT_SQUARE_APPROVED | FINAL__SPLIT_DIXIE_APPROVED | FINAL__SPLIT_SEQ_DONE))
  1107.             fail("This concept not allowed here.");
  1108.  
  1109.          /* Now we demand that, if the concept was given, the call had the appropriate flag set saying
  1110.             that the concept is legal and will be inherited to the children.  We make heavy use here
  1111.             of the fact that the "FINAL__XXX" bits and the "cflag__xxx" bits match. */
  1112.  
  1113.          if (HERITABLE_FLAG_MASK & new_final_concepts & (~callspec->callflags)) fail("Can't do this call with this concept.");
  1114.  
  1115.          if (the_schema == schema_sequential || the_schema == schema_split_sequential) {
  1116.             int current_elongation;
  1117.             int finalsetupflags = 0;
  1118.             int highlimit;
  1119.             long_boolean instant_stop;
  1120.             long_boolean first_call = TRUE;
  1121.  
  1122.             qtf = qtfudged;
  1123.  
  1124.             if (new_final_concepts & FINAL__SPLIT) {
  1125.                if (callspec->callflags & cflag__split_like_square_thru)
  1126.                   new_final_concepts |= FINAL__SPLIT_SQUARE_APPROVED;
  1127.                else if (callspec->callflags & cflag__split_like_dixie_style)
  1128.                   new_final_concepts |= FINAL__SPLIT_DIXIE_APPROVED;
  1129.             }
  1130.  
  1131.             /* Iterate over the parts of the call. */
  1132.  
  1133.             highlimit = 1000000;
  1134.             subcall_index = 0;          /* Where we start, in the absence of special stuff. */
  1135.             instant_stop = FALSE;
  1136.  
  1137.             /* If SETUPFLAG__FRACTIONALIZE_MASK stuff is nonzero, we are being asked to do something special.
  1138.                Read the three indicators.  Their meaning is as follows:
  1139.                   high 3 bits   middle 3 bits   low 3 bits
  1140.                      "key"       "denom"          "numer"
  1141.                        2          zero            nonzero       do just the indicated part, set
  1142.                                                                     RESULTFLAG__DID_LAST_PART if it was last
  1143.                      zero        nonzero          nonzero       fractional - do first <numer>/<denom> of the call */
  1144.  
  1145.             if (ss->setupflags & SETUPFLAG__FRACTIONALIZE_MASK) {
  1146.                int numer = ((ss->setupflags & (SETUPFLAG__FRACTIONALIZE_BIT*07))   / SETUPFLAG__FRACTIONALIZE_BIT);
  1147.                int denom = ((ss->setupflags & (SETUPFLAG__FRACTIONALIZE_BIT*070))  / (SETUPFLAG__FRACTIONALIZE_BIT*8));
  1148.                int key   = ((ss->setupflags & (SETUPFLAG__FRACTIONALIZE_BIT*0700)) / (SETUPFLAG__FRACTIONALIZE_BIT*64));
  1149.                int t;
  1150.  
  1151.                for (t=0 ; callspec->stuff.def.defarray[t].call_id; t++);   /* Now t = number of parts in call. */
  1152.  
  1153.                if (key == 2) {
  1154.                   /* Just do the "numer" part of the call, and tell if it was last. */
  1155.                   subcall_index = numer-1;
  1156.                   if (subcall_index >= t) fail("The indicated part number doesn't exist.");
  1157.                   instant_stop = TRUE;
  1158.                   /* If only first part is visible, this is illegal unless we are doing first part. */
  1159.                   if (!(callspec->callflags & cflag__visible_fractions) && (subcall_index != 0))
  1160.                      fail("This call can't be fractionalized.");
  1161.                }
  1162.                else {
  1163.                   /* Do parts up to (key=0) or after (key=1) the indicated part.  The indicated
  1164.                      part may be an absolute part number (denom=0) or a fraction. */
  1165.  
  1166.                   int indicated_part;
  1167.  
  1168.                   if (denom) {
  1169.                      /* Amount to do was given as a fraction. */
  1170.                      if (numer >= denom) fail("Fraction must be proper.");
  1171.                      indicated_part = t * numer;
  1172.                      if ((indicated_part % denom) != 0) fail("This call can't be fractionalized with this fraction.");
  1173.                      indicated_part = indicated_part / denom;
  1174.                      /* If only first part is visible, this is illegal. */
  1175.                      if (!(callspec->callflags & cflag__visible_fractions))
  1176.                         fail("This call can't be fractionalized.");
  1177.                   }
  1178.                   else {
  1179.                      indicated_part = numer;
  1180.                      /* If only first part is visible, this is illegal unless we are breaking just after the first part. */
  1181.                      if (!(callspec->callflags & cflag__visible_fractions) && (indicated_part != 1))
  1182.                         fail("This call can't be fractionalized.");
  1183.                   }
  1184.  
  1185.                   if (key == 1) {
  1186.                      /* Do the last section of the call, starting just after indicated_part. */
  1187.                      subcall_index = indicated_part;
  1188.                      if (subcall_index > t) fail("The indicated part number doesn't exist.");
  1189.                   }
  1190.                   else {
  1191.                      /* Do the first section of the call, up to the indicated_part. */
  1192.                      highlimit = indicated_part;
  1193.                      if (highlimit > t) fail("The indicated part number doesn't exist.");
  1194.                   }
  1195.                }
  1196.             }
  1197.  
  1198.             current_elongation = ss->setupflags & SETUPFLAG__ELONGATE_MASK;
  1199.             if (ss->kind != s2x2 && ss->kind != s_short6) current_elongation = 0;
  1200.  
  1201.             /* Did we neglect to do the touch/rear back stuff because fractionalization was enabled?
  1202.                If so, now is the time to correct that.  We only do it for the first part. */
  1203.  
  1204.             if ((!(ss->setupflags & SETUPFLAG__NO_STEP_TO_WAVE)) && (subcall_index == 0) &&
  1205.                   (callspec->callflags & (cflag__rear_back_from_r_wave | cflag__rear_back_from_qtag | cflag__step_to_wave))) {
  1206.  
  1207.                ss->setupflags |= SETUPFLAG__NO_STEP_TO_WAVE;  /* Can only do it once. */
  1208.  
  1209.                if (final_concepts & FINAL__LEFT) {
  1210.                   mirror = TRUE;
  1211.                   mirror_this(ss);
  1212.                }
  1213.  
  1214.                if (setup_limits[ss->kind] >= 0)          /* We don't understand absurd setups. */
  1215.                   touch_or_rear_back(ss, callspec->callflags);
  1216.             }
  1217.  
  1218.             *result = *ss;
  1219.  
  1220.             for (;;) {
  1221.                int j;
  1222.                setup tttt;
  1223.                by_def_item *this_item;
  1224.                defmodset this_mod;
  1225.  
  1226.                if (subcall_index >= highlimit) break;
  1227.  
  1228.                this_item = &callspec->stuff.def.defarray[subcall_index];
  1229.  
  1230.                if (!this_item->call_id) break;
  1231.  
  1232.                this_mod = this_item->modifiers;
  1233.  
  1234.                temp_concepts = get_mods_for_subcall(new_final_concepts, this_mod, callspec->callflags);
  1235.                get_real_subcall(parseptr, this_item, temp_concepts, &cp1, &call1, &conc1);
  1236.  
  1237.                /* If this context requires a tagging or scoot call, pass that fact on. */
  1238.                if (dfm_must_be_tag_call & this_mod) conc1 |= FINAL__MUST_BE_TAG;
  1239.                else if (dfm_must_be_scoot_call & this_mod) conc1 |= FINAL__MUST_BE_SCOOT;
  1240.  
  1241.                if (dfm_repeat_n & this_mod) {
  1242.                   tempsetup = *result;
  1243.                   for (j=1; j<=parseptr->number; j++) {
  1244.                      tttt = tempsetup;
  1245.                      tttt.setupflags = (ss->setupflags & ~(SETUPFLAG__FRACTIONALIZE_MASK | SETUPFLAG__ELONGATE_MASK)) | current_elongation;
  1246.                      if (!first_call) tttt.setupflags |= SETUPFLAG__NO_CHK_ELONG;
  1247.  
  1248.                      move(&tttt, cp1, call1, conc1, qtf, &tempsetup);
  1249.                      finalsetupflags |= tempsetup.setupflags;
  1250.                   }
  1251.                }
  1252.                else if (dfm_repeat_nm1 & this_mod) {
  1253.                   tempsetup = *result;
  1254.                   for (j=1; j<=parseptr->number-1; j++) {
  1255.                      tttt = tempsetup;
  1256.                      tttt.setupflags = (ss->setupflags & ~(SETUPFLAG__FRACTIONALIZE_MASK | SETUPFLAG__ELONGATE_MASK)) | current_elongation;
  1257.                      if (!first_call) tttt.setupflags |= SETUPFLAG__NO_CHK_ELONG;
  1258.  
  1259.                      move(&tttt, cp1, call1, conc1, qtf, &tempsetup);
  1260.                      finalsetupflags |= tempsetup.setupflags;
  1261.                   }
  1262.                }
  1263.                else if (dfm_repeat_n_alternate & this_mod) {
  1264.                   tempsetup = *result;
  1265.  
  1266.                   /* Read the call after this one -- we will alternate between the two. */
  1267.                   get_real_subcall(parseptr, &callspec->stuff.def.defarray[subcall_index+1], temp_concepts, &cp2, &call2, &conc2);
  1268.  
  1269.                   for (j=1; j<=parseptr->number; j++) {
  1270.                      tttt = tempsetup;
  1271.                      tttt.setupflags = (ss->setupflags & ~(SETUPFLAG__FRACTIONALIZE_MASK | SETUPFLAG__ELONGATE_MASK)) | current_elongation;
  1272.                      if (!first_call) tttt.setupflags |= SETUPFLAG__NO_CHK_ELONG;
  1273.  
  1274.                      if (j&1)
  1275.                         move(&tttt, cp1, call1, conc1, qtf, &tempsetup);
  1276.                      else
  1277.                         move(&tttt, cp2, call2, conc2, qtf, &tempsetup);
  1278.                      finalsetupflags |= tempsetup.setupflags;
  1279.                   }
  1280.                   subcall_index++;     /* Skip over the second call. */
  1281.                }
  1282.                else {
  1283.                   tttt = *result;
  1284.                   tttt.setupflags = (ss->setupflags & ~(SETUPFLAG__FRACTIONALIZE_MASK | SETUPFLAG__ELONGATE_MASK)) | current_elongation;
  1285.                   if (!first_call) tttt.setupflags |= SETUPFLAG__NO_CHK_ELONG;
  1286.  
  1287.                   if ((dfm_cpls_unless_single & this_mod) && !(new_final_concepts & FINAL__SINGLE)) {
  1288.                      tandem_couples_move(&tttt, cp1, call1, conc1,
  1289.                            selector_uninitialized, 0, 0, 0, 1, &tempsetup);
  1290.                   }
  1291.                   else
  1292.                      move(&tttt, cp1, call1, conc1, qtf, &tempsetup);
  1293.  
  1294.                   finalsetupflags |= tempsetup.setupflags;
  1295.                }
  1296.  
  1297.                /* If this call is "roll transparent", restore roll info from before the call for those people
  1298.                   that are marked as roll-neutral. */
  1299.  
  1300.                if (dfm_roll_transparent & this_mod) {
  1301.                   /* Can only do this if we understand the setups. */
  1302.                   if ((setup_limits[tempsetup.kind] >= 0) && (setup_limits[result->kind] >= 0)) {
  1303.                      int u, v;
  1304.  
  1305.                      for (u=0; u<=setup_limits[tempsetup.kind]; u++) {
  1306.                         if (tempsetup.people[u].id1 & ROLLBITM) {
  1307.                            /* This person is roll-neutral.  Reinstate his original roll info, by
  1308.                               searching for him in the starting setup. */
  1309.                            tempsetup.people[u].id1 &= ~ROLL_MASK;
  1310.                            for (v=0; v<=setup_limits[result->kind]; v++) {
  1311.                               if (((tempsetup.people[u].id1 ^ result->people[v].id1) & 0700) == 0)
  1312.                                  tempsetup.people[u].id1 |= (result->people[v].id1 & ROLL_MASK);
  1313.                            }
  1314.                         }
  1315.                      }
  1316.                   }
  1317.                }
  1318.  
  1319.                /* Whenever the ending setup is 2x2, we are responsible for computing the
  1320.                   "current_elongation" field of the result. */
  1321.  
  1322.                /* The following comments used to be in this code, back when it was much
  1323.                   more complicated than it is now. */
  1324.  
  1325.                /* If the setup has undergone a transition from a 1x4/diamond to a 2x2, make a note
  1326.                   of what the elongation should be according to the call's "parallel_conc_end" flag.
  1327.                   Remember that "result" contatins the setup at the start of this step, and "tempsetup"
  1328.                   has the setup at the end of this step.  The xor's with the rotations are done to
  1329.                   compensate for the fact that we may have done, say, a lockit, in an earlier step.
  1330.                   The final value that we have to provide for result->parallel_conc_end must state
  1331.                   the elongation relative to the original 1x4 orientation.  Whew!
  1332.                I believe the test cases for this stuff are locker's choice and split cast, the
  1333.                   latter being done from a grand line.  In that case, the ends have to hinge and
  1334.                   then trade.  The hinge has them stay close together, so they are as if in columns.
  1335.                   The trade then must preserve that.
  1336.                This used to xor the current setup rotation and the original setup rotation into the
  1337.                   equation, believing that, if a lockit happened earlier, we want the
  1338.                   "parallel_conc_end" property on the 1x4->2x2 subcall to apply to the entire
  1339.                   call.  I no longer believe that is correct.  What happens now is that each subcall is
  1340.                   evaluated independently.  If a 1x4->2x2 subcall occurs, we find out, from looking
  1341.                   at the call descriptor, where the dancers would like to finish that subcall,
  1342.                   and proceed from there. */
  1343.  
  1344.                /* Next part: we have to preserve the meaning of the "parallel_conc_end" flag
  1345.                   for 2x2->2x2 calls also.  Its meaning in this case is "go to antispots".
  1346.                   This is necessary to make "ENDS counter rotate" work from lines.  (Note
  1347.                   that counter rotate has this flag set.)  For a compound call, we obviously
  1348.                   want to xor all the flags of the constituent parts.  That is why we do an
  1349.                   xor here.  (Note that we initialized it to zero.)  But the stuff just above
  1350.                   overwrote it.  That's OK.  Any 1x4->2x2 call ought to destroy the "antispots"
  1351.                   info from previous 2x2->2x2 calls.  But after the 1x4->2x2 call has filled
  1352.                   in the elongation info, subsequent 2x2->2x2 calls requesting antispots should
  1353.                   flip the elongation.
  1354.                The test case for this is N/4 chain and circulate in.  The ends do a double
  1355.                   circulate, which is defined sequentially as 2 circulates.  Before this code was
  1356.                   put in, basic_move honored the parallel_conc_end/antispots flag for atomic
  1357.                   2x2->2x2 calls, but it wasn't honored here for concatenations of such.  The
  1358.                   final state of result->parallel_conc_end was random, so it would sometimes
  1359.                   think the ends should go to column spots. */
  1360.  
  1361.                if (tempsetup.kind == s2x2) {
  1362.                   switch (result->kind) {
  1363.                      case s1x4: case sdmd: case s2x2:
  1364.                         current_elongation = (((tempsetup.setupflags & RESULTFLAG__ELONGATE_MASK) / RESULTFLAG__ELONGATE_BIT) * SETUPFLAG__ELONGATE_BIT);
  1365.                         break;
  1366.    
  1367.                      /* Otherwise (perhaps the setup was a star) we have no idea how to elongate the setup. */
  1368.    
  1369.                      default:
  1370.                         current_elongation = 0;
  1371.                         break;
  1372.                   }
  1373.                }
  1374.                else
  1375.                   current_elongation = 0;
  1376.  
  1377.                *result = tempsetup;
  1378.  
  1379.                /* Remove outboard phantoms. 
  1380.                   It used to be that normalize_setup was not called
  1381.                   here.  It was found that we couldn't do things like, from a suitable offset wave,
  1382.                   [triple line 1/2 flip] back to a wave, that is, start offset and finish normally.
  1383.                   So this has been added.  However, there may have been a reason for not normalizing.
  1384.                   If any problems are found, it may be that a flag needs to be added to seqdef calls
  1385.                   saying whether to remove outboard phantoms after each part. */
  1386.  
  1387.                normalize_setup(result, simple_normalize);
  1388.  
  1389.                qtf = FALSE;
  1390.  
  1391.                new_final_concepts &= ~(FINAL__SPLIT | FINAL__SPLIT_SQUARE_APPROVED | FINAL__SPLIT_DIXIE_APPROVED);
  1392.  
  1393.                subcall_index++;
  1394.                first_call = FALSE;
  1395.  
  1396.                /* If we are being asked to do just one part of a call (from SETUPFLAG__FRACTIONALIZE_MASK),
  1397.                   exit now.  Also, see if we just did the last part. */
  1398.  
  1399.                if ((ss->setupflags & SETUPFLAG__FRACTIONALIZE_MASK) && instant_stop) {
  1400.                   /* Check whether this is last part of the call, by peeking ahead. */
  1401.                   if (!(&callspec->stuff.def.defarray[subcall_index])->call_id) finalsetupflags |= RESULTFLAG__DID_LAST_PART;
  1402.                   break;
  1403.                }
  1404.             }
  1405.  
  1406.             result->setupflags = (finalsetupflags & ~RESULTFLAG__ELONGATE_MASK) | ((current_elongation / SETUPFLAG__ELONGATE_BIT) * RESULTFLAG__ELONGATE_BIT);
  1407.          }
  1408.          else {
  1409.  
  1410.             /* Must be some form of concentric. */
  1411.    
  1412.             saved_warnings = history[history_ptr+1].warnings;
  1413.  
  1414.             temp_concepts = get_mods_for_subcall(new_final_concepts, callspec->stuff.conc.innerdef.modifiers, callspec->callflags);
  1415.             get_real_subcall(parseptr, &callspec->stuff.conc.innerdef, temp_concepts, &cp1, &call1, &conc1);
  1416.  
  1417.             /* Do it again. */
  1418.  
  1419.             temp_concepts = get_mods_for_subcall(new_final_concepts, callspec->stuff.conc.outerdef.modifiers, callspec->callflags);
  1420.             get_real_subcall(parseptr, &callspec->stuff.conc.outerdef, temp_concepts, &cp2, &call2, &conc2);
  1421.  
  1422.             /* Fudge a 3x4 into a 1/4-tag if appropriate. */
  1423.  
  1424.             if (ss->kind == s3x4 && (callspec->callflags & cflag__fudge_to_q_tag) &&
  1425.                   (the_schema == schema_concentric || the_schema == schema_cross_concentric)) {
  1426.  
  1427.                if (ss->people[0].id1) {
  1428.                   if (ss->people[1].id1) fail("Can't do this call from arbitrary 3x4 setup.");
  1429.                }
  1430.                else (void) copy_person(ss, 0, ss, 1);
  1431.  
  1432.                if (ss->people[3].id1) {
  1433.                   if (ss->people[2].id1) fail("Can't do this call from arbitrary 3x4 setup.");
  1434.                   else (void) copy_person(ss, 1, ss, 3);
  1435.                }
  1436.                else (void) copy_person(ss, 1, ss, 2);
  1437.  
  1438.                (void) copy_person(ss, 2, ss, 4);
  1439.                (void) copy_person(ss, 3, ss, 5);
  1440.  
  1441.                if (ss->people[6].id1) {
  1442.                   if (ss->people[7].id1) fail("Can't do this call from arbitrary 3x4 setup.");
  1443.                   else (void) copy_person(ss, 4, ss, 6);
  1444.                }
  1445.                else (void) copy_person(ss, 4, ss, 7);
  1446.  
  1447.                if (ss->people[9].id1) {
  1448.                   if (ss->people[8].id1) fail("Can't do this call from arbitrary 3x4 setup.");
  1449.                   else (void) copy_person(ss, 5, ss, 9);
  1450.                }
  1451.                else (void) copy_person(ss, 5, ss, 8);
  1452.  
  1453.                (void) copy_person(ss, 6, ss, 10);
  1454.                (void) copy_person(ss, 7, ss, 11);
  1455.  
  1456.                ss->kind = s_qtag;
  1457.                ss->setupflags |= SETUPFLAG__DISTORTED;
  1458.             }
  1459.  
  1460.             concentric_move(
  1461.                ss,
  1462.                cp1, cp2, call1, call2, conc1, conc2,
  1463.                the_schema,
  1464.                callspec->stuff.conc.innerdef.modifiers,
  1465.                callspec->stuff.conc.outerdef.modifiers,
  1466.                result);
  1467.  
  1468.             if (dfm_suppress_elongation_warnings & callspec->stuff.conc.outerdef.modifiers) {
  1469.                history[history_ptr+1].warnings.bits[0] &= ~(Warnings_About_Conc_elongation);
  1470.             }
  1471.             history[history_ptr+1].warnings.bits[0] |= saved_warnings.bits[0];
  1472.             history[history_ptr+1].warnings.bits[1] |= saved_warnings.bits[1];
  1473.          }
  1474.  
  1475.          break;
  1476.    }
  1477.  
  1478.    /* Reflect back if necessary. */
  1479.    if (mirror) mirror_this(result);
  1480.    canonicalize_rotation(result);
  1481. }
  1482.  
  1483.  
  1484.  
  1485. /* The current interpretation of the elongation flags, on input and output, is now
  1486.    as follows:
  1487.    
  1488.    Note first that, on input and output, the elongation bits are only meaningful in
  1489.       a 2x2 or short6 setup.
  1490.    On input, nonzero bits in the SETUPFLAG__ELONGATE_MASK with a 2x2 setup mean that
  1491.       the setup _was_ _actually_ _elongated_, and that the elongation is actually felt
  1492.       by the dancers.  In this case, the "move" routine is entitled to raise an error
  1493.       if the 2x2 call is awkward.  For example, a star thru from facing couples is
  1494.       illegal if the elongate bit is on that makes the people far away from the one
  1495.       they are facing.  It follows from this that, if we call concentric star thru,
  1496.       these bits will be cleared before calling "move", since the concentric concept
  1497.       forgives awkward elongation.  Of course, the "concentric_move" routine will
  1498.       remember the actual elongation in order to move people to the correct ending
  1499.       formation.
  1500.    On output, nonzero bits in the RESULTFLAG__ELONGATE_MASK with a 2x2 setup mean that,
  1501.       _if_ _result_ _elongation_ _is_ _required_, this is what it should be.  It does
  1502.       _not_ mean that such elongation actually exists.  Whoever called "move" must
  1503.       make that judgement.  These bits are set when, for example, a 1x4 -> 2x2 call
  1504.       is executed, to indicate how to elongate the result, if it turns out that those
  1505.       people were working around the outside.  This determination is made not just on
  1506.       the "checkpoint rule" that says they go perpendicular to their original axis,
  1507.       but also on the basis of the "parallel_conc_end" flag in the call.  That is, these
  1508.       bits tell which way to go if the call had been "ends do <whatever>".  Of course,
  1509.       if the concentric concept was in use, this information is not used.  Instead,
  1510.       "concentric_move" overrides the bits we return with an absolute "checkpoint rule"
  1511.       application.
  1512.  
  1513.    It may well be that the goal described above is not actually implemented correctly.
  1514. */
  1515.  
  1516.  
  1517. extern void move(
  1518.    setup *ss,
  1519.    parse_block *parseptr,
  1520.    callspec_block *callspec,
  1521.    final_set final_concepts,
  1522.    long_boolean qtfudged,
  1523.    setup *result)
  1524.  
  1525. {
  1526.    parse_block *saved_magic_diamond;
  1527.    final_set new_final_concepts;
  1528.    final_set check_concepts;
  1529.    parse_block *parseptrcopy;
  1530.  
  1531.    clear_people(result);
  1532.    result->setupflags = 0;
  1533.  
  1534.    if (callspec) {
  1535.       move_with_real_call(ss, parseptr, callspec, final_concepts, qtfudged, result);
  1536.       return;
  1537.    }
  1538.  
  1539.    /* Scan the "final" concepts, remembering them and their end point. */
  1540.    last_magic_diamond = 0;
  1541.    parseptrcopy = process_final_concepts(parseptr, TRUE, &new_final_concepts);
  1542.  
  1543.    saved_magic_diamond = last_magic_diamond;
  1544.  
  1545.    /* See if there were any "non-final" ones present also. */
  1546.  
  1547.    new_final_concepts |= final_concepts;         /* Include any old ones we had. */
  1548.  
  1549.    /* These are the concepts that we are interested in. */
  1550.  
  1551.    check_concepts = new_final_concepts & ~(FINAL__MUST_BE_TAG | FINAL__MUST_BE_SCOOT);
  1552.  
  1553.    if (parseptrcopy->concept->kind <= marker_end_of_list) {
  1554.       selector_kind saved_selector = current_selector;
  1555.  
  1556.       /* There are no "non-final" concepts.  The only concepts are the final ones that
  1557.          have been encoded into new_final_concepts. */
  1558.  
  1559.       /* We must read the selector out of the concept list and use it for this call to "move".
  1560.          We are effectively using it as an argument to "move", with all the care that must go
  1561.          into invocations of recursive procedures.  However, at its point of actual use, it
  1562.          must be in a global variable.  Therefore, we explicitly save and restore that
  1563.          global variable (in a dynamic variable local to this instance) rather than passing
  1564.          it as an explicit argument.  By saving it and restoring it in this way, we make
  1565.          things like "checkpoint bounce the beaux by bounce the belles" work. */
  1566.       
  1567.       current_selector = parseptrcopy->selector;
  1568.       move_with_real_call(ss, parseptrcopy, parseptrcopy->call, new_final_concepts, qtfudged, result);
  1569.       current_selector = saved_selector;
  1570.    }
  1571.    else {
  1572.       /* We now know that there are "non-final" (virtual setup) concepts present. */
  1573.  
  1574.  
  1575.       if (check_concepts == 0) {
  1576.          /* Look for virtual setup concept that can be done by dispatch from table, with no
  1577.             intervening final concepts. */
  1578.    
  1579.          if (do_big_concept(ss, parseptrcopy, result)) {
  1580.             canonicalize_rotation(result);
  1581.             return;
  1582.          }
  1583.       }
  1584.  
  1585.       /* Some final concept (e.g. "magic") is present in front of our virtual setup concept.
  1586.          We have to dispose of it.  This means that expanding the matrix (e.g. 2x4->2x6)
  1587.          and stepping to a wave or rearing back from one are no longer legel. */
  1588.  
  1589.       ss->setupflags |= (SETUPFLAG__NO_EXPAND_MATRIX | SETUPFLAG__NO_STEP_TO_WAVE);
  1590.  
  1591.       /* There are a few "final" concepts that
  1592.          will not be treated as such if there are non-final concepts occurring
  1593.          after them.  Instead, they will be treated as virtual setup concepts.
  1594.          This is what makes "magic once removed trade" work, for
  1595.          example.  On the other hand, if there are no non-final concepts following, treat these as final.
  1596.          This is what makes "magic transfer" or "split square thru" work. */
  1597.  
  1598.       if (check_concepts == FINAL__SPLIT) {
  1599.          map_thing *split_map;
  1600.    
  1601.          ss->setupflags |= SETUPFLAG__SAID_SPLIT;
  1602.  
  1603.          if (ss->kind == s2x4) split_map = (*map_lists[s2x2][1])[MPKIND__SPLIT][0];
  1604.          else if (ss->kind == s1x8) split_map = (*map_lists[s1x4][1])[MPKIND__SPLIT][0];
  1605.          else if (ss->kind == s_ptpd) split_map = (*map_lists[sdmd][1])[MPKIND__SPLIT][0];
  1606.          else if (ss->kind == s_qtag) split_map = (*map_lists[sdmd][1])[MPKIND__SPLIT][1];
  1607.          else fail("Can't do split concept in this setup.");
  1608.    
  1609.          divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__SPLIT,
  1610.             split_map, phantest_ok, TRUE, result);
  1611.       }
  1612.       else if ((check_concepts & ~FINAL__DIAMOND) == FINAL__MAGIC) {
  1613.          if (ss->kind == s2x4) {
  1614.             divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__MAGIC,
  1615.                &map_2x4_magic, phantest_ok, TRUE, result);
  1616.          }
  1617.          else if (ss->kind == s_qtag) {
  1618.             divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__MAGIC,
  1619.                &map_qtg_magic, phantest_ok, TRUE, result);
  1620.  
  1621.             /* Since more concepts follow the magic and/or interlocked stuff, we can't
  1622.                allow the concept to be just "magic" etc.  We have to change it to
  1623.                "magic diamond, ..."  Otherwise, things could come out sounding like
  1624.                "magic diamond as couples quarter right" when we should really be saying
  1625.                "magic diamond, diamond as couples quarter right".  Therefore, we are going
  1626.                to do something seriously hokey: we are going to change the concept descriptor
  1627.                to one whose name has the extra "diamond" word.  We do this by marking the
  1628.                setupflags word in the result.  The actual hokey stuff will be done presently. */
  1629.  
  1630.             result->setupflags |= RESULTFLAG__NEED_DIAMOND;
  1631.          }
  1632.          else if (ss->kind == s_ptpd) {
  1633.             divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__MAGIC,
  1634.                &map_ptp_magic, phantest_ok, TRUE, result);
  1635.             result->setupflags |= RESULTFLAG__NEED_DIAMOND;      /* See above. */
  1636.          }
  1637.          else
  1638.             fail("Can't do magic concept in this context.");
  1639.       }
  1640.       else if ((check_concepts & ~FINAL__DIAMOND) == FINAL__INTERLOCKED) {
  1641.          if (ss->kind == s_qtag) {
  1642.             divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__INTERLOCKED,
  1643.                &map_qtg_intlk, phantest_ok, TRUE, result);
  1644.             result->setupflags |= RESULTFLAG__NEED_DIAMOND;      /* See above. */
  1645.          }
  1646.          if (ss->kind == s_ptpd) {
  1647.             divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__INTERLOCKED,
  1648.                &map_ptp_intlk, phantest_ok, TRUE, result);
  1649.             result->setupflags |= RESULTFLAG__NEED_DIAMOND;      /* See above. */
  1650.          }
  1651.          else
  1652.             fail("Can't do interlocked concept in this context.");
  1653.       }
  1654.       else if ((check_concepts & ~FINAL__DIAMOND) == (FINAL__INTERLOCKED | FINAL__MAGIC)) {
  1655.          if (ss->kind == s_qtag) {
  1656.             divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~(FINAL__INTERLOCKED | FINAL__MAGIC),
  1657.                   &map_qtg_magic_intlk, phantest_ok, TRUE, result);
  1658.             result->setupflags |= RESULTFLAG__NEED_DIAMOND;      /* See above. */
  1659.          }
  1660.          if (ss->kind == s_ptpd) {
  1661.             divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~(FINAL__INTERLOCKED | FINAL__MAGIC),
  1662.                   &map_ptp_magic_intlk, phantest_ok, TRUE, result);
  1663.             result->setupflags |= RESULTFLAG__NEED_DIAMOND;      /* See above. */
  1664.          }
  1665.          else
  1666.             fail("Can't do magic interlocked concept in this context.");
  1667.       }
  1668.       else if (check_concepts == FINAL__DIAMOND) {
  1669.          if (ss->kind == sdmd) {
  1670.             divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__DIAMOND,
  1671.                   (*map_lists[s_1x2][1])[MPKIND__DMD_STUFF][0], phantest_ok, TRUE, result);
  1672.          }
  1673.          else if (ss->kind == s_qtag) {
  1674.             /* Divide into diamonds and try again.  (Note that we back up the concept pointer.) */
  1675.             divided_setup_move(ss, parseptr, NULLCALLSPEC, 0,
  1676.                   (*map_lists[sdmd][1])[MPKIND__SPLIT][1], phantest_ok, FALSE, result);
  1677.          }
  1678.          else
  1679.             fail("Must have diamonds for this concept.");
  1680.       }
  1681.       else
  1682.          fail2("Can't do this concept with other concepts preceding it:", parseptrcopy->concept->name);
  1683.    }
  1684.  
  1685.    /* If execution of the call raised a request that we change a concept name from "magic" to
  1686.       "magic diamond,", for example, do so. */
  1687.  
  1688.    if (result->setupflags & RESULTFLAG__NEED_DIAMOND) {
  1689.       if (saved_magic_diamond && saved_magic_diamond->concept->value.arg1 == 0) {
  1690.          if (saved_magic_diamond->concept->kind == concept_magic) saved_magic_diamond->concept = &special_magic;
  1691.          else if (saved_magic_diamond->concept->kind == concept_interlocked) saved_magic_diamond->concept = &special_interlocked;
  1692.       }
  1693.    }
  1694. }
  1695.