!
! DictUtils V0.0a
!
! %include '...' this file in your programs BEFORE the %begin
!
! ######################################################################
! Store management for optimised file IO
%const %string(255) Dictcopyright = "DictUtils V1.0 (c) Graham Toal 1986"
%integer master store tag, old store tag

%record %format Store fm(%c
  %integer Base, Size, Free,
           SizeOfLargest, FreeModuleSpace, LargestModuleSpace)

%external %routine %spec XGetStoreInformation(%record(store fm) %name s)

%external %integer %fn %spec  get new tag
%external %routine %spec  return tag(%integer tag)
%external %routine %spec  reset heap end
%external %integer %fn %spec  set store tag(%integer tag)
%external %routine %spec  xdeallocate group(%integer tag)
%external %integer %fn %spec Allocate(%integername addr, %integer size)
%external %routine %spec  FREE SPACE %alias "xdeallocate" (%integer ad)

! ######################################################################

%const %string(255) master dict dir = "adfs::0.$.words.longmans"

! External routines lacking in IMP: should be removed later...

%constant %integer  true = 0, false = 1

%external %integer %fn %spec  Proc ID (%routine X)

%external %routine %spec Stop(%integer res)

%string (63) %fn str(%byte %array %name dict, %integer word length, idx)
%string(63) s
  s = ""
  s = s.dict(idx) %for idx = idx, 1, idx+word length-1
  %result = s
%end

%string (255) %fn  sub string (%string (255) s, %integer first, last)
   %integer i
   %string (255) sub
   %if last > LENGTH(s) %then %signal 15,first,last
   sub = ""
   %for i = first, 1, last %cycle
      sub = sub. char no (s, i)
   %repeat
   %result = sub
%end

%integerfn CPU Time {Courtesy of Mark Taunton}
%record %format tfm(%integer low, %byte high)
%record (tfm) T
  *MovQB _ #1, 1
  *Addr  _ T, 2
  *Svc   _ 7
  %result = T_low
%end

! ######################################################################
! External routine specs for command line decoding

%integer verbosity
%string(255) in param1, in param2, output param

%external %integer %fn %spec Verbosity Required(%integer Handle)

%external %integer %fn %spec ArgumentInit(%c
  %integer %name Handle,
  %string(255) keyString,
  %integer Input wanted, Output wanted,
  %string(255) Identification,
  %integer Help proc)

%external %integer %fn %spec XGetPresence(%c
  %string(255) key,
  %integer Handle)

%external %integer %fn %spec XGetStateArg(%c
  %string(255) key,
  %integer Handle)

%external %integer %fn %spec XGetCardinalArg(%c
  %string(255) key,
  %integer Index,
  %integer Handle)

%external %integer %fn %spec XGetStringArg(%c
  %string(*) %name argument,
  %string(255) Key,
  %integer Index,
           handle)

%external %integer %fn %spec XGetNumberOfValues(%c
  %string(255) Key,
  %integer handle)

! ######################################################################
! External routine specs for file I/O           (blocked)
! and dictionary management

%external %integerfn %spec Get Global String(%c
  %integer address, maxlength, %integername length,
  %string(255) Name)  
  
%external %integerfn %spec  Load File (%string (255) file name,
                                     %integer      buffer size,
                                     %integer      address)
%external %routine %spec XLoad File (%string (255) file name,
                                     %integer      buffer size,
                                     %integer      address)
%external %integer %fn %spec Save File (%integer straddr,strlen,
                                     %integer     buffer size,
                                                  address)
%constant %integer os length index = 2
%record %format osfile array fm (%integer %array element (0:3))
%record %format Time stamp fm (%integer low, high)
%external %integer %fn %spec Get file information(%c
  %record (osfile array fm) %name osfile array, 
  %record (time stamp fm) %name time stamp,
  %string (255) file name)

! ######################################################################
! External routine specs for console/printer IO (sequential)
%integer ttchan
%external %routine %spec XSWriteByte(%integer s,ch)
%external %integer %fn %spec XFindOutput(%string(255)s)
%external %integer %fn %spec XFindInput(%string(255)s)
%external %integer %fn %spec XSBlockRead(%integer str,len,buffaddr)

! ######################################################################
! Command-line decoding interface

%string(255) %fn default dir prefix
%integer i
%string(255) env var                                            
  %if Get Global String(ADDR(env var)+1, 255, i,
                                        "DICT$UNCHECKED") >= 0 %start
    LENGTH(env var)=i
    %result = env var
  %finish
  %result = master dict dir
%end

