use crate::util::SliceHelp;
use std::cmp::Ordering;
use std::iter::once;
pub type CodePoint = u32;
pub const CODE_POINT_MAX: CodePoint = 0x10FFFF;
#[derive(Debug, Copy, Clone)]
pub struct Interval {
pub first: CodePoint,
pub last: CodePoint,
}
impl Interval {
fn is_before(self, other: Interval) -> bool {
self.last < other.first
}
fn is_strictly_before(self, rhs: Interval) -> bool {
self.last + 1 < rhs.first
}
fn mergecmp(self, rhs: Interval) -> Ordering {
if self.is_strictly_before(rhs) {
Ordering::Less
} else if rhs.is_strictly_before(self) {
Ordering::Greater
} else {
Ordering::Equal
}
}
fn mergeable(self, rhs: Interval) -> bool {
self.mergecmp(rhs) == Ordering::Equal
}
pub fn contains(self, cp: CodePoint) -> bool {
self.first <= cp && cp <= self.last
}
pub fn overlaps(self, other: Interval) -> bool {
!self.is_before(other) && !other.is_before(self)
}
pub fn codepoints(self) -> std::ops::Range<u32> {
debug_assert!(self.last + 1 > self.last, "Overflow");
self.first..(self.last + 1)
}
pub fn count_codepoints(self) -> usize {
(self.last - self.first + 1) as usize
}
}
fn merge_intervals(x: Interval, y: &Interval) -> Interval {
debug_assert!(x.mergeable(*y), "Ranges not mergeable");
Interval {
first: std::cmp::min(x.first, y.first),
last: std::cmp::max(x.last, y.last),
}
}
#[derive(Clone, Debug, Default)]
pub struct CodePointSet {
ivs: Vec<Interval>,
}
impl CodePointSet {
pub fn new() -> CodePointSet {
CodePointSet { ivs: Vec::new() }
}
fn assert_is_well_formed(&self) {
if cfg!(debug_assertions) {
for iv in &self.ivs {
debug_assert!(iv.last <= CODE_POINT_MAX);
debug_assert!(iv.first <= iv.last);
}
for w in self.ivs.windows(2) {
debug_assert!(w[0].is_strictly_before(w[1]));
}
}
}
pub fn from_sorted_disjoint_intervals(ivs: Vec<Interval>) -> CodePointSet {
let res = CodePointSet { ivs };
res.assert_is_well_formed();
res
}
pub fn add(&mut self, new_iv: Interval) {
let mergeable = self.ivs.equal_range_by(|iv| iv.mergecmp(new_iv));
if cfg!(debug_assertions) {
for (idx, iv) in self.ivs.iter().enumerate() {
if idx < mergeable.start {
debug_assert!(iv.is_strictly_before(new_iv));
} else if idx >= mergeable.end {
debug_assert!(new_iv.is_strictly_before(*iv));
} else {
debug_assert!(iv.mergeable(new_iv) && new_iv.mergeable(*iv));
}
}
}
let merged_iv = self.ivs[mergeable.clone()]
.iter()
.fold(new_iv, merge_intervals);
self.ivs.splice(mergeable, once(merged_iv));
self.assert_is_well_formed();
}
pub fn add_one(&mut self, cp: CodePoint) {
self.add(Interval {
first: cp,
last: cp,
})
}
pub fn add_set(&mut self, mut rhs: CodePointSet) {
if self.ivs.len() < rhs.ivs.len() {
std::mem::swap(self, &mut rhs);
}
for iv in rhs.intervals() {
self.add(*iv)
}
}
pub fn intervals(&self) -> &[Interval] {
self.ivs.as_slice()
}
pub fn inverted_interval_count(&self) -> usize {
let mut result = 0;
let mut start: CodePoint = 0;
for iv in &self.ivs {
if start < iv.first {
result += 1;
}
start = iv.last + 1;
}
if start <= CODE_POINT_MAX {
result += 1;
}
result
}
pub fn inverted(&self) -> CodePointSet {
let mut inverted_ivs = Vec::new();
let mut start: CodePoint = 0;
for iv in &self.ivs {
if start < iv.first {
inverted_ivs.push(Interval {
first: start,
last: iv.first - 1,
})
}
start = iv.last + 1;
}
if start <= CODE_POINT_MAX {
inverted_ivs.push(Interval {
first: start,
last: CODE_POINT_MAX,
})
}
CodePointSet::from_sorted_disjoint_intervals(inverted_ivs)
}
}