This site contains older material on Eiffel. For the main Eiffel page, see http://www.eiffel.com.

EiffelBase class
(HTML page generated by ISE Eiffel 4.2)

Eiffel Class
indexing
	description: "Lists implemented as sequences of arrays, the last of which may be non-full. No limit on size (a new array is allocated if list outgrows its initial allocation).";
	status: "See notice at end of class";
	names: list, sequence;
	representation: array, linked;
	access: index, cursor, membership;
	contents: generic;
	date: "$Date: 2007-03-30 19:10:11 +0000 (Fri, 30 Mar 2007) $";
	revision: "$Revision: 95354 $"

class MULTI_ARRAY_LIST [G]

inherit
	DYNAMIC_LIST [G]
		redefine
			start, finish, move, has, first, last, prune_all, search, remove, duplicate, wipe_out, put_left, put_right, put_front, extend
		end

creation
	make

feature -- Initialization

	make (b: INTEGER) is
			-- Create an empty list, setting block_size to b
		local
			first_array: ARRAYED_LIST [G]
		do
			block_size := b;
			create first_array.make (b);
			first_element := new_cell (first_array);
			last_element := first_element;
			active := first_element
		end;

feature -- Access

	item: G is
			-- Item at cursor position
		do
			Result := active.item.item
		end;

	first: like item is
			-- Item at first position
		do
			Result := first_element.item.first
		end;

	last: like item is
			-- Item at last position
		do
			Result := last_element.item.last
		end;

	has (v: like item): BOOLEAN is
			-- Does list include v?
			-- (Reference or object equality,
			-- based on object_comparison.)
		local
			pos: CURSOR
		do
			if notempty then
				pos := cursor;
				start;
				search (v);
				Result := notafter;
				go_to (pos)
			end
		end;

	index: INTEGER;
			-- Current cursor index

	cursor: CURSOR is
			-- Current cursor position
		do
			!MULTAR_LIST_CURSOR [G]! Result.make (active, active.item.index, index)
		end;

	first_element: BI_LINKABLE [ARRAYED_LIST [G]];
			-- First array_sequence element of the list

	last_element: BI_LINKABLE [ARRAYED_LIST [G]];
			-- Last array_sequence element of the list

	block_size: INTEGER;

feature -- Measurement

	count: INTEGER;
			-- Number of items

feature -- Status report

	full: BOOLEAN is
		do
			Result := false
		end;

	valid_cursor (p: CURSOR): BOOLEAN is
			-- Can the cursor be moved to position p?
		local
			al_c: MULTAR_LIST_CURSOR [G]
		do
			al_c ?= p;
			check
				al_c /= void
			end;
			Result := (al_c /= void) and then valid_cursor_index (al_c.index) and then al_c.active.item.valid_cursor_index (al_c.active_index)
		end;

