note
	description: "Sequential, one-way linked lists"
	library: "Free implementation of ELKS library"
	legal: "See notice at end of class."
	status: "See notice at end of class."
	names: linked_list, sequence
	representation: linked
	access: index, cursor, membership
	contents: generic
	date: "$Date: 2018-11-14 15:15:17 +0000 (Wed, 14 Nov 2018) $"
	revision: "$Revision: 102463 $"

class 
	LINKED_LIST [G]

inherit
	DYNAMIC_LIST [G]
		redefine
			go_i_th,
			put_left,
			move,
			wipe_out,
			isfirst,
			islast,
			is_inserted,
			first,
			last,
			finish,
			merge_left,
			merge_right,
			readable,
			start,
			before,
			after,
			off,
			copy,
			new_cursor
		end

create 
	make,
	make_from_iterable

feature {NONE} -- Initialization

	make
			-- Create an empty list.
		do
			before := True
		ensure
			is_before: before
		end

	make_from_iterable (other: ITERABLE [G])
			-- Create a linked list with all items obtained from other.
		do
			make
			across
				other as o
			loop
				extend (o.item)
			end
		end
	
feature -- Access

	item: G
			-- Current item
		do
			check
					attached active as a
			then
				Result := a.item
			end
		end

	first: like item
			-- Item at first position
		do
			check
					attached first_element as f
			then
				Result := f.item
			end
		end

	last: like item
			-- Item at last position
		do
			check
					attached last_element as l
			then
				Result := l.item
			end
		end

	index: INTEGER_32
			-- Index of current position
		local
			l_active, l_active_iterator: like active
		do
			if after then
				Result := count + 1
			elseif not before then
				from
					Result := 1
					l_active := active
					l_active_iterator := first_element
				until
					l_active_iterator = l_active or else l_active_iterator = Void
				loop
					l_active_iterator := l_active_iterator.right
					Result := Result + 1
				end
			end
		end

	cursor: LINKED_LIST_CURSOR [G]
			-- Current cursor position
		do
			create Result.make (active, after, before)
		end
	
feature -- Access: Cursor

	new_cursor: LINKED_LIST_ITERATION_CURSOR [G]
			-- Fresh cursor associated with current structure
		do
			create Result.make (Current);
			Result.start
		end
	
feature {LINKED_LIST, LINKED_LIST_ITERATION_CURSOR} -- Access

	first_element: detachable like new_cell
			-- Head of list
	
feature -- Measurement

	count: INTEGER_32
			-- Number of items
	
feature -- Status report

	readable: BOOLEAN
			-- Is there a current item that may be read?
		do
			Result := not off
		end

	after: BOOLEAN
			-- Is there no valid cursor position to the right of cursor?

	before: BOOLEAN
			-- Is there no valid cursor position to the left of cursor?

	off: BOOLEAN
			-- Is there no current item?
		do
			Result := after or before
		end

	isfirst: BOOLEAN
			-- Is cursor at first position?
		do
			Result := not after and not before and (active = first_element)
		end

	islast: BOOLEAN
			-- Is cursor at last position?
		local
			a: like active
		do
			if not after and then not before then
				a := active
				Result := a /= Void and then a.right = Void
			end
		end

	valid_cursor (p: CURSOR): BOOLEAN
			-- Can the cursor be moved to position p?
		local
			temp, sought: like first_element
		do
			if attached {like cursor} p as ll_c then
				from
					temp := first_element
					sought := ll_c.active
					Result := ll_c.after or else ll_c.before
				until
					Result or else temp = Void
				loop
					Result := temp = sought
					temp := temp.right
				end
			end
		end

	Full: BOOLEAN = False
			-- Is structured filled to capacity? (Answer: no.)

	is_inserted (v: G): BOOLEAN
			-- Has v been inserted at the end by the most recent put or
			-- extend?
		local
			l: like last_element
		do
			l := last_element
			if l /= Void then
				check
					put_constraint: (v /= l.item) implies not off
				end
				Result := (v = l.item) or else (v = item)
			end
		end
	
