Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

IBM - Telco Benchmark program

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • IBM - Telco Benchmark program

    My effort...

    Edit: added URL to obtain correct test file

    Code:
    ' File: telco0.bas
    ' IBM telco billing challenge
    ' SPECIFICATIONS AT : [url="http://www2.hursley.ibm.com/decimal/telco.html"]http://www2.hursley.ibm.com/decimal/telco.html[/url] 
    ' Author: Michael Mattias, Tal Systems Inc. Racine WI USA
    ' Code released to public domain by author
    ' Compiler: PowerBASIC Inc "PB/Windows" version 7.02
    ' Machine (for time comparisons)  400 Hmz PII, 128 Mb RAM. Normal mix of programs running.
    '
    
    #COMPILE  EXE
    #DEBUG    ERROR ON
    #REGISTER NONE       ' We'll assign our own, thank you.
    #DIM      ALL
    #TOOLS    OFF
    
    '---[ TEST FILES : Use the "BINARY" version]-----------
    ' The ten record file available at [url="http://www2.hursley.ibm.com/decimal/telco.testb"]http://www2.hursley.ibm.com/decimal/telco.testb[/url] 
    ' Obtain the 1 million record BINARY test file at [url="http://www2.hursley.ibm.com/decimal/expon180-1e6b.zip"]http://www2.hursley.ibm.com/decimal/expon180-1e6b.zip[/url] 
    '----------------------------
    '$INPUT_FILE_NAME    =  "s:\testdata\telco\telco.testb"       ' 10 record demo file, with Comp-5 data
    $INPUT_FILE_NAME    =  "s:\testdata\telco\expon180.1e6b"    ' 1 million record file, Comp-5
    
    $OUTPUT_FILE_NAME   =  "s:\testdata\telco\telco_out.txt"   ' required output file
    
    ' ========================
    ' CORRECT CONTROL TOTALS
    ' ========================
    ' FOR 1M RECORD FILE:
    ' sumT  = 1004737.58
    ' sumB  =   57628.30
    ' sumD  =   25042.17
    
    ' FOR 10 RECORD FILE
    ' sumT  =  8.91
    ' sumB  =   .50
    ' sumD  =   .22
    
    ' =====  RESULTS with Mempory-mapped input, output = 1 string per line, no alignment(per specs).
    '------------------------------------------
    ' t1:48.06 seconds; t2: 47.47; t3: 47.51
    
    '=====[Windows API Header Files] ============================
    '  If you don't need all of the functionality supported by these APIs
    '  (and who does?), you can selectively turn off various modules by putting
    '  the following constants in your program BEFORE you #include "win32api.inc":
    '
    '  %NOGDI = 1     ' no GDI (Graphics Device Interface) functions
    '  %NOMMIDS = 1   ' no Multimedia ID definitions
    %NOGDI   = 1
    %NOMMIDS = 1
    #INCLUDE "WIN32API.INC"    ' date: 5/0/02
    '==[End Windows API Header Files]============================
    ' File mapping:
    ' returns: TRUE = success, pointer to file data in piFDATA, FALSE=Error
    FUNCTION MapThisFile (szFileIn AS ASCIIZ, hSys AS LONG, hFileMapping AS LONG, cbMapping AS LONG) AS DWORD
    
      LOCAL w32 AS WIN32_FIND_DATA, hSearch AS LONG
      LOCAL pIFdata AS DWORD
      LOCAL dwShareMode AS DWORD, dwDesiredAccess AS DWORD, _  ' createfile flags
            dwCreationDisposition AS DWORD, dwFlagsAndAttributes AS DWORD
      LOCAL fProtect    AS DWORD                               ' createFileMapping flag
      LOCAL dwmvDesiredAccess AS DWORD                         ' mapviewoffileflags
    
      hSearch      = FindFirstFile (szFileIn, W32)
      IF hSearch <> -1& THEN
          cbMapping =  w32.nFileSizeLow
          CloseHandle hSearch
      ELSE
          FUNCTION = 0
          EXIT FUNCTION
      END IF
      
      ' set flags for CreateFile and MapViewOfFile
      dwShareMode             = %FILE_SHARE_READ  ' ms recommends exclusive use for mapping, I choose this
      dwDesiredAccess         = %GENERIC_READ
      dwCreationDisposition   = %OPEN_EXISTING
      fProtect                = %PAGE_READONLY
      dwMvDesiredAccess       = %FILE_MAP_READ
      dwFlagsAndAttributes    = %FILE_ATTRIBUTE_NORMAL
    
      ' Open the file for reading, get a system handle and map the entire file
      hSys          = CreateFile (szFileIn, dwDesiredAccess, dwShareMode, BYVAL %NULL, DwCreationDisposition, dwFlagsandAttributes, BYVAL %NULL)
      IF ISTRUE hSys  THEN
         hFileMapping     = CreateFileMapping (hSys, BYVAL %NULL, fProtect, BYVAL %NULL, BYVAL %NULL, BYVAL %NULL)
         IF ISTRUE hFileMapping THEN
             ' map the file to our memory space, returning the pointer..
             ' NOTE CALLING PROCEDURE NEEDS THE VALUES of pIFData, hSys and hFileMapping TO UNMAP the file
             pIFData    = MapViewOfFile (hFileMapping, dwMvDesiredAccess, BYVAL %NULL, BYVAL %NULL, BYVAL %NULL)
             FUNCTION   = pIFDATA
         ELSE
             FUNCTION    = 0
             Closehandle hSys
             EXIT FUNCTION
         END IF
      ELSE
          FUNCTION = 0
          EXIT FUNCTION
      END IF
    
    END FUNCTION
    
    FUNCTION UnmapMappedFile (pIfData AS DWORD, hSys AS LONG, hFileMapping AS LONG) AS LONG
        UnmapViewOfFile pIFData
        CloseHandle     hFileMapping
        CloseHandle     hSys
    END FUNCTION
    
    
    FUNCTION WINMAIN (BYVAL hInstance     AS LONG, _
                      BYVAL hPrevInstance AS LONG, _
                      lpCmdLine           AS ASCIIZ PTR, _
                      BYVAL iCmdShow      AS LONG) AS LONG
    
       CALL ProcessTheFilePerSpecs ()
    
    END FUNCTION
    
    ' ----------------------------------------------------------------------------------------------
    '  PROCESS THE FILE USING FILE MAPPING INPUT, SINGLE STRING SEQUENTIAL OUTPUT (output per spec)
    ' ----------------------------------------------------------------------------------------------
    FUNCTION ProcessTheFilePerSpecs () AS LONG
     'The compiler provides four extended precision REGISTER variables... so let's tell the compiler
     'the ones we will be  using the most
    
      REGISTER  sumB   AS EXT,  _   ' sum of basic tax
                pRate  AS EXT,  _   ' basic price, used every line
                sumt   AS EXT, _    ' sum of everything
                p      AS EXT       ' Line item 'base' price
    
      ' We also get two integer-class REGISTER variables to use..these are both used on every record
      REGISTER Z AS LONG, _      ' loop counter
           isDistance AS LONG    ' line by line is this a distance call flag
    
      LOCAL bRate    AS   EXT     ' basic tax rate, 6.75% applies to each call
      LOCAL dRate    AS   EXT     ' distance tax rate, 3.41% applies to distance calls only
      LOCAL sumD     AS   EXT     ' Sum of distance tax; since not used every line, no sense wasting a register var on it.
    
    
      ' other working variables
      LOCAL s AS STRING, sTotal AS STRING, nRec AS LONG
      LOCAL szInFile AS ASCIIZ * %MAX_PATH, szOutFile AS ASCIIZ * %MAX_PATH, hOut AS LONG
    
      LOCAL q   AS QUAD         ' for input data buffering
      LOCAL pQ  AS QUAD PTR     ' ditto
      LOCAL pb  AS BYTE PTR     ' for converting input to Intel format
      LOCAL b AS EXT            ' line item basic tax
      LOCAL d AS EXT            ' line item distance tax
      LOCAL t AS EXT            ' line item total price
    
      LOCAL pRateLocal  AS EXT, pRateDistance AS EXT
    
    
    ' timer
     LOCAL TimeStart AS DOUBLE, TimeEnd AS DOUBLE, TimeElapsed AS DOUBLE
    ' filemapping input side:
     LOCAL hSys AS LONG, hFileMapping AS LONG, cbmapping AS LONG, pMBase AS DWORD
     ' output vars
     LOCAL sOut AS STRING
    
    
     ' if output file exists, erase it. (Before starting timer)
        szOutFile     =   $OUTPUT_FILE_NAME
        IF DIR$(szOutFile) > "" THEN
             KILL szOutFile
        END IF
    
     ' start timer  (After erasing the output file, if it exists)
      TimeStart = TIMER
    
     ' set constants
     pRateLocal    = .0013##
     pRateDistance = .00894##
     bRate         =  .0675##
     dRate         =  .0341##
     szInFile      =   $INPUT_FILE_NAME
     pb            =   VARPTR(Q)      ' where we swap bytes
    
     ' initialize total fields. Not necessary with this compiler, but condition of specifications)
     sumb = 0##
     sumt = 0##
     sumd = 0##
    
      ' ------------------------------------------
      '       OPEN THE INPUT FILE
      ' ------------------------------------------
      ' Map the file to a memory-mapped file object, returning a pointer to the first record.
    
       pMBASE    =  MapThisFile (szInFile, hSys, hFileMapping, cbMapping)
       nRec      =  cbMapping \ SIZEOF(q)
    
       ' OPEN THE OUTPUT FILE
    
       hOut  = FREEFILE
       OPEN    szOutFile  FOR OUTPUT AS hOut LEN = 65536
    
    '   Set our MMF pointers:
        pQ     = pmBASE     ' point input  at first record of input file
    
      ' ------------------------------------------
      ' Process Each record of input file
      ' Loop is entered with pq pointing at the next unprocessed input record
      ' ------------------------------------------
       FOR Z = 1 TO nRec
    
          Q        = @pQ    ' move 8 bytes from input file to local work area
         ' -----------------------------------------------------------------------------------
         ' Put bytes of Q into Intel order. COMP-5 is "native" binary format but apparently "native"
         ' format on spec machine is IBM/MF not Intel. pB is already set.
         ' -----------------------------------------------------------------------------------
         SWAP   @pb[7], @pb[0]   ' could speed up by calculating all offsets and swapping byte vars?
         SWAP   @pb[6], @pb[1]   ' doubt it, because compiler must reduce vars to addresses anyway
         SWAP   @pb[5], @pb[2]   ' probably end up with 'real close' generated code.
         SWAP   @pb[4], @pb[3]
    
          ' ---------------------------------------
          ' Set base call price rate
          ' ---------------------------------------
         ' is the call duration odd or even? If even, rate is L, else D
         ' rate depends on Local or Distance
          isDistance  =  (@pb AND 1?)   ' odd is distance, even is local. Btye 0 of Q is the LSB of LSW of LSDW
    
          IF isDistance THEN
             prate     =  pRateDistance ' base rate
          ELSE
             pRate    =   pRateLocal    ' base rate
          END IF
    
         '"A price, p, for the call is then calculated (p=r*n). This is rounded to
         ' exactly 2 fractional digits using round-half-even (Banker’s round to  nearest)"
    
          p       = prate *  Q         ' unrounded
    
         ' p  = ROUND(p,2)        ' compiler's ROUND function follows banker's rule.
         ' ************************************************************************
         ' No, it doesn't; compiler is rounding .065 to .07 COMPILER BUG. REPORTED.
         ' ************************************************************************
         ' inserted to overcome compiler bug:
         LOCAL iP AS QUAD, efp AS EXT
    
         efp = FRAC(p*100##)    ' Find third digit after decimal; Is it a 5?
    
         IF FIX(efp * 10000) = 5000 THEN    ' use enough decimals to exceed the max precision expected
           iP = FIX (p*100##)               ' get the two digits of cents
           IF IP MOD 2 = 0 THEN             ' last digit is even, so truncate the half-cent (round down)
               p = p - .005##
           ELSE                             ' odd, round up
               p = p + .005##
           END IF
         ELSE                              '  compiler handles rounding correctly here
             p = ROUND(p,2)
         END IF
    
          ' ---------------------------------------
          ' Calculate and add tax
          ' ---------------------------------------
          ' all calls subject to basic tax rate of .0675, any fractional cents truncated
    
           b = FIX(p*bRate*100##)/100##          ' INT does rounding, FIX does not
    
           IF IsDistance THEN                    ' add distance tax
               d =  FIX(p*dRate*100##)/100##
               t =  b + d + p
           ELSE                                  '  line total is price plus basic tax only
              d =  0##                           '  reset so our totals are correct
              t =  b + p
           END IF
    
           ' add this line's amounts to our totals
           Sumb       = sumb + b
           sumt       = sumt + p + b
           IF  d <> 0## THEN
               sumd = sumd + d
               sumt = sumt + d
           END IF
    
          ' ---------------------------------------
          ' format output and write to output file
          ' ---------------------------------------
          sOut                       = FORMAT$(t)
          PRINT #hOut, sOut
    
          INCR       pQ       ' advance to next record in mapped input object
    
    
      NEXT  Z
    
      ' unmap ("close") the input file:
       UnmapMappedFile pmBASE, hSys, hFileMapping
       ' close output file
       CLOSE hOut
    
      ' STOP TIMING HERE. Get ending time and add line for elapsed time and number of records
        TimeEnd      = TIMER
        TimeElapsed  = TimeEnd - TimeStart
    
        ' Put totals in a string for display
         sTotal =  USING$ ("Sumt #########.##  sumb  #########.##  sumD #########.##", sumt, sumb, sumd) & $CRLF
         S       = USING$("Processed #,###,### records in ###.## seconds", nRec, TimeElapsed) & $CRLF
    
    
       ' launch the output file if small enough in notepad for testing
       IF nRec < 500 THEN
           Z = SHELL ("notepad.exe " & szOutFile)
       END IF
    
       MSGBOX sTotal & S,%MB_ICONINFORMATION OR %MB_APPLMODAL, "IBM-Telco Challenge"
    
    END FUNCTION
    
    
    ' END OF FILE   telco0.bas

    ------------------
    Michael Mattias
    Tal Systems Inc.
    Racine WI USA
    mailto:[email protected]ystems.com[email protected]</A>
    www.talsystems.com

    [This message has been edited by Michael Mattias (edited May 20, 2004).]
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com
Working...
X