class EL_ZSTRING_INTERVALS

(source code)

description

Traverseable intervals for ZSTRING including both encoded and unencoded

note
	description: "Traverseable intervals for ${ZSTRING} including both encoded and unencoded"

	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: "2024-01-20 19:18:25 GMT (Saturday 20th January 2024)"
	revision: "8"

class
	EL_ZSTRING_INTERVALS

inherit
	EL_SEQUENTIAL_INTERVALS
		rename
			make as make_sized
		redefine
			copy
		end

	EL_INTERVAL_CONSTANTS
		export
			{NONE} all
		end

	EL_ZCODE_CONVERSION
		export
			{NONE} all
		undefine
			copy, is_equal, out
		end

	EL_SHARED_IMMUTABLE_32_MANAGER

create
	make

feature {NONE} -- Initialization

	make
		do
			make_sized (10)
		end

feature -- Access

	first_index_of_encoded (encoded_area: SPECIAL [CHARACTER_8]): INTEGER
		-- zero based index of first interval that covers encoded characters in `encoded_area'
		do
			Result := (encoded_area [first_lower - 1] = Substitute).to_integer
		end

feature -- Element change

	set (a_unencoded_area: like unencoded_area; lower_A, upper_A: INTEGER)
		local
			i, lower_B, upper_B, overlap_status: INTEGER; l_unencoded: like unencoded_area
			searching, done: BOOLEAN; ir: EL_INTERVAL_ROUTINES; l_area: like area
		do
			wipe_out
			unencoded_area := a_unencoded_area; l_unencoded := a_unencoded_area
			if l_unencoded.count > 0 then
				l_area := area_v2
				searching := True
				from i := 0 until done or else i = l_unencoded.count loop
	--				[lower_A, upper_A] is A interval
					lower_B := l_unencoded [i].code; upper_B := l_unencoded [i + 1].code -- is B interval
					overlap_status := ir.overlap_status (lower_A, upper_A, lower_B, upper_B)
					if searching and then ir.is_overlapping (overlap_status) then
						searching := False
					end
					if not searching then
--						ensure enough space for at least 3 more additions
						if l_area.count + 6 > l_area.capacity then
							l_area := l_area.aliased_resized_area (l_area.count + 6 + additional_space)
							area_v2 := l_area
						end
						if is_empty then
							if lower_A < lower_B then
								l_area.extend (lower_A); l_area.extend (lower_B - 1)
							end

						elseif ir.is_overlapping (overlap_status) and then (lower_B - last_upper) >= 1 then
							l_area.extend (last_upper + 1); l_area.extend (lower_B - 1)
						end
						inspect overlap_status
							when A_overlaps_B_left then
								l_area.extend (lower_B); l_area.extend (upper_A)
							when A_overlaps_B_right then
								l_area.extend (lower_A); l_area.extend (upper_B)
							when A_contains_B then
								l_area.extend (lower_B); l_area.extend (upper_B)
							when B_contains_A then
								l_area.extend (lower_A); l_area.extend (upper_A)
						else
							done := True
						end
					end
					i := i + upper_B - lower_B + 3
				end
				if is_empty then
					l_area.extend (lower_A); l_area.extend (upper_A)
				elseif upper_A > last_upper then
					l_area.extend (last_upper + 1); l_area.extend (upper_A)
				end
			else
				extend (lower_A, upper_A)
			end
		end

feature -- Status query

	same_encoded_characters (
		encoded_area, other_encoded_area: SPECIAL [CHARACTER_8]; other: EL_ZSTRING_INTERVALS
	): BOOLEAN
		require
			not_empty: not is_empty
			similar_to_other: similar_to (other)
			valid_encoded_area: encoded_area.valid_index (last_upper -  1)
			valid_other_encoded_area: other_encoded_area.valid_index (other.last_upper -  1)
		local
			i, j, intervals_count, lower, upper, start_index, other_start_index: INTEGER
			l_area, o_area: like area_v2
		do
			l_area := area; o_area := other.area; intervals_count := count

			start_index := first_index_of_encoded (encoded_area)
			other_start_index := other.first_index_of_encoded (other_encoded_area)

			Result := start_index = other_start_index
			from i := start_index until not Result or else i >= intervals_count loop
				j := i * 2; lower := l_area [j]; upper := l_area [j + 1]
				Result := encoded_area.same_items (other_encoded_area, o_area [i] - 1, lower - 1, upper - lower + 1)
				i := i + 4 -- every second one is encoded
			end
		end

	similar_to (other: EL_ZSTRING_INTERVALS): BOOLEAN
		-- `True' if `Current' has the same count and size of intervals
		local
			l_area, o_area: like area; i, lower, upper, o_lower, o_upper: INTEGER
			l_unencoded_area, o_unencoded_area: like unencoded_area
		do
			l_area := area_v2; o_area := other.area_v2
			Result := l_area.count = o_area.count
			l_unencoded_area := unencoded_area; o_unencoded_area := other.unencoded_area
			from i := 0 until not Result or i = l_area.count loop
				lower := (l_area [i] |>> 32).to_integer_32
				upper := l_area [i].to_integer_32
				o_lower := (o_area [i] |>> 32).to_integer_32
				o_upper := o_area [i].to_integer_32
				Result := upper - lower = o_upper - o_lower
				i := i + 1
			end
		ensure
			same_intervals: Result implies count = other.count
					and then across 1 |..| count as n all i_th_count (n.item) = other.i_th_count (n.item) end
		end

feature -- Duplication

	copy (other: like Current)
		do
			Precursor {EL_SEQUENTIAL_INTERVALS} (other)
			unencoded_area := other.unencoded_area.twin
		end

feature -- Factory

	new_unencoded_sources (lower_A, upper_A: INTEGER): like area
		-- array of source and count indexes
		local
			i, l_index, lower_B, upper_B, l_count, overlap_status: INTEGER; l_unencoded: like unencoded_area
			searching, done: BOOLEAN; ir: EL_INTERVAL_ROUTINES
		do
			create Result.make_empty (count // 2 + 1)
			l_unencoded := unencoded_area
			searching := True
			from i := 0 until done or else i = l_unencoded.count loop
--				[lower_A, upper_A] is A interval
				lower_B := l_unencoded [i].code; upper_B := l_unencoded [i + 1].code -- is B interval
				overlap_status := ir.overlap_status (lower_A, upper_A, lower_B, upper_B)
				if searching then
					searching := not ir.is_overlapping (overlap_status)
				end
				if not searching then
					l_count := 0
					inspect overlap_status
						when A_overlaps_B_left then
							l_count := upper_A - lower_B + 1
							l_index := 0

						when A_overlaps_B_right then
							l_count := upper_B - lower_A + 1
							l_index := lower_A - lower_B

						when A_contains_B then
							l_count := upper_B - lower_B + 1
							l_index := 0

						when B_contains_A then
							l_count := upper_A - lower_A + 1
							l_index := lower_A - lower_B
					else
						done := True
					end
					if l_count.to_boolean then
						Result.extend (i + 2 + l_index)
						Result.extend (l_count)
					end
				end
				i := i + upper_B - lower_B + 3
			end
		end

feature {EL_ZSTRING_INTERVALS} -- Internal attributes

	unencoded_area: SPECIAL [CHARACTER_32]

end