\ Forth Inc. benchmark tests adapted by Tom Zimmer, MPE, et. al.
\ Other tests added by MPE.

\ The application tests have been separated from the primitive tests.
\ Constants have been declared and modified so that the runtimes
\ of the application tests (Sieve, Fibonacci, QuickSort) can be
\ made similar.

\ The QuickSort test has been refactored to reduce the effect of the
\ array initialisation, and this is tested in a separate test.

\ Note that SwiftForth 2.0 includes special optimiser rules to eliminate
\ some of the benchmark code! This is seen in the some of the primitive
\ test results which are faster than the DO ... LOOP test.

\ Note th use of the word [o/n] whose job is to stop some optimising compilers
\ from throwing away the multiply and divide operations.
\ The implementation of [o/n] should lay a NOP opcode opcode on optimising
\ systems, and may be an immediate NOOP on others

decimal

\ ************************************************
\ Select system to be tested, set FORTHSYSTEM
\ to value of selected target.
\ Set SPECIFICS false to avoid system dependencies
\ ************************************************

7 constant ForthSystem
false constant specifics        \ true to use system dependent code

1  constant PfwVfx              \ MPE ProForth VFX 3.0
2  constant Pfw22               \ MPE ProForth 2.2
3  constant SwiftForth20        \ FI SwiftForth 2.0
4  constant SwiftForth15        \ FI SwiftForth 1.5
5  constant Win32Forth          \ Win32Forth 4.2
6  constant BigForth            \ BigForth 26sep1999
7  constant BigForth-Linux      \ BigForth 26sep1999

: .specifics    \ -- ; display trick state
  ."  using"  specifics 0=
  if  ."  no"  then
  ."  extensions"
;


\ ********************
\ ProForth VFX harness
\ ********************

PfwVfx ForthSystem = [if]
extern: DWORD PASCAL GetTickCount( void )

: COUNTER       \ -- ms
  GetTickCount ;

: >pos          \ n -- ; step to position n
  out @ - spaces
;

: [o/n]         \ --
  postpone []
; immediate
[then]


\ ********************
\ ProForth 2.2 harness
\ ********************

Pfw22 ForthSystem = [if]

include valPFW22

: COUNTER       \ -- ms
  WinGetTickCount ;

: >pos          \ n -- ; step to position n
  out @ - spaces
;

: M/            \ d n1 -- quot
  m/mod nip
;

: buffer:       \ n -- ; -- addr
  create
    here  over allot  swap erase
;

: m+            \ d n -- d'
  s>d d+
;

: [o/n]         \ -- ; stop optimiser treating * DROP etc as no code
; immediate

: SendMessage   \ hwn msg wparam lparam -- result
  WinSendMessage
;
[then]


\ ********************
\ SwiftForth15 harness
\ ********************

SwiftForth15 ForthSystem = [if]
: >pos          \ n -- ; step to position n
  c# @ - spaces
;

: [o/n]         \ -- ; stop optimiser treating * DROP etc as no code
; immediate
[then]


\ ********************
\ SwiftForth20 harness
\ ********************

SwiftForth20 ForthSystem = [if]
: >pos          \ n -- ; step to position n
  get-xy drop - spaces
;

: [o/n]         \ -- ; stop optimiser treating * DROP etc as no code
  postpone noop
; immediate
[then]


\ ******************
\ Win32Forth harness
\ ******************

Win32Forth ForthSystem = [if]
: COUNTER       \ -- ms
  Call GetTickCount ;

: >pos          \ n -- ; step to position n
  getxy drop - spaces
;

: M/            \ d n1 -- quot
  fm/mod nip
;

: buffer:       \ n -- ; -- addr
  create
    here  over allot  swap erase
;

: 2-            \ n -- n-2
  2 -
;

: [o/n]         \ -- ; stop optimiser treating * DROP etc as no code
; immediate

: SendMessage   \ h m w l -- res
  swap 2swap swap               \ Win32Forth uses reverse order
  Call SendMessage
;

