/* PI calculation program
   by Stephen Jones, Wainuiomata, New Zealand
   (Censored by AJW)
*/

#include <exec/types.h>
#include <intuition/intuition.h>

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

#define INTUITION_REV 29L
#define GRAPHICS_REV  29L

struct NewWindow win;
struct Window *W;

char buffer[32] = "100";
char fibuff[32] = "";

int quat;

SHORT SJXY[]= {-1,-1,8*8,-1,8*8,8,-1,8,-1,-1};
SHORT SJXY2[]={8*8+1,8,8*8+1,1,8*8+2,1,8*8+2,9,2,9};

struct Border Bord2 = {
  0,0,
  2,0,JAM1,
  5,
  &SJXY2,
  NULL
};

struct Border SjBorder = {
  0,0,      /* LeftEdge, TopEdge */
  3,0,JAM1, /* FrontPen, BackPen, Drawmode */
  5,        /* Count (number of pairs of co-ords) */
  &SJXY,    /* XY - ptr to coord array */
  &Bord2    /* NextBorder */
};

struct TextAttr MyFont = {
  "topaz.font",
  TOPAZ_EIGHTY,
  FS_NORMAL,
  FPF_ROMFONT
};

struct IntuiText SjGajTex = {
  1,0,JAM2,-17*8,0,
  &MyFont,
  (UBYTE *) "Number of digits",
  NULL
};

struct IntuiText SjFiTex = {
  1,0,JAM2,-17*8,0,
  &MyFont,
  (UBYTE *) " Output filename",
  NULL
};

struct StringInfo GajStrDef = {
  buffer,         /* Buffer ptr */
  NULL,           /* Undo buffer ptr */
  3,              /* BufferPos */
  32,             /* MaxChars */
  0,              /* DispPos Buffer pos of the first displayed char */
  0,              /* UndoPos */
  0,              /* Numchars */
  0,              /* Dispcount */
  0, 0,           /* CLeft, CTop */
  0,              /* LayerPtr */
  100,            /* LongInt */
  0               /* AltKeyMap */
};

struct StringInfo FiStrDef = {
  fibuff,
  NULL,
  0,
  32,
  0,
  0,
  0,
  0,
  0, 0,
  0,
  0,
  0
};

USHORT GoingRenData[] = {
  0x7FFF, 0xFFFE,
  0x8000, 0x0001,
  0x9FF8, 0X3FF9,
  0xB800, 0x701D,
  0xB800, 0x701D,
  0xB800, 0x701D,
  0xB800, 0x701D,
  0xB8FC, 0x701D,
  0xB81C, 0x701D,
  0xB81C, 0x701D,
  0xB81C, 0x701D,
  0xB81C, 0x701D,
  0xB81C, 0x701D,
  0x9FF8, 0x3FF9,
  0x8000, 0x0001,
  0x7FFF, 0xFFFE
};

struct Image GoingRen = {
  0, 0,       /* Left Top */
  32, 16, 1,  /* width, height, depth */
  GoingRenData,
  1, 0,
  NULL        /* NextImage */
};

struct Gadget Going = {
  NULL,
  29*8, 24,
  32, 16,
  GADGHCOMP | GADGIMAGE,
  GADGIMMEDIATE,
  BOOLGADGET,
  &GoingRen,
  NULL,
  NULL,                /* No associated text */
  NULL,
  NULL,
  NULL
};

struct Gadget FiGaj = {
  &Going,              /* Next gadget */
  16+17*8, 32,
  8*8, 8,
  NULL,
  NULL,                /* Activation flags */
  STRGADGET,
  &SjBorder,
  NULL,
  &SjFiTex,
  NULL,
  &FiStrDef,
  NULL,
  NULL
};

struct Gadget Gaj = {
  &FiGaj,              /* Next gadget */
  16+17*8, 16,         /* LeftEdge, TopEdge */
  8*8, 8,              /* Width, Height */
  NULL,                /* No special highlighting */
  LONGINT,             /* Activation flags */
  STRGADGET,           /* GadgetType - String */
  &SjBorder,           /* GadgetRender */
  NULL,                /* SelectRender */
  &SjGajTex,           /* Associated text IntuiText structure ! */
  NULL,                /* Mutual exclude */
  &GajStrDef,          /* This gadget's string definition */
  NULL,                /* Gadget ID for my use! */
  NULL                 /* UserData - a ptr to any data you want */
};

struct IntuiMessage *MyIntuiMsg;

