This subroutine inverts a list of segment ranges representing Unicode characters. It compute the complement of the given ranges and modifies the list accordingly.
The complement is computed only over the domain [UTF8_CODE_MIN, UTF8_CODE_MAX];
code points below UTF8_CODE_MIN (control characters) are deliberately excluded
from every negated class (see README). Segments in list that lie below UTF8_CODE_MIN
(e.g. SEG_TAB, SEG_LF, SEG_FF, SEG_CR) must therefore never pull current_min
back below the domain floor, or spurious low-valued gap segments occur.
Clamping with max() enforces that invariant.
With that invariant in place, a single pass over an upper-bound sized buffer
suffices: the complement of n disjoint sorted segments has at most n+1 gaps
(before, between, and after them). A prior version used two separate loops (one
to count, one to fill) that had to strictly control the size and order; they produced
a corrupted list due to a minor mistake, so this was collapsed into one loop to
remove that failure mode entirely.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(segment_t), | intent(inout), | allocatable | :: | list(:) |
pure subroutine invert_segment_list(list) implicit none type(segment_t), intent(inout), allocatable :: list(:) type(segment_t), allocatable :: new_list(:) integer :: i, n, count integer :: current_min if (.not. allocated(list)) return ! sort and merge segments call sort_segment_by_min(list) call merge_segments(list) !! The complement is computed only over the domain [UTF8_CODE_MIN, UTF8_CODE_MAX]; !! code points below UTF8_CODE_MIN (control characters) are deliberately excluded !! from every negated class (see README). Segments in `list` that lie below UTF8_CODE_MIN !! (e.g. SEG_TAB, SEG_LF, SEG_FF, SEG_CR) must therefore never pull `current_min` !! back below the domain floor, or spurious low-valued gap segments occur. !! Clamping with max() enforces that invariant. !! !! With that invariant in place, a single pass over an upper-bound sized buffer !! suffices: the complement of `n` disjoint sorted segments has at most `n+1` gaps !! (before, between, and after them). A prior version used two separate loops (one !! to count, one to fill) that had to strictly control the size and order; they produced !! a corrupted list due to a minor mistake, so this was collapsed into one loop to !! remove that failure mode entirely. n = size(list, dim=1) allocate(new_list(n+1)) count = 0 current_min = UTF8_CODE_MIN do i = 1, n if (current_min < list(i)%min) then count = count + 1 new_list(count)%min = current_min new_list(count)%max = list(i)%min - 1 end if current_min = max(current_min, list(i)%max + 1) end do if (current_min <= UTF8_CODE_MAX) then count = count + 1 new_list(count)%min = current_min new_list(count)%max = UTF8_CODE_MAX end if ! Deallocate old list and reassign the trimmed new list deallocate(list) list = new_list(1:count) end subroutine invert_segment_list