Logo Search packages:      
Sourcecode: xabacus version File versions  Download package

Abacus.c

/*-
# X-BASED ABACUS
#
#  Abacus.c
#
###
#
#  Copyright (c) 1994 - 2005  David Albert Bagley, bagleyd@tux.org
#
#                   All Rights Reserved
#
#  Permission to use, copy, modify, and distribute this software and
#  its documentation for any purpose and without fee is hereby granted,
#  provided that the above copyright notice appear in all copies and
#  that both that copyright notice and this permission notice appear in
#  supporting documentation, and that the name of the author not be
#  used in advertising or publicity pertaining to distribution of the
#  software without specific, written prior permission.
#
#  This program is distributed in the hope that it will be "useful",
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
*/

/* Methods file for Abacus */

#include "file.h"
#include "sound.h"
#include "AbacusP.h"
static Boolean DeleteSpecialRail(AbacusWidget w,
            Boolean sign, Boolean piece, Boolean piecePercent);

#if 1
#ifndef SCRIPTFILE
#define SCRIPTFILE "Abacus.les"
#endif
#endif

#ifdef WINVER
#ifndef INIFILE
#define INIFILE "wabacus.ini"
#endif
#define SECTION "setup"
#else

static Boolean SetValuesAbacus(Widget current, Widget request, Widget renew);
static void QuitAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void DestroyAbacus(Widget old);
static void ResizeAbacus(AbacusWidget w);
static void InitializeAbacus(Widget request, Widget renew);
static void ExposeAbacus(Widget renew, XEvent * event, Region region);
static void HideAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void SelectAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void ReleaseAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void ClearAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void ClearAbacusMaybe(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void ClearAbacus2(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void IncrementAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void DecrementAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void SpeedAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void SlowAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void SoundAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void FormatAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void RomanNumeralsAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void SignAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void QuarterAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void QuarterPercentAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void TwelfthAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void AnomalyAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void WatchAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void DemoAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void NextAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void RepeatAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void MoreAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void EnterAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);
static void LeaveAbacus(AbacusWidget w, XEvent * event, char **args, int nArgs);

static char defaultTranslationsAbacus[] =
"<KeyPress>q: Quit()\n\
 Ctrl<KeyPress>C: Quit()\n\
 <KeyPress>osfCancel: Hide()\n\
 <KeyPress>Escape: Hide()\n\
 <KeyPress>osfEscape: Hide()\n\
 Ctrl<KeyPress>[: Hide()\n\
 <KeyPress>0x1B: Hide()\n\
 <Btn1Down>: Select()\n\
 <Btn1Up>: Release()\n\
 <KeyPress>c: Clear()\n\
 <Btn3Down>: ClearMaybe()\n\
 <Btn3Down>(2+): Clear2()\n\
 <KeyPress>i: Increment()\n\
 <KeyPress>d: Decrement()\n\
 <KeyPress>0x2E: Speed()\n\
 <KeyPress>0x3E: Speed()\n\
 <KeyPress>0x3C: Slow()\n\
 <KeyPress>0x2C: Slow()\n\
 <KeyPress>@: Sound()\n\
 <KeyPress>f: Format()\n\
 <KeyPress>m: RomanNumerals()\n\
 <KeyPress>s: Sign()\n\
 <KeyPress>u: Quarter()\n\
 <KeyPress>p: QuarterPercent()\n\
 <KeyPress>t: Twelfth()\n\
 <KeyPress>a: Anomaly()\n\
 <KeyPress>w: Watch()\n\
 <KeyPress>o: Demo()\n\
 <KeyPress>n: Next()\n\
 <KeyPress>r: Repeat()\n\
 <KeyPress>0x20: More()\n\
 <KeyPress>KP_Space: More()\n\
 <KeyPress>Return: More()\n\
 <EnterWindow>: Enter()\n\
 <LeaveWindow>: Leave()";

static XtActionsRec actionsListAbacus[] =
{
      {(char *) "Quit", (XtActionProc) QuitAbacus},
      {(char *) "Hide", (XtActionProc) HideAbacus},
      {(char *) "Select", (XtActionProc) SelectAbacus},
      {(char *) "Release", (XtActionProc) ReleaseAbacus},
      {(char *) "Clear", (XtActionProc) ClearAbacus},
      {(char *) "ClearMaybe", (XtActionProc) ClearAbacusMaybe},
      {(char *) "Clear2", (XtActionProc) ClearAbacus2},
      {(char *) "Increment", (XtActionProc) IncrementAbacus},
      {(char *) "Decrement", (XtActionProc) DecrementAbacus},
      {(char *) "Speed", (XtActionProc) SpeedAbacus},
      {(char *) "Slow", (XtActionProc) SlowAbacus},
      {(char *) "Sound", (XtActionProc) SoundAbacus},
      {(char *) "Format", (XtActionProc) FormatAbacus},
      {(char *) "RomanNumerals", (XtActionProc) RomanNumeralsAbacus},
      {(char *) "Sign", (XtActionProc) SignAbacus},
      {(char *) "Quarter", (XtActionProc) QuarterAbacus},
      {(char *) "QuarterPercent", (XtActionProc) QuarterPercentAbacus},
      {(char *) "Twelfth", (XtActionProc) TwelfthAbacus},
      {(char *) "Anomaly", (XtActionProc) AnomalyAbacus},
      {(char *) "Watch", (XtActionProc) WatchAbacus},
      {(char *) "Demo", (XtActionProc) DemoAbacus},
      {(char *) "Next", (XtActionProc) NextAbacus},
      {(char *) "Repeat", (XtActionProc) RepeatAbacus},
      {(char *) "More", (XtActionProc) MoreAbacus},
      {(char *) "Enter", (XtActionProc) EnterAbacus},
      {(char *) "Leave", (XtActionProc) LeaveAbacus}
};

static XtResource resourcesAbacus[] =
{
      {XtNwidth, XtCWidth, XtRDimension, sizeof (Dimension),
       XtOffset(AbacusWidget, core.width),
       XtRString, (caddr_t) "234"},
      {XtNheight, XtCHeight, XtRDimension, sizeof (Dimension),
       XtOffset(AbacusWidget, core.height),
       XtRString, (caddr_t) "123"},
      {XtNmono, XtCMono, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.mono),
       XtRString, (caddr_t) "FALSE"},
      {XtNreverse, XtCReverse, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.reverse),
       XtRString, (caddr_t) "FALSE"},
      {XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
       XtOffset(AbacusWidget, abacus.foreground),
       XtRString, (caddr_t) XtDefaultForeground},
      {XtNbackground, XtCBackground, XtRPixel, sizeof (Pixel),
       XtOffset(AbacusWidget, abacus.background),
       XtRString, (caddr_t) XtDefaultBackground},
      {XtNframeColor, XtCColor, XtRPixel, sizeof (Pixel),
       XtOffset(AbacusWidget, abacus.frameColor),
       XtRString, (caddr_t) "cyan" /*XtDefaultForeground*/},
      {XtNrailColor, XtCForeground, XtRPixel, sizeof (Pixel),
       XtOffset(AbacusWidget, abacus.railColor),
       XtRString, (caddr_t) "gold" /*XtDefaultForeground*/},
      {XtNbeadColor, XtCForeground, XtRPixel, sizeof (Pixel),
       XtOffset(AbacusWidget, abacus.beadColor),
       XtRString, (caddr_t) "DarkRed" /*XtDefaultForeground*/},
      {XtNbeadBorder, XtCForeground, XtRPixel, sizeof (Pixel),
       XtOffset(AbacusWidget, abacus.borderColor),
       XtRString, (caddr_t) "gray25" /*XtDefaultForeground*/},
      {XtNdelay, XtCDelay, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.delay),
       XtRString, (caddr_t) "50"},
      {XtNsound, XtCSound, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.sound),
       XtRString, (caddr_t) "FALSE"},
      {XtNbumpSound, XtCBumpSound, XtRString, sizeof (String),
       XtOffset(AbacusWidget, abacus.bumpSound),
       XtRString, (caddr_t) BUMPSOUND},
      {XtNmoveSound, XtCMoveSound, XtRString, sizeof (String),
       XtOffset(AbacusWidget, abacus.moveSound),
       XtRString, (caddr_t) MOVESOUND},
      {XtNscript, XtCScript, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.script),
       XtRString, (caddr_t) "FALSE"},
      {XtNdemo, XtCDemo, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.demo),
       XtRString, (caddr_t) "FALSE"},
      {XtNdemoPath, XtCDemoPath, XtRString, sizeof (String),
       XtOffset(AbacusWidget, abacusDemo.path),
       XtRString, (caddr_t) DEMOPATH},
      {XtNdemoFont, XtCDemoFont, XtRString, sizeof (Font),
       XtOffset(AbacusWidget, abacusDemo.font),
       XtRString, (caddr_t) "-*-times-*-r-*-*-*-180-*-*-*-*"},
      {XtNdemoForeground, XtCForeground, XtRPixel, sizeof (Pixel),
       XtOffset(AbacusWidget, abacusDemo.foreground),
       XtRString, (caddr_t) XtDefaultForeground},
      {XtNdemoBackground, XtCBackground, XtRPixel, sizeof (Pixel),
       XtOffset(AbacusWidget, abacusDemo.background),
       XtRString, (caddr_t) XtDefaultBackground},
      {XtNvertical, XtCVertical, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.vertical),
       XtRString, (caddr_t) "FALSE"},
      {XtNslot, XtCSlot, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.slot),
       XtRString, (caddr_t) "FALSE"},
      {XtNdiamond, XtCDiamond, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.diamond),
       XtRString, (caddr_t) "FALSE"},
      {XtNtopOrient, XtCTopOrient, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.decks[TOP].orientation),
       XtRString, (caddr_t) "TRUE"},
      {XtNbottomOrient, XtCBottomOrient, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.decks[BOTTOM].orientation),
       XtRString, (caddr_t) "FALSE"},
      {XtNtopNumber, XtCTopNumber, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.decks[TOP].number),
       XtRString, (caddr_t) "2"},
      {XtNbottomNumber, XtCBottomNumber, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.decks[BOTTOM].number),
       XtRString, (caddr_t) "5"},
      {XtNtopFactor, XtCTopFactor, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.decks[TOP].factor),
       XtRString, (caddr_t) "5"},
      {XtNbottomFactor, XtCBottomFactor, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.decks[BOTTOM].factor),
       XtRString, (caddr_t) "1"},
      {XtNtopSpaces, XtCTopSpaces, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.decks[TOP].spaces),
       XtRString, (caddr_t) "2"},
      {XtNbottomSpaces, XtCBottomSpaces, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.decks[BOTTOM].spaces),
       XtRString, (caddr_t) "3"},
      {XtNtopPiece, XtCTopPiece, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.decks[TOP].piece),
       XtRString, (caddr_t) "0"},
      {XtNbottomPiece, XtCBottomPiece, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.decks[BOTTOM].piece),
       XtRString, (caddr_t) "0"},
      {XtNtopPiecePercent, XtCTopPiecePercent, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.decks[TOP].piecePercent),
       XtRString, (caddr_t) "0"},
      {XtNbottomPiecePercent, XtCBottomPiecePercent, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.decks[BOTTOM].piecePercent),
       XtRString, (caddr_t) "0"},
      {XtNshiftPercent, XtCShiftPercent, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.shiftPercent),
       XtRString, (caddr_t) "2"},
      {XtNsign, XtCSign, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.sign),
       XtRString, (caddr_t) "FALSE"},
      {XtNdecimalPosition, XtCDecimalPosition, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.decimalPosition),
       XtRString, (caddr_t) "2"}, /* same as shiftPercent */
      {XtNgroupSize, XtCGroupSize, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.groupSize),
       XtRString, (caddr_t) "3"},
      {XtNrails, XtCRails, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.rails),
       XtRString, (caddr_t) "13"},
      {XtNbase, XtCBase, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.base),
       XtRString, (caddr_t) "10"},
      {XtNanomaly, XtCAnomaly, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.anomaly),
       XtRString, (caddr_t) "0"},
      {XtNshiftAnomaly, XtCShiftAnomaly, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.shiftAnomaly),
       XtRString, (caddr_t) "2"},
      {XtNanomalySq, XtCAnomalySq, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.anomalySq),
       XtRString, (caddr_t) "0"},
      {XtNshiftAnomalySq, XtCShiftAnomalySq, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.shiftAnomalySq),
       XtRString, (caddr_t) "2"},
      {XtNdisplayBase, XtCDisplayBase, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.displayBase),
       XtRString, (caddr_t) "10"},
      {XtNromanNumerals, XtCRomanNumerals, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.romanNumerals),
       XtRString, (caddr_t) "FALSE"},
      {XtNmode, XtCMode, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.mode),
       XtRString, (caddr_t) "5"},
      {XtNformat, XtCFormat, XtRString, sizeof (String),
       XtOffset(AbacusWidget, abacus.format),
       XtRString, (caddr_t) "Other"},
      {XtNmenu, XtCMenu, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.menu),
       XtRString, (caddr_t) "-1"},
      {XtNdeck, XtCDeck, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.deck),
       XtRString, (caddr_t) "-1"},
      {XtNrail, XtCRail, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.rail),
       XtRString, (caddr_t) "0"},
      {XtNnumber, XtCNumber, XtRInt, sizeof (int),
       XtOffset(AbacusWidget, abacus.number),
       XtRString, (caddr_t) "0"},
      {XtNaux, XtCAux, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.aux),
       XtRString, (caddr_t) "FALSE"},
      {XtNmathBuffer, XtCMathBuffer, XtRString, sizeof (String),
       XtOffset(AbacusWidget, abacus.mathBuffer),
       XtRString, (caddr_t) ""},
      {XtNversionOnly, XtCBoolean, XtRBoolean, sizeof (Boolean),
       XtOffset(AbacusWidget, abacus.versionOnly),
       XtRString, (caddr_t) "FALSE"},
      {XtNselectCallback, XtCCallback, XtRCallback, sizeof (caddr_t),
       XtOffset(AbacusWidget, abacus.select),
       XtRCallback, (caddr_t) NULL}
};

AbacusClassRec abacusClassRec =
{
      {
            (WidgetClass) & widgetClassRec,           /* superclass */
            (char *) "Abacus",      /* class name */
            sizeof (AbacusRec),     /* widget size */
            NULL,       /* class initialize */
            NULL,       /* class part initialize */
            FALSE,            /* class inited */
            (XtInitProc) InitializeAbacus,      /* initialize */
            NULL,       /* initialize hook */
            XtInheritRealize, /* realize */
            actionsListAbacus,      /* actions */
            XtNumber(actionsListAbacus),  /* num actions */
            resourcesAbacus,  /* resources */
            XtNumber(resourcesAbacus),    /* num resources */
            NULLQUARK,  /* xrm class */
            TRUE,       /* compress motion */
            TRUE,       /* compress exposure */
            TRUE,       /* compress enterleave */
            TRUE,       /* visible interest */
            (XtWidgetProc) DestroyAbacus, /* destroy */
            (XtWidgetProc) ResizeAbacus,  /* resize */
            (XtExposeProc) ExposeAbacus,  /* expose */
            (XtSetValuesFunc) SetValuesAbacus,  /* set values */
            NULL,       /* set values hook */
            XtInheritSetValuesAlmost,     /* set values almost */
            NULL,       /* get values hook */
            NULL,       /* accept focus */
            XtVersion,  /* version */
            NULL,       /* callback private */
            defaultTranslationsAbacus,    /* tm table */
            NULL,       /* query geometry */
            NULL,       /* display accelerator */
            NULL        /* extension */
      },
      {
            0           /* ignore */
      }
};

WidgetClass abacusWidgetClass = (WidgetClass) & abacusClassRec;

void
SetAbacus(AbacusWidget w, int reason)
{
      abacusCallbackStruct cb;

      cb.reason = reason;
      XtCallCallbacks((Widget) w, (char *) XtNselectCallback, &cb);
}

void
SetAbacusMove(AbacusWidget w, int reason, int aux, int deck,
            int rail, int number)
{
      abacusCallbackStruct cb;

      cb.reason = reason;
      cb.aux = aux;
      cb.deck = deck;
      cb.rail = rail;
      cb.number = number;
      XtCallCallbacks((Widget) w, (char *) XtNselectCallback, &cb);
}

static void
SetAbacusString(AbacusWidget w, int reason, char * string)
{
      abacusCallbackStruct cb;

      cb.reason = reason;
      cb.buffer = string;
      XtCallCallbacks((Widget) w, (char *) XtNselectCallback, &cb);
}
#endif

static void ResizeBead(AbacusWidget w);

static void
SetModeFromFormat(AbacusWidget w)
{
      if (strncasecmp("chinese", w->abacus.format,
                  MAXLENFORMAT) == 0) {
            w->abacus.mode = CHINESE;
      } else if (strncasecmp("japanese", w->abacus.format,
                  MAXLENFORMAT) == 0) {
            w->abacus.mode = JAPANESE;
      } else if (strncasecmp("korean", w->abacus.format,
                  MAXLENFORMAT) == 0) {
            w->abacus.mode = KOREAN;
      } else if (strncasecmp("roman", w->abacus.format,
                  MAXLENFORMAT) == 0) {
            w->abacus.mode = ROMAN;
      } else if (strncasecmp("italian", w->abacus.format,
                  MAXLENFORMAT) == 0) {
            w->abacus.mode = ROMAN;
      } else if (strncasecmp("russian", w->abacus.format,
                  MAXLENFORMAT) == 0) {
            w->abacus.mode = RUSSIAN;
      } else if (strncasecmp("cn", w->abacus.format, MAXLENFORMAT) == 0) {
            w->abacus.mode = CHINESE;
      } else if (strncasecmp("ja", w->abacus.format, MAXLENFORMAT) == 0) {
            w->abacus.mode = JAPANESE;
      } else if (strncasecmp("jp", w->abacus.format, MAXLENFORMAT) == 0) {
            w->abacus.mode = JAPANESE;
      } else if (strncasecmp("ko", w->abacus.format, MAXLENFORMAT) == 0) {
            w->abacus.mode = KOREAN;
      } else if (strncasecmp("ro", w->abacus.format, MAXLENFORMAT) == 0) {
            w->abacus.mode = ROMAN;
      } else if (strncasecmp("it", w->abacus.format, MAXLENFORMAT) == 0) {
            w->abacus.mode = ROMAN;
      } else if (strncasecmp("ru", w->abacus.format, MAXLENFORMAT) == 0) {
            w->abacus.mode = RUSSIAN;
      } else {
            w->abacus.mode = OTHER;
      }
}

static int
CheckBottomSpace(AbacusWidget w)
{
      return w->abacus.decks[BOTTOM].spaces +
            baseToBottom(w->abacus.base) - 1;
}

static Boolean
CheckAnomaly(AbacusWidget w)
{
      /* Anomaly must be protected from carries */
      if (w->abacus.anomaly != 0 && w->abacus.decks[BOTTOM].piece == 0 &&
          ((w->abacus.decks[BOTTOM].number == w->abacus.decks[TOP].factor - 1 &&
          w->abacus.decks[TOP].number == w->abacus.base / w->abacus.decks[TOP].factor - 1) ||
          (w->abacus.decks[TOP].number == 0 &&
          w->abacus.decks[BOTTOM].number == w->abacus.base - 1))) {
            return True;
      }
      return False;
}

static int
numberPieces(AbacusWidget w, int deck)
{
      int pieces = 0;

      if (deck == BOTTOM) {
            pieces = w->abacus.decks[BOTTOM].piece;
            if (w->abacus.decks[TOP].number == 0 &&
                        w->abacus.decks[TOP].piece != 0)
                  pieces *= w->abacus.decks[TOP].piece;
            if (w->abacus.decks[BOTTOM].number ==
                        w->abacus.decks[TOP].factor - 1)
                  pieces -= 1;
      } else {
            if (w->abacus.decks[TOP].number != 0 &&
                        w->abacus.decks[TOP].piece != 0) {
                  pieces = w->abacus.decks[TOP].piece;
                  if ((w->abacus.decks[TOP].number + 1) *
                              w->abacus.decks[TOP].factor ==
                              w->abacus.base)
                        pieces -= 1;
            }
      }
      return pieces;
}

static int numberPiecePercents(AbacusWidget w, int deck)
{
      int piecePercents = 0;

      if (deck == BOTTOM) {
            piecePercents = w->abacus.decks[BOTTOM].piecePercent;
            if (w->abacus.decks[TOP].number == 0 &&
                        w->abacus.decks[TOP].piecePercent != 0)
                  piecePercents *= w->abacus.decks[TOP].piecePercent;
            if (w->abacus.decks[BOTTOM].number ==
                        w->abacus.decks[TOP].factor - 1)
                  piecePercents -= 1;
      } else {
            if (w->abacus.decks[TOP].number != 0 &&
                        w->abacus.decks[TOP].piecePercent != 0) {
                  piecePercents = w->abacus.decks[TOP].piecePercent;
                  if ((w->abacus.decks[TOP].number + 1) *
                              w->abacus.decks[TOP].factor ==
                              w->abacus.base)
                        piecePercents -= 1;
            }
      }
      return piecePercents;
}