main()
{
  LONG i;
  LONG OpenLibrary(), OpenWindow(), GetMsg();

  IntuitionBase = (struct IntuitionBase *)
            OpenLibrary("intuition.library", INTUITION_REV);
  if (IntuitionBase == NULL) shoot("I can't open intuition library!",1);
  GfxBase = (struct GfxBase *)
            OpenLibrary("graphics.library", GRAPHICS_REV);
  if (GfxBase == NULL) shoot("I can't open graphics library!!",1);
  win.LeftEdge   = 20;
  win.TopEdge    = 20;
  win.Title      = (UBYTE *) "PI by Stephen Jones";
  win.Width      = 35*8;
  win.Height     = 64;
  win.DetailPen  = 2;
  win.BlockPen   = 1;
  win.Flags      = SMART_REFRESH | ACTIVATE | WINDOWCLOSE | WINDOWDRAG |
                   WINDOWDEPTH | NOCAREREFRESH;
  win.IDCMPFlags = CLOSEWINDOW | GADGETDOWN;
  win.Type       = WBENCHSCREEN;
  win.FirstGadget= &Gaj;
  win.CheckMark  = NULL;
  win.Screen     = NULL;
  win.BitMap     = NULL;
  win.MinWidth   = 0;
  win.MinHeight  = 0;
  win.MaxWidth   = 0;
  win.MaxHeight  = 0;
  W = (struct Window *)OpenWindow(&win);
  while (1) {
    Wait (1L << ((LONG) W->UserPort->mp_SigBit));
    while (MyIntuiMsg = (struct IntuiMessage *) GetMsg(W->UserPort)) {
      do {
        quat=0;
        if (MyIntuiMsg->Class == CLOSEWINDOW) {
          closeall();
          exit(TRUE);
        }
        if (MyIntuiMsg->Class == GADGETDOWN) {
          if (((long)&Going) == ((long)MyIntuiMsg->IAddress)) {
            calcpi();
          }
        }
      } while (quat);
    }
  }
}

struct IntuiText CalcText = {
  1, 3, JAM2,
  32, 48,
  &MyFont,
  NULL,    /* IText Filled in later */
  NULL 
};

prtcalc()
{
  PrintIText(W->RPort, &CalcText, 0L, 0L);
}

USHORT *term, *power, *result;
ULONG digits;
ULONG words;

uncalc()
{
  prtcalc();
  if (term)   free(term);
  if (power)  free(power);
  if (result) free(result);
}

calcpi()
{
  USHORT *calloc();

  digits=GajStrDef.LongInt;
  if (digits<2L || digits>100000000L) {
    shoot("Bad number of digits!",0);
    return;
  };
  CalcText.IText = (UBYTE *) "CALCULATING PI!";
  CalcText.BackPen = 3;
  prtcalc();
  CalcText.IText = (UBYTE *) "               ";
  CalcText.BackPen = 0;
  term=power=result=0;
  words=digits*10/48+2;
  term  = calloc((USHORT)words,sizeof(USHORT));
  power = calloc((USHORT)words,sizeof(USHORT));
  result= calloc((USHORT)words,sizeof(USHORT));
  if (term==0 || power==0 || result==0) {
    shoot("Not enough memory for that many digits!",0);
    uncalc();
    return;
  }
  quat=0;
  zero(result,0);
  pass(16L,5L,1L);
  if (quat) {uncalc(); return;}
  pass(4L,239L,-1L);
  if (quat) {uncalc(); return;}
  printanswer(result);
  uncalc();
}

printanswer(res)
USHORT *res;
{
  LONG i, j, roll;
  int op;
  UBYTE och[5];

  och[4]='\n';
  if (fibuff[0])
    op=creat(fibuff,0);
  else
    op=1;
  if (op==-1) {
    shoot("Cannot open output file!",0);
    return;
  }
  for (i=0; i<digits; i+=4) {
    if (i) {
      och[0]=(res[0]/1000)+'0';
      och[1]=(res[0]/100)%10+'0';
      och[2]=(res[0]/10)%10+'0';
      och[3]=res[0]%10+'0';
    }
    else {
      och[0]=' ';
      och[1]=' ';
      och[2]=res[0]+'0';
      och[3]='.';
    }
    if (digits-i<4)
      write(op,och,digits-i);
    else
    if (i%72==68)
      write(op,och,5);
    else
      write(op,och,4);
    roll=0;
    res[0]=0;
    for (j=words; j--;) {
      roll=res[j]*10000L+roll;
      res[j]=(roll & 0xFFFFL);
      roll=roll >> 16L;
    }
  }
  och[0]='\n';
  write(op,och,1);
  if (fibuff[0])
    close(op);
}

zero(var,n)
USHORT *var;
USHORT n;
{
  LONG i;

  for (i=0; i<words; i++)
    var[i]=0;
  var[0]=n;
}

