       REM PERT CHART GENERATION PROGRAM 4/6/81
       REM ACCEPTS LIST OF:
       REM 1) ACTIVITIES AND RESOURCES REQUIRED TO PERFORM THEM
       REM 2) LIST OF ACTIVITIES REQUIRED TO COMPLETE AN ACTIVITY
       REM 3) RENEWABLE RESOURCES AND EXCHANGEABLE RESOURCES

       REM PRODUCES A PERT CHART BASED ON SAME
       REM AN ACTIVITY CONSISTS OF AN ACTIVITY NAME, AND
       REM A SET OF RESOURCES REQUIRED TO PERFORM THAT ACTIVITY
       REM A "MILESTONE" IS EASILY SIMULATED USING AN ACTIVITY
       REM THAT TAKES 0 TIME AND 0 RESOURCES
       REM ?? ACTIVITIES MAY ALSO HAVE PRIORITIES ATTACHED
       REM RESOURCES MAY BE SPECIFIED IN WHOLE OR PARTIAL UNITS
       REM A RESOURCE CONSISTS OF A NAME, AND A RENEWAL RATE FOR THAT RESOURCE
       REM (I.E., DOLLARS PER MONTH, ETC.)
       REM THIS PROGRAM DETERMINES THE OPTIMAL TIME ORDER FOR COMPLETION
       REM OF THE COMPLETE SET OF ACTIVITIES
       REM THE DATA SPECIFIED IS STORED IN A FILE FOR LATER RETREIVAL/EDITING

       REM INPUT FORMAT:
       REM .SYNTAX PERTCHARTINPUT
       REM PERTCHARTINPUT = "PERT DATA FOR PROJECT:" IDENTIFIER
       REM     $( ACTIVITYDEFN / RESOURCEDEFN )
       REM     "END." ;
       REM ACTIVITYDEFN = "ACTIVITY:" IDENTIFIER
       REM     ( *1?UNDEFINED *1=TYPEACTIVITY / *1?TYPEACTIVITY )
       REM     ( *1?TAGDEFINED ??'ACTIVITY NAME ALREADY DEFINED' /
       REM       .EMPTY *1+TAGDEFINED )
       REM     ( "DEADLINE" DATE / "COMPLETE" DATE / .EMPTY )
       REM     ( "NEEDS" ACTIVITYLIST "COMPLETE" / .EMPTY )
       REM     ( "CONSUMES" RESOURCESCONSUMED / .EMPTY ) '.' ;
       REM ACTIVITYLIST = ACTIVITYNAME $( CONJUNCTION ACTIVITYNAME ) ;
       REM ACTIVITYNAME = IDENTIFIER
       REM     ( *1?UNDEFINED *1=TYPEACTIVITY / *1?TYPEACTIVITY ) ;
       REM RESOURCESCONSUMED = RESOURCECONSUMED
       REM     $( CONJUNCTION RESOUCECONSUMED ) ;
       REM RESOURCECONSUMED = ( QUANTITYPERMONTH / .EMPTY )
       REM     ( RESOURCENAME ( "FOR" DURATION / .EMPTY ) / .EMPTY )
       REM     ( "MONTHS" / "DAYS " );
       REM RESOURCEDEFN = "RESOURCE:" IDENTIFIER
       REM     "RENEWED AT" RATE "PER" ( "MONTH" / "DAY" )
       REM     "EXCHANGABLE" "WITH" $( EXCHANGERATE RESOURCENAME )
       REM     ( "CONSUMES" RESOURCESCONSUMED / .EMPTY ) ;
       REM CONJUNCTION = "," / "AND" ;
REM WHAT ABOUT THE CASE WHERE A PROJECT HAS ONE-TIME COSTS, LIKE
REM BUYING A COMPUTER BEFORE THE PROJECT BEGINS????
REM NEED NOTATION LIKE ...CONSUMES <NUMBER> RESOURCE...
REM TO MEAN ONE-TIME CONSUMPTION REQUIREMENT
REM WHAT ABOUT ALTERNATIVE IMPLEMENTATIONS, TOO ?
REM I.E., XXX CONSUMES AAA OR BBB.

       REM SAMPLE INPUT:
       REM    PERT DATA FOR PROJECT: X
       REM    ACTIVITY: DONE DEADLINE 6/1/80
       REM    NEEDS DOCUMENTATION AND DEVELOPMENT COMPLETE.
       REM    ACTIVITY: DESIGNCOMPLETE NEEDS RESEARCH COMPLETE.
       REM    ACTIVITY: FUNDING COMPLETE 10/15/79.
       REM    ACTIVITY: RESEARCH NEEDS FUNDING COMPLETE,
       REM    CONSUMES DESIGNER FOR 2.5 MONTHS, 4000 MONEY
       REM    AND LABORATORY FOR 1 MONTH.
       REM    ACTIVITY: DEVELOPMENT NEEDS DESIGNCOMPLETE,
       REM    CONSUMES WORKER FOR 4 MONTHS, TOOLS FOR 2 MONTHS.
       REM    ACTIVITY: DOCUMENTATION NEEDS DESIGNCOMPLETE,
       REM    CONSUMES WRITER FOR 2 MONTHS, TOOLS FOR 1 MONTH.
       REM    RESOURCE: LABORATORY RENEWED AT 1 PER MONTH.
       REM    RESOURCE: DESIGNER RENEWED AT 0 PER MONTH,
       REM    EXCHANGEABLE WITH MOSTSKILLEDWORKER AT 1 PER MONTH.
       REM    RESOURCE: WORKER RENEWED AT 0 PER MONTH,
       REM    EXCHANGEABLE WITH MOSTSKILLEDWORKER AT .5 PER MONTH,
       REM    EXCHANGEABLE WITH ORDINARYWORKER AT 2 PER MONTH.
       REM    RESOURCE: MONEY RENEWED AT 0 PER MONTH, INITIAL VALUE 100000.
       REM    RESOURCE: WRITER RENEWED AT 0 PER MONTH,
       REM    EXCHANGEABLE WITH MOSTSKILLEDWORKER AT .25 PER MONTH,
       REM    EXCHANGEABLE WITH ORDINARYWORKER AT 1 PER MONTH.
       REM    RESOURCE: MOSTSKILLEDWORKER RENEWED AT 1 PER MONTH,
       REM    CONSUMES 4000 MONEY PER MONTH.
       REM    RESOURCE: ORDINARYWORKER RENEWED AT 2 PER MONTH,
       REM    CONSUMES 1500 MONEY PER MONTH.
       REM    RESOURCE: TOOLS RENEWED AT 1 PER MONTH,
       REM    CONSUMES 500 MONEY PER MONTH, .2 ORDINARYWORKER PER MONTH.
       REM    END.

       REM ACTIVITIES ARRAY: AN ARRAY OF DATA STORING ALL PERTINANT INFO
       REM ABOUT AN ACTIVITY. EACH ROW REPRESENTS ONE ACTIVITY.
       REM THE INFORMATION IN A ROW CONSISTS OF: DEADLINE, PRIORITY, RESOURCES USED
       REM RESOURCES ARRAY: AN ARRAY OF DATA STORING ALL PERTINANT DATA