feature -- Cursor movement

	start
			-- Move cursor to first position.
		do
			if first_element /= Void then
				active := first_element
				after := False
			else
				after := True
			end
			before := False
		ensure then
			empty_convention: is_empty implies after
		end

	finish
			-- Move cursor to last position.
			-- (Go before if empty)
		local
			p: detachable like new_cell
		do
			from
				p := active
			until
				p = Void
			loop
				active := p
				p := p.right
			end
			after := False
			before := active = Void
		ensure then
			empty_convention: is_empty implies before
		end

	forth
			-- Move cursor to next position.
		local
			a: like active
		do
			if before then
				before := False
				if is_empty then
					after := True
				end
			else
				a := active
				if a /= Void and then a.right /= Void then
					active := a.right
				else
					after := True
				end
			end
		end

	back
			-- Move to previous item.
		do
			if is_empty then
				before := True
				after := False
			elseif after then
				after := False
			elseif isfirst then
				before := True
			else
				active := previous
			end
		end

	move (i: INTEGER_32)
			-- Move cursor i positions. The cursor
			-- may end up off if the offset is too big.
		local
			counter, new_index: INTEGER_32
			p: like first_element
		do
			if i > 0 then
				if before then
					before := False
					counter := 1
				end
				from
					p := active
				until
					(counter = i) or else (p = Void)
				loop
					active := p
					p := p.right
					counter := counter + 1
				end
				if p = Void then
					after := True
				else
					active := p
				end
			elseif i < 0 then
				new_index := index + i
				before := True
				after := False
				active := first_element
				if new_index > 0 then
					move (new_index)
				end
			end
		ensure then
			moved_if_inbounds: ((old index + i) >= 0 and (old index + i) <= (count + 1)) implies index = (old index + i)
			before_set: (old index + i) <= 0 implies before
			after_set: (old index + i) >= (count + 1) implies after
		end

	go_i_th (i: INTEGER_32)
			-- Move cursor to i-th position.
		do
			if i = 0 then
				before := True
				after := False
				active := first_element
			elseif i = count + 1 then
				before := False
				after := True
				active := last_element
			else
				move (i - index)
			end
		end

	go_to (p: CURSOR)
			-- Move cursor to position p.
		do
			if attached {like cursor} p as ll_c then
				after := ll_c.after
				before := ll_c.before
				if before then
					active := first_element
				elseif after then
					active := last_element
				else
					active := ll_c.active
				end
			else
				check
					correct_cursor_type: False
				end
			end
		end
	
feature -- Element change

	put_front (v: like item)
			-- Add v to beginning.
			-- Do not move cursor.
		local
			p: like new_cell
		do
			p := new_cell (v);
			p.put_right (first_element)
			first_element := p
			if before or is_empty then
				active := p
			end
			count := count + 1
		end

	extend (v: like item)
			-- Add v to end.
			-- Do not move cursor.
		local
			p: like first_element
			l: like last_element
		do
			p := new_cell (v)
			if is_empty then
				first_element := p
				active := p
			else
				l := last_element
				if l /= Void then
					l.put_right (p)
					if after then
						active := p
					end
				end
			end
			count := count + 1
		end

	put_left (v: like item)
			-- Add v to the left of cursor position.
			-- Do not move cursor.
		local
			p: like new_cell
			a: like active
		do
			a := active
			if a = Void then
				put_front (v)
			elseif after then
				back
				put_right (v)
				move (2)
			else
				p := new_cell (a.item);
				p.put_right (a.right);
				a.put (v);
				a.put_right (p)
				active := p
				count := count + 1
			end
		ensure then
			previous_exists: previous /= Void
			item_inserted: attached previous as q and then q.item = v
		end

	put_right (v: like item)
			-- Add v to the right of cursor position.
			-- Do not move cursor.
		local
			p: like new_cell
			a: like active
		do
			p := new_cell (v)
			check
					is_empty implies before
			end
			if before then
				p.put_right (first_element)
				first_element := p
				active := p
			else
				a := active
				if a /= Void then
					p.put_right (a.right);
					a.put_right (p)
				end
			end
			count := count + 1
		ensure then
			next_exists: next /= Void
			item_inserted: not old before implies (attached next as n and then n.item = v)
			item_inserted_before: old before implies (attached active as c and then c.item = v)
		end

	replace (v: like item)
			-- Replace current item by v.
		local
			a: like active
		do
			a := active
			if a /= Void then
				a.put (v)
			end
		end

	merge_left (other: like Current)
			-- Merge other into current structure before cursor
			-- position. Do not move cursor. Empty other.
		local
			other_first_element: like first_element
			other_count: INTEGER_32
			p: like first_element
		do
			if attached other.last_element as other_last_element then
				other_first_element := other.first_element
				other_count := other.count;
				other.wipe_out
				check
						other_first_element /= Void
				end
				if is_empty then
					first_element := other_first_element
					active := first_element
				elseif isfirst then
					other_last_element.put_right (first_element)
					first_element := other_first_element
				else
					p := previous
					if p /= Void then
						p.put_right (other_first_element)
					end;
					other_last_element.put_right (active)
				end
				count := count + other_count
			end
		end

	merge_right (other: like Current)
			-- Merge other into current structure after cursor
			-- position. Do not move cursor. Empty other.
		local
			other_first_element: like first_element
			other_count: INTEGER_32
			a: like active
		do
			if attached other.last_element as other_last_element then
				other_first_element := other.first_element
				other_count := other.count;
				other.wipe_out
				check
						other_first_element /= Void
				end
				a := active
				if a = Void then
					first_element := other_first_element
					active := first_element
				else
					if not islast then
						other_last_element.put_right (a.right)
					end;
					a.put_right (other_first_element)
				end
				count := count + other_count
			end
		end
	
