--------------------------------------------------------------------
-- Class: MATRIX_NODE
--------------------------------------------------------------------
-- Original Author: Justin Finnerty
-- Original Date:
-- Current Date: $Date: 2005/09/02 07:25:59 $
-- Current Author: $Author: finnerty $
-- Revision: $Revision: 2.1 $
-- Log: $Log: matrix_node.e,v $
-- Log: Revision 2.1  2005/09/02 07:25:59  finnerty
-- Log: Modified to work with VisualEiffel
-- Log:
-- Log: Revision 2.0  2005/08/26 23:19:46  finnerty
-- Log: == alpha version ==
-- Log:
-- Log: Revision 1.1  2005/08/04 09:51:25  finnerty
-- Log: Bi-linkable type that is linkable in two directions (termed vertical and horizontal). This enforces 1-1 arity in both directions.
-- Log:
--------------------------------------------------------------------
indexing
	-----------------------------------------------------------------
	-- Class categorising information
	-----------------------------------------------------------------
	namespace: "candidate"
	cluster: "collection"
	description: "This class represents a matrix_node object. The %
	%linkable notions are up, down and left, right."
class MATRIX_NODE
	-----------------------------------------------------------------
	-- Description of class
	-- -----------------------------------------------------------------
	-- This class represents a matrix_node object. The three linkable
	-- notions are parent, down and sibling.

inherit
	QUADLINK_NODE

feature {ANY}
	-----------------------------------------------------------------
	-- Attributes
	-----------------------------------------------------------------
	up, down, left, right: MATRIX_NODE

feature {ANY}
	-----------------------------------------------------------------
	-- Attribute queries
	-----------------------------------------------------------------
	has_down: BOOLEAN is
			-- indicate whether the current object has any down link
		do
			Result := (down /= Void)
		ensure
			Check_constness: Current.is_equal(old Current)
		end

	has_left: BOOLEAN is
			-- indicate whether the object has any sibling objects. A 
			-- sibling object is one that has the same up.
		do
			Result := (left /= Void)
		ensure
			Check_constness: Current.is_equal(old Current)
		end

	has_right: BOOLEAN is
			-- indicate whether the object has any sibling objects. A 
			-- sibling object is one that has the same up.
		do
			Result := (right /= Void)
		ensure
			Check_constness: Current.is_equal(old Current)
		end

	has_up: BOOLEAN is
			-- indicate whether the object has any ups. This will
			-- only be false for the root element.
		do
			Result := (up /= Void)
		ensure
			Check_constness: Current.is_equal(old Current)
		end

feature {MATRIX_NODE}
	----------------------------------------------------------------------
	-- Procedures to change links. These features inserts one node into
	-- a list in the given direction. The Current object is unlinked
	-- from its current connections in the given direction using
	-- `unlink_horizontal' or `unlink_vertical'. Links in the
	-- orthogonal direction are not changed.
	
	-- eg A-B-C and X-Y-Z with Y.insert_right(B) would give A-C and
	-- X-Y-B-Z. Y.insert_left(B) would give A-C and X-B-Y-Z.
	----------------------------------------------------------------------
	insert_down(a_node: MATRIX_NODE) is
			-- `unlink_vertical' `a_node' from its current position then
			-- insert it into our down link list.
		require
			a_node /= Void -- a_node must not be void
		do
			a_node.unlink_vertical
			if has_down then
				a_node.set_down( down )
			end
			set_down( a_node )
		ensure
			down = a_node
			down.down = old down
		end
	
	insert_left(a_node: MATRIX_NODE) is
			-- `unlink_horizontal' `a_node' from its current position then
			-- insert it into our left link list.
		require
			a_node /= Void -- a_node cannot be void and
		do
			a_node.unlink_horizontal
			if has_left then
				a_node.set_left( left )
			end
			set_left( a_node )
		ensure
			left = a_node
			left.left = old left
		end

	insert_right(a_node: MATRIX_NODE) is
			-- `unlink_horizontal' `a_node' from its current position then
			-- insert it into our right link list.
		require
			a_node /= Void -- a_node cannot be void and
		do
			a_node.unlink_horizontal
			if has_right then
				a_node.set_right( right )
			end
			set_right( a_node )
		ensure
			right = a_node
			right.right = old right
		end

	insert_up(a_node: MATRIX_NODE) is
			-- `unlink_horizontal' `a_node' from its current position then
			-- insert it into our up link list.
		require
			a_node /= Void -- a_node must not be void
		do
			a_node.unlink_vertical
			if has_up then
				a_node.set_up( up )
			end
			set_up( a_node )
		ensure
			up = a_node
			up.up = old up
		end