dim tokenidentifier/1/,tokennumber/2/
DIM TOKENRESOURCE/3/,TOKENEXCHANGEABLE/4/,TOKENWITH/5/,TOKENRENEWED/6/
DIM TOKENCONSUMES/7/,TOKENPER/8/,TOKENAT/9/,TOKENMONTH/10/,TOKENMONTHS/11/
DIM TOKENEND/12/,TOKENACTIVITY/13/,TOKENDEADLINE/14/,TOKENCOMPLETE/15/
DIM TOKENAND/16/,TOKENNEEDS/17/,TOKENFOR/18/,TOKENDAY/19/,TOKENDAYS/20/

DIM PROJECTNAME$(40)
DIM NACTIVITIES/0/,MAXACTIVITIES/20/,ACTIVITYNAME$(20)[32],ACTIVITYDEFINED(20)
!DIM ACTIVITYSLOP(20)
dim ACTIVITYMINTIME(20),ACTIVITYAVGTIME(20),ACTIVITYMAXTIME(20)

DIM BESTSOLNSTARTDATE(20),BESTSOLNSTOPDATE(20)
DIM BESTSOLNRESOURCEQTY(20,10),BESTSOLNRESOURCETIME(20,10)
DIM THISSOLNSTARTDATE(20),THISSOLNSTOPDATE(20)
DIM THISSOLNRESOURCEQTY(20,10),THISSOLNRESOURCETIME(20,10)

DIM nresources/1/,MAXRESOURCES/10/,RESOURCENAME$(10)[32],RESOURCEDEFINED(10)
REM RESOURCE #1 IS ALWAYS "DAYS"
DIM RESOURCERENEWALRATE(10),RESOURCEPERISHRATE(10),RESOURCEINITIAL(10)
DIM RESOURCEREQDQTY(20,10),RESOURCEREQDTIME(20,10),RESOURCEEXCHANGE(20,10)

! DIM ACTIVITYDEPENDENCE(20,20),TRANSITIVEDEPENDENCE(20,20)
! ?? CAN'T THESE TWO MATRICES BE THE SAME??
DIM ACTIVITYDEPENDENCE$(1000)

DIM OBJECTREJECTED/0/,OBJECTSTRING$(32)
DIM INPUTLINE$(100),ERRORLINE$(100)
DIM ALPHANUM$/"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"/,DIGITS$/"0123456789."/
DIM PUNCTUATION$/:9," .,:"/
DIM IN/1/,OUT/2/,infinity/1e125/

DIM TEMP$(50)

DEF ACTIVITYDEPENDENCE(ACTIVITYDEPENDENCEROW,ACTIVITYDEPENDENCECOLUMN)
    RETURN ACTIVITYDEPENDENCE$[ACTIVITYDEPENDENCEROW*MAXACTIVITIES+...
&                              ACTIVITYDEPENDENCECOLUMN]&1
END

SUBROUTINE SETACTIVITYDEPENDENCE(SETACTIVITYDEPENDENCEROW,...
&                                SETACTIVITYDEPENDENCECOLUMN,...
&                                SETACTIVITYDEPENDENCENEWVALUE)
    TEMP=SETACTIVITYDEPENDENCEROW*MAXACTIVITIES+SETACTIVITYDEPENDENCECOLUMN
    ACTIVITYDEPENDENCE$[TEMP]=...
&       (ACTIVITYDEPENDENCE$[TEMP]&:FE)!SETACTIVITYDEPENDENCENEWVALUE
    RETURN SUBROUTINE
END

DEF TRANSITIVEDEPENDENCE(TRANSITIVEDEPENDENCEROW,TRANSITIVEDEPENDENCECOLUMN)
    RETURN (ACTIVITYDEPENDENCE$[TRANSITIVEDEPENDENCEROW*MAXACTIVITIES+...
&                               TRANSITIVEDEPENDENCECOLUMN]&2)/2
END

SUBROUTINE SETTRANSITIVEDEPENDENCE(SETTRANSITIVEDEPENDENCEROW,...
&                                SETTRANSITIVEDEPENDENCECOLUMN,...
&                                SETTRANSITIVEDEPENDENCENEWVALUE)
    TEMP=SETTRANSITIVEDEPENDENCEROW*MAXACTIVITIES+...
&        SETTRANSITIVEDEPENDENCECOLUMN
    ACTIVITYDEPENDENCE$[TEMP]=...
&       (ACTIVITYDEPENDENCE$[TEMP]&:FD)!(SETTRANSITIVEDEPENDENCENEWVALUE*2)
    RETURN SUBROUTINE
END

def resourcesavailable(aa,bb)
    print "notimplemented!"
    error 9876
end

DEF KEYWORDMATCH( KEYWORD$, VALUE )
    IF FIND(INPUTLINE$,KEYWORD$)=1 ...
&   AND ( LEN(INPUTLINE$)=LEN(KEYWORD$) OR...
&         NOT FIND(ALPHANUM$,INPUTLINE$(LEN(KEYWORD$)+1,1)) )
    THEN
        LET INPUTLINE$=RIGHT$(INPUTLINE$,LEN(KEYWORD$)+1)
        LET OBJECTTYPE=VALUE
        RETURN TRUE
    ELSE RETURN FALSE
END

SUBROUTINE ERRORDISPLAY(ERRORMESSAGE$)
    PRINT ERRORMESSAGE$
    PRINT ERRORLINE$
    PRINT TAB(LEN(ERRORLINE$)-LEN(INPUTLINE$)+1);"^"
    EXIT
END

DEF GETOBJECT1
       REM GETOBJECT: READ NEXT OBJECT FROM PERT INPUT FILE
       REM RETURNS WITH:
       REM     IF (NON-KEYWORD) IDENTIFIER
       REM     OBJECTTYPE=1, OBJECTSTRING$ = STRING
       REM     IF NUMBER OR DATE
       REM     OBJECTTYPE=2, OBJECTVALUE = VALUE OF NUMBER (Y*1000+M*100+D)
       REM     IF KEYWORD:   OBJECTVALUE
       REM        RESOURCE        3
       REM        EXCHANGEABLE    4
       REM        WITH            5
       REM        RENEWED         6
       REM        CONSUMES        7
       REM        PER             8
       REM        AT              9
       REM        MONTH          10
       REM        MONTHS         11
       REM        END            12
       REM        ACTIVITY       13
       REM        DEADLINE       14
       REM        COMPLETE       15
       REM        WHEN           16
       REM        NEEDS          17
       REM        FOR            18
       REM        DAY            19
       REM        DAYS           20
       REM
       REM     IF PUNCTUATION : , .
       REM     THEN OBJECTVALUE = ASCII CODE FOR PUNCTUATION
       REM     IF SPACE, TAB OR <CR>, THEN IGNORE IT.
       REM     OTHERWISE COMPLAIN AND EXIT.
       IF OBJECTREJECTED
       THEN
           OBJECTREJECTED=FALSE
           RETURN OBJECTTYPE
       FI
SKIPBLANKS:
       WHILE LEN(INPUTLINE$)=0 DO
           INPUT #IN,INPUTLINE$
           LET ERRORLINE$=INPUTLINE$ \ REM SAVE THE LINE IN CASE AN ERROR OCCURS
           IF EOF(IN) THEN ERROR 1001 FI
           LET INPUTLINE$=UPPERCASE$(INPUTLINE$)
       END
       IF FIND(PUNCTUATION$,INPUTLINE$(1,1))
       THEN
           OBJECTTYPE=INPUTLINE$(1)
           LET INPUTLINE$=RIGHT$(INPUTLINE$,2)
           IF OBJECTTYPE=ASC(" ") OR OBJECTTYPE=:9 THEN SKIPBLANKS
           ELSE RETURN OBJECTTYPE
       FI
       REM NOT PUNCTUATION OR BLANKS, TRY FOR KEYWORD
       IF KEYWORDMATCH("RESOURCE",TOKENRESOURCE) OR...