: GetTickCount  \ -- ms
  Call GetTickCount
;
[then]


\ ******************
\ BigForth harness
\ ******************

BigForth ForthSystem =
BigForth-Linux ForthSystem =  or
[if]

include ans.fs

Code u2/        \ n -- n/2
  1 # AX shr
  Next
end-code  macro

: COUNTER       \ -- ms
  timer@ >us &1000 um/mod nip ;

: >pos          \ n -- ; step to position n
  at? swap drop - spaces
;

: buffer:       \ n -- ; -- addr
  create
    here  over allot  swap erase
;

: [o/n]         \ -- ; stop optimiser treating * DROP etc as no code
; immediate

BigForth ForthSystem = [if]
also DOS
1 legacy !
4 User32 SendMessage SendMessageA       ( l w m h -- res )
0 kernel32 GetTickCount GetTickCount    ( -- ticks )
legacy on
previous

0 constant HWND_DESKTOP
16 constant WM_CLOSE
[else]
: GetTickCount  timer@ >us &1000 um/mod nip ;
: SendMessage  2drop 2drop 0 ;

0 constant HWND_DESKTOP
16 constant WM_CLOSE
[then]
[then]


\ ************************************
\ FORTH, Inc.  32 Bit Benchmark Source
\ ************************************

CELL NEGATE CONSTANT -CELL

CR .( Loading benchmark routines)


\ ***********************
\ Benchmark support words
\ ***********************

\ column positions
40 constant time-pos
50 constant iter-pos
60 constant each-pos

: .HEADER       \ -- ; display test header
  cr ." Test time including overhead"
  time-pos 3 + >pos  ." ms"
  iter-pos >pos ." times"
  each-pos >pos  ." ns (each)"
;

: TIMER ( ms iterations -- )
  >r                                    \ number of iterations
  counter swap -                        \ elapsed time in ms
  time-pos >pos  dup 5 .r
  iter-pos >pos  r@ .
  r@ 1 >
  if
    each-pos >pos
    1000000 r> */ 5 .r
  else
    drop  r> drop
  then
;

: .ann          \ -- ; banner announcment
  CR  ;

: [$            \ -- ms
  COUNTER ;

\ $]  is the suffix to a testing word.  It takes the fast ticks
\    timer value and calculates the elapsed time.  It does do
\    some display words before calculating the time, but it is
\    assumed that this will take minimal time to execute.

: $]            ( n -- )   TIMER ;

\ CARRAY  creates a byte size array.
: CARRAY ( n)   CREATE  ALLOT
                DOES> ( n - a)  + ;

\ ARRAY  creates a word size array.
: ARRAY ( n)    CREATE  CELLS ALLOT
                DOES> ( n - a) SWAP CELLS + ;


\ ****************************
\ Basic FORTH, Inc. Benchmarks
\ ****************************
\ This series of tests analyses the Forth primitives.

1000000 constant /prims
\ -- #iterations; all of these words return the number of iterations
: $DO$    .ann ." DO LOOP"  [$  /prims DUP 0 DO  I [o/n] DROP LOOP  $] ;
: $*$     .ann ." *"        [$  /prims DUP 0 DO  I I * [o/n] DROP  LOOP  $] ;
: $/$     .ann ." /"        [$  /prims DUP 1+ 1 DO  1000 I / [o/n] DROP LOOP  $] ;
: $+$     .ann ." +"        [$  /prims DUP 1+ 1 DO  1000 I + [o/n] DROP  LOOP  $] ;
: $M*$    .ann ." M*"       [$  /prims DUP    0 DO  I I M* [o/n] 2DROP  LOOP  $] ;
: $M/$    .ann ." M/"       [$  /prims DUP 1+ 1 DO  1000 0 I M/ [o/n] DROP  LOOP  $] ;
: $M+$    .ann ." M+"       [$  /prims DUP 1+ 1 DO  1000 0 I M+ [o/n] 2DROP  LOOP  $] ;
: $/MOD$  .ann ." /MOD"     [$  /prims DUP 1+ 1 DO  1000 I /MOD [o/n] 2DROP  LOOP  $] ;