%routine Decode standard params
%integer result, handle, string length, j,
  in count, out count

  tt chan = XFind Output("RAWVDU:")

  result = ArgumentInit(%c
    handle,
    "first/2 second=TO/1",
    0 , 0,
    ident,
    Proc ID(Help))
  %if result < 0 %then Stop(result)

  verbosity = Verbosity Required(handle)
  in count = XGetNumberOfvalues("first", handle)
  out count = XGetNumberOfvalues("second", handle)

  %if in count >= 1 %start
    string length = XGetStringArg(in param1, "first", 1, handle) 
    %for j = string length, -1, 1 %cycle
      charno(in param1, j) = charno(in param1, j-1)
    %repeat
    length(in param1) = string length
  %finish %else %start
    in param1 = default dir prefix
  %finish

  %if in count >=2 %start
    string length = XGetStringArg(in param2, "first", 2, handle)
    %for j = string length, -1, 1 %cycle
      charno(in param2, j) = charno(in param2, j-1)
    %repeat
    length(in param2) = string length
  %finish %else in param2 = default dir prefix

  %if out count >= 1 %start  
    string length = XGetStringArg(output param, "second", 1, handle)
    %for j = string length, -1, 1 %cycle
      charno(output param, j) = charno(output param, j-1)
    %repeat
    length(output param) = string length
  %finish %else output param = default dir prefix

  string length = XName(program name)
  %for j = string length, -1, 1 %cycle
    charno(program name, j) = charno(program name, j-1)
  %repeat
  length(program name) = string length

  reset heap end {**********************}

  master store tag = get new tag
  old store tag = set store tag(master store tag)

%end

%routine terminate
  xdeallocate group(master store tag)
  return tag(master store tag)
  master store tag = set store tag(old store tag)
%end

! ######################################################################
! File loading interface

%predicate Exists(%string (255) File, %integer %name len)
  %record (osfile array fm) osfile array
  %record (time stamp fm) time stamp
  osfile array_element(os length index) = 0
  %integer success = Get file information(osfile array,
                                          time stamp,
                                          File)
  %false %if success # 1
  len = osfile array_element(os length index)
  %true
%end

%routine loadup(%string (255) file name,
                          %integer address, actual length, max length)
  %external %routine %spec  X Load File (%string (255) file name,
                                         %integer      buffer size,
                                         %integer      address)
  %if max length < actual length %then %start
    print string("File ".file name." of ".itos(actual length,0). %c
                 " bytes is too big for buffer of ". %c
                 itos(max length,0)." bytes".nl)
    %stop
  %finish
  X Load file(file name, max length, address)
%end

%routine writeout(%string (255) file name, %integer address, len)
%external %integer %fn %spec Save File (%integer straddr,strlen,
                                     %integer     buffer size,
                                                  address)
%integer cc
  cc = Save file(ADDR(file name)+1, LENGTH(file name), len, address)
  %if cc < 0 %then printstring ("Failed to write out file ".file name.nl) %c
    %and %stop
%end
! ######################################################################
! Dictionary loading/saving interface

%constant %integer  max word length = 32
%own %integer %array  dict start(1:max word length) = -1(max word length)
%own %integer %array  dict next free(1:max word length) = -1(max word length)
%own %integer %array  dict loaded(1:max word length) = false(max word length)

%routine Load dict(%string (255) dict dir prefix, %integer word length)
%string(255) file name = dict dir prefix.".".itos(word length,0)
%integer dict length, dict address, i


  %if dict loaded(word length) = true %then %start
    %return
  %finish

  %if %not exists(file name, dict length) %then %start
    ! Doesn't exist!!!
    %return
  %finish

  i = Allocate(dict address, dict length+2*(word length+1))
  %if i < 0 %then %start
    %if verbosity > 0 %then %start
      Printstring("No room for ".itos(word length, 0)." letter words".nl)
      Stop(i)
    %finish
    %return
  %finish

  loadup(filename, dict address+(word length+1),
                   dict length, dict length)
  dict start(word length)=dict address
  dict next free(word length)=dict address %c
                               + dict length + (word length+1)*2
  dict loaded(word length)=true

  %if verbosity > 4 %then %c
    printstring(itos(word length,0)."-letter words loaded".nl)

  %for i = dict address, 1, dict address+word length-1 %cycle
    BYTE(i) = ' '
  %repeat
  BYTE(i+1)=nl

  %for i = dict address + word length+1 + dict length, 1, %c
             dict address + word length+1 + dict length + word length-1 %cycle
    BYTE(i)='~'
  %repeat
  BYTE(i+1)=nl

%end

%routine  preload dicts(%string(255) dict dir prefix)
%integer word length
  %for word length=1,1,max word length %cycle
    load dict(dict dir prefix, word length)
  %repeat