&         KEYWORDMATCH("EXCHANGEABLE",TOKENEXCHANGEABLE) OR...
&         KEYWORDMATCH("WITH",TOKENWITH) OR...
&         KEYWORDMATCH("RENEWED",TOKENRENEWED) OR...
&         KEYWORDMATCH("CONSUMES",TOKENCONSUMES) OR...
&         KEYWORDMATCH("PER",TOKENPER) OR...
&         KEYWORDMATCH("AT",TOKENAT) OR...
&         KEYWORDMATCH("MONTH",TOKENMONTH) OR...
&         KEYWORDMATCH("MONTHS",TOKENMONTHS) OR...
&         KEYWORDMATCH("END",TOKENEND) OR...
&         KEYWORDMATCH("ACTIVITY",TOKENACTIVITY) OR...
&         KEYWORDMATCH("DEADLINE",TOKENDEADLINE) OR...
&         KEYWORDMATCH("COMPLETE",TOKENCOMPLETE) OR...
&         KEYWORDMATCH("AND",TOKENAND) OR...
&         KEYWORDMATCH("NEEDS",TOKENNEEDS) OR...
&         KEYWORDMATCH("FOR",TOKENFOR) OR...
&         KEYWORDMATCH("DAYS",TOKENDAYS) OR...
&         KEYWORDMATCH("DAY",TOKENDAY) THEN RETURN OBJECTTYPE
       REM NOT A KEYWORD, TRY FOR NUMBER OR SYMBOL.
       IF ERROR WHEN OBJECTVALUE=VAL(INPUTLINE$)
       THEN
           REM IT MUST BE THE NAME OF SOMETHING
           OBJECTTYPE=TOKENIDENTIFIER
           IF NOT FIND(ALPHANUM$,INPUTLINE$(1,1))
           THEN ERRORDISPLAY("ILLEGAL CHARACTER")
           FOR I=2 TO LEN(INPUTLINE$) WHILE FIND(ALPHANUM$,INPUTLINE$(I,1))
           END
           LET OBJECTSTRING$=INPUTLINE$(1,I-1)
           LET INPUTLINE$=RIGHT$(INPUTLINE$,I)
           RETURN OBJECTTYPE
       ELSE
           REM ITS A NUMBER! EAT UP FOLLOWING "/" IF IT IS A DATE
           OBJECTTYPE=TOKENNUMBER
           FOR I=1 TO LEN(INPUTLINE$) WHILE FIND(DIGITS$,INPUTLINE$(I,1))
           END
           LET INPUTLINE$=RIGHT$(INPUTLINE$,I)
           REM CHECK FOR DATE
           IF LEN(INPUTLINE$)>=1 AND INPUTLINE$(1,1)="/"
           THEN
               IF ERROR WHEN
                  OBJECTVALUE=OBJECTVALUE*100+VAL(RIGHT$(INPUTLINE$,2))
              THEN ERRORDISPLAY("ILLEGAL DATE!")
              ELSE
                  FOR I=2 TO LEN(INPUTLINE$)...
&                     WHILE FIND(DIGITS$,INPUTLINE$(I,1)) DO REM
                  LET INPUTLINE$=RIGHT$(INPUTLINE$,I)
              FI
           FI
           RETURN OBJECTTYPE
       FI
END

DEF GETOBJECT
    PRINT "GETOBJECT RETURNS OBJECTYPE=";GETOBJECT1
    RETURN OBJECTTYPE
END

SUBROUTINE GETACTIVITYNAME
    REM RETURNS ACTIVITYSELECTOR = INDEX OF ACTIVITY NAME, OR ERRORS
    IF GETOBJECT<>TOKENIDENTIFIER
    THEN ERRORDISPLAY("NEED ACTIVITY NAME HERE!")
    FOR ACTIVITYSELECTOR=1 TO NACTIVITIES
        IF ACTIVITYNAME$(ACTIVITYSELECTOR)=OBJECTSTRING$
        THEN RETURN SUBROUTINE
    NEXT ACTIVITYSELECTOR
    IF ACTIVITYSELECTOR>LEN(ACTIVITYNAME$)
    THEN ERRORDISPLAY("TOO MANY ACTIVITY NAMES!")
    LET NACTIVITIES=ACTIVITYSELECTOR
    LET ACTIVITYNAME$(ACTIVITYSELECTOR)=OBJECTSTRING$
    LET ACTIVITYDEFINED(ACTIVITYSELECTOR)=FALSE
    FOR RESOURCESELECTOR=1 TO COLUMNS(RESOURCEREQDQTY)
        REM MARK RESOURCES REQ'D AS NOT GIVEN
        RESOURCEREQDQTY(ACTIVITYSELECTOR,RESOURCESELECTOR)=0
        RESOURCEREQDTIME(ACTIVITYSELECTOR,RESOURCESELECTOR)=0
    NEXT RESOURCESELECTOR
    RETURN SUBROUTINE
END

SUBROUTINE GETRESOURCENAME
    REM RETURNS RESOURCESELECTOR = INDEX OF RESOURCE NAME, OR ERRORS
    IF GETOBJECT<>TOKENIDENTIFIER
    THEN ERRORDISPLAY("NEED RESOURCE NAME HERE!")
    FOR RESOURCESELECTOR=2 TO NRESOURCES
        REM RESOURCE #1 IS IMPLICITLY "TIME"
        IF RESOURCENAME$(RESOURCESELECTOR)=OBJECTSTRING$
        THEN RETURN SUBROUTINE
    NEXT RESOURCESELECTOR
    IF RESOURCESELECTOR>LEN(RESOURCENAME$)
    THEN ERRORDISPLAY("TOO MANY RESOURCE NAMES!")
    LET NRESOURCES=RESOURCESELECTOR
    LET RESOURCENAME$(RESOURCESELECTOR)=OBJECTSTRING$
    LET RESOURCEDEFINED(RESOURCESELECTOR)=FALSE
    RETURN SUBROUTINE
END

SUBROUTINE VERIFYTOKEN(VERIFYOBJECT)
    IF GETOBJECT<>VERIFYOBJECT
    THEN ERRORDISPLAY("SYNTAX ERROR")
    ELSE RETURN SUBROUTINE
END

SUBROUTINE GETRESOURCECONSUMED
    IF GETOBJECT<>TOKENNUMBER
    THEN
        REM NO QUANTITY --> UNIT QUANTITY
        OBJECTREJECTED=TRUE
        OBJECTVALUE=1
    FI
    LET RESOURCEQTYCONSUMED=OBJECTVALUE
    IF GETOBJECT=TOKENDAY OR OBJECTTYPE=TOKENDAYS...
