home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 3 / AACD03.BIN / AACD / Programming / sofa / archive / SmallEiffel.lha / SmallEiffel / lib_std / dictionary.e < prev    next >
Text File  |  1999-06-05  |  18KB  |  631 lines

  1. -- This file is  free  software, which  comes  along  with  SmallEiffel. This
  2. -- software  is  distributed  in the hope that it will be useful, but WITHOUT 
  3. -- ANY  WARRANTY;  without  even  the  implied warranty of MERCHANTABILITY or
  4. -- FITNESS  FOR A PARTICULAR PURPOSE. You can modify it as you want, provided
  5. -- this header is kept unaltered, and a notification of the changes is added.
  6. -- You  are  allowed  to  redistribute  it and sell it, alone or as a part of 
  7. -- another product.
  8. --          Copyright (C) 1994-98 LORIA - UHP - CRIN - INRIA - FRANCE
  9. --            Dominique COLNET and Suzanne COLLIN - colnet@loria.fr 
  10. --                       http://SmallEiffel.loria.fr
  11. --
  12. class DICTIONARY[V,K->HASHABLE]
  13.    --
  14.    -- Associative memory. 
  15.    -- Values of type `V' are stored using Keys of type `K'.
  16.    --
  17.  
  18. inherit 
  19.    ANY 
  20.       redefine is_equal, copy 
  21.       end;
  22.  
  23. creation make, with_capacity
  24.  
  25. feature 
  26.  
  27.    Default_size: INTEGER is 32;
  28.          -- Minimum size for storage in muber of items.
  29.  
  30. feature {DICTIONARY}
  31.    
  32.    keys: FIXED_ARRAY[K];
  33.          -- Storage for keys of type `K'.
  34.  
  35.    store: FIXED_ARRAY[V];
  36.          -- Storage for values of type `V'.
  37.     
  38.    modulus: INTEGER;
  39.          -- To compute a hash value in range [0 .. `modulus'-1].
  40.  
  41.    buckets: FIXED_ARRAY[INTEGER];
  42.          -- Valid index range is always [0 .. `modulus'-1].
  43.          -- Contents is a hash code value in range [0 .. `keys.upper'] to 
  44.          -- acess `keys', `store' and `chain' as well.
  45.     
  46.    chain: FIXED_ARRAY[INTEGER]; 
  47.          -- Used to chain both free slots and hash-code clash.
  48.          -- Value -1 mark the end of a list.
  49.     
  50.    first_free_slot: INTEGER;
  51.          -- First element of the free-slot list or -1 when no more
  52.          -- free slot are available.
  53.  
  54. feature {DICTIONARY}  -- Internal cache handling :
  55.     
  56.    cache_keys_idx: INTEGER;
  57.          -- Contents is -1 or caches the last visited entry using : `has',
  58.          -- `at', `item', or `key'.
  59.  
  60.    cache_user_idx: INTEGER;
  61.          -- Contents is -1 or in range [1 .. `count']. When not -1, it 
  62.          -- caches the last user's index used with `item' or `key'.
  63.  
  64.    cache_buckets_idx: INTEGER;
  65.          -- Contents means nothing when `cache_user_idx' is -1.
  66.          -- Otherwise, gives the current position in `buckets' during 
  67.          -- traversal.
  68.  
  69. feature {NONE}
  70.  
  71.    buckets_keys_ratio: INTEGER is 3;
  72.          -- To compute `modulus' as `ratio' * `capacity'.
  73.  
  74.    make is
  75.          -- Internal initial storage size of the dictionary is set to
  76.          -- the default `Default_size' value. Then, tuning of needed storage 
  77.          -- size is done automatically according to usage. 
  78.          -- If you are really sure that your dictionary is always really
  79.          -- bigger than `Default_size', you may use `with_capacity' to save some 
  80.          -- execution time.
  81.       do
  82.          with_capacity(Default_size);
  83.       ensure
  84.          empty;
  85.          capacity = Default_size
  86.       end;
  87.    
  88.    with_capacity(medium_size: INTEGER) is
  89.          -- May be used to save some execution time if one is sure 
  90.          -- that storage size will rapidly become really bigger than
  91.          -- `Default_size'. When first `remove' occurs, storage size may 
  92.          -- naturally become smaller than `medium_size'. Afterall, 
  93.          -- tuning of storage size is done automatically according to
  94.          -- usage.
  95.       require
  96.          medium_size > 0
  97.       local
  98.          i: INTEGER;
  99.       do
  100.          !!keys.make(medium_size);
  101.          !!store.make(medium_size);
  102.          modulus := buckets_keys_ratio * medium_size;
  103.          !!buckets.make(modulus);
  104.          buckets.set_all_with(-1);
  105.          from
  106.             !!chain.make(medium_size);
  107.             i := chain.upper;
  108.             first_free_slot := i;
  109.          until
  110.             i < 0
  111.          loop
  112.             chain.put(i - 1, i);
  113.             i := i - 1;
  114.          end;
  115.          cache_keys_idx := -1;
  116.          cache_user_idx := -1;
  117.          count := 0;
  118.       ensure
  119.          empty;
  120.          capacity = medium_size
  121.       end;
  122.  
  123. feature -- Counting :
  124.  
  125.    count: INTEGER;
  126.          -- Actual `count' of stored elements.
  127.  
  128.    empty: BOOLEAN is
  129.       do
  130.          Result := count = 0;
  131.       ensure
  132.          Result = (count = 0)
  133.       end;
  134.       
  135. feature -- Basic access :
  136.  
  137.    has(k: K): BOOLEAN is
  138.          -- Is there an item currently associated with key `k' ?
  139.       do
  140.          if cache_keys_idx < 0 or else k /= keys.item(cache_keys_idx) then
  141.             from
  142.                cache_user_idx := -1;
  143.                cache_keys_idx := buckets.item(k.hash_code \\ modulus);
  144.             until
  145.                cache_keys_idx < 0 or else
  146.                k.is_equal(keys.item(cache_keys_idx))
  147.             loop
  148.                cache_keys_idx := chain.item(cache_keys_idx);
  149.             end;
  150.          end;
  151.          Result := (cache_keys_idx >= 0);
  152.       end;
  153.    
  154.    at, infix "@" (k: K): V is
  155.          -- Return the item stored at key `k'.
  156.       require
  157.          has(k)
  158.       do
  159.          if cache_keys_idx < 0 or else k /= keys.item(cache_keys_idx) then
  160.             from
  161.                cache_user_idx := -1;
  162.                cache_keys_idx := buckets.item(k.hash_code \\ modulus);
  163.             until
  164.                k.is_equal(keys.item(cache_keys_idx))
  165.             loop
  166.                cache_keys_idx := chain.item(cache_keys_idx);
  167.             end;
  168.          end;
  169.          Result := store.item(cache_keys_idx);
  170.       end;
  171.    
  172. feature -- The only way to add or to change an entry :
  173.  
  174.    put(v: V; k: K) is
  175.          -- If there is as yet no key `k' in the dictionary, enter 
  176.          -- it with item `v'. Otherwise overwrite the item associated
  177.          -- with key `k'.
  178.       local
  179.          h: INTEGER;
  180.       do
  181.          if cache_keys_idx < 0 or else k /= keys.item(cache_keys_idx) then
  182.             from
  183.                cache_user_idx := -1;
  184.                h := k.hash_code \\ modulus;
  185.                cache_keys_idx := buckets.item(h);
  186.             until
  187.                cache_keys_idx < 0 or else 
  188.                k.is_equal (keys.item (cache_keys_idx))
  189.             loop
  190.                cache_keys_idx := chain.item (cache_keys_idx);
  191.             end;
  192.             if cache_keys_idx < 0 then
  193.                if first_free_slot < 0 then
  194.                   expand;
  195.                   h := k.hash_code \\ modulus;
  196.                end;
  197.                keys.put(k,first_free_slot);
  198.                store.put(v,first_free_slot);
  199.                cache_keys_idx := first_free_slot;
  200.                first_free_slot := chain.item(first_free_slot);
  201.                chain.put(buckets.item(h),cache_keys_idx);
  202.                buckets.put(cache_keys_idx,h);
  203.                count := count + 1;
  204.             else
  205.                store.put(v,cache_keys_idx);
  206.             end;
  207.          else
  208.             store.put(v,cache_keys_idx);
  209.          end;
  210.       ensure
  211.          v = at(k)
  212.       end;
  213.  
  214. feature -- Looking and Searching :
  215.  
  216.    nb_occurrences(v: V): INTEGER is
  217.          -- Number of occurrences using `equal'.
  218.          -- See also `fast_nb_occurrences' to chose
  219.          -- the apropriate one.
  220.       local
  221.          i: INTEGER;
  222.       do
  223.          from
  224.             i := 1
  225.          until
  226.             i > count
  227.          loop
  228.             if equal_like(v,item(i)) then
  229.                Result := Result + 1;
  230.             end;
  231.             i := i + 1;
  232.          end;
  233.       ensure
  234.          Result >= 0
  235.       end;
  236.       
  237.    fast_nb_occurrences(v: V): INTEGER is
  238.          -- Number of occurrences using `='.
  239.       local
  240.          i: INTEGER;
  241.       do
  242.          from
  243.             i := 1
  244.          until
  245.             i > count
  246.          loop
  247.             if v = item(i) then
  248.                Result := Result + 1;
  249.             end;
  250.             i := i + 1;
  251.          end;
  252.       ensure
  253.          Result >= 0;
  254.       end;
  255.  
  256.    key_at(v: V): K is
  257.          -- Retrieve the key used for value `v' using `equal' for comparison. 
  258.       require
  259.          nb_occurrences(v) = 1
  260.       local
  261.          i: INTEGER;
  262.       do
  263.          from  
  264.             i := 1;
  265.          until
  266.             equal_like(v,item(i))
  267.          loop
  268.             i := i + 1;
  269.          end;
  270.          Result := keys.item(cache_keys_idx);
  271.       ensure
  272.          equal(at(Result),v)
  273.       end;
  274.    
  275.    fast_key_at(v: V): K is
  276.          -- Retrieve the key used for value `v' using `=' for comparison. 
  277.       require
  278.          fast_nb_occurrences(v) = 1
  279.       local
  280.          i: INTEGER;
  281.       do
  282.          from  
  283.             i := 1;
  284.          until
  285.             v = item(i)
  286.          loop
  287.             i := i + 1;
  288.          end;
  289.          Result := keys.item(cache_keys_idx);
  290.       ensure
  291.          at(Result) = v
  292.       end;
  293.  
  294.    capacity: INTEGER is
  295.       do
  296.          Result := keys.count;
  297.       end;
  298.  
  299. feature -- Removing :
  300.  
  301.    remove(k: K) is
  302.          -- Remove entry `k' (which may exist or not before this call).
  303.       local
  304.          h, keys_idx, keys_next_idx: INTEGER;
  305.       do
  306.          h := k.hash_code \\ modulus;
  307.          keys_idx := buckets.item(h);
  308.          if keys_idx < 0 then
  309.          elseif keys.item(keys_idx).is_equal(k) then
  310.             buckets.put(chain.item(keys_idx),h);
  311.             chain.put(first_free_slot,keys_idx);
  312.             first_free_slot := keys_idx;
  313.             cache_user_idx := -1;
  314.             cache_keys_idx := -1;
  315.             count := count - 1;
  316.          else
  317.             from
  318.                keys_next_idx := chain.item(keys_idx);
  319.             until
  320.                keys_next_idx < 0 or else
  321.                keys.item(keys_next_idx).is_equal(k)
  322.                loop
  323.                   keys_idx := keys_next_idx;
  324.                   keys_next_idx := chain.item(keys_next_idx);
  325.                end;
  326.                if keys_next_idx >= 0 then
  327.                   chain.put(chain.item(keys_next_idx),keys_idx);
  328.                   chain.put(first_free_slot,keys_next_idx);
  329.                   first_free_slot := keys_next_idx;
  330.                   cache_user_idx := -1;
  331.                   cache_keys_idx := -1;
  332.                   count := count - 1;
  333.                end;
  334.             end;
  335.       ensure
  336.          not has(k)
  337.       end;
  338.  
  339.    clear is
  340.          -- Discard all items.
  341.       local
  342.          i: INTEGER;
  343.       do
  344.          buckets.set_all_with(-1);
  345.          from
  346.             i := chain.upper;
  347.             first_free_slot := i;
  348.          until
  349.             i < 0
  350.          loop
  351.             chain.put(i - 1, i);
  352.             i := i - 1;
  353.          end;
  354.          cache_keys_idx := -1;
  355.          cache_user_idx := -1;
  356.          count := 0;
  357.       ensure
  358.          empty;
  359.       end;
  360.  
  361. feature -- To provide iterating facilities :
  362.  
  363.    lower: INTEGER is 1;
  364.  
  365.    upper: INTEGER is
  366.       do
  367.          Result := count;
  368.       ensure
  369.          Result = count
  370.       end;
  371.  
  372.    valid_index(idx: INTEGER): BOOLEAN is
  373.       do
  374.          Result := (1 <= idx) and then (idx <= count);
  375.       ensure
  376.          Result =  (1 <= idx and then idx <= count);
  377.       end;
  378.    
  379.    item(idx: INTEGER): V is
  380.       require
  381.          valid_index(idx)
  382.       do
  383.          set_cache_user_idx(idx);
  384.          Result := store.item(cache_keys_idx);
  385.       ensure
  386.          Result = at(key(idx))
  387.       end;
  388.    
  389.    key(idx: INTEGER): K is
  390.       require
  391.          valid_index(idx)
  392.       do
  393.          set_cache_user_idx(idx);
  394.          Result := keys.item(cache_keys_idx);
  395.       ensure
  396.          at(Result) = item(idx)
  397.       end;
  398.  
  399. feature
  400.    
  401.    is_equal(other: like current): BOOLEAN is
  402.       local
  403.          buckets_idx, keys_idx: INTEGER;
  404.          k: K;
  405.          v1, v2: V;
  406.       do
  407.          if Current = other then
  408.             Result := true;
  409.          elseif count = other.count then
  410.             from
  411.                Result := true;
  412.                buckets_idx := 0;
  413.             until
  414.                not Result or else buckets_idx > buckets.upper
  415.             loop
  416.                keys_idx := buckets.item(buckets_idx); 
  417.                if keys_idx >= 0 then
  418.                   from
  419.                   until
  420.                      not Result or else keys_idx < 0
  421.                   loop
  422.                      k := keys.item(keys_idx);
  423.                      if other.has(k) then
  424.                         v1 := store.item(keys_idx);
  425.                         v2 := other.at(k);
  426.                         Result := equal_like(v1,v2);
  427.                      else
  428.                         Result := false;
  429.                      end;
  430.                      keys_idx := chain.item(keys_idx);
  431.                   end;
  432.                end;
  433.                buckets_idx := buckets_idx + 1;
  434.             end;
  435.          end;
  436.       end;
  437.  
  438.    copy(other: like current) is
  439.       do
  440.          count := other.count;
  441.          modulus := other.modulus;
  442.          first_free_slot := other.first_free_slot;
  443.          cache_keys_idx := other.cache_keys_idx;
  444.          cache_user_idx := other.cache_user_idx;
  445.          cache_buckets_idx := other.cache_buckets_idx;
  446.          if buckets = Void then
  447.             buckets := other.buckets.twin;
  448.             keys := other.keys.twin;
  449.             store := other.store.twin;
  450.             chain := other.chain.twin;
  451.          else
  452.             buckets.copy(other.buckets);
  453.             keys.copy(other.keys);
  454.             store.copy(other.store);
  455.             chain.copy(other.chain);
  456.          end;
  457.       end;
  458.  
  459. feature {NONE} 
  460.    
  461.    expand is
  462.          -- The dictionary must grow.
  463.       local
  464.          i: INTEGER;
  465.       do
  466.          from
  467.             i := keys.count;
  468.             resize_buckets(i * 2 * buckets_keys_ratio);
  469.          until
  470.             i = 0
  471.          loop
  472.             chain.add_last(first_free_slot);
  473.             first_free_slot := chain.upper;
  474.             i := i - 1;
  475.          end;
  476.          keys.resize(chain.count);
  477.          store.resize(chain.count);
  478.       ensure
  479.          first_free_slot >= 0
  480.       end;
  481.  
  482.    resize_buckets(new_modulus: INTEGER) is
  483.       local
  484.          h, i: INTEGER;
  485.       do
  486.          modulus := new_modulus;
  487.          buckets.resize(new_modulus);
  488.          buckets.set_all_with(-1);
  489.          from
  490.          until
  491.             first_free_slot < 0
  492.          loop
  493.             i := chain.item(first_free_slot);
  494.             chain.put(-2,first_free_slot);
  495.             first_free_slot := i;
  496.          end;
  497.          check 
  498.             first_free_slot = -1;
  499.          end;
  500.          from
  501.             i := chain.upper;
  502.          until
  503.             i < 0
  504.          loop
  505.             if chain.item(i) = -2 then
  506.                chain.put(first_free_slot,i);
  507.                first_free_slot := i;
  508.             else
  509.                h := keys.item(i).hash_code \\ new_modulus;
  510.                chain.put(buckets.item(h),i);
  511.                buckets.put(i,h);
  512.             end;
  513.             i := i - 1;
  514.          end;
  515.       end;
  516.  
  517.    equal_like(v1, v2: V): BOOLEAN is
  518.          -- Note: to avoid calling `equal' :-(
  519.          -- Because SmallEiffel is not yet able to infer 
  520.          -- arguments types.
  521.       do
  522.          if v1.is_expanded_type then
  523.             Result := v1 = v2 or else v1.is_equal(v2);
  524.          elseif v1 = v2 then
  525.             Result := true;
  526.          elseif v1 = Void or else v2 = Void then
  527.          else
  528.             Result := v1.is_equal(v2);
  529.          end;
  530.       end;
  531.  
  532.    set_cache_user_idx(idx: INTEGER) is
  533.       require
  534.          valid_index(idx)
  535.       local
  536.          i: INTEGER;
  537.       do
  538.          if idx = cache_user_idx + 1 then
  539.             cache_user_idx := idx;
  540.             if chain.item(cache_keys_idx) < 0 then
  541.                from
  542.                   cache_buckets_idx := cache_buckets_idx + 1;
  543.                until
  544.                   buckets.item(cache_buckets_idx) >= 0
  545.                loop
  546.                   cache_buckets_idx := cache_buckets_idx + 1;
  547.                end;
  548.                cache_keys_idx := buckets.item(cache_buckets_idx);
  549.             else
  550.                cache_keys_idx := chain.item(cache_keys_idx);
  551.             end;
  552.          elseif idx = cache_user_idx - 1 then
  553.             cache_user_idx := idx;
  554.             if cache_keys_idx = buckets.item(cache_buckets_idx) then
  555.                from
  556.                   cache_buckets_idx := cache_buckets_idx - 1;
  557.                until
  558.                   buckets.item(cache_buckets_idx) >= 0
  559.                loop
  560.                   cache_buckets_idx := cache_buckets_idx - 1;
  561.                end;
  562.                from
  563.                   cache_keys_idx := buckets.item(cache_buckets_idx);
  564.                until
  565.                   chain.item(cache_keys_idx) < 0
  566.                loop
  567.                   cache_keys_idx := chain.item(cache_keys_idx);
  568.                end;
  569.             else
  570.                from
  571.                   i := buckets.item(cache_buckets_idx);
  572.                until
  573.                   chain.item(i) = cache_keys_idx
  574.                loop
  575.                   i := chain.item(i);
  576.                end;
  577.                cache_keys_idx := i;
  578.             end;
  579.          elseif idx = cache_user_idx then
  580.          elseif idx = 1 then
  581.             cache_user_idx := 1;
  582.             from
  583.                cache_buckets_idx := 0;
  584.             until
  585.                buckets.item(cache_buckets_idx) >= 0
  586.             loop
  587.                cache_buckets_idx := cache_buckets_idx + 1;
  588.             end;
  589.             cache_keys_idx := buckets.item(cache_buckets_idx);
  590.          elseif idx = count then
  591.             cache_user_idx := idx;
  592.             from
  593.                cache_buckets_idx := buckets.upper;
  594.             until
  595.                buckets.item(cache_buckets_idx) >= 0
  596.             loop
  597.                cache_buckets_idx := cache_buckets_idx - 1;
  598.             end;
  599.             from
  600.                cache_keys_idx := buckets.item(cache_buckets_idx);
  601.             until
  602.                chain.item(cache_keys_idx) < 0
  603.             loop
  604.                cache_keys_idx := chain.item(cache_keys_idx);
  605.             end;
  606.          else
  607.             from 
  608.                set_cache_user_idx(1);
  609.             until
  610.                cache_user_idx = idx
  611.             loop
  612.                set_cache_user_idx(cache_user_idx + 1);
  613.             end;
  614.          end;
  615.       ensure
  616.          cache_user_idx = idx;
  617.          buckets.valid_index(cache_buckets_idx);
  618.          keys.valid_index(cache_keys_idx);
  619.       end;
  620.    
  621. invariant
  622.  
  623.    (keys.upper = store.upper) and (store.upper = chain.upper);
  624.  
  625.    buckets.upper = modulus - 1;
  626.  
  627.    -1 <= first_free_slot and first_free_slot <= chain.upper;
  628.    
  629. end -- DICTIONARY[V,K->HASHABLE]
  630.  
  631.