class SE_ARRAY2

(source code)

Description

SmartEiffel compatible 2 dimensional array

note
	description: "SmartEiffel compatible 2 dimensional array"

	author: "Finnian Reilly"
	copyright: "Copyright (c) 2001-2022 Finnian Reilly"
	contact: "finnian at eiffel hyphen loop dot com"

	license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
	date: "2022-11-15 19:56:03 GMT (Tuesday 15th November 2022)"
	revision: "4"

class
	SE_ARRAY2 [G]

inherit
	ARRAY [G]
		rename
			make as array_make,
			item as array_item,
			put as array_put,
			force as array_force,
			resize as array_resize,
			wipe_out as array_wipe_out,
			make_filled as array_make_filled
		export
			{NONE}
				array_make, array_force,
				array_resize, array_wipe_out, make_from_array,
				array_make_filled, make_from_special, make_from_cil,
				remove_head, remove_tail, keep_head, keep_tail,
				grow, conservative_resize, conservative_resize_with_default,
				automatic_grow
			{ARRAY2}
				array_put, array_item
			{ANY}
				copy, is_equal, area, to_c
		end

create
	make, make_filled, make_row_columns

feature {NONE} -- Initialization

	make (row_min, row_max, column_min, column_max: INTEGER)
		do
			make_row_columns (row_max - row_min + 1, column_max - column_min + 1)
			row_offset := row_min - 1
			column_offset := column_min - 1
		end

	make_row_columns (nb_rows, nb_columns: INTEGER)
			-- Create a two dimensional array which has `nb_rows'
			-- rows and `nb_columns' columns,
			-- with lower bounds starting at 1.
		obsolete
			"`make' is not void-safe statically. Use `make_filled' instead. [07-2010]"
		require
			nb_rows_non_negative: nb_rows >= 0
			nb_columns_non_negative: nb_columns >= 0
			both_zero_or_both_non_zero: not (nb_rows = 0 xor nb_columns = 0)
			has_default: (nb_rows * nb_columns) /= 0 implies ({G}).has_default
		do
			height := nb_rows
			width := nb_columns
			array_make (1, nb_rows * nb_columns)
		ensure
			new_count: count = height * width
		end

	make_filled (a_default_value: G; nb_rows, nb_columns: INTEGER)
			-- Create a two dimensional array which has `nb_rows'
			-- rows and `nb_columns' columns,
			-- with lower bounds starting at 1 filled with value `a_default_value'.
		require
			nb_rows_non_negative: nb_rows >= 0
			nb_columns_non_negative: nb_columns >= 0
			both_zero_or_both_non_zero: not (nb_rows = 0 xor nb_columns = 0)
		do
			height := nb_rows
			width := nb_columns
			array_make_filled (a_default_value, 1, nb_rows * nb_columns)
		ensure
			new_count: count = height * width
			filled: filled_with (a_default_value)
		end

	initialize (v: G)
			-- Make each entry have value `v'.
		do
			fill_with (v)
		end

feature -- Access

	item alias "[]" (row, column: INTEGER): G assign put
			-- Entry at coordinates (`row', `column')
		require
			valid_row: (1 <= row - row_offset) and (row - row_offset <= height)
			valid_column: (1 <= column - column_offset) and (column - column_offset <= width)
		do
			Result := array_item ((row - row_offset - 1) * width + (column - column_offset))
		end

feature -- Measurement

	height: INTEGER
			-- Number of rows

	width: INTEGER
			-- Number of columns

	column_offset: INTEGER

	row_offset: INTEGER

feature -- Element change

	put (v: like item; row, column: INTEGER)
			-- Assign item `v' at coordinates (`row', `column').
		require
			valid_row: 1 <= row - row_offset and row - row_offset <= height
			valid_column: 1 <= column - column_offset and column - column_offset <= width
		do
			array_put (v, (row - row_offset - 1) * width + (column - column_offset))
		end

	force (v: like item; row, column: INTEGER)
			-- Assign item `v' at coordinates (`row', `column').
			-- Resize if necessary.
		require
			row_large_enough: 1 <= row - row_offset
			column_large_enough: 1 <= column - column_offset
			has_default: ({G}).has_default
		do
			resize_with_default (({G}).default, row - row_offset, column - column_offset)
			put (v, row - row_offset, column - column_offset)
		end

feature -- Removal

	wipe_out
			-- Remove all items.
		do
			height := 0
			width := 0
			make_empty
		end

feature -- Resizing

	resize (nb_rows, nb_columns: INTEGER)
			-- Rearrange array so that it can accommodate
			-- `nb_rows' rows and `nb_columns' columns,
			-- without losing any previously
			-- entered items, nor changing their coordinates;
			-- do nothing if new values are smaller.
		obsolete
			"`resize' is not void-safe statically. Use `resize_with_default' instead. [07-2010]"
		require
			valid_row: nb_rows >= 1
			valid_column: nb_columns >= 1
			has_default: ({G}).has_default
		do
			if (nb_columns > width) or (nb_rows > height) then
				resize_with_default (({G}).default, nb_rows, nb_columns)
			end
		ensure
			no_smaller_height: height >= old height
			no_smaller_width: width >= old width
		end

	resize_with_default (a_default: G; nb_rows, nb_columns: INTEGER)
			-- Rearrange array so that it can accommodate
			-- `nb_rows' rows and `nb_columns' columns,
			-- without losing any previously
			-- entered items, nor changing their coordinates;
			-- do nothing if new values are smaller.
		require
			valid_row: nb_rows >= 1
			valid_column: nb_columns >= 1
		local
			i: INTEGER
			new_height: like height
			previous_width: like width
		do
			if nb_columns > width then
					-- Both rows and columns have been added. We resize `area' to the requested size.
				new_height := nb_rows.max (height)
				previous_width := width
				conservative_resize_with_default (a_default, 1, new_height * nb_columns)

					-- Go through all rows and shift items at their right place in the resized `area'.
				from
					i := height
				until
					i = 0
				loop
					area.move_data ((i - 1) * previous_width, (i - 1) * nb_columns, previous_width)
					area.fill_with (a_default, (i - 1) * nb_columns + previous_width, i * nb_columns - 1)
					i := i - 1
				end
				width := nb_columns
				height := new_height
			elseif nb_rows > height then
					-- Only a new row was added, we simply extend `area' by that many new rows.
				conservative_resize_with_default (a_default, 1, nb_rows * width)
				height := nb_rows
			end
		ensure
			no_smaller_height: height >= old height
			no_smaller_width: width >= old width
		end

invariant
	items_number: count = width * height

end