feature {MATRIX_NODE}
	-----------------------------------------------------------------
	-- Procedures to change links. These methods replace one object 
	-- with another in one direction.
	-- -----------------------------------------------------------------
	set_down(a_node: MATRIX_NODE) is
			-- Set/change our down to `a_node' and change `a_node''s up to 
			-- this object. The 
		require
			a_node /= Void -- a_node must not be void
		do
			a_node.unlink_up
			unlink_down
			down := a_node
			a_node.relink_up( Current )
		ensure
			down = a_node
		end
	
	set_left(a_node: MATRIX_NODE) is
			-- Set/change our left to `a_node' and change `a_node''s right
			-- to this object.
		require
			a_node /= Void -- a_node cannot be void and
		do
			a_node.unlink_right
			unlink_left
			left := a_node
			a_node.relink_right( Current )
		ensure
			left = a_node
		end

	set_right(a_node: MATRIX_NODE) is
			-- Set/change our right to `a_node' and change `a_node''s left
			-- to this object.
		require
			a_node /= Void -- a_node cannot be void and
		do
			a_node.unlink_left
			unlink_right
			right := a_node
			a_node.relink_left( Current )
		ensure
			right = a_node
		end

	set_up(a_node: MATRIX_NODE) is
			-- Set/change our up to `a_node'.
		require
			a_node /= Void -- a_node must not be void
		do
			a_node.unlink_down
			unlink_up
			up := a_node
			a_node.relink_down( Current )
		ensure
			up = a_node
		end

feature {MATRIX_NODE}
	-----------------------------------------------------------------
	-- Procedures to rebuild a link
	-- -----------------------------------------------------------------
	relink_down(a_node: MATRIX_NODE) is
			-- Set/change our down to `a_node' and change `a_node''s up to 
			-- this object. Argument may be `Void'.
		do
			down := a_node
		ensure
			down = a_node
		end
	
	relink_left(a_node: MATRIX_NODE) is
			-- Set/change our left to `a_node' and change `a_node''s right
			-- to this object. Argument may be `Void'.
		do
			left := a_node
		ensure
			left = a_node
		end

	relink_right(a_node: MATRIX_NODE) is
			-- Set/change our right to `a_node' and change `a_node''s left
			-- to this object. Argument may be `Void'.
		do
			right := a_node
		ensure
			right = a_node
		end

	relink_up(a_node: MATRIX_NODE) is
			-- Set/change our up to `a_node'. Argument may be `Void'.
		do
			up := a_node
		ensure
			up = a_node
			end
feature {MATRIX_NODE}
	-----------------------------------------------------------------
	-- Procedures to break links.

	-- The feature of this is if X-Y-Z then Y.unlink_left gives X-0 and
	-- 0-Y-Z, where 0 <=> Void. Y.unlink_right gives X-Y-0 and 0-Z.
	
	-- -----------------------------------------------------------------
	unlink_all is
			-- Set all links to Void
		do
			unlink_down
			unlink_left
			unlink_right
			unlink_up
		ensure
			down = Void
			left = Void
			right = Void
			up = Void
		end

	unlink_down is
			-- Set our down and our old down.up to Void
		do
			if has_down then
				down.relink_up( Void )
				down := Void
			end
		ensure
			down = Void
		end

	unlink_horizontal is
			-- This removes the current node from a list in the
			-- left-right direction. At the end of this method the old
			-- left and right objects will point to each other
		do
			if has_left then
				left.set_right( right )
				left := Void
				right := Void
			else
				unlink_right
			end
		ensure
			left = Void
			right = Void
		end

	unlink_left is
			-- Set/change our up (and up.down) to Void
		do
			if has_left then
				left.relink_right( Void )
				left := Void
			end
		ensure
			left = Void
		end

	unlink_right is
			-- Set our sibling to Void
		do
			if has_right then
				right.relink_left( Void )
				right := Void
			end
		ensure
			right = Void
		end

	unlink_up is
			-- Set/change our up to Void
		do
			if has_up then
				up.relink_down( Void )
				up := Void
			end
		ensure
			up = Void
		end

	unlink_vertical is
			-- This removes the current node from a list in the up-down
			-- direction. At the end of this method the old down and up
			-- objects will point to each other
		do
			if has_up then
				up.set_down( down )
				up := Void
				down := Void
			else
				unlink_down
			end
		ensure
			up = Void
			down = Void
		end

invariant
	-----------------------------------------------------------------
	-- Assertions about what is always true
	-----------------------------------------------------------------
	No_self_reference: Current /= up
	No_self_reference: Current /= right
	No_self_reference: Current /= down
	No_self_reference: Current /= left
	-- The only time down.up = Void (etc) should occur is during
	-- relinking.
	-- No_lost_link: down /= Void implies (down.up = Current or down.up = Void)
	-- No_lost_link: left /= Void implies (left.right = Current or left.right = Void)
	-- No_lost_link: right /= Void implies (right.left = Current or right.left = Void)
	-- No_lost_link: up /= Void implies (up.down = Current or up.down = Void)
	No_lost_link: down /= Void implies down.up = Current
	No_lost_link: left /= Void implies left.right = Current
	No_lost_link: right /= Void implies right.left = Current
	No_lost_link: up /= Void implies up.down = Current
end -- class MATRIX_NODE
