PROGRAM BIOSIN;

(*      Permission is hereby granted to republish,
 *      but not for profit, any or all of this program,
 *      provided that this copyright notice is included
 *
 *      Copyright 1978, Oregon Minicomputer Software, Inc.
 *              2340 SW Canyon Road
 *              Portland, Oregon 97201
 *              (503) 226-7760
 *)

CONST   MAXCHAR= 64;
        PI= 3.141592;

TYPE    PLOTVALUE=      RECORD
                        SPACENUMBER:    INTEGER;
                        PLOTKIND:       CHAR;
                        END;
        LINEARRAY=      ARRAY [1..4] OF PLOTVALUE;
        MONTHS= (MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC,JAN,FEB);
        MONTHASCII= ARRAY [1..3] OF CHAR;
        STRINGTYPE= ARRAY [1..10] OF CHAR;
        DATES=  RECORD
                DAY:    INTEGER;
                MON:    MONTHASCII;
                MONTH:  MONTHS;
                YEAR:   INTEGER;
                ABSOLUTE: INTEGER;
                END;

VAR     MO:     MONTHASCII;
        MONTH:  MONTHS;
        DA,YR,PLOTABS,BIRTHABS,JULIANDATE,DAYSALIVE: INTEGER;
        ERRORFLAG: BOOLEAN;
        FIRSTTIME: BOOLEAN;
        MAXDAY,I,MONTHSTOPLOT:  INTEGER;
        CURRENTLINE:    LINEARRAY;
        SINVAL33,SINVAL28,SINVAL23: INTEGER;
        BDATE,PDATE: DATES;


FUNCTION XROUND (INPUT:REAL): INTEGER;
VAR ROUNDX: INTEGER;
BEGIN
IF INPUT >= 0 THEN ROUNDX:= TRUNC(INPUT + 0.5)
ELSE ROUNDX:= TRUNC(INPUT - 0.5);
XROUND:= ROUNDX;
END;

PROCEDURE SINVALUE (PERIOD,DAYSALIVE,DAYOFMONTH:INTEGER;
                                VAR SINCHAREQUIV:INTEGER);
VAR PERIODFRACTION: REAL;
    STARTOFFSET: INTEGER;
BEGIN
STARTOFFSET:= DAYSALIVE MOD PERIOD;
PERIODFRACTION:= ((STARTOFFSET + DAYOFMONTH - 1) / PERIOD) * 2 * PI;
SINCHAREQUIV:= XROUND((SIN(PERIODFRACTION) + 1) * MAXCHAR) DIV 2;
END;

PROCEDURE JULIAN(MONTH: MONTHS;DA: INTEGER;VAR JULIANDATE: INTEGER);
BEGIN
CASE MONTH OF
        MAR: BEGIN MAXDAY:= 31;JULIANDATE:= DA; END;
        APR: BEGIN MAXDAY:= 30;JULIANDATE:= DA + 31; END;
        MAY: BEGIN MAXDAY:= 31;JULIANDATE:= DA + 61; END;
        JUN: BEGIN MAXDAY:= 30;JULIANDATE:= DA + 92; END;
        JUL: BEGIN MAXDAY:= 31;JULIANDATE:= DA + 122; END;
        AUG: BEGIN MAXDAY:= 31;JULIANDATE:= DA + 153; END;
        SEP: BEGIN MAXDAY:= 30;JULIANDATE:= DA + 184; END;
        OCT: BEGIN MAXDAY:= 31;JULIANDATE:= DA + 214; END;
        NOV: BEGIN MAXDAY:= 30;JULIANDATE:= DA + 245; END;
        DEC: BEGIN MAXDAY:= 31;JULIANDATE:= DA + 275; END;
        JAN: BEGIN MAXDAY:= 31;JULIANDATE:= DA + 306; END;
        FEB: BEGIN
             IF YR MOD 4 = 3 THEN MAXDAY:= 29
             ELSE MAXDAY:= 28;
             JULIANDATE:= DA + 337;
             END;
        END;
END;

PROCEDURE ERROR ( STRING: STRINGTYPE);
BEGIN
WRITELN ('ERROR IN ENTRY OF ', STRING);
ERRORFLAG:= TRUE;
END;

PROCEDURE ABSOLUTEDATE (VAR DATE:DATES);
VAR JULIANDATE: INTEGER;
    MONTHERROR,YEARERROR: BOOLEAN;