\ $*/$  tests the math primitive  */ .  This may or may not tell
\    you how the other math primitives perform depending on
\    how  */  has been coded.
: $*/$    .ann ." */"       [$  /prims DUP 1+ 1 DO  I I I */ [o/n] DROP  LOOP  $] ;


\ ****************************************
\ Eratosthenes sieve benchmark program
\ This is NOT the original BYTE benchmark.
\ ****************************************

8190 CONSTANT SIZE
SIZE buffer: FLAGS

: DO-PRIME
      1000 0 DO
               FLAGS SIZE -1 FILL
               0 SIZE 0
               DO I FLAGS + C@
                    IF I 2* 3 + DUP I +
                         BEGIN DUP SIZE <
                         WHILE DUP FLAGS + 0 SWAP C! OVER +
                         REPEAT 2DROP
                              1+
                    THEN
               LOOP
               DROP
         LOOP
           ;

: $SIEVE$   .ann ." Eratosthenes sieve "  [$  DO-PRIME  SIZE 1000 *  ." 1899 Primes"  $] ;


\ *******************
\ Fibonacci recursion
\ *******************

34 constant /fib

: FIB ( n -- n' )
   DUP 1 > IF
      DUP 1- RECURSE  SWAP 2-  RECURSE  +
   THEN ;

: $FIB$
   .ann ." Fibonacci recursion ( "
   [$  /fib dup . ." -> " FIB dup . ." )" /fib - $] ;


\ *********************************
\ QuickSort from Hoare & Wil Baden
\ also contains the array fill test
\ *********************************

7 CELLS CONSTANT THRESHOLD

specifics [if]

PfwVfx ForthSystem = [if]
%macro Precedes ( n1 n2 -- f )
  u<
%endmacro
%macro Exchange ( a1 a2 -- )
  2dup @ swap @ rot ! swap !
%endmacro
[then]

Pfw22 ForthSystem = [if]
: Precedes  ( n n - f )    u< ;
: Exchange  ( a1 a2 -- )   2DUP  @ SWAP @ ROT !  SWAP ! ;
[then]

SwiftForth15 ForthSystem = [if]
: Precedes  ( n n - f )    u< ;
: Exchange  ( a1 a2 -- )   2DUP  @ SWAP @ ROT !  SWAP ! ;
[then]

SwiftForth20 ForthSystem = [if]
: Precedes  ( n n - f )    u< ;
: Exchange  ( a1 a2 -- )   2DUP  @ SWAP @ ROT !  SWAP ! ;
[then]

Win32Forth ForthSystem = [if]
: Precedes  ( n n - f )    u< ;
: Exchange  ( a1 a2 -- )   2DUP  @ SWAP @ ROT !  SWAP ! ;
[then]

BigForth ForthSystem =
BigForth-Linux ForthSystem = or [if]
: Precedes  ( n n - f )    u< ; macro
: Exchange  ( a1 a2 -- )   2DUP  @ SWAP @ ROT !  SWAP ! ; macro
[then]

[else]
: Precedes  ( n n - f )    u< ;
: Exchange  ( a1 a2 -- )   2DUP  @ SWAP @ ROT !  SWAP ! ;
[then]

: Both-Ends  ( f l pivot - f l )
    >R  BEGIN   OVER @ R@ precedes
        WHILE  CELL 0 D+   REPEAT
        BEGIN   R@ OVER @ precedes
        WHILE  CELL -      REPEAT   R> DROP ;

: Order3  ( f l - f l pivot)   2DUP OVER - 2/ -CELL AND + >R
      DUP @ R@ @ precedes IF DUP R@ Exchange THEN
      OVER @ R@ @ SWAP precedes
        IF OVER R@ Exchange  DUP @ R@ @ precedes
          IF DUP R@ Exchange THEN  THEN   R>  ;

: Partition  ( f l - f l' f' l)   Order3 @ >R  2DUP
      CELL -CELL D+  BEGIN    R@ Both-Ends 2DUP 1+ precedes
      IF  2DUP Exchange CELL -CELL D+  THEN
      2DUP SWAP precedes   UNTIL R> DROP SWAP ROT ;

: Sink  ( f key where - f)   ROT >R
   BEGIN   CELL - 2DUP @ precedes
   WHILE  DUP @ OVER CELL + !  DUP R@ =
        IF  ! R> EXIT THEN   ( key where)
   REPEAT  CELL + ! R> ;

: Insertion  ( f l)   2DUP precedes
    IF  CELL + OVER CELL +   DO  I @ I Sink  CELL +LOOP DROP
    ELSE  ( f l) 2DROP  THEN ;

: Hoarify  ( f l - ...)
    BEGIN   2DUP THRESHOLD 0 D+ precedes
    WHILE  Partition  2DUP - >R  2OVER - R> >  IF 2SWAP THEN
    REPEAT Insertion ;

: QUICK  ( f l)   DEPTH >R   BEGIN  Hoarify DEPTH R@ <
      UNTIL  R> DROP ;

: SORT  ( a n)   DUP 0= ABORT" Nothing to sort "
    1- CELLS  OVER +  QUICK ;

10000 constant /array
/array 1+ array pointers

: fillp         \ -- ; fill sort array once
  /array 0 ?DO  /array I -  I POINTERS !  LOOP ;

: $FILL$  .ann ." ARRAY fill"   [$  100 0 DO  fillp  LOOP  100 /array * $]  ;

: (sort)  100 0 DO   fillp  0 POINTERS 10000 SORT   LOOP ;

: $SORT$
  .ann ." Hoare's quick sort (reverse order)  "
  [$  (sort) 100 /array *  $] ;


\ *********************************
\ "Random" Numbers
\ *********************************

1024 constant /random

variable ShiftRegister
       1 ShiftRegister !

: RandBit       \ -- 0..1 ; Generates a "random" bit.
  ShiftRegister @ $00000001 and         \ Gen result bit for this time thru.
  dup 0<>                               \ Tap at position 31.
  ShiftRegister @ $00000008 and 0<>     \ Tap at position 28.
  xor 0<>                               \ If the XOR of the taps is non-zero...
  if
    $40000000                           \ ...shift in a "one" bit else...
  else
    $00000000                           \ ...shift in a "zero" bit.
  then
  ShiftRegister @ u2/                   \ Shift register one bit right.
  or                                    \ OR in new left-hand bit.
  ShiftRegister !                       \ Store new shift register value.
;

: RandBits      \ n -- 0..2^(n-1) ; Generate an n-bit "random" number.
  0                                     \ Result's start value.
  swap 0
  do
    2* RandBit or                       \ Generate next "random" bit.
  loop
;

: (randtest)    \ --
  1 ShiftRegister !
  /random 256 cells * allocate
  if
    cr ." Failed to allocate " /random . ." kb for test"
    abort
  then
  /random 256 * 0 do 32 RandBits over i cells + ! loop
  free drop
;

: $RAND$
  .ann ." Generate random numbers (" /random . ." kb array)"
  [$  (randtest)  /random 256 * $] ;


\ *********************************
\ LZ77 compression
\ *********************************

0       Value   lz77-buffer
0       Value   lz77-Pos
0       Value   lz77-BytesLeft

100 constant /lz77-size

: init-test-buffer
  /lz77-size 256 cells * to lz77-BytesLeft
  lz77-BytesLeft allocate
  if
    cr ." Failed to allocate " /lz77-size . ." kb for test"
    abort
  then
  dup to lz77-buffer to lz77-pos
  /lz77-size 256 * 0
  do  32 randbits lz77-buffer i cells + !  loop
;

: free-test-buffer
  lz77-buffer free drop
;

: getnextchar           \ -- char true | false
  lz77-BytesLeft dup
  if
    drop
    lz77-BytesLeft 1- to lz77-BytesLeft
    lz77-Pos dup 1+ to lz77-Pos
    c@
    true
  then
;

: lz77-read-file        \ addr len fileid -- u2 ior
  drop
  0 rot rot
  0 do                  \ done addr --
    getnextchar if
      over c! 1+ swap 1+ swap
    else
      leave
    then
  loop
  drop 0
;

: lz77-write-file       \ addr len fileid -- ior
  drop 2drop 0
;

: closed
  drop
;

: checked       \ flag --
  ABORT" File Access Error. " ;

: read-char     \ file -- char 
        drop getnextchar 0= if -1 then
;

(     LZSS -- A Data Compression Program )
(     89-04-06 Standard C by Haruhiko Okumura )
(     94-12-09 Standard Forth by Wil Baden )
(     Use, distribute, and modify this program freely. )

4096  CONSTANT    N     ( Size of Ring Buffer )
18    CONSTANT    F     ( Upper Limit for match-length )
2     CONSTANT    Threshold ( Encode string into position & length
                        ( if match-length is greater. )
N     CONSTANT    Nil   ( Index for Binary Search Tree Root )

VARIABLE    textsize    ( Text Size Counter )
VARIABLE    codesize    ( Code Size Counter )
\ VARIABLE  printcount  ( Counter for Reporting Progress )

( These are set by InsertNode procedure. )

VARIABLE    match-position
VARIABLE    match-length

N F + 1 -   carray text-buf   ( Ring buffer of size N, with extra
                  ( F-1 bytes to facilitate string comparison. )

( Left & Right Children and Parents -- Binary Search Trees )

N 1 +             array lson
N 257 +           array rson
N 1 +             array dad

specifics  PfwVfx ForthSystem = and [if]
  0 lson constant .lson   %cmacro lson  cells .lson +  %endmacro
  0 rson constant .rson   %cmacro rson  cells .rson +  %endmacro
  0 dad  constant .dad    %cmacro dad   cells .dad  +  %endmacro
[then]

( Input & Output Files )

0 VALUE     infile      0 VALUE     outfile

( For i = 0 to N - 1, rson[i] and lson[i] will be the right and
( left children of node i.  These nodes need not be initialized.
( Also, dad[i] is the parent of node i.  These are initialized to
( Nil = N, which stands for `not used.'
( For i = 0 to 255, rson[N + i + 1] is the root of the tree
( for strings that begin with character i.  These are initialized
( to Nil.  Note there are 256 trees. )

( Initialize trees. )

: InitTree                                ( -- )
      N 257 +  N 1 +  DO    Nil  I rson !    LOOP
      N  0  DO    Nil  I dad !    LOOP
;

( Insert string of length F, text_buf[r..r+F-1], into one of the
( trees of text_buf[r]'th tree and return the longest-match position
( and length via the global variables match-position and match-length.
( If match-length = F, then remove the old node in favor of the new
( one, because the old one will be deleted sooner.
( Note r plays double role, as tree node and position in buffer. )

: InsertNode                              ( r -- )

      Nil OVER lson !    Nil OVER rson !    0 match-length !
      DUP text-buf C@  N +  1 +                 ( r p)

      1                                         ( r p cmp)
      BEGIN                                     ( r p cmp)
            0< not IF                           ( r p)

                  DUP rson @ Nil = not IF
                        rson @
                  ELSE

                        2DUP rson !
                        SWAP dad !              ( )
                        EXIT

                  THEN
            ELSE                                ( r p)

                  DUP lson @ Nil = not IF
                        lson @
                  ELSE

                        2DUP lson !
                        SWAP dad !              ( )
                        EXIT

                  THEN
            THEN                                ( r p)

            0 F DUP 1 DO                        ( r p 0 F)

                  3 PICK I + text-buf C@        ( r p 0 F c)
                  3 PICK I + text-buf C@ -      ( r p 0 F diff)
                  ?DUP IF
                        NIP NIP I
                        LEAVE
                  THEN                          ( r p 0 F)

            LOOP                                ( r p cmp i)

            DUP match-length @ > IF

                  2 PICK match-position !
                  DUP match-length !
                  F < not

            ELSE
                  DROP FALSE
            THEN                                ( r p cmp flag)
      UNTIL                                     ( r p cmp)
      DROP                                      ( r p)

      2DUP dad @ SWAP dad !
      2DUP lson @ SWAP lson !
      2DUP rson @ SWAP rson !

      2DUP lson @ dad !
      2DUP rson @ dad !

      DUP dad @ rson @ OVER = IF
            TUCK dad @ rson !
      ELSE
            TUCK dad @ lson !
      THEN                                      ( p)

      dad Nil SWAP !    ( Remove p )            ( )
;

( Deletes node p from tree. )

: DeleteNode                              ( p -- )

      DUP dad @ Nil = IF    DROP EXIT    THEN   ( Not in tree. )

      ( CASE )                                  ( p)
            DUP rson @ Nil =
      IF
            DUP lson @
      ELSE
            DUP lson @ Nil =
      IF
            DUP rson @
      ELSE

            DUP lson @                          ( p q)

            DUP rson @ Nil = not IF

                  BEGIN
                        rson @
                        DUP rson @ Nil =
                  UNTIL

                  DUP lson @ OVER dad @ rson !
                  DUP dad @ OVER lson @ dad !

                  OVER lson @ OVER lson !
                  OVER lson @ dad OVER SWAP !
            THEN

            OVER rson @ OVER rson !
            OVER rson @ dad OVER SWAP !

      ( ESAC ) THEN THEN                        ( p q)

      OVER dad @ OVER dad !

      OVER DUP dad @ rson @ = IF
            OVER dad @ rson !
      ELSE
            OVER dad @ lson !
      THEN                                      ( p)

      dad Nil SWAP !                            ( )
;

      17 carray   code-buf

      VARIABLE    len
      VARIABLE    last-match-length
      VARIABLE    code-buf-ptr

      VARIABLE    mask

: Encode                                  ( -- )

      0 textsize !    0 codesize !

      InitTree    ( Initialize trees. )

      ( code_buf[1..16] saves eight units of code, and code_buf[0]
      ( works as eight flags, "1" representing that the unit is an
      ( unencoded letter in 1 byte, "0" a position-and-length pair
      ( in 2 bytes.  Thus, eight units require at most 16 bytes
      ( of code. )

      0 0 code-buf C!
      1 mask C!   1 code-buf-ptr !
      0    N F -                                ( s r)

      ( Clear the buffer with any character that will appear often. )

      0 text-buf  N F -  BL  FILL

      ( Read F bytes into the last F bytes of the buffer. )

      DUP text-buf F infile LZ77-READ-FILE checked   ( s r count)
      DUP len !    DUP textsize !
      0= IF    EXIT    THEN                     ( s r)

      ( Insert the F strings, each of which begins with one or more
      ( `space' characters.  Note the order in which these strings
      ( are inserted.  This way, degenerate trees will be less
      ( likely to occur. )

      F 1 + 1 DO                                ( s r)
            DUP I - InsertNode
      LOOP

      ( Finally, insert the whole string just read.  The
      ( global variables match-length and match-position are set. )

      DUP InsertNode

      BEGIN                                     ( s r)
\            key? drop
            ( match_length may be spuriously long at end of text. )
            match-length @ len @ > IF    len @ match-length !   THEN

            match-length @ Threshold > not IF

                  ( Not long enough match.  Send one byte. )
                  1 match-length !
                  ( `send one byte' flag )
                  mask C@ 0 code-buf C@ OR 0 code-buf C!
                  ( Send uncoded. )
                  DUP text-buf C@ code-buf-ptr @ code-buf C!
                  1 code-buf-ptr +!

            ELSE
                  ( Send position and length pair.
                  ( Note match-length > Threshold. )

                  match-position @  code-buf-ptr @ code-buf C!
                  1 code-buf-ptr +!

                  match-position @  8 RSHIFT  4 LSHIFT ( . . j)
                        match-length @  Threshold -  1 -  OR
                        code-buf-ptr @  code-buf C!  ( . .)
                  1 code-buf-ptr +!

            THEN

            ( Shift mask left one bit. )        ( . .)

            mask C@  2*  mask C!    mask C@ 0= IF

                  ( Send at most 8 units of code together. )

                  0 code-buf  code-buf-ptr @    ( . . a k)
                        outfile LZ77-WRITE-FILE checked ( . .)
                  code-buf-ptr @  codesize  +!
                  0 0 code-buf C!    1 code-buf-ptr !    1 mask C!

            THEN                                ( s r)

            match-length @ last-match-length !

            last-match-length @ DUP 0 DO        ( s r n)

                  infile read-char              ( s r n c)
                  DUP 0< IF   2DROP I LEAVE   THEN

                  ( Delete old strings and read new bytes. )

                  3 PICK DeleteNode
                  DUP 3 1 + PICK text-buf C!

                  ( If the position is near end of buffer, extend
                  ( the buffer to make string comparison easier. )

                  3 PICK F 1 - < IF             ( s r n c)
                        DUP 3 1 + PICK N + text-buf C!
                  THEN
                  DROP                          ( s r n)

                  ( Since this is a ring buffer, increment the
                  ( position modulo N. )

                  >R >R                         ( s)
                        1 +    N 1 - AND
                  R>                            ( s r)
                        1 +    N 1 - AND
                  R>                            ( s r n)

                  ( Register the string in text_buf[r..r+F-1]. )

                  OVER InsertNode

            LOOP                                ( s r i)
            DUP textsize +!

            \ textsize @  printcount @ > IF

            \     ( Report progress each time the textsize exceeds
            \     ( multiples of 1024. )
            \     textsize @ 12 .R
            \     1024 printcount +!

            \ THEN

            ( After the end of text, no need to read, but
            ( buffer may not be empty. )

            last-match-length @ SWAP ?DO        ( s r)

                  OVER DeleteNode

                  >R  1 +  N 1 - AND  R>
                  1 +  N 1 - AND

                  -1 len +!    len @ IF
                        DUP InsertNode
                  THEN
            LOOP

            len @ 0> not
      UNTIL                                     2DROP

      ( Send remaining code. )

      code-buf-ptr @ 1 > IF
            0 code-buf  code-buf-ptr @  outfile  LZ77-WRITE-FILE checked
            code-buf-ptr @ codesize +!
      THEN
;

: code77        \ --
  init-test-buffer
  encode
  free-test-buffer
;

: $CODE77$
  .ann ." LZ77 Comp. (" /lz77-size . ." kb Random Data Mem>Mem)"
  [$  code77  1 $] ;


\ *********************************
\ API Call OverHead
\ *********************************

HWND_DESKTOP VALUE hWnd

40000 constant /api1

: (api1)        \ -- ; SENDMESSAGE is probably the most used API function there is!
  hWnd WM_CLOSE 0 0 SendMessage drop
;

: $API1$        \ --
  .ann ." Win32 API: SendMessage"
  [$  /api1 0 do  (api1)  loop  /api1 $]
;

1000000 constant /api2

: $API2$        \ --
  .ann ." Win32 API: GetTickCount"
  [$  /api2 0 do  counter drop  loop  /api2 $]
;


\ *************************
\ The main benchmark driver
\ *************************

: BENCHMARK
   .ann ." This system's primitives" .specifics cr
   .header
   [$  
     $DO$
     $+$  $M+$
     $*$  $/$  $M*$  $M/$  $/MOD$  $*/$
     $FILL$  $API1$  $API2$
   CR  ." Total:"  1 $]
   CR
   .ann ." This system's application performance" .specifics CR
   .header
   [$  
     $SIEVE$  $FIB$  $SORT$  $RAND$  $CODE77$
   CR  ." Total:"  1 $]
;

BENCHMARK

CR CR .( To run the benchmark program again, type BENCHMARK )