feature -- Cursor movement

	start is
			-- Move cursor to first position.
			-- (No effect if empty)
		do
			active := first_element;
			active.item.start;
			index := 1
		end;

	finish is
			-- Move cursor to last position.
			-- (No effect if empty)
		do
			active := last_element;
			active.item.finish;
			index := count
		end;

	forth is
			-- Move cursor to next position, if any.
		local
			current_array: ARRAYED_LIST [G]
		do
			if notempty then
				current_array := active.item;
				current_array.forth;
				if current_array.after then
					if active /= last_element then
						active := active.right;
						active.item.start
					end
				end
			end;
			index := index + 1
		end;

	back is
			-- Move cursor to previous position, if any.
		local
			current_array: ARRAYED_LIST [G]
		do
			if notempty then
				current_array := active.item;
				current_array.back;
				if current_array.before then
					if active /= first_element then
						active := active.left;
						active.item.finish
					end
				end
			end;
			index := index - 1
		end;

	move (i: INTEGER) is
			-- Move cursor i positions. The cursor
			-- may end up off if the offset is too big.
		local
			counter: INTEGER;
			cell: like active;
			current_array: ARRAYED_LIST [G]
		do
			cell := active;
			current_array := cell.item;
			if i > 0 then
				from
					counter := i + active.item.index
				until
					counter <= current_array.count or cell = void
				loop
					counter := counter - current_array.count;
					cell := cell.right;
					if cell /= void then
						current_array := cell.item
					end
				end;
				if cell = void then
					cell := last_element;
					current_array.finish;
					current_array.forth
				else
					active := cell;
					current_array.go_i_th (counter)
				end
			elseif i < 0 then
				from
					counter := current_array.count - current_array.index - i
				until
					counter <= current_array.count or cell = void
				loop
					counter := counter - current_array.count;
					cell := cell.left;
					if cell /= void then
						current_array := cell.item
					end
				end;
				if counter = current_array.count then
					counter := 0;
					cell := cell.left;
					if cell /= void then
						current_array := cell.item
					end
				end;
				if cell = void then
					cell := first_element;
					current_array.go_i_th (0)
				else
					active := cell;
					current_array.go_i_th (current_array.count - counter)
				end
			end;
			if i /= 0 then
				if current_array.before then
					index := 0
				elseif current_array.after then
					index := count + 1
				else
					index := index + i
				end
			end
		end;

	go_to (p: CURSOR) is
			-- Move cursor to position p
		local
			al_c: MULTAR_LIST_CURSOR [G]
		do
			al_c ?= p;
			check
				al_c /= void
			end;
			active := al_c.active;
			active.item.go_i_th (al_c.active_index);
			index := al_c.index
		end;

	search (v: like item) is
			-- Move cursor to first position (at or after current
			-- cursor position) where item and v are equal.
			-- (Reference or object equality,
			-- based on object_comparison.)
		local
			current_array: ARRAYED_LIST [G];
			old_index: INTEGER;
			cell: like active
		do
			cell := active;
			current_array := cell.item;
			old_index := current_array.index;
			if object_comparison then
				current_array.compare_objects
			else
				current_array.compare_references
			end;
			current_array.search (v);
			if current_array.after then
				cell := cell.right;
				index := index + current_array.count - old_index + 1
			else
				index := index + current_array.index - old_index
			end;
			from
			invariant
				index <= count + 1
			until
				notcurrent_array.after or else cell = void
			loop
				current_array := cell.item;
				if object_comparison then
					current_array.compare_objects
				else
					current_array.compare_references
				end;
				current_array.start;
				current_array.search (v);
				if current_array.after then
					cell := cell.right;
					index := index + current_array.count
				else
					index := index + current_array.index - 1
				end
			end;
			if cell /= void then
				active := cell
			else
				active := last_element
			end
		end;

