note
	description: "Original values of the attributes of a mismatched object."
	instructions: "[
		This object will contain the original values of the attributes
		of a retrieved object for which a mismatched was detected at
		retrieval time. The value are indexed by the name of the attribute
		in the stored object. One extra entry is provided to contain the
		original name of the class in the storing system, indexed by the
		string 'class'. This allows correct_mismatch to determine the
		original name of its generating class in case class-name
		translation is in effect.
	]"
	library: "Free implementation of ELKS library"
	status: "See notice at end of class."
	legal: "See notice at end of class."
	date: "$Date: 2020-05-19 14:29:33 +0000 (Tue, 19 May 2020) $"
	revision: "$Revision: 104259 $"

class 
	MISMATCH_INFORMATION

create {MISMATCH_CORRECTOR}
	default_create


create 
	make

feature -- Initialization

	accommodate (n: INTEGER_32)
			-- Reallocate table with enough space for n items;
			-- keep all current items.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
				n >= 0
		local
			i, nb: INTEGER_32
			new_table: MISMATCH_INFORMATION
			l_content: like content
			l_keys: like keys
		do
			from
				new_table := empty_duplicate (keys.count.max (n))
				l_content := content
				l_keys := keys
				nb := l_keys.count
			until
				i = nb
			loop
				if occupied (i) then
					new_table.put (l_content.item (i), l_keys.item (i))
				end
				i := i + 1
			end
			if has_default then
				i := indexes_map.item (capacity);
				new_table.put (l_content.item (i), keys.item (i))
			end
			set_content (new_table.content)
			set_keys (new_table.keys)
			set_deleted_marks (new_table.deleted_marks)
			set_indexes_map (new_table.indexes_map)
			capacity := new_table.capacity
			iteration_position := new_table.iteration_position
		ensure -- from HASH_TABLE
			count_not_changed: count = old count
			breathing_space: count < capacity
		end

	default_create
			-- Make container with default size
		do
			make (5)
			Set_callback_pointers
		end

	make (n: INTEGER_32)
			-- Allocate hash table for at least n items.
			-- The table will be resized automatically
			-- if more than n items are inserted.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			n_non_negative: n >= 0
		local
			clever: PRIMES
			l_default_value: detachable ANY
			l_default_key: detachable STRING_8
			l_size: INTEGER_32
		do
			create clever
			l_size := n.max (Minimum_capacity)
			l_size := l_size + l_size // 2 + 1
			l_size := clever.higher_prime (l_size)
			capacity := l_size
			create content.make_empty (n + 1)
			create keys.make_empty (n + 1)
			create deleted_marks.make_filled (False, n + 1)
			create indexes_map.make_filled (Ht_impossible_position, l_size + 1)
			iteration_position := n + 1
			count := 0
			deleted_item_position := Ht_impossible_position
			control := 0
			found_item := l_default_value
			has_default := False
			item_position := 0
			ht_lowest_deleted_position := Ht_max_position
			ht_deleted_item := l_default_value
			ht_deleted_key := l_default_key
		ensure -- from HASH_TABLE
			breathing_space: n < capacity
			more_than_minimum: capacity > Minimum_capacity
			no_status: not special_status
		end

	make_equal (n: INTEGER_32)
			-- Allocate hash table for at least n items.
			-- The table will be resized automatically
			-- if more than n items are inserted.
			-- Use ~ to compare items.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			n_non_negative: n >= 0
		do
			make (n)
			compare_objects
		ensure -- from HASH_TABLE
			breathing_space: n < capacity
			more_than_minimum: capacity > Minimum_capacity
			no_status: not special_status
			compare_objects: object_comparison
		end
	