static void
CheckBeads(AbacusWidget w)
{
      char *buf1, *buf2;

      if (w->abacus.aux && w->abacus.vertical) {
            /* not allowed by default, but user can change later */
            w->abacus.vertical = False;
      }
      if (w->abacus.groupSize <= 0) {
            w->abacus.groupSize = 3;
      }
      if (w->abacus.shiftPercent <= 0) {
            w->abacus.shiftPercent = 2;
      }
      if (w->abacus.decimalPosition < 0) {
            w->abacus.decimalPosition = 0;
      }
      if (w->abacus.decks[BOTTOM].piece > MAXBASE) {
            intCat(&buf1, "Bottom Piece must be less than or equal to ", MAXBASE);
            DISPLAY_WARNING(buf1);
            free(buf1);
            w->abacus.decks[BOTTOM].piece = 0;
      } else if (w->abacus.decks[BOTTOM].piece != 0 && w->abacus.decks[BOTTOM].piece < MINBASE) {
            intCat(&buf1, "Bottom Piece must be greater than or equal to ", MINBASE);
            stringCat(&buf2, buf1, ", or 0");
            free(buf1);
            DISPLAY_WARNING(buf2);
            free(buf2);
            w->abacus.decks[BOTTOM].piece = 0;
      }
      if (w->abacus.decks[TOP].piece != 0 && w->abacus.decks[TOP].piece < MINBASE) {
            intCat(&buf1, "Top Piece must be greater than or equal to ", MINBASE);
            stringCat(&buf2, buf1, ", or 0");
            free(buf1);
            DISPLAY_WARNING(buf2);
            free(buf2);
            w->abacus.decks[TOP].piece = 0;
      }
      if (w->abacus.base > MAXBASE) {
            intCat(&buf1, "Base must be less than or equal to ", MAXBASE);
            DISPLAY_WARNING(buf1);
            free(buf1);
            w->abacus.base = DEFAULTBASE;
            SetAbacus(w, ABACUS_BASE_DEFAULT);
      } else if (w->abacus.base < MINBASE) {
            intCat(&buf1, "Base must be greater than or equal to ", MINBASE);
            DISPLAY_WARNING(buf1);
            free(buf1);
            w->abacus.base = DEFAULTBASE;
            SetAbacus(w, ABACUS_BASE_DEFAULT);
      } else if (w->abacus.base != DEFAULTBASE && w->abacus.demo) {
            intCat(&buf1, "Base must be equal to ", DEFAULTBASE);
            stringCat(&buf2, buf1, ", for demo");
            free(buf1);
            DISPLAY_WARNING(buf2);
            free(buf2);
            w->abacus.base = DEFAULTBASE;
            SetAbacus(w, ABACUS_BASE_DEFAULT);
#if 0
      } else if (w->abacus.decks[BOTTOM].piece != 0 &&
                  w->abacus.base % 2 == 1) {
            /* Odd bases produce round-off errors */
            DISPLAY_WARNING("Base can not be odd with piece set");
            w->abacus.base = DEFAULTBASE;
            SetAbacus(w, ABACUS_BASE_DEFAULT);
      } else if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                  w->abacus.base % 2 == 1) {
            /* Odd bases produce round-off errors */
            DISPLAY_WARNING("Base can not be odd with piece percent set");
            w->abacus.base = DEFAULTBASE;
            SetAbacus(w, ABACUS_BASE_DEFAULT);
#endif
      }
      if (w->abacus.decks[BOTTOM].piece != 0 &&
                  (CheckBottomSpace(w) < w->abacus.decks[BOTTOM].piece)) {
            /* Odd bases produce round-off errors but OK */
            /* When base divisible by piece, kind of silly but OK */
            DISPLAY_WARNING("Bottom Spaces must be large enough with base when piece set");
            w->abacus.decks[BOTTOM].spaces -=
                  (CheckBottomSpace(w) - w->abacus.decks[BOTTOM].piece);
      }
      if (w->abacus.decks[BOTTOM].piecePercent > 1 &&
                  (CheckBottomSpace(w) < w->abacus.decks[BOTTOM].piecePercent)) {
            /* Odd bases produce round-off errors but OK */
            /* When base divisible by piece, kind of silly but OK */
            DISPLAY_WARNING("Bottom Spaces must be large enough with base when piece percent set");
            w->abacus.decks[BOTTOM].spaces -=
                  (CheckBottomSpace(w) - w->abacus.decks[BOTTOM].piecePercent);
      }
      if (w->abacus.anomaly < 0) {
            DISPLAY_WARNING("Anomaly must be greater than or equal to 0");
            w->abacus.anomaly = 0;
      }
      if (w->abacus.anomaly >= w->abacus.base) {
            intCat(&buf1, "Anomaly must be less than ", w->abacus.base);
            DISPLAY_WARNING(buf1);
            free(buf1);
            w->abacus.anomaly = 0;
      }
      if (w->abacus.shiftAnomaly < 0) {
            DISPLAY_WARNING("Shift Anomaly must be greater than 0");
            w->abacus.shiftAnomaly = 2;
      }
      if (w->abacus.anomalySq < 0) {
            DISPLAY_WARNING("Anomaly Squared must be greater than or equal to 0");
            w->abacus.anomalySq = 0;
      }
      if (w->abacus.anomalySq >= w->abacus.base) {
            intCat(&buf1, "Anomaly Squared must be less than ", w->abacus.base);
            DISPLAY_WARNING(buf1);
            free(buf1);
            w->abacus.anomalySq = 0;
      }
      if (w->abacus.shiftAnomalySq < 0) {
            DISPLAY_WARNING("Shift Anomaly Squared must be greater than 0");
            w->abacus.shiftAnomalySq = 2;
      }
      if (w->abacus.displayBase > MAXBASE) {
            intCat(&buf1, "Display Base must be less than or equal to ", MAXBASE);
            DISPLAY_WARNING(buf1);
            free(buf1);
            w->abacus.displayBase = DEFAULTBASE;
      } else if (w->abacus.displayBase < MINBASE) {
            intCat(&buf1, "Display Base must be greater than or equal to ",
                  MINBASE);
            DISPLAY_WARNING(buf1);
            free(buf1);
            w->abacus.displayBase = DEFAULTBASE;
      } else if (w->abacus.displayBase != DEFAULTBASE && w->abacus.demo) {
            intCat(&buf1, "Display Base must be equal to ", DEFAULTBASE);
            stringCat(&buf2, buf1, ", for demo");
            free(buf1);
            DISPLAY_WARNING(buf2);
            free(buf2);
            w->abacus.displayBase = DEFAULTBASE;
      }
      if (w->abacus.mode == OTHER && w->abacus.demo && !w->abacus.aux) {
            DISPLAY_WARNING("Format must not be \"Other\", for demo");
            w->abacus.mode = CHINESE;
      }
      /* If a particular mode, ignore improper settings */
      if (w->abacus.mode == RUSSIAN) {
            w->abacus.decks[BOTTOM].factor = 1;
            w->abacus.decks[TOP].factor = w->abacus.base;
            w->abacus.decks[BOTTOM].number = w->abacus.base;
            w->abacus.decks[TOP].number = 0;
            w->abacus.decks[BOTTOM].orientation = DEFAULTTOPORIENT;
            w->abacus.decks[TOP].orientation = DEFAULTTOPORIENT;
            if (w->abacus.decks[BOTTOM].spaces < DEFAULTBOTTOMSPACES)
                  w->abacus.decks[BOTTOM].spaces = DEFAULTBOTTOMSPACES;
            w->abacus.decks[TOP].spaces = 0;
            w->abacus.decks[TOP].room = w->abacus.decks[TOP].number +
                  w->abacus.decks[TOP].spaces;
            w->abacus.decks[BOTTOM].room = w->abacus.decks[BOTTOM].number +
                  w->abacus.decks[BOTTOM].spaces;
            if (w->abacus.decks[BOTTOM].piece != 0) {
                  int pieces;

                  pieces = w->abacus.decks[BOTTOM].piece;
                  if (w->abacus.decks[TOP].number == 0 &&
                        w->abacus.decks[TOP].piece != 0)
                        pieces *= w->abacus.decks[TOP].piece;
                  if (pieces >= w->abacus.decks[BOTTOM].room) {
                        w->abacus.decks[BOTTOM].room = pieces + 1;
                        w->abacus.decks[BOTTOM].spaces =
                              w->abacus.decks[BOTTOM].room -
                              w->abacus.decks[BOTTOM].number;
                  }
            }
            if (w->abacus.decks[BOTTOM].piecePercent != 0) {
                  int piecePercents;

                  piecePercents = w->abacus.decks[BOTTOM].piecePercent;
                  if (w->abacus.decks[TOP].number == 0 &&
                              w->abacus.decks[TOP].piecePercent != 0)
                        piecePercents *= w->abacus.decks[TOP].piecePercent;
                  if (piecePercents >= w->abacus.decks[BOTTOM].room) {
                        w->abacus.decks[BOTTOM].room = piecePercents + 1;
                        w->abacus.decks[BOTTOM].spaces =
                              w->abacus.decks[BOTTOM].room -
                              w->abacus.decks[BOTTOM].number;
                  }
            }
            w->abacus.vertical = True;
            w->abacus.slot = False;
            w->abacus.diamond = False;
      } else if (w->abacus.mode == JAPANESE || w->abacus.mode == ROMAN) {
            w->abacus.decks[BOTTOM].factor = 1;
            w->abacus.decks[TOP].factor = baseToBottom(w->abacus.base);
            w->abacus.decks[BOTTOM].number = w->abacus.decks[TOP].factor -
                  1;
            w->abacus.decks[TOP].number = w->abacus.base /
                  w->abacus.decks[TOP].factor - 1;
            w->abacus.decks[BOTTOM].orientation = DEFAULTBOTTOMORIENT;
            w->abacus.decks[TOP].orientation = DEFAULTTOPORIENT;
            w->abacus.decks[BOTTOM].spaces = DEFAULTBOTTOMSPACES - 1;
            if (w->abacus.decks[BOTTOM].piece != 0 && (CheckBottomSpace(w) <=
                        w->abacus.decks[BOTTOM].piece)) {
                  w->abacus.decks[BOTTOM].spaces -= (CheckBottomSpace(w) -
                        w->abacus.decks[BOTTOM].piece);
            } else if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                        (CheckBottomSpace(w) <=
                        w->abacus.decks[BOTTOM].piecePercent)) {
                  w->abacus.decks[BOTTOM].spaces -= (CheckBottomSpace(w) -
                        w->abacus.decks[BOTTOM].piecePercent);
            }
            w->abacus.decks[TOP].spaces = DEFAULTTOPSPACES - 1;
            w->abacus.decks[TOP].room = w->abacus.decks[TOP].number +
                  w->abacus.decks[TOP].spaces;
            w->abacus.decks[BOTTOM].room = w->abacus.decks[BOTTOM].number +
                  w->abacus.decks[BOTTOM].spaces;
            w->abacus.vertical = False;
            if (w->abacus.mode == JAPANESE) {
                  w->abacus.slot = False;
                  w->abacus.diamond = True;
            }
            if (w->abacus.mode == ROMAN) {
                  w->abacus.slot = True;
                  w->abacus.diamond = False;
            }
      } else if (w->abacus.mode == KOREAN) {
            w->abacus.decks[BOTTOM].factor = 1;
            w->abacus.decks[TOP].factor = baseToBottom(w->abacus.base);
            w->abacus.decks[BOTTOM].number = w->abacus.decks[TOP].factor;
            w->abacus.decks[TOP].number = w->abacus.base /
                  w->abacus.decks[TOP].factor - 1;
            w->abacus.decks[BOTTOM].orientation = DEFAULTBOTTOMORIENT;
            w->abacus.decks[TOP].orientation = DEFAULTTOPORIENT;
            w->abacus.decks[BOTTOM].spaces = DEFAULTBOTTOMSPACES - 1;
            if (w->abacus.decks[BOTTOM].piece != 0 && (CheckBottomSpace(w) <=
                        w->abacus.decks[BOTTOM].piece)) {
                  w->abacus.decks[BOTTOM].spaces -= (CheckBottomSpace(w) -
                        w->abacus.decks[BOTTOM].piece);
            } else if (w->abacus.decks[BOTTOM].piecePercent > 0 &&
                        (CheckBottomSpace(w) <=
                        w->abacus.decks[BOTTOM].piecePercent)) {
                  w->abacus.decks[BOTTOM].spaces -= (CheckBottomSpace(w) -
                        w->abacus.decks[BOTTOM].piecePercent);
            }
            w->abacus.decks[TOP].spaces = DEFAULTTOPSPACES - 1;
            w->abacus.decks[TOP].room = w->abacus.decks[TOP].number +
                  w->abacus.decks[TOP].spaces;
            w->abacus.decks[BOTTOM].room = w->abacus.decks[BOTTOM].number +
                  w->abacus.decks[BOTTOM].spaces;
            w->abacus.vertical = False;
            w->abacus.slot = False;
            w->abacus.diamond = True;
      } else if (w->abacus.mode == CHINESE) {
            w->abacus.decks[BOTTOM].factor = 1;
            w->abacus.decks[TOP].factor = baseToBottom(w->abacus.base);
            w->abacus.decks[BOTTOM].number = w->abacus.decks[TOP].factor;
            w->abacus.decks[TOP].number = w->abacus.base /
                  w->abacus.decks[TOP].factor;
            w->abacus.decks[BOTTOM].orientation = DEFAULTBOTTOMORIENT;
            w->abacus.decks[TOP].orientation = DEFAULTTOPORIENT;
            w->abacus.decks[BOTTOM].spaces = DEFAULTBOTTOMSPACES;
            if (w->abacus.decks[BOTTOM].piece > 0 && (CheckBottomSpace(w) <=
                        w->abacus.decks[BOTTOM].piece)) {
                  w->abacus.decks[BOTTOM].spaces -= (CheckBottomSpace(w) -
                        w->abacus.decks[BOTTOM].piece);
            } else if (w->abacus.decks[BOTTOM].piecePercent > 0 &&
                        (CheckBottomSpace(w) <=
                        w->abacus.decks[BOTTOM].piecePercent)) {
                  w->abacus.decks[BOTTOM].spaces -= (CheckBottomSpace(w) -
                        w->abacus.decks[BOTTOM].piecePercent);
            }
            w->abacus.decks[TOP].spaces = DEFAULTTOPSPACES;
            w->abacus.decks[TOP].room = w->abacus.decks[TOP].number +
                  w->abacus.decks[TOP].spaces;
            w->abacus.decks[BOTTOM].room = w->abacus.decks[BOTTOM].number +
                  w->abacus.decks[BOTTOM].spaces;
            w->abacus.vertical = False;
            w->abacus.slot = False;
            w->abacus.diamond = False;
      }
      if (w->abacus.demo && !w->abacus.aux) {
            /* Trying to keep these at a minimum... */
            if (w->abacus.rails < MINDEMORAILS) {
                  intCat(&buf1, "Number of rails must be at least ",
                        MINDEMORAILS);
                  stringCat(&buf2, buf1, ", for demo");
                  free(buf1);
                  DISPLAY_WARNING(buf2);
                  free(buf2);
                  w->abacus.rails = MINDEMORAILS;
            }
            if (w->abacus.rails - w->abacus.decimalPosition <
                        MINDEMORAILS) {
                  if (w->abacus.decks[BOTTOM].piecePercent != 0) {
                        w->abacus.decks[BOTTOM].piecePercent = 0;
                  }
                  if (w->abacus.decks[BOTTOM].piece != 0) {
                        w->abacus.decks[BOTTOM].piece = 0;
                  }
                  w->abacus.decimalPosition = 0;
            }
#if 0
            /* Increment and decrement will get goofed up, if enabled. */
            if (!(w->abacus.rails & 1)) {
                  w->abacus.rails++;
                  DISPLAY_WARNING(
                        "Number of rails must be odd, for demo");
            }
#endif
      } else {
            if (w->abacus.rails < MINRAILS) {
                  intCat(&buf1, "Number of rails must be at least ",
                        MINRAILS);
                  DISPLAY_WARNING(buf1);
                  free(buf1);
                  w->abacus.rails = MINRAILS;
            }
            if (w->abacus.decks[TOP].factor < 1 ||
                        w->abacus.decks[TOP].factor > w->abacus.base) {
                  intCat(&buf1, "Factor of Top Beads out of bounds, use 1..",
                        w->abacus.base);
                  DISPLAY_WARNING(buf1);
                  free(buf1);
                  w->abacus.decks[TOP].factor = 5;
            }
            if (w->abacus.decks[BOTTOM].factor < 1 ||
                        w->abacus.decks[BOTTOM].factor >
                        w->abacus.base) {
                  intCat(&buf1,
                        "Factor of Bottom Beads out of bounds, use 1..",
                        w->abacus.base);
                  DISPLAY_WARNING(buf1);
                  free(buf1);
                  w->abacus.decks[BOTTOM].factor = 1;
            }
      }
      if (w->abacus.decks[TOP].number < 0 ||
                  w->abacus.decks[TOP].number > w->abacus.base) {
            intCat(&buf1, "Number of Top Beads out of bounds, use 1..",
                  w->abacus.base);
            DISPLAY_WARNING(buf1);
            free(buf1);
            w->abacus.decks[TOP].number = 2;
      }
      if (w->abacus.decks[BOTTOM].number < 0 ||
                  w->abacus.decks[BOTTOM].number > w->abacus.base) {
            intCat(&buf1, "Number of Bottom Beads out of bounds, use 1..",
                  w->abacus.base);
            DISPLAY_WARNING(buf1);
            free(buf1);
            w->abacus.decks[BOTTOM].number = 5;
      }
      if (w->abacus.decks[TOP].spaces < 0) {
            DISPLAY_WARNING("Number of Top Spaces must be at least 0");
            w->abacus.decks[TOP].spaces = 2;
      }
      if (w->abacus.decks[BOTTOM].spaces < 0) {
            DISPLAY_WARNING("Number of Bottom Spaces must be at least 0");
            w->abacus.decks[BOTTOM].spaces = 2;
      }
      if (w->abacus.decks[TOP].spaces == 0 && w->abacus.decks[BOTTOM].spaces == 0) {
            DISPLAY_WARNING("Number of Top + Bottom Spaces must be at least 1");
            w->abacus.decks[BOTTOM].spaces = 2;
      }
      if (w->abacus.groupSize < 2) {
            DISPLAY_WARNING("Group Size must be at least 2");
            w->abacus.groupSize = 3;
      }
      if (w->abacus.delay < 0) {
            DISPLAY_WARNING("Delay must be at least 0");
            w->abacus.delay = -w->abacus.delay;
      }
}

static Boolean
CheckMove(AbacusWidget w)
{
      int deck_number, deck_position;
      char *buf1, *buf2;

      if (w->abacus.deck < 0 || w->abacus.deck > 2) {
            intCat(&buf1, "Corrupted deck input value ", w->abacus.deck);
            stringCat(&buf2, buf1, " out of bounds, use 0..2, ignoring");
            free(buf1);
            DISPLAY_WARNING(buf2);
            free(buf2);
            return False;
      }
      if (w->abacus.rail < -w->abacus.decimalPosition || w->abacus.rail >
                        w->abacus.rails - w->abacus.decimalPosition) {
            intCat(&buf1, "Number of rails to small for input value ",
                  w->abacus.rail);
            DISPLAY_WARNING(buf1);
            free(buf1);
            return False;
      }
      if (w->abacus.deck == PLACESETTING) {
            /* moving decimal point */
            if (w->abacus.number + w->abacus.decimalPosition >=
                        w->abacus.rails || w->abacus.number +
                        w->abacus.decimalPosition < 0) {
                  intCat(&buf1, "Corrupted number for input value ",
                        w->abacus.number);
                  stringCat(&buf2, buf1, " out of bounds, use ");
                  free(buf1);
                  intCat(&buf1, buf2, -w->abacus.decimalPosition);
                  free(buf2);
                  stringCat(&buf2, buf1, "..");
                  free(buf1);
                  intCat(&buf1, buf2, w->abacus.rails -
                        w->abacus.decimalPosition);
                  free(buf2);
                  DISPLAY_WARNING(buf1);
                  free(buf1);
                  return False;
            }
            return True;
      }
      deck_number = w->abacus.decks[w->abacus.deck].number;
      deck_position = w->abacus.decks[w->abacus.deck].position[w->abacus.rail
            + w->abacus.decimalPosition];
      if (w->abacus.decks[w->abacus.deck].orientation &&
                  (w->abacus.number < -deck_number + deck_position ||
                  w->abacus.number > deck_position)) {
            intCat(&buf1, "Corrupted number for input value ",
                  w->abacus.number);
            stringCat(&buf2, buf1, " out of bounds, use ");
            free(buf1);
            intCat(&buf1, buf2, -deck_number + deck_position);
            free(buf2);
            stringCat(&buf2, buf1, "..");
            free(buf1);
            intCat(&buf1, buf2, deck_position);
            free(buf2);
            DISPLAY_WARNING(buf1);
            free(buf1);
            return False;
      }
      if (!w->abacus.decks[w->abacus.deck].orientation &&
                  (w->abacus.number < -deck_position ||
                  w->abacus.number > deck_number - deck_position)) {
            intCat(&buf1, "Corrupted number for input value ",
                  w->abacus.number);
            stringCat(&buf2, buf1, " out of bounds, use ");
            free(buf1);
            intCat(&buf1, buf2, -deck_position);
            free(buf2);
            stringCat(&buf2, buf1, "..");
            free(buf1);
            intCat(&buf1, buf2, deck_number - deck_position);
            free(buf2);
            DISPLAY_WARNING(buf1);
            free(buf1);
            return False;
      }
      return True;
}

static void
ResetBeads(AbacusWidget w)
{
      int deck, rail;

      w->abacus.currentDeck = -1;
      w->abacus.numDigits = w->abacus.rails + CARRY + 1;
      for (deck = BOTTOM; deck <= TOP; deck++) {
            if (w->abacus.decks[deck].position)
                  free(w->abacus.decks[deck].position);
            if (!(w->abacus.decks[deck].position = (int *)
                        malloc(sizeof (int) * w->abacus.rails))) {
                  DISPLAY_ERROR("Not enough memory, exiting.");
            }
      }
      if (w->abacus.digits)
            free(w->abacus.digits);
      if (!(w->abacus.digits = (char *)
                  malloc(sizeof (char) * w->abacus.numDigits))) {
            DISPLAY_ERROR("Not enough memory, exiting.");
      }
      for (deck = BOTTOM; deck <= TOP; deck++) {
            w->abacus.decks[deck].room = w->abacus.decks[deck].number +
                  w->abacus.decks[deck].spaces;
            for (rail = 0; rail < w->abacus.rails; rail++)
                  w->abacus.decks[deck].position[rail] =
                        (w->abacus.decks[deck].orientation) ?
                        w->abacus.decks[deck].number : 0;
      }
      if (w->abacus.sign) {
            rail = w->abacus.rails - 1;
            w->abacus.decks[BOTTOM].position[rail] =
                  (w->abacus.decks[BOTTOM].orientation) ? 1 : 0;
      }
      if (w->abacus.decks[BOTTOM].piece != 0) {
            rail = w->abacus.decimalPosition - 1;
            w->abacus.decks[BOTTOM].position[rail] =
                  (w->abacus.decks[BOTTOM].orientation) ?
                  numberPieces(w, BOTTOM) : 0;
            if (w->abacus.decks[TOP].number != 0 &&
                        w->abacus.decks[TOP].piece != 0) {
                  w->abacus.decks[TOP].position[rail] =
                        (w->abacus.decks[TOP].orientation) ?
                        numberPieces(w, TOP) : 0;
            }
      }
      if (w->abacus.decks[BOTTOM].piecePercent != 0) {
            rail = w->abacus.decimalPosition - w->abacus.shiftPercent - 1 -
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0);
            w->abacus.decks[BOTTOM].position[rail] =
                  (w->abacus.decks[BOTTOM].orientation) ?
                  numberPiecePercents(w, BOTTOM) : 0;
            if (w->abacus.decks[TOP].number != 0 &&
                        w->abacus.decks[TOP].piecePercent != 0) {
                  w->abacus.decks[TOP].position[rail] =
                        (w->abacus.decks[TOP].orientation) ?
                        numberPiecePercents(w, TOP) : 0;
            }
      }
      for (rail = 0; rail < w->abacus.numDigits - 1; rail++)
            w->abacus.digits[rail] = '0';
      w->abacus.digits[w->abacus.numDigits - 1] = '\0';
}

static Boolean
EmptyCounter(AbacusWidget w)
{
      int n = 0;
      Boolean good = True;

      while (n < w->abacus.numDigits - 2) {
            if (w->abacus.digits[n] != '0') {
                  good = False;
                  w->abacus.digits[n] = '0';
            }
            n++;
      }
      return good;
}