%end

%routine Save dict(%string (255) dict dir prefix, %integer word length)
%string(255) file name = dict dir prefix.".".itos(word length,0)
  %if dict loaded(word length)=false %start
    printstring("Save dict ".file name.": dict not loaded?".nl)
    %return
  %finish
  WriteOut(file name, dict start(word length)+word length+1,
                      dict next free(word length)-(word length+1) %c
                       - (dict start(word length)+word length+1))
%end

! ######################################################################
! Dictionary procedures

%constant %integer less = -1, equal = 0, greater = 1

%integerfn compare(%byte %array %name dict, %integer word length,
                   word 1 index, word 2 index)
%integer ix
%for ix = 0,1,word length-1 %cycle
  %if dict(word 1 index+ix) < dict(word 2 index+ix) %then %result=less
  %if dict(word 1 index+ix) > dict(word 2 index+ix) %then %result=greater
%repeat
%result=equal
%end

%routine Sort(%byte %array %name dict, %integer word length, a, b)
  %integer step factor=word length+1
%routine vsort(%integer l,r)
%byte %array v(0:max word length),t(0:max word length)
%byte test
%integer i,ix,j
resort:
%if l>=r %then %return
%for ix=0,1,word length %cycle
  v(ix)=dict(r+ix)
%repeat
i = l-step factor
j = r
%cycle
  %cycle
    i=i+step factor
    ! test = dict(i+ix)>=v
    test=true
    %for ix=0,1,word length %cycle
      %if dict(i+ix)<v(ix) %then test=false %and %exit
      %if dict(i+ix)>v(ix) %then test=true %and %exit
    %repeat
  %repeat %until test=true
  %cycle
    j=j-step factor
    ! test = dict(j+ix)<=v
    test=true
    %for ix=0,1,word length %cycle
      %if dict(j+ix)>v(ix) %then test=false %and %exit
      %if dict(j+ix)<v(ix) %then test=true %and %exit
    %repeat
  %repeat %until test=true
  ! t = dict(i)
  t(ix)=dict(i+ix) %for ix=0,1,word length
  ! dict(i) = dict(j)
  dict(i+ix)=dict(j+ix) %for ix=0,1,word length
  ! dict(j) = t
  dict(j+ix)=t(ix) %for ix=0,1,word length
%repeat %until j<=i
! dict(j)=dict(i)
dict(j+ix)=dict(i+ix) %for ix=0,1,word length
! dict(i)=dict(r)
dict(i+ix)=dict(r+ix) %for ix=0,1,word length
! dict(r)=t
dict(r+ix)=t(ix) %for ix=0,1,word length
%if i-step factor - l > r - i+step factor %start
  vsort(i+step factor,r)
  r = i-step factor; -> resort
%else
  vsort(l,i-step factor)
  l = i+step factor; -> resort
%finish
%end
  vsort(a,b)
%end