feature -- Access

	at alias "@" (key: STRING_8): detachable ANY assign force
			-- Item associated with key, if present
			-- otherwise default value of type G.
			-- Was declared in HASH_TABLE as synonym of item.
			-- (from HASH_TABLE)
		note
			option: stable
		local
			l_default_key: detachable STRING_8
			hash_value, increment, l_pos, l_item_pos, l_capacity: INTEGER_32
			l_first_deleted_position: INTEGER_32
			stop: INTEGER_32
			l_keys: like keys
			l_indexes: like indexes_map
			l_deleted_marks: like deleted_marks
			l_key: STRING_8
		do
			l_first_deleted_position := Ht_impossible_position
			if key = l_default_key or key = Void then
				if has_default then
					Result := content.item (indexes_map.item (capacity))
				end
			else
				from
					l_keys := keys
					l_indexes := indexes_map
					l_deleted_marks := deleted_marks
					l_capacity := capacity
					stop := l_capacity
					hash_value := hash_code_of (key)
					increment := 1 + hash_value \\ (l_capacity - 1)
					l_item_pos := (hash_value \\ l_capacity) - increment
				until
					stop = 0
				loop
					l_item_pos := (l_item_pos + increment) \\ l_capacity
					l_pos := l_indexes [l_item_pos]
					if l_pos >= 0 then
						l_key := l_keys.item (l_pos)
						debug ("detect_hash_table_catcall")
							check
								catcall_detected: l_key /= Void and then l_key.same_type (key)
							end
						end
						if same_keys (l_key, key) then
							stop := 1
							Result := content.item (l_pos)
						end
					elseif l_pos = Ht_impossible_position then
						stop := 1
					elseif l_first_deleted_position = Ht_impossible_position then
						l_pos := - l_pos + Ht_deleted_position
						check
							l_pos_valid: l_pos < l_deleted_marks.count
						end
						if not l_deleted_marks [l_pos] then
							stop := 1
						else
							l_first_deleted_position := l_item_pos
						end
					end
					stop := stop - 1
				end
			end
		ensure then -- from HASH_TABLE
			default_value_if_not_present: (not has (key)) implies (Result = computed_default_value)
		end

	class_name: STRING_8
			-- Name of generating class which held attribute values
		do
			check
				has_class_entry: has (Type_name_key)
			end
			check
					attached {STRING_8} item (Type_name_key) as r
			then
				Result := r
			end
		ensure
			result_exists: Result /= Void
		end

	current_keys: ARRAY [STRING_8]
			-- New array containing actually used keys, from 1 to count
			-- (from HASH_TABLE)
		local
			j: INTEGER_32
			old_iteration_position: INTEGER_32
		do
			if is_empty then
				create Result.make_empty
			else
				old_iteration_position := iteration_position
				from
					start
					create Result.make_filled (key_for_iteration, 1, count)
					j := 1
					forth
				until
					off
				loop
					j := j + 1;
					Result.put (key_for_iteration, j)
					forth
				end
				iteration_position := old_iteration_position
			end
		ensure -- from HASH_TABLE
			good_count: Result.count = count
		end

	current_version: detachable IMMUTABLE_STRING_8
			-- Version associated to class_name in the current system.

	cursor: CURSOR
			-- Current cursor position
			-- (from HASH_TABLE)
		do
			create {HASH_TABLE_CURSOR} Result.make (iteration_position)
		ensure -- from HASH_TABLE
			cursor_not_void: Result /= Void
		end

	definite_item (key: STRING_8): detachable ANY
			-- Entry of key k.
			-- (from HASH_TABLE)
		require -- from TABLE
			valid_key: has (key)
		require -- from TABLE
			valid_key: has (key)
		local
			old_position: like item_position
			old_control: like control
		do
			old_position := item_position
			old_control := control
			internal_search (key)
			Result := content.item (position)
			control := old_control
			item_position := old_position
		end

	found_item: detachable ANY
			-- Item, if any, yielded by last search operation
			-- (from HASH_TABLE)

	generating_type: TYPE [detachable MISMATCH_INFORMATION]
			-- Type of current object
			-- (type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generating_type_not_void: Result /= Void
		end

	generator: STRING_8
			-- Name of current object's generating class
			-- (base class of the type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generator_not_void: Result /= Void
			generator_not_empty: not Result.is_empty
		end

	has (key: STRING_8): BOOLEAN
			-- Is there an item in the table with key key?
			-- (from HASH_TABLE)
		require -- from  TABLE
			True
		local
			l_default_key: detachable STRING_8
			hash_value, increment, l_pos, l_item_pos, l_capacity: INTEGER_32
			l_first_deleted_position: INTEGER_32
			stop: INTEGER_32
			l_keys: like keys
			l_indexes: like indexes_map
			l_deleted_marks: like deleted_marks
			l_key: STRING_8
		do
			l_first_deleted_position := Ht_impossible_position
			if key = l_default_key or key = Void then
				if has_default then
					Result := True
				end
			else
				from
					l_keys := keys
					l_indexes := indexes_map
					l_deleted_marks := deleted_marks
					l_capacity := capacity
					stop := l_capacity
					hash_value := hash_code_of (key)
					increment := 1 + hash_value \\ (l_capacity - 1)
					l_item_pos := (hash_value \\ l_capacity) - increment
				until
					stop = 0
				loop
					l_item_pos := (l_item_pos + increment) \\ l_capacity
					l_pos := l_indexes [l_item_pos]
					if l_pos >= 0 then
						l_key := l_keys.item (l_pos)
						debug ("detect_hash_table_catcall")
							check
								catcall_detected: l_key /= Void and then l_key.same_type (key)
							end
						end
						if same_keys (l_key, key) then
							stop := 1
							Result := True
						end
					elseif l_pos = Ht_impossible_position then
						stop := 1
					elseif l_first_deleted_position = Ht_impossible_position then
						l_pos := - l_pos + Ht_deleted_position
						check
							l_pos_valid: l_pos < l_deleted_marks.count
						end
						if not l_deleted_marks [l_pos] then
							stop := 1
						else
							l_first_deleted_position := l_item_pos
						end
					end
					stop := stop - 1
				end
			end
		ensure then -- from HASH_TABLE
			default_case: (key = computed_default_key) implies (Result = has_default)
		end

	has_item (v: detachable ANY): BOOLEAN
			-- Does structure include v?
			-- (Reference or object equality, based on object_comparison.)
			-- (from HASH_TABLE)
		local
			i, nb: INTEGER_32
			l_content: like content
		do
			if has_default then
				Result := v = content.item (indexes_map.item (capacity))
			end
			if not Result then
				l_content := content
				nb := l_content.count
				if object_comparison then
					from
					until
						i = nb or else Result
					loop
						Result := occupied (i) and then (v ~ l_content.item (i))
						i := i + 1
					end
				else
					from
					until
						i = nb or else Result
					loop
						Result := occupied (i) and then (v = l_content.item (i))
						i := i + 1
					end
				end
			end
		ensure -- from CONTAINER
			not_found_in_empty: Result implies not is_empty
		end

	has_key (key: STRING_8): BOOLEAN
			-- Is there an item in the table with key key?
			-- Set found_item to the found item.
			-- (from HASH_TABLE)
		local
			old_position: INTEGER_32
			l_default_value: detachable ANY
		do
			old_position := item_position
			internal_search (key)
			Result := found
			if Result then
				found_item := content.item (position)
			else
				found_item := l_default_value
			end
			item_position := old_position
		ensure then -- from HASH_TABLE
			default_case: (key = computed_default_key) implies (Result = has_default)
			found: Result = found
			item_if_found: found implies (found_item = item (key))
		end

	hash_code_of (a_key: attached STRING_8): INTEGER_32
			-- Hash_code value associated to a_key.
			-- (from HASH_TABLE)
		do
			Result := a_key.hash_code
		ensure -- from HASH_TABLE
			non_negative: Result >= 0
		end

	item alias "[]" (key: STRING_8): detachable ANY assign force
			-- Item associated with key, if present
			-- otherwise default value of type G.
			-- Was declared in HASH_TABLE as synonym of at.
			-- (from HASH_TABLE)
		note
			option: stable
		local
			l_default_key: detachable STRING_8
			hash_value, increment, l_pos, l_item_pos, l_capacity: INTEGER_32
			l_first_deleted_position: INTEGER_32
			stop: INTEGER_32
			l_keys: like keys
			l_indexes: like indexes_map
			l_deleted_marks: like deleted_marks
			l_key: STRING_8
		do
			l_first_deleted_position := Ht_impossible_position
			if key = l_default_key or key = Void then
				if has_default then
					Result := content.item (indexes_map.item (capacity))
				end
			else
				from
					l_keys := keys
					l_indexes := indexes_map
					l_deleted_marks := deleted_marks
					l_capacity := capacity
					stop := l_capacity
					hash_value := hash_code_of (key)
					increment := 1 + hash_value \\ (l_capacity - 1)
					l_item_pos := (hash_value \\ l_capacity) - increment
				until
					stop = 0
				loop
					l_item_pos := (l_item_pos + increment) \\ l_capacity
					l_pos := l_indexes [l_item_pos]
					if l_pos >= 0 then
						l_key := l_keys.item (l_pos)
						debug ("detect_hash_table_catcall")
							check
								catcall_detected: l_key /= Void and then l_key.same_type (key)
							end
						end
						if same_keys (l_key, key) then
							stop := 1
							Result := content.item (l_pos)
						end
					elseif l_pos = Ht_impossible_position then
						stop := 1
					elseif l_first_deleted_position = Ht_impossible_position then
						l_pos := - l_pos + Ht_deleted_position
						check
							l_pos_valid: l_pos < l_deleted_marks.count
						end
						if not l_deleted_marks [l_pos] then
							stop := 1
						else
							l_first_deleted_position := l_item_pos
						end
					end
					stop := stop - 1
				end
			end
		ensure then -- from HASH_TABLE
			default_value_if_not_present: (not has (key)) implies (Result = computed_default_value)
		end

	item_for_iteration: detachable ANY
			-- Element at current iteration position
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			not_off: not off
		do
			Result := content.item (iteration_position)
		end

	iteration_item (i: INTEGER_32): detachable ANY
			-- Entry at position i.
			-- (from HASH_TABLE)
		require -- from READABLE_INDEXABLE
			valid_index: valid_iteration_index (i)
		do
			Result := content.item (i)
		end

	key_for_iteration: STRING_8
			-- Key at current iteration position
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			not_off: not off
		do
			Result := keys.item (iteration_position)
		end

	new_cursor: HASH_TABLE_ITERATION_CURSOR [detachable ANY, STRING_8]
			-- Fresh cursor associated with current structure
			-- (from HASH_TABLE)
		require -- from  ITERABLE
			True
		do
			create Result.make (Current);
			Result.start
		ensure -- from ITERABLE
			result_attached: Result /= Void
		end

	stored_version: detachable IMMUTABLE_STRING_8
			-- Version associated to class_name in the stored system.

	Type_name_key: STRING_8 = "_type_name"
			-- Associated key for retrieving the type name of a mismatch.
	
feature -- Measurement

	capacity: INTEGER_32
			-- Number of items that may be stored.
			-- (from HASH_TABLE)

	count: INTEGER_32
			-- Number of items in table
			-- (from HASH_TABLE)

	index_set: INTEGER_INTERVAL
		obsolete "Use `lower' and `upper' instead. [2017-05-31]"
			-- Range of acceptable indexes.
			-- (from READABLE_INDEXABLE)
		do
			create Result.make (iteration_lower, iteration_upper)
		ensure -- from READABLE_INDEXABLE
			not_void: Result /= Void
			empty_if_not_in_order: (iteration_lower > iteration_upper) implies Result.is_empty
			same_lower_if_not_empty: (iteration_lower <= iteration_upper) implies Result.lower = iteration_lower
			same_upper_if_not_empty: (iteration_lower <= iteration_upper) implies Result.upper = iteration_upper
		end

	iteration_lower: INTEGER_32
			-- Minimum index.
			-- (from HASH_TABLE)
		do
			Result := next_iteration_position (-1)
		end

	iteration_upper: INTEGER_32
			-- Maximum index.
			-- (from HASH_TABLE)
		do
			Result := previous_iteration_position (keys.count)
		end

	occurrences (v: detachable ANY): INTEGER_32
			-- Number of table items equal to v.
			-- (from HASH_TABLE)
		local
			old_iteration_position: INTEGER_32
		do
			old_iteration_position := iteration_position
			if object_comparison then
				from
					start
				until
					off
				loop
					if item_for_iteration ~ v then
						Result := Result + 1
					end
					forth
				end
			else
				from
					start
				until
					off
				loop
					if item_for_iteration = v then
						Result := Result + 1
					end
					forth
				end
			end
			iteration_position := old_iteration_position
		ensure -- from BAG
			non_negative_occurrences: Result >= 0
		end
	
feature {NONE} -- Measurement

	estimated_count_of (other: ITERABLE [detachable ANY]): INTEGER_32
			-- Estimated number of elements in other.
			-- (from CONTAINER)
		do
			if attached {FINITE [detachable ANY]} other as f then
				Result := f.count
			elseif attached {READABLE_INDEXABLE [detachable ANY]} other as r then
				Result := r.upper - r.lower + 1
			end
		ensure -- from CONTAINER
			instance_free: class
			non_negative_result: Result >= 0
		end
	
feature -- Comparison

	frozen deep_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void
			-- or attached to isomorphic object structures?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_deep_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			shallow_implies_deep: standard_equal (a, b) implies Result
			both_or_none_void: (a = Void) implies (Result = (b = Void))
			same_type: (Result and (a /= Void)) implies (b /= Void and then a.same_type (b))
			symmetric: Result implies deep_equal (b, a)
		end

	disjoint (other: HASH_TABLE [detachable ANY, STRING_8]): BOOLEAN
			-- Is Current and other disjoint on their keys?
			-- Use same_keys for comparison.
			-- (from HASH_TABLE)
		do
			Result := is_empty or else other.is_empty or else not across
				other as o
			some
				has (o.key)
			end
		end

	frozen equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached
			-- to objects considered equal?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.is_equal (b))
		end

	frozen is_deep_equal alias "≡≡≡" (other: MISMATCH_INFORMATION): BOOLEAN
			-- Are Current and other attached to isomorphic object structures?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			shallow_implies_deep: standard_is_equal (other) implies Result
			same_type: Result implies same_type (other)
			symmetric: Result implies other.is_deep_equal (Current)
		end

	is_equal (other: MISMATCH_INFORMATION): BOOLEAN
			-- Does table contain the same information as other?
			-- (from HASH_TABLE)
		require -- from ANY
			other_not_void: other /= Void
		do
			if count = other.count and then object_comparison = other.object_comparison and then has_default = other.has_default then
				Result := True
				across
					Current as l_c
				until
					not Result
				loop
					other.search (l_c.key)
					if other.found then
						if object_comparison then
							Result := l_c.item ~ other.found_item
						else
							Result := l_c.item = other.found_item
						end
					else
						Result := False
					end
				end
			end
		ensure -- from ANY
			symmetric: Result implies other ~ Current
			consistent: standard_is_equal (other) implies Result
		end

	same_keys (a_search_key, a_key: STRING_8): BOOLEAN
			-- Does a_search_key equal to a_key?
			-- (from HASH_TABLE)
		do
			Result := a_search_key ~ a_key
		end

	frozen standard_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached to
			-- field-by-field identical objects of the same type?
			-- Always uses default object comparison criterion.
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.standard_is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.standard_is_equal (b))
		end

	frozen standard_is_equal alias "≜" (other: MISMATCH_INFORMATION): BOOLEAN
			-- Is other attached to an object of the same type
			-- as current object, and field-by-field identical to it?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			same_type: Result implies same_type (other)
			symmetric: Result implies other.standard_is_equal (Current)
		end
	