BEGIN
MONTHERROR:= FALSE;
YEARERROR:= FALSE;
WITH DATE DO
    BEGIN
    IF FIRSTTIME THEN
        BEGIN
        IF MO = 'MAR' THEN MONTH:= MAR
        ELSE IF MO = 'APR' THEN MONTH:= APR
        ELSE IF MO = 'MAY' THEN MONTH:= MAY
        ELSE IF MO = 'JUN' THEN MONTH:= JUN
        ELSE IF MO = 'JUL' THEN MONTH:= JUL
        ELSE IF MO = 'AUG' THEN MONTH:= AUG
        ELSE IF MO = 'SEP' THEN MONTH:= SEP
        ELSE IF MO = 'OCT' THEN MONTH:= OCT
        ELSE IF MO = 'NOV' THEN MONTH:= NOV
        ELSE IF MO = 'DEC' THEN MONTH:= DEC
        ELSE IF MO = 'JAN' THEN MONTH:= JAN
        ELSE IF MO = 'FEB' THEN MONTH:= FEB
        ELSE MONTHERROR:= TRUE;
        END;
    IF MONTH > DEC THEN YR:= YEAR - 1 ELSE YR:= YEAR;
    IF YR < 0 THEN YEARERROR:= TRUE;
    ABSOLUTE:= YR DIV 4 * 1461 + YR MOD 4 * 365;
    END;
IF MONTHERROR THEN ERROR ('MONTH     ');
IF YEARERROR THEN ERROR ('YEAR      ');
JULIAN (DATE.MONTH,DATE.DAY,JULIANDATE);
WITH DATE DO ABSOLUTE:= ABSOLUTE + JULIANDATE;
END;

PROCEDURE SORT (VAR INOUT: LINEARRAY);
VAR     TEMPHOLDER:     PLOTVALUE;
        I:              INTEGER;
BEGIN
FOR I:= 1 TO 3 DO
        BEGIN
        IF INOUT[ I ].SPACENUMBER > INOUT[ I+1 ].SPACENUMBER THEN
                BEGIN
                TEMPHOLDER:= INOUT[ I ];
                INOUT[ I ]:= INOUT[ I+1 ];
                INOUT[ I+1 ]:= TEMPHOLDER;
                IF I >= 2 THEN I:= I - 2;
                END;
        END;
END;

PROCEDURE LINEARRANGE (VAR INOUT: LINEARRAY);
VAR I: INTEGER;
BEGIN
FOR I:= 4 DOWNTO 2 DO
    BEGIN
    INOUT[I].SPACENUMBER:=
                INOUT[I].SPACENUMBER - INOUT[I-1].SPACENUMBER;
    IF INOUT[I].SPACENUMBER = 0 THEN
        BEGIN
        IF (INOUT[I].PLOTKIND<>':')&(INOUT[I-1].PLOTKIND<>':') THEN
            BEGIN
            INOUT[I].PLOTKIND:= 'X';
            INOUT[I-1].PLOTKIND:= 'X';
            END
        ELSE
            BEGIN
            IF INOUT[I].PLOTKIND = ':' THEN INOUT[I].PLOTKIND:=
                                        INOUT[I-1].PLOTKIND
            ELSE INOUT[I-1].PLOTKIND:= INOUT[I].PLOTKIND;
            END;
        END;
    END;
WITH INOUT[1] DO SPACENUMBER:= SPACENUMBER + 2;
END;

PROCEDURE MAKESPACES (NUMBER:INTEGER);
VAR I: INTEGER;
BEGIN
FOR I:= 1 TO NUMBER DO WRITE(' ');
END;

PROCEDURE MAKELINEFEEDS (NUMBER:INTEGER);
VAR I: INTEGER;
BEGIN
FOR I:= 1 TO NUMBER DO WRITELN;
END;

PROCEDURE MAKESIGNS;
VAR I: INTEGER;
BEGIN
MAKESPACES (MAXCHAR DIV 4 + 3);
WRITE('-');
MAKESPACES (MAXCHAR DIV 2);
WRITELN('+');
END;

PROCEDURE MAKECOLON;
VAR I: INTEGER;
BEGIN
MAKESPACES (MAXCHAR DIV 2 + 3);
WRITELN(':');
END;

PROCEDURE COMPUTE (DAYSALIVE,DA:INTEGER;VAR CURRENTLINE: LINEARRAY);
BEGIN
SINVALUE (33,DAYSALIVE,DA,SINVAL33);
SINVALUE (28,DAYSALIVE,DA,SINVAL28);
SINVALUE (23,DAYSALIVE,DA,SINVAL23);
WITH CURRENTLINE[1] DO
        BEGIN
        SPACENUMBER:= SINVAL33;
        PLOTKIND:= 'I';
        END;