&   OR OBJECTTYPE=TOKENMONTH OR OBJECTTYPE=TOKENMONTHS
    THEN
        REM RESOURCE BEING USED IS "DAYS"
        IF OBJECTTYPE=TOKENMONTH OR OBJECTTYPE=TOKENMONTHS
        THEN RESOURCETIMECONSUMED=RESOURCEQTYCONSUMED*30
        ELSE RESOURCETIMECONSUMED=RESOURCEQTYCONSUMED
        LET RESOURCEQTYCONSUMED=1
        RESOURCESELECTOR=1
    ELSE
        OBJECTREJECTED=TRUE
        GETRESOURCENAME
        IF GETOBJECT=TOKENFOR
        THEN
            IF GETOBJECT<>TOKENNUMBER
            THEN ERRORDISPLAY("MUST SPECIFY DURATION OF RESOURCE USAGE")
            RESOURCETIMECONSUMED=OBJECTVALUE
            IF GETOBJECT=TOKENDAY OR OBJECTTYPE=TOKENDAYS
            THEN REM DURATION IS  "DAYS", NO SCALING NEEDED
            ELSEIF OBJECTTYPE=TOKENMONTH OR OBJECTTYPE=TOKENMONTHS
            THEN RESOURCETIMECONSUMED=RESOURCETIMECONSUMED*30
            ELSE ERRORDISPLAY("MUST SPECIFY 'DAYS' OR 'MONTHS'")
        ELSE
            REM NO DURATION SPECIFIED, MUST BE ONE-SHOT RESOURCE RQMT
            OBJECTREJECTED=TRUE
            RESOURCETIMECONSUMED=0
        FI
    FI
    REM RECORD RESOURCE CONSUMED
    IF RESOURCEQTYCONSUMED=0
    THEN ERRORDISPLAY("ILLEGAL RESOURCE REQUIREMENT")
    IF RESOURCEREQDQTY(ACTIVITYNEW,RESOURCESELECTOR)>0
    THEN ERRORDISPLAY("RESOURCE QUANTITY REQUIRED ALREADY SPECIFIED")
    RESOURCEREQDQTY(ACTIVITYNEW,RESOURCESELECTOR)=RESOURCEQTYCONSUMED
    RESOURCEREQDTIME(ACTIVITYNEW,RESOURCESELECTOR)=RESOURCETIMECONSUMED
    RETURN SUBROUTINE
END

PERTCHART: REM THIS IS WHERE THE MAIN PROGRAM STARTS
    FOR I=1 TO LEN(ACTIVITYDEFINED) DO ACTIVITYDEFINED[I]=FALSE
    REM LOCATE THE INPUT AND OUTPUT FILES
    IF COL(0)=1
    THEN
        REM PROMPT TO GET DATA
        PRINT "PERT CHART GENERATOR V1.0 (C) 1981 SOFTWARE DYNAMICS"
        INPUT "PERT DATA INPUT: " INPUTLINE$
        OPEN #IN,INPUTLINE$
        INPUT "PERT CHART OUTPUT: " INPUTLINE$
        IF INPUTLINE$=""
        THEN LET OUT=0
        ELSE CREATE #OUT,INPUTLINE$
    ELSE
        REM EAT COMMAND LINE TO GET REQUIRED DATA
        INPUT '' INPUTLINE$
        PRINT "PERT CHART GENERATOR V1.0 (C) 1981 SOFTWARE DYNAMICS"
        LET INPUTLINE$=UPPERCASE$(INPUTLINE$)
        OPEN #IN,INPUTLINE$
        IF FIND(INPUTLINE$," TO ")
        THEN CREATE #OUT,RIGHT$(INPUTLINE$,FIND(INPUTLINE$," TO ")+4)
        ELSE LET OUT=0
    FI
    REM NOW PROCESS THE PERT CHART DESCRIPTION
    REM PICK OFF THE PROJECT NAME
    INPUT #IN,INPUTLINE$
    LET ERRORLINE$=INPUTLINE$ \ ! IN CASE OF ERROR
    LET INPUTLINE$=UPPERCASE$(INPUTLINE$)
    IF FIND(INPUTLINE$,"PERT DATA FOR PROJECT: ")<>1
    THEN ERRORDISPLAY("FIRST LINE MUST BE 'PERT DATA FOR PROJECT: XXX'")
    LET PROJECTNAME$=RIGHT$(ERRORLINE$,24)
    PRINT TIME$;" START ANALYSIS OF PERT PROJECT: ";PROJECTNAME$
    LET INPUTLINE$="" \ ! FORCE READ OF NEXT LINE FROM FILE
    REM MARK "NO ACTIVITY DEPENDENCIES"
    FOR I=1 TO MAXACTIVITIES
        FOR J=1 TO MAXACTIVITIES
            LET ACTIVITYDEPENDENCE(I,J)=0
        NEXT J
    NEXT I
    LET RESOURCENAME$(1)="DAYS"
INPUTNEXTDEFINITION: REM INPUT A DEFINITION AND PROCESS IT
    PRINT "READING A DEFINITION!"
    IF GETOBJECT=TOKENACTIVITY
    THEN
        REM PROCESS ACTIVITY DEFINITION
        VERIFYTOKEN(ASC(":"))
        GETACTIVITYNAME
        IF ACTIVITYDEFINED(ACTIVITYSELECTOR)
        THEN ERRORDISPLAY("ACTIVITY NAME ALREADY DEFINED")
        REM ACTIVITYNEW IS ASSIGNED ACTIVITY NUMBER
        LET ACTIVITYNEW=ACTIVITYSELECTOR
        LET ACTIVITYDEFINED(ACTIVITYNEW)=TRUE
        IF GETOBJECT=TOKENDEADLINE
        THEN REM EAT UP DEADLINE DATE
        ELSEIF OBJECTTYPE=TOKENCOMPLETE
        THEN REM EAT UP COMPLETION DATE
        ELSE OBJECTREJECTED=TRUE
        IF GETOBJECT=TOKENNEEDS
        THEN
            REM GET LIST OF ACTIVITIES ON WHICH THIS ACTIVITY DEPENDS
            REPEAT
                GETACTIVITYNAME
                LET ACTIVITYDEPENDENCE(ACTIVITYNEW,ACTIVITYSELECTOR)=TRUE
            WHEN GETOBJECT=ASC(",") END
            IF OBJECTTYPE=TOKENAND
            THEN
                GETACTIVITYNAME
                LET ACTIVITYDEPENDENCE(ACTIVITYNEW,ACTIVITYSELECTOR)=TRUE
            ELSE OBJECTREJECTED=TRUE
            VERIFYTOKEN(TOKENCOMPLETE)
        ELSE OBJECTREJECTED=TRUE
        IF GETOBJECT=TOKENCONSUMES
        THEN
            REPEAT
                GETRESOURCECONSUMED
            WHEN GETOBJECT=ASC(",") END
            IF OBJECTTYPE=TOKENAND
            THEN CALL GETRESOURCECONSUMED
            ELSE OBJECTREJECTED=TRUE
        ELSE OBJECTREJECTED=TRUE
        VERIFYTOKEN(ASC("."))
        GOTO INPUTNEXTDEFINITION
    ELSEIF OBJECTTYPE=TOKENRESOURCE
    THEN
        VERIFYTOKEN(ASC(":"))
        REM PROCESS RESOURCE DEFINITION
       REM RESOURCEDEFN = "RESOURCE:" IDENTIFIER
       REM     "RENEWED AT" RATE "PER" "MONTH"
       REM     "EXCHANGABLE" "WITH" $( EXCHANGERATE RESOURCENAME )
       REM     ( "CONSUMES" RESOURCESCONSUMED / .EMPTY ) ;
       REM CONJUNCTION = "," / "AND" ;
        VERIFYTOKEN(ASC("."))
        GOTO INPUTNEXTDEFINITION
    ELSEIF OBJECTTYPE<>TOKENEND
    THEN ERRORDISPLAY("NOT AN ACTIVITY OR RESOURCE DEFINITION")
    VERIFYTOKEN(ASC("."))
    PRINT "PERT PROJECT DESCRIPTION READ AT ";TIME$