static void
SetCounter(AbacusWidget w, int deck, int rail, int number)
{
      int n = 0, s = 0, i;
      int m = 0, o = 0, half;
      char *buffer;
      Boolean anomaly;

      /* n digits above decimal *
       * m digits below decimal */
      while (n < w->abacus.numDigits - CARRY - w->abacus.decimalPosition &&
                  w->abacus.digits[n] == '0')
            n++;
      while (m < w->abacus.decimalPosition - 1 &&
                  w->abacus.digits[w->abacus.numDigits - CARRY - m] ==
                  '0')
            m++;
      while (o < w->abacus.numDigits - 1 && w->abacus.digits[o] == '0')
            o++;
      half = w->abacus.numDigits - CARRY - w->abacus.decimalPosition - n + 1;
      s = (w->abacus.sign &&
            (((w->abacus.decks[BOTTOM].position[w->abacus.rails - 1] == 1 &&
            !w->abacus.decks[BOTTOM].orientation) ||
            (w->abacus.decks[BOTTOM].position[w->abacus.rails - 1] == 0 &&
            w->abacus.decks[BOTTOM].orientation)) ||
            (w->abacus.decks[TOP].number != 0 &&
             w->abacus.decks[TOP].piecePercent != 0 &&
            ((w->abacus.decks[TOP].position[w->abacus.rails - 1] == 1 &&
            !w->abacus.decks[TOP].orientation) ||
            (w->abacus.decks[TOP].position[w->abacus.rails - 1] == 0 &&
            w->abacus.decks[TOP].orientation)))) &&
            o < w->abacus.numDigits - 1) ? 1 : 0;
      if (!(buffer = (char *) malloc(sizeof (char) *
                  (w->abacus.numDigits + 12 + sizeofRoman(w->abacus.base,
                  w->abacus.romanNumerals))))) {
            DISPLAY_ERROR("Not enough memory, exiting.");
      }
      if (s == 1)
            buffer[0] = '-';
      for (i = 0; i < half; i++)
            buffer[s + i] = w->abacus.digits[n + i];
      buffer[s + half] = '.';
      if (w->abacus.decks[BOTTOM].piece != 0) {
            char *stringBuf, *finalBuf, *midBuf;
            int pieces = w->abacus.decks[BOTTOM].piece;
            int precision;

            if (!(stringBuf = (char *) malloc(sizeof (char) *
                        w->abacus.numDigits + 2))) {
                  DISPLAY_ERROR("Not enough memory, exiting.");
            }
            if (!(midBuf = (char *) malloc(sizeof (char) *
                        w->abacus.numDigits + 2))) {
                  DISPLAY_ERROR("Not enough memory, exiting.");
            }
            if (!(finalBuf = (char *) malloc(sizeof (char) *
                        w->abacus.numDigits + 2))) {
                  DISPLAY_ERROR("Not enough memory, exiting.");
            }
            precision = ((w->abacus.decks[BOTTOM].piecePercent != 0) ?
                  2 : 1) * w->abacus.shiftPercent + 2;
            if (precision <= w->abacus.decimalPosition)
                  precision = w->abacus.decimalPosition +
                        ((w->abacus.decks[BOTTOM].piecePercent == 0) ? 1 : 0);
            if (w->abacus.decks[TOP].piece != 0)
                  pieces *= w->abacus.decks[TOP].piece;
            dividePieces(midBuf, w->abacus.base, pieces,
                  char2Int(w->abacus.digits[n + half]),
                  precision);
            if (w->abacus.decks[BOTTOM].piecePercent != 0) {
                  int piecePercents =
                        w->abacus.decks[BOTTOM].piecePercent;

                  if (w->abacus.decks[TOP].piecePercent != 0)
                        piecePercents *=
                              w->abacus.decks[TOP].piecePercent;
                  dividePieces(stringBuf, w->abacus.base, piecePercents,
                        char2Int(w->abacus.digits[n + half + w->abacus.shiftPercent + 1]),
                        precision);
                  shiftDecimal(finalBuf, stringBuf, w->abacus.shiftPercent, 0);
                  addStrings(stringBuf, finalBuf, midBuf, w->abacus.base);
                  for (i = 0; i < w->abacus.decimalPosition - m; i++)
                        buffer[s + half + 1 + i] =
                              w->abacus.digits[n + half + 1 + i];
                  if (m == w->abacus.decimalPosition - 1)
                        m--;
                  buffer[s + half + w->abacus.decimalPosition - m] = '\0';
                  shiftDecimal(midBuf, buffer, -1, w->abacus.shiftPercent);
                  addStrings(finalBuf, stringBuf, midBuf, w->abacus.base);
            } else {
                  for (i = 0; i < w->abacus.decimalPosition - m; i++)
                        buffer[s + half + 1 + i] =
                              w->abacus.digits[n + half + 1 + i];
                  if (m == w->abacus.decimalPosition - 1)
                        m--;
                  buffer[s + half + w->abacus.decimalPosition - m] = '\0';
                  addStrings(finalBuf, buffer, midBuf, w->abacus.base);
            }
            (void) strcpy(buffer, finalBuf);
            free(stringBuf);
            free(midBuf);
            free(finalBuf);
      }
      if (w->abacus.decks[BOTTOM].piece == 0 &&
                  w->abacus.decks[BOTTOM].piecePercent == 0) {
            int offset = w->abacus.decimalPosition - m + 1;

            if (offset < 0)
                  offset = 0;
            for (i = 0; i < offset; i++)
                  buffer[s + half + 1 + i] =
                        w->abacus.digits[n + half + i];
#if 0
            if (offset == 0) {
                  buffer[s + half + 1] = '0';
                  buffer[s + half + 2] = '\0';
            } else
#endif
                  buffer[s + half + offset] = '\0';
      }
      if (w->abacus.script) {
#ifdef SCRIPTFILE
            (void) fprintf(w->abacus.fp, "%d %d %d %d 4\n", PRIMARY, deck,
                  rail - w->abacus.decimalPosition, number);
            (void) fprintf(w->abacus.fp, "Lesson\n\n\nPress Space-bar\n");
#else
            SetAbacusMove(w, ABACUS_SCRIPT, PRIMARY /* FIXME */, deck,
                  rail - w->abacus.decimalPosition, number);
#endif
      }
      anomaly = CheckAnomaly(w);
      if (w->abacus.base != w->abacus.displayBase || anomaly) {
            char buff[1024];

            if (anomaly) {
            convertString(buff, buffer, w->abacus.base,
                  w->abacus.displayBase, w->abacus.decimalPosition,
                  w->abacus.anomaly, w->abacus.shiftAnomaly,
                  w->abacus.anomalySq, w->abacus.shiftAnomalySq);
            } else {
            convertString(buff, buffer, w->abacus.base,
                  w->abacus.displayBase, w->abacus.decimalPosition,
                  0, w->abacus.shiftAnomaly,
                  0, w->abacus.shiftAnomalySq);
            }
            free(buffer);
            if (!(buffer = (char *) malloc(sizeof (char) * 1024))) {
                  DISPLAY_ERROR("Not enough memory, exiting.");
            }
            (void) strcpy(buffer, buff);
      }
      if (w->abacus.romanNumerals) {
            char * romanString;

            (void) strcat(buffer, "          ");
            if (!(romanString = (char *) malloc(sizeof (char) *
                        sizeofRoman(w->abacus.displayBase,
                        w->abacus.romanNumerals)))) {
                  DISPLAY_ERROR("Not enough memory, exiting.");
            }
            (void) string2Roman(romanString, buffer, w->abacus.displayBase);
            (void) strcat(buffer, romanString);
            free(romanString);
      }
      SetAbacusString(w, ABACUS_IGNORE, buffer);
      free(buffer);
}

static void
DrawDecimalSeparator(AbacusWidget w, Boolean show, int rail)
{
      int x, y;
      GC gc = ((show) ? w->abacus.beadShadeGC[1] : w->abacus.inverseGC);
      Pixmap dr = 0;

      x = 1 - w->abacus.railWidth / 2 + w->abacus.offset.x +
            rail * w->abacus.pos.x - (w->abacus.pos.x - 1) / 2;
      y = w->abacus.decks[TOP].room * w->abacus.pos.y + w->abacus.delta.y -
            1 + w->abacus.offset.y;
      if (w->abacus.vertical) {
            y = w->abacus.frameSize.y - w->abacus.midHeight - y;
            FILLRECTANGLE(w, dr, gc, y, x,
                  w->abacus.midHeight, w->abacus.railWidth + 4);
      } else {
            FILLRECTANGLE(w, dr, gc, x, y,
                  w->abacus.railWidth + 4, w->abacus.midHeight);
      }
}

static void
DrawGroupSeparator(AbacusWidget w, Boolean show, int rail)
{
      GC gc = ((show) ? w->abacus.beadShadeGC[1] : w->abacus.inverseGC);
      int x, y;
      Pixmap dr = 0;

      x = 1 - w->abacus.railWidth / 2 + w->abacus.offset.x +
            rail * w->abacus.pos.x - (w->abacus.pos.x - 1) / 2;
      y = w->abacus.decks[TOP].room * w->abacus.pos.y + w->abacus.delta.y -
            1 + w->abacus.offset.y;
      if (w->abacus.vertical) {
            y = w->abacus.frameSize.y - w->abacus.midHeight - y;
            FILLRECTANGLE(w, dr, gc,
                  y + 1, x + 1,
                  w->abacus.midHeight - 2, w->abacus.railWidth + 2);
            FILLRECTANGLE(w, dr, gc,
                  y + (w->abacus.midHeight - 1) / 2, x,
                  (w->abacus.midHeight - 1) % 2 + 1, 1);
            FILLRECTANGLE(w, dr, gc,
                  y + (w->abacus.midHeight - 1) / 2,
                  x + w->abacus.railWidth + 3,
                  (w->abacus.midHeight - 1) % 2 + 1, 1);
            FILLRECTANGLE(w, dr, gc,
                  y, x + (w->abacus.railWidth + 1) / 2 + 1,
                  1, (w->abacus.railWidth + 1) % 2 + 1);
            FILLRECTANGLE(w, dr, gc,
                  y + w->abacus.midHeight - 1,
                  x + (w->abacus.railWidth + 1) / 2 + 1,
                  1, (w->abacus.railWidth + 1) % 2 + 1);
      } else {
            FILLRECTANGLE(w, dr, gc,
                  x + 1, y + 1,
                  w->abacus.railWidth + 2, w->abacus.midHeight - 2);
            FILLRECTANGLE(w, dr, gc,
                  x, y + (w->abacus.midHeight - 1) / 2,
                  1, (w->abacus.midHeight - 1) % 2 + 1);
            FILLRECTANGLE(w, dr, gc,
                  x + w->abacus.railWidth + 3,
                  y + (w->abacus.midHeight - 1) / 2,
                  1, (w->abacus.midHeight - 1) % 2 + 1);
            FILLRECTANGLE(w, dr, gc,
                  x + (w->abacus.railWidth + 1) / 2 + 1, y,
                  (w->abacus.railWidth + 1) % 2 + 1, 1);
            FILLRECTANGLE(w, dr, gc,
                  x + (w->abacus.railWidth + 1) / 2 + 1,
                  y + w->abacus.midHeight - 1,
                  (w->abacus.railWidth + 1) % 2 + 1, 1);
      }
}

static void
DrawAllGroupSeparators(AbacusWidget w, Boolean show)
{
      int separator;

      for (separator = 1; separator <= ((w->abacus.rails -
                  ((w->abacus.sign) ? 1 : 0) -
                  w->abacus.decimalPosition - 1) / w->abacus.groupSize);
                  separator++)
            DrawGroupSeparator(w, show, w->abacus.rails -
                  w->abacus.decimalPosition -
                  w->abacus.groupSize * separator);
}

static void
DrawNegative(AbacusWidget w, Boolean show, int rail)
{
      int x, y;
      GC gc = ((show) ? w->abacus.beadShadeGC[1] : w->abacus.inverseGC);
      Pixmap dr = 0;

      x = 1 - w->abacus.railWidth / 2 + w->abacus.offset.x +
            rail * w->abacus.pos.x - (w->abacus.pos.x - 1) / 2;
      y = w->abacus.decks[TOP].room * w->abacus.pos.y + w->abacus.delta.y -
            1 + w->abacus.offset.y;
      if (w->abacus.vertical) {
            y = w->abacus.frameSize.y - w->abacus.midHeight - y;
            FILLRECTANGLE(w, dr, gc,
                  y + 2, x,
                  2,
                  w->abacus.railWidth + 4);
      } else {
            FILLRECTANGLE(w, dr, gc,
                  x, y + 2,
                  w->abacus.railWidth + 4, 2);
      }
}

static void
DrawPiece(AbacusWidget w, Boolean show, int rail)
{
      int x, y;
      GC gc = ((show) ? w->abacus.beadShadeGC[1] : w->abacus.inverseGC);
      Pixmap dr = 0;

      x = 1 - w->abacus.railWidth / 2 + w->abacus.offset.x +
            rail * w->abacus.pos.x - (w->abacus.pos.x - 1) / 2;
      y = w->abacus.decks[TOP].room * w->abacus.pos.y + w->abacus.delta.y -
            1 + w->abacus.offset.y;
      if (w->abacus.vertical) {
            y = w->abacus.frameSize.y - w->abacus.midHeight - y;
            FILLRECTANGLE(w, dr, gc,
                  y, x + 1,
                  1, w->abacus.railWidth + 2);
            FILLRECTANGLE(w, dr, gc,
                  y + w->abacus.midHeight - 1, x + 1,
                  1, w->abacus.railWidth + 2);
            FILLRECTANGLE(w, dr, gc,
                  y + 1, x,
                  w->abacus.midHeight - 2, 1);
            FILLRECTANGLE(w, dr, gc,
                  y + 1, x + w->abacus.railWidth + 3,
                  w->abacus.midHeight - 2, 1);
      } else {
            FILLRECTANGLE(w, dr, gc,
                  x + 1, y,
                  w->abacus.railWidth + 2, 1);
            FILLRECTANGLE(w, dr, gc,
                  x + 1, y + w->abacus.midHeight - 1,
                  w->abacus.railWidth + 2, 1);
            FILLRECTANGLE(w, dr, gc,
                  x, y + 1,
                  1, w->abacus.midHeight - 2);
            FILLRECTANGLE(w, dr, gc,
                  x + w->abacus.railWidth + 3, y + 1,
                  1, w->abacus.midHeight - 2);
      }
}

static void
DrawAnomaly(AbacusWidget w, Boolean show, int rail)
{
      int x, y;
      GC gc = ((show) ? w->abacus.beadShadeGC[1] : w->abacus.inverseGC);
      Pixmap dr = 0;

      x = 1 - w->abacus.railWidth / 2 + w->abacus.offset.x +
            rail * w->abacus.pos.x - (w->abacus.pos.x - 1) / 2;
      y = w->abacus.decks[TOP].room * w->abacus.pos.y + w->abacus.delta.y -
            1 + w->abacus.offset.y;
      if (w->abacus.vertical) {
            y = w->abacus.frameSize.y - w->abacus.midHeight - y;
            DRAWLINE(w, dr, gc, y, x,
#ifdef WINVER
                  y + w->abacus.midHeight
#else
                  y + w->abacus.midHeight - 1
#endif
                  , x + w->abacus.railWidth + 3);
            DRAWLINE(w, dr, gc, y + w->abacus.midHeight - 1, x,
#ifdef WINVER
                  y - 1
#else
                  y
#endif
                  , x + w->abacus.railWidth + 3);
      } else {
            DRAWLINE(w, dr, gc, x, y, x + w->abacus.railWidth + 3,
#ifdef WINVER
                  y + w->abacus.midHeight
#else
                  y + w->abacus.midHeight - 1
#endif
                  );
            DRAWLINE(w, dr, gc, x, y + w->abacus.midHeight - 1,
                  x + w->abacus.railWidth + 3,
#ifdef WINVER
                  y - 1
#else
                  y
#endif
                  );
      }
}

static void
EraseFrame(const AbacusWidget w, Pixmap dr)
{
      FILLRECTANGLE(w, dr, w->abacus.inverseGC,
            0, 0, w->core.width, w->core.height);
}

static void
DrawFrame(const AbacusWidget w, Pixmap dr, Boolean show, Boolean focus)
{
      int deck, x, y, yOffset;
      GC gc = (show) ? ((focus) ? w->abacus.frameGC : w->abacus.borderGC) :
            w->abacus.inverseGC;
      Boolean anomaly;

      x = w->abacus.rails * w->abacus.pos.x + w->abacus.delta.x - 1;
      if (w->abacus.vertical) {
            /* Top */
            FILLRECTANGLE(w, dr, gc,
                  0, 0, w->abacus.frameSize.y, w->abacus.offset.x + 1);
            /* Bottom */
            FILLRECTANGLE(w, dr, gc,
                  0, x + w->abacus.offset.x,
                  w->abacus.frameSize.y,
                  w->abacus.frameSize.x - (x + w->abacus.offset.x));
      } else {
            /* Left */
            FILLRECTANGLE(w, dr, gc,
                  0, 0, w->abacus.offset.x + 1, w->abacus.frameSize.y);
            /* Right */
            FILLRECTANGLE(w, dr, gc,
                  x + w->abacus.offset.x, 0,
                  w->abacus.frameSize.x - (x + w->abacus.offset.x),
                  w->abacus.frameSize.y);
      }
      for (deck = TOP; deck >= BOTTOM; deck--) {
            yOffset = (deck == TOP) ? 0 : w->abacus.decks[TOP].height;
            y = w->abacus.decks[deck].room * w->abacus.pos.y +
                  w->abacus.delta.y - 1;
            if (deck == TOP) {
                  if (w->abacus.vertical) {
                        /* Right */
                        FILLRECTANGLE(w, dr, gc,
                              w->abacus.frameSize.y - yOffset - w->abacus.offset.y,
                              w->abacus.offset.x + 1,
                              w->abacus.offset.y,
                              x - 1);
                        /* Middle */
                        FILLRECTANGLE(w, dr, gc,
                              w->abacus.frameSize.y - (y + yOffset + w->abacus.offset.y) - w->abacus.midHeight,
                              w->abacus.offset.x + 1,
                              w->abacus.midHeight,
                              x - 1);
                  } else {
                        /* Top */
                        FILLRECTANGLE(w, dr, gc,
                              w->abacus.offset.x + 1,
                              yOffset,
                              x - 1,
                              w->abacus.offset.y);
                        /* Middle */
                        FILLRECTANGLE(w, dr, gc,
                              w->abacus.offset.x + 1,
                              y + yOffset + w->abacus.offset.y,
                              x - 1,
                              w->abacus.midHeight);
                  }
                  DrawDecimalSeparator(w, show, w->abacus.rails -
                        w->abacus.decimalPosition);
                  DrawAllGroupSeparators(w, show);
                  if (w->abacus.sign)
                        DrawNegative(w, show, 1);
                  if (w->abacus.decks[BOTTOM].piece != 0)
                        DrawPiece(w, show, w->abacus.rails -
                              w->abacus.decimalPosition + 1);
                  if (w->abacus.decks[BOTTOM].piecePercent != 0)
                        DrawPiece(w, show, w->abacus.rails -
                              w->abacus.decimalPosition + 1 +
                              ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0) +
                              w->abacus.shiftPercent);
                  anomaly = CheckAnomaly(w);
                  if (anomaly)
                        DrawAnomaly(w, show, w->abacus.rails -
                              w->abacus.decimalPosition -
                              w->abacus.shiftAnomaly);
                  if (anomaly && w->abacus.anomalySq != 0)
                        DrawAnomaly(w, show, w->abacus.rails -
                              w->abacus.decimalPosition -
                              w->abacus.shiftAnomaly -
                              w->abacus.shiftAnomalySq);
            } else {
                  if (w->abacus.vertical) {
                        /* Left */
                        FILLRECTANGLE(w, dr, gc,
                              0,
                              w->abacus.offset.x + 1,
                              w->abacus.frameSize.y -
                              (y + yOffset + w->abacus.offset.y + w->abacus.midHeight - 3),
                              x - 1);
                  } else {
                        /* Bottom */
                        FILLRECTANGLE(w, dr, gc,
                              w->abacus.offset.x + 1,
                              y + yOffset + w->abacus.offset.y + w->abacus.midHeight - 3,
                              x - 1,
                              w->abacus.frameSize.y - (y + yOffset + w->abacus.offset.y + w->abacus.midHeight - 3));
                  }
            }
      }
}

static void
FillRectClipX(AbacusWidget w, Pixmap dr, GC gc, int dx, int dy, int sx, int sy,
            int ox, int wox, int wsx)
{
      int nox = ox, nsx = sx;

      if (ox + sx < wox || ox > wox + wsx || wsx <= 0)
            return;
      if (nox < wox) {
            nox = wox;
            nsx = sx - wox + ox;
      }
      if (nox + nsx > wox + wsx) {
            nsx = wsx + wox - nox;
      }
      FILLRECTANGLE(w, dr, gc, dx + nox, dy, nsx, sy);
}

static void
FillRectClipY(AbacusWidget w, Pixmap dr, GC gc, int dx, int dy, int sx, int sy,
            int oy, int woy, int wsy)
{
      int noy = oy, nsy = sy;

      if (oy + sy < woy || oy > woy + wsy || wsy <= 0)
            return;
      if (noy < woy) {
            noy = woy;
            nsy = sy - woy + oy;
      }
      if (noy + nsy > woy + wsy) {
            nsy = wsy + woy - noy;
      }
      FILLRECTANGLE(w, dr, gc, dx, dy + noy, sx, nsy);
}

static void
DrawRail(AbacusWidget w, const int deck, const int rail, const int j,
             const int offset, const int size)
{
      int dx, dy, yOffset;
      Pixmap dr = 0;

      yOffset = (deck == TOP) ? 0 :
            w->abacus.decks[TOP].height + w->abacus.midHeight - 3;
      dx = (w->abacus.rails - rail - 1) * w->abacus.pos.x +
            w->abacus.delta.x + w->abacus.offset.x;
      dy = (j - 1) * w->abacus.pos.y + w->abacus.delta.y +
            yOffset + w->abacus.offset.y - 2;
      if (w->abacus.vertical) {
            dy = w->abacus.frameSize.y - w->abacus.beadSize.y - dy - 3;
            FillRectClipX(w, dr, w->abacus.inverseGC,
              dy, dx,
              w->abacus.beadSize.y + 3, w->abacus.beadSize.x + 2,
              0, offset, size);
            if (w->abacus.slot &&
                j == w->abacus.decks[deck].room) {
              FillRectClipX(w, dr, w->abacus.borderGC,
                dy,
                dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                5 * w->abacus.beadSize.y / 8 + 4,
                w->abacus.railWidth,
                3 * w->abacus.beadSize.y / 8, offset, size);
              /* round off the right of rail */
              FillRectClipX(w, dr, w->abacus.inverseGC,
                dy - 1,
                dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                2, 1, 3 * w->abacus.beadSize.y / 8, offset, size);
              FillRectClipX(w, dr, w->abacus.inverseGC,
                dy - 1,
                dx + w->abacus.beadSize.x / 2 + (w->abacus.railWidth - 1) / 2,
                2, 1, 3 * w->abacus.beadSize.y / 8, offset, size);
            } else if (w->abacus.slot && j == 1) {
              FillRectClipX(w, dr, w->abacus.borderGC,
                dy,
                dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                5 * w->abacus.beadSize.y / 8 + 3,
                w->abacus.railWidth,
                0, offset, size);
              /* round off the left of rail */
              FillRectClipX(w, dr, w->abacus.inverseGC,
                dy,
                dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                2, 1, 2 + 5 * w->abacus.beadSize.y / 8, offset, size);
              FillRectClipX(w, dr, w->abacus.inverseGC,
                dy,
                dx + w->abacus.beadSize.x / 2 + (w->abacus.railWidth - 1) / 2,
                2, 1, 2 + 5 * w->abacus.beadSize.y / 8, offset, size);
            } else {
              FillRectClipX(w, dr, (w->abacus.slot) ?
                w->abacus.borderGC : w->abacus.railGC,
                dy,
                dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                w->abacus.beadSize.y + 3, w->abacus.railWidth,
                0, offset, size);
            }
      } else {
            FillRectClipY(w, dr, w->abacus.inverseGC,
              dx, dy,
              w->abacus.beadSize.x + 2, w->abacus.beadSize.y + 3,
              0, offset, size);
            if (w->abacus.slot && j == 1) {
              FillRectClipY(w, dr, w->abacus.borderGC,
                dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                dy,
                w->abacus.railWidth,
                5 * w->abacus.beadSize.y / 8 + 4,
                3 * w->abacus.beadSize.y / 8, offset, size);
              /* round off the top of rail */
              FillRectClipY(w, dr, w->abacus.inverseGC,
                dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                dy - 1,
                1, 2, 3 * w->abacus.beadSize.y / 8, offset, size);
              FillRectClipY(w, dr, w->abacus.inverseGC,
                dx + w->abacus.beadSize.x / 2 + (w->abacus.railWidth - 1) / 2,
                dy - 1,
                1, 2, 3 * w->abacus.beadSize.y / 8, offset, size);
            } else if (w->abacus.slot &&
                j == w->abacus.decks[deck].room) {
              FillRectClipY(w, dr, w->abacus.borderGC,
                dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                dy,
                w->abacus.railWidth,
                5 * w->abacus.beadSize.y / 8 + 3,
                0, offset, size);
              /* round off the bottom of rail */
              FillRectClipY(w, dr, w->abacus.inverseGC,
                dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                dy,
                1, 2, 2 + 5 * w->abacus.beadSize.y / 8, offset, size);
              FillRectClipY(w, dr, w->abacus.inverseGC,
                dx + w->abacus.beadSize.x / 2 + (w->abacus.railWidth - 1) / 2,
                dy,
                1, 2, 2 + 5 * w->abacus.beadSize.y / 8, offset, size);
            } else {
              FillRectClipY(w, dr, (w->abacus.slot) ?
                w->abacus.borderGC : w->abacus.railGC,
                dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                dy,
                w->abacus.railWidth, w->abacus.beadSize.y + 3,
                0, offset, size);
            }
      }
}

