class EL_HASH_SET_BASE
Implementation of EL_HASH_SET [HASHABLE]
note
description: "Implementation of ${EL_HASH_SET [HASHABLE]}"
author: "Finnian Reilly"
copyright: "Copyright (c) 2001-2022 Finnian Reilly"
contact: "finnian at eiffel hyphen loop dot com"
license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
date: "2025-11-13 16:46:01 GMT (Thursday 13th November 2025)"
revision: "13"
deferred class
EL_HASH_SET_BASE [H -> HASHABLE]
inherit
TRAVERSABLE_SUBSET [H]
rename
extend as put,
item as iteration_item,
remove as remove_item
end
FINITE [H]
undefine
changeable_comparison_criterion, is_empty
end
EL_HASH_SET_CONSTANTS
feature {NONE} -- Initialization
make_with_key_tester (n: INTEGER; a_key_tester: detachable EQUALITY_TESTER [H])
-- Allocate hash table for at least `n' items with `a_key_tester' to compare keys.
-- If `a_key_tester' is void, it uses `~' for comparison.
-- The table will be resized automatically if more than `n' items are inserted.
require
n_non_negative: n >= 0
local
p: PRIMES; default_value: detachable H
do
key_tester := a_key_tester
create p
capacity := p.higher_prime ((3 * n) // 2)
if capacity < 5 then
capacity := 5
end
create content.make_filled (default_value, capacity)
create deleted_marks.make_filled (False, capacity)
count := 0; control := 0; position := 0; position_default_key := -1
iteration_position := capacity -- satisfies invariant: is_empty implies off
compare_references
found_item := default_value
position_default_key := -1
ensure
capacity_big_enough: capacity >= n
count_set: count = 0
end
feature -- Measurement
capacity: INTEGER
-- Size of the table
count: INTEGER
-- Number of items actually inserted in `Current'
feature -- Access
cursor: INTEGER
-- Cursor
do
Result := iteration_position
end
found_item: detachable H
-- Item found during a search with `has' to reduce the number of
-- search for clients
item (key: H): detachable H
-- Item associated with `key', if present
-- otherwise default value of type `G'
do
if attached content as area then
set_position (key, area, deleted_marks)
inspect control
when Found_constant then
Result := area [position]
else
end
end
end
iteration_item, key_for_iteration: H
-- Item at cursor position
do
check attached content [iteration_position] as l_item then
Result := l_item
end
end
key_at (n: INTEGER): detachable H
-- Key corresponding to entry `n'
do
if n >= 0 and n < content.count then
Result := content [n]
end
end
key_tester: detachable EQUALITY_TESTER [H]
-- Tester used for comparing keys.
feature -- Status query
conflict: BOOLEAN
-- Did last operation insert an item?
do
Result := not inserted
end
extendible: BOOLEAN = True
-- May new items be added?
found: BOOLEAN
-- Did last operation find the item sought?
do
Result := control = Found_constant
end
full: BOOLEAN = False
has (key: H): BOOLEAN
-- Is `access_key' currently used?
-- (Shallow equality)
do
if count > 0 then
set_position (key, content, deleted_marks)
Result := (control = Found_constant)
end
end
has_key (key: H): BOOLEAN
-- Is `access_key' currently used?
-- (Shallow equality)
do
search (key)
Result := (control = Found_constant)
end
inserted: BOOLEAN
-- Did last operation insert an item?
is_empty: BOOLEAN
-- Is structure empty?
do
Result := (count = 0)
end
prunable: BOOLEAN = True
reference_comparison: BOOLEAN
-- Is current comparing keys using `='.
do
Result := not object_comparison
end
feature {EL_HASH_SET_ITERATION_CURSOR} -- Implementation access
next_iteration_position (index: INTEGER): INTEGER
-- Given an iteration position `index', compute the next one
do
Result := next_iteration_index (index, capacity - 1, content, deleted_marks)
end
next_iteration_index (index, last_index: INTEGER; area: like content; deleted: like deleted_marks): INTEGER
do
from Result := index + 1 until Result > last_index or else valid_key (Result, area, deleted) loop
Result := Result + 1
end
end
set_key_tester (a_key_tester: like key_tester)
do
key_tester := a_key_tester
set_comparison_method
end
feature {NONE} -- Implementation
append_to (other: EL_HASH_SET [H])
local
index, last_index: INTEGER; break: BOOLEAN
do
if attached content as area and then attached deleted_marks as deleted then
last_index := capacity - 1
from index := -1 until break loop
index := next_iteration_index (index, last_index, area, deleted)
if index > last_index then
break := True
else
other.put (area [index])
end
end
end
end
expand_size
-- Double the capacity of `Current'.
-- Transfer everything except deleted keys.
do
resize ((3 * capacity) // 2)
end
deleted_count: INTEGER
-- count of deleted marks that are `True'
local
i: INTEGER
do
if attached deleted_marks as marks then
from i := 1 until i = marks.count loop
Result := Result + marks [i].to_integer
i := i + 1
end
end
end
new_key_tester: like key_tester
do
end
set_comparison_method
do
if ({H}).is_expanded then
comparison_method := Compare_expanded
elseif attached key_tester then
comparison_method := Compare_with_test
elseif object_comparison then
comparison_method := Compare_is_equal
else
comparison_method := Compare_reference
end
end
same_keys (a_search_key, a_key: H): BOOLEAN
-- Does `a_search_key' equal to `a_key' using `comparison_method'?
do
inspect comparison_method
when Compare_expanded, Compare_reference then
Result := a_search_key = a_key
when Compare_is_equal then
Result := a_search_key ~ a_key
when Compare_with_test then
if attached key_tester as l_tester then
Result := l_tester.test (a_search_key, a_key)
end
else
end
end
set_position (search_key: H; area: like content; deleted: like deleted_marks)
-- Set position of item for `search_key'.
-- If successful, set `position' to index
-- of item with this key (the same index as the key's index).
-- If not, set position to possible position for insertion.
-- Set `control' to `Found_constant' or `Not_found_constant'.
local
increment, hash_code, table_size, index: INTEGER
first_available_position, visited_count: INTEGER
old_key, default_key: H; break: BOOLEAN
do
control := Not_found_constant
first_available_position := -1
table_size := capacity
hash_code := search_key.hash_code
-- Increment computed for no cycle: `table_size' is prime
increment := 1 + hash_code \\ (table_size - 1)
from
index := (hash_code \\ table_size) - increment
until
break or else visited_count >= table_size
loop
index := (index + increment) \\ table_size
visited_count := visited_count + 1
old_key := area [index]
if old_key = default_key or old_key = Void then
if index = position_default_key then
control := Found_constant
break := True
elseif not deleted [index] then
control := Not_found_constant
break := True
if first_available_position >= 0 then
index := first_available_position
end
elseif first_available_position < 0 then
first_available_position := index
end
elseif same_keys (search_key, old_key) then
control := Found_constant
break := True
end
end
if not break and then first_available_position >= 0 then
index := first_available_position
end
position := index
end
soon_full: BOOLEAN
-- Is `Current' close to being filled?
-- (If so, resizing is needed to avoid performance degradation.)
do
Result := (content.count * Size_threshold <= 100 * count)
end
valid_key (index: INTEGER; area: like content; deleted: like deleted_marks): BOOLEAN
require
valid_index: 0 <= index and index < capacity
local
default_key: H
do
if not deleted_marks [index] then
if index = position_default_key and then area [index] = default_key then
Result := True
else
Result := area [index] /= default_key
end
end
ensure
definition: Result implies area [index] /= Void
end
feature {NONE} -- Deferred
resize (n: INTEGER)
-- Resize table to accommodate `n' elements.
require
n_non_negative: n >= 0
n_greater_than_count: n >= count
deferred
end
search (key: H)
-- Search for item of key `key'
-- If found, set `found' to True, and set
-- `found_item' to item associated with `key'.
deferred
ensure
item_if_found: found implies (found_item = content [position])
end
feature {EL_HASH_SET_BASE, EL_HASH_SET_ITERATION_CURSOR} -- Internal attributes access
content: SPECIAL [detachable H]
-- Content
deleted_marks: SPECIAL [BOOLEAN]
-- deleted marks
feature {NONE} -- Internal attributes
control: INTEGER
-- Control code set by operations that may return
-- several possible conditions.
-- Possible control codes are the following:
comparison_method: NATURAL_8
iteration_position: INTEGER
-- Iteration position value
position: INTEGER
-- Hash table cursor
position_default_key: INTEGER
-- position of key that is a default value if keys are expanded types
-- Useful if we want to use 0 as key for `EL_HASH_SET [INTEGER]'
invariant
count_big_enough: 0 <= count
comparison_method_set: comparison_method > 0
end