home *** CD-ROM | disk | FTP | other *** search
- ' Hashed Access Demonstration Program For The QuickBasic Echo
- ' By Mike Avery, Started 12-28-91
- ' Version 1:00.00 - Make it work. 12-28-91
- ' Version 1:01.00 - Add Disk Functions 12-29-91
- ' ========================================================================
-
- DECLARE FUNCTION Hash! (TestString$)
- DECLARE SUB GetData (Key$, Index%, SeekCount%, SaveIndex%)
- DECLARE SUB Waiter ()
- DECLARE FUNCTION WhackIt$ (InputString$)
-
- CONST DeletedValue$ = "EXPLETIVE DELETED"
- CONST ArraySize% = 531 'Change the size here - the rest adjusts itself
- CONST RetryLimit% = 100 'I get bored easily....
- CONST ScreenLimit% = 21 'how many lines do we show at once?
- CONST True = -1: CONST False = NOT (True)
-
- DIM SHARED A$(ArraySize%, 1) 'our little data base
- DIM SHARED SortSpace$(ArraySize%, 1) 'Workspace for sorted lists
-
- PowerMax% = INT((LOG(ArraySize%) / LOG(2)) + 2)
- DIM SHARED PowersOfTwo%(PowerMax%)
-
- 'build the table - lookup is faster than calculation
- FOR I% = 0 TO PowerMax%
- PowersOfTwo%(I%) = 2 ^ I%
- NEXT I%
-
- DO WHILE TestName$ <> "STOP"
- CLS
- PRINT "Doofus Phone Book System"
- PRINT
- PRINT
- INPUT "Name/Help/Dump/Sort/Load/Save/Analyse/Stop"; TestName$
- TestName$ = UCASE$(RTRIM$(LTRIM$(TestName$)))
-
- IF TestName$ = "DUMP" THEN
- GOSUB DumpIt
-
- ELSEIF TestName$ = "SORT" THEN
- GOSUB SortIt
-
- ELSEIF TestName$ = "ANALYSE" THEN
- GOSUB Analyse
-
- ELSEIF TestName$ = "HELP" THEN
- GOSUB Help
-
- ELSEIF TestName$ = "LOAD" THEN
- GOSUB LoadIt
-
- ELSEIF TestName$ = "SAVE" THEN
- GOSUB SaveIt
-
- ELSEIF TestName$ <> "" AND TestName$ <> "STOP" THEN
- CALL GetData(TestName$, Index%, SeekCount%, SaveIndex%)
-
- ' At this point, one of 3 conditions exists.
- ' 1. We ran out of retries, and it doesn't matter what Index% points to,
- ' 2. Index% points to our data, or
- ' 3. Index% points to an empty record and SaveIndex may or may not
- ' point to a deleted record we can reuse.
-
- PRINT
- PRINT "It took "; SeekCount%; "tries to determine that..."
- 'in a productional program, you'd probably drop that message...
-
- PRINT
-
- IF SeekCount% >= RetryLimit% THEN
- PRINT "The data base is full and/or needs to be resized"
- YesOrNo$ = ""
- DO WHILE YesOrNo$ <> "Y" AND YesOrNo$ <> "N"
- INPUT "Do you want to see a data base dump (Y/N)"; YesOrNo$
- IF YesOrNo$ <> "" THEN
- YesOrNo$ = WhackIt$(YesOrNo$)
- END IF
- IF YesOrNo$ = "Y" THEN
- GOSUB DumpIt
- ELSEIF YesOrNo$ <> "N" THEN
- PRINT "Please Enter A Y for Yes or a N for NO."
- END IF
- LOOP
-
- TestName$ = "STOP"'force a shutdown
- CALL Waiter
- ' save data base here, if converted to a disk based system
-
- ELSEIF A$(Index%, 0) = TestName$ THEN
- PRINT A$(Index%, 0); "'s Phone Number Is "; A$(Index%, 1); "."
- Action$ = "Dummy"
- DO WHILE Action$ <> "" AND Action$ <> "C" AND Action$ <> "D"
- INPUT "Change the number, Delete The Number, or enter"; Action$
-
- IF Action$ <> "" THEN
- Action$ = WhackIt$(Action$)
-
- IF Action$ = "C" THEN
- 'else if we are to change the number
- INPUT "New phone number please"; PhoneNumber$
- PhoneNumber$ = UCASE$(RTRIM$(LTRIM$(PhoneNumber$)))
-
- IF PhoneNumber$ = "" THEN
- PRINT "Number not changed"
- ELSE
- A$(Index%, 1) = PhoneNumber$
- PRINT "Phone number has been updated."
- END IF
-
- ELSEIF Action$ = "D" THEN
- A$(Index%, 0) = DeletedValue$
- PRINT "Entry has been deleted."
-
- ELSE
- 'an invalid entry was made
- PRINT "Please enter a D to Delete the number,"
- PRINT "a C to Change it, or"
- PRINT "just press Enter to continue."
- Action$ = "DUMMY"
- END IF
- END IF
- LOOP
-
- ELSE
- PRINT TestName$; "'s Phone Number Is Not On File. You May Enter It To Add"
- PRINT "It, Or Just Press "; CHR$(34); "ENTER"; CHR$(34); " To Continue.";
- INPUT PhoneNumber$
- PhoneNumber$ = UCASE$(RTRIM$(LTRIM$(PhoneNumber$)))
-
- IF PhoneNumber$ <> "" THEN
- IF SaveIndex% <> -1 THEN
- 'reuse delete space
- Index% = SaveIndex%
- PRINT "We are reclaiming unused space! Ain't it great!"
- CALL Waiter
- END IF
-
- A$(Index%, 0) = TestName$
- A$(Index%, 1) = PhoneNumber$
- END IF
- END IF
-
- END IF
- LOOP
-
- ExitRoutine:
- SYSTEM
-
- Analyse:
- 'process all the data elements in A$ to see:
- ' how full A$ is,
- ' best and worst case access to A$,
- ' mean, SD of access count
-
- ' Statistics routines "borrowed" in part from
- ' "Some Common Basic Programs" pg 121-122
- ' by Lon Poole and Mary Borchers
- ' Published by Adam Osborne
- ' Copyright 1977
- ' pages 121-123
- PRINT "Analysis Begins.... Please Wait....."
-
- Best% = 999
- Worst% = 0
- S = 0 ' we are dealing with a population, not a sample
- N = 0 ' count of active elements
- M = 0 ' Sum of X^2
- P = 0 ' Sum of X
-
- FOR I% = 0 TO ArraySize%
- IF A$(I%, 0) <> "" AND A$(I%, 0) <> DeletedValue$ THEN
- CALL GetData(A$(I%, 0), Index%, Tries%, FirstDeleted%)
- N = N + 1 ' Bump entry count
- P = P + Tries% ' Bump sum of X
- M = M + (Tries% ^ 2) ' Bump sum of X^2
-
- IF Tries% < Best% THEN
- Best% = Tries%
- BestOne% = Index%
- END IF
-
- IF Tries% > Worst% THEN
- Worst% = Tries%
- WorstOne% = Index%
- END IF
- END IF
- NEXT I%
-
- IF N > 0 THEN
- PRINT "Access Analysis....."
- R = P / N
- PRINT "Number Of Entries ="; N
- PRINT "Percent Full ="; INT((N / (ArraySize% + 1)) * 100); "%"
- PRINT "Average Access ="; R; "Seeks."
- V = (M - N * R ^ 2) / (N - S)
- SD = SQR(V)
- PRINT "Standard Deviation ="; SD
- PRINT "Best Access ="; Best%; "Seeks On "; A$(BestOne%, 0); "."
- PRINT "Worst Access ="; Worst%; "Seeks On "; A$(WorstOne%, 0); "."
- ELSE
- PRINT "No Data To Analyze. Sorry."
- END IF
-
- CALL Waiter
- RETURN
-
- DumpIt:
- DisplayControl% = 0
- FOR I% = 0 TO ArraySize%
- PRINT I%, A$(I%, 0), A$(I%, 1)
- DisplayControl% = DisplayControl% + 1
- IF DisplayControl% > ScreenLimit% THEN
- CALL Waiter
- DisplayControl% = 0
- END IF
- NEXT I%
-
- CALL Waiter
- RETURN
-
- ErrorHandler:
-
- PRINT "ErrorHandler Sez...."
-
- IF ERR = 53 OR ERR = 76 OR ERR = 68 OR ERR = 52 OR ERR = 64 OR ERR = 75 THEN
- PRINT "A file you wanted to process, "; FileName$
- PRINT "Could not be found/created."
- Found = False
- CALL Waiter
- RESUME NEXT
- END IF
-
- IF ERR = 61 THEN
- PRINT "Sorry, the disk is full."
- ELSE
- PRINT "You had an Error #"; ERR
- END IF
-
- PRINT "Press any key to quit...."
- K$ = ""
- DO WHILE K$ = ""
- K$ = INKEY$
- LOOP
- RESUME ExitRoutine
-
- Help:
- 'Display a primitive help screen
- CLS
- PRINT "Doofus Phone Book System"
- PRINT
- PRINT
- PRINT "Name/Help/Dump/Sort/Load/Save/Analyse/Stop? HELP"
- PRINT
- PRINT "The Doofus Phone Book System was written as a demonstration of Hashed"
- PRINT "Data Access, rather than as a phone book system. If it works for you,"
- PRINT "fine, but that was not the author's intent."
- PRINT
- PRINT "At the first prompt "; CHR$(34); "Name/Help/Dump/Sort/Load/Save/Analyse:"; CHR$(34); ","
- PRINT "You may enter a name to be added or looked up in the data base by entering"
- PRINT "the name."
- PRINT "You may ask for help by entering "; CHR$(34); "HELP"; CHR$(34); "."
- PRINT "You may see a raw dump of the data array by entering "; CHR$(34); "DUMP"; CHR$(34); "."
- PRINT "You may see a sorted data dump of the array by entering "; CHR$(34); "SORT"; CHR$(34); "."
- PRINT "You may load or save the data to/from disk with the LOAD and SAVE commands."
- PRINT "You may analyse the data set by entering the command "; CHR$(34); "ANALYSE"; CHR$(34); "."
- PRINT "You may exit the application by entering the command "; CHR$(34); "STOP"; CHR$(34); "."
- CALL Waiter
- CLS
- PRINT "Doofus Phone Book System"
- PRINT
- PRINT
- PRINT "Name/Help/Dump/Sort/Load/Save/Analyse/Stop? HELP"
- PRINT
- PRINT "Once you have called up a phone number entry, you may continue by pressing"
- PRINT CHR$(34); "ENTER"; CHR$(34); ", or you may change the data by entering a "; CHR$(34); "C"; CHR$(34); ","
- PRINT "or you may delete the data by pressing a "; CHR$(34); "D"; CHR$(34); "."
- CALL Waiter
- CLS
- RETURN
-
- LoadIt:
- 'load the data from a data file
-
- Free% = 0
- Empty% = Empty% + 1
-
- FOR I% = 0 TO ArraySize%
- IF A$(I%, 0) = "" THEN
- Free% = Free% + 1
- Empty% = Empty% + 1
-
- ELSEIF A$(I%, 0) = DeletedValue$ THEN
- Free% = Free% + 1
- END IF
- NEXT I%
-
- IF Empty% = 0 THEN
- GOSUB SorryFull
-
- ELSE
- INPUT "File To Load From:"; FileName$
- ON ERROR GOTO ErrorHandler
- Found = True
- OPEN FileName$ FOR INPUT AS 1
-
- IF Found = True THEN
- DO WHILE NOT EOF(1) AND Free% > 0
- INPUT #1, TestName$, PhoneNumber$
- CALL GetData(TestName$, Index%, Seeks%, SaveIndex%)
-
- IF SeekCount% >= RetryLimit% THEN
- PRINT "The data base is full and/or needs to be resized"
- YesOrNo$ = ""
- DO WHILE YesOrNo$ <> "Y" AND YesOrNo$ <> "N"
- INPUT "Do you want to see a data base dump (Y/N)"; YesOrNo$
- IF YesOrNo$ <> "" THEN
- YesOrNo$ = WhackIt$(YesOrNo$)
- END IF
- IF YesOrNo$ = "Y" THEN
- GOSUB DumpIt
- ELSEIF YesOrNo$ <> "N" THEN
- PRINT "Please Enter A Y for Yes or a N for NO."
- END IF
- LOOP
-
- Free% = 0 'force a shutdown
- CALL Waiter
-
- ELSEIF A$(Index%, 0) = TestName$ THEN
- ' the value is already on file
- ' we'll just replace the old value for now,
- ' and keep on truckin - we could ask the user
- ' what we should do, but not for a test program!
- A$(Index%, 1) = PhoneNumber$
- PRINT A$(Index%, 0); "has been updated!"
-
- ELSE
- IF SaveIndex% <> -1 THEN
- 'reuse deleted space
- Index% = SaveIndex%
- PRINT "We are reclaiming unused space! Ain't it great!"
- END IF
- A$(Index%, 0) = TestName$
- A$(Index%, 1) = PhoneNumber$
- Free% = Free% - 1
- END IF
-
- IF Free% < 1 THEN
- PRINT "The data base has been completely filled."
- PRINT "Some data was not loaded from the file you selected."
- PRINT
- GOSUB SorryFull
-
- CALL Waiter
- END IF
- LOOP
- CLOSE 1
- END IF
- ON ERROR GOTO 0
- END IF
-
- RETURN
-
- SaveIt:
- 'Save data to a selected file
-
- ON ERROR GOTO ErrorHandler
-
- INPUT "Name of file to save data to:"; FileName$
-
- OPEN FileName$ FOR OUTPUT AS 1
- FOR I% = 0 TO ArraySize%
- IF A$(I%, 0) <> "" AND A$(I%, 0) <> DeletedValue THEN
- PRINT #1, A$(I%, 0); ","; A$(I%, 1)
- END IF
- NEXT I%
- CLOSE 1
- RETURN
-
- SortIt:
- ' convert, sort, and dump the data base
-
- 'convert the hashed A$() into a packed SortSpace$()
-
- PRINT "Converting the data into a linear array...."
- NextEntry% = 0
- FOR I% = 0 TO ArraySize%
- IF A$(I%, 0) <> "" AND A$(I%, 0) <> DeletedValue$ THEN
- SortSpace$(NextEntry%, 0) = A$(I%, 0)
- SortSpace$(NextEntry%, 1) = STR$(I%)
- 'track the location of the data, not the data....
- NextEntry% = NextEntry% + 1
- END IF
- NEXT I%
-
- IF NextEntry% <= 0 THEN
- PRINT "No Data Was Found To Display."
-
- ELSE
- 'now that all the data has been moved from A$() to SortSpace$(), we
- 'need to sort it. How about an exchange sort?
- LastItem% = NextEntry% - 1
- IF LastItem% > 1 THEN
- PRINT "Sorting"; LastItem% + 1; "items. Please Wait....."
- FOR I% = 0 TO LastItem% - 1
- Lowest% = I%
- FOR J% = I% + 1 TO LastItem%
- CompareCount! = CompareCount! + 1
- IF SortSpace$(J%, 0) < SortSpace$(Lowest%, 0) THEN
- Lowest% = J%
- END IF
- NEXT J%
- IF Lowest% <> I% THEN
- SWAP SortSpace$(I%, 0), SortSpace$(Lowest%, 0)
- SWAP SortSpace$(I%, 1), SortSpace$(Lowest%, 1)
- END IF
- NEXT I%
- ELSE
- PRINT "1 item found, the sort will be skipped this time...."
- END IF
-
- 'Now the keys are sorted, so let's display the data....
- PRINT "Order", "Name", "Phone #", "Place in A$"
-
- DisplayCount% = 0
- FOR I% = 0 TO LastItem%
- Pointer% = VAL(SortSpace$(I%, 1))
- PRINT I%, A$(Pointer%, 0), A$(Pointer%, 1), Pointer%
- DisplayCount% = DisplayCount% + 1
- IF DisplayCount% > ScreenLimit% THEN
- CALL Waiter
- DisplayCount% = 0
- END IF
- NEXT I%
- END IF
- CALL Waiter
- RETURN
-
- SorryFull:
- PRINT "Sorry, but there is no space available in the array."
- PRINT "Try saving your data, stopping this program, resizing"; CHR$(34); "ArraySize%"; CHR$(34); ","
- PRINT "reloading the saved data, and then retry this load."
- RETURN
-
- SUB GetData (Key$, Index%, SeekCount%, SaveIndex%)
- ' Try to get the index to the record that that contains Key$ as it's key
- ' Key$ - the value are looking for
- ' Found% - did we find Key$ - True/False returned
- ' Index% - a pointer to where Key$ was found
- ' SeekCount% - how many tries it took us to fing Key$
- ' SaveIndex% - the pointer to the first deleted value we found, if any
-
- Index% = Hash(Key$) 'start the search
- SaveIndex% = -1
- SeekCount% = 1
-
- IF A$(Index%, 0) <> "" AND A$(Index%, 0) <> Key$ THEN
- 'if data in entry, and not a match, do a retry
- ReHashCount% = 0
-
- DO WHILE SeekCount% < RetryLimit% AND A$(Index%, 0) <> "" AND A$(Index%, 0) <> Key$
- IF A$(Index%, 0) = DeletedValue$ AND SaveIndex% = -1 THEN
- 'if this is the first deleted value, save it for data insertion
- SaveIndex% = Index%
- END IF
-
- Index% = Index% + PowersOfTwo%(ReHashCount%)
- DO WHILE Index% > ArraySize%
- Index% = Index% - ArraySize%
- LOOP
-
- ReHashCount% = ReHashCount% + 1
- IF ReHashCount% > PowerMax% THEN
- ReHashCount% = 0
- END IF
-
- SeekCount% = SeekCount% + 1
-
- LOOP
- END IF
-
- END SUB
-
- FUNCTION Hash (TestString$)
- ' turn TestString into a number in the range of 0 - ArraySize%
- ' the function can be tailored to suit the users needs
-
- Trial = 0
-
- FOR I% = 1 TO LEN(TestString$)
- Trial = Trial + ASC(MID$(TestString$, I%, 1))
- NEXT I%
-
- Hash = (Trial * Trial) MOD ArraySize%
-
- END FUNCTION
-
- SUB Waiter
- ' wait for a keypress, then return to caller
- PRINT "Press (almost) any key to continue..."
- K$ = ""
- DO WHILE K$ = ""
- K$ = INKEY$
- LOOP
- END SUB
-
- FUNCTION WhackIt$ (InputString$)
- 'whack the input string -
- ' strip leading and trailing spaces,
- ' make the remainder upper case, and
- ' make it a single letter response.
- TestString$ = UCASE$(RTRIM$(LTRIM$(InputString$)))
- IF LEN(TestString$) > 1 THEN
- TestString$ = LEFT$(TestString$, 1)
- END IF
- WhackIt$ = TestString$
- END FUNCTION
-