static void
DrawBead(AbacusWidget w, const int deck, const int rail, const int bead,
            const int j, const Boolean show, const Boolean moving,
            const int pressedOffset,
            const int offsetX, const int offsetY)
{
      int dx, dy, yOffset, special = 0, pieces, piecePercents;
      Point size;
      Pixmap dr = 0;

      yOffset = (deck == TOP) ? 0 :
            w->abacus.decks[TOP].height + w->abacus.midHeight - 3;
      dx = (w->abacus.rails - rail - 1) * w->abacus.pos.x +
            w->abacus.delta.x + w->abacus.offset.x + offsetX;
      dy = (j - 1) * w->abacus.pos.y + w->abacus.delta.y +
            yOffset + w->abacus.offset.y - 1 + offsetY;
      if (show) {
            if ((rail == w->abacus.rails - 1) && w->abacus.sign) {
                  special++;
            } else if (w->abacus.decks[BOTTOM].piece != 0 &&
                  (rail == w->abacus.decimalPosition - 1)) {
              if (w->abacus.decks[(deck == TOP) ? BOTTOM : TOP].number == 0) {
                  pieces = numberPieces(w, BOTTOM);
                  if ((((bead == pieces / 2) && ((pieces & 1) == 0)) ||
                        bead == pieces / 2 + 1) && pieces > 2)
                     special++;
              } else {
                  special++;
              }
            } else if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                  (rail == w->abacus.decimalPosition -
                  w->abacus.shiftPercent - 1 -
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0))) {
              if (w->abacus.decks[(deck == TOP) ? BOTTOM : TOP].number == 0) {
                  if ((((bead == w->abacus.decks[BOTTOM].piecePercent / 2) && ((w->abacus.decks[BOTTOM].piecePercent & 1) == 0)) ||
                              bead == w->abacus.decks[BOTTOM].piecePercent / 2 + 1) && w->abacus.decks[BOTTOM].piecePercent > 2) {
                    special++;
                  piecePercents = numberPiecePercents(w, BOTTOM);
                  if ((((bead == piecePercents / 2) && ((piecePercents & 1) == 0)) ||
                        bead == piecePercents / 2 + 1) && piecePercents > 2)
                     special++;
                  }
              } else {
                  special++;
              }
            } else if (!((rail == w->abacus.rails - 1) && w->abacus.sign) &&
              w->abacus.decks[(deck == TOP) ? BOTTOM : TOP].number == 0) {
              if ((((bead == w->abacus.base / 2) &&
                        ((w->abacus.decks[deck].number & 1) == 0)) ||
                        bead == w->abacus.decks[deck].number / 2 + 1) &&
                        w->abacus.base > 2) {
                  special++;
              }
              if (bead == w->abacus.base &&
                        rail - w->abacus.decimalPosition > 0 &&
                        (rail - w->abacus.decimalPosition) %
                        w->abacus.groupSize == 0)
                  special++;
            }
            if (w->abacus.vertical)
                  dy = w->abacus.frameSize.y - w->abacus.beadSize.y - dy - 1;
            dx += pressedOffset;
            dy += pressedOffset;
      if (w->abacus.vertical) {
            if (!moving && pressedOffset == 0) {
                FILLRECTANGLE(w, dr, w->abacus.inverseGC,
                        dy + w->abacus.beadSize.y + 1,
                        dx,
                        1, w->abacus.beadSize.x);
                /* Draw the rail around bead */
                if (!w->abacus.slot || j != 1) {
                  FILLRECTANGLE(w, dr, (w->abacus.slot) ?
                        w->abacus.borderGC : w->abacus.railGC,
                        dy + w->abacus.beadSize.y - 1,
                        dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                        3, w->abacus.railWidth);
                }
                if (!w->abacus.slot || j != w->abacus.decks[deck].room) {
                  FILLRECTANGLE(w, dr, (w->abacus.slot) ?
                        w->abacus.borderGC : w->abacus.railGC,
                        dy - 1,
                        dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                        3, w->abacus.railWidth);
                }
            } else {
                /* Tweak */
                FILLRECTANGLE(w, dr, w->abacus.inverseGC,
                        dy - pressedOffset,
                        dx - pressedOffset,
                        1, w->abacus.beadSize.x);
                FILLRECTANGLE(w, dr, w->abacus.inverseGC,
                        dy - pressedOffset,
                        dx - pressedOffset,
                        w->abacus.beadSize.y, 1);
                if (!w->abacus.slot || j != w->abacus.decks[deck].room) {
                  FILLRECTANGLE(w, dr, (w->abacus.slot) ?
                        w->abacus.borderGC : w->abacus.railGC,
                        dy - pressedOffset,
                        dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2 - pressedOffset,
                        1, w->abacus.railWidth);
                }
            }
      } else {
            if (!moving && pressedOffset == 0) {
                FILLRECTANGLE(w, dr, w->abacus.inverseGC,
                        dx,
                        dy + w->abacus.beadSize.y + 1,
                        w->abacus.beadSize.x, 1);
                /* Draw the rail around bead */
                if (!w->abacus.slot || j != 1) {
                  FILLRECTANGLE(w, dr, (w->abacus.slot) ?
                        w->abacus.borderGC : w->abacus.railGC,
                        dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                        dy - 1,
                        w->abacus.railWidth, 3);
                }
                if (!w->abacus.slot || j != w->abacus.decks[deck].room) {
                  FILLRECTANGLE(w, dr, (w->abacus.slot) ?
                        w->abacus.borderGC : w->abacus.railGC,
                        dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2,
                        dy + w->abacus.beadSize.y - 1,
                        w->abacus.railWidth, 3);
                }
            } else {
                /* Tweak */
                FILLRECTANGLE(w, dr, w->abacus.inverseGC,
                        dx - pressedOffset,
                        dy - pressedOffset,
                        w->abacus.beadSize.x, 1);
                FILLRECTANGLE(w, dr, w->abacus.inverseGC,
                        dx - pressedOffset,
                        dy - pressedOffset,
                        1, w->abacus.beadSize.y);
                if (!w->abacus.slot || j != 1) {
                  FILLRECTANGLE(w, dr, (w->abacus.slot) ?
                        w->abacus.borderGC : w->abacus.railGC,
                        dx + w->abacus.beadSize.x / 2 - w->abacus.railWidth / 2 - pressedOffset,
                        dy - pressedOffset,
                        w->abacus.railWidth, 1);
                }
            }
      }
            if (w->abacus.vertical) {
                  size.x = w->abacus.pos.y - 1;
                  size.y = w->abacus.beadSize.x + 1;
                  yOffset = dx;
                  dx = dy;
                  dy = yOffset;
            } else {
                  size.x = w->abacus.beadSize.x + 1;
                  size.y = w->abacus.pos.y - 1;
            }
#ifdef WINVER
            w->core.hOldBitmap = (HBITMAP) SelectObject(w->core.memDC,
                  w->abacus.bufferBead[pressedOffset][special]);
            BitBlt(w->core.hDC,
                  dx, dy,
                  size.x, size.y,
                  w->core.memDC,
                  0, 0,
                  SRCCOPY);
            (void) SelectObject(w->core.memDC, w->core.hOldBitmap);
#else
            (void) XSetGraphicsExposures(XtDisplay(w), w->abacus.frameGC,
                  False);
            (void) XCopyArea(XtDisplay(w),
                  w->abacus.bufferBead[pressedOffset][special],
                  XtWindow(w),
                  w->abacus.frameGC,
                  0, 0,
                  size.x, size.y,
                  dx, dy);
#endif
      } else {
            DrawRail(w, deck, rail, j, pressedOffset,
                  w->abacus.beadSize.y + 3);
      }
}

static void
DrawBufferedBead(AbacusWidget w, const int pressedOffset, const int special)
{
      int shadeFill, shadeLine, shadeDot;
      Pixmap *dr;

      dr = &(w->abacus.bufferBead[pressedOffset][special]);
      if (pressedOffset == 1) {
#ifdef INSIDEOUT
            if (w->abacus.diamond) {
                  shadeDot = 2;
                  shadeLine = shadeFill = 1;
            } else {
#endif
            shadeFill = 2;
            shadeLine = shadeDot = 1;
#ifdef INSIDEOUT
            }
#endif
      } else {
            shadeFill = 1;
            shadeLine = 2;
            shadeDot = 0;
      }
      if (special == 1) {
            shadeFill++;
            shadeLine++;
            shadeDot++;
      }
      if (w->abacus.vertical) {
            FILLRECTANGLE(w, *dr, w->abacus.inverseGC,
                  0, 0, w->abacus.pos.y - 1, w->abacus.beadSize.x + 1);
            if (w->abacus.diamond) {
                  Point tempList[5];
                  int railWid = MIN(w->abacus.beadSize.x - 5,
                        w->abacus.railWidth);

                  tempList[0].x = w->abacus.beadSize.y;
                  tempList[0].y = w->abacus.beadSize.x / 2 +
                        (railWid - 1) / 2 + 2;
                  tempList[1].x = w->abacus.beadSize.y;
                  tempList[1].y = w->abacus.beadSize.x / 2 -
                        railWid / 2 - 2;
                  tempList[2].x = w->abacus.beadSize.y / 2;
                  tempList[2].y = 0;
                  tempList[3].x = w->abacus.beadSize.y / 2;
                  tempList[3].y = w->abacus.beadSize.x;
                  tempList[4].x = w->abacus.beadSize.y;
                  tempList[4].y = w->abacus.beadSize.x / 2 +
                        (railWid - 1) / 2 + 2;
                  POLYGON(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadShadeGC[shadeFill],
                        tempList, 4, True, True);
                  tempList[0].x = 0;
                  tempList[0].y = w->abacus.beadSize.x / 2 -
                        railWid / 2 - 2;
                  tempList[1].x = 0;
                  tempList[1].y = w->abacus.beadSize.x / 2 +
                        (railWid - 1) / 2 + 2;
                  tempList[2].x = w->abacus.beadSize.y / 2;
                  tempList[2].y = w->abacus.beadSize.x;
                  tempList[3].x = w->abacus.beadSize.y / 2;
                  tempList[3].y = 0;
                  tempList[4].x = 0;
                  tempList[4].y = w->abacus.beadSize.x / 2 -
                        railWid / 2 - 2;
                  POLYGON(w, *dr, w->abacus.beadShadeGC[shadeDot],
                        w->abacus.beadShadeGC[shadeDot],
                        tempList, 4, True, True);
            } else {
                if (w->abacus.beadSize.x > w->abacus.beadSize.y) {
                  DRAWCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.y - 1,
                        (w->abacus.beadSize.y - 1) / 2,
                        w->abacus.beadSize.x / 2 -
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2);
                  DRAWCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.y - 1,
                        (w->abacus.beadSize.y - 1) / 2,
                        w->abacus.beadSize.x / 2 +
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2);
                  DRAWRECTANGLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        0, w->abacus.beadSize.x / 2 -
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2,
                        w->abacus.beadSize.y,
                        w->abacus.beadSize.x - w->abacus.beadSize.y);
                  FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.y - 1,
                        (w->abacus.beadSize.y - 1) / 2,
                        w->abacus.beadSize.x / 2 -
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2);
                  FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.y - 1,
                        (w->abacus.beadSize.y - 1) / 2,
                        w->abacus.beadSize.x / 2 +
                     (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2);
                  FILLRECTANGLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        0, w->abacus.beadSize.x / 2 -
                      (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2,
                        w->abacus.beadSize.y,
                        w->abacus.beadSize.x - w->abacus.beadSize.y);
#ifdef INSIDEOUT
                   if (pressedOffset == 0) {
#endif
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.y / 6,
                              -w->abacus.beadSize.y  / 5 +
                              (w->abacus.beadSize.y - 1) / 2,
                              -w->abacus.beadSize.y / 5 +
                              w->abacus.beadSize.x / 2 -
                     (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2);
#ifdef INSIDEOUT
                      } else {
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.y / 6,
                              w->abacus.beadSize.y  / 5 +
                              (w->abacus.beadSize.y - 1) / 2,
                              w->abacus.beadSize.y / 5 +
                              w->abacus.beadSize.x / 2 +
                     (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2);
                      }
#endif
                  } else if (w->abacus.beadSize.x < w->abacus.beadSize.y) {
                      DRAWCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.x - 1,
                        w->abacus.beadSize.y / 2 - 2 -
                    (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2,
                        (w->abacus.beadSize.x - 1) / 2);
                      DRAWCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.x - 1,
                        w->abacus.beadSize.y / 2 + 1 +
                    (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2,
                        (w->abacus.beadSize.x - 1) / 2);
                      DRAWRECTANGLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.y / 2 - 1 -
                     (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2, 0,
                        w->abacus.beadSize.y - w->abacus.beadSize.x + 2,
                        w->abacus.beadSize.x);
                       FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.x - 1,
                        w->abacus.beadSize.y / 2 - 2 -
                    (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2,
                        (w->abacus.beadSize.x - 1) / 2);
                       FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.x - 1,
                        w->abacus.beadSize.y / 2 + 1 +
                    (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2,
                        (w->abacus.beadSize.x - 1) / 2);
                      FILLRECTANGLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.y / 2 -
                     (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2, 0,
                        w->abacus.beadSize.y - w->abacus.beadSize.x + 2,
                        w->abacus.beadSize.x);
#ifdef INSIDEOUT
                      if (pressedOffset == 0) {
#endif
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.x / 6,
                        -w->abacus.beadSize.x / 5 +
                        w->abacus.beadSize.y / 2 -
                     (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2,
                        -w->abacus.beadSize.x / 5 +
                        (w->abacus.beadSize.x - 1) / 2);
#ifdef INSIDEOUT
                      } else {
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.x / 6,
                        w->abacus.beadSize.x / 5 +
                        w->abacus.beadSize.y / 2 +
                     (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2,
                        w->abacus.beadSize.x / 5 +
                        (w->abacus.beadSize.x - 1) / 2);
                      }
#endif
                  } else {
                      DRAWCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.y - 1,
                        (w->abacus.beadSize.y - 1) / 2,
                        (w->abacus.beadSize.x - 1) / 2);
                      FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.y - 1,
                        (w->abacus.beadSize.y - 1) / 2,
                        (w->abacus.beadSize.x - 1) / 2);
                      if (pressedOffset == 0) {
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.x / 6,
                        -w->abacus.beadSize.x / 5 +
                        (w->abacus.beadSize.y - 1) / 2,
                        -w->abacus.beadSize.x / 5 +
                        (w->abacus.beadSize.x - 1) / 2);
                      } else {
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.x / 6,
                        w->abacus.beadSize.x / 5 +
                        (w->abacus.beadSize.y - 1) / 2,
                        w->abacus.beadSize.x / 5 +
                        (w->abacus.beadSize.x - 1) / 2);
                      }
                  }
            }
      } else {
            FILLRECTANGLE(w, *dr, w->abacus.inverseGC,
                  0, 0, w->abacus.beadSize.x + 1, w->abacus.pos.y - 1);
            if (w->abacus.diamond) {
                  Point tempList[5];
                  int railWid = MIN(w->abacus.beadSize.x - 5,
                        w->abacus.railWidth);

                  tempList[0].x = w->abacus.beadSize.x / 2 +
                        (railWid - 1) / 2 + 2;
                  tempList[0].y = w->abacus.beadSize.y;
                  tempList[1].x = w->abacus.beadSize.x / 2 -
                        railWid / 2 - 2;
                  tempList[1].y = w->abacus.beadSize.y;
                  tempList[2].x = 0;
                  tempList[2].y = w->abacus.beadSize.y / 2;
                  tempList[3].x = w->abacus.beadSize.x;
                  tempList[3].y = w->abacus.beadSize.y / 2;
                  tempList[4].x = w->abacus.beadSize.x / 2 +
                        (railWid - 1) / 2 + 2;
                  tempList[4].y = w->abacus.beadSize.y;
                  POLYGON(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadShadeGC[shadeFill],
                        tempList, 4, True, True);
                  tempList[0].x = w->abacus.beadSize.x / 2 -
                        railWid / 2 - 2;
                  tempList[0].y = 0;
                  tempList[1].x = w->abacus.beadSize.x / 2 +
                        (railWid - 1) / 2 + 2;
                  tempList[1].y = 0;
                  tempList[2].x = w->abacus.beadSize.x;
                  tempList[2].y = w->abacus.beadSize.y / 2;
                  tempList[3].x = 0;
                  tempList[3].y = w->abacus.beadSize.y / 2;
                  tempList[4].x = w->abacus.beadSize.x / 2 -
                        railWid / 2 - 2;
                  tempList[4].y = 0;
                  POLYGON(w, *dr, w->abacus.beadShadeGC[shadeDot],
                        w->abacus.beadShadeGC[shadeDot],
                        tempList, 4, True, True);
            } else {
                if (w->abacus.beadSize.x > w->abacus.beadSize.y) {
                  DRAWCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.y - 1,
                        w->abacus.beadSize.x / 2 -
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2,
                        (w->abacus.beadSize.y - 1) / 2);
                  DRAWCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.y - 1,
                        w->abacus.beadSize.x / 2 +
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2,
                        (w->abacus.beadSize.y - 1) / 2);
                  DRAWRECTANGLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.x / 2 -
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2, 0,
                        w->abacus.beadSize.x - w->abacus.beadSize.y,
                        w->abacus.beadSize.y);
                  FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.y - 1,
                        w->abacus.beadSize.x / 2 -
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2,
                        (w->abacus.beadSize.y - 1) / 2);
                  FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.y - 1,
                        w->abacus.beadSize.x / 2 +
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2,
                        (w->abacus.beadSize.y - 1) / 2);
                  FILLRECTANGLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.x / 2 -
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2, 0,
                        w->abacus.beadSize.x - w->abacus.beadSize.y,
                        w->abacus.beadSize.y);
#ifdef INSIDEOUT
                      if (pressedOffset == 0) {
#endif
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.y / 6,
                              -w->abacus.beadSize.y / 5 +
                              w->abacus.beadSize.x / 2 -
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2,
                              -w->abacus.beadSize.y / 5 +
                              (w->abacus.beadSize.y - 1) / 2);
#ifdef INSIDEOUT
                      } else {
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.y / 6,
                              w->abacus.beadSize.y / 5 +
                              w->abacus.beadSize.x / 2 +
                        (w->abacus.beadSize.x - w->abacus.beadSize.y) / 2,
                              w->abacus.beadSize.y / 5 +
                              (w->abacus.beadSize.y - 1) / 2);
                      }
#endif
                  } else if (w->abacus.beadSize.x < w->abacus.beadSize.y) {
                      DRAWCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.x - 1,
                        (w->abacus.beadSize.x - 1) / 2,
                        w->abacus.beadSize.y / 2 - 2 -
                    (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2);
                      DRAWCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.x - 1,
                        (w->abacus.beadSize.x - 1) / 2,
                        w->abacus.beadSize.y / 2 + 1 +
                    (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2);
                      DRAWRECTANGLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        0, w->abacus.beadSize.y / 2 - 1 -
                        (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2,
                        w->abacus.beadSize.x,
                        w->abacus.beadSize.y - w->abacus.beadSize.x + 2);
                      FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.x - 1,
                        (w->abacus.beadSize.x - 1) / 2,
                        w->abacus.beadSize.y / 2 - 2 -
                        (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2);
                      FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.x - 1,
                        (w->abacus.beadSize.x - 1) / 2,
                        w->abacus.beadSize.y / 2 + 1 +
                        (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2);
                      FILLRECTANGLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        0, w->abacus.beadSize.y / 2 -
                        (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2,
                        w->abacus.beadSize.x,
                        w->abacus.beadSize.y - w->abacus.beadSize.x + 2);
#ifdef INSIDEOUT
                      if (pressedOffset == 0) {
#endif
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.x / 6,
                        -w->abacus.beadSize.x / 5 +
                        (w->abacus.beadSize.x - 1) / 2,
                        -w->abacus.beadSize.x / 5 +
                         w->abacus.beadSize.y / 2 -
                        (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2);
#ifdef INSIDEOUT
                      } else {
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.x / 6,
                        w->abacus.beadSize.x / 5 +
                        (w->abacus.beadSize.x - 1) / 2,
                        w->abacus.beadSize.x / 5 +
                        w->abacus.beadSize.y / 2 +
                        (w->abacus.beadSize.y - w->abacus.beadSize.x) / 2);
                      }
#endif
                  } else {
                      DRAWCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeLine],
                        w->abacus.beadSize.y - 1,
                        (w->abacus.beadSize.x - 1) / 2,
                        (w->abacus.beadSize.y - 1) / 2);
                      FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeFill],
                        w->abacus.beadSize.y - 1,
                        (w->abacus.beadSize.x - 1) / 2,
                        (w->abacus.beadSize.y - 1) / 2);
                      if (pressedOffset == 0) {
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.x / 6,
                        -w->abacus.beadSize.x / 5 +
                        (w->abacus.beadSize.x - 1) / 2,
                        -w->abacus.beadSize.x / 5 +
                         (w->abacus.beadSize.y - 1) / 2);
                      } else {
                        FILLCIRCLE(w, *dr, w->abacus.beadShadeGC[shadeDot],
                              w->abacus.beadSize.x / 6,
                        w->abacus.beadSize.x / 5 +
                        (w->abacus.beadSize.x - 1) / 2,
                        w->abacus.beadSize.x / 5 +
                        (w->abacus.beadSize.y - 1) / 2);
                      }
                  }
            }
      }
}

static void
DrawAllBufferedBeads(AbacusWidget w)
{
      int pressedOffset, shade;

      for (pressedOffset = 0; pressedOffset < 2; pressedOffset++) {
            for (shade = 0; shade < 2; shade++) {
                  DrawBufferedBead(w, pressedOffset, shade);
            }
      }
}

static void
AnimateSlide(AbacusWidget w, int deck, int rail, int bead, int j, int spaces,
            int dir, int delay)
{
      int space, inc, aBead, numBeads;
      int gapJ;
      int posOff, beadOff;

      if (dir == UP)
            numBeads = bead - w->abacus.decks[deck].position[rail];
      else
            numBeads = w->abacus.decks[deck].position[rail] - bead + 1;
      for (space = 0; space < spaces; space++) {
        gapJ = w->abacus.pos.y / w->abacus.numSlices;
        if (gapJ == 0)
          gapJ++;
        FLUSH(w);
        initTimer(w->abacus.oldTime);
        for (inc = 0; inc < w->abacus.pos.y + gapJ; inc += gapJ) {
          if (inc > w->abacus.pos.y) {
            gapJ = w->abacus.pos.y + gapJ - inc;
            inc = w->abacus.pos.y;
          }
          for (aBead = numBeads - 1; aBead >= 0; aBead--) {
            beadOff = NEWPOS(dir, aBead);
            posOff = NEWPOS(dir, (aBead + space));
            /* actual bead, bead position */
            DrawBead(w, deck, rail, bead + beadOff, j,
            True, True, FALSE, 0,
            NEWPOS(dir, inc) + posOff * w->abacus.pos.y);
            /* Erase old slivers */
            if ((w->abacus.vertical && dir == DOWN) ||
              (!w->abacus.vertical && dir == UP)) {
            DrawRail(w, deck, rail, j + posOff,
              w->abacus.pos.y - inc,
              gapJ);
            } else {
            DrawRail(w, deck, rail, j + posOff,
              inc - gapJ + 1,
              gapJ);
            }
          }
          FLUSH(w);
          useTimer(&(w->abacus.oldTime), delay);
        }
      }
}

static void
AddBead(AbacusWidget w, const int d, const int p)
{
      int position = w->abacus.numDigits - 2 - p;
      int digit = char2Int(w->abacus.digits[position]);
      int base = w->abacus.base;

      digit += d;
      if (w->abacus.decks[BOTTOM].piece != 0 && (p == w->abacus.decimalPosition - 1)) {
            base = w->abacus.decks[BOTTOM].piece;
            if (w->abacus.decks[TOP].piece != 0)
                  base *= w->abacus.decks[TOP].piece;
      } else if (w->abacus.decks[BOTTOM].piecePercent != 0 && (p == w->abacus.decimalPosition - w->abacus.shiftPercent - 1 - ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0))) {
            base = w->abacus.decks[BOTTOM].piecePercent;
            if (w->abacus.decks[TOP].piecePercent != 0)
                  base *= w->abacus.decks[TOP].piecePercent;
      }
      w->abacus.digits[position] = int2Char(digit % base);
      if (digit >= base) {
            if ((w->abacus.decks[BOTTOM].piece != 0 && (p + 1 == w->abacus.decimalPosition - 1)) ||
                (w->abacus.decks[BOTTOM].piecePercent != 0 && (p + 1 == w->abacus.decimalPosition -
            w->abacus.shiftPercent - 1 - ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0))))
                  AddBead(w, digit / base, p + 2);
            else
                  AddBead(w, digit / base, p + 1);
      }
}