feature -- Removal

	remove
			-- Remove current item.
			-- Move cursor to right neighbor
			-- (or after if no right neighbor).
		local
			succ: like first_element
			removed: like active
			a: like active
			p: like previous
		do
			removed := active
			if removed /= Void then
				if isfirst then
					first_element := removed.right;
					removed.forget_right
					active := first_element
					if count = 1 then
						check
							no_active: active = Void
						end
						after := True
					end
				elseif islast then
					active := previous
					a := active
					if a /= Void then
						a.forget_right
					end
					after := True
				else
					succ := removed.right
					p := previous
					if p /= Void then
						p.put_right (succ)
					end;
					removed.forget_right
					active := succ
				end
				count := count - 1
				cleanup_after_remove (removed)
			end
		end

	remove_left
			-- Remove item to the left of cursor position.
			-- Do not move cursor.
		do
			move (-2)
			remove_right
			forth
		end

	remove_right
			-- Remove item to the right of cursor position.
			-- Do not move cursor.
		local
			removed: like first_element
			f: like first_element
			a: like active
			succ: like active
		do
			if before then
				f := first_element
				if f /= Void then
					removed := f
					first_element := f.right
					a := active
					if a /= Void then
						a.forget_right
					end
					active := first_element
				end
			else
				a := active
				if a /= Void then
					succ := a.right
					if succ /= Void then
						removed := succ;
						a.put_right (succ.right);
						succ.forget_right
					end
				end
			end
			count := count - 1
			cleanup_after_remove (removed)
		end

	wipe_out
			-- Remove all items.
		do
			internal_wipe_out
		end
	
feature -- Duplication

	copy (other: like Current)
			-- Update current object using fields of object attached
			-- to other, so as to yield equal objects.
		local
			cur: LINKED_LIST_CURSOR [G]
		do
			if other /= Current then
				standard_copy (other)
				if not other.is_empty then
					internal_wipe_out
					cur := other.cursor
					from
						other.start
					until
						other.off
					loop
						extend (other.item)
						finish;
						other.forth
					end
					if cur /= Void then
						other.go_to (cur)
					end
				end
			end
		end
	
feature {LINKED_LIST}{DYNAMIC_CHAIN} -- Implementation

	new_chain: like Current
		obsolete "Use explicit creation instead. See also explanations for `duplicate`. [2018-11-30]"
			-- 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
		end

	new_cell (v: like item): LINKABLE [like item]
			-- A newly created instance of the same type as first_element.
			-- This feature may be redefined in descendants so as to
			-- produce an adequately allocated and initialized object.
		do
			create Result.put (v)
		ensure
			result_exists: Result /= Void
		end

	previous: like first_element
			-- Element left of cursor
		local
			p: like first_element
		do
			if after then
				Result := active
			elseif not (isfirst or before) then
				from
					p := first_element
				until
					p = Void or else p.right = active
				loop
					p := p.right
				end
				Result := p
			end
		end

	next: like first_element
			-- Element right of cursor
		local
			a: like active
		do
			if before then
				Result := active
			else
				a := active
				if a /= Void then
					Result := a.right
				end
			end
		end

	active: like first_element
			-- Element at cursor position

	last_element: like first_element
			-- Tail of list
		local
			p: like first_element
		do
			from
				p := active
			until
				p = Void
			loop
				Result := p
				p := p.right
			end
		end

	cleanup_after_remove (v: like first_element)
			-- Clean-up a just removed cell.
		require
			non_void_cell: v /= Void
		do
		end
	
feature {NONE} -- Implementation

	frozen internal_wipe_out
			-- Remove all items.
		require
				prunable
		do
			active := Void
			first_element := Void
			before := True
			after := False
			count := 0
		ensure
			wiped_out: is_empty
			is_before: before
		end
	
invariant
	prunable: prunable
	empty_constraint: is_empty implies ((first_element = Void) and (active = Void))
	not_void_unless_empty: (active = Void) implies is_empty
	before_constraint: before implies (active = first_element)
	after_constraint: after implies (active = last_element)

note
	copyright: "Copyright (c) 1984-2018, 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 LINKED_LIST

Generated by ISE EiffelStudio