Skip to content

Commit e1bd299

Browse files
committed
perf: more efficient relation_with
1 parent 336f342 commit e1bd299

File tree

3 files changed

+159
-30
lines changed

3 files changed

+159
-30
lines changed

Diff for: src/internal/incompatibility.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use std::collections::HashSet as Set;
77
use std::fmt;
88

99
use crate::package::Package;
10-
use crate::range::Range;
10+
use crate::range::{self, Range};
1111
use crate::report::{DefaultStringReporter, DerivationTree, Derived, External};
1212
use crate::solver::DependencyConstraints;
13-
use crate::term::{self, Term};
13+
use crate::term::Term;
1414
use crate::type_aliases::Map;
1515
use crate::version::Version;
1616

@@ -233,12 +233,12 @@ impl<P: Package, V: Version> Incompatibility<P, V> {
233233
let mut relation = Relation::Satisfied;
234234
for (package, incompat_term) in self.package_terms.iter() {
235235
match incompat_term.relation_with(&terms(package)) {
236-
term::Relation::Satisfied => {}
237-
term::Relation::Contradicted => {
236+
range::Relation::Satisfied => {}
237+
range::Relation::Contradicted => {
238238
relation = Relation::Contradicted((package.clone(), incompat_term.clone()));
239239
break;
240240
}
241-
term::Relation::Inconclusive => {
241+
range::Relation::Inconclusive => {
242242
if relation == Relation::Satisfied {
243243
relation =
244244
Relation::AlmostSatisfied((package.clone(), incompat_term.clone()));

Diff for: src/range.rs

+134
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,93 @@ impl<V: Version> Range<V> {
235235
}
236236
}
237237

238+
/// Describe a relation between a set of terms S and another term t.
239+
///
240+
/// As a shorthand, we say that a term v
241+
/// satisfies or contradicts a term t if {v} satisfies or contradicts it.
242+
#[derive(Eq, PartialEq, Debug)]
243+
pub(crate) enum Relation {
244+
/// We say that a set of terms S "satisfies" a term t
245+
/// if t must be true whenever every term in S is true.
246+
Satisfied,
247+
/// Conversely, S "contradicts" t if t must be false
248+
/// whenever every term in S is true.
249+
Contradicted,
250+
/// If neither of these is true we say that S is "inconclusive" for t.
251+
Inconclusive,
252+
}
253+
254+
// Set operations.
255+
impl<V: Version> Range<V> {
256+
/// Compute the relation of two sets of versions.
257+
pub(crate) fn relation(&self, other: &Self) -> Relation {
258+
let mut state = None;
259+
for s in &self.segments {
260+
match other.contains_interval(s) {
261+
Relation::Satisfied => match state {
262+
None | Some(Relation::Satisfied) => state = Some(Relation::Satisfied),
263+
_ => return Relation::Inconclusive,
264+
},
265+
Relation::Inconclusive => return Relation::Inconclusive,
266+
Relation::Contradicted => match state {
267+
None | Some(Relation::Contradicted) => state = Some(Relation::Contradicted),
268+
_ => return Relation::Inconclusive,
269+
},
270+
};
271+
}
272+
state.unwrap_or(Relation::Satisfied)
273+
}
274+
275+
fn contains_interval(&self, other: &Interval<V>) -> Relation {
276+
match other {
277+
(o1, Some(o2)) => {
278+
for seg in &self.segments {
279+
match seg {
280+
(s1, Some(s2)) => {
281+
if o2 < s1 {
282+
break;
283+
}
284+
if s1 <= o1 && o2 <= s2 {
285+
return Relation::Satisfied;
286+
}
287+
if !(s1 >= o2 || o1 >= s2) {
288+
return Relation::Inconclusive;
289+
}
290+
}
291+
(s1, None) => {
292+
if s1 <= o1 {
293+
return Relation::Satisfied;
294+
}
295+
if s1 < o2 {
296+
return Relation::Inconclusive;
297+
}
298+
}
299+
}
300+
}
301+
}
302+
(o1, None) => {
303+
if let Some((s1, None)) = self.segments.iter().rev().next() {
304+
if s1 <= o1 {
305+
return Relation::Satisfied;
306+
}
307+
}
308+
if self
309+
.segments
310+
.iter()
311+
.find(|&seg| match seg {
312+
(_, Some(s2)) => o1 < s2,
313+
_ => true,
314+
})
315+
.is_some()
316+
{
317+
return Relation::Inconclusive;
318+
}
319+
}
320+
};
321+
Relation::Contradicted
322+
}
323+
}
324+
238325
// Other useful functions.
239326
impl<V: Version> Range<V> {
240327
/// Check if a range contains a given version.
@@ -379,6 +466,53 @@ pub mod tests {
379466
assert_eq!(r1.intersection(&r2).contains(&version), r1.contains(&version) && r2.contains(&version));
380467
}
381468

469+
// Testing relation -----------------------------------
470+
471+
#[test]
472+
fn relation_with_any_is_satisfied(range in strategy()) {
473+
prop_assert_eq!(range.relation(&Range::any()), Relation::Satisfied);
474+
}
475+
476+
#[test]
477+
fn relation_with_none_is_contradicted(range in strategy()) {
478+
prop_assert_eq!(range.relation(&Range::none()), if range == Range::none() {
479+
Relation::Satisfied
480+
} else {
481+
Relation::Contradicted
482+
});
483+
}
484+
485+
#[test]
486+
fn relation_with_self_is_satisfied(range in strategy()) {
487+
prop_assert_eq!(range.relation(&range), Relation::Satisfied);
488+
}
489+
490+
#[test]
491+
fn relation_matchs_intersection(r1 in strategy(), r2 in strategy()) {
492+
let full_intersection = r1.intersection(&r2);
493+
let by_intersection = if full_intersection == r2 {
494+
Relation::Satisfied
495+
} else if full_intersection == Range::none() {
496+
Relation::Contradicted
497+
} else {
498+
Relation::Inconclusive
499+
};
500+
prop_assert_eq!(by_intersection, r2.relation(&r1));
501+
}
502+
503+
#[test]
504+
fn relation_contains_both(r1 in strategy(), r2 in strategy(), version in version_strat()) {
505+
match r1.relation(&r2) {
506+
Relation::Satisfied => {
507+
prop_assert!(r2.contains(&version) || !r1.contains(&version));
508+
}
509+
Relation::Contradicted => {
510+
prop_assert!(!(r1.contains(&version) && r2.contains(&version)));
511+
}
512+
_ => {}
513+
}
514+
}
515+
382516
// Testing union -----------------------------------
383517

384518
#[test]

Diff for: src/term.rs

+20-25
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! A term is the fundamental unit of operation of the PubGrub algorithm.
44
//! It is a positive or negative expression regarding a set of versions.
55
6-
use crate::range::Range;
6+
use crate::range::{Range, Relation};
77
use crate::version::Version;
88
use std::fmt;
99

@@ -28,7 +28,7 @@ impl<V: Version> Term<V> {
2828
}
2929

3030
/// A term that is never true.
31-
pub(crate) fn empty() -> Self {
31+
pub fn empty() -> Self {
3232
Self::Positive(Range::none())
3333
}
3434

@@ -100,21 +100,6 @@ impl<V: Version> Term<V> {
100100
}
101101
}
102102

103-
/// Describe a relation between a set of terms S and another term t.
104-
///
105-
/// As a shorthand, we say that a term v
106-
/// satisfies or contradicts a term t if {v} satisfies or contradicts it.
107-
pub(crate) enum Relation {
108-
/// We say that a set of terms S "satisfies" a term t
109-
/// if t must be true whenever every term in S is true.
110-
Satisfied,
111-
/// Conversely, S "contradicts" t if t must be false
112-
/// whenever every term in S is true.
113-
Contradicted,
114-
/// If neither of these is true we say that S is "inconclusive" for t.
115-
Inconclusive,
116-
}
117-
118103
/// Relation between terms.
119104
impl<'a, V: 'a + Version> Term<V> {
120105
/// Check if a set of terms satisfies this term.
@@ -144,14 +129,24 @@ impl<'a, V: 'a + Version> Term<V> {
144129

145130
/// Check if a set of terms satisfies or contradicts a given term.
146131
/// Otherwise the relation is inconclusive.
147-
pub(crate) fn relation_with(&self, other_terms_intersection: &Term<V>) -> Relation {
148-
let full_intersection = self.intersection(other_terms_intersection.as_ref());
149-
if &full_intersection == other_terms_intersection {
150-
Relation::Satisfied
151-
} else if full_intersection == Self::empty() {
152-
Relation::Contradicted
153-
} else {
154-
Relation::Inconclusive
132+
pub(crate) fn relation_with(&self, other: &Term<V>) -> Relation {
133+
match (self, other) {
134+
(Self::Positive(r1), Self::Positive(r2)) => r2.relation(r1),
135+
(Self::Positive(r1), Self::Negative(r2)) => match r2.negate().relation(r1) {
136+
Relation::Satisfied => {
137+
if r2 == &Range::any() {
138+
Relation::Contradicted
139+
} else {
140+
Relation::Inconclusive
141+
}
142+
}
143+
x => x,
144+
},
145+
(Self::Negative(r1), Self::Positive(r2)) => r2.relation(&r1.negate()),
146+
(Self::Negative(r1), Self::Negative(r2)) => match r1.relation(r2) {
147+
Relation::Contradicted => Relation::Inconclusive,
148+
x => x,
149+
},
155150
}
156151
}
157152
}

0 commit comments

Comments
 (0)