feature -- Status report

	after: BOOLEAN
			-- Is cursor past last item?
			-- Was declared in HASH_TABLE as synonym of off.
			-- (from HASH_TABLE)
		do
			Result := is_off_position (iteration_position)
		end

	changeable_comparison_criterion: BOOLEAN
			-- May object_comparison be changed?
			-- (Answer: yes by default.)
			-- (from CONTAINER)
		do
			Result := True
		end

	conflict: BOOLEAN
			-- Did last operation cause a conflict?
			-- (from HASH_TABLE)
		do
			Result := control = Conflict_constant
		end

	conforms_to (other: ANY): BOOLEAN
			-- Does type of current object conform to type
			-- of other (as per Eiffel: The Language, chapter 13)?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		end

	empty: BOOLEAN
		obsolete "ELKS 2000: Use `is_empty' instead. [2017-05-31]"
			-- Is there no element?
			-- (from CONTAINER)
		do
			Result := is_empty
		end

	Extendible: BOOLEAN = False
			-- May new items be added?
			-- (from HASH_TABLE)

	found: BOOLEAN
			-- Did last operation find the item sought?
			-- (from HASH_TABLE)
		do
			Result := control = Found_constant
		end

	Full: BOOLEAN = False
			-- Is structure filled to capacity?
			-- (from HASH_TABLE)

	inserted: BOOLEAN
			-- Did last operation insert an item?
			-- (from HASH_TABLE)
		do
			Result := control = Inserted_constant
		end

	is_empty: BOOLEAN
			-- Is structure empty?
			-- (from FINITE)
		require -- from  CONTAINER
			True
		do
			Result := count = 0
		end

	is_inserted (v: detachable ANY): BOOLEAN
			-- Has v been inserted by the most recent insertion?
			-- (By default, the value returned is equivalent to calling 
			-- has (v). However, descendants might be able to provide more
			-- efficient implementations.)
			-- (from COLLECTION)
		do
			Result := has_item (v)
		end

	is_version_mismatched: BOOLEAN
			-- Is the stored_version different from the current system version?
		do
			Result := stored_version /~ current_version
		end

	not_found: BOOLEAN
			-- Did last operation fail to find the item sought?
			-- (from HASH_TABLE)
		do
			Result := control = Not_found_constant
		end

	object_comparison: BOOLEAN
			-- Must search operations use equal rather than =
			-- for comparing references? (Default: no, use =.)
			-- (from CONTAINER)

	off: BOOLEAN
			-- Is cursor past last item?
			-- Was declared in HASH_TABLE as synonym of after.
			-- (from HASH_TABLE)
		do
			Result := is_off_position (iteration_position)
		end

	prunable: BOOLEAN
			-- May items be removed? (Answer: yes.)
			-- (from DYNAMIC_TABLE)
		do
			Result := True
		end

	removed: BOOLEAN
			-- Did last operation remove an item?
			-- (from HASH_TABLE)
		do
			Result := control = Removed_constant
		end

	replaced: BOOLEAN
			-- Did last operation replace an item?
			-- (from HASH_TABLE)
		do
			Result := control = Replaced_constant
		end

	same_type (other: ANY): BOOLEAN
			-- Is type of current object identical to type of other?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			definition: Result = (conforms_to (other) and other.conforms_to (Current))
		end

	valid_cursor (c: CURSOR): BOOLEAN
			-- Can cursor be moved to position c?
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			c_not_void: c /= Void
		local
			i: INTEGER_32
		do
			if attached {HASH_TABLE_CURSOR} c as ht_cursor then
				i := ht_cursor.position
				Result := is_off_position (i) or else truly_occupied (i)
			end
		end

	valid_iteration_index (i: INTEGER_32): BOOLEAN
			-- Is i a valid index?
			-- (from HASH_TABLE)
		do
			Result := truly_occupied (i)
		ensure -- from READABLE_INDEXABLE
			only_if_in_index_set: Result implies (iteration_lower <= i and i <= iteration_upper)
		end

	valid_key (k: STRING_8): BOOLEAN
		obsolete "Remove the call to this feature or use `has` instead. [2018-11-30]"
			-- Is k a valid key?
			-- (from HASH_TABLE)
		do
			Result := True
			debug ("prevent_hash_table_catcall")
				if not ({STRING_8}).is_expanded and then attached k then
					Result := k.generating_type ~ {detachable STRING_8}
				end
			end
		end
	
feature -- Status setting

	compare_objects
			-- Ensure that future search operations will use equal
			-- rather than = for comparing references.
			-- (from CONTAINER)
		require -- from CONTAINER
			changeable_comparison_criterion: changeable_comparison_criterion
		do
			object_comparison := True
		ensure -- from CONTAINER
				object_comparison
		end

	compare_references
			-- Ensure that future search operations will use =
			-- rather than equal for comparing references.
			-- (from CONTAINER)
		require -- from CONTAINER
			changeable_comparison_criterion: changeable_comparison_criterion
		do
			object_comparison := False
		ensure -- from CONTAINER
			reference_comparison: not object_comparison
		end
	