WITH CURRENTLINE[2] DO
        BEGIN
        SPACENUMBER:= SINVAL28;
        PLOTKIND:= 'E';
        END;
WITH CURRENTLINE[3] DO
        BEGIN
        SPACENUMBER:= SINVAL23;
        PLOTKIND:= 'P';
        END;
WITH CURRENTLINE[4] DO
        BEGIN
        SPACENUMBER:= MAXCHAR DIV 2;
        PLOTKIND:= ':';
        END;
END;

PROCEDURE PRINTCHART;
VAR DA:INTEGER;
    I:INTEGER;
BEGIN
IF FIRSTTIME THEN I:= 10 ELSE I:= 2;
MAKELINEFEEDS (I);
IF FIRSTTIME THEN WRITELN('BIRTHDATE = ',
            BDATE.DAY:2,' ',BDATE.MON,' ',(BDATE.YEAR + 1900):4);
WRITELN('PLOT FOR THE MONTH OF ',PDATE.MON,' ',
                        (PDATE.YEAR + 1900):4);
IF FIRSTTIME THEN MAKESIGNS;
MAKECOLON;
FOR DA:= 1 TO MAXDAY DO
        BEGIN
        COMPUTE (DAYSALIVE,DA,CURRENTLINE);
        SORT(CURRENTLINE);
        LINEARRANGE(CURRENTLINE);
        WRITE(DA:2);
        FOR I:= 1 TO 4 DO
          BEGIN
          MAKESPACES(CURRENTLINE[I].SPACENUMBER - 1);
          IF CURRENTLINE[I].SPACENUMBER <> 0 THEN
                WRITE(CURRENTLINE[I].PLOTKIND);
          END;
        WRITELN;
        MAKECOLON;
        END;
END;

PROCEDURE MONTHDECODE (INPUTMONTH:MONTHS;VAR MONTH:MONTHASCII);
BEGIN
CASE INPUTMONTH OF
        JAN:    MONTH:= 'JAN';
        FEB:    MONTH:= 'FEB';
        MAR:    MONTH:= 'MAR';
        APR:    MONTH:= 'APR';
        MAY:    MONTH:= 'MAY';
        JUN:    MONTH:= 'JUN';
        JUL:    MONTH:= 'JUL';
        AUG:    MONTH:= 'AUG';
        SEP:    MONTH:= 'SEP';
        OCT:    MONTH:= 'OCT';
        NOV:    MONTH:= 'NOV';
        DEC:    MONTH:= 'DEC';
     END;
END;

(* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX *)
BEGIN
FIRSTTIME:= TRUE;
ERRORFLAG:= FALSE;
WRITE('BIRTHDATE? ');
READLN(DA,MO,YR);
WITH BDATE DO
    BEGIN
    DAY:= DA;
    MON:= MO;
    YEAR:= YR;
    END;
ABSOLUTEDATE(BDATE);
IF   (DA < 1) OR (DA > MAXDAY)   THEN ERROR ('DATE      ');
WRITE('MONTH TO START PLOT? ');
READLN(MO,YR);
WITH PDATE DO
    BEGIN
    DAY:= 1;
    MON:= MO;
    YEAR:= YR;
    END;
ABSOLUTEDATE(PDATE);
DAYSALIVE:= PDATE.ABSOLUTE - BDATE.ABSOLUTE;
IF DAYSALIVE < 0 THEN ERROR ('PLOT MONTH');
WRITE('NUMBER OF MONTHS TO PLOT? ');
READLN(MONTHSTOPLOT);
FOR I:= 1 TO MONTHSTOPLOT DO
    BEGIN
    IF NOT ERRORFLAG THEN PRINTCHART;    FIRSTTIME:= FALSE;
    WITH PDATE DO
        BEGIN
        IF MONTH = DEC THEN YEAR:= YEAR + 1;
        IF MONTH <> FEB THEN MONTH:= SUCC(MONTH)
        ELSE MONTH:= MAR;
        END;
    MONTHDECODE (PDATE.MONTH,PDATE.MON);
    ABSOLUTEDATE (PDATE);
    DAYSALIVE:= PDATE.ABSOLUTE - BDATE.ABSOLUTE;
    END;
END.