static void
SubBead(AbacusWidget w, const int d, const int p)
{
      int position = w->abacus.numDigits - 2 - p;
      int digit = char2Int(w->abacus.digits[position]);
      int base = w->abacus.base;

      digit -= d;
      if (w->abacus.decks[BOTTOM].piece != 0 && (p == w->abacus.decimalPosition - 1)) {
            base = w->abacus.decks[BOTTOM].piece;
            if (w->abacus.decks[TOP].piece != 0)
                  base *= w->abacus.decks[TOP].piece;
      } else if (w->abacus.decks[BOTTOM].piecePercent != 0 && (p == w->abacus.decimalPosition - w->abacus.shiftPercent - 1 - ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0))) {
            base = w->abacus.decks[BOTTOM].piecePercent;
            if (w->abacus.decks[TOP].piecePercent != 0)
                  base *= w->abacus.decks[TOP].piecePercent;
      }
      w->abacus.digits[position] = int2Char(((digit + base) % base));
      if (digit < 0) {
            if ((w->abacus.decks[BOTTOM].piece != 0 && (p + 1 == w->abacus.decimalPosition - 1)) ||
                (w->abacus.decks[BOTTOM].piecePercent != 0 && (p + 1 == w->abacus.decimalPosition -
            w->abacus.shiftPercent - 1 - ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0))))
                  SubBead(w, 1 + (-1 - digit) / base, p + 2);
            else
                  SubBead(w, 1 + (-1 - digit) / base, p + 1);
      }
}

static void
MoveUp(AbacusWidget w, const int deck, const int rail, const int j,
            const int factor, const int spaces,
            const int fast, const int delay)
{
      if (j > MAXBASE) {
            DISPLAY_WARNING("corruption (MoveUp)");
            return;
      }
      if (j > w->abacus.decks[deck].position[rail] + spaces) {
            int temp = w->abacus.decks[deck].position[rail];

            if (fast == INSTANT || delay == 0) {
                  int l;

                  FLUSH(w);
                  initTimer(w->abacus.oldTime);
                  for (l = 0; l < spaces; l++) {
                        int k;

                        for (k = temp + spaces + 1; k <= j; k++) {
                              DrawBead(w, deck, rail,
                                    k - spaces, k - l,
                                    False, False, FALSE, 0, 0);
                              DrawBead(w, deck, rail,
                                    k - spaces, k - l - 1,
                                    True, False, FALSE, 0, 0);
                        }
                        if (l + 1 != spaces) {
                              FLUSH(w);
                              useTimer(&(w->abacus.oldTime), delay);
                        }
                  }
            } else {
                  AnimateSlide(w, deck, rail, j - spaces, j, spaces, UP,
                        delay / w->abacus.numSlices);
            }
#ifdef USE_SOUND
            if (w->abacus.sound) {
                  playSound((char *) BUMPSOUND);
            }
#endif
            w->abacus.decks[deck].position[rail] = j - spaces;
            if (w->abacus.decks[deck].orientation) {
                  SubBead(w, factor * (j - spaces - temp), rail);
                  SetCounter(w, deck, rail, -(j - spaces - temp));
            } else {    /* w->abacus.decks[deck].orientation == DOWN */
                  AddBead(w, factor * (j - spaces - temp), rail);
                  SetCounter(w, deck, rail, j - spaces - temp);
            }
      }
}

static void
MoveDown(AbacusWidget w, const int deck, const int rail, const int j,
            const int factor, const int spaces,
            const int fast, const int delay)
{
      if (-j > MAXBASE) {
            DISPLAY_WARNING("corruption (MoveDown)");
            return;
      }
      if (j <= w->abacus.decks[deck].position[rail]) {
            int temp = w->abacus.decks[deck].position[rail];

            if (fast == INSTANT || delay == 0) {
                  int l;

                  FLUSH(w);
                  initTimer(w->abacus.oldTime);
                  for (l = 0; l < spaces; l++) {
                        int k;

                        for (k = temp; k >= j; k--) {
                              DrawBead(w, deck, rail, k, k + l,
                                    False, False, FALSE, 0, 0);
                              DrawBead(w, deck, rail, k, k + l + 1,
                                    True, False, FALSE, 0, 0);
                        }
                        if (l + 1 != spaces) {
                              FLUSH(w);
                              useTimer(&(w->abacus.oldTime), delay);
                        }
                  }
            } else {
                  AnimateSlide(w, deck, rail, j, j, spaces, DOWN,
                        delay / w->abacus.numSlices);
            }
#ifdef USE_SOUND
            if (w->abacus.sound) {
                  playSound((char *) BUMPSOUND);
            }
#endif
            w->abacus.decks[deck].position[rail] = j - 1;
            if (w->abacus.decks[deck].orientation) {
                  AddBead(w, factor * (temp - j + 1), rail);
                  SetCounter(w, deck, rail, temp - j + 1);
            } else {    /* w->abacus.decks[deck].orientation == DOWN */
                  SubBead(w, factor * (temp - j + 1), rail);
                  SetCounter(w, deck, rail, -(temp - j + 1));
            }
      }
}

static void
MoveBeadsUp(AbacusWidget w, const int deck, const int rail, const int j,
            const Boolean fast)
{
      int factor = 1, pieces, piecePercents, spaces;

#if 0
      (void) printf("MoveBeadsUp: deck %d, rail %d, j %d\n",
                  deck, rail, j);
#endif
      if (w->abacus.sign && (rail == w->abacus.rails - 1)) {
            factor = 0;
            spaces = w->abacus.decks[deck].room - 1;
            MoveUp(w, deck, rail, j, factor, spaces, NORMAL,
                  (fast) ? 0 : w->abacus.delay *
                  w->abacus.decks[BOTTOM].spaces /
                  (w->abacus.decks[BOTTOM].room - 1));
      } else if (w->abacus.decks[BOTTOM].piece != 0 &&
                  (rail == w->abacus.decimalPosition - 1)) {
            pieces = numberPieces(w, deck);
            if (deck == TOP)
                  factor *= w->abacus.decks[BOTTOM].piece;
            spaces = w->abacus.decks[deck].room - pieces;
            MoveUp(w, deck, rail, j, factor, spaces, NORMAL,
                  (fast) ? 0 : w->abacus.delay *
                  w->abacus.decks[deck].spaces /
                  (w->abacus.decks[deck].room - pieces));
      } else if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                  (rail == w->abacus.decimalPosition -
                  w->abacus.shiftPercent - 1 -
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0))) {
            piecePercents = numberPiecePercents(w, deck);
            if (deck == TOP)
                  factor *= w->abacus.decks[BOTTOM].piecePercent;
            spaces = w->abacus.decks[deck].room - piecePercents;
            MoveUp(w, deck, rail, j, factor, spaces, NORMAL,
                  (fast) ? 0 : w->abacus.delay *
                  w->abacus.decks[deck].spaces /
                  (w->abacus.decks[deck].room - piecePercents));
      } else if (j > w->abacus.decks[deck].position[rail] +
                  w->abacus.decks[deck].spaces) {
            factor = w->abacus.decks[deck].factor;
            spaces = w->abacus.decks[deck].spaces;
            MoveUp(w, deck, rail, j, factor, spaces, NORMAL,
                  (fast) ? 0 : w->abacus.delay);
      }
}

static void
MoveBeadsDown(AbacusWidget w, const int deck, const int rail, const int j,
            const Boolean fast)
{
      int factor = 1, pieces, piecePercents, spaces;

#if 0
      (void) printf("MoveBeadsDown: deck %d, rail %d, j %d\n",
                  deck, rail, j);
#endif
      if ((rail == w->abacus.rails - 1) && w->abacus.sign) {
            factor = 0;
            spaces = w->abacus.decks[deck].room - 1;
            MoveDown(w, deck, rail, j, factor, spaces, NORMAL,
                  (fast) ? 0 : w->abacus.delay *
                  w->abacus.decks[BOTTOM].spaces /
                  (w->abacus.decks[BOTTOM].room - 1));
      } else if (w->abacus.decks[BOTTOM].piece != 0 &&
                  (rail == w->abacus.decimalPosition - 1)) {
            pieces = numberPieces(w, deck);
            if (deck == TOP)
                  factor *= w->abacus.decks[BOTTOM].piece;
            spaces = w->abacus.decks[deck].room - pieces;
            MoveDown(w, deck, rail, j, factor, spaces, NORMAL,
                  (fast) ? 0 : w->abacus.delay *
                  w->abacus.decks[deck].spaces /
                  (w->abacus.decks[deck].room - pieces));
      } else if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                  (rail == w->abacus.decimalPosition -
                  w->abacus.shiftPercent - 1 -
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0))) {
            piecePercents = numberPiecePercents(w, deck);
            if (deck == TOP)
                  factor *= w->abacus.decks[BOTTOM].piecePercent;
            spaces = w->abacus.decks[deck].room - piecePercents;
            MoveDown(w, deck, rail, j, factor, spaces, NORMAL,
                  (fast) ? 0 : w->abacus.delay *
                  w->abacus.decks[deck].spaces /
                  (w->abacus.decks[deck].room - piecePercents));
      } else if (j <= w->abacus.decks[deck].position[rail]) {
            factor = w->abacus.decks[deck].factor;
            spaces = w->abacus.decks[deck].spaces;
            MoveDown(w, deck, rail, j, factor, spaces, NORMAL,
                  (fast) ? 0 : w->abacus.delay);
      }
}

static Boolean
PositionToBead(AbacusWidget w, int x, int y, int *deck, int *rail, int *j)
{
      int pieces, piecePercents;

      if (w->abacus.vertical) {
            int temp = x;

            x = y;
            y = w->abacus.frameSize.y - 1 - temp;
      }
      x -= w->abacus.offset.x;
      y -= w->abacus.offset.y;
      if (y > w->abacus.decks[TOP].height) {
            y = y - w->abacus.decks[TOP].height;
            *deck = BOTTOM;
      } else {
            *deck = TOP;
      }
      if (w->abacus.decks[*deck].number == 0) {
            return False;
      }
      *rail = w->abacus.rails - 1 - (x - w->abacus.delta.x / 2) /
            w->abacus.pos.x;
      *j = (y - w->abacus.delta.y / 2) / w->abacus.pos.y + 1;
      if (*rail < 0)
            *rail = 0;
      else if (*rail >= w->abacus.rails)
            *rail = w->abacus.rails - 1;
      if (*j < 1)
            *j = 1;
      else if (*j > w->abacus.decks[*deck].room)
            *j = w->abacus.decks[*deck].room;
      if (*rail == w->abacus.rails - 1 && w->abacus.sign) {
            if (*deck == TOP)
                  return False;
            return ((*j == 1 &&
                   w->abacus.decks[*deck].position[*rail] == 1) ||
                  (*j == w->abacus.decks[*deck].room &&
                   w->abacus.decks[*deck].position[*rail] == 0));
      }
      if (w->abacus.decks[BOTTOM].piece != 0 &&
                  (*rail == w->abacus.decimalPosition - 1)) {
            if (*deck == TOP) {
                  pieces = numberPieces(w, TOP);
                  if (pieces == 0)
                        return False;
                  return ((*j > w->abacus.decks[*deck].position[*rail] +
                        w->abacus.decks[*deck].room - pieces) ||
                        (*j <= w->abacus.decks[*deck].position[*rail]));
            }
            pieces = numberPieces(w, BOTTOM);
            return ((*j > w->abacus.decks[*deck].position[*rail] +
                  w->abacus.decks[*deck].room - pieces) ||
                  (*j <= w->abacus.decks[*deck].position[*rail]));
      }
      if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                  (*rail == w->abacus.decimalPosition -
                  w->abacus.shiftPercent - 1 -
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0))) {
            if (*deck == TOP) {
                  piecePercents = numberPiecePercents(w, TOP);
                  if (piecePercents == 0)
                        return False;
                  return ((*j > w->abacus.decks[*deck].position[*rail] +
                        w->abacus.decks[*deck].room - piecePercents) ||
                        (*j <= w->abacus.decks[*deck].position[*rail]));
            }
            piecePercents = numberPiecePercents(w, BOTTOM);
            return ((*j > w->abacus.decks[*deck].position[*rail] +
                  w->abacus.decks[*deck].room - piecePercents) ||
                  (*j <= w->abacus.decks[*deck].position[*rail]));
      }
      return ((*j > w->abacus.decks[*deck].position[*rail] +
             w->abacus.decks[*deck].spaces) ||
            (*j <= w->abacus.decks[*deck].position[*rail]));
}

static void
MoveBeadsByPos(AbacusWidget w, const int deck, const int rail, const int pos,
            const Boolean fast)
{
      if (w->abacus.sign && (rail == w->abacus.rails - 1)) {
            if (deck == TOP)
                  return;
      }
      if (w->abacus.decks[BOTTOM].piece != 0 &&
                  (rail == w->abacus.decimalPosition - 1)) {
            if (deck == TOP && (w->abacus.decks[TOP].piece == 0 ||
                        w->abacus.decks[TOP].number == 0))
                  return;
      }
      if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                  (rail == w->abacus.decimalPosition -
                  w->abacus.shiftPercent - 1 -
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0))) {
            if (deck == TOP && (w->abacus.decks[TOP].piecePercent == 0 ||
                        w->abacus.decks[TOP].number == 0))
                  return;
      }
      if (pos <= w->abacus.decks[deck].position[rail]) {
            MoveBeadsDown(w, deck, rail, pos, fast);
      } else {
            MoveBeadsUp(w, deck, rail, pos, fast);
      }
}

static void
ShiftBar(AbacusWidget w, int decimalPosition)
{
      int rail;
      int pieces[MAXDECKS], piecePercents[MAXDECKS];
      int pieceRail = w->abacus.decimalPosition - 1;
      int piecePercentRail = w->abacus.decimalPosition -
            w->abacus.shiftPercent - 1 -
            ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0);
      int oldPieceRail = decimalPosition - 1;
      int oldPiecePercentRail = decimalPosition -
            w->abacus.shiftPercent - 1 -
            ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0);
      char q;

      pieces[TOP] = 0;
      pieces[BOTTOM] = 0;
      piecePercents[TOP] = 0;
      piecePercents[BOTTOM] = 0;
      if (w->abacus.decks[BOTTOM].piece != 0) {
            pieces[BOTTOM] =
                  w->abacus.decks[BOTTOM].position[oldPieceRail];
            if (w->abacus.decks[TOP].piece != 0 ||
                        w->abacus.decks[TOP].number == 0)
                  pieces[TOP] =
                        w->abacus.decks[TOP].position[oldPieceRail];
      }
      if (w->abacus.decks[BOTTOM].piecePercent != 0) {
            piecePercents[BOTTOM] =
                  w->abacus.decks[BOTTOM].position[oldPiecePercentRail];
            if (w->abacus.decks[TOP].piecePercent != 0 ||
                        w->abacus.decks[TOP].number == 0)
                  piecePercents[TOP] =
                        w->abacus.decks[TOP].position[oldPiecePercentRail];
      }
      /* shift around */
      if (w->abacus.decimalPosition > decimalPosition) {
            for (rail = oldPieceRail; rail < pieceRail; rail++) {
                  w->abacus.decks[BOTTOM].position[rail] =
                        w->abacus.decks[BOTTOM].position[rail + 1];
                  w->abacus.decks[TOP].position[rail] =
                        w->abacus.decks[TOP].position[rail + 1];
                  if (w->abacus.decks[BOTTOM].piecePercent != 0) {
                        w->abacus.decks[BOTTOM].position[rail - w->abacus.shiftPercent - 1] =
                              w->abacus.decks[BOTTOM].position[rail - w->abacus.shiftPercent];
                        w->abacus.decks[TOP].position[rail - w->abacus.shiftPercent - 1] =
                              w->abacus.decks[TOP].position[rail - w->abacus.shiftPercent];
                  }
            }
      } else if (w->abacus.decimalPosition < decimalPosition) {
            for (rail = oldPieceRail; rail > pieceRail; rail--) {
                  w->abacus.decks[BOTTOM].position[rail] =
                        w->abacus.decks[BOTTOM].position[rail - 1];
                  w->abacus.decks[TOP].position[rail] =
                        w->abacus.decks[TOP].position[rail - 1];
                  if (w->abacus.decks[BOTTOM].piecePercent != 0) {
                        w->abacus.decks[BOTTOM].position[rail - w->abacus.shiftPercent - 1] =
                              w->abacus.decks[BOTTOM].position[rail - w->abacus.shiftPercent - 2];
                        w->abacus.decks[TOP].position[rail - w->abacus.shiftPercent - 1] =
                              w->abacus.decks[TOP].position[rail - w->abacus.shiftPercent - 2];
                  }
            }
      }
      if (w->abacus.decks[BOTTOM].piece != 0) {
            w->abacus.decks[BOTTOM].position[pieceRail] = pieces[BOTTOM];
            if (w->abacus.decks[TOP].piece != 0 &&
                        w->abacus.decks[TOP].number != 0)
                  w->abacus.decks[TOP].position[pieceRail] = pieces[TOP];
            else
                  w->abacus.decks[TOP].position[pieceRail] = 0;
      }
      if (w->abacus.decks[BOTTOM].piecePercent != 0) {
            w->abacus.decks[BOTTOM].position[piecePercentRail] =
                  piecePercents[BOTTOM];
            if (w->abacus.decks[TOP].piecePercent != 0 &&
                        w->abacus.decks[TOP].number != 0)
                  w->abacus.decks[TOP].position[piecePercentRail] =
                        piecePercents[TOP];
            else
                  w->abacus.decks[TOP].position[piecePercentRail] = 0;
      }
      q = w->abacus.digits[w->abacus.rails + CARRY - decimalPosition];
      if (w->abacus.decimalPosition > decimalPosition)
            for (rail = w->abacus.rails + CARRY - decimalPosition;
                        rail > w->abacus.rails + CARRY -
                        w->abacus.decimalPosition; rail--)
                  w->abacus.digits[rail] = w->abacus.digits[rail - 1];
      else if (w->abacus.decimalPosition < decimalPosition)
            for (rail = w->abacus.rails + CARRY - decimalPosition;
                        rail < w->abacus.rails + CARRY -
                        w->abacus.decimalPosition; rail++)
                  w->abacus.digits[rail] = w->abacus.digits[rail + 1];
      w->abacus.digits[w->abacus.rails + CARRY -
            w->abacus.decimalPosition] = q;
}

static void
ClearAllBeads(AbacusWidget w)
{
      int rail, deck;

      for (rail = 0; rail < w->abacus.rails; rail++) {
            for (deck = BOTTOM; deck <= TOP; deck++) {
                  if (w->abacus.sign && (rail == w->abacus.rails - 1)) {
                        if (deck == TOP)
                              continue;
                  }
                  if (w->abacus.decks[BOTTOM].piece != 0 &&
                              (rail == w->abacus.decimalPosition - 1)) {
                        if (deck == TOP &&
                                    (w->abacus.decks[TOP].number == 0 ||
                                    w->abacus.decks[TOP].piece == 0))
                              continue;
                  }
                  if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                              (rail == w->abacus.decimalPosition -
                              w->abacus.shiftPercent - 1 -
                              ((w->abacus.decks[BOTTOM].piece != 0) ?
                              1 : 0))) {
                        if (deck == TOP &&
                                    (w->abacus.decks[TOP].number == 0 ||
                                    w->abacus.decks[TOP].piecePercent == 0))
                              continue;
                  }
                  if (w->abacus.decks[deck].orientation)
                        MoveBeadsUp(w, deck, rail,
                              w->abacus.decks[deck].room, True);
                  else  /* w->abacus.decks[deck].orientation == DOWN */
                        MoveBeadsDown(w, deck, rail, 1, True);
            }
      }
}

static void
DrawAllBeads(AbacusWidget w)
{
      int deck, rail, j, spaces;

      if (w->abacus.sign) {
            deck = BOTTOM;
            rail = w->abacus.rails - 1;
            DrawBead(w, deck, rail, 1, 1,
                  (1 == w->abacus.decks[deck].position[rail]),
                  False, FALSE, 0, 0);
            for (j = 2; j < w->abacus.decks[deck].room; j++)
                  DrawBead(w, deck, rail, 0, j, False,
                        False, FALSE, 0, 0);
            DrawBead(w, deck, rail, 1, w->abacus.decks[deck].room,
                  (0 == w->abacus.decks[deck].position[rail]),
                  False, FALSE, 0, 0);
      }
      if (w->abacus.decks[BOTTOM].piece != 0) {
            int pieces = 0;

            for (deck = BOTTOM; deck <= TOP; deck++) {
                  rail = w->abacus.decimalPosition - 1;
                  pieces = numberPieces(w, deck);
                  if (pieces == 0)
                        continue;
                  spaces = w->abacus.decks[deck].room - pieces;
                  for (j = 1; j <= w->abacus.decks[deck].position[rail];
                              j++) {
                        DrawBead(w, deck, rail, j, j,
                              True, False, FALSE, 0, 0);
                  }
                  for (j = w->abacus.decks[deck].position[rail] + 1;
                              j < spaces +
                              w->abacus.decks[deck].position[rail] +
                              1; j++) {
                        DrawBead(w, deck, rail, 0, j,
                              False, False, FALSE, 0, 0);
                  }
                  for (j = spaces + w->abacus.decks[deck].position[rail] + 1;
                              j <= w->abacus.decks[deck].room; j++) {

                        DrawBead(w, deck, rail, j - spaces, j,
                              True, False, FALSE, 0, 0);
                  }
            }
      }
      if (w->abacus.decks[BOTTOM].piecePercent != 0) {
            int piecePercents = 0;

            for (deck = BOTTOM; deck <= TOP; deck++) {
                  rail = w->abacus.decimalPosition -
                        w->abacus.shiftPercent - 1 -
                        ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0);
                  piecePercents = numberPiecePercents(w, deck);
                  if (piecePercents == 0)
                        continue;
                  spaces = w->abacus.decks[deck].room - piecePercents;
                  for (j = 1; j <= w->abacus.decks[deck].position[rail]; j++)
                        DrawBead(w, deck, rail, j, j, True, False, FALSE,
                              0, 0);
                  for (j = w->abacus.decks[deck].position[rail] + 1;
                              j < spaces +
                              w->abacus.decks[deck].position[rail] + 1; j++)
                        DrawBead(w, deck, rail, 0, j, False, False, FALSE,
                              0, 0);
                  for (j = spaces + w->abacus.decks[deck].position[rail] + 1;
                       j <= w->abacus.decks[deck].room; j++)
                        DrawBead(w, deck, rail, j - spaces, j,
                              True, False, FALSE, 0, 0);
            }
      }
      for (rail = 0; rail < w->abacus.rails - ((w->abacus.sign) ? 1 : 0);
                  rail++) {
            if ((w->abacus.decks[BOTTOM].piece != 0 &&
                        (rail == w->abacus.decimalPosition - 1)) ||
                        (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                        (rail == w->abacus.decimalPosition -
                        w->abacus.shiftPercent - 1 -
                        ((w->abacus.decks[BOTTOM].piece != 0) ?
                        1 : 0)))) {
                  continue;
            }
            for (deck = BOTTOM; deck <= TOP; deck++) {
                  for (j = 1; j <= w->abacus.decks[deck].position[rail]; j++)
                        DrawBead(w, deck, rail, j, j,
                              True, False, FALSE, 0, 0);
                  for (j = w->abacus.decks[deck].position[rail] + 1;
                       j < w->abacus.decks[deck].spaces + w->abacus.decks[deck].position[rail] + 1; j++)
                        DrawBead(w, deck, rail, 0, j,
                              False, False, FALSE, 0, 0);
                  for (j = w->abacus.decks[deck].spaces + w->abacus.decks[deck].position[rail] + 1;
                       j <= w->abacus.decks[deck].room; j++)
                        DrawBead(w, deck, rail,
                              j - w->abacus.decks[deck].spaces, j,
                              True, False, FALSE, 0, 0);
            }
      }
      SetCounter(w, 0, w->abacus.decimalPosition, 0);
}