pass(multer,diver,sgn)
LONG multer,diver,sgn;
{
  LONG zro,exp;
  LONG ddiver, divide(), GetMsg();

  zero(power,(int) multer);
  divide(power,diver,power);
  exp=1;
  ddiver=diver*diver;
  do {
    MyIntuiMsg=(struct IntuiMessage *) GetMsg(W->UserPort);
    if (MyIntuiMsg) {quat=1; return;}   /* Abort if there's a message */
    divide(power,exp,term);
    if (sgn>0)
      add(term,result);
    else
      sub(term,result);
    sgn=-sgn;
    exp+=2L;
    zro=divide(power,ddiver,power);
  } while (!zro);
}

add(src,dst)
USHORT *src, *dst;
{
  LONG tmp,i;
  LONG carry;

  carry=0;
  for (i=words; i--;) {
    tmp=((long)src[i])+((long)dst[i])+carry;
    if (tmp>=0x10000L)
      {tmp-=0x10000L; carry=1;}
    else
      carry=0;
    dst[i]=tmp;
  }
}

sub(src,dst)
USHORT *src, *dst;
{
  LONG tmp,i;
  LONG borrow;

  borrow=0;
  for (i=words; i--;) {
    tmp=((long)dst[i])-((long)src[i])-borrow;
    if (tmp<0) {
      tmp+=0x10000L;
      borrow=1;
    }
    else
      borrow=0;
    dst[i]=tmp;
  }
}

LONG divide(src,divider,dst)
USHORT *src, *dst;
LONG divider;
{
  ULONG rem,i,tmp;
  LONG zro;

  zro=1;
  rem=0;
  for (i=0; i<words; i++) {
    tmp   = (rem << 16L) | (long) src[i];
    if (tmp) {
      rem   = tmp % divider;
      dst[i]= tmp / divider;
      zro=0;
    } else
      dst[i]=0;
  }
  return zro;
}

/*
LONG divide(src,divider,dst)
USHORT *src,*dst;
LONG divider;
{
#asm
   move.l   #1,-16(a5)
   clr.l   -4(a5)
   clr.l   -8(a5)
   bra   sj_skip
sj_loop:
   move.l   -8(a5),d3
   asl.l   #1,d3
   move.l   8(a5),a6
   move.l   #0,d2
   move.w   (a6,d3.l),d2
   move.l   -4(a5),d3
   move.l   #16,d1
   asl.l   d1,d3
   or.l   d3,d2
   move.l   d2,-12(a5)
   tst.l   -12(a5)
   beq   sj_if
   move.l   -12(a5),d0
   move.l   12(a5),d1
   jsr   .modu#
   move.l   d0,-4(a5)
   move.l   -8(a5),d3
   asl.l   #1,d3
   move.l   16(a5),a6
   move.l   -12(a5),d0
   move.l   12(a5),d1
   jsr   .divu#
   move.w   d0,(a6,d3.l)
   clr.l   -16(a5)
   bra   sj_endif
sj_if:
   move.l   -8(a5),d3
   asl.l   #1,d3
   move.l   16(a5),a6
   clr.w   (a6,d3.l)
sj_endif
sj_98:
   add.l   #1,-8(a5)
sj_skip:
   move.l   -8(a5),d3
   cmp.l   _words,d3
   bcs   sj_loop
sj_99:
   move.l   -16(a5),d0
#endasm
}
*/

closeall()
{
  if (W) {CloseWindow(W);  W=0;}
  if (IntuitionBase)  {CloseLibrary(IntuitionBase); IntuitionBase=0;}
  if (GfxBase)        {CloseLibrary(GfxBase);       GfxBase=0;}
}

struct TextAttr   Meins = {
  (UBYTE *) "topaz.font",
  TOPAZ_EIGHTY,
  0,
  0
};

shoot(msg,qexit)
UBYTE *msg;
int qexit;
{
  struct IntuiText  body;
  struct IntuiText  neg;

if (W) {
  body.FrontPen = 0;
  body.BackPen  = 1;
  body.DrawMode = JAM2;
  body.LeftEdge = 8;
  body.TopEdge  = 8;
  body.ITextFont = &Meins;
  body.IText    = msg;
  body.NextText = NULL;
  neg.FrontPen  = 0;
  neg.BackPen   = 1;
  neg.DrawMode  = JAM2;
  neg.LeftEdge  = 3;
  neg.TopEdge   = 3;
  neg.ITextFont = &Meins;
  neg.IText     = (UBYTE *) "SHOOT";
  neg.NextText  = NULL;
  AutoRequest(W,&body,0L,&neg,0L,0L,8L*(strlen(msg))+40L,64L);
}
else
  printf("Shoot- %s\n",msg);
  if (qexit) {
    closeall();
    exit(FALSE);
  }
}
