use super::types::{Level, LevelView};
use crate::{Name, NameView};
pub(super) fn name_cmp(a: &Name, b: &Name) -> std::cmp::Ordering {
use std::cmp::Ordering;
fn tag(n: &Name) -> u8 {
match n.view() {
NameView::Anonymous => 0,
NameView::Str(_, _) => 1,
NameView::Num(_, _) => 2,
}
}
match (a.view(), b.view()) {
(NameView::Anonymous, NameView::Anonymous) => Ordering::Equal,
(NameView::Str(p1, s1), NameView::Str(p2, s2)) => name_cmp(p1, p2).then_with(|| s1.cmp(s2)),
(NameView::Num(p1, n1), NameView::Num(p2, n2)) => {
name_cmp(p1, p2).then_with(|| n1.cmp(&n2))
}
_ => tag(a).cmp(&tag(b)),
}
}
pub fn mk_max(l1: Level, l2: Level) -> Level {
if l1 == l2 {
return l1;
}
if l1.is_zero() {
return l2;
}
if l2.is_zero() {
return l1;
}
if let (Some(n1), Some(n2)) = (l1.to_nat(), l2.to_nat()) {
return if n1 >= n2 { l1 } else { l2 };
}
Level::max(l1, l2)
}
pub fn mk_imax(l1: Level, l2: Level) -> Level {
if l2.is_zero() {
return Level::zero();
}
if l2.is_not_zero() {
return mk_max(l1, l2);
}
if l1.is_zero() {
return l2;
}
if l1 == l2 {
return l1;
}
Level::imax(l1, l2)
}
fn to_offset(l: &Level) -> (&Level, u32) {
match l.view() {
LevelView::Succ(inner) => {
let (base, k) = to_offset(inner);
(base, k + 1)
}
_ => (l, 0),
}
}
pub(super) fn is_leq_core(l1: &Level, l2: &Level, diff: i64) -> bool {
if crate::deadline::is_expired() {
return false;
}
if l1 == l2 {
return diff >= 0;
}
match (l1.view(), l2.view()) {
(LevelView::Zero, _) if diff >= 0 => true,
(_, LevelView::Zero) if diff < 0 => false,
(LevelView::Param(a), LevelView::Param(b)) => a == b && diff >= 0,
(LevelView::Param(_), LevelView::Zero) => false,
(LevelView::Zero, LevelView::Param(_)) => false,
(LevelView::Succ(a), _) => is_leq_core(a, l2, diff - 1),
(_, LevelView::Succ(b)) => is_leq_core(l1, b, diff + 1),
(LevelView::Max(a, b), _) => is_leq_core(a, l2, diff) && is_leq_core(b, l2, diff),
(LevelView::Param(_) | LevelView::Zero | LevelView::MVar(_), LevelView::Max(a, b)) => {
if let Some(name) = find_split_param(l2) {
let param = Level::param(name.clone());
imax_param_split(l1, l2, ¶m, diff)
} else {
is_leq_core(l1, a, diff) || is_leq_core(l1, b, diff)
}
}
(LevelView::IMax(a, b), _) => {
let simplified = simplify_imax(a, b);
match simplified {
Some(s) => is_leq_core(&s, l2, diff),
None => imax_param_split(l1, l2, b, diff),
}
}
(_, LevelView::IMax(a, b)) => {
let simplified = simplify_imax(a, b);
match simplified {
Some(s) => is_leq_core(l1, &s, diff),
None => imax_param_split(l1, l2, b, diff),
}
}
_ => {
let (b1, k1) = to_offset(l1);
let (b2, k2) = to_offset(l2);
if b1 == b2 {
(k1 as i64) <= (k2 as i64) + diff
} else {
false
}
}
}
}
fn find_split_param(l: &Level) -> Option<&Name> {
match l.view() {
LevelView::Zero | LevelView::Param(_) | LevelView::MVar(_) => None,
LevelView::Succ(a) => find_split_param(a),
LevelView::Max(a, b) => find_split_param(a).or_else(|| find_split_param(b)),
LevelView::IMax(a, b) => {
if !b.is_zero() && !b.is_not_zero() {
if let Some(n) = first_crit_param(b) {
return Some(n);
}
}
find_split_param(a).or_else(|| find_split_param(b))
}
}
}
fn first_crit_param(l: &Level) -> Option<&Name> {
match l.view() {
LevelView::Param(n) => Some(n),
LevelView::Zero | LevelView::Succ(_) | LevelView::MVar(_) => None,
LevelView::Max(a, b) => first_crit_param(a).or_else(|| first_crit_param(b)),
LevelView::IMax(_, b) => first_crit_param(b),
}
}
fn imax_param_split(l1: &Level, l2: &Level, param: &Level, diff: i64) -> bool {
let name = match param.view() {
LevelView::Param(n) => n.clone(),
_ => return false,
};
let zero_l1 = subst_param(l1, &name, &Level::zero());
let zero_l2 = subst_param(l2, &name, &Level::zero());
let succ = Level::succ(Level::param(name.clone()));
let succ_l1 = subst_param(l1, &name, &succ);
let succ_l2 = subst_param(l2, &name, &succ);
is_leq_core(&zero_l1, &zero_l2, diff) && is_leq_core(&succ_l1, &succ_l2, diff)
}
fn simplify_imax(a: &Level, b: &Level) -> Option<Level> {
if b.is_zero() {
return Some(Level::zero());
}
if b.is_not_zero() {
return Some(Level::max(a.clone(), b.clone()));
}
match b.view() {
LevelView::Max(c, d) => Some(Level::max(
Level::imax(a.clone(), c.clone()),
Level::imax(a.clone(), d.clone()),
)),
LevelView::IMax(c, d) => Some(Level::max(
Level::imax(a.clone(), d.clone()),
Level::imax(c.clone(), d.clone()),
)),
_ => None,
}
}
fn subst_param(l: &Level, name: &Name, value: &Level) -> Level {
match l.view() {
LevelView::Param(n) if n == name => value.clone(),
LevelView::Param(_) | LevelView::Zero | LevelView::MVar(_) => l.clone(),
LevelView::Succ(inner) => Level::succ(subst_param(inner, name, value)),
LevelView::Max(a, b) => {
Level::max(subst_param(a, name, value), subst_param(b, name, value))
}
LevelView::IMax(a, b) => {
Level::imax(subst_param(a, name, value), subst_param(b, name, value))
}
}
}