static void
SetDecimal(AbacusWidget w, int rail)
{
      int j;

      if (w->abacus.script) {
#ifdef SCRIPTFILE
            (void) fprintf(w->abacus.fp, "%d %d %d %d 4\n", PRIMARY, 2,
                  0, rail - w->abacus.decimalPosition);
            (void) fprintf(w->abacus.fp, "Lesson\n\n\nPress Space-bar\n");
#else
            SetAbacusMove(w, ABACUS_SCRIPT, PRIMARY /* FIXME */, 2,
                  0, rail - w->abacus.decimalPosition);
#endif
      }
      if (rail <= w->abacus.shiftPercent + 1  && w->abacus.decks[BOTTOM].piecePercent != 0) {
            w->abacus.decks[BOTTOM].piecePercent = 0;
            w->abacus.decks[TOP].piecePercent = 0;
            if (rail <= 0 && w->abacus.decks[BOTTOM].piece != 0)
                  rail = 1;
            (void) DeleteSpecialRail(w, False, False, True);
            SetAbacus(w, ABACUS_QUARTERPERCENT);
      } else if (rail <= 0 && w->abacus.decks[BOTTOM].piece != 0) {
            w->abacus.decks[BOTTOM].piece = 0;
            w->abacus.decks[TOP].piece = 0;
            (void) DeleteSpecialRail(w, False, True, False);
            SetAbacus(w, ABACUS_QUARTER);
      }
      if (w->abacus.sign && rail >= w->abacus.rails - 1)
            rail = w->abacus.rails - 2;
      if (w->abacus.decks[BOTTOM].piece != 0 ||
                  w->abacus.decks[BOTTOM].piecePercent != 0) {
            EraseFrame(w, 0);
      }
      DrawFrame(w, 0, False, True);
      j = w->abacus.decimalPosition;
      w->abacus.decimalPosition = rail;
      DrawFrame(w, 0, True, True);
      if (w->abacus.decks[BOTTOM].piece != 0 ||
                  w->abacus.decks[BOTTOM].piecePercent != 0) {
            ShiftBar(w, j);
      }
      DrawAllBeads(w);
      SetCounter(w, 0, w->abacus.decimalPosition, 0);
#ifdef USE_SOUND
      if (w->abacus.sound) {
            playSound((char *) MOVESOUND);
      }
#endif
}

static void
MoveBeadsByValue(AbacusWidget w, const int deck, const int rail,
            const int number, const Boolean fast)
{
      if (deck != BOTTOM && deck != TOP) {
            SetDecimal(w, number + w->abacus.decimalPosition);
            return;
      }
      if (w->abacus.sign && (rail == w->abacus.rails - 1)) {
            if (deck == TOP)
                  return;
            if (number <= w->abacus.decks[deck].position[rail]) {
                  MoveBeadsDown(w, deck, rail, 1, fast);
            } else {
                  MoveBeadsUp(w, deck, rail,
                        w->abacus.decks[BOTTOM].room, fast);
            }
            return;
      } else if ((w->abacus.decks[deck].orientation && number < 0) ||
                  (!w->abacus.decks[deck].orientation && number > 0)) {
            int spaces = w->abacus.decks[deck].spaces;

            if (w->abacus.decks[BOTTOM].piece != 0 &&
                        rail == w->abacus.decimalPosition - 1) {
                  if (w->abacus.decks[BOTTOM].number ==
                              w->abacus.decks[TOP].factor - 1) {
                        spaces = w->abacus.decks[deck].room -
                              w->abacus.decks[BOTTOM].piece + 1;
                  } else {
                        spaces = w->abacus.decks[deck].room -
                              w->abacus.decks[BOTTOM].piece;
                  }
            }
            if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                        rail == w->abacus.decimalPosition - 1 -
                        w->abacus.shiftPercent -
                        ((w->abacus.decks[BOTTOM].piece != 0) ?
                        1 : 0)) {
                  if (w->abacus.decks[BOTTOM].number ==
                              w->abacus.decks[TOP].factor - 1) {
                        spaces = w->abacus.decks[deck].room -
                              w->abacus.decks[BOTTOM].piecePercent +
                              1;
                  } else {
                        spaces = w->abacus.decks[deck].room -
                              w->abacus.decks[BOTTOM].piecePercent;
                  }
            }
            MoveBeadsUp(w, deck, rail, spaces +
                  w->abacus.decks[deck].position[rail] +
                  ((number >= 0) ? number : -number),
                  fast);
      } else if ((!w->abacus.decks[deck].orientation && number < 0) ||
                  (w->abacus.decks[deck].orientation && number > 0)) {
            MoveBeadsDown(w, deck, rail,
                  w->abacus.decks[deck].position[rail] + 1 -
                  ((number >= 0) ? number : -number),
                  fast);
      }
}

static Boolean
SetBeadsForValue(AbacusWidget w, char * expression, Boolean minusSign)
{
      int i, val = -1, topUnits, bottomUnits;
      int percentPosition = w->abacus.decimalPosition -
            w->abacus.shiftPercent -
            ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0);
      int nPieces = 0; int nPiecePercents = 0;

      for (i = 0; i < (int) strlen(expression) - CARRY; i++) {
            char a = expression[i + CARRY];

            val = char2Int(a);
            if (w->abacus.decks[BOTTOM].piece != 0 &&
                        w->abacus.rails - i !=
                        w->abacus.decimalPosition) {
                  nPieces = w->abacus.decks[BOTTOM].piece;
                  if (w->abacus.decks[TOP].piece)
                        nPieces *= w->abacus.decks[TOP].piece;
            } else if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                        w->abacus.rails - i != percentPosition) {
                  nPiecePercents = w->abacus.decks[BOTTOM].piecePercent;
                  if (w->abacus.decks[TOP].piecePercent)
                        nPiecePercents *= w->abacus.decks[TOP].piecePercent;
            }
            if (w->abacus.decks[BOTTOM].piece != 0 &&
                        w->abacus.rails - i ==
                        w->abacus.decimalPosition) {
                  if (val >= nPieces)
                        val -= nPieces;
                  if (w->abacus.decks[TOP].number == 0) {
                        bottomUnits = val % nPieces;
                        MoveBeadsByValue(w, BOTTOM,
                              w->abacus.rails - i - 1,
                              bottomUnits, True);
                  } else {
                        topUnits = val / w->abacus.decks[BOTTOM].piece;
                        bottomUnits = val % w->abacus.decks[BOTTOM].piece;
                        if (topUnits > w->abacus.decks[TOP].piece) {
                              return False;
                        }
                        MoveBeadsByValue(w, TOP,
                              w->abacus.rails - i - 1,
                              topUnits, True);
                        MoveBeadsByValue(w, BOTTOM,
                              w->abacus.rails - i - 1,
                              bottomUnits, True);
                  }
            } else if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                  w->abacus.rails - i == percentPosition) {
                  if (val >= nPiecePercents)
                        val -= nPiecePercents;
                  if (w->abacus.decks[TOP].number == 0) {
                        bottomUnits = val % nPiecePercents;
                        MoveBeadsByValue(w, BOTTOM,
                              w->abacus.rails - i - 1,
                              bottomUnits, True);
                  } else {
                        topUnits = val / w->abacus.decks[BOTTOM].piecePercent;
                        bottomUnits = val % w->abacus.decks[BOTTOM].piecePercent;
                        if (topUnits > w->abacus.decks[TOP].piecePercent) {
                              return False;
                        }
                        MoveBeadsByValue(w, TOP, w->abacus.rails - i - 1,
                              topUnits, True);
                        MoveBeadsByValue(w, BOTTOM, w->abacus.rails - i - 1,
                              bottomUnits, True);
                  }
            } else {
                  topUnits = val / w->abacus.decks[TOP].factor;
                  bottomUnits = (val % w->abacus.decks[TOP].factor) /
                        w->abacus.decks[BOTTOM].factor;
                  if (topUnits > w->abacus.decks[TOP].number) {
                        return False;
                  }
                  MoveBeadsByValue(w, TOP, w->abacus.rails - i - 1,
                        topUnits, True);
                  MoveBeadsByValue(w, BOTTOM, w->abacus.rails - i - 1,
                        bottomUnits, True);
            }
      }
      if (w->abacus.sign) {
            MoveBeadsByValue(w, BOTTOM, w->abacus.rails - 1,
                  (w->abacus.decks[BOTTOM].orientation) ?
                  ((minusSign) ? 0 : w->abacus.decks[BOTTOM].room -
                  w->abacus.decks[BOTTOM].spaces) :
                  ((minusSign) ? w->abacus.decks[BOTTOM].room -
                  w->abacus.decks[BOTTOM].spaces: 0),
                  True);
      }
#if 0
      /* These are already in expression, so do not do again */
      MoveBeadsByValue(w, BOTTOM, w->abacus.decimalPosition - 1,
            pieces, True);
      MoveBeadsByValue(w, BOTTOM, w->abacus.decimalPosition -
            w->abacus.shiftPercent - 1 - ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0),
            piecePercents, True);
#endif
      return True;
}

void
ClearRails(AbacusWidget w)
{
      ClearAllBeads(w);
      if (w->abacus.demo) {
            SetAbacus(w, ABACUS_CLEAR);
      }
      if (!EmptyCounter(w)) {
            DISPLAY_WARNING("corruption (ClearRails)");
      }
      SetCounter(w, 0, w->abacus.decimalPosition, 0); /* needed when 0 */
}

static void
CheckDecimal(AbacusWidget w)
{
      if (w->abacus.decimalPosition >= w->abacus.rails -
                  ((w->abacus.sign) ? 1 : 0) +
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0) +
                  ((w->abacus.decks[BOTTOM].piecePercent != 0) ? 1 : 0)) {
            DrawFrame(w, 0, False, True);
            w->abacus.decimalPosition = w->abacus.rails - 1 -
                  ((w->abacus.sign) ? 1 : 0) +
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0) +
                  ((w->abacus.decks[BOTTOM].piecePercent != 0) ? 1 : 0);
            DrawFrame(w, 0, True, True);
            SetCounter(w, 0, w->abacus.decimalPosition, 0);
      }
}

/* via increment/decrement or by sign and pieces */
static void
ShiftRails(AbacusWidget w, int oldRails, int newRails,
            Boolean sign, Boolean piece, Boolean piecePercent)
{
      AbacusPart old;
      int deck, rail, offset;
      int signPosition;
      int piecePosition[MAXDECKS], piecePercentPosition[MAXDECKS];
      int pieceNumber[MAXDECKS], piecePercentNumber[MAXDECKS];
      int shift = newRails - oldRails;
      int decimalShift = 0;

      signPosition = 0; 
      piecePosition[TOP] = piecePosition[BOTTOM] = 0;
      piecePercentPosition[TOP] = piecePercentPosition[BOTTOM] = 0;
      pieceNumber[BOTTOM] = numberPieces(w, BOTTOM);
      pieceNumber[TOP] = numberPieces(w, TOP);
      piecePercentNumber[BOTTOM] = numberPiecePercents(w, BOTTOM);
      piecePercentNumber[TOP] = numberPiecePercents(w, TOP);
      old.sign = w->abacus.sign;
      old.decks[TOP].piece = w->abacus.decks[TOP].piece;
      old.decks[BOTTOM].piece = w->abacus.decks[BOTTOM].piece;
      old.decks[TOP].piecePercent = w->abacus.decks[TOP].piecePercent;
      old.decks[BOTTOM].piecePercent = w->abacus.decks[BOTTOM].piecePercent;
      deck = BOTTOM;
      /* special items added already, 2 is a dummy value */
      if (sign)
            old.sign = !w->abacus.sign;
      if (piece) {
            old.decks[BOTTOM].piece =
                  (w->abacus.decks[BOTTOM].piece != 0) ? 0 : 2;
      }
      if (piecePercent) {
            old.decks[BOTTOM].piecePercent =
                  (w->abacus.decks[BOTTOM].piecePercent != 0) ? 0 : 2; 
      }
      /* Save sign, this will be erased */
      if (old.sign && !sign)
            signPosition = ((w->abacus.decks[deck].orientation &&
                  w->abacus.decks[deck].position[oldRails - 1] == 0) || 
                  (!w->abacus.decks[deck].orientation &&
                  w->abacus.decks[deck].position[oldRails - 1] != 0)); 
      old.rails = oldRails + ((shift > 0) ? shift : 0);
      old.decimalPosition = w->abacus.decimalPosition;
      for (deck = BOTTOM; deck <= TOP; deck++) {
            /* Alloc space to save the rails */
            if (!(old.decks[deck].position = (int *)
                        calloc((unsigned int) (old.rails),
                        sizeof (int)))) {
                  DISPLAY_ERROR("Not enough memory, exiting.");
            }
            /* initialization could be wrong if oriented from top */
            /* current pieces will be initialized later */
            if (w->abacus.decks[deck].orientation) {
                  for (rail = 0; rail < old.rails; rail++) {
                        old.decks[deck].position[rail] =
                              w->abacus.decks[deck].number;
                  }
                  if (old.decks[BOTTOM].piece == 0 && piece)
                        piecePosition[deck] = pieceNumber[deck];
                  if (old.decks[BOTTOM].piecePercent == 0 &&
                              piecePercent)
                        piecePercentPosition[deck] =
                              piecePercentNumber[deck];
            }
      }
      /* initialization from old */
      for (deck = BOTTOM; deck <= TOP; deck++) {
            offset = 0;
            if (old.decks[BOTTOM].piece != 0 && !piece)
                  offset--;
            if (old.decks[BOTTOM].piecePercent != 0 && !piecePercent)
                  offset--;
            for (rail = oldRails - ((old.sign) ? 1 : 0) - 1; rail >= 0; rail--) {
                  if (old.decks[BOTTOM].piece != 0 &&
                              rail == w->abacus.decimalPosition - 1) {
                        if (old.decks[deck].piece != 0 && !piece)
                              piecePosition[deck] =
                                    w->abacus.decks[deck].position[rail];
                        offset++;
                  } else if (old.decks[BOTTOM].piecePercent != 0 &&
                              rail == w->abacus.decimalPosition -
                              w->abacus.shiftPercent - 1 -
                              ((old.decks[BOTTOM].piece != 0) ? 1 : 0)) {
                        if (old.decks[deck].piecePercent != 0 &&
                                    !piecePercent)
                              piecePercentPosition[deck] =
                                    w->abacus.decks[deck].position[rail];
                        offset++;
                  } else {
                        old.decks[deck].position[rail + offset] =
                              w->abacus.decks[deck].position[rail];
                  }
            }
      }
      w->abacus.rails = newRails;
#ifdef DEBUG
      for (deck = TOP; deck >= BOTTOM; deck--) {
            for (rail = old.rails - 1; rail >= 0; rail--) {
                  (void) printf("%c",
                        int2Char(old.decks[deck].position[rail]));
            }
            (void) printf(":%d,p%d,pp%d\n", old.rails,
                  piecePosition[deck], piecePercentPosition[deck]);
      }
      (void) printf("ShiftRails decimalPosition w%d, rails w%d, shift%d\n",
            w->abacus.decimalPosition, w->abacus.rails, shift);
#endif
      if (w->abacus.decimalPosition > w->abacus.rails - 1 -
                  ((w->abacus.sign) ? 1 : 0)) {
            decimalShift = w->abacus.decimalPosition;
            w->abacus.decimalPosition = w->abacus.rails - 1 -
                  ((w->abacus.sign) ? 1 : 0);
            decimalShift -= w->abacus.decimalPosition;
      } else if (w->abacus.decimalPosition < ((w->abacus.sign) ? 1 : 0)) {
            w->abacus.decimalPosition = 0;
      }
      offset = 0;
      if (piece && old.decks[BOTTOM].piece == 0) {
            w->abacus.decimalPosition++;
      }
      if (piece && old.decks[BOTTOM].piece != 0) {
            offset--;
            w->abacus.decimalPosition--;
      }
      if (piecePercent && old.decks[BOTTOM].piecePercent == 0) {
            w->abacus.decimalPosition++;
      }
      if (piecePercent && old.decks[BOTTOM].piecePercent != 0) {
            offset--;
            w->abacus.decimalPosition--;
      }
#ifdef DEBUG
      (void) printf("ShiftRails decimalPosition w%d, rails w%d, shift%d, offset%d\n",
            w->abacus.decimalPosition, w->abacus.rails, shift, offset);
#endif
      ResetBeads(w);
      ResizeAbacus(w);
      ResizeBead(w);
      DrawAllBufferedBeads(w);
      EraseFrame(w, 0);
      DrawFrame(w, 0, True, True);
      DrawAllBeads(w);
      for (deck = BOTTOM; deck <= TOP; deck++) {
            int localOffset = offset;

            for (rail = 0; rail < w->abacus.rails + ((offset < 0) ? -offset : 0) -
                        (((old.sign && !sign) || (!old.sign && sign)) ? 1 : 0);
                        rail++) {
                  if (w->abacus.decks[BOTTOM].piece != 0 &&
                              rail + localOffset == w->abacus.decimalPosition - 1) {
                        if (deck != TOP || pieceNumber[TOP] != 0) {
                              if (w->abacus.decks[deck].orientation) {
                                    MoveBeadsByValue(w, deck, rail + localOffset,
                                          pieceNumber[deck] -
                                          piecePosition[deck], True);
                              } else {
                                    MoveBeadsByValue(w, deck, rail + localOffset,
                                          piecePosition[deck], True);
                              }
                        }
                        localOffset++;
                  } else if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                              rail + localOffset == w->abacus.decimalPosition -
                              w->abacus.shiftPercent - 1 -
                              ((w->abacus.decks[BOTTOM].piece != 0) ?
                              1 : 0)) {
                        if (deck != TOP || piecePercentNumber[TOP] != 0) {
                              if (w->abacus.decks[deck].orientation) {
                                    MoveBeadsByValue(w, deck, rail + localOffset,
                                          piecePercentNumber[deck] -
                                          piecePercentPosition[deck], True);
                              } else {
                                    MoveBeadsByValue(w, deck, rail + localOffset,
                                          piecePercentPosition[deck], True);
                              }
                        }
                        localOffset++;
                  }
                  if (w->abacus.decks[deck].orientation)
                        MoveBeadsByValue(w, deck, rail + localOffset,
                              w->abacus.decks[deck].number -
                              old.decks[deck].position[rail + decimalShift],
                              True);
                  else
                        MoveBeadsByValue(w, deck, rail + localOffset,
                              old.decks[deck].position[rail + decimalShift],
                              True);
            }
      }
      if ((old.sign && !sign) || (!old.sign && sign)) {
            deck = BOTTOM;
            rail = w->abacus.rails - 1;
            if (w->abacus.decks[deck].orientation) {
                  MoveBeadsByPos(w, deck, rail,
                        (signPosition != 0) ?
                        1 : w->abacus.decks[deck].room,
                        True);
            } else {
                  MoveBeadsByPos(w, deck, rail,
                        (signPosition != 0) ?
                        w->abacus.decks[deck].room : 1,
                        True);
            }
      }
#ifdef DEBUG
      for (deck = TOP; deck >= BOTTOM; deck--) {
            for (rail = w->abacus.rails - 1; rail >= 0; rail--) {
                  (void) printf("%c",
                        int2Char(w->abacus.decks[deck].position[rail]));
            }
            (void) printf(":%d,p%d,pp%d\n", w->abacus.rails,
                  piecePosition[deck], piecePercentPosition[deck]);
            if (deck == TOP) {
                  for (rail = w->abacus.rails - 1; rail >= 0; rail--) {
                        if (w->abacus.decimalPosition == rail)
                              (void) printf(".");
                        else if ((rail > w->abacus.decimalPosition) &&
                                    ((w->abacus.decimalPosition - rail) % 3 == 0))
                              (void) printf(",");
                        else if ((w->abacus.decks[BOTTOM].piece != 0) &&
                                    (rail == w->abacus.decimalPosition - 1))
                              (void) printf("O");
                        else if ((w->abacus.decks[BOTTOM].piecePercent != 0) &&
                                    (rail == w->abacus.decimalPosition -
                                    w->abacus.shiftPercent - 1 -
                                    ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0)))
                              (void) printf("O");
                        else
                              (void) printf(" ");
                  }
                  (void) printf("\n");
            }
      }
#endif
      for (deck = BOTTOM; deck <= TOP; deck++) {
            if (old.decks[deck].position)
                  free(old.decks[deck].position);
      }
}

static Boolean
InsertSpecialRail(AbacusWidget w,
            Boolean sign, Boolean piece, Boolean piecePercent)
{
      int minRails = ((w->abacus.demo) ? MINDEMORAILS : MINRAILS);

      if (w->abacus.rails + 2 <= minRails + ((w->abacus.sign) ? 1 : 0) +
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0) +
                  ((w->abacus.decks[BOTTOM].piecePercent != 0) ?
                  w->abacus.shiftPercent + 1 : 0))
            return False;
      ShiftRails(w, w->abacus.rails, w->abacus.rails + 1,
            sign, piece, piecePercent);
      SetAbacus(w, ABACUS_INC);
      return True;
}

static Boolean
DeleteSpecialRail(AbacusWidget w,
            Boolean sign, Boolean piece, Boolean piecePercent)
{
      int minRails = ((w->abacus.demo) ? MINDEMORAILS : MINRAILS);

      if (w->abacus.rails <= minRails + ((w->abacus.sign) ? 1 : 0) +
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0) +
                  ((w->abacus.decks[BOTTOM].piecePercent != 0) ?
                  w->abacus.shiftPercent + 1 : 0))
            return False;
      ShiftRails(w, w->abacus.rails, w->abacus.rails - 1,
            sign, piece, piecePercent);
      SetAbacus(w, ABACUS_DEC);
      CheckDecimal(w);
      return True;
}

static void
IncrementRails(AbacusWidget w)
{
      ShiftRails(w, w->abacus.rails, w->abacus.rails + 1,
            False, False, False);
      SetAbacus(w, ABACUS_INC);
}

static void
DecrementRails(AbacusWidget w)
{
      int minRails = ((w->abacus.demo) ? MINDEMORAILS : MINRAILS);

      if (w->abacus.rails <= minRails + ((w->abacus.sign) ? 1 : 0) +
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0) +
                  ((w->abacus.decks[BOTTOM].piecePercent != 0) ?
                  w->abacus.shiftPercent + 1 : 0))
            return;
      ShiftRails(w, w->abacus.rails, w->abacus.rails - 1,
            False, False, False);
      SetAbacus(w, ABACUS_DEC);
      CheckDecimal(w);
}

static void
SpeedBead(AbacusWidget w)
{
      w->abacus.delay -= 10;
      if (w->abacus.delay < 0)
            w->abacus.delay = 0;
}

static void
SlowBead(AbacusWidget w)
{
      w->abacus.delay += 10;
}

static void
SoundBead(AbacusWidget w)
{
      w->abacus.sound = !w->abacus.sound;
}

static void
ReformatRails(AbacusWidget w)
{
      char * buffer;
      Boolean minusSign = False;
      Boolean orientBottom = w->abacus.decks[BOTTOM].orientation;
      int value;

      if (w->abacus.sign) {
            value = w->abacus.decks[BOTTOM].position[w->abacus.rails - 1];
            minusSign = ((value == 1 && !orientBottom) ||
                  (value == 0 && orientBottom));
      }
      if (!(buffer = (char *) malloc(sizeof (char) *
                  (strlen(w->abacus.digits) + 1)))) {
            DISPLAY_ERROR("Not enough memory, exiting.");
      }
      (void) strcpy(buffer, w->abacus.digits);
      ResetBeads(w);
      ResizeAbacus(w);
      ResizeBead(w);
      DrawAllBufferedBeads(w);
      EraseFrame(w, 0);
      DrawFrame(w, 0, True, True);
      DrawAllBeads(w);
      if (w->abacus.decks[BOTTOM].piecePercent != 0 &&
                  w->abacus.decimalPosition - w->abacus.shiftPercent -
                  ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0) < 1)
            w->abacus.decks[BOTTOM].piecePercent = 0;
      if (w->abacus.decks[BOTTOM].piece != 0 &&
                  w->abacus.decimalPosition < 1)
            w->abacus.decks[BOTTOM].piece = 0;
      if (!SetBeadsForValue(w, buffer, minusSign)) {
            ResetBeads(w);
      }
      free(buffer);
}