feature -- Element change

	replace (v: like item) is
			-- Replace current item by v.
		do
			active.item.replace (v)
		end;

	extend (v: like item) is
			-- Add v to end.
		local
			cell: like first_element;
			current_array: ARRAYED_LIST [G]
		do
			current_array := last_element.item;
			if current_array.count = block_size then
				create current_array.make (block_size);
				last_element.put_right (new_cell (current_array));
				last_element := last_element.right
			end;
			current_array.extend (v);
			count := count + 1
		end;

	put_front (v: like item) is
			-- Add v at the beginning.
			-- Do not move cursor.
		local
			first_array: ARRAYED_LIST [G];
			pos: INTEGER
		do
			first_array := first_element.item;
			if active = first_element then
				pos := first_array.index
			else
				pos := -1
			end;
			first_array.start;
			put_left (v);
			if pos > 0 then
				first_array.go_i_th (pos + 1)
			elseif pos = 0 then
				first_array.go_i_th (0)
			end;
			if notbefore then
				index := index + 1
			end;
			count := count + 1
		end;

	put_left (v: like item) is
			-- Add v to the left of current position.
			-- Do not move cursor.
		local
			cell: like first_element;
			current_array, previous_array: ARRAYED_LIST [G];
			pos, cut: INTEGER
		do
			current_array := active_array;
			check
				empty implies after
			end;
			if after then
				extend (v)
			elseif active /= first_element and then notactive.left.item.full then
				active.left.item.extend (v)
			elseif current_array.count <= block_size then
				current_array.put_left (v)
			else
				pos := current_array.index;
				current_array.go_i_th (block_size // 2 + 1);
				cut := index;
				cell := new_cell (current_array.duplicate (count));
				cell.put_right (active.right);
				cell.put_left (active);
				if last_element = active then
					last_element := cell
				end;
				if pos < cut then
					current_array.go_i_th (pos);
					current_array.put_left (v)
				elseif pos = cut then
					current_array.extend (v);
					active := cell;
					active.item.start
				else
					active := cell;
					current_array := cell.item;
					current_array.go_i_th (pos - cut + 1);
					current_array.put_left (v)
				end
			end;
			index := index + 1;
			count := count + 1
		end;

	put_right (v: like item) is
			-- Add v to the left of current position.
			-- Do not move cursor.
		do
			forth;
			put_left (v);
			back;
			back
		end;

feature -- Removal

	wipe_out is
			-- Remove all items.
		do
			count := 0;
			index := 0;
			check
				first_element /= void
			end;
			first_element.item.wipe_out;
			first_element.forget_right;
			active := first_element;
			last_element := first_element
		end;

	remove is
			-- Remove current item
		local
			current_array: ARRAYED_LIST [G]
		do
			current_array := active.item;
			current_array.remove;
			if current_array.empty then
				if active = first_element then
					if active /= last_element then
						first_element := active.right;
						first_element.forget_left
					end
				elseif active = last_element then
					last_element := active.left;
					last_element.forget_right
				else
					active.right.put_left (active.left);
					active := active.right
				end
			elseif current_array.after then
				if active /= last_element then
					active := active.right;
					active.item.start
				end
			end;
			count := count - 1
		end;

	remove_left is
		do
			back;
			remove
		end;

	remove_right is
		do
			forth;
			remove;
			back
		end;

	prune_all (v: like item) is
		local
			cell: like active;
			array: ARRAYED_LIST [G]
		do
			from
				cell := first_element
			until
				cell = void
			loop
				array := cell.item;
				if object_comparison then
					array.compare_objects
				else
					array.compare_references
				end;
				count := count - array.count;
				array.prune_all (v);
				count := count + array.count;
				if array.empty then
					if cell = first_element then
						if cell /= last_element then
							first_element := cell.right;
							cell := first_element;
							cell.forget_left
						else
							cell := void
						end
					elseif cell = last_element then
						last_element := cell.left;
						last_element.forget_right;
						cell := void
					else
						cell.right.put_left (cell.left);
						cell := cell.right
					end
				else
					cell := cell.right
				end
			end;
			active := last_element;
			index := count + 1
		end;

feature -- Duplication

	duplicate (n: INTEGER): like Current is
			-- Copy of sub-list beginning at cursor position
			-- and having min (n, count - index + 1) items
		local
			pos: CURSOR;
			counter, i: INTEGER
		do
			from
				pos := cursor;
				Result := new_chain
			until
				(counter = n) or exhausted
			loop
				Result.extend (item);
				forth;
				counter := counter + 1
			end;
			go_to (pos)
		end;

feature {MULTI_ARRAY_LIST} -- Implementation

	new_chain: like Current is
			-- A newly created instance of the same type.
			-- This feature may be redefined in descendants so as to
			-- produce an adequately allocated and initialized object.
		do
			create Result.make (block_size)
		end;

	active_array: ARRAYED_LIST [G] is
			-- Array_sequence at cursor position
		require
			active_exists: active /= void;
			not_off: notoff
		do
			Result := active.item
		end;

	active: like first_element;
			-- Element at cursor position

feature {NONE} -- Implementation

	go_before is
			-- Move the cursor before first position.
		do
			active := void;
			index := 0
		ensure
			is_before: before
		end;

	new_cell (a: ARRAYED_LIST [G]): like first_element is
		do
			create Result;
			Result.put (a)
		end;

invariant

	writable_definition: writable = notoff;
	readable_definition: readable = notoff;
	extendible_definition: extendible;

end -- class MULTI_ARRAY_LIST