%predicate spell check(%byte %array %name target, %integer word length,
                       %byte %array %name dict, %integer next free)
  %integer i, splitp, step factor=word length+1
  %integer subdict beg=step factor, subdict end=next free-step factor
    %cycle
      %if subdict beg > subdict end %then %false
      splitp=(((subdict end-subdict beg)//step factor)//2)*step factor %c
             +subdict beg
      splitp=splitp-REM(splitp-subdict beg,step factor)
      %if verbosity>4 %start
        printstring("beg = ".itos(subdict beg,0).nl)
        printstring("splitp = ".itos(splitp,0).nl)
        printstring("end = ".itos(subdict end,0).nl)
      %finish
      %if splitp>subdict end %or splitp<subdict beg %then %false
      %if verbosity > 4 %start
        printstring("checking against ")
        %for i = 0,1,step factor-1 %cycle
          printsymbol(dict(splitp+i))
        %repeat
        newline
      %finish
      %for i = 0,1,word length-1 %cycle
        %if dict(splitp+i)>target(i) %then %start
          subdict end = splitp-step factor; ->continue2
        %finish
        %if dict(splitp+i)<target(i) %then %start
          subdict beg = splitp+step factor; ->continue2
        %finish
      %repeat
      %true
continue2:
    %repeat
%end

%integerfn Byte Compare(%integer word length, word 1 index, word 2 index)
%integer ix
%for ix = 0,1,word length %cycle
  %if BYTE(word 1 index+ix) < BYTE(word 2 index+ix) %then %result=less
  %if BYTE(word 1 index+ix) > BYTE(word 2 index+ix) %then %result=greater
%repeat
%result=equal
%end

%routine Common(%integer word length,
                        ad1, ad2, commonad,
                        len1, len2,
               %integername result len)
%routine Step(%integername i, count)
  i = i + (word length+1); count = count + (word length+1)
%end
%routine Transfer(%integer srce,dest)
%integer i
  %for i = 0,1,word length %cycle
    BYTE(dest+i)=BYTE(srce+i)
  %repeat
  result len=result len+(word length+1)
  commonad=commonad+(word length+1)
%end
%integer cc,count1=0,count2=0,i
  result len=0
  %cycle
    %if count1=len1 %or count2=len2 %then %return
    cc = Byte Compare(word length, ad1, ad2)
    %if verbosity > 4 %start
      printsymbol(BYTE(i)) %for i = ad1,1,ad1+word length-1
    %finish
    %if cc=equal %then %start
      %if verbosity>4 %start
        printstring(" = ")
        printsymbol(BYTE(i)) %for i = ad2,1,ad2+word length
      %finish
      Transfer(ad1,commonad); Step(ad1,count1); Step(ad2,count2)
    %else %if cc = less
      %if verbosity>4 %start
        printstring(" < ")
        printsymbol(BYTE(i)) %for i = ad2,1,ad2+word length
      %finish
      Step(ad1,count1)
    %else ;! Greater
      %if verbosity>4 %start
        printstring(" > ")
        printsymbol(BYTE(i)) %for i = ad2,1,ad2+word length
      %finish
      Step(ad2,count2)
    %finish
  %repeat
%end

%routine Exclude(%integer word length,
                        ad1, ad2, commonad,
                        len1, len2,
               %integername result len)
%routine Step(%integername i, count)
  i = i + (word length+1); count = count + (word length+1)
%end
%routine Transfer(%integer srce,dest)
%integer i
  %for i = 0,1,word length %cycle
    BYTE(dest+i)=BYTE(srce+i)
  %repeat
  result len=result len+(word length+1)
  commonad=commonad+(word length+1)
%end
%integer cc,count1=0,count2=0,i
  result len=0
  %cycle
    %if count2=len2 %then %return
    %if count1=len1 %start
      %cycle
        %if count2=len2 %then %return
        Transfer(ad2,commonad)
        Step(ad2,count2)
      %repeat
    %finish
    cc = Byte Compare(word length, ad1, ad2)
    %if verbosity > 4 %start
      printsymbol(BYTE(i)) %for i = ad1,1,ad1+word length-1
    %finish
    %if cc=equal %then %start
      %if verbosity>4 %start
        printstring(" = ")
        printsymbol(BYTE(i)) %for i = ad2,1,ad2+word length
      %finish
      ! f1 = f2
      Step(ad1,count1)
      Step(ad2,count2)
    %else %if cc = less
      %if verbosity>4 %start
        printstring(" < ")
        printsymbol(BYTE(i)) %for i = ad2,1,ad2+word length
      %finish
      ! f1 < f2
      Step(ad1,count1)
    %else ;! Greater
      %if verbosity>4 %start
        printstring(" > ")
        printsymbol(BYTE(i)) %for i = ad2,1,ad2+word length
      %finish
      ! f1 > f2
      Transfer(ad2,commonad)
      Step(ad2,count2)
    %finish
  %repeat
%end

%routine Merge(%integer word length,
                        ad1, ad2, commonad,
                        len1, len2,
               %integername result len)
%routine Step(%integername i, count)
  i = i + (word length+1); count = count + (word length+1)
%end
%routine Transfer(%integer srce,dest)
%integer i
  %for i = 0,1,word length %cycle
    BYTE(dest+i)=BYTE(srce+i)
  %repeat
  result len=result len+(word length+1)
  commonad=commonad+(word length+1)
%end
%integer cc,count1=0,count2=0,i
  result len=0
  %cycle
    %if count2=len2 %start
      %cycle
        %if count1=len1 %then %return
        Transfer(ad1,commonad)
        Step(ad1,count1)
      %repeat
    %finish

    %if count1=len1 %start
      %cycle
        %if count2=len2 %then %return
        Transfer(ad2,commonad)
        Step(ad2,count2)
      %repeat
    %finish
    cc = Byte Compare(word length, ad1, ad2)
    %if verbosity > 4 %start
      printsymbol(BYTE(i)) %for i = ad1,1,ad1+word length-1
    %finish
    %if cc=equal %then %start
      %if verbosity>4 %start
        printstring(" = ")
        printsymbol(BYTE(i)) %for i = ad2,1,ad2+word length
      %finish
      ! f1 = f2
      Transfer(ad2,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              еееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееееее