-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathunicode_ranges.py
More file actions
97 lines (79 loc) · 3.47 KB
/
Copy pathunicode_ranges.py
File metadata and controls
97 lines (79 loc) · 3.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
from typing import List, Union, Tuple, Iterable
class UnicodeRanges:
def __init__(self, ranges: List[Union[int, Tuple[int, int]]] = None):
# Initialize the ranges as an empty list of tuples (start, end)
if ranges is None:
ranges: List[Tuple[int, int]] = []
# Convert any single integers to tuples
ranges = [(r, r) if isinstance(r, int) else r for r in ranges]
self._ranges = []
self.add_ranges(ranges)
def _merge_in_place(self, start: int, end: int, insert_index: int):
"""Merge the new range in place starting at insert_index."""
n = len(self._ranges)
i = insert_index
# Expand the start and end if overlapping or contiguous ranges are found
while i < n and self._ranges[i][0] <= end + 1:
start = min(start, self._ranges[i][0])
end = max(end, self._ranges[i][1])
i += 1
# Replace the current range and delete any overlapped ranges
self._ranges[insert_index: i] = [(start, end)]
def add_ranges(self, ranges: Iterable[Tuple[int, int]]):
for start, end in ranges:
self.add_range(start, end)
def add_range(self, start: int, end: int):
"""Add a new range (start, end) while maintaining sorted order and merging if necessary."""
if start > end:
start, end = end, start
n = len(self._ranges)
i = 0
# Find the insertion point
while i < n and self._ranges[i][1] < start - 1:
i += 1
# Merge the new range into the correct position
self._merge_in_place(start, end, i)
def add_ordinal(self, ordinal: int):
"""Add a single ordinal and merge it with existing ranges if necessary."""
self.add_range(ordinal, ordinal)
def add_ordinals(self, ordinals: Iterable[int]):
"""Add a list of ordinals and merge them with existing ranges if necessary."""
for ordinal in ordinals:
self.add_ordinal(ordinal)
def get_ranges(self) -> List[Tuple[int, int]]:
return self._ranges
def merge(self, other):
for start, end in other.get_ranges():
self.add_range(start, end)
return self
def get_unicode_ranges_string(self):
def int_to_hex(i: int) -> str:
return hex(i)[2:].upper()
parts = []
for unicode_range in self._ranges:
parts.append(f'{int_to_hex(unicode_range[0])}-{int_to_hex(unicode_range[1])}')
return ','.join(parts)
def contains_ordinal(self, ordinal: int) -> bool:
for start, end in self._ranges:
if start <= ordinal <= end:
return True
return False
def contains_range(self, start: int, end: int) -> bool:
for s, e in self._ranges:
if s <= start and end <= e:
return True
return False
def iter_ordinals(self) -> Iterable[int]:
for start, end in self._ranges:
yield from range(start, end + 1)
def intersect(self, other: 'UnicodeRanges') -> 'UnicodeRanges':
"""
Get the intersection of two unicode ranges, returning a new UnicodeRanges object that contains the ordinals that
are in both ranges.
"""
# TODO: this is pretty inefficient to be iterating over all ordinals.
result = UnicodeRanges()
for ordinal in self.iter_ordinals():
if other.contains_ordinal(ordinal):
result.add_ordinal(ordinal)
return result