feature -- Cursor movement

	forth
			-- Advance cursor to next occupied position,
			-- or off if no such position remains.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			not_off: not off
		do
			iteration_position := next_iteration_position (iteration_position)
		end

	go_to (c: CURSOR)
			-- Move to position c.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			c_not_void: c /= Void
			valid_cursor: valid_cursor (c)
		do
			if attached {HASH_TABLE_CURSOR} c as ht_cursor then
				iteration_position := ht_cursor.position
			end
		end

	search (key: STRING_8)
			-- Search for item of key key.
			-- If found, set found to true, and set
			-- found_item to item associated with key.
			-- (from HASH_TABLE)
		local
			old_position: INTEGER_32
			l_default_value: detachable ANY
		do
			old_position := item_position
			internal_search (key)
			if found then
				found_item := content.item (position)
			else
				found_item := l_default_value
			end
			item_position := old_position
		ensure -- from HASH_TABLE
			found_or_not_found: found or not_found
			item_if_found: found implies (found_item = item (key))
		end

	search_item: detachable ANY
		obsolete "Use `found_item` instead. [2017-05-31]"
			-- (from HASH_TABLE)
		do
			Result := found_item
		end

	start
			-- Bring cursor to first position.
			-- (from HASH_TABLE)
		do
			iteration_position := next_iteration_position (-1)
		end
	
feature {HASH_TABLE_ITERATION_CURSOR} -- Cursor movement

	next_iteration_position (a_position: like iteration_position): like iteration_position
			-- Given an iteration position, advanced to the next one taking into account deleted
			-- slots in the content and keys structures.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			a_position_big_enough: a_position >= -1
			a_position_small_enough: a_position < keys.count
		local
			l_deleted_marks: like deleted_marks
			l_table_size: INTEGER_32
		do
			l_deleted_marks := deleted_marks
			l_table_size := content.count
			from
				Result := a_position + 1
			until
				Result >= l_table_size or else not l_deleted_marks.item (Result)
			loop
				Result := Result + 1
			end
		ensure -- from HASH_TABLE
			is_increased: Result > a_position
			is_below_upper_bound: Result <= keys.count
		end

	previous_iteration_position (a_position: like iteration_position): like iteration_position
			-- Given an iteration position, go to the previous one taking into account deleted
			-- slots in the content and keys structures.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			a_position_big_enough: a_position >= 0
			a_position_small_enough: a_position <= keys.count
		local
			l_deleted_marks: like deleted_marks
		do
			l_deleted_marks := deleted_marks
			from
				Result := a_position - 1
			until
				Result <= -1 or else not l_deleted_marks.item (Result)
			loop
				Result := Result - 1
			end
		ensure -- from HASH_TABLE
			is_decreased: Result < a_position
			is_above_lower_bound: Result >= -1
		end
	
feature -- Element change

	extend (new: detachable ANY; key: STRING_8)
			-- Assuming there is no item of key key,
			-- insert new with key.
			-- Set inserted.
			--
			-- To choose between various insert/replace procedures,
			-- see instructions in the Indexing clause.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			not_present: not has (key)
		local
			l_default_key: detachable STRING_8
			l_new_pos, l_new_index_pos: like position
		do
			search_for_insertion (key)
			if soon_full then
				add_space
				search_for_insertion (key)
			end
			if deleted_item_position = Ht_impossible_position then
				l_new_pos := keys.count
				l_new_index_pos := item_position
			else
				l_new_pos := deleted_position (deleted_item_position)
				l_new_index_pos := deleted_item_position;
				deleted_marks.force (False, l_new_pos)
			end;
			indexes_map.put (l_new_pos, l_new_index_pos);
			content.force (new, l_new_pos);
			keys.force (key, l_new_pos)
			if key = l_default_key then
				has_default := True
			end
			count := count + 1
			control := Inserted_constant
		ensure -- from HASH_TABLE
			inserted: inserted
			insertion_done: item (key) = new
			one_more: count = old count + 1
			default_property: has_default = ((key = computed_default_key) or (old has_default))
		end

	fill (other: CONTAINER [detachable ANY])
			-- Fill with as many items of other as possible.
			-- The representations of other and current structure
			-- need not be the same.
			-- (from COLLECTION)
		require -- from COLLECTION
			other_not_void: other /= Void
			extendible: Extendible
		local
			lin_rep: LINEAR [detachable ANY]
		do
			lin_rep := other.linear_representation
			from
				lin_rep.start
			until
				not Extendible or else lin_rep.off
			loop
				collection_extend (lin_rep.item);
				lin_rep.forth
			end
		end

	force (new: detachable ANY; key: STRING_8)
			-- Update table so that new will be the item associated
			-- with key.
			-- If there was an item for that key, set found
			-- and set found_item to that item.
			-- If there was none, set not_found and set
			-- found_item to the default value.
			--
			-- To choose between various insert/replace procedures,
			-- see instructions in the Indexing clause.
			-- (from HASH_TABLE)
		require -- from TABLE
			valid_key: has (key)
		require else -- from HASH_TABLE
				True
		local
			l_default_key: detachable STRING_8
			l_default_value: detachable ANY
			l_new_pos, l_new_index_pos: like position
		do
			internal_search (key)
			if not_found then
				if soon_full then
					add_space
					internal_search (key)
				end
				if deleted_item_position = Ht_impossible_position then
					l_new_pos := keys.count
					l_new_index_pos := item_position
				else
					l_new_pos := deleted_position (deleted_item_position)
					l_new_index_pos := deleted_item_position;
					deleted_marks.force (False, l_new_pos)
				end;
				indexes_map.put (l_new_pos, l_new_index_pos);
				keys.force (key, l_new_pos)
				if key = l_default_key then
					has_default := True
				end
				count := count + 1
				found_item := l_default_value
			else
				l_new_pos := position
				found_item := content.item (l_new_pos)
			end;
			content.force (new, l_new_pos)
		ensure -- from TABLE
			inserted: definite_item (key) = new
		ensure then -- from HASH_TABLE
			insertion_done: item (key) = new
			now_present: has (key)
			found_or_not_found: found or not_found
			not_found_if_was_not_present: not_found = not (old has (key))
			same_count_or_one_more: (count = old count) or (count = old count + 1)
			found_item_is_old_item: found implies (found_item = old (item (key)))
			default_value_if_not_found: not_found implies (found_item = computed_default_value)
			default_property: has_default = ((key = computed_default_key) or ((key /= computed_default_key) and (old has_default)))
		end

	merge (other: HASH_TABLE [detachable ANY, STRING_8])
			-- Merge other into Current. If other has some elements
			-- with same key as in Current, replace them by one from
			-- other.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			other_not_void: other /= Void
		do
			across
				other as other_cursor
			loop
				force (other_cursor.item, other_cursor.key)
			end
		ensure -- from HASH_TABLE
			inserted: across
					other as other_cursor
				all
					has (other_cursor.key)
				end
		end

	put (new: detachable ANY; key: STRING_8)
			-- Insert new with key if there is no other item
			-- associated with the same key.
			-- Set inserted if and only if an insertion has
			-- been made (i.e. key was not present).
			-- If so, set position to the insertion position.
			-- If not, set conflict.
			-- In either case, set found_item to the item
			-- now associated with key (previous item if
			-- there was one, new otherwise).
			--
			-- To choose between various insert/replace procedures,
			-- see instructions in the Indexing clause.
			-- (from HASH_TABLE)
		require -- from TABLE
			valid_key: has (key)
		require else -- from HASH_TABLE
				True
		local
			l_default_key: detachable STRING_8
			l_new_pos, l_new_index_pos: like position
		do
			internal_search (key)
			if found then
				set_conflict
				found_item := content.item (position)
			else
				if soon_full then
					add_space
					internal_search (key)
					check
						not_present: not found
					end
				end
				if deleted_item_position = Ht_impossible_position then
					l_new_pos := keys.count
					l_new_index_pos := item_position
				else
					l_new_pos := deleted_position (deleted_item_position)
					l_new_index_pos := deleted_item_position;
					deleted_marks.force (False, l_new_pos)
				end;
				indexes_map.put (l_new_pos, l_new_index_pos);
				content.force (new, l_new_pos);
				keys.force (key, l_new_pos)
				if key = l_default_key then
					has_default := True
				end
				count := count + 1
				found_item := new
				control := Inserted_constant
			end
		ensure then -- from HASH_TABLE
			conflict_or_inserted: conflict or inserted
			insertion_done: inserted implies item (key) = new
			now_present: inserted implies has (key)
			one_more_if_inserted: inserted implies (count = old count + 1)
			unchanged_if_conflict: conflict implies (count = old count)
			same_item_if_conflict: conflict implies (item (key) = old (item (key)))
			found_item_associated_with_key: found_item = item (key)
			new_item_if_inserted: inserted implies (found_item = new)
			old_item_if_conflict: conflict implies (found_item = old (item (key)))
			default_property: has_default = ((inserted and (key = computed_default_key)) or ((conflict or (key /= computed_default_key)) and (old has_default)))
		end

	replace (new: detachable ANY; key: STRING_8)
			-- Replace item at key, if present,
			-- with new; do not change associated key.
			-- Set replaced if and only if a replacement has been made
			-- (i.e. key was present); otherwise set not_found.
			-- Set found_item to the item previously associated
			-- with key (default value if there was none).
			--
			-- To choose between various insert/replace procedures,
			-- see instructions in the Indexing clause.
			-- (from HASH_TABLE)
		local
			l_default_item: detachable ANY
		do
			internal_search (key)
			if found then
				found_item := content.item (position);
				content.put (new, position)
				control := Replaced_constant
			else
				found_item := l_default_item
			end
		ensure -- from HASH_TABLE
			replaced_or_not_found: replaced or not_found
			insertion_done: replaced implies item (key) = new
			no_change_if_not_found: not_found implies item (key) = old item (key)
			found_item_is_old_item: found_item = old item (key)
		end

	replace_key (new_key: STRING_8; old_key: STRING_8)
			-- If there is an item of key old_key and no item of key
			-- new_key, replace the former's key by new_key,
			-- set replaced, and set found_item to the item
			-- previously associated with old_key.
			-- Otherwise set not_found or conflict respectively.
			-- If conflict, set found_item to the item previously
			-- associated with new_key.
			--
			-- To choose between various insert/replace procedures,
			-- see instructions in the Indexing clause.
			-- (from HASH_TABLE)
		local
			l_item: detachable ANY
		do
			internal_search (new_key)
			if not found then
				internal_search (old_key)
				if found then
					l_item := content.item (position)
					remove (old_key)
					put (l_item, new_key)
					control := Replaced_constant
				end
			else
				set_conflict
				found_item := content.item (position)
			end
		ensure -- from HASH_TABLE
			same_count: count = old count
			replaced_or_conflict_or_not_found: replaced or conflict or not_found
			old_absent: (replaced and not same_keys (new_key, old_key)) implies (not has (old_key))
			new_present: (replaced or conflict) = has (new_key)
			new_item: replaced implies (item (new_key) = old (item (old_key)))
			not_found_implies_no_old_key: not_found implies old (not has (old_key))
			conflict_iff_already_present: conflict = old has (new_key)
			not_inserted_if_conflict: conflict implies (item (new_key) = old item (new_key))
		end
	