static void
FormatRails(AbacusWidget w)
{
      int value;

      if (w->abacus.decks[BOTTOM].piece != 0) {
            value = w->abacus.decks[BOTTOM].position[w->abacus.decimalPosition -
                  1];
            if (w->abacus.decks[TOP].number != 0 &&
                        w->abacus.decks[TOP].piece != 0) {
                  value = w->abacus.decks[TOP].position[w->abacus.decimalPosition -
                        1];
            }
      }
      if (w->abacus.decks[BOTTOM].piecePercent != 0) {
            value = w->abacus.decks[BOTTOM].position[w->abacus.decimalPosition -
            w->abacus.shiftPercent - 1 - ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0)];
            if (w->abacus.decks[TOP].number != 0 &&
                        w->abacus.decks[TOP].piece != 0) {
                  value = w->abacus.decks[TOP].position[w->abacus.decimalPosition -
                  w->abacus.shiftPercent - 1 - ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0)];
            }
      }
      if (w->abacus.mode < CHINESE || w->abacus.mode > MAXMODES)
            w->abacus.mode = CHINESE;
      else
            w->abacus.mode = (w->abacus.mode + 1) % MAXFORMATS;
      if (w->abacus.demo && w->abacus.mode == OTHER) {
            w->abacus.mode = CHINESE;
      }
      CheckBeads(w);
      ReformatRails(w);
      SetAbacus(w, ABACUS_FORMAT);
}

static void
RomanNumeralsRails(AbacusWidget w)
{
      w->abacus.romanNumerals = !w->abacus.romanNumerals;
      SetCounter(w, 0, w->abacus.decimalPosition, 0);
}

static void
SignRails(AbacusWidget w)
{
      Boolean oldSign = w->abacus.sign;

      w->abacus.sign = !w->abacus.sign;
      if (w->abacus.sign) {
            if (!InsertSpecialRail(w,
                        True, False, False))
                  w->abacus.sign = oldSign;
      } else {
            if (!DeleteSpecialRail(w, True, False, False))
                  w->abacus.sign = oldSign;
      }
      if (w->abacus.sign != oldSign) {
            SetAbacus(w, ABACUS_SIGN);
      }
}

static void
PieceRails(AbacusWidget w, int topPieces, int bottomPieces)
{
      int oldPiece[MAXDECKS];

      oldPiece[TOP] = w->abacus.decks[TOP].piece;
      oldPiece[BOTTOM] = w->abacus.decks[BOTTOM].piece;
      if (w->abacus.decks[BOTTOM].piece != 0 &&
                  w->abacus.decks[BOTTOM].piecePercent != 0)
            return;
      if (w->abacus.decks[TOP].number == 0 && topPieces != 0) {
            if (w->abacus.decks[BOTTOM].room <= topPieces * bottomPieces) {
                  w->abacus.decks[BOTTOM].room = topPieces * bottomPieces + 1;
                  w->abacus.decks[BOTTOM].spaces = w->abacus.decks[BOTTOM].room -
                        w->abacus.decks[BOTTOM].number;
            }
      } else {
            if (CheckBottomSpace(w) < bottomPieces) {
                  w->abacus.decks[BOTTOM].spaces -=
                        (CheckBottomSpace(w) - bottomPieces);
                  w->abacus.decks[BOTTOM].room = w->abacus.decks[BOTTOM].number +
                        w->abacus.decks[BOTTOM].spaces;
            }
      }
      w->abacus.decks[BOTTOM].piece =
            (w->abacus.decks[BOTTOM].piece != 0) ?
            0 : bottomPieces;
      w->abacus.decks[TOP].piece =
            (w->abacus.decks[TOP].piece != 0 ||
             w->abacus.decks[BOTTOM].piece == 0) ?
            0 : topPieces;
      if (w->abacus.decks[BOTTOM].piece != 0) {
            if (!InsertSpecialRail(w,
                        False, True, False)) {
                  w->abacus.decks[BOTTOM].piece = oldPiece[BOTTOM];
                  w->abacus.decks[TOP].piece = oldPiece[TOP];
            }
      } else {
            if (!DeleteSpecialRail(w, False, True, False)) {
                  w->abacus.decks[BOTTOM].piece = oldPiece[BOTTOM];
                  w->abacus.decks[TOP].piece = oldPiece[TOP];
            }
      }
      if (w->abacus.decks[BOTTOM].piece != oldPiece[BOTTOM] ||
                  w->abacus.decks[TOP].piece != oldPiece[TOP]) {
            if (bottomPieces == QUARTERS && topPieces == 0)
                  SetAbacus(w, ABACUS_QUARTER);
            else if (bottomPieces == TWELFTH / 2 && topPieces == 2)
                  SetAbacus(w, ABACUS_TWELFTH);
      }
}

static void
PiecePercentRails(AbacusWidget w, int topPiecePercents, int bottomPiecePercents)
{
      int oldPiecePercent[MAXDECKS];

      oldPiecePercent[TOP] = w->abacus.decks[TOP].piecePercent;
      oldPiecePercent[BOTTOM] = w->abacus.decks[BOTTOM].piecePercent;
      if (w->abacus.decimalPosition < 1 + w->abacus.shiftPercent)
            return;
      if (w->abacus.decks[BOTTOM].piece == 0 &&
                  w->abacus.decks[BOTTOM].piecePercent == 0)
            return;
      if (w->abacus.decks[TOP].number == 0 && topPiecePercents != 0) {
            if (w->abacus.decks[BOTTOM].room <= topPiecePercents * bottomPiecePercents) {
                  w->abacus.decks[BOTTOM].room = topPiecePercents * bottomPiecePercents + 1;
                  w->abacus.decks[BOTTOM].spaces = w->abacus.decks[BOTTOM].room -
                        w->abacus.decks[BOTTOM].number;
            }
      } else {
            if (CheckBottomSpace(w) < bottomPiecePercents) {
                  w->abacus.decks[BOTTOM].spaces -=
                        (CheckBottomSpace(w) - bottomPiecePercents);
                  w->abacus.decks[BOTTOM].room = w->abacus.decks[BOTTOM].number +
                        w->abacus.decks[BOTTOM].spaces;
            }
      }
      w->abacus.decks[BOTTOM].piecePercent =
            (w->abacus.decks[BOTTOM].piecePercent != 0) ?
            0 : bottomPiecePercents;
      w->abacus.decks[TOP].piecePercent =
            (w->abacus.decks[TOP].piecePercent != 0 ||
             w->abacus.decks[BOTTOM].piecePercent == 0) ?
            0 : topPiecePercents;
      if (w->abacus.decks[BOTTOM].piecePercent != 0) {
            if (!InsertSpecialRail(w, False, False, True)) {
                  w->abacus.decks[BOTTOM].piecePercent = oldPiecePercent[BOTTOM];
                  if (topPiecePercents != 0)
                        w->abacus.decks[TOP].piecePercent = oldPiecePercent[TOP];
            }
      } else {
            if (!DeleteSpecialRail(w, False, False, True)) {
                  w->abacus.decks[BOTTOM].piecePercent = oldPiecePercent[BOTTOM];
                  if (topPiecePercents != 0)
                        w->abacus.decks[TOP].piecePercent = oldPiecePercent[TOP];
            }
      }
      if (w->abacus.decks[BOTTOM].piecePercent != oldPiecePercent[BOTTOM] ||
                  w->abacus.decks[TOP].piecePercent !=
                  oldPiecePercent[TOP]) {
            if (bottomPiecePercents == QUARTERPERCENTS &&
                        topPiecePercents == 0)
                  SetAbacus(w, ABACUS_QUARTERPERCENT);
      }
}

static void
AnomalyRails(AbacusWidget w, int anomaly, int anomalySq)
{
      if (w->abacus.anomaly != 0)
            w->abacus.anomaly = 0;
      else  
            w->abacus.anomaly = anomaly;
      w->abacus.anomalySq = anomalySq;
      ReformatRails(w);
}

#if 0
static void
VerticalRails(AbacusWidget w)
{
      EraseFrame(w, 0);
      w->abacus.vertical = !w->abacus.vertical;
      ResizeAbacus(w);
      ResizeBead(w);
      DrawAllBufferedBeads(w);
      DrawFrame(w, 0, True, True);
      DrawAllBeads(w);
}
#endif

#define FACTOR 0.7

#ifdef WINVER
#define MAXINTENSITY 0xFF
static int
brighter(const int light)
{
      int i = (int) ((1 - FACTOR) * MAXINTENSITY);
      int temp = light;

      if (temp < i)
            temp = i;
      return MIN(temp / FACTOR, MAXINTENSITY);
}

static int
darker(const int light)
{
      return (int) (light * FACTOR);
}

static void
SetValuesAbacus(AbacusWidget w)
{
      struct tagColor {
            int   red, green, blue;
      } color;
      char  szBuf[80];

      w->abacus.decks[TOP].factor =
            GetPrivateProfileInt(SECTION, "topFactor", 5, INIFILE);
      w->abacus.decks[BOTTOM].factor =
            GetPrivateProfileInt(SECTION, "bottomFactor", 1, INIFILE);
      w->abacus.decks[TOP].orientation = (BOOL)
            GetPrivateProfileInt(SECTION, "topOrient", 1, INIFILE);
      w->abacus.decks[BOTTOM].orientation = (BOOL)
            GetPrivateProfileInt(SECTION, "bottomOrient", 0, INIFILE);
      w->abacus.decks[TOP].number =
            GetPrivateProfileInt(SECTION, "topNumber", 2, INIFILE);
      w->abacus.decks[BOTTOM].number =
            GetPrivateProfileInt(SECTION, "bottomNumber", 5, INIFILE);
      w->abacus.decks[TOP].spaces =
            GetPrivateProfileInt(SECTION, "topSpaces", 2, INIFILE);
      w->abacus.decks[BOTTOM].spaces =
            GetPrivateProfileInt(SECTION, "bottomSpaces", 3, INIFILE);
      w->abacus.decks[TOP].piece = GetPrivateProfileInt(SECTION,
            "topPiece", 2, INIFILE);
      w->abacus.decks[BOTTOM].piece = GetPrivateProfileInt(SECTION,
            "bottomPiece", 0, INIFILE);
      w->abacus.decks[TOP].piece = GetPrivateProfileInt(SECTION,
            "topPiecePercent", 2, INIFILE);
      w->abacus.decks[BOTTOM].piece = GetPrivateProfileInt(SECTION,
            "bottomPiecePercent", 0, INIFILE);
      w->abacus.shiftPercent = GetPrivateProfileInt(SECTION,
            "shiftPercent", 2, INIFILE);
      w->abacus.decimalPosition = GetPrivateProfileInt(SECTION,
            "decimalPosition", 2, INIFILE);
      w->abacus.groupSize = GetPrivateProfileInt(SECTION,
            "groupSize", 2, INIFILE);
      w->abacus.rails = GetPrivateProfileInt(SECTION, "rails", 13, INIFILE);
      w->abacus.slot = (BOOL) GetPrivateProfileInt(SECTION,
            "slot", FALSE, INIFILE);
      w->abacus.diamond = (BOOL) GetPrivateProfileInt(SECTION,
            "diamond", FALSE, INIFILE);
      w->abacus.vertical = (BOOL) GetPrivateProfileInt(SECTION,
            "vertical", FALSE, INIFILE);
      (void) GetPrivateProfileString(SECTION, "format", "Other",
            szBuf, sizeof (szBuf), INIFILE);
      (void) strcpy(w->abacus.format, szBuf);
      w->abacus.format[80] = 0;
      w->abacus.base = GetPrivateProfileInt(SECTION,
            "base", 10, INIFILE);
      w->abacus.anomaly = GetPrivateProfileInt(SECTION,
            "anomaly", 0, INIFILE);
      w->abacus.shiftAnomaly = GetPrivateProfileInt(SECTION,
            "shiftAnomaly", 2, INIFILE);
      w->abacus.anomalySq = GetPrivateProfileInt(SECTION,
            "anomalySq", 0, INIFILE);
      w->abacus.shiftAnomalySq = GetPrivateProfileInt(SECTION,
            "shiftAnomalySq", 2, INIFILE);
      w->abacus.displayBase = GetPrivateProfileInt(SECTION,
            "displayBase", 10, INIFILE);
      w->abacus.romanNumerals = (BOOL) GetPrivateProfileInt(SECTION,
            "romanNumerals", FALSE, INIFILE);
      w->abacus.mono = (BOOL) GetPrivateProfileInt(SECTION,
            "mono", DEFAULTMONO, INIFILE);
      w->abacus.reverse = (BOOL) GetPrivateProfileInt(SECTION,
            "reverse", DEFAULTREVERSE, INIFILE);
      /* DarkRed */
      (void) GetPrivateProfileString(SECTION, "beadColor", "139 0 0",
            szBuf, sizeof (szBuf), INIFILE);
      (void) sscanf(szBuf, "%d %d %d",
            &(color.red), &(color.green), &(color.blue));
      w->abacus.beadShadeGC[1] = RGB(color.red, color.green, color.blue);
      w->abacus.beadShadeGC[0] = RGB(brighter(color.red),
            brighter(color.green), brighter(color.blue));
      w->abacus.beadShadeGC[2] = RGB(darker(color.red),
            darker(color.green), darker(color.blue));
      w->abacus.beadShadeGC[3] = RGB(darker(darker(color.red)),
            darker(darker(color.green)), darker(darker(color.blue)));
      w->abacus.beadShadeGC[4] = RGB(darker(darker(darker(color.red))),
            darker(darker(darker(color.green))),
            darker(darker(darker(color.blue))));
      /* gray25 */
      (void) GetPrivateProfileString(SECTION, "beadBorder", "64 64 64",
            szBuf, sizeof (szBuf), INIFILE);
      (void) sscanf(szBuf, "%d %d %d",
            &(color.red), &(color.green), &(color.blue));
      w->abacus.borderGC = RGB(color.red, color.green, color.blue);
      /* gold */
      (void) GetPrivateProfileString(SECTION, "railColor", "255 215 0",
            szBuf, sizeof (szBuf), INIFILE);
      (void) sscanf(szBuf, "%d %d %d",
            &(color.red), &(color.green), &(color.blue));
      w->abacus.railGC = RGB(color.red, color.green, color.blue);
      /* cyan */
      (void) GetPrivateProfileString(SECTION, "frameColor", "0 255 255",
            szBuf, sizeof (szBuf), INIFILE);
      (void) sscanf(szBuf, "%d %d %d",
            &(color.red), &(color.green), &(color.blue));
      w->abacus.frameGC = RGB(color.red, color.green, color.blue);
#if 0
      /* black */
      (void) GetPrivateProfileString(SECTION, "foreground", "0 0 0",
            szBuf, sizeof (szBuf), INIFILE);
      (void) sscanf(szBuf, "%d %d %d",
            &(color.red), &(color.green), &(color.blue));
      w->abacus.foregroundGC = RGB(color.red, color.green, color.blue);
#endif
      /* #AEB2C3 */
      (void) GetPrivateProfileString(SECTION, "background", "174 178 195",
            szBuf, sizeof (szBuf), INIFILE);
      (void) sscanf(szBuf, "%d %d %d",
            &(color.red), &(color.green), &(color.blue));
      w->abacus.inverseGC = RGB(color.red, color.green, color.blue);
      w->abacus.delay = GetPrivateProfileInt(SECTION, "delay", 50, INIFILE);
      w->abacus.sound = (BOOL)
            GetPrivateProfileInt(SECTION, "sound", 0, INIFILE);
      (void) GetPrivateProfileString(SECTION, "bumpSound", BUMPSOUND,
            szBuf, sizeof (szBuf), INIFILE);
      (void) strcpy(w->abacus.bumpSound, szBuf);
      (void) GetPrivateProfileString(SECTION, "moveSound", MOVESOUND,
            szBuf, sizeof (szBuf), INIFILE);
      (void) strcpy(w->abacus.moveSound, szBuf);
      w->abacus.script = (BOOL)
            GetPrivateProfileInt(SECTION, "script", 0, INIFILE);
      w->abacus.demo = (BOOL)
            GetPrivateProfileInt(SECTION, "demo", 0, INIFILE);
      (void) GetPrivateProfileString(SECTION, "textColor", "0 255 0",
            szBuf, sizeof (szBuf), INIFILE);
      (void) sscanf(szBuf, "%d %d %d",
            &(color.red), &(color.green), &(color.blue));
      /* Bright Green */
      w->abacusDemo.foregroundGC = RGB(color.red, color.green, color.blue);
      (void) GetPrivateProfileString(SECTION, "demoPath", DEMOPATH,
            szBuf, sizeof (szBuf), INIFILE);
      (void) strcpy(w->abacusDemo.path, szBuf);
      w->abacusDemo.path[80] = 0;
}

void
DestroyAbacus(AbacusWidget w, HBRUSH brush)
{
#ifdef SCRIPTFILE
      if (w->abacus.script) {
            char buf[512];

            (void) fclose(w->abacus.fp);
            (void) sprintf(buf, "Saved to %s.", SCRIPTFILE);
            DISPLAY_INFO(buf);
      }
#endif
      (void) DeleteObject(brush);
      PostQuitMessage(0);
}

void
SetAbacusMove(AbacusWidget w, int reason, int aux, int deck,
            int rail, int number)
{
      if (aux != PRIMARY)
            return;
      w->abacus.deck = deck;
      w->abacus.rail = rail;
      w->abacus.number = number;
      if (CheckMove(w)) {
            MoveBeadsByValue(w, deck, rail
                  + w->abacus.decimalPosition, w->abacus.number,
                  False);
      }
}

#else
#define MAXINTENSITY 0xFFFF

static Pixel
brighter(AbacusWidget w, Pixel pixel)
{
      XColor color;
      int i = (int) ((1 - FACTOR) * MAXINTENSITY);

      color.pixel = pixel;
      (void) XQueryColor(XtDisplay(w), w->abacus.colormap, &color);
      if (color.red < i)
            color.red = i;
      if (color.green < i)
            color.green = i;
      if (color.blue < i)
            color.blue = i;
      color.red = (unsigned short) MIN(color.red / FACTOR, MAXINTENSITY);
      color.green = (unsigned short) MIN(color.green / FACTOR, MAXINTENSITY);
      color.blue = (unsigned short) MIN(color.blue / FACTOR, MAXINTENSITY);
      if (XAllocColor(XtDisplay(w), w->abacus.colormap, &color))
            return color.pixel;
      return pixel;
}

static Pixel
darker(AbacusWidget w, Pixel pixel)
{
      XColor color;

      color.pixel = pixel;
      (void) XQueryColor(XtDisplay(w), w->abacus.colormap, &color);
      color.red = (unsigned short) (color.red * FACTOR);
      color.green = (unsigned short) (color.green * FACTOR);
      color.blue = (unsigned short) (color.blue * FACTOR);
      if (XAllocColor(XtDisplay(w), w->abacus.colormap, &color))
            return color.pixel;
      return pixel;
}

static void
SetAllColors(AbacusWidget w)
{
      XGCValues values;
      XtGCMask valueMask;

      valueMask = GCForeground | GCBackground;
      if (w->abacus.reverse) {
            values.foreground = w->abacus.foreground;
            values.background = w->abacus.background;
      } else {
            values.foreground = w->abacus.background;
            values.background = w->abacus.foreground;
      }
      if (w->abacus.inverseGC)
            XtReleaseGC((Widget) w, w->abacus.inverseGC);
      w->abacus.inverseGC = XtGetGC((Widget) w, valueMask, &values);
      if (w->abacus.mono) {
            if (w->abacus.reverse) {
                  values.foreground = w->abacus.background;
                  values.background = w->abacus.foreground;
            } else {
                  values.foreground = w->abacus.foreground;
                  values.background = w->abacus.background;
            }
      } else {
                  values.foreground = w->abacus.frameColor;
                  values.background = w->abacus.borderColor;
      }
      if (w->abacus.frameGC)
            XtReleaseGC((Widget) w, w->abacus.frameGC);
      w->abacus.frameGC = XtGetGC((Widget) w, valueMask, &values);
      if (w->abacus.mono) {
            if (w->abacus.reverse) {
                  values.foreground = w->abacus.background;
                  values.background = w->abacus.foreground;
            } else {
                  values.foreground = w->abacus.foreground;
                  values.background = w->abacus.background;
            }
      } else {
            values.foreground = w->abacus.railColor;
            values.background = w->abacus.borderColor;
      }
      if (w->abacus.railGC)
            XtReleaseGC((Widget) w, w->abacus.railGC);
      w->abacus.railGC = XtGetGC((Widget) w, valueMask, &values);
      if (w->abacus.mono) {
            if (w->abacus.reverse) {
                  values.foreground = w->abacus.background;
                  values.background = w->abacus.foreground;
            } else {
                  values.foreground = w->abacus.foreground;
                  values.background = w->abacus.background;
            }
      } else {
            values.foreground = w->abacus.beadColor;
            values.background = w->abacus.borderColor;
      }
      if (!w->abacus.mono && (BlackPixelOfScreen(XtScreen(w)) ==
                  w->abacus.beadColor)) {
            w->abacus.beadColor = brighter(w, values.foreground);
            values.foreground = w->abacus.beadColor;
      }
      if (w->abacus.beadShadeGC[1])
            XtReleaseGC((Widget) w, w->abacus.beadShadeGC[1]);
      w->abacus.beadShadeGC[1] = XtGetGC((Widget) w, valueMask, &values);
      if (!w->abacus.mono) {
            values.foreground = brighter(w, w->abacus.beadColor);
      }
      if (w->abacus.beadShadeGC[0])
            XtReleaseGC((Widget) w, w->abacus.beadShadeGC[0]);
      w->abacus.beadShadeGC[0] = XtGetGC((Widget) w, valueMask, &values);
      if (!w->abacus.mono) {
            values.foreground = darker(w, w->abacus.beadColor);
      }
      if (w->abacus.beadShadeGC[2])
            XtReleaseGC((Widget) w, w->abacus.beadShadeGC[2]);
      w->abacus.beadShadeGC[2] = XtGetGC((Widget) w, valueMask, &values);
      if (!w->abacus.mono) {
            values.foreground = darker(w, values.foreground);
      }
      if (w->abacus.beadShadeGC[3])
            XtReleaseGC((Widget) w, w->abacus.beadShadeGC[3]);
      w->abacus.beadShadeGC[3] = XtGetGC((Widget) w, valueMask, &values);
      if (!w->abacus.mono) {
            values.foreground = darker(w, values.foreground);
      }
      if (w->abacus.beadShadeGC[4])
            XtReleaseGC((Widget) w, w->abacus.beadShadeGC[4]);
      w->abacus.beadShadeGC[4] = XtGetGC((Widget) w, valueMask, &values);
      if (w->abacus.mono) {
            if (w->abacus.reverse) {
                  values.foreground = w->abacus.foreground;
                  values.background = w->abacus.background;
            } else {
                  values.foreground = w->abacus.background;
                  values.background = w->abacus.foreground;
            }
      } else {
            values.foreground = w->abacus.borderColor;
            values.background = w->abacus.beadColor;
      }
      if (w->abacus.borderGC)
            XtReleaseGC((Widget) w, w->abacus.borderGC);
      w->abacus.borderGC = XtGetGC((Widget) w, valueMask, &values);
}