REM *******************************************************************************

SUBROUTINE COMPUTECLOSURE(RELATIONSIZE)
    REM THIS SUBROUTINE COMPUTES TRANSITIVE CLOSURE OF "TRANSITIVEDEPENDENCE"
    REM TRANSITIVEDEPENDENCE[I,J] SAYS "I HAS THE RELATION R TO J"
    REM USING "WARSHALL"'S ALGORITHM
    REM SEE DAVID GRIES, "COMPILER CONSTRUCTION FOR DIGITAL COMPUTERS"
    REM EXERCISE 2.7
    FOR CLOSUREI=1 TO RELATIONSIZE
        FOR CLOSUREJ=1 TO RELATIONSIZE
            IF TRANSITIVEDEPENDENCE(CLOSUREJ,CLOSUREI)
            THEN
                FOR CLOSUREK=1 TO RELATIONSIZE
                    REM A[J,K]=A[J,K]+A[I,K]
                    REM WHEN A[,] ARE ELEMENTS OF 0..1 AND + IS "OR"
                    REM THEN IF A[I,K] IS 1, A[J,K]=1 WORKS!
                    IF TRANSITIVEDEPENDENCE(CLOSUREI,CLOSUREK)
                    THEN LET TRANSITIVEDEPENDENCE(CLOSUREJ,CLOSUREK)=TRUE
                NEXT CLOSUREK
            FI
        NEXT CLOSUREJ
    NEXT CLOSUREI
    RETURN SUBROUTINE
END

FOR I=1 TO NRESOURCES
    RESOURCERENEWALRATE(I)=1
    RESOURCEPERISHRATE(I)=1
    RESOURCEINITIAL(I)=1
NEXT I

SUBROUTINE PRINTACTIVITYDEPENDENCE
    FOR I=1 TO NACTIVITIES
        PRINT ACTIVITYNAME$(I),
        FOR J=1 TO NACTIVITIES
            PRINT ACTIVITYDEPENDENCE(I,J);
        NEXT J
        PRINT
    NEXT I
    RETURN SUBROUTINE
END

SUBROUTINE PRINTRESOURCESREQD
    FOR I=1 TO NACTIVITIES
        PRINT ACTIVITYNAME$(I);" REQUIRES:"
        FOR J=1 TO NRESOURCES
            IF RESOURCEREQDQTY(I,J)>0
            THEN
                PRINT RESOURCEREQDQTY(I,J);"OF ";RESOURCENAME$(J);
                PRINT " FOR ";RESOURCEREQDTIME(I,J);" DAYS"
            FI
        NEXT J
    NEXT I
    RETURN SUBROUTINE
END

SUBROUTINE EXCHANGEACTIVITY(ACT1,ACT2)
    REM SWAP ACTIVITY ACT1 AND ACT2
    IF ACT1=ACT2 THEN RETURN SUBROUTINE
    REM FIRST, SWAP NAMES
    PRINT "EXCHANGING ACTIVITIES ";ACT1;"AND";ACT2
    LET TEMP$=ACTIVITYNAME$(ACT1)
    LET ACTIVITYNAME$(ACT1)=ACTIVITYNAME$(ACT2)
    LET ACTIVITYNAME$(ACT2)=TEMP$
    REM SWAP MIN TIMES
    TEMP=ACTIVITYMINTIME(ACT1)
    ACTIVITYMINTIME(ACT1)=ACTIVITYMINTIME(ACT2)
    ACTIVITYMINTIME(ACT2)=TEMP
    REM SWAP MAX TIMES
    TEMP=ACTIVITYMAXTIME(ACT1)
    ACTIVITYMAXTIME(ACT1)=ACTIVITYMAXTIME(ACT2)
    ACTIVITYMAXTIME(ACT2)=TEMP
    REM SWAP RESOURCE INFORMATION TIED TO ACTIVITY NUMBER
    FOR ACTJ=1 TO NRESOURCES
        REM SWAP RESOURCE REQ'D ITEM
        LET TEMP=RESOURCEREQDQTY(ACT1,ACTJ)
        LET RESOURCEREQDQTY(ACT1,ACTJ)=RESOURCEREQDQTY(ACT2,ACTJ)
        LET RESOURCEREQDQTY(ACT2,ACTJ)=TEMP
        REM SWAP RESOURCE REQ'D TIME
        LET TEMP=RESOURCEREQDTIME(ACT1,ACTJ)
        LET RESOURCEREQDTIME(ACT1,ACTJ)=RESOURCEREQDTIME(ACT2,ACTJ)
        LET RESOURCEREQDTIME(ACT2,ACTJ)=TEMP
    NEXT ACTJ
    REM SWAP ROWS IN ACTIVITY DEPENDENCY MATRICES
    FOR ACTJ=1 TO NACTIVITIES
        REM EXCHANGE ROWS OF ACTIVITY DEPENDENCE MATRIX
        LET TEMP=ACTIVITYDEPENDENCE(ACT1,ACTJ)
        LET ACTIVITYDEPENDENCE(ACT1,ACTJ)=ACTIVITYDEPENDENCE(ACT2,ACTJ)
        LET ACTIVITYDEPENDENCE(ACT2,ACTJ)=TEMP
        REM EXCHANGE ROWS OF TRANSITIVE ACTIVITY DEPENDENCE MATRIX
        LET TEMP=TRANSITIVEDEPENDENCE(ACT1,ACTJ)
        LET TRANSITIVEDEPENDENCE(ACT1,ACTJ)=...
&           TRANSITIVEDEPENDENCE(ACT2,ACTJ)
        LET TRANSITIVEDEPENDENCE(ACT2,ACTJ)=TEMP
    NEXT ACTJ
    REM NOW EXCHANGE COLUMNS TO MATCH
    FOR ACTJ=1 TO NACTIVITIES
        LET TEMP=ACTIVITYDEPENDENCE(ACTJ,ACT1)
        LET ACTIVITYDEPENDENCE(ACTJ,ACT1)=ACTIVITYDEPENDENCE(ACTJ,ACT2)
        LET ACTIVITYDEPENDENCE(ACTJ,ACT2)=TEMP
        LET TEMP=TRANSITIVEDEPENDENCE(ACTJ,ACT1)
        LET TRANSITIVEDEPENDENCE(ACTJ,ACT1)=...
&           TRANSITIVEDEPENDENCE(ACTJ,ACT2)
        LET TRANSITIVEDEPENDENCE(ACTJ,ACT2)=TEMP
    NEXT ACTJ
    RETURN SUBROUTINE
END

SUBROUTINE PRINTSOLUTION
    PRINT "ACTIVITY","START DAY","STOP DAY"
    FOR I=1 TO NACTIVITIES
        PRINT ACTIVITYNAME$(I),BESTSOLNSTARTDATE(I),BESTSOLNSTOPDATE(I)
    NEXT I
    RETURN SUBROUTINE
END

REM PERT CHART PROBLEM SOLN
REM GIVEN ACTIVITYDEPENDENCE BIT ARRAY
REM (ACTIVITYDEPENDENCE(I,J) = 1 --> ACTIVITY I DEPENDS ON ACTIVITY J COMPLETE)
REM ACTIVITIES ARE SORTED SUCH THAT FOR ALL ACTIVITIES I AND J,
REM IF I<J, THEN ACTIVITY I DOES NOT DEPEND ON ACTIVITY J.
REM THIS ALLOWS PROGRAM TO ASSIGN RESOURCES TO ACTIVITIES IN ASCENDING ORDER
REM AN ACTIVITY MUST WAIT UNTIL ALL RESOURCES REQUIRED BY IT ARE AVAILABLE
REM THEN THOSE RESOURCES ARE TIED UP FOR RESOURCEREQDTIME(...,J)
REM ACTIVITYSLOP(I) GIVES SLOP TIME ON ACTIVITY I
REM ACTIVITYMINTIME(I) GIVES AMOUNT OF TIME TO DO ACTIVITY I
REM ACTIVITYMAXTIME(I) GIVES MAXIMUM TIME TO DO ACTIVITY I
REM RESOURCESAVAILABLE(T,J) = AMOUNT OF RESOURCE J AVAILABLE AT TIME T
REM RESOURCERENEWALRATE(J) = RATE OF RENEWAL OF RESOURCE J PER UNIT TIME
REM RESOURCEREQDQTY(I,J) = HOW MANY UNITS OF RESOURCE J REQ'D BY ACTIVITY I
REM RESOURCEREQDTIME(I,J) = TIME THAT QTY UNITS OF RESOURCE ARE TIED UP
REM RESOURCEEXCHANGE(I,J) = RATE OF EXCHANGE OF RESOURCE I WITH J PER UNIT TIME
REM ACTIVITYNAME$(I) = TEXTUAL NAME OF ACTIVITY I
REM RESOURCENAME$(I) = TEXTUAL NAME OF RESOURCE I
REM BESTSOLNSTART(I) = BEST SOLN START DATE OF ACTIVITY I
REM BESTSOLNSTOPDATE(I) = BEST SOLN FINISH DATE OF ACTIVITY I
REM BESTSOLNRESOURCEQTY(I,J) = HOW MUCH RESOURCE J USED BY ACTIVITY I
REM BESTSOLNRESOURCETIME(I,J)= HOW LONG RESOURCE J USED BY ACTIVITY I
REM THISSOLNSTARTDATE(I) GIVES DATE OF START OF ACTIVITY I
REM THISSOLNSTOPDATE(I) GIVES DATE OF COMPLETION OF ACTIVITY I
REM (INITIAL VALUES ARE SET TO INFINITY)
REM THISSOLNRESOURCEQTY(I,J) = HOW MUCH RESOURCE J USED BY ACTIVITY I
REM THISSOLNRESOURCETIME(I,J)= HOW LONG RESOURCE J USED BY ACTIVITY I

PRINTACTIVITYDEPENDENCE
PRINT "SORTING TO OBTAIN ORDERING ON ACTIVITIES"
    REM COMPUTE TRANSITIVE CLOSURE OF ACTIVITY DEPENDENCE MATRIX
    REM READ TRANSITIVEDEPENDENCE(I,J) AS "I DEPENDS TRANSITIVELY ON J"
    FOR I=1 TO NACTIVITIES
        FOR J=1 TO NACTIVITIES
            TRANSITIVEDEPENDENCE(I,J)=ACTIVITYDEPENDENCE(I,J)
        NEXT J
    NEXT I
    COMPUTECLOSURE(NACTIVITIES)
    REM VERIFY NO CIRCULAR DEPENDENCIES
    FOR I=1 TO NACTIVITIES
        IF TRANSITIVEDEPENDENCE(I,I)
        THEN
            PRINT "ACTIVITY ";ACTIVITYNAME$(I); "DEPENDS ON ITSELF!"
            PRINT "PERT CHART GENERATION ABORTED."
            EXIT
        FI
    NEXT I
print "SORT THE ACTIVITYDEPENDENCE MATRIX SO IT HAS THE PROPERTY"
REM THAT I<J MEANS ACTIVITY I DOES NOT DEPEND (TRANSITIVELY) ON ACTIVITY J
REM WE HAVE VERIFIED THAT THERE ARE NO CYCLES IN DEPENDENCE GRAPH
REM --> THERE EXISTS AN ACTIVITY WHICH DEPENDS ON NO OTHERS!
    FOR I=1 TO NACTIVITIES-1
        REM PICK THE EARLIEST ACTIVITY FROM THE ONES REMAINING
        LET BESTACTIVITY=I
        FOR J=I+1 TO NACTIVITIES
            REM IF BEST ACTIVITY DEPENDS (TRANSITIVELY) ON ACTIVITY J
            REM THEN ACTIVITY J MUST BE "EARLIER" IN THE PERT CHART THAN "BEST"
            IF TRANSITIVEDEPENDENCE(BESTACTIVITY,J) THEN BESTACTIVITY=J
        NEXT J
        EXCHANGEACTIVITY(I,BESTACTIVITY)
    NEXT I
PRINT "PARTIAL ORDERING ESTABLISHED."
REM VERIFY THAT LAST ACTIVITY DEPENDS TRANSITIVELY ON ALL OTHERS
REM THIS ALSO VERIFIES THAT THE ACTIVITY DEPENDENCY RELATION IS A SINGLE GRAPH
FOR I=1 TO NACTIVITIES-1
    FOR J=1 TO NACTIVITIES
        IF ACTIVITYDEPENDENCE(J,I) THEN CYCLE I
    NEXT J
    PRINT "NO ACTIVITY DEPENDS ON ACTIVITY ";ACTIVITYNAME$(I)
    EXIT
NEXT I
PRINTACTIVITYDEPENDENCE
PRINTRESOURCESREQD
PRINT "TRYING SOLUTIONS."
REM EXHAUSTIVE SEARCH FOR SOLNS.
REM FOR EACH ACTIVITY, IN ORDER, GENERATE ALL POSSIBLE RESOURCE ASSIGNMENTS.
REM FOR EACH ASSIGNMENT, COMPUTE COMPLETION DATE.
REM IF BEST SOLN HAS A COMPLETION DATE EARLIER THAN THAT JUST COMPUTED,
REM AND BEST SOLN HAS AT LEAST AS MUCH RESOURCE OF EACH TYPE
REM AVAILABLE AT COMPLETION TIME JUST COMPUTED AS THIS SOLN,
REM THEN REJECT THE CURRENT RESOURCE ASSIGNMENT. OTHERWISE,
REM COMPUTE RESOURCE ASSIGNMENTS FOR NEXT ACTIVITY IN ORDER.
REM IF ALL ACTIVITIES HAVE RESOURCE ASSIGNMENTS, THEN
REM DETERMINE IF THIS SOLN IS BETTER THAN THE BEST SOLN.
REM IF SO, THEN SAVE IT; ELSE REJECT RESOURCE ASSIGNMENT
REM OF LAST ACTIVITY.
REM (THIS AMOUNTS TO A TREE SEARCH WITH PRUNING AT OBVIOUS CUTOFF POINTS)
    FOR I=1 TO NACTIVITIES DO BESTSOLNSTOPDATE(I)=INFINITY
    ACTIVITY=1
    GOSUB SATISFYRESTOFACTIVITIES
    REM NOW WE HAVE THE BEST SOLN, PRINT THE RESULTS.
    PRINT "*** PERT CHART SOLUTION ***"
    PRINTSOLUTION
    EXIT

DEF RESOURCEAVAILABLE(RESOURCEDESIRED,ACTIVITIESCOMPLETE,...
&                     WHENDESIRED,QTYDESIRED,DURATIONDESIRED)
    REM THIS FUNCTION RETURNS TRUE IF THE RESOURCEDESIRED...
    REM IS AVAILABLE WHENDESIRED FOR THE DURATIONDESIRED
    REM ASSUMING THAT ACTIVITIES 1 THRU ACTIVITIESCOMPLETE
    REM ALREADY HAVE THEIR RESOURCES ASSIGNED
    REM OTHERWISE RETURNS FALSE
    REM THIS IMPLEMENTATION ASSUMES NO SUBSTITUTIONS
    IF RESOURCEDESIRED=1 THEN RETURN TRUE \ ! TIME IS ALWAYS AVAILABLE
    IF QTYDESIRED=0 THEN RETURN TRUE \ ! ZERO RESOURCES ALWAYS AVAILABLE
REM THIS ROUTINE SHOULD BE GREATLY OPTIMIZED!
    REM COMPUTE RESOURCE QUANTITY AVAILABLE AT BEGINNING OF DATE 0
    LET RESOURCEQTYAVAILABLE=RESOURCEINITIAL(RESOURCEDESIRED)
    REM A "DATE" STARTS AT 12:01 A.M.
    REM AN ACTIVITY "STARTS" ON A DATE IF ALL RESOURCES ARE AVAILABLE AT 12:01
    REM A ONE DAY ACTIVITY COMPLETES AT THE START OF THE NEXT DATE
    REM RESOURCEREQDQTY IS AMOUNT OF RESOURCE USED PER DAY!
    REM RESOURCEREQDTIME IS THE NUMBER OF DAYS THAT ..QTY IS NEEDED
    REM SO TOTAL RESOURCE USAGE IS ...QTY*...TIME
    FOR DATE=0 TO WHENDESIRED-1
        REM COMPUTE AMOUNT USED BY ACTIVITIES ON THIS DATE
        AMOUNTUSEDBYACTIVITIES=0 \ ! FOR THIS PARTICULAR DATE
        FOR ACTIVITYAVL=1 TO ACTIVITIESCOMPLETE
            IF DATE>=THISSOLNSTARTDATE(ACTIVITYAVL) AND...
&              DATE<THISSOLNSTARTDATE(ACTIVITYAVL)+...
&                   RESOURCEREQDTIME(ACTIVITYAVL,RESOURCEDESIRED)...
&           OR DATE=THISSOLNSTARTDATE(ACTIVITYAVL) AND...
&                  RESOURCEREQDTIME(ACTIVITYAVL,RESOURCEDESIRED)=0
            THEN
                REM ANOTHER ACTIVITY IS "ACTIVE" ON "DATE"
                REM ACCUMULATE AMOUNT OF RESOURCE USED ON THIS DATE
                AMOUNTUSEDBYACTIVITIES=AMOUNTUSEDBYACTIVITIES...
&                   +RESOURCEREQDQTY(ACTIVITYAVL,RESOURCEDESIRED)
            FI
        NEXT ACTIVITYAVL
        REM COMPUTE RESOURCEQTYAVAILABLE AT END OF DATE (11:59 PM)
        REM (I.E., ...QTY... FOR DATE+1)
        REM ADD AMOUNT RENEWED, SUBTRACT AMOUNT PERISHED (OR USED!)
        LET RESOURCEQTYAVAILABLE=RESOURCEQTYAVAILABLE...
&               +RESOURCERENEWALRATE(RESOURCEDESIRED)...
&               -IF AMOUNTUSEDBYACTIVITIES<...
&                   RESOURCEPERISHRATE(RESOURCEDESIRED)
                 THEN RESOURCEPERISHRATE(RESOURCEDESIRED)
                 ELSE AMOUNTUSEDBYACTIVITIES FI
    NEXT DATE
    REM ASSERT RESOURCEQTYAVAILABLE = QTY RESOURCE AVAIL ON DATE "WHENDESIRED"
    REM NOW RESOURCEQTYAVAILABLE= AMOUNT OF RESOURCE AVAILABLE WHENDESIRED
    REM IF QTYDESIRED NOT AVAILABLE NOW --> NOT AVAILABLE FOR DURATION
    REM (THIS TEST ALSO NEEDED IF DURATION = 0) I'LL BET THIS IS WRONG!
    IF QTYDESIRED>RESOURCEQTYAVAILABLE THEN RETURN FALSE
    REM NOW MAKE SURE RESOURCEREQD IS AVAILABLE FOR THE DURATION
    FOR DATE=WHENDESIRED TO WHENDESIRED+DURATIONDESIRED-1
        REM COMPUTE RESOURCE QTY USED BY ACTIVITIES ON THIS DATE
        REM INCLUDING PROPROSED RESOURCE ALLOCATION
        AMOUNTUSEDBYACTIVITIES=QTYDESIRED \ ! FOR THIS PARTICULAR DATE
        FOR ACTIVITYAVL=1 TO ACTIVITIESCOMPLETE
            IF DATE>=THISSOLNSTARTDATE(ACTIVITYAVL) AND...
&              DATE<THISSOLNSTARTDATE(ACTIVITYAVL)+...
&                   RESOURCEREQDTIME(ACTIVITYAVL,RESOURCEDESIRED)...
&           OR DATE=THISSOLNSTARTDATE(ACTIVITYAVL) AND...
&                  RESOURCEREQDTIME(ACTIVITYAVL,RESOURCEDESIRED)=0
            THEN
                REM ANOTHER ACTIVITY IS "ACTIVE" ON "DATE"
                REM ACCUMULATE AMOUNT OF RESOURCE USED ON THIS DATE
                AMOUNTUSEDBYACTIVITIES=AMOUNTUSEDBYACTIVITIES...
&                   +RESOURCEREQDQTY(ACTIVITYAVL,RESOURCEDESIRED)
            FI
        NEXT ACTIVITYAVL
        REM COMPUTE RESOURCEQTYAVAILABLE AT END OF DAY NUMBER DATE
        REM SUBTRACT AMOUNT PROPOSED FOR NEW ACTIVITY
        LET RESOURCEQTYAVAILABLE=RESOURCEQTYAVAILABLE...
&               -IF AMOUNTUSEDBYACTIVITIES<...
&                   RESOURCEPERISHRATE(RESOURCEDESIRED)
                 THEN RESOURCEPERISHRATE(RESOURCEDESIRED)
                 ELSE AMOUNTUSEDBYACTIVITIES FI
        REM IF QTYDESIRED NOT AVAILABLE NOW --> NOT AVAILABLE FOR DURATION
        IF RESOURCEQTYAVAILABLE<0 THEN RETURN FALSE
        REM COMPUTE AMOUNT OF RESOURCE AVAILABLE ON DATE+1
        LET RESOURCEQTYAVAILABLE=RESOURCEQTYAVAILABLE...
&               +RESOURCERENEWALRATE(RESOURCEDESIRED)
    NEXT DATE
    REM QTYDESIRED IS AVAILABLE EVERY DAY THROUGHOUT THE PERIOD
    RETURN TRUE
END
REM PEOPLE RESOURCES SELF-RENEW AT 1 UNIT PER DAY, AND SELF-EXHAUST
REM AT SAME RATE IF NOT USED (PERISHABLE)
REM LIKEWISE BUILDINGS, LABORATORIES, ETC.
REM MONEY RENEWS AT SOME OUTSIDE RATE, BUT IS NOT PERISHABLE
REM TIME RENEWS AT A ZERO RATE; DOES NOT PERISH, AND HAS AN
REM INITIAL VALUE OF +INFINITY (I.E., MANY PROJECTS THAT NEED TIME
REM DO NOT CONFLICT WITH ONE ANOTHER)
REM NOTE THAT RENEW "RATES" ARE ACTUALLY QUANTITY/DAY, AS ARE PERISH RATES


SATISFYRESTOFACTIVITIES:
REM RECURSIVE SUBROUTINE SATISFYRESTOFACTIVITIES
REM COMPUTES ALL POSSIBLE RESOURCE ASSIGNMENTS FOR ACTIVITIES >= ACTIVITY
REM IT DOES SO BY COMPUTING ALL POSSIBLE RESOURCE ASSIGNMENTS...
REM FOR ACTIVITY AND THEN INVOKING ITSELF RECURSIVELY ON ACTIVITY+1
PRINT "SATISFYRESTOFACTIVITIES";ACTIVITY
    IF ACTIVITY>NACTIVITIES
    THEN
        REM WE HAVE ACTUALLY ACHEIVED A SOLUTION!
        REM ASSERT THIS SOLUTION IS BETTER THAN ANY ACHEIVED SO FAR
        IF THISSOLNSTOPDATE(NACTIVITIES)<BESTSOLNSTOPDATE(NACTIVITIES)
        THEN
            REM SAVE THIS SOLUTION AS BEST SO FAR!
            FOR I=1 TO NACTIVITIES
                BESTSOLNSTARTDATE(I)=THISSOLNSTARTDATE(I)
                BESTSOLNSTOPDATE(I)=THISSOLNSTOPDATE(I)
            NEXT I
            PRINT "BEST SOLUTION SO FAR STOPS AT DAY ";...
&                  BESTSOLNSTOPDATE(NACTIVITIES)
            PRINTSOLUTION
        ELSE PRINT "A WORSE SOLUTION FOUND???"
        RETURN
    FI
    PRINT "LOOKING FOR SOLUTION FOR ACTIVITY ";ACTIVITYNAME$(ACTIVITY)
    REM COMPUTE EARLIEST POSSIBLE START TIME
    REM = MAX(STOP TIMES OF ALL ACTIVITIES THIS ACTIVITY DEPENDS ON)
    LET EARLIESTSTARTDATE=0 \ ! START WITH START TIME
    FOR J=1 TO ACTIVITY-1
        REM THIS ACTIVITY CAN ONLY DEPEND ON ACTIVITIES OF LOWER INDEX
        IF ACTIVITYDEPENDENCE(ACTIVITY,J)...
&          AND EARLIESTSTARTDATE<THISSOLNSTOPDATE(J)
        THEN EARLIESTSTARTDATE=THISSOLNSTOPDATE(J)
    NEXT J
    PRINT "EARLIEST START DATE IS:";EARLIESTSTARTDATE
    LET THISSOLNSTARTDATE(ACTIVITY)=EARLIESTSTARTDATE
    UNTIL THISSOLNSTARTDATE(ACTIVITY)+RESOURCEREQDTIME(ACTIVITY,1)>=...
&         BESTSOLNSTOPDATE(NACTIVITIES) DO
        PRINT "CHECK: CAN ACTIVITY ";ACTIVITYNAME$(ACTIVITY);"START AT DATE";...
&             THISSOLNSTARTDATE(ACTIVITY)
        LET THISSOLNSTOPDATE(ACTIVITY)=THISSOLNSTARTDATE(ACTIVITY)
        FOR I=1 TO NRESOURCES
            IF NOT RESOURCEAVAILABLE(I,ACTIVITY-1,...
&                                    THISSOLNSTARTDATE(ACTIVITY),...
&                                    RESOURCEREQDQTY(ACTIVITY,I),...
&                                    RESOURCEREQDTIME(ACTIVITY,I))
            THEN
                REM TRY TO FIND A SUBSTITUTE RESOURCE
                REM ...(CODE TO FIND ALL POSSIBLE SUBSTITUTES)...
                REM NO SUBSTITUTES AVAILABLE
                EXIT I\! ALL RESOURCES NEEDED ARE NOT AVAILABLE NOW
            FI
            REM RESOURCE I IS AVAILABLE FOR THE DURATION NEEDED
            REM COMPUTE WHEN RESOURCE I IS NO LONGER NEEDED,
            REM AND ADJUST COMPLETION DATE OF THIS ACTIVITY
            REM IF NEED FOR RESOURCE I IS LONGER THAN NEED FOR OTHER RESOURCES
            IF THISSOLNSTOPDATE(ACTIVITY)<...
&                  THISSOLNSTARTDATE(ACTIVITY)+RESOURCEREQDTIME(ACTIVITY,I)
            THEN
                THISSOLNSTOPDATE(ACTIVITY)=...
&                   THISSOLNSTARTDATE(ACTIVITY)+RESOURCEREQDTIME(ACTIVITY,I)
                REM IF THIS ACTIVITY FINISHES AFTER BEST SOLUTION, GIVE UP!
                IF THISSOLNSTOPDATE(ACTIVITY)>=BESTSOLNSTOPDATE(NACTIVITIES)
                THEN RETURN \ ! GO TRY DIFFERENT START DATE FOR PREV ACTITIVITY
            FI
PRINT "RESOURCE ";RESOURCENAME$(I);" LETS ACTIVITY ";ACTIVITYNAME$(ACTIVITY);
PRINT " FINISH AT DATE ";THISSOLNSTOPDATE(ACTIVITY)
        NEXT I
        IF I=NRESOURCES+1
        THEN
            PRINT "ALL RESOURCES PRESENT FOR ACTIVITY ";ACTIVITYNAME$(ACTIVITY)
            REM ALL RESOURCES REQUIRED ARE PRESENT NOW FOR THE DURATION REQD
            ACTIVITY=ACTIVITY+1
            GOSUB SATISFYRESTOFACTIVITIES
            ACTIVITY=ACTIVITY-1
        FI
        REM FIRST ACTIVITY CANNOT DEPEND ON ANY OTHER --> START DATE=0
        IF ACTIVITY=1 THEN RETURN
        THISSOLNSTARTDATE(ACTIVITY)=THISSOLNSTARTDATE(ACTIVITY)+1
    END
    RETURN

REM *** THE ***
        END
rem        UNLESS THISACTIVITYISONCRITICALPATH END
REM AN APPROXIMATION OF ON CRITICAL PATH IS WHEN ACTIVITY STARTS
REM AFTER ALL OTHER ACTIVITIES STOP! ARE YOU SURE?
REM NOTE: RESOURCE SUBSTITUTIONS GIVEN BY SPECIFICATION LANGUATE FORM AT TREE
REM FOR CONVENIENCE IN THIS PROGRAM, THE TRANSITIVE CLOSURE OF
REM SUBSTITUTIONS IS STORED IN THE RESOURCES EXCHANGE ARRAY,
REM SO A TREE SEARCH NEED NOT BE PERFORMED TO DISCOVER ALL POSSIBLE
REM SUBSTITUTIONS
REM IN THIS CLOSURE, A RESOURCE MAY BE SUBSTITUTED FOR 1 OF ITSELF