feature -- Removal

	clear_all
		obsolete "Use `wipe_out' instead. [2017-05-31]"
			-- (from HASH_TABLE)
		do
			wipe_out
		end

	prune (v: detachable ANY)
			-- Remove first occurrence of v, if any,
			-- after cursor position.
			-- Move cursor to right neighbor.
			-- (or after if no right neighbor or v does not occur)
			-- (from HASH_TABLE)
		require -- from COLLECTION
			prunable: prunable
		do
			if object_comparison then
				from
				until
					after or else item_for_iteration ~ v
				loop
					forth
				end
			else
				from
				until
					after or else item_for_iteration = v
				loop
					forth
				end
			end
			if not after then
				remove (key_for_iteration)
			end
		end

	prune_all (v: detachable ANY)
			-- Remove all occurrences of v.
			-- (Reference or object equality,
			-- based on object_comparison.)
			-- (from COLLECTION)
		require -- from COLLECTION
			prunable: prunable
		do
			from
			until
				not has_item (v)
			loop
				prune (v)
			end
		ensure -- from COLLECTION
			no_more_occurrences: not has_item (v)
		end

	remove (key: STRING_8)
			-- Remove item associated with key, if present.
			-- Set removed if and only if an item has been
			-- removed (i.e. key was present);
			-- if so, set position to index of removed element.
			-- If not, set not_found.
			-- Reset found_item to its default value if removed.
			-- (from HASH_TABLE)
		require -- from DYNAMIC_TABLE
			prunable: prunable
			valid_key: has (key)
		require else -- from HASH_TABLE
				prunable
		local
			l_default_key: detachable STRING_8
			l_default_value: detachable ANY
			l_pos: like position
			l_nb_removed_items: INTEGER_32
		do
			internal_search (key)
			if found then
				l_pos := position
				if key = l_default_key then
					has_default := False
				end;
				deleted_marks.put (True, l_pos);
				indexes_map.put (- l_pos + Ht_deleted_position, item_position)
				if iteration_position = l_pos then
					forth
				end
				count := count - 1
				ht_lowest_deleted_position := l_pos.min (ht_lowest_deleted_position)
				if ht_lowest_deleted_position = count then
					l_nb_removed_items := content.count - ht_lowest_deleted_position;
					content.remove_tail (l_nb_removed_items);
					keys.remove_tail (l_nb_removed_items);
					deleted_marks.fill_with (False, ht_lowest_deleted_position, deleted_marks.count - 1)
					ht_deleted_item := l_default_value
					ht_deleted_key := l_default_key
					ht_lowest_deleted_position := Ht_max_position
				elseif attached ht_deleted_item as l_item and attached ht_deleted_key as l_key then
					content.put (l_item, l_pos);
					keys.put (l_key, l_pos)
				else
					ht_deleted_item := content.item (l_pos)
					ht_deleted_key := keys.item (l_pos)
				end
				control := Removed_constant
				found_item := l_default_value
			end
		ensure then -- from HASH_TABLE
			removed_or_not_found: removed or not_found
			not_present: not has (key)
			one_less: found implies (count = old count - 1)
			default_case: (key = computed_default_key) implies (not has_default)
			non_default_case: (key /= computed_default_key) implies (has_default = old has_default)
		end

	wipe_out
			-- Reset all items to default values; reset status.
			-- (from HASH_TABLE)
		require -- from COLLECTION
			prunable: prunable
		local
			l_default_value: detachable ANY
		do
			content.wipe_out;
			keys.wipe_out;
			deleted_marks.fill_with (False, 0, deleted_marks.upper);
			indexes_map.fill_with (Ht_impossible_position, 0, capacity)
			found_item := l_default_value
			count := 0
			item_position := 0
			iteration_position := keys.count
			control := 0
			has_default := False
		ensure -- from COLLECTION
			wiped_out: is_empty
		ensure then -- from HASH_TABLE
			position_equal_to_zero: item_position = 0
			count_equal_to_zero: count = 0
			has_default_set: not has_default
			no_status: not special_status
		end
	
feature -- Transformation

	correct_mismatch
			-- Attempt to correct object mismatch during retrieve using Mismatch_information.
			-- (from HASH_TABLE)
		local
			l_old_deleted_marks: detachable SPECIAL [BOOLEAN]
			i, l_capacity, l_count: INTEGER_32
			l_new_table: MISMATCH_INFORMATION
			l_default_item: like ht_deleted_item
			l_default_key: like ht_deleted_key
		do
			if not Mismatch_information.has ("hash_table_version_64") then
				if attached {ARRAY [detachable ANY]} Mismatch_information.item ("content") as array_content then
					content := array_content.area
				end
				if attached {ARRAY [STRING_8]} Mismatch_information.item ("keys") as array_keys then
					keys := array_keys.area
				end
				if attached {ARRAY [BOOLEAN]} Mismatch_information.item ("deleted_marks") as array_marks then
					deleted_marks := array_marks.area
				end
				if deleted_marks /= Void and keys /= Void and then not Mismatch_information.has ("hash_table_version_57") and then deleted_marks.count /= keys.count then
					l_old_deleted_marks := deleted_marks
					create deleted_marks.make_empty (keys.count);
					deleted_marks.copy_data (l_old_deleted_marks, 0, 0, l_old_deleted_marks.count)
				end
				if attached {INTEGER_32} Mismatch_information.item ("count") as l_retrieved_count then
					l_count := l_retrieved_count
				end
				if content = Void or keys = Void or deleted_marks = Void then
					Precursor {MISMATCH_CORRECTOR}
				else
					from
						l_capacity := keys.count
						l_new_table := empty_duplicate (l_count)
					until
						i = l_capacity
					loop
						if attached keys.item (i) as l_key_item and then l_key_item /= l_default_key then
							l_new_table.put (content.item (i), l_key_item)
						end
						i := i + 1
					end
					if attached {BOOLEAN} Mismatch_information.item ("has_default") as l_bool and then l_bool then
						l_new_table.put (content.item (content.capacity - 1), keys.item (l_capacity - 1))
					end
					set_content (l_new_table.content)
					set_keys (l_new_table.keys)
					set_deleted_marks (l_new_table.deleted_marks)
					set_indexes_map (l_new_table.indexes_map)
					capacity := l_new_table.capacity
					iteration_position := l_new_table.iteration_position
					deleted_item_position := l_new_table.deleted_item_position
					item_position := l_new_table.item_position
					ht_lowest_deleted_position := Ht_max_position
					ht_deleted_item := l_default_item
					ht_deleted_key := l_default_key
				end
				control := 0
			end
		end
	