static Boolean
SetValuesAbacus(Widget current, Widget request, Widget renew)
{
      AbacusWidget c = (AbacusWidget) current, w = (AbacusWidget) renew;
      Boolean redraw = False;
      Boolean redrawBeads = False;
      int value;

      if (w->abacus.demo && ((w->abacus.mode != c->abacus.mode) ||
                  (w->abacus.demo != c->abacus.demo))) {
            ClearAllBeads(w);
            if (!EmptyCounter(w)) {
                  DISPLAY_WARNING("corruption (SetValuesAbacus)");
            }
            SetCounter(w, 0, w->abacus.decimalPosition, 0); /* needed when 0 */
      }
      if (strncasecmp(w->abacus.format, c->abacus.format,
                  MAXLENFORMAT) != 0) {
            SetModeFromFormat(w);
            SetAbacus(w, ABACUS_FORMAT);
            if (w->abacus.demo)
                  ClearRails(w);
      }
      if (w->abacus.decks[BOTTOM].piece != 0 &&
                  w->abacus.decimalPosition < 1) {
            w->abacus.decks[BOTTOM].piece = 0;
            c->abacus.decks[BOTTOM].piece = 0;
            SetAbacus(w, ABACUS_QUARTER);
      }
      CheckBeads(w);
      if (w->abacus.decks[BOTTOM].piece != 0) {
            value = w->abacus.decks[BOTTOM].position[w->abacus.decimalPosition -
                  1];
      
            if (w->abacus.decks[TOP].number != 0 &&
                        w->abacus.decks[TOP].piece != 0) {
                  value = w->abacus.decks[TOP].position[w->abacus.decimalPosition -
                        1];
            }
      }
      if (w->abacus.decks[BOTTOM].piecePercent != 0) {
            value = w->abacus.decks[BOTTOM].position[w->abacus.decimalPosition -
            w->abacus.shiftPercent - 1 - ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0)];
            if (w->abacus.decks[TOP].number != 0 &&
                        w->abacus.decks[TOP].piece != 0) {
                  value = w->abacus.decks[TOP].position[w->abacus.decimalPosition -
                  w->abacus.shiftPercent - 1 - ((w->abacus.decks[BOTTOM].piece != 0) ? 1 : 0)];
            }
      }
      CheckBeads(w);
      if (w->core.background_pixel != c->core.background_pixel ||
                  w->abacus.foreground != c->abacus.foreground ||
                  w->abacus.borderColor != c->abacus.borderColor ||
                  w->abacus.beadColor != c->abacus.beadColor ||
                  w->abacus.railColor != c->abacus.railColor ||
                  w->abacus.reverse != c->abacus.reverse ||
                  w->abacus.mono != c->abacus.mono) {
            SetAllColors(w);
            redrawBeads = True;
      }
      if (w->abacus.rails != c->abacus.rails) {
            /* via slider */
            (void) ShiftRails(w, c->abacus.rails, w->abacus.rails,
                  False, False, False);
      }
      if (w->abacus.sign != c->abacus.sign) {
            if (w->abacus.sign) {
                  if (!InsertSpecialRail(w, True, False, False)) {
                        w->abacus.sign = !w->abacus.sign;
                        SetAbacus(w, ABACUS_SIGN);
                  }
            } else {
                  if (!DeleteSpecialRail(w, True, False, False)) {
                        w->abacus.sign = !w->abacus.sign;
                        SetAbacus(w, ABACUS_SIGN);
                  }
            }
      }
      if (w->abacus.decks[BOTTOM].piece != c->abacus.decks[BOTTOM].piece) {
            if (w->abacus.decks[BOTTOM].piece != 0) {
                  if (!InsertSpecialRail(w, False, True, False)) {
                        w->abacus.decks[BOTTOM].piece = c->abacus.decks[BOTTOM].piece;
                        SetAbacus(w, ABACUS_QUARTER);
                  }
            } else {
                  if (!DeleteSpecialRail(w, False, True, False)) {
                        w->abacus.decks[BOTTOM].piece = c->abacus.decks[BOTTOM].piece;
                        SetAbacus(w, ABACUS_QUARTER);
                  }
            }
      }
      if (w->abacus.decks[BOTTOM].piecePercent != c->abacus.decks[BOTTOM].piecePercent) {
            if (w->abacus.decks[BOTTOM].piecePercent != 0) {
                  if (!InsertSpecialRail(w, False, False, True)) {
                        w->abacus.decks[BOTTOM].piecePercent = (w->abacus.decks[BOTTOM].piecePercent != 0) ?
                              0 : QUARTERPERCENTS;
                        SetAbacus(w, ABACUS_QUARTERPERCENT);
                  }
            } else {
                  if (!DeleteSpecialRail(w, False, False, True)) {
                        w->abacus.decks[BOTTOM].piecePercent = (w->abacus.decks[BOTTOM].piecePercent != 0) ?
                              0 : QUARTERPERCENTS;
                        SetAbacus(w, ABACUS_QUARTERPERCENT);
                  }
            }
      }
      if (w->abacus.vertical != c->abacus.vertical) {
            ResizeAbacus(w);
            ResizeBead(w);
            DrawAllBufferedBeads(w);
            redraw = True;
      }
      if (w->abacus.displayBase != c->abacus.displayBase) {
            SetCounter(w, 0, w->abacus.decimalPosition, 0);
      }
      if (w->abacus.decks[BOTTOM].number != c->abacus.decks[BOTTOM].number ||
                  w->abacus.decks[TOP].number !=
                  c->abacus.decks[TOP].number ||
                  w->abacus.decks[BOTTOM].factor !=
                  c->abacus.decks[BOTTOM].factor ||
                  w->abacus.decks[TOP].factor !=
                  c->abacus.decks[TOP].factor ||
                  w->abacus.decks[BOTTOM].spaces !=
                  c->abacus.decks[BOTTOM].spaces ||
                  w->abacus.decks[TOP].spaces !=
                  c->abacus.decks[TOP].spaces ||
                  w->abacus.diamond != c->abacus.diamond ||
                  w->abacus.slot != c->abacus.slot ||
                  w->abacus.decimalPosition !=
                  c->abacus.decimalPosition ||
                  w->abacus.base != c->abacus.base) {
            ReformatRails(w);
            redraw = False;
      }
      if (w->abacus.decks[BOTTOM].orientation !=
                  c->abacus.decks[BOTTOM].orientation ||
                  w->abacus.decks[TOP].orientation !=
                  c->abacus.decks[TOP].orientation) {
            redraw = True;
      }
      if (w->abacus.pos.x != c->abacus.pos.x ||
                  w->abacus.pos.y != c->abacus.pos.y)
            redrawBeads = True;
      if (redrawBeads && !redraw && XtIsRealized(renew) &&
                  renew->core.visible) {
            EraseFrame(c, 0);
            DrawFrame(w, 0, True, True);
            DrawAllBeads(w);
      }
      if (w->abacus.menu != -1) {
            int menu = w->abacus.menu;

            w->abacus.menu = -1;
            switch (menu) {
            case 1:
                  ClearRails(w);
                  break;
            case 2:
                  IncrementRails(w);
                  break;
            case 3:
                  DecrementRails(w);
                  break;
            case 4:
                  FormatRails(w);
                  break;
            case 5:
                  RomanNumeralsRails(w);
                  break;
            case 6:
                  SignRails(w);
                  break;
            case 7:
                  PieceRails(w, 0, QUARTERS);
                  break;
            case 8:
                  PiecePercentRails(w, 0, QUARTERPERCENTS);
                  break;
            case 9:
                  PieceRails(w, 2, TWELFTH / 2);
                  break;
            case 10:
                  AnomalyRails(w, 2, 0);
                  break;
            case 11:
                  AnomalyRails(w, 4, 4);
                  break;
            case 12:
                  SpeedBead(w);
                  break;
            case 13:
                  SlowBead(w);
                  break;
            case 14:
                  SoundBead(w);
                  break;
            /*case 15:
                  VerticalRails(w);
                  break;*/
            default:
                  break;
            }
      }
      if (w->abacus.deck == ABACUS_CLEAR) {
            w->abacus.deck = ABACUS_IGNORE;
            ClearAllBeads(w);
      } else if (w->abacus.deck == ABACUS_CALC) {
            w->abacus.deck = ABACUS_IGNORE;
            calculate(w, w->abacus.mathBuffer);
      } else if (w->abacus.deck != ABACUS_IGNORE) {
            if (CheckMove(w)) {
                  MoveBeadsByValue(w, w->abacus.deck, w->abacus.rail
                        + w->abacus.decimalPosition, w->abacus.number,
                        False);
            }
            w->abacus.deck = ABACUS_IGNORE;
      }
      return (redraw);
}

static void
QuitAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
#ifdef WINVER
      if (w->abacus.freeDC) {
            int sel, shade;

            for (sel = 0; sel < 2; sel++)
                  for (shade = 0; shade < 2; shade++) {
                        if (w->abacus.bufferBead[sel][shade] != NULL) {
                              DeleteObject(w->abacus.bufferBead[sel][shade]);
                              w->abacus.bufferBead[sel][shade] = NULL;
                        }
                  }
            DeleteDC(w->core.memDC);
            w->core.memDC = NULL;
      }
#else
      XtCloseDisplay(XtDisplay(w));
#endif
      exit(0);
}

static void
DestroyAbacus(Widget old)
{
      AbacusWidget w = (AbacusWidget) old;
      int i;

#ifdef SCRIPTFILE
      if (w->abacus.script) {
            char buf[512];

            (void) fclose(w->abacus.fp);
            (void) sprintf(buf, "Saved to %s.", SCRIPTFILE);
            DISPLAY_INFO(buf);
      }
#endif
      for (i = 0; i < 5; i++)
            XtReleaseGC(old, w->abacus.beadShadeGC[i]);
      XtReleaseGC(old, w->abacus.borderGC);
      XtReleaseGC(old, w->abacus.railGC);
      XtReleaseGC(old, w->abacus.frameGC);
      XtReleaseGC(old, w->abacus.inverseGC);
      XtRemoveCallbacks(old, XtNselectCallback, w->abacus.select);
}
#endif

static void
ResizeBead(AbacusWidget w)
{
      int sel, shade;
      Point size;

#ifdef WINVER
      if (w->core.memDC == NULL) {
            w->core.memDC = CreateCompatibleDC(w->core.hDC);
            if (w->core.memDC == NULL) {
                  DISPLAY_ERROR("Not enough memory, exiting.");
            }
      }
#else
      Display *display = XtDisplay(w);
      Window window = XtWindow(w);
      XWindowAttributes xgwa;

      (void) XGetWindowAttributes(display, window, &xgwa);
      if (w->abacus.colormap == None) {
            w->abacus.mono = (xgwa.depth < 2 || w->abacus.mono);
            w->abacus.colormap = xgwa.colormap;
      }
#endif
      if (w->abacus.vertical) {
            size.x = w->abacus.pos.y - 1;
            size.y = w->abacus.beadSize.x + 1;
      } else {
            size.x = w->abacus.beadSize.x + 1;
            size.y = w->abacus.pos.y - 1;
      }
      for (sel = 0; sel < 2; sel++)
            for (shade = 0; shade < 2; shade++) {
#ifdef WINVER
                  if (w->abacus.bufferBead[sel][shade] != NULL) {
                        DeleteObject(w->abacus.bufferBead[sel][shade]);
                        w->abacus.bufferBead[sel][shade] = NULL;
                  }
                  if ((w->abacus.bufferBead[sel][shade] =
                              CreateCompatibleBitmap(w->core.hDC,
                              size.x, size.y)) == NULL) {
                        DISPLAY_ERROR("Not enough memory, exiting.");
                  }
#else
                  if (w->abacus.bufferBead[sel][shade] != None) {
                        (void) XFreePixmap(display,
                              w->abacus.bufferBead[sel][shade]);
                        w->abacus.bufferBead[sel][shade] = None;
                  }
                  if ((w->abacus.bufferBead[sel][shade] =
                        XCreatePixmap(display, window,
                              size.x, size.y, xgwa.depth)) == None) {
                        DISPLAY_ERROR("Not enough memory, exiting.");
                  }
#endif
            }
#ifndef WINVER
      SetAllColors(w);
#endif
}

#ifndef WINVER
static
#endif
void
ResizeAbacus(AbacusWidget w)
{
      int height;
#ifdef WINVER
      RECT rect;

      /* Determine size of client area */
      (void) GetClientRect(w->core.hWnd, &rect);
      w->core.width = rect.right;
      w->core.height = rect.bottom;
      if (w->abacus.demo) {
            w->abacusDemo.fontHeight = 16;
            w->core.height -= LINES * 16; /* fontHeight? */
            w->abacusDemo.framed = True;
      }
#endif

      w->abacus.delta.x = 8;
      w->abacus.delta.y = 2;
      w->abacus.midHeight = 6;
      w->abacus.frameSize.x = ((w->abacus.vertical) ? w->core.height :
            w->core.width);
      w->abacus.frameSize.y = ((w->abacus.vertical) ? w->core.width :
            w->core.height);
      w->abacus.pos.x = MAX(((int) w->abacus.frameSize.x -
            w->abacus.delta.x) / w->abacus.rails, w->abacus.delta.x);
      w->abacus.pos.y = MAX(((int) w->abacus.frameSize.y - 2 *
            w->abacus.delta.y - w->abacus.midHeight) /
            (w->abacus.decks[TOP].room + w->abacus.decks[BOTTOM].room),
            w->abacus.delta.y);
      w->abacus.width = w->abacus.pos.x * w->abacus.rails +
            w->abacus.delta.x + 2;
      w->abacus.decks[TOP].height = w->abacus.pos.y *
            w->abacus.decks[TOP].room + w->abacus.delta.y + 2;
      w->abacus.decks[BOTTOM].height = w->abacus.pos.y *
            w->abacus.decks[BOTTOM].room + w->abacus.delta.y + 2;
      height = w->abacus.decks[TOP].height + w->abacus.decks[BOTTOM].height;
      w->abacus.offset.x = ((int) w->abacus.frameSize.x - w->abacus.width +
            2) / 2;
      w->abacus.offset.y = ((int) w->abacus.frameSize.y - height -
            w->abacus.midHeight + 5) / 2 + 1;
      w->abacus.midBarY = w->abacus.decks[TOP].room * w->abacus.pos.y +
            w->abacus.delta.y - 1 + w->abacus.offset.y;
      if (w->abacus.slot)
            w->abacus.railWidth = w->abacus.pos.x / 8 + 1;
      else
            w->abacus.railWidth = w->abacus.pos.x / 40 + 1;
      w->abacus.beadSize.x = w->abacus.pos.x - w->abacus.delta.x;
      w->abacus.beadSize.y = w->abacus.pos.y - w->abacus.delta.y;
}

#ifndef WINVER
static
#endif
void
InitializeAbacus(
#ifdef WINVER
AbacusWidget w, HBRUSH brush
#else
Widget request, Widget renew
#endif
)
{
      int deck;
#ifdef WINVER
      SetValuesAbacus(w);
      brush = CreateSolidBrush(w->abacus.inverseGC);
      SETBACK(w->core.hWnd, brush);
      w->abacus.bufferBead[0][0] = NULL;
      w->abacus.bufferBead[0][1] = NULL;
      w->abacus.bufferBead[1][0] = NULL;
      w->abacus.bufferBead[1][1] = NULL;
#else
      AbacusWidget w = (AbacusWidget) renew;
      int i;

      w->abacus.bufferBead[0][0] = None;
      w->abacus.bufferBead[0][1] = None;
      w->abacus.bufferBead[1][0] = None;
      w->abacus.bufferBead[1][1] = None;
      w->abacus.colormap = None;
      for (i = 0; i < 5; i++)
            w->abacus.beadShadeGC[i] = NULL;
      w->abacus.borderGC = NULL;
      w->abacus.railGC = NULL;
      w->abacus.frameGC = NULL;
      w->abacus.inverseGC = NULL;
#endif
      w->abacus.focus = False;
      w->abacus.numSlices = ((w->abacus.delay < 5 * MAXSLICES) ?
            w->abacus.delay / 5 + 1 : MAXSLICES);
      for (deck = 0; deck < MAXDECKS; deck++)
            w->abacus.decks[deck].position = NULL;
      w->abacus.digits = NULL;
      w->abacus.decimalPosition = w->abacus.shiftPercent;
      if (w->abacus.decks[BOTTOM].piece != 0)
            w->abacus.decimalPosition++;
      if (w->abacus.decks[BOTTOM].piecePercent != 0)
            w->abacus.decimalPosition++;
      /* w->abacus.decimalPosition = w->abacus.rails / 2 */
      SetModeFromFormat(w);
      CheckBeads(w);
      ResetBeads(w);
      ResizeAbacus(w);
#ifdef SCRIPTFILE
      if (w->abacus.script) {
            if ((w->abacus.fp = fopen(SCRIPTFILE, "w")) == NULL) {
                  char buf[512];

                  (void) sprintf(buf, "Can not write to %s.",
                        SCRIPTFILE);
                  DISPLAY_WARNING(buf);
            }
      }
#endif
}

#ifndef WINVER
static
#endif
void
ExposeAbacus(
#ifdef WINVER
AbacusWidget w
#else
Widget renew, XEvent * event, Region region
#endif
)
{
#ifndef WINVER
      AbacusWidget w = (AbacusWidget) renew;

      if (!w->core.visible)
            return;
#endif
      ResizeBead(w);
      DrawAllBufferedBeads(w);
      EraseFrame(w, 0);
      DrawFrame(w, 0, True, w->abacus.focus);
      DrawAllBeads(w);
}

#ifndef WINVER
static
#endif
void
HideAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      SetAbacus(w, ABACUS_HIDE);
}

#ifndef WINVER
static
#endif
void
SelectAbacus(AbacusWidget w
#ifdef WINVER
, const int x, const int y
#else
, XEvent * event, char **args, int nArgs
#endif
)
{
      int deck, rail = 0, j = 0, bead;
#ifndef WINVER
      int x = event->xbutton.x, y = event->xbutton.y;
#endif

      if (w->abacus.demo) {
            ClearAllBeads(w);
            SetAbacus(w, ABACUS_CLEAR);
            w->abacus.currentDeck = -1;
      } else if ((!w->abacus.vertical && (y >= w->abacus.midBarY) &&
                  (y <= w->abacus.midBarY + w->abacus.midHeight))) {
            rail = w->abacus.rails -
                  (x + 1 + w->abacus.railWidth / 2 -
                  w->abacus.delta.x - w->abacus.offset.x) /
                  w->abacus.pos.x - 1;
            SetDecimal(w, rail);
      } else if ((w->abacus.vertical && (x <= w->abacus.frameSize.y - 1 - w->abacus.midBarY) &&
            (x >= w->abacus.frameSize.y - 1 - w->abacus.midBarY - w->abacus.midHeight))) {
            rail = w->abacus.rails -
                  (y - w->abacus.beadSize.x / 2 -
                  w->abacus.delta.x - w->abacus.offset.x) /
                  w->abacus.pos.x - 1;
            SetDecimal(w, rail);
      } else if (PositionToBead(w, x, y, &deck, &rail, &j)) {
            w->abacus.currentDeck = deck;
            w->abacus.currentRail = rail;
            w->abacus.currentPosition = j;
            bead = j;
            if (bead > w->abacus.decks[deck].position[rail])
                  bead -= w->abacus.decks[deck].spaces;
            DrawBead(w, deck, rail, bead, j, True, False, TRUE, 0, 0);
      } else
            w->abacus.currentDeck = -1;
}

#ifndef WINVER
static
#endif
void
ReleaseAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      int bead;

      if (w->abacus.currentDeck < 0)
            return;
      bead = w->abacus.currentPosition;
      if (bead > w->abacus.decks[w->abacus.currentDeck].position[w->abacus.currentRail])
            bead -= w->abacus.decks[w->abacus.currentDeck].spaces;
      DrawBead(w, w->abacus.currentDeck, w->abacus.currentRail,
            bead, w->abacus.currentPosition, False, False, TRUE, 0, 0);
      DrawBead(w, w->abacus.currentDeck, w->abacus.currentRail,
            bead, w->abacus.currentPosition, True, False, FALSE, 0, 0);
      MoveBeadsByPos(w, w->abacus.currentDeck, w->abacus.currentRail,
            w->abacus.currentPosition, False);
      w->abacus.currentDeck = -1;
}

#ifndef WINVER
static
#endif
void
ClearAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      ClearRails(w);
}

#ifndef WINVER
static
void
ClearAbacusMaybe(AbacusWidget w
, XEvent * event, char **args, int nArgs
)
{
#ifdef HAVE_MOTIF
      if (!EmptyCounter(w) && !w->abacus.aux) {
            /* Check if one really wants to destroy calculations. */
            SetAbacus(w, ABACUS_CLEAR_QUERY);
      }
#endif
}

static
void
ClearAbacus2(AbacusWidget w
, XEvent * event, char **args, int nArgs
)
{
#ifndef HAVE_MOTIF
      ClearRails(w);
#endif
}
#endif

#ifndef WINVER
static
#endif
void
IncrementAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      IncrementRails(w);
}

#ifndef WINVER
static
#endif
void
DecrementAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      DecrementRails(w);
}

#ifndef WINVER
static
#endif
void
SpeedAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      SpeedBead(w);
}

#ifndef WINVER
static
#endif
void
SlowAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      SlowBead(w);
}

#ifndef WINVER
static
#endif
void
SoundAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      SoundBead(w);
}

#ifndef WINVER
static
#endif
void
FormatAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      FormatRails(w);
}

#ifndef WINVER
static
#endif
void
RomanNumeralsAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      RomanNumeralsRails(w);
}

#ifndef WINVER
static
#endif
void
SignAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      SignRails(w);
}

#ifndef WINVER
static
#endif
void
QuarterAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      PieceRails(w, 0, QUARTERS);
}

#ifndef WINVER
static
#endif
void
QuarterPercentAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      PiecePercentRails(w, 0, QUARTERPERCENTS);
}

#ifndef WINVER
static
#endif
void
TwelfthAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      PieceRails(w, 2, TWELFTH / 2);
}

#ifndef WINVER
static
#endif
void
AnomalyAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      AnomalyRails(w, 2, 0);
}

#ifndef WINVER
static
#endif
void
WatchAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      AnomalyRails(w, 4, 4);
}

#ifndef WINVER
static
void
DemoAbacus(AbacusWidget w
, XEvent * event, char **args, int nArgs
)
{
      SetAbacus(w, ABACUS_DEMO);
}

static
void
NextAbacus(AbacusWidget w
, XEvent * event, char **args, int nArgs
)
{
      if (w->abacus.demo) {
            SetAbacus(w, ABACUS_NEXT);
      }
}

static
void
RepeatAbacus(AbacusWidget w
, XEvent * event, char **args, int nArgs
)
{
      if (w->abacus.demo) {
            SetAbacus(w, ABACUS_REPEAT);
      }
}

static
void
MoreAbacus(AbacusWidget w
, XEvent * event, char **args, int nArgs
)
{
      if (w->abacus.demo) {
            SetAbacus(w, ABACUS_MORE);
      }
}
#endif

#ifndef WINVER
static
#endif
void
EnterAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      w->abacus.focus = True;
      DrawFrame(w, 0, True, w->abacus.focus);
}

#ifndef WINVER
static
#endif
void
LeaveAbacus(AbacusWidget w
#ifndef WINVER
, XEvent * event, char **args, int nArgs
#endif
)
{
      w->abacus.focus = False;
      DrawFrame(w, 0, True, w->abacus.focus);
}

#ifdef OLDANDINTHEWAY
/* This routine will do a XFillArc for a circle */
DiskXI(Display * display, Window window, GC gc, int diameter,
            int ctrX, int ctrY)
{
      int x, y, p, d;

      x = 0;
      y = diameter >> 1;
      p = diameter & 1;
      d = 1 - 2 * y + p;
      while (x < y) {
            DiskPointsXI(display, window, gc, ctrX, ctrY, x, y, p);
            if (d < 0)
                  d = d + (4 * x) + 6;
            else {
                  d = d + (4 * (x - y)) + 10;
                  y--;
            }
            x++;
      }
      if (x == y)
            DiskPointsXI(display, window, gc, ctrX, ctrY, x, y, p);
}                       /* DiskXI */

DiskPointsXI(Display * display, Window window, GC gc, int ctrX, int ctrY,
            int x, int y, int p)
{
      XFillRectangle(display, window, gc,
            ctrX - x, ctrY - y, 2 * x + p + 1, 1);
      XFillRectangle(display, window, gc,
            ctrX - x, ctrY + y + p, 2 * x + p + 1, 1);
      XFillRectangle(display, window, gc,
            ctrX - y, ctrY - x, 2 * y + p + 1, 1);
      XFillRectangle(display, window, gc,
            ctrX - y, ctrY + x + p, 2 * y + p + 1, 1);
      /*XDrawLine(display, window, gc, ctrX - x, ctrY - y, ctrX + x + p, ctrY - y);
      XDrawLine(display, window, gc,
            ctrX - x, ctrY + y + p, ctrX + x + p, ctrY + y + p);
      XDrawLine(display, window, gc, ctrX - y, ctrY - x, ctrX + y + p, ctrY - x);
      XDrawLine(display, window, gc,
            ctrX - y, ctrY + x + p, ctrX + y + p, ctrY + x + p); */
}                       /* DiskPointsXI */

#endif

Generated by  Doxygen 1.6.0   Back to index