use std::fmt;
use crate::api::context::Context;
use crate::api::expr::{Ex, SetEx};
use crate::base::interval::{Interval, IntervalKind};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Kind {
Continuous,
Discrete,
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum Piece {
Interval(Interval<Ex>),
Point(Ex),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Support {
kind: Kind,
pieces: Vec<Piece>,
}
pub(crate) fn is_pos_inf(e: &Ex) -> bool {
*e == e.context().infinity()
}
pub(crate) fn is_neg_inf(e: &Ex) -> bool {
*e == e.context().neg_infinity()
}
fn with_infinite_ends_open(iv: Interval<Ex>) -> Interval<Ex> {
let kind = IntervalKind::from_open_ends(
iv.kind.lower_open() || is_neg_inf(&iv.lower),
iv.kind.upper_open() || is_pos_inf(&iv.upper),
);
iv.with_kind(kind)
}
fn max_lo(a: &Interval<Ex>, b: &Interval<Ex>) -> (Ex, bool) {
let (a_open, b_open) = (a.kind.lower_open(), b.kind.lower_open());
let (a, b) = (&a.lower, &b.lower);
if is_neg_inf(a) {
return (b.clone(), b_open);
}
if is_neg_inf(b) {
return (a.clone(), a_open);
}
match (a - b).is_positive() {
Some(true) => (a.clone(), a_open),
Some(false) => match (a - b).is_zero() {
Some(true) => (a.clone(), a_open || b_open),
_ => (b.clone(), b_open),
},
None => (a.max_with(b), a_open || b_open),
}
}
fn min_hi(a: &Interval<Ex>, b: &Interval<Ex>) -> (Ex, bool) {
let (a_open, b_open) = (a.kind.upper_open(), b.kind.upper_open());
let (a, b) = (&a.upper, &b.upper);
if is_pos_inf(a) {
return (b.clone(), b_open);
}
if is_pos_inf(b) {
return (a.clone(), a_open);
}
match (b - a).is_positive() {
Some(true) => (a.clone(), a_open),
Some(false) => match (a - b).is_zero() {
Some(true) => (a.clone(), a_open || b_open),
_ => (b.clone(), b_open),
},
None => (a.min_with(b), a_open || b_open),
}
}
fn interval_empty(iv: &Interval<Ex>) -> Option<bool> {
let (lo, hi) = (&iv.lower, &iv.upper);
if is_neg_inf(lo) || is_pos_inf(hi) {
return Some(false);
}
if is_pos_inf(lo) || is_neg_inf(hi) {
return Some(true);
}
let d = hi - lo;
match d.is_positive() {
Some(true) => Some(false),
Some(false) => match d.is_zero() {
Some(true) => Some(iv.kind != IntervalKind::Closed),
Some(false) => Some(true),
None => None,
},
None => None,
}
}
fn interval_contains(iv: &Interval<Ex>, v: &Ex) -> Option<bool> {
let above = if is_neg_inf(&iv.lower) {
Some(true)
} else {
let d = v - &iv.lower;
if iv.kind.lower_open() {
d.is_positive()
} else {
d.is_nonnegative()
}
};
let below = if is_pos_inf(&iv.upper) {
Some(true)
} else {
let d = &iv.upper - v;
if iv.kind.upper_open() {
d.is_positive()
} else {
d.is_nonnegative()
}
};
match (above, below) {
(Some(false), _) | (_, Some(false)) => Some(false),
(Some(true), Some(true)) => Some(true),
_ => None,
}
}
impl Support {
pub fn reals(ctx: &Context) -> Self {
Support::interval(ctx.neg_infinity(), ctx.infinity())
}
pub fn interval(lo: Ex, hi: Ex) -> Self {
Support {
kind: Kind::Continuous,
pieces: vec![Piece::Interval(with_infinite_ends_open(Interval::closed(
lo, hi,
)))],
}
}
pub fn half_line(lo: Ex) -> Self {
let inf = lo.context().infinity();
Support::interval(lo, inf)
}
pub fn integers(ctx: &Context, lo: Option<Ex>, hi: Option<Ex>) -> Self {
let lo = lo.unwrap_or_else(|| ctx.neg_infinity());
let hi = hi.unwrap_or_else(|| ctx.infinity());
Support {
kind: Kind::Discrete,
pieces: vec![Piece::Interval(with_infinite_ends_open(Interval::closed(
lo, hi,
)))],
}
}
pub fn points(values: Vec<Ex>) -> Self {
Support {
kind: Kind::Discrete,
pieces: values.into_iter().map(Piece::Point).collect(),
}
}
pub fn from_pieces(kind: Kind, pieces: Vec<Piece>) -> Self {
let pieces = pieces
.into_iter()
.map(|p| match p {
Piece::Interval(iv) => Piece::Interval(with_infinite_ends_open(iv)),
Piece::Point(v) => Piece::Point(v),
})
.collect();
Support { kind, pieces }
}
pub fn kind(&self) -> Kind {
self.kind
}
pub fn pieces(&self) -> &[Piece] {
&self.pieces
}
pub fn with_kind(mut self, kind: Kind) -> Self {
self.kind = kind;
self
}
pub fn is_empty(&self) -> bool {
self.pieces.is_empty()
}
pub fn is_interval(&self) -> bool {
matches!(self.pieces.as_slice(), [Piece::Interval(_)])
}
pub fn as_interval(&self) -> Option<&Interval<Ex>> {
match self.pieces.as_slice() {
[Piece::Interval(iv)] => Some(iv),
_ => None,
}
}
pub fn as_points(&self) -> Option<Vec<Ex>> {
self.pieces
.iter()
.map(|p| match p {
Piece::Point(v) => Some(v.clone()),
Piece::Interval(_) => None,
})
.collect()
}
pub fn contains(&self, v: &Ex) -> Option<bool> {
let mut undecided = false;
for p in &self.pieces {
let inside = match p {
Piece::Point(w) => w.equals(v),
Piece::Interval(iv) => {
let in_interval = interval_contains(iv, v);
if self.kind == Kind::Discrete {
match (in_interval, v.is_integer()) {
(Some(false), _) | (_, Some(false)) => Some(false),
(Some(true), Some(true)) => Some(true),
_ => None,
}
} else {
in_interval
}
}
};
match inside {
Some(true) => return Some(true),
Some(false) => {}
None => undecided = true,
}
}
if undecided { None } else { Some(false) }
}
pub fn normalize_lattice(&self) -> Self {
if self.kind != Kind::Discrete {
return self.clone();
}
let pieces = self
.pieces
.iter()
.map(|p| match p {
Piece::Interval(iv) => {
let ctx = iv.lower.context();
let lo = if is_neg_inf(&iv.lower) {
iv.lower.clone()
} else if iv.kind.lower_open() {
(iv.lower.floor() + ctx.one()).simplify()
} else {
iv.lower.ceiling().simplify()
};
let hi = if is_pos_inf(&iv.upper) {
iv.upper.clone()
} else if iv.kind.upper_open() {
(iv.upper.ceiling() - ctx.one()).simplify()
} else {
iv.upper.floor().simplify()
};
Piece::Interval(with_infinite_ends_open(Interval::closed(lo, hi)))
}
Piece::Point(v) => Piece::Point(v.clone()),
})
.collect();
Support {
kind: self.kind,
pieces,
}
}
pub fn intersect(&self, region: &Support) -> Option<Self> {
let me = self.normalize_lattice();
let other = if self.kind == Kind::Discrete {
region.clone().with_kind(Kind::Discrete).normalize_lattice()
} else {
region.clone()
};
let mut out = Vec::new();
for a in &me.pieces {
for b in &other.pieces {
match (a, b) {
(Piece::Interval(ia), Piece::Interval(ib)) => {
let (lo, lo_open) = max_lo(ia, ib);
let (hi, hi_open) = min_hi(ia, ib);
let iv = Interval {
lower: lo,
upper: hi,
kind: IntervalKind::from_open_ends(lo_open, hi_open),
};
if interval_empty(&iv) != Some(true) {
out.push(Piece::Interval(iv));
}
}
(Piece::Point(v), Piece::Interval(_)) => {
let single = Support::from_pieces(self.kind, vec![b.clone()]);
let single = single.with_kind(Kind::Continuous);
if single.contains(v)? {
out.push(Piece::Point(v.clone()));
}
}
(Piece::Interval(_), Piece::Point(v)) => {
let single = Support::from_pieces(self.kind, vec![a.clone()]);
match single.contains(v) {
Some(true) => out.push(Piece::Point(v.clone())),
Some(false) => {}
None => out.push(Piece::Point(v.clone())),
}
}
(Piece::Point(v), Piece::Point(w)) => match v.equals(w) {
Some(true) => out.push(Piece::Point(v.clone())),
Some(false) => {}
None => return None,
},
}
}
}
Some(Support {
kind: self.kind,
pieces: out,
})
}
pub fn to_set(&self, ctx: &Context) -> SetEx {
let mut acc: Option<SetEx> = None;
let mut points = Vec::new();
for p in &self.pieces {
match p {
Piece::Interval(iv) => {
let s = ctx.interval(&iv.lower, &iv.upper, iv.kind);
acc = Some(match acc {
Some(a) => a.union(&s),
None => s,
});
}
Piece::Point(v) => points.push(v.clone()),
}
}
if !points.is_empty() {
let s = ctx.finite_set(&points);
acc = Some(match acc {
Some(a) => a.union(&s),
None => s,
});
}
acc.unwrap_or_else(|| ctx.empty_set())
}
pub fn from_set(kind: Kind, set: &SetEx) -> Option<Self> {
if let Some(values) = set.as_finite_set() {
return Some(Support {
kind,
pieces: values.into_iter().map(Piece::Point).collect(),
});
}
let parts = set.as_intervals()?;
let pieces = parts
.into_iter()
.map(|iv| {
if iv.kind == IntervalKind::Closed && iv.lower == iv.upper {
Piece::Point(iv.lower)
} else {
Piece::Interval(iv)
}
})
.collect();
Some(Support { kind, pieces })
}
}
impl fmt::Display for Support {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.pieces.is_empty() {
return f.write_str("∅");
}
let mut points = Vec::new();
let mut first = true;
for p in &self.pieces {
match p {
Piece::Interval(iv) => {
if !first {
f.write_str(" ∪ ")?;
}
first = false;
write!(f, "{iv}")?;
}
Piece::Point(v) => points.push(v.to_string()),
}
}
if !points.is_empty() {
if !first {
f.write_str(" ∪ ")?;
}
write!(f, "{{{}}}", points.join(", "))?;
}
if self.kind == Kind::Discrete
&& self.pieces.iter().any(|p| matches!(p, Piece::Interval(_)))
{
f.write_str(" ∩ ℤ")?;
}
Ok(())
}
}