#![allow(dead_code)]
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct Range<T>
where
T: Ord + Clone,
{
#[serde(rename = "min")]
pub min: T,
#[serde(rename = "max")]
pub max: T,
#[serde(rename = "isMinInclusive")]
pub is_min_inclusive: bool,
#[serde(rename = "isMaxInclusive")]
pub is_max_inclusive: bool,
}
impl<T> Range<T>
where
T: Ord + Clone,
{
pub fn new(min: T, max: T, is_min_inclusive: bool, is_max_inclusive: bool) -> Self {
Self {
min,
max,
is_min_inclusive,
is_max_inclusive,
}
}
pub fn get_point_range(value: T) -> Self {
Self {
min: value.clone(),
max: value,
is_min_inclusive: true,
is_max_inclusive: true,
}
}
pub fn get_empty_range(value: T) -> Self {
Self {
min: value.clone(),
max: value,
is_min_inclusive: true,
is_max_inclusive: false,
}
}
pub fn is_single_value(&self) -> bool {
self.is_min_inclusive && self.is_max_inclusive && self.min == self.max
}
pub fn is_empty(&self) -> bool {
self.min == self.max && !(self.is_min_inclusive && self.is_max_inclusive)
}
pub fn contains(&self, value: &T) -> bool {
let min_satisfied = if self.is_min_inclusive {
&self.min <= value
} else {
&self.min < value
};
let max_satisfied = if self.is_max_inclusive {
&self.max >= value
} else {
&self.max > value
};
min_satisfied && max_satisfied
}
pub fn check_overlapping(range1: &Range<T>, range2: &Range<T>) -> bool {
if range1.is_empty() || range2.is_empty() {
return false;
}
let cmp1 = range1.min.cmp(&range2.max);
let cmp2 = range2.min.cmp(&range1.max);
if cmp1 <= Ordering::Equal && cmp2 <= Ordering::Equal {
if (cmp1 == Ordering::Equal && !(range1.is_min_inclusive && range2.is_max_inclusive))
|| (cmp2 == Ordering::Equal
&& !(range2.is_min_inclusive && range1.is_max_inclusive))
{
return false;
}
return true;
}
false
}
}
impl<T> PartialEq for Range<T>
where
T: Ord + Clone,
{
fn eq(&self, other: &Self) -> bool {
self.min == other.min
&& self.max == other.max
&& self.is_min_inclusive == other.is_min_inclusive
&& self.is_max_inclusive == other.is_max_inclusive
}
}
impl<T> Eq for Range<T> where T: Ord + Clone {}
impl<T> Hash for Range<T>
where
T: Ord + Clone + Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.min.hash(state);
self.max.hash(state);
self.is_min_inclusive.hash(state);
self.is_max_inclusive.hash(state);
}
}
impl<T> fmt::Display for Range<T>
where
T: Ord + Clone + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}{},{}{}",
if self.is_min_inclusive { "[" } else { "(" },
self.min,
self.max,
if self.is_max_inclusive { "]" } else { ")" }
)
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct MinComparer;
impl MinComparer {
pub fn compare<T>(left: &Range<T>, right: &Range<T>) -> Ordering
where
T: Ord + Clone,
{
let result = left.min.cmp(&right.min);
if result != Ordering::Equal || left.is_min_inclusive == right.is_min_inclusive {
return result;
}
if left.is_min_inclusive {
Ordering::Less
} else {
Ordering::Greater
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct MaxComparer;
impl MaxComparer {
pub fn compare<T>(left: &Range<T>, right: &Range<T>) -> Ordering
where
T: Ord + Clone,
{
let result = left.max.cmp(&right.max);
if result != Ordering::Equal || left.is_max_inclusive == right.is_max_inclusive {
return result;
}
if left.is_max_inclusive {
Ordering::Greater
} else {
Ordering::Less
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn range_creation() {
let range = Range::new(10, 20, true, false);
assert_eq!(range.min, 10);
assert_eq!(range.max, 20);
assert!(range.is_min_inclusive);
assert!(!range.is_max_inclusive);
}
#[test]
fn point_range() {
let range = Range::get_point_range(5);
assert_eq!(range.min, 5);
assert_eq!(range.max, 5);
assert!(range.is_single_value());
}
#[test]
fn empty_range() {
let range = Range::get_empty_range(10);
assert!(range.is_empty());
assert_eq!(range.min, 10);
assert_eq!(range.max, 10);
}
#[test]
fn contains() {
let range = Range::new(10, 20, true, false);
assert!(range.contains(&10)); assert!(range.contains(&15)); assert!(!range.contains(&20)); assert!(!range.contains(&5)); assert!(!range.contains(&25)); }
#[test]
fn contains_inclusive() {
let range = Range::new(10, 20, true, true);
assert!(range.contains(&10));
assert!(range.contains(&20)); }
#[test]
fn check_overlapping() {
let range1 = Range::new(10, 20, true, false);
let range2 = Range::new(15, 25, true, false);
assert!(Range::check_overlapping(&range1, &range2));
let range3 = Range::new(25, 30, true, false);
assert!(!Range::check_overlapping(&range1, &range3));
}
#[test]
fn check_overlapping_edge_cases() {
let range1 = Range::new(10, 20, true, false);
let range2 = Range::new(20, 30, true, false);
assert!(!Range::check_overlapping(&range1, &range2));
let range3 = Range::new(10, 20, true, true);
let range4 = Range::new(20, 30, true, false);
assert!(Range::check_overlapping(&range3, &range4));
}
#[test]
fn check_overlapping_with_empty() {
let range1 = Range::new(10, 20, true, false);
let empty = Range::get_empty_range(15);
assert!(!Range::check_overlapping(&range1, &empty));
}
#[test]
fn equality() {
let range1 = Range::new(10, 20, true, false);
let range2 = Range::new(10, 20, true, false);
let range3 = Range::new(10, 20, true, true);
assert_eq!(range1, range2);
assert_ne!(range1, range3);
}
#[test]
fn display() {
let range1 = Range::new(10, 20, true, false);
assert_eq!(format!("{}", range1), "[10,20)");
let range2 = Range::new(5, 15, false, true);
assert_eq!(format!("{}", range2), "(5,15]");
}
#[test]
fn min_comparer() {
let range1 = Range::new(10, 20, true, false);
let range2 = Range::new(15, 25, true, false);
let range3 = Range::new(10, 30, false, false);
assert_eq!(MinComparer::compare(&range1, &range2), Ordering::Less);
assert_eq!(MinComparer::compare(&range2, &range1), Ordering::Greater);
assert_eq!(MinComparer::compare(&range1, &range3), Ordering::Less);
}
#[test]
fn max_comparer() {
let range1 = Range::new(10, 20, true, false);
let range2 = Range::new(15, 25, true, false);
let range3 = Range::new(5, 20, true, true);
assert_eq!(MaxComparer::compare(&range1, &range2), Ordering::Less);
assert_eq!(MaxComparer::compare(&range2, &range1), Ordering::Greater);
assert_eq!(MaxComparer::compare(&range1, &range3), Ordering::Less);
assert_eq!(MaxComparer::compare(&range3, &range1), Ordering::Greater);
}
#[test]
fn string_ranges() {
let range = Range::new("AA".to_string(), "FF".to_string(), true, false);
assert!(range.contains(&"BB".to_string()));
assert!(range.contains(&"AA".to_string()));
assert!(!range.contains(&"FF".to_string()));
assert!(!range.contains(&"ZZ".to_string()));
}
#[test]
fn serialization() {
let range = Range::new("00".to_string(), "FF".to_string(), true, false);
let json = serde_json::to_string(&range).unwrap();
let deserialized: Range<String> = serde_json::from_str(&json).unwrap();
assert_eq!(range, deserialized);
assert!(json.contains("\"min\":\"00\""));
assert!(json.contains("\"max\":\"FF\""));
assert!(json.contains("\"isMinInclusive\":true"));
assert!(json.contains("\"isMaxInclusive\":false"));
}
}