home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload / ShartewareOverload.cdr / database / simlib.zip / SIMLIB.SC < prev   
Text File  |  1990-02-24  |  50KB  |  1,015 lines

  1. ;╔════════════════════════════════════════════════════════════════════════════╗
  2. ;║          This PARADOX code is placed in the public domain                  ║
  3. ;╠════════════════════════════════════════════════════════════════════════════╣
  4. ;║  SIMLIB is a queueing simulation language first presented by Averill       ║
  5. ;║  M. Law and W. David Kelton in their book "Simulation Modeling And         ║
  6. ;║  Analyis" (McGraw-Hill (c) 1982 ISBM 0-07-036696-9)                        ║
  7. ;╟────────────────────────────────────────────────────────────────────────────╢
  8. ;║  SIMLIB is a toolbox of utilities consisting of:                           ║
  9. ;║          INITIAL,FILE,REMOVE,CANCEL,SAMPST,TIMEST,                         ║
  10. ;║          TIMING,UNIFORM,RANDI,EXPON, ERLANG, and NORMAL                    ║
  11. ;║  The author intended the user to write the main routine, the arrival       ║
  12. ;║  procedure, the departure procedure, and any other supporting routines.    ║
  13. ;║  A generic set of routines is given in section two of the file. These      ║
  14. ;║  routines can build simple queuing simulations or can serve as a template  ║
  15. ;║  for building more complicated queuing simulations.                        ║
  16. ;║                                                                            ║
  17. ;║  Originally written in fortran, it is re-written in PARADOX PAL with       ║
  18. ;║  a few modifications (and possibly even improvements). First, SIMLIB       ║
  19. ;║  originally used only arrays. I have replaced many of the arrays with      ║
  20. ;║  PARADOX tables:                                                           ║
  21. ;║          MASTER  - replaces MASTER array. Stores queue information         ║
  22. ;║          SAMPST  - replaces a series of different arrays (Avg, Max, etc.)  ║
  23. ;║                  - Stores sampling statistics.                             ║
  24. ;║          TIMEST  - replaces a series of different arrays                   ║
  25. ;║                  - Stores time related statistics.                         ║
  26. ;║          RESULTS - Stores a variety of simulation results                  ║
  27. ;║  Furthermore, the file MASTER2.DB stores a backup copy of every element    ║
  28. ;║  ever queued. This data can be used to calculate results "after-the-fact." ║
  29. ;║  See the procedure PISSOFF in section 3.                                   ║
  30. ;║                                                                            ║
  31. ;║  Other minor changes include moving the event list from #25 to #1, the     ║
  32. ;║  addition of a new few variables, and the elimination of most size         ║
  33. ;║  constraints.                                                              ║
  34. ;╚════════════════════════════════════════════════════════════════════════════╝
  35. ;╔════════════════════════════════════════════════════════════════════════════╗
  36. ;║                       SECTION ONE - SIMLIB routines                        ║
  37. ;╚════════════════════════════════════════════════════════════════════════════╝
  38. libname="simlib"
  39. CREATELIB libname
  40.  
  41. PROC initial()                                              ;Initialize system variables
  42. PRIVATE I
  43.     clock=0                                                 ;Set clock to zero
  44.     IF NOT(ISASSIGNED(maxatr)) THEN
  45.         maxatr=10                                           ;<---- Maximum # of tracked attributes
  46.     ENDIF
  47.     IF ISTABLE("master2") THEN                              ;Retain one generation
  48.        RENAME "master2" "M2bak"
  49.        MESSAGE "Existing MASTER2.DB renamed TO M2BAK.DB"
  50.        SLEEP 1000
  51.     ENDIF
  52.     IF ISTABLE("master") THEN                               ;Retain one generation
  53.        RENAME "master" "Mbak"
  54.        MESSAGE "Existing MASTER.DB renamed TO MBAK.DB"
  55.        SLEEP 1000
  56.     ENDIF
  57.     {Create} {master}                                       ;Create main queue file
  58.        "List" Enter "N" Enter
  59.        FOR I FROM 1 TO maxatr
  60.          TYPEIN "Attribute #"+STRVAL(I)
  61.          Enter "N" ENTER
  62.        ENDFOR
  63.        Do_It!
  64.     CREATE "master2" LIKE "master"                          ;Create history file
  65.     IF NOT(ISASSIGNED(blowout)) THEN
  66.         blowout=1000                                        ;<--- Maximum Queue Size
  67.     ENDIF
  68.     IF NOT(ISASSIGNED(maxlist)) THEN
  69.         maxlist=25                                          ;<--- Maximum number of queues kept
  70.     ENDIF
  71.     SAMPST(0,0)                                             ;Initialize SAMPST.DB
  72.     TIMEST(0,0)                                             ;Initialize TIMEST.DB
  73.     ARRAY transfer[max(maxatr,5)]                           ;Build & initialize the...
  74.     FOR I FROM 1 TO MAX(maxatr,5)                           ;transfer variables
  75.         transfer[I]=0                                       ;
  76.     ENDFOR
  77.     ARRAY lrank[maxlist]                                    ;Stores attribute used for sorting
  78.     ARRAY lsize[maxlist]                                    ;Queue size of a particular list
  79.     FOR I FROM 1 TO maxlist
  80.         lrank[I]=0                                          ;Initialize variable
  81.         lsize[I]=0                                          ;
  82.     ENDFOR
  83.     lrank[1]=1                                              ;Rank main queue on time (attribute #1)
  84.     CLEARALL
  85.     VIEW "Master"                                           ;
  86.     VIEW "Master2"                                          ;
  87.     VIEW "Results"                                          ;Place files on workspace
  88.     IF ISTABLE("Sampst") THEN VIEW "Sampst" ENDIF           ;
  89.     IF ISTABLE("Timest") THEN VIEW "Timest" ENDIF           ;
  90.     MOVETO "Master"
  91.     COEDITKEY
  92. ENDPROC
  93. WRITELIB libname initial
  94. RELEASE PROCS initial
  95.  
  96. PROC file(option,list)                                      ;File records in queue
  97. PRIVATE x,bigger,item
  98. ;╔════════════════════════════════════════════════════════════════════════════╗
  99. ;║  FILE options:                                                             ║
  100. ;║       1) File transfer variables before first record in list               ║
  101. ;║       2) File transfer variables after last record in list                 ║
  102. ;║       3) File transfer variables in increasing order based upon            ║
  103. ;║          the attribute stored in LRANK[list]                               ║
  104. ;║       4) File transfer variables in decreasing order based upon            ║
  105. ;║          the attribute stored in LRANK[list]                               ║
  106. ;╚════════════════════════════════════════════════════════════════════════════╝
  107.     IF nrecords("master")>blowout THEN                      ;Protect against runaway queues
  108.        MESSAGE "Queue reached maximum size of "+STRVAL(blowout)+
  109.                " at time "+STRVAL(clock)
  110.        x=getchar()
  111.        QUIT
  112.     ENDIF
  113.     IF ((list>=1) AND (list<=maxlist)) THEN                 ;Make sure list exists
  114.        MOVETO [Master->List]
  115.        SWITCH
  116.            CASE option=1 :                                  ;Insert new record before first record
  117.                 HOME
  118.                 INS
  119.            CASE option=2 :                                  ;Insert new record after the last record
  120.                 LOCATE list                                 ;
  121.                 WHILE retval                                ;
  122.                    SKIP 1                                   ;Locate records until no more
  123.                    IF NOT EOT() THEN                        ;...are found or EndOfTable.
  124.                       LOCATE NEXT list                      ;
  125.                    ELSE                                     ;
  126.                       retval=FALSE                          ;
  127.                    ENDIF
  128.                 ENDWHILE
  129.                 IF ATLAST() THEN                            ;Increase the last record meets criteria
  130.                    DOWN
  131.                 ELSE
  132.                    INS
  133.                 ENDIF
  134.            CASE option=3 :                                  ;Insert in ranked order list (increasing order)
  135.                 item=STRVAL(lrank[list])                    ;Attribute used in ranking
  136.                 bigger=FALSE
  137.                 LOCATE list                                 ;Find first record
  138.                 IF RETVAL THEN
  139.                    EXECUTE "bigger=transfer["+item+"]>[Attribute #"+item+"]"  ;Check size
  140.                    WHILE retval and bigger                  ;While records exist and sort location hasn't been found
  141.                        SKIP 1
  142.                        IF NOT EOT() THEN                    ;Are we at the end of the file
  143.                           LOCATE NEXT list                  ;Locate next record
  144.                           IF retval THEN
  145.                              EXECUTE "bigger=transfer["+item+"]>[Attribute #"+item+"]"
  146.                           ENDIF
  147.                        ELSE
  148.                           retval=FALSE
  149.                        ENDIF
  150.                    ENDWHILE
  151.                 ENDIF
  152.                 IF ATLAST() AND bigger                      ;If on last record in file
  153.                    THEN DOWN
  154.                 ELSE
  155.                    INS
  156.                 ENDIF
  157.            CASE option=4 :                                  ;Insert in ranked order list (descending order)
  158.                 item=STRVAL(lrank[list])
  159.                 LOCATE list                                 ;find first record
  160.                 IF RETVAL THEN
  161.                    EXECUTE "retval=transfer["+item+"]<[Attribute #"+item+"]"
  162.                    WHILE retval                             ;While we haven't found our spot
  163.                        SKIP 1
  164.                        IF NOT EOT() THEN                    ;Are we at the last record
  165.                           LOCATE NEXT list                  ;Locate next record
  166.                           IF retval THEN
  167.                              EXECUTE "retval=transfer["+item+"]<[Attribute #"+item+"]"
  168.                           ENDIF
  169.                        ELSE
  170.                           retval=FALSE
  171.                        ENDIF
  172.                    ENDWHILE
  173.                 ENDIF
  174.                 IF ATLAST() THEN                            ;If on last record
  175.                    DOWN
  176.                 ELSE
  177.                    INS
  178.                 ENDIF
  179.            OTHERWISE :
  180.                 MESSAGE "An improper option was passed TO FILE"
  181.                 SLEEP 2000
  182.                 RETURN
  183.        ENDSWITCH
  184.        [List]=list                                          ;
  185.        FOR I FROM 1 TO maxatr                               ;Plug variables
  186.            EXECUTE "[Attribute #"+STRVAL(I)+"]=transfer["+STRVAL(I)+"]"
  187.        ENDFOR
  188.        lsize[list]=lsize[list]+1                            ;Increment queue size
  189.        TIMEST(lsize[list],list)                             ;Calculate time related variables
  190.     ELSE
  191.        MESSAGE "An improper value for file list was passed TO FILE"
  192.        SLEEP 2000
  193.     ENDIF
  194. ENDPROC
  195. WRITELIB libname file
  196. RELEASE PROCS file
  197.  
  198. PROC remove(option,list)                                    ;Remove a particular record
  199. PRIVATE x,i,a
  200. ;╔════════════════════════════════════════════════════════════════════════════╗
  201. ;║  REMOVE options:                                                           ║
  202. ;║       1) Remove the first record for a particular list                     ║
  203. ;║       2) Remove the last record for a particular list                      ║
  204. ;║                                                                            ║
  205. ;║       Values are placed in the transfer array                              ║
  206. ;╚════════════════════════════════════════════════════════════════════════════╝
  207.     IF ((list>=1) AND (list<=maxlist)) THEN                 ;Check for valid list
  208.         IF lsize[list]=0 THEN                               ;Check queue size
  209.             MESSAGE "Underflow of list "+strval(list)+" at time "+strval(clock)
  210.             x=getchar()
  211.             QUIT
  212.         ENDIF
  213.         MOVETO [Master->List]
  214.         SWITCH
  215.             CASE option=1 :                                 ;Remove the first record
  216.                  LOCATE list
  217.             CASE option=2 :                                 ;Remove the last record
  218.                  LOCATE list                                ;
  219.                  WHILE retval                               ;Locate until the last...
  220.                     SKIP 1                                  ;...record is found or
  221.                     IF NOT EOT() THEN                       ;...EndOfTable
  222.                        LOCATE NEXT list                     ;
  223.                     ELSE
  224.                        retval=FALSE
  225.                     ENDIF
  226.                  ENDWHILE
  227.                  IF LIST<>[] THEN                           ;In case the last record...
  228.                     UP                                      ;...meets the criteria
  229.                  ENDIF                                      ;
  230.             OTHERWISE :
  231.                 MESSAGE "An improper option was passed TO REMOVE"
  232.                 SLEEP 2000
  233.                 RETURN
  234.         ENDSWITCH
  235.         IF list=[] THEN                                     ;If the record was found
  236.            FOR I FROM 1 TO maxatr                           ;Record variables
  237.                EXECUTE "transfer["+strval(I)+"]=[Attribute #"+STRVAL(I)+"]"
  238.            ENDFOR
  239.            COPYTOARRAY a                                    ;
  240.            MOVETO "master2"                                 ;Make a backup copy
  241.            END DOWN                                         ;
  242.            COPYFROMARRAY a                                  ;
  243.            MOVETO "master"                                  ;
  244.            DEL
  245.            lsize[list]=lsize[list]-1                        ;Decrement queue size
  246.            TIMEST(lsize[list],list)                         ;Record time related statistics
  247.         ELSE
  248.            MESSAGE "REMOVE did not find the record"
  249.            x=getchar()
  250.            QUIT
  251.         ENDIF
  252.     ELSE
  253.        MESSAGE "An improper value for file list was passed TO REMOVE"
  254.        SLEEP 2000
  255.     ENDIF
  256. ENDPROC
  257. WRITELIB libname remove
  258. RELEASE PROCS remove
  259.  
  260. PROC cancel(etype)                                          ;Only removes from the event list #1
  261. PRIVATE i,a,found
  262.     MOVETO [Master->List]
  263.     LOCATE 1                                                ;Locate main queue records
  264.     IF retval THEN
  265.         found=etype=[Attribute #2]                          ;Is this the type we're looking for?
  266.         WHILE NOT(found) AND retval
  267.             SKIP 1                                          ;
  268.             IF NOT EOT() THEN                               ;Locate until the last...
  269.                LOCATE NEXT 1                                ;...record is found or
  270.                IF retval THEN                               ;...EndOfTable
  271.                   found=etype=[Attribute #2]                ;
  272.                ENDIF
  273.             ELSE
  274.                retval=FALSE
  275.             ENDIF
  276.         ENDWHILE
  277.         IF found THEN
  278.             FOR I FROM 1 TO maxatr                          ;Record variables
  279.                 EXECUTE "transfer["+strval(I)+"]=[Attribute #"+STRVAL(I)+"]"
  280.             ENDFOR
  281.             COPYTOARRAY a                                   ;
  282.             MOVETO "master2"                                ;Make a backup copy
  283.             END DOWN                                        ;
  284.             COPYFROMARRAY a                                 ;
  285.             MOVETO "master"                                 ;
  286.             DEL
  287.             lsize[1]=lsize[1]-1                             ;Decrement queue size
  288.             TIMEST(lsize[1],1)                              ;Record time related statistics
  289.         ELSE
  290.             MESSAGE "CANCEL did not find the correct record"
  291.         ENDIF
  292.     ENDIF
  293. ENDPROC
  294. WRITELIB libname cancel
  295. RELEASE PROCS cancel
  296.  
  297. PROC sampst(value,var)                                      ;Statistic collection routine
  298. PRIVATE i
  299. ;╔════════════════════════════════════════════════════════════════════════════╗
  300. ;║ TRANSFER variables for SAMPST:                                             ║
  301. ;║     1) Sample mean                                                         ║
  302. ;║     2) Number of observations                                              ║
  303. ;║     3) Maximum value recorded                                              ║
  304. ;║     4) Minimum value recorded                                              ║
  305. ;║     5) Sum of all variables recorded                                       ║
  306. ;╚════════════════════════════════════════════════════════════════════════════╝
  307.     IF ((var>=-sample_vars) and (var<=sample_vars)) THEN
  308.        SWITCH
  309.         CASE var=0:                                         ;Build the SAMPST.DB table
  310.            IF ISTABLE("sampst") THEN                        ;Maintain one generation
  311.               RENAME "sampst" "ssbak"
  312.               MESSAGE "Existing SAMPST.DB renamed TO SSBAK.DB"
  313.            ENDIF
  314.            CREATE "sampst"
  315.              "Sum"           : "N",
  316.              "Maximum"       : "N",
  317.              "Minimum"       : "N",
  318.              "Number of Obs" : "N"
  319.            View "sampst"
  320.            COEDITKEY
  321.            For I from 1 TO sample_vars
  322.               [Sum]=0                                       ;
  323.               [Maximum]=-1.E+20                             ;Set to initial value
  324.               [Minimum]= 1.E+20                             ;
  325.               [Number of Obs]=0                             ;
  326.               DOWN
  327.            ENDFOR
  328.            DO_IT!
  329.            CLEARIMAGE
  330.         CASE var>0 :                                        ;Add new values to file
  331.            MOVETO "sampst"
  332.            MOVETO RECORD var
  333.            [Sum]=[Sum]+value
  334.            [Maximum]=MAX([Maximum],value)
  335.            [Minimum]=MIN([Minimum],value)
  336.            [Number of Obs]=[Number of Obs]+1
  337.            MOVETO "master"
  338.         CASE var<0 :                                        ;Place results in transfer array
  339.            ivar=-var
  340.            MOVETO "sampst"
  341.            MOVETO RECORD ivar
  342.            transfer[2]=[Number of Obs]
  343.            transfer[3]=[Maximum]
  344.            transfer[4]=[Minimum]
  345.            transfer[5]=[Sum]
  346.            IF transfer[2]=0 THEN
  347.               transfer[1]=0
  348.            ELSE
  349.               transfer[1]=transfer[5]/transfer[2]           ;Calc average
  350.            ENDIF
  351.            MOVETO "master"
  352.        ENDSWITCH
  353.     ELSE
  354.         MESSAGE "An invalid variable has been passed TO SAMPST"
  355.         sleep 2000
  356.     ENDIF
  357. ENDPROC
  358. WRITELIB libname sampst
  359. RELEASE PROCS sampst
  360.  
  361. PROC timest(value,var)                                      ;Collect time weighted statistics
  362. PRIVATE i,ivar
  363. ;╔════════════════════════════════════════════════════════════════════════════╗
  364. ;║ TRANSFER variables for TIMEST:                                             ║
  365. ;║     1) Time average (mean) of the variables recorded                       ║
  366. ;║     2) Maximum value recorded                                              ║
  367. ;║     3) Minimum value recorded                                              ║
  368. ;╚════════════════════════════════════════════════════════════════════════════╝
  369.     IF ((var>=-maxlist) and (var<=maxlist)) THEN            ;Check variable range
  370.        SWITCH
  371.         CASE var=0:                                         ;Build TIMEST.DB table
  372.            IF ISTABLE("timest") THEN                        ;Maintain one generation
  373.               RENAME "timest" "tsbak"
  374.               MESSAGE "Existing TIMEST.DB renamed TO TSBAK.DB"
  375.            ENDIF
  376.            CREATE "timest"
  377.              "Area"             : "N",
  378.              "Maximum"          : "N",
  379.              "Minimum"          : "N",
  380.              "Previous Value"   : "N",
  381.              "Last Time Change" : "N"
  382.            View "timest"
  383.            COEDITKEY
  384.            For I from 1 TO maxlist                          ;
  385.               [Area]=0                                      ;
  386.               [Maximum]=-1.E+20                             ;Set to initial value
  387.               [Minimum]= 1.E+20                             ;
  388.               [Previous Value]=0                            ;
  389.               [Last Time Change]=clock                      ;
  390.               DOWN
  391.            ENDFOR
  392.            DO_IT!
  393.            treset=clock
  394.            CLEARIMAGE
  395.         CASE var>0 :                                        ;Add new values to file
  396.            MOVETO "timest"
  397.            MOVETO RECORD var
  398.            [Area]=[Area]+((clock-[Last Time Change])*[Previous Value])
  399.            [Maximum]=MAX([Maximum],value)
  400.            [Minimum]=MIN([Minimum],value)
  401.            [Previous Value]=value
  402.            [Last Time Change]=clock
  403.            MOVETO "master"
  404.         CASE var<0 :                                        ;Place results in transfer array
  405.            ivar=-var
  406.            MOVETO "timest"
  407.            MOVETO RECORD ivar
  408.            [Area]=[Area]+((clock-[Last Time Change])*[Previous Value])
  409.            [Last Time Change]=clock
  410.            transfer[1]=[Area]/(clock-treset)                ;Calc average
  411.            transfer[2]=[Maximum]
  412.            transfer[3]=[Minimum]
  413.            MOVETO "master"
  414.        ENDSWITCH
  415.     ELSE
  416.         MESSAGE "An invalid variable has been passed TO TIMEST"
  417.         sleep 2000
  418.     ENDIF
  419. ENDPROC
  420. WRITELIB libname timest
  421. RELEASE PROCS timest
  422.  
  423. PROC filest(list)                                           ;Generate TIMEST results
  424. PRIVATE ilist
  425. ;╔════════════════════════════════════════════════════════════════════════════╗
  426. ;║ TRANSFER variables for FILEST:                                             ║
  427. ;║     1) Time average (mean) of the variables recorded                       ║
  428. ;║     2) Maximum value recorded                                              ║
  429. ;║     3) Minimum value recorded                                              ║
  430. ;╚════════════════════════════════════════════════════════════════════════════╝
  431.     ilist=-list
  432.     TIMEST(0,ilist)
  433. ENDPROC
  434. WRITELIB libname filest
  435. RELEASE PROCS filest
  436.  
  437. PROC timing()                                               ;Remove the next event from the event queue
  438. PRIVATE x
  439.     REMOVE(1,1)                                             ;Remove event
  440.     IF transfer[1]>=clock THEN                              ;Don't let the clock go backwards
  441.         clock=transfer[1]                                   ;Update clock
  442.         next=transfer[2]                                    ;Set "next" event flag
  443.     ELSE
  444.         MESSAGE "Attempt TO schedule event type "+STRVAL(transfer[2])+
  445.                 " at time "+STRVAL(transfer[1])+" when clock is "+STRVAL(clock)
  446.         SLEEP 5000
  447.         x=getchar()
  448.         QUIT
  449.     ENDIF
  450. ENDPROC
  451. WRITELIB libname timing
  452. RELEASE PROCS timing
  453.  
  454. PROC uniform(A,B)                                           ;Generate a random number uniformly between two values
  455. PRIVATE u,uniform
  456.     u=RAND()                                                ;Get random number
  457.     uniform=A+(u*(B-A))                                     ;Calc value
  458.     RETURN uniform
  459. ENDPROC
  460. WRITELIB libname uniform
  461. RELEASE PROCS uniform
  462.  
  463. PROC randi()                                                ;Generate a discrete value based upon PROBD distribution
  464. PRIVATE u,n1,i
  465.     u=RAND()                                                ;Get random number
  466.     n1=ARRAYSIZE(probd)-1
  467.     FOR I FROM 1 TO n1
  468.         IF u<probd[I] THEN                                  ;PROBD is cumulative (PROBD[1]=.50 PROBD[2]=.90 PROBD[3]=.95 etc.)
  469.             RETURN I                                        ;Return discrete value
  470.         ENDIF
  471.     ENDFOR
  472.     RETURN n1+1                                             ;Otherwise its largest value
  473. ENDPROC
  474. WRITELIB libname randi
  475. RELEASE PROCS randi
  476.  
  477. PROC expon(rmean)                                           ;Generate an exponentially distributed value
  478. PRIVATE u,expon
  479.     u=RAND()
  480.     expon=-RMEAN*LN(u)                                      ;Excellent distribution for arrival and departure rates
  481.     RETURN expon
  482. ENDPROC
  483. WRITELIB libname expon
  484. RELEASE PROCS expon
  485.  
  486. PROC erlang(k,rmean)                                        ;Generate an m-ERLANG distribution
  487. PRIVATE mexp,erl
  488.     mexp=rmean/k
  489.     erl=0                                                   ;Initialize value
  490.     FOR I FROM 1 TO K
  491.         erl=erl+EXPON(mexp)                                 ;get exponential value
  492.     ENDFOR
  493.     RETURN erl
  494. ENDPROC
  495. WRITELIB libname erlang
  496. RELEASE PROCS erlang
  497.  
  498. PROC normal(mean,sd)                                        ;Generate a normal distribution (negative numbers may generate)
  499. PRIVATE v1,v2,w,y
  500.     w=9999
  501.     WHILE w>1
  502.       v1=2*RAND()-1       
  503.       v2=2*RAND()-1
  504.       w=(v1*v1)+(v2*v2)
  505.     ENDWHILE
  506.     y=SQRT((-2*LN(w))/w)                                    ;Generates normal dist. mean=0 st=1
  507.     norm=v1*y                                               ;Calc distribution for given range
  508.     RETURN norm                                             ;Alternatively "norm=v2*y" 
  509. ENDPROC
  510. WRITELIB libname normal
  511. RELEASE PROCS normal
  512. ;
  513. ;╔════════════════════════════════════════════════════════════════════════════╗
  514. ;║                        SECTION TWO - Generic routines                      ║
  515. ;╟────────────────────────────────────────────────────────────────────────────╢
  516. ;║  MAIN          - Query user for run parameters and initialize variables.   ║
  517. ;║  MAINLOOP      - Determine event type and call relevant procedure.         ║
  518. ;║  ARRIVE        - Process the current arrival and schedule next arrival.    ║
  519. ;║  DEPART        - Record the current departure & pull next item from queue. ║
  520. ;║  OUTPUT        - Calculate output for current run.                         ║
  521. ;║  SETUP_REPORT  - Setup the report display screen.                          ║
  522. ;║  UPDATE_REPORT - Print current status to screen.                           ║
  523. ;╚════════════════════════════════════════════════════════════════════════════╝
  524. ;
  525. PROC CLOSED main()                                         ;Query the user for settings
  526. USEVARS autolib
  527. PRIVATE mexp,erl
  528.     CLEAR
  529.     @1,0
  530.     SHOWMENU
  531.         "MSSQ" : "Multiple Server/Single Queue",
  532.         "MSMQ" : "Multiple Server/Multiple Queue",
  533.         "SSSQ" : "Single Server/Single Queue"
  534.     TO system
  535.     IF system="Esc" THEN
  536.         RETURN
  537.     ENDIF
  538.     SHOWMENU
  539.         "Time"   : "End the process at a preset time",
  540.         "Create" : "End the process after a certain count created",
  541.         "Serve"  : "End the process after a certain count served",
  542.         "Queue"  : "End the process at a certain queue size"
  543.     DEFAULT "Time"
  544.     TO eoj
  545.     SWITCH
  546.         CASE eoj="Esc"  :
  547.              RETURN
  548.         CASE eoj="Create" :
  549.             ? "=> Enter total number of jobs created " CLEAR EOL
  550.             ACCEPT "N" TO eojval
  551.         CASE eoj="Serve" :
  552.             ? "=> Enter total number of jobs served " CLEAR EOL
  553.             ACCEPT "N" TO eojval
  554.         CASE eoj="Time"  :
  555.             ? "=> Enter end of job time " CLEAR EOL
  556.             ACCEPT "N" TO eojval
  557. ;*** Immediately stop the run or should the system simulate closing
  558. ;*** the doors and waiting for the queue to empty?
  559.             ? "===> Should the queue be completed? (Y/N) "
  560.             ACCEPT "a1" picture "{Y,N}" TO eojqueue
  561.         CASE eoj="Queue" :
  562.             ? "=> Enter the maximum queue size " CLEAR EOL
  563.             ACCEPT "N" TO eojval
  564.     ENDSWITCH
  565.     ? "=> Enter mean arrival rate "
  566.     ACCEPT "N" TO marrive
  567.     ? "=> Enter service rate "
  568.     ACCEPT "N" TO mservice
  569.     minserv=1                                               ;Minimum number of servers
  570.     maxserv=1                                               ;Maximum number of servers
  571.     increm=1                                                ;Incremental unit to step number of servers
  572.     IF system="MSSQ" OR system="MSMQ" THEN
  573.         ? "=> Enter the minimum number of servers to test "
  574.         ACCEPT "S" TO minserv
  575.         ? "=> Enter the maximum number of servers to test "
  576.         ACCEPT "S" TO maxserv
  577.         ? "=> Enter incremental unit "
  578.         ACCEPT "S" default 1 TO increm
  579.     ENDIF
  580.     ? "=> Enter how many repetitive runs to execute? "
  581.     ACCEPT "S" DEFAULT 1 TO number_of_runs
  582. ;*** An initialproc can be added which adds new events, initializes new
  583. ;*** variables, etc.
  584.     ? "=> Enter INITIAL Proc name "
  585.     ACCEPT "A20" TO initialproc
  586. ;*** An arrivalproc can be added which tests the length of the queue and
  587. ;*** removes the last arrival if too long...or it could be used to collect
  588. ;*** particular statistics.
  589.     ? "=> Enter ARRIVAL Proc name "
  590.     ACCEPT "A20" TO arriveproc
  591. ;*** A departproc can be added which jockeys the queues after every
  592. ;*** departure. An example jockey proc is given.
  593.     ? "=> Enter DEPART Proc name  "
  594.     ACCEPT "A20" TO departproc
  595.     IF ISTABLE("results") THEN
  596.        RENAME "results" "Rbak"
  597.        MESSAGE "Existing RESULTS.DB renamed TO RBAK.DB"
  598.     ENDIF
  599.     CREATE "results"
  600.        "Run Number"                : "N",
  601.        "Number of Servers"         : "N",
  602.        "Average Number in Queue"   : "N",
  603.        "Maximum Number in a Queue" : "N",
  604.        "Maximum Number in Queue"   : "N",
  605.        "Average Delay"             : "N",
  606.        "Maximum Delay"             : "N",
  607.        "Server Number"             : "N",
  608.        "Server Utilization"        : "N"
  609.     setup_report()
  610.     mainloop()                                              ;Main execution loop
  611.     CLEAR CLEARALL
  612.     STYLE
  613.     VIEW "results"
  614. ENDPROC
  615. WRITELIB libname main
  616. RELEASE PROCS main
  617.  
  618. PROC mainloop()                                             ;Main execution loop
  619.    FOR run_number FROM 1 TO number_of_runs                  ;Execute a certain number of times
  620.       FOR numtel FROM minserv TO maxserv STEP increm        ;Execute for a range of servers
  621. ;*** Initialize variables
  622.           IF system="MSMQ" THEN
  623.              numque=numtel                                  ;Number of queues
  624.           ELSE
  625.              numque=1                                       ;Only one queue is used
  626.           ENDIF
  627.           maxlist=1+numque+numtel                           ;maxlist is used to set most array sizes
  628.           sample_vars=2                                     ;Number of sample statistics kept
  629.           maxatr=5                                          ;Number of attributes kept (default=5)
  630.           nojobs=0                                          ;Counter for number of jobs created or served
  631.           total_que=0                                       ;Total queue size
  632.           INITIAL()                                         ;Initialize other variables
  633. ;*** Schedule first arrival
  634.           transfer[1]=EXPON(marrive)                        ;arrival time
  635.           transfer[2]=1                                     ;Arrival code
  636.           FILE(3,1)                                         ;File in increasing order
  637.           IF eoj="Create" THEN
  638.               nojobs=nojobs+1                               ;increment number of jobs
  639.           ENDIF
  640. ;*** Schedule end of job if available
  641.           IF eoj="Time" THEN
  642.              transfer[1]=eojval                             ;Ending time
  643.              transfer[2]=3                                  ;End of run code
  644.              FILE(3,1)                                      ;File in increasing order
  645.           ENDIF
  646. ;
  647. ;***  An initialproc can be added which adds new events, initializes new
  648. ;***  variables, etc.
  649. ;
  650.           IF initialproc<>"" THEN
  651.               EXECPROC initialproc
  652.           ENDIF
  653.           WHILE TRUE
  654.              TIMING()                                       ;Remove next event
  655.              update_report()                                ;Print current status
  656.              SWITCH
  657.                 CASE next=1 :                               ;Process an arrival
  658.                      ARRIVE()
  659.                 CASE next=2 :                               ;Process a departure
  660.                      DEPART(transfer[3])                    ;Departure from a particular teller
  661.                      IF lsize[1]=0 THEN                     ;If CASE 3 has been run and queue is empty
  662.                         OUTPUT()                            ;Built RESULTS table
  663.                         QUITLOOP                            ;Exit system
  664.                      ENDIF
  665.                 CASE next=3 :                               ;End the run
  666.                      IF ISASSIGNED(eojqueue) and eojqueue="Y" THEN  ;Quit or just close the doors
  667.                         CANCEL(1)                           ;Cancel the next arrival
  668.                         IF lsize[1]=0 THEN                  ;IF the system is empty
  669.                            OUTPUT()                         ;Built RESULTS table
  670.                            QUITLOOP                         ;Exit system
  671.                         ENDIF
  672.                      ELSE
  673.                         OUTPUT()                            ;Built RESULTS table
  674.                         QUITLOOP                            ;Exit system
  675.                      ENDIF
  676. ;╔════════════════════════════════════════════════════════════════════════════╗
  677. ;║  Additional CASEs could exist. For example, to accurately portray the      ║
  678. ;║  arrival rate of a McDonalds's a new arrival rate must change at least     ║
  679. ;║  once an hour, also new servers must be frequently added or removed. In    ║
  680. ;║  this example the user initialize the event queue with rate changes:       ║
  681. ;║        transfer(1)=60                                                      ║
  682. ;║        transfer(2)=4                                                       ║
  683. ;║        transfer(3)=.50                                                     ║
  684. ;║        FILE(3,1)                                                           ║
  685. ;║  and write a CASE four routine to set marrive=transfer(3). To change the   ║
  686. ;║  number of servers the same process would be used for CASE next=5:         ║
  687. ;║        transfer(1)=60                                                      ║
  688. ;║        transfer(2)=5                                                       ║
  689. ;║        transfer(3)=-1                                                      ║
  690. ;║        FILE(3,1)                                                           ║
  691. ;║  with a CASE five routine to empty the servers queue and set the number of ║
  692. ;║  of servers...numtel=numtel+transfer(3)                                    ║
  693. ;╚════════════════════════════════════════════════════════════════════════════╝
  694.              ENDSWITCH
  695.           ENDWHILE
  696.       ENDFOR
  697.     ENDFOR
  698. ENDPROC
  699. WRITELIB libname mainloop
  700. RELEASE PROCS mainloop
  701.  
  702. ;*** Arrival procs must perform two processes. It must handle the current
  703. ;*** arrival (by sending to a server, queuing, or exiting the system) and
  704. ;*** schedule the next arrival.
  705. PROC arrive()
  706. PRIVATE i,delay,shortest_q
  707.    IF eoj="Create" THEN                                     ;Are we tracking arrivals
  708.       nojobs=nojobs+1                                       ;Increment counter
  709.       IF nojobs >= eojval THEN                              ;Should an immediate exit be scheduled
  710.          transfer[1]=clock
  711.          transfer[2]=3                                      ;Exit code
  712.          FILE(1,1)                                          ;File in front
  713.       ENDIF
  714.   ENDIF
  715. ; Check server status
  716.    FOR teller FROM 1 TO numtel
  717.       IF LSIZE[numque+teller+1]=0 THEN                      ;Is server available?
  718.          QUITLOOP
  719.       ENDIF
  720.    ENDFOR
  721.    teller=MIN(teller,numtel)
  722.    IF LSIZE[numque+teller+1]=0 THEN                         ;See if server is busy
  723.       delay=0
  724.       SAMPST(delay,1)
  725.       FILE(1,numque+teller+1)                               ;Make server busy
  726.       transfer[1]=clock+EXPON(mservice)                     ;Schedule departure
  727.       transfer[2]=2                                         ;Depart code
  728.       transfer[3]=teller                                    ;Teller number
  729.       transfer[5]=transfer[1]-transfer[4]                   ;Calc entire time in system
  730.       FILE(3,1)
  731.    ELSE
  732.       IF eoj="Queue" THEN                                   ;If monitoring queue size
  733.          IF total_que >= eojval THEN                        ;Schedule an immediate exit
  734.             transfer[1]=clock
  735.             transfer[2]=3                                   ;Exit code
  736.             FILE(1,1)                                       ;File in front
  737.          ENDIF
  738.       ENDIF
  739.       shortest_q = 1.E+20                                   ;Determine shortest Queue
  740.       FOR I from 1 TO numque                                ;
  741.           IF LSIZE[I+1] < shortest_q THEN                   ;
  742.              shortest_q=LSIZE[I+1]                          ;
  743.              choice=I+1                                     ;
  744.           ENDIF                                             ;
  745.       ENDFOR
  746.       transfer[1]=clock                                     ;Used to calculate delay
  747.       FILE(2,choice)                                        ;File in back of queue
  748.       total_que=total_que+1
  749.       SAMPST(total_que,2)
  750.    ENDIF
  751. ;*** Schedule next arrival
  752. ;*** Contrary to the style given in the book, schedule the next arrival
  753. ;*** as the last step in the arrival procedure; otherwise, the transfer
  754. ;*** variables may be overwritten.
  755.    transfer[1]=clock+EXPON(marrive)                         ;When the arrival
  756.    transfer[2]=1                                            ;Arrival code
  757.    transfer[4]=transfer[1]                                  ;Stamp the original arrival time
  758.    FILE(3,1)
  759. ;*** An arrivalproc can be added which tests the length of the queue and
  760. ;*** removes the last arrival if too long...or it could be used to collect
  761. ;*** particular statistics.
  762.    IF arriveproc<>"" THEN
  763.       EXECPROC arriveproc
  764.    ENDIF
  765. ENDPROC
  766. WRITELIB libname arrive
  767. RELEASE PROCS arrive
  768.  
  769. ;*** Depart procs must perform two processes. It must handle the current
  770. ;*** departure and pull the next customer from the queue (or set the server's
  771. ;*** availability flag).
  772. PROC depart(teller)                                         ;Manage next departure
  773. PRIVATE delay,queue
  774.    queue=MIN(numque,teller)                                 ;Which queue is used
  775.    IF LSIZE[queue+1]=0 THEN                                 ;If queue is empty
  776.       REMOVE(1,numque+teller+1)                             ;Remove "in use" queue
  777.    ELSE
  778.       REMOVE(1,queue+1)                                     ;Remove first member in queue
  779.       total_que=total_que-1                                 ;Decrement total queue size
  780.       SAMPST(total_que,2)                                   ;Calculate total queue size
  781.       delay=clock-transfer[1]                               ;DELAY = time in queue
  782.       SAMPST(delay,1)                                       ;Calculate delay statistics
  783.       transfer[1]=clock+EXPON(mservice)                     ;Schedule service
  784.       transfer[2]=2
  785.       transfer[3]=teller                                    ;Teller number
  786.       transfer[5]=transfer[1]-transfer[4]                   ;Calculate time in system
  787.       FILE(3,1)                                             ;File in time order sequence
  788.    ENDIF
  789.    IF eoj="Serve" THEN                                      ;If track number of members through system
  790.       nojobs=nojobs+1                                       ;Increment counter
  791.       IF nojobs >= eojval THEN                              ;Should an immediate exit be scheduled
  792.          transfer[1]=clock
  793.          transfer[2]=3                                      ;Exit code
  794.          FILE(1,1)                                          ;File in front
  795.       ENDIF
  796.    ENDIF
  797. ;*** A departproc can be added which jockies the queues after every
  798. ;*** departure. An example proc is given in section three
  799.    IF departproc<>"" THEN
  800.       EXECPROC departproc
  801.    ENDIF
  802. ENDPROC
  803. WRITELIB libname depart
  804. RELEASE PROCS depart
  805.  
  806. ;*** The output proc builds a record in the RESULTS.DB table for each
  807. ;*** teller tested.
  808. PROC output()                                               ;Build RESULTS table
  809. PRIVATE avgquesize,maxaque,i,avgdelay,maxdelay,maxque
  810.     avgquesize=0                                            ;
  811.     maxaque=-1.E+20                                         ;Initialize variables
  812.     FOR I FROM 1 TO numque
  813.         FILEST(I+1)                                         ;Get each queue's statistics
  814.         avgquesize=avgquesize+transfer[1]
  815.         IF transfer[2]>maxaque THEN maxaque=transfer[2] ENDIF
  816.     ENDFOR
  817.     SAMPST(0,-1)                                            ;Get DELAY statistics
  818.     AvgDelay=transfer[1]
  819.     MaxDelay=transfer[3]
  820.     SAMPST(0,-2)                                            ;Get statistics for total queue size
  821.     MaxQue=transfer[3]
  822.     MOVETO "results"
  823.     END DOWN
  824.     FOR I FROM 1 TO numtel                                  ;For each teller
  825.         [Run Number]=run_number                             ;Enter run number
  826.         [Number of Servers]=numtel                          ;Enter total number of tellers is test
  827.         [Average Number in Queue]=avgquesize                ;Average size of total queue
  828.         [Maximum Number in a Queue]=maxaque                 ;Max in one of the multiple queues
  829.         [Maximum Number in Queue]=maxque                    ;Max in all queues
  830.         [Average Delay]=AvgDelay                            ;Delay equals time standing in queue
  831.         [Maximum Delay]=MaxDelay                            ;
  832.         [Server Number]=I                                   ;Stats for this teller
  833.         FILEST(numque+I+1)                                  ;Get teller stats
  834.         MOVETO "results"
  835.         [Server Utilization]=transfer[1]
  836.         DOWN
  837.     ENDFOR
  838.     DO_IT!
  839. ENDPROC
  840. WRITELIB libname output
  841. RELEASE PROCS output
  842.  
  843. PROC setup_report()                                        ;Sets up a "percent done" scale
  844.   STYLE ATTRIBUTE 78  
  845.   oldpercentdone=0
  846.   newposition=0
  847.   oldposition=0
  848.   @13,12 ?? "╔═════════════════╤════════════════╤═══════════════════╗"
  849.   @14,12 ?? "║   RUN NUMBER    │ SERVER NUMBER  │ PERCENT COMPLETED ║"
  850.   @15,12 ?? "║                 │                │            %      ║"
  851.   @16,12 ?? "╟─────────────────┼────────────────┼───────────────────╢"
  852.   @17,12 ?? "║      CLOCK      │   QUEUE SIZE   │    JOB COUNTER    ║"
  853.   @18,12 ?? "║                 │                │                   ║"
  854.   @19,12 ?? "╟─────────────────┴────────────────┴───────────────────╢"
  855.   @20,12 ?? "║  PERCENT COMPLETED                                   ║"
  856.   @21,12 ?? "║  ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░  ║"
  857.   @22,12 ?? "║  0%        25%          50%         75%        100%  ║"
  858.   @23,12 ?? "╚══════════════════════════════════════════════════════╝"
  859.   passes=number_of_runs*(INT((maxserv-minserv+1)/increm))  ;How many passes will be made
  860.   STYLE ATTRIBUTE 79
  861. ENDPROC
  862. WRITELIB libname setup_report
  863. RELEASE PROCS setup_report
  864.  
  865. PROC update_report()                                       ;Update the scale
  866.   pass=run_number*(INT((numtel-minserv+1)/increm))
  867.   SWITCH
  868.     CASE eoj="Create" :
  869.          percentpass=MIN(1,nojobs/eojval)                  ;
  870.     CASE eoj="Serve"  :                                    ;Calculate percent completed for this pass
  871.          percentpass=MIN(1,nojobs/eojval)                  ;
  872.     CASE eoj="Time"   :                                    ;
  873.          percentpass=MIN(1,clock/eojval)                   ;
  874.     CASE eoj="Queue"  :                                    ;<--- Can't be computed
  875.          percentpass=0                                     ;
  876.   ENDSWITCH
  877.   completed=((percentpass*100/passes)+(100*(pass-1)/passes))  ;What percent is completed?
  878.   percentdone=INT(completed)                               ;Used to display bar
  879.   IF percentdone>=(oldpercentdone+2) THEN                  ;If the percent is large enough
  880.     newposition=INT((percentdone)/2)                       ;Calc the number of places to print
  881.     STYLE ATTRIBUTE 79  
  882.     @21,15+oldposition ?? FILL("█",newposition-oldposition)  ;Print bar
  883.     oldposition=newposition
  884.     oldpercentdone=percentdone
  885.   ENDIF
  886.   @15,16 ?? FORMAT("W6",run_number)                        ;
  887.   @15,35 ?? FORMAT("W6",numtel)                            ;Print results
  888.   @15,53 ?? FORMAT("W7.2",completed)                       ;
  889.   @18,16 ?? FORMAT("W10.4",clock)                          ;
  890.   @18,35 ?? FORMAT("W6",total_que)                         ;
  891.   IF nojobs>0 THEN                                         ;<--- Are we keeping track?
  892.      @18,51 ?? FORMAT("W10",nojobs)                        ;
  893.   ENDIF
  894. ENDPROC
  895. WRITELIB libname update_report
  896. RELEASE PROCS update_report
  897. ;
  898. ;╔════════════════════════════════════════════════════════════════════════════╗
  899. ;║                    SECTION THREE - Supporting script(s)                    ║
  900. ;╚════════════════════════════════════════════════════════════════════════════╝
  901. ;
  902. ;*** JOCKEY checks the queue and bounces one customer around based upon current
  903. ;*** server status and queue lengths.
  904. ;*** To test this procedure select a multiple server/multiple queue system
  905. ;*** and define the departure proc as "JOCKEY" (omit quotes)
  906. PROC jockey()
  907. PRIVATE I,savail
  908.     IF total_que=0 THEN
  909.        RETURN
  910.     ENDIF
  911.     savail=FALSE
  912.     FOR teller FROM 1 TO numtel
  913.         IF LSIZE[numque+teller+1]=0 THEN                    ;Is server available?
  914.            savail=TRUE
  915.            QUITLOOP
  916.         ENDIF
  917.     ENDFOR
  918. ;*** If a server is available and another queue has records then bounce
  919. ;*** from another queue.
  920.     IF savail THEN
  921.        FOR queue FROM 1 TO numque
  922.            IF lsize[queue+1]<>0 THEN
  923.                 REMOVE(1,queue+1)                           ;Remove first member in queue
  924.                 total_que=total_que-1                       ;Decrement total queue size
  925.                 SAMPST(total_que,2)                         ;Calculate total queue size
  926.                 delay=clock-transfer[1]                     ;DELAY = time in queue
  927.                 SAMPST(delay,1)                             ;Calculate delay statistics
  928.                 transfer[1]=clock+EXPON(mservice)           ;Schedule service
  929.                 transfer[2]=2
  930.                 transfer[3]=teller                          ;Teller number
  931.                 transfer[5]=transfer[1]-transfer[4]         ;Calculate time in system
  932.                 FILE(3,1)                                   ;File in time order sequence
  933.                 FILE(1,numque+teller+1)                     ;Make server busy
  934.                 RETURN
  935.            ENDIF
  936.        ENDFOR
  937. ;*** Otherwise, just play with the queue lenghts.
  938.     ELSE
  939.       shortest_q = 1.E+20                                   ;Determine shortest Queue
  940.       FOR I from 1 TO numque                                ;
  941.           IF LSIZE[I+1] < shortest_q THEN                   ;
  942.              shortest_q=LSIZE[I+1]                          ;
  943.              choice1=I+1                                    ;
  944.           ENDIF                                             ;
  945.       ENDFOR
  946.       longest_q =-1.E+20                                    ;Determine longest Queue
  947.       FOR I from 1 TO numque                                ;
  948.           IF LSIZE[I+1] > longest_q THEN                    ;
  949.              longest_q=LSIZE[I+1]                           ;
  950.              choice2=I+1                                    ;
  951.           ENDIF                                             ;
  952.       ENDFOR
  953.       IF longest_q>(shortest_Q+2) THEN
  954.          REMOVE(2,choice2)                                  ;Remove last member in longest queue
  955.          FILE(2,choice1)                                    ;File as last member in shortest queue
  956. ;*** Theoretically, this member could be stuck in the system all day.
  957. ;*** Then again...this has happened to me a few times.
  958.       ENDIF
  959.     ENDIF
  960. ENDPROC
  961. WRITELIB libname jockey
  962. RELEASE PROCS jockey
  963.  
  964. ;*** The PISSOFF proc is just a little post-processing procedure which
  965. ;*** utilizes history (MASTER2.DB) to due a little further analysis.
  966. ;*** It not meant to be used for every simulation run...its only an example.
  967. PROC pissoff()
  968. CLEAR RESET
  969. Query
  970.  
  971.  Master2 |  List   | Attribute #1 | Attribute #2 | Attribute #3 |
  972.          | Check 1 | Check        | Check 2      | Check        |
  973.  
  974.  Master2 | Attribute #4 | Attribute #5 |
  975.          | Check        | Check        |
  976.  
  977. Endquery
  978. DO_IT!
  979. ;*** Where [List]=1 and [Attribute #2]=2 the record represents the last
  980. ;*** exit of a customer. Furthermore [Attribute #5] on these records
  981. ;*** represents the total time in the system
  982. ARRAY peeved[6]
  983. FOR I FROM 1 TO 6
  984.     peeved[I]=0
  985. ENDFOR
  986. VIEW "answer"
  987. MOVETO [Attribute #5]
  988. SCAN
  989.     SWITCH
  990.        CASE [] > 30 :
  991.            peeved[6]=peeved[6]+1
  992.        CASE [] > 25 :
  993.            peeved[5]=peeved[5]+1
  994.        CASE [] > 20 :
  995.            peeved[4]=peeved[4]+1
  996.        CASE [] > 15 :
  997.            peeved[3]=peeved[3]+1
  998.        CASE [] > 10 :
  999.            peeved[2]=peeved[2]+1
  1000.        OTHERWISE    :
  1001.            peeved[1]=peeved[1]+1
  1002.     ENDSWITCH
  1003. ENDSCAN
  1004. @0,0 ?? "Annoyance Ratio"
  1005. @1,0 ?? "---------------"
  1006. FOR I FROM 1 TO 6
  1007.     @I+2,0 ?? "Customers annoyed at "+strval(FORMAT("W4",(I-1)*20))+"% "+strval(FORMAT("w5",peeved[I]))
  1008. ENDFOR
  1009. MESSAGE "Press any key to continue"
  1010. x=getchar()
  1011. RESET
  1012. ENDPROC
  1013. WRITELIB libname pissoff
  1014. RELEASE PROCS pissoff
  1015.