feature {NONE} -- Transformation

	hash_table_version_64: BOOLEAN
			-- Fake attribute for versioning purposes. Used in correct_mismatch.
			-- (from HASH_TABLE)
	
feature -- Conversion

	linear_representation: ARRAYED_LIST [detachable ANY]
			-- Representation as a linear structure
			-- (from HASH_TABLE)
		require -- from  CONTAINER
			True
		local
			old_iteration_position: INTEGER_32
		do
			old_iteration_position := iteration_position
			from
				create Result.make (count)
				start
			until
				off
			loop
				Result.extend (item_for_iteration)
				forth
			end
			iteration_position := old_iteration_position
		ensure then -- from HASH_TABLE
			result_exists: Result /= Void
			good_count: Result.count = count
		end
	
feature -- Duplication

	frozen clone (other: detachable ANY): like other
		obsolete "Use `twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- equal to other
			--
			-- For non-void other, clone calls copy;
			-- to change copying/cloning semantics, redefine copy.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.twin
			end
		ensure -- from ANY
			instance_free: class
			equal: Result ~ other
		end

	copy (other: MISMATCH_INFORMATION)
			-- Re-initialize from other.
			-- (from HASH_TABLE)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		do
			if other /= Current then
				standard_copy (other)
				set_content (other.content.twin)
				set_keys (other.keys.twin)
				set_deleted_marks (other.deleted_marks.twin)
				set_indexes_map (other.indexes_map.twin)
			end
		ensure -- from ANY
			is_equal: Current ~ other
		end

	frozen deep_clone (other: detachable ANY): like other
		obsolete "Use `deep_twin' instead. [2017-05-31]"
			-- Void if other is void: otherwise, new object structure
			-- recursively duplicated from the one attached to other
			-- (from ANY)
		do
			if other /= Void then
				Result := other.deep_twin
			end
		ensure -- from ANY
			instance_free: class
			deep_equal: deep_equal (other, Result)
		end

	frozen deep_copy (other: MISMATCH_INFORMATION)
			-- Effect equivalent to that of:
			--		copy (other . deep_twin)
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		do
			copy (other.deep_twin)
		ensure -- from ANY
			deep_equal: deep_equal (Current, other)
		end

	frozen deep_twin: MISMATCH_INFORMATION
			-- New object structure recursively duplicated from Current.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			deep_twin_not_void: Result /= Void
			deep_equal: deep_equal (Current, Result)
		end

	frozen standard_clone (other: detachable ANY): like other
		obsolete "Use `standard_twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.standard_twin
			end
		ensure -- from ANY
			instance_free: class
			equal: standard_equal (Result, other)
		end

	frozen standard_copy (other: MISMATCH_INFORMATION)
			-- Copy every field of other onto corresponding field
			-- of current object.
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		external
			"built_in"
		ensure -- from ANY
			is_standard_equal: standard_is_equal (other)
		end

	frozen standard_twin: MISMATCH_INFORMATION
			-- New object field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			standard_twin_not_void: Result /= Void
			equal: standard_equal (Result, Current)
		end

	frozen twin: MISMATCH_INFORMATION
			-- New object equal to Current
			-- twin calls copy; to change copying/twinning semantics, redefine copy.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			twin_not_void: Result /= Void
			is_equal: Result ~ Current
		end
	
feature {NONE} -- Duplication

	empty_duplicate (n: INTEGER_32): MISMATCH_INFORMATION
			-- Create an empty copy of Current that can accommodate n items
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			n_non_negative: n >= 0
		do
			create Result.make (n)
			if object_comparison then
				Result.compare_objects
			end
		ensure -- from HASH_TABLE
			empty_duplicate_attached: Result /= Void
		end
	
feature -- Basic operations

	frozen as_attached: attached MISMATCH_INFORMATION
		obsolete "Remove calls to this feature. [2017-05-31]"
			-- Attached version of Current.
			-- (Can be used during transitional period to convert
			-- non-void-safe classes to void-safe ones.)
			-- (from ANY)
		do
			Result := Current
		end

	frozen default: detachable MISMATCH_INFORMATION
			-- Default value of object's type
			-- (from ANY)
		do
		end

	frozen default_pointer: POINTER
			-- Default value of type POINTER
			-- (Avoid the need to write p.default for
			-- some p of type POINTER.)
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end

	default_rescue
			-- Process exception for routines with no Rescue clause.
			-- (Default: do nothing.)
			-- (from ANY)
		do
		end

	frozen do_nothing
			-- Execute a null action.
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end
	
feature -- Inapplicable

	bag_put (v: detachable ANY)
			-- Ensure that structure includes v.
			-- (from TABLE)
		require -- from COLLECTION
			extendible: Extendible
		do
		ensure -- from COLLECTION
			item_inserted: is_inserted (v)
		end

	collection_extend (v: detachable ANY)
			-- Insert a new occurrence of v.
			-- (from HASH_TABLE)
		require -- from COLLECTION
			extendible: Extendible
		do
		ensure -- from COLLECTION
			item_inserted: is_inserted (v)
		end
	
feature {NONE} -- Implementation

	add_space
			-- Increase capacity.
			-- (from HASH_TABLE)
		do
			accommodate (count + count // 2)
		ensure -- from HASH_TABLE
			count_not_changed: count = old count
			breathing_space: count < capacity
		end

	computed_default_key: detachable STRING_8
			-- Default key
			-- (For performance reasons, used only in assertions;
			-- elsewhere, see use of local entity l_default_key.)
			-- (from HASH_TABLE)
		do
		end

	computed_default_value: detachable ANY
			-- Default value of type G
			-- (For performance reasons, used only in assertions;
			-- elsewhere, see use of local entity l_default_value.)
			-- (from HASH_TABLE)
		do
		end

	Conflict_constant: INTEGER_32 = 1
			-- Could not insert an already existing key
			-- (from HASH_TABLE)

	default_key_value: detachable ANY
			-- Value associated with the default key, if any
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			has_default: has_default
		do
			Result := content [indexes_map [capacity]]
		end

	deleted (i: INTEGER_32): BOOLEAN
			-- Is position i that of a deleted item?
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			in_bounds: i >= 0 and i <= capacity
		do
			Result := indexes_map.item (i) <= Ht_deleted_position
		end

	deleted_position (a_pos: INTEGER_32): INTEGER_32
			-- Given the position of a deleted item at a_pos gives the associated position
			-- in content/keys.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			deleted: deleted (a_pos)
		do
			Result := - indexes_map.item (a_pos) + Ht_deleted_position
			Result := Result.min (keys.count)
		ensure -- from HASH_TABLE
			deleted_position_non_negative: Result >= 0
			deleted_position_valid: Result <= keys.count and Result <= content.count
		end

	Found_constant: INTEGER_32 = 2
			-- Key found
			-- (from HASH_TABLE)

	ht_deleted_item: detachable ANY
			-- (from HASH_TABLE)

	ht_deleted_key: detachable STRING_8
			-- Store the item and key that will be used to replace an element of the HASH_TABLE
			-- that will be removed. If elements being removed are at the end of content or keys
			-- then they are both Void. It is only used when removing an element at a position strictly
			-- less than count.
			-- (from HASH_TABLE)

	Ht_deleted_position: INTEGER_32 = -2
			-- Marked a deleted position.
			-- (from HASH_TABLE)

	Ht_impossible_position: INTEGER_32 = -1
			-- Position outside the array indices.
			-- (from HASH_TABLE)

	ht_lowest_deleted_position: INTEGER_32
			-- Index of the lowest deleted position thus far.
			-- (from HASH_TABLE)

	Ht_max_position: INTEGER_32 = 2147483645
			-- Maximum possible position
			-- (from HASH_TABLE)

	initial_position (hash_value: INTEGER_32): INTEGER_32
			-- Initial position for an item of hash code hash_value
			-- (from HASH_TABLE)
		do
			Result := hash_value \\ capacity
		end

	Inserted_constant: INTEGER_32 = 4
			-- Insertion successful
			-- (from HASH_TABLE)

	internal_put (value: ANY; ckey: POINTER)
			-- Allows run-time to insert items into table
		do
			put (value, create {STRING_8}.make_from_c (ckey))
		end

	internal_search (key: STRING_8)
			-- Search for item of key 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,
			-- and set status to found or not_found.
			-- (from HASH_TABLE)
		local
			l_default_key: detachable STRING_8
			hash_value, increment, l_pos, l_item_pos, l_capacity: INTEGER_32
			l_first_deleted_position: INTEGER_32
			stop: INTEGER_32
			l_keys: like keys
			l_indexes: like indexes_map
			l_deleted_marks: like deleted_marks
			l_key: STRING_8
		do
			l_first_deleted_position := Ht_impossible_position
			if key = l_default_key or key = Void then
				item_position := capacity
				if has_default then
					control := Found_constant
				else
					control := Not_found_constant
				end
			else
				from
					l_keys := keys
					l_indexes := indexes_map
					l_deleted_marks := deleted_marks
					l_capacity := capacity
					stop := l_capacity
					hash_value := hash_code_of (key)
					increment := 1 + hash_value \\ (l_capacity - 1)
					l_item_pos := (hash_value \\ l_capacity) - increment
					control := Not_found_constant
				until
					stop = 0
				loop
					l_item_pos := (l_item_pos + increment) \\ l_capacity
					l_pos := l_indexes [l_item_pos]
					if l_pos >= 0 then
						l_key := l_keys.item (l_pos)
						debug ("detect_hash_table_catcall")
							check
								catcall_detected: l_key /= Void and then l_key.same_type (key)
							end
						end
						if same_keys (l_key, key) then
							stop := 1
							control := Found_constant
						end
					elseif l_pos = Ht_impossible_position then
						stop := 1
					elseif l_first_deleted_position = Ht_impossible_position then
						l_pos := - l_pos + Ht_deleted_position
						check
							l_pos_valid: l_pos < l_deleted_marks.count
						end
						if not l_deleted_marks [l_pos] then
							stop := 1
						else
							l_first_deleted_position := l_item_pos
						end
					end
					stop := stop - 1
				end
				item_position := l_item_pos
			end
			deleted_item_position := l_first_deleted_position
		ensure -- from HASH_TABLE
			found_or_not_found: found or not_found
			deleted_item_at_deleted_position: (deleted_item_position /= Ht_impossible_position) implies deleted (deleted_item_position)
			default_iff_at_capacity: (item_position = capacity) = (key = computed_default_key)
		end

	is_off_position (pos: INTEGER_32): BOOLEAN
			-- Is pos a cursor position outside the authorized range?
			-- (from HASH_TABLE)
		do
			Result := pos < 0 or pos >= keys.count
		end

	key_at (n: INTEGER_32): detachable STRING_8
			-- Key at position n
			-- (from HASH_TABLE)
		do
			if keys.valid_index (n) then
				Result := keys.item (n)
			end
		end

	Minimum_capacity: INTEGER_32 = 2
			-- (from HASH_TABLE)

	Not_found_constant: INTEGER_32 = 8
			-- Key not found
			-- (from HASH_TABLE)

	occupied (i: INTEGER_32): BOOLEAN
			-- Is position i occupied by a non-default key and a value?
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			in_bounds: deleted_marks.valid_index (i)
		do
			if has_default then
				Result := i /= indexes_map.item (capacity) and then not deleted_marks.item (i)
			else
				Result := not deleted_marks.item (i)
			end
		end

	position_increment (hash_value: INTEGER_32): INTEGER_32
			-- Distance between successive positions for hash code
			-- hash_value (computed for no cycle: capacity is prime)
			-- (from HASH_TABLE)
		do
			Result := 1 + hash_value \\ (capacity - 1)
		end

	Removed_constant: INTEGER_32 = 16
			-- Remove successful
			-- (from HASH_TABLE)

	Replaced_constant: INTEGER_32 = 32
			-- Replaced value
			-- (from HASH_TABLE)

	search_for_insertion (key: STRING_8)
			-- Assuming there is no item of key key, compute
			-- position at which to insert such an item.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			not_present: not has (key)
		local
			l_default_key: detachable STRING_8
			hash_value, increment, l_pos, l_item_pos, l_capacity: INTEGER_32
			l_first_deleted_position: INTEGER_32
			stop: INTEGER_32
			l_indexes: like indexes_map
			l_deleted_marks: like deleted_marks
		do
			l_first_deleted_position := Ht_impossible_position
			if key = l_default_key or key = Void then
				check
						not has_default
				end
				item_position := capacity
			else
				from
					l_indexes := indexes_map
					l_deleted_marks := deleted_marks
					l_capacity := capacity
					stop := l_capacity
					hash_value := hash_code_of (key)
					increment := 1 + hash_value \\ (l_capacity - 1)
					l_item_pos := (hash_value \\ l_capacity) - increment
				until
					stop = 0
				loop
					l_item_pos := (l_item_pos + increment) \\ l_capacity
					l_pos := l_indexes [l_item_pos]
					if l_pos >= 0 then
					elseif l_pos = Ht_impossible_position then
						stop := 1
					elseif l_first_deleted_position = Ht_impossible_position then
						l_pos := - l_pos + Ht_deleted_position
						check
							l_pos_valid: l_pos < l_deleted_marks.count
						end
						if not l_deleted_marks [l_pos] then
							stop := 1
						else
							l_first_deleted_position := l_item_pos
						end
					end
					stop := stop - 1
				end
				item_position := l_item_pos
			end
			deleted_item_position := l_first_deleted_position
		ensure -- from HASH_TABLE
			deleted_item_at_deleted_position: (deleted_item_position /= Ht_impossible_position) implies deleted (deleted_item_position)
			default_iff_at_capacity: (item_position = capacity) = (key = computed_default_key)
		end

	Set_callback_pointers
			-- Sets call-back pointers in the run-time
		once
			set_mismatch_information_access (Current, $wipe_out, $internal_put, $set_string_versions)
		end

	set_conflict
			-- Set status to conflict.
			-- (from HASH_TABLE)
		do
			control := Conflict_constant
		ensure -- from HASH_TABLE
			conflict: conflict
		end

	set_content (c: like content)
			-- Assign c to content.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			c_attached: c /= Void
		do
			content := c
		ensure -- from HASH_TABLE
			content_set: content = c
		end

	set_deleted_marks (d: like deleted_marks)
			-- Assign c to content.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			d_attached: d /= Void
		do
			deleted_marks := d
		ensure -- from HASH_TABLE
			deleted_marks_set: deleted_marks = d
		end

	set_found
			-- Set status to found.
			-- (from HASH_TABLE)
		do
			control := Found_constant
		ensure -- from HASH_TABLE
			found: found
		end

	set_indexes_map (v: like indexes_map)
			-- Assign v to indexes_map.
			-- (from HASH_TABLE)
		do
			indexes_map := v
		ensure -- from HASH_TABLE
			indexes_map_set: indexes_map = v
		end

	set_inserted
			-- Set status to inserted.
			-- (from HASH_TABLE)
		do
			control := Inserted_constant
		ensure -- from HASH_TABLE
			inserted: inserted
		end

	set_keys (c: like keys)
			-- Assign c to keys.
			-- (from HASH_TABLE)
		require -- from HASH_TABLE
			c_attached: c /= Void
		do
			keys := c
		ensure -- from HASH_TABLE
			keys_set: keys = c
		end

	set_no_status
			-- Set status to normal.
			-- (from HASH_TABLE)
		do
			control := 0
		ensure -- from HASH_TABLE
			default_status: not special_status
		end

	set_not_found
			-- Set status to not found.
			-- (from HASH_TABLE)
		do
			control := Not_found_constant
		ensure -- from HASH_TABLE
			not_found: not_found
		end

	set_removed
			-- Set status to removed.
			-- (from HASH_TABLE)
		do
			control := Removed_constant
		ensure -- from HASH_TABLE
			removed: removed
		end

	set_replaced
			-- Set status to replaced.
			-- (from HASH_TABLE)
		do
			control := Replaced_constant
		ensure -- from HASH_TABLE
			replaced: replaced
		end

	set_string_versions (a_stored_version, a_current_version: POINTER)
			-- Set stored_version with a_stored_version.
			-- Set current_version with a_current_version.
		local
			l_imm: IMMUTABLE_STRING_8
		do
			if a_stored_version = default_pointer then
				stored_version := Void
			else
				create l_imm.make_from_c (a_stored_version)
				if l_imm.is_empty then
					stored_version := Void
				else
					stored_version := l_imm
				end
			end
			if a_current_version = default_pointer then
				current_version := Void
			else
				create l_imm.make_from_c (a_current_version)
				if l_imm.is_empty then
					current_version := Void
				else
					current_version := l_imm
				end
			end
		end

	special_status: BOOLEAN
			-- Has status been set to some non-default value?
			-- (from HASH_TABLE)
		do
			Result := control > 0
		ensure -- from HASH_TABLE
				Result = (control > 0)
		end

	truly_occupied (i: INTEGER_32): BOOLEAN
			-- Is position i occupied by a key and a value?
			-- (from HASH_TABLE)
		do
			if i >= 0 and i < keys.count then
				Result := (has_default and i = indexes_map.item (capacity)) or else occupied (i)
			end
		ensure -- from HASH_TABLE
			normal_key: (i >= 0 and i < keys.count and i /= indexes_map.item (capacity)) implies (occupied (i) implies Result)
			default_key: (i = indexes_map.item (capacity)) implies (Result = has_default)
		end
	
feature -- Correction

	Mismatch_information: MISMATCH_INFORMATION
			-- Original attribute values of mismatched object
			-- (from MISMATCH_CORRECTOR)
		once
			create Result
		end
	
feature {NONE} -- Externals

	set_mismatch_information_access (obj: ANY; init, add, set_vers: POINTER)
		external
			"C signature (EIF_OBJECT, EIF_PROCEDURE, EIF_PROCEDURE, EIF_PROCEDURE) use <eif_retrieve.h>"
		end
	
feature {HASH_TABLE, HASH_TABLE_ITERATION_CURSOR} -- Implementation: content attributes and preservation

	content: SPECIAL [detachable ANY]
			-- Array of contents
			-- (from HASH_TABLE)

	keys: SPECIAL [STRING_8]
			-- Array of keys
			-- (from HASH_TABLE)
	
feature {HASH_TABLE} -- Implementation: content attributes and preservation

	deleted_marks: SPECIAL [BOOLEAN]
			-- Indexes of deleted positions in content and keys.
			-- (from HASH_TABLE)

	has_default: BOOLEAN
			-- Is the default key present?
			-- (from HASH_TABLE)

	indexes_map: SPECIAL [INTEGER_32]
			-- Indexes of items in content, and keys.
			-- If item is not present, then it has Ht_impossible_position.
			-- If item is deleted, then it has Ht_deleted_position.
			-- (from HASH_TABLE)

	item_position: INTEGER_32
			-- Position in indexes_map for item at position position. Set by internal_search.
			-- (from HASH_TABLE)
	
feature {HASH_TABLE} -- Implementation: search attributes

	control: INTEGER_32
			-- Control code set by operations that may produce
			-- several possible conditions.
			-- (from HASH_TABLE)

	deleted_item_position: INTEGER_32
			-- Place where a deleted element was found during a search
			-- (from HASH_TABLE)

	iteration_position: INTEGER_32
			-- Cursor for iteration primitives
			-- (from HASH_TABLE)

	position: INTEGER_32
			-- Hash table cursor, updated after each operation:
			-- put, remove, has, replace, force, change_key...
			-- (from HASH_TABLE)
		do
			Result := indexes_map.item (item_position)
		end

	soon_full: BOOLEAN
			-- Is table close to being filled to current capacity?
			-- (from HASH_TABLE)
		do
			Result := keys.count = keys.capacity
		ensure -- from HASH_TABLE
				Result = (keys.count = keys.capacity)
		end
	
feature -- Output

	Io: STD_FILES
			-- Handle to standard file setup
			-- (from ANY)
		once
			create Result;
			Result.set_output_default
		ensure -- from ANY
			instance_free: class
			io_not_void: Result /= Void
		end

	out: STRING_8
			-- Printable representation of attributes values
		local
			i: detachable ANY
			k: detachable STRING_8
		do
			from
				create Result.make (20 + class_name.count + 40 * count);
				Result.append ("Attributes of original class ");
				Result.append (class_name);
				Result.append (": ");
				Result.append_integer (count - 1);
				Result.append (" item")
				if count - 1 /= 1 then
					Result.append_character ('s')
				end;
				Result.append_character ('%N')
				start
			until
				after
			loop
				k := key_for_iteration
				if k /~ Type_name_key then
					Result.append ("  ")
					if k = Void then
						Result.append ("Void")
					else
						Result.append (k)
					end;
					Result.append (": ")
					i := item_for_iteration
					if i = Void then
						Result.append ("Void")
					else
						Result.append (i.out)
					end;
					Result.append_character ('%N')
				end
				forth
			end
		ensure -- from ANY
			out_not_void: Result /= Void
		end

	print (o: detachable ANY)
			-- Write terse external representation of o
			-- on standard output.
			-- (from ANY)
		local
			s: READABLE_STRING_8
		do
			if attached o then
				s := o.out
				if attached {READABLE_STRING_32} s as s32 then
					Io.put_string_32 (s32)
				elseif attached {READABLE_STRING_8} s as s8 then
					Io.put_string (s8)
				else
					Io.put_string_32 (s.as_string_32)
				end
			end
		ensure -- from ANY
			instance_free: class
		end

	frozen tagged_out: STRING_8
			-- New string containing terse printable representation
			-- of current object
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			tagged_out_not_void: Result /= Void
		end
	
feature -- Platform

	Operating_environment: OPERATING_ENVIRONMENT
			-- Objects available from the operating system
			-- (from ANY)
		once
			create Result
		ensure -- from ANY
			instance_free: class
			operating_environment_not_void: Result /= Void
		end
	
feature {NONE} -- Retrieval

	frozen internal_correct_mismatch
			-- Called from runtime to perform a proper dynamic dispatch on correct_mismatch
			-- from MISMATCH_CORRECTOR.
			-- (from ANY)
		local
			l_msg: STRING_32
			l_exc: EXCEPTIONS
		do
			if attached {MISMATCH_CORRECTOR} Current as l_corrector then
				l_corrector.correct_mismatch
			else
				create l_msg.make_from_string ("Mismatch: ".as_string_32)
				create l_exc;
				l_msg.append (generating_type.name_32);
				l_exc.raise_retrieval_exception (l_msg)
			end
		end
	
feature -- Settings

	set_versions (a_stored_version: like stored_version; a_current_version: like current_version)
			-- Set stored_version with a_stored_version.
			-- Set current_version with a_current_version.
		do
			stored_version := a_stored_version
			current_version := a_current_version
		ensure
			stored_version_set: stored_version = a_stored_version
			current_version_set: current_version = a_current_version
		end
	
invariant
		-- from HASH_TABLE
	keys_not_void: keys /= Void
	content_not_void: content /= Void
	keys_enough_capacity: keys.count <= capacity + 1
	content_enough_capacity: content.count <= capacity + 1
	valid_iteration_position: off or truly_occupied (iteration_position)
	control_non_negative: control >= 0
	special_status: special_status = (conflict or inserted or replaced or removed or found or not_found)
	count_big_enough: 0 <= count
	count_small_enough: count <= capacity
	slot_count_big_enough: 0 <= count

		-- from FINITE
	empty_definition: is_empty = (count = 0)

		-- from ANY
	reflexive_equality: standard_is_equal (Current)
	reflexive_conformance: conforms_to (Current)

		-- from READABLE_INDEXABLE
	consistent_boundaries: iteration_lower <= iteration_upper or else iteration_lower = iteration_upper + 1

note
	copyright: "Copyright (c) 1984-2020, Eiffel Software and others"
	license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
	source: "[
		Eiffel Software
		5949 Hollister Ave., Goleta, CA 93117 USA
		Telephone 805-685-1006, Fax 805-685-6869
		Website http://www.eiffel.com
		Customer support http://support.eiffel.com
	]"

end -- class MISMATCH_INFORMATION

Generated by ISE EiffelStudio