diff --git a/examples/borrow_check.rs b/examples/borrow_check.rs index 8f2197a..f01b866 100644 --- a/examples/borrow_check.rs +++ b/examples/borrow_check.rs @@ -1,5 +1,5 @@ extern crate datafrog; -use datafrog::Iteration; +use datafrog::{Iteration, Relation}; type Region = u32; type Borrow = u32; @@ -14,15 +14,15 @@ fn main() { let subset = iteration1.variable::<(Region, Region, Point)>("subset"); // different indices for `subset`. - let subset_r1p = iteration1.variable::<((Region, Point), Region)>("subset_r1p"); - let subset_r2p = iteration1.variable::<((Region, Point), Region)>("subset_r2p"); - let subset_p = iteration1.variable::<(Point, (Region, Region))>("subset_p"); + let subset_r1p = iteration1.variable::<(Region, Point, Region)>("subset_r1p"); + let subset_r2p = iteration1.variable::<(Region, Point, Region)>("subset_r2p"); + let subset_p = iteration1.variable::<(Point, Region, Region)>("subset_p"); // temporaries as we perform a multi-way join. - let subset_1 = iteration1.variable::<((Region, Point), Region)>("subset_1"); - let subset_2 = iteration1.variable::<((Region, Point), Region)>("subset_2"); + let subset_1 = iteration1.variable::<(Region, Point, Region)>("subset_1"); + let subset_2 = iteration1.variable::<(Region, Point, Region)>("subset_2"); - let region_live_at = iteration1.variable::<((Region, Point), ())>("region_live_at"); + let region_live_at = iteration1.variable::<(Region, Point)>("region_live_at"); let cfg_edge_p = iteration1.variable::<(Point, Point)>("cfg_edge_p"); // load initial facts. @@ -33,9 +33,9 @@ fn main() { // .. and then start iterating rules! while iteration1.changed() { // remap fields to re-index by keys. - subset_r1p.from_map(&subset, |&(r1, r2, p)| ((r1, p), r2)); - subset_r2p.from_map(&subset, |&(r1, r2, p)| ((r2, p), r1)); - subset_p.from_map(&subset, |&(r1, r2, p)| (p, (r1, r2))); + subset_r1p.from_map(&subset, |&(r1, r2, p)| (r1, p, r2)); + subset_r2p.from_map(&subset, |&(r1, r2, p)| (r2, p, r1)); + subset_p.from_map(&subset, |&(r1, r2, p)| (p, r1, r2)); // R0: subset(R1, R2, P) :- outlives(R1, R2, P). // Already loaded; outlives is static. @@ -43,7 +43,7 @@ fn main() { // R1: subset(R1, R3, P) :- // subset(R1, R2, P), // subset(R2, R3, P). - subset.from_join(&subset_r2p, &subset_r1p, |&(_r2, p), &r1, &r3| (r1, r3, p)); + subset.from_join(&subset_r2p, &subset_r1p, |(_r2, p), r1, r3| (r1, r3, p)); // R2: subset(R1, R2, Q) :- // subset(R1, R2, P), @@ -51,11 +51,11 @@ fn main() { // region_live_at(R1, Q), // region_live_at(R2, Q). - subset_1.from_join(&subset_p, &cfg_edge_p, |&_p, &(r1, r2), &q| ((r1, q), r2)); - subset_2.from_join(&subset_1, ®ion_live_at, |&(r1, q), &r2, &()| { - ((r2, q), r1) + subset_1.from_join_first(&subset_p, &cfg_edge_p, |_p, (r1, r2), q| (r1, q, r2)); + subset_2.from_join(&subset_1, ®ion_live_at, |(r1, q), r2, ()| { + (r2, q, r1) }); - subset.from_join(&subset_2, ®ion_live_at, |&(r2, q), &r1, &()| (r1, r2, q)); + subset.from_join(&subset_2, ®ion_live_at, |(r2, q), r1, ()| (r1, r2, q)); } subset_r1p.complete() @@ -69,23 +69,23 @@ fn main() { let requires = iteration2.variable::<(Region, Borrow, Point)>("requires"); requires.insert(Vec::new().into()); - let requires_rp = iteration2.variable::<((Region, Point), Borrow)>("requires_rp"); - let requires_bp = iteration2.variable::<((Borrow, Point), Region)>("requires_bp"); + let requires_rp = iteration2.variable::<(Region, Point, Borrow)>("requires_rp"); + let requires_bp = iteration2.variable::<(Borrow, Point, Region)>("requires_bp"); - let requires_1 = iteration2.variable::<(Point, (Borrow, Region))>("requires_1"); - let requires_2 = iteration2.variable::<((Region, Point), Borrow)>("requires_2"); + let requires_1 = iteration2.variable::<(Point, Borrow, Region)>("requires_1"); + let requires_2 = iteration2.variable::<(Region, Point, Borrow)>("requires_2"); - let subset_r1p = iteration2.variable::<((Region, Point), Region)>("subset_r1p"); + let subset_r1p = iteration2.variable::<(Region, Point, Region)>("subset_r1p"); subset_r1p.insert(subset); - let killed = Vec::new().into(); - let region_live_at = iteration2.variable::<((Region, Point), ())>("region_live_at"); + let killed: Relation<(Borrow, Point)> = Vec::new().into(); + let region_live_at = iteration2.variable::<(Region, Point)>("region_live_at"); let cfg_edge_p = iteration2.variable::<(Point, Point)>("cfg_edge_p"); // .. and then start iterating rules! while iteration2.changed() { - requires_rp.from_map(&requires, |&(r, b, p)| ((r, p), b)); - requires_bp.from_map(&requires, |&(r, b, p)| ((b, p), r)); + requires_rp.from_map(&requires, |&(r, b, p)| (r, p, b)); + requires_bp.from_map(&requires, |&(r, b, p)| (b, p, r)); // requires(R, B, P) :- borrow_region(R, B, P). // Already loaded; borrow_region is static. @@ -93,7 +93,7 @@ fn main() { // requires(R2, B, P) :- // requires(R1, B, P), // subset(R1, R2, P). - requires.from_join(&requires_rp, &subset_r1p, |&(_r1, p), &b, &r2| (r2, b, p)); + requires.from_join(&requires_rp, &subset_r1p, |(_r1, p), b, r2| (r2, b, p)); // requires(R, B, Q) :- // requires(R, B, P), @@ -101,9 +101,9 @@ fn main() { // cfg_edge(P, Q), // (region_live_at(R, Q); universal_region(R)). - requires_1.from_antijoin(&requires_bp, &killed, |&(b, p), &r| (p, (b, r))); - requires_2.from_join(&requires_1, &cfg_edge_p, |&_p, &(b, r), &q| ((r, q), b)); - requires.from_join(&requires_2, ®ion_live_at, |&(r, q), &b, &()| (r, b, q)); + requires_1.from_antijoin(&requires_bp, &killed, |(b, p, r)| (p, b, r)); + requires_2.from_join_first(&requires_1, &cfg_edge_p, |_p, (b, r), q| (r, q, b)); + requires.from_join(&requires_2, ®ion_live_at, |(r, q), b, ()| (r, b, q)); } requires.complete() diff --git a/examples/graspan1.rs b/examples/graspan1.rs index 31225b1..f43440d 100644 --- a/examples/graspan1.rs +++ b/examples/graspan1.rs @@ -49,7 +49,7 @@ fn main() { // .. and then start iterating rules! while iteration.changed() { // N(a,c) <- N(a,b), E(b,c) - variable1.from_join(&variable1, &variable2, |_b, &a, &c| (c, a)); + variable1.from_join_first(&variable1, &variable2, |_b, a, c| (c, a)); } let reachable = variable1.complete(); diff --git a/src/join.rs b/src/join.rs index 2495dd7..cf793d6 100644 --- a/src/join.rs +++ b/src/join.rs @@ -1,6 +1,6 @@ //! Join functionality. -use super::{Relation, Variable}; +use super::{Relation, Split, Variable}; use std::cell::Ref; use std::ops::Deref; @@ -9,28 +9,38 @@ use std::ops::Deref; /// because relations have no "recent" tuples, so the fn would be a /// guaranteed no-op if both arguments were relations. See also /// `join_into_relation`. -pub(crate) fn join_into<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: Ord>( - input1: &Variable<(Key, Val1)>, - input2: impl JoinInput<'me, (Key, Val2)>, - output: &Variable, - mut logic: impl FnMut(&Key, &Val1, &Val2) -> Result, -) { +pub(crate) fn join_into<'me, P, A, B, O>( + input1: &Variable, + input2: impl JoinInput<'me, B>, + output: &Variable, + mut logic: impl FnMut(P, A::Suffix, B::Suffix) -> O, +) where + P: Ord, + A: Copy + Split

, + B: Copy + Split

, + O: Ord, +{ let mut results = Vec::new(); - let push_result = |k: &Key, v1: &Val1, v2: &Val2| results.push(logic(k, v1, v2)); + let push_result = |k, v1, v2| results.push(logic(k, v1, v2)); join_delta(input1, input2, push_result); output.insert(Relation::from_vec(results)); } -pub(crate) fn join_and_filter_into<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: Ord>( - input1: &Variable<(Key, Val1)>, - input2: impl JoinInput<'me, (Key, Val2)>, - output: &Variable, - mut logic: impl FnMut(&Key, &Val1, &Val2) -> Option, -) { +pub(crate) fn join_and_filter_into<'me, P, A, B, O>( + input1: &Variable, + input2: impl JoinInput<'me, B>, + output: &Variable, + mut logic: impl FnMut(P, A::Suffix, B::Suffix) -> Option, +) where + P: Ord, + A: Copy + Split

, + B: Copy + Split

, + O: Ord, +{ let mut results = Vec::new(); - let push_result = |k: &Key, v1: &Val1, v2: &Val2| { + let push_result = |k, v1, v2| { if let Some(result) = logic(k, v1, v2) { results.push(result); } @@ -43,11 +53,15 @@ pub(crate) fn join_and_filter_into<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: /// Joins the `recent` tuples of each input with the `stable` tuples of the other, then the /// `recent` tuples of *both* inputs. -fn join_delta<'me, Key: Ord, Val1: Ord, Val2: Ord>( - input1: &Variable<(Key, Val1)>, - input2: impl JoinInput<'me, (Key, Val2)>, - mut result: impl FnMut(&Key, &Val1, &Val2), -) { +fn join_delta<'me, P, A, B>( + input1: &Variable, + input2: impl JoinInput<'me, B>, + mut result: impl FnMut(P, A::Suffix, B::Suffix), +) where + P: Ord, + A: Copy + Split

, + B: Copy + Split

, +{ let recent1 = input1.recent(); let recent2 = input2.recent(); @@ -63,11 +77,17 @@ fn join_delta<'me, Key: Ord, Val1: Ord, Val2: Ord>( } /// Join, but for two relations. -pub(crate) fn join_into_relation<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: Ord>( - input1: &Relation<(Key, Val1)>, - input2: &Relation<(Key, Val2)>, - mut logic: impl FnMut(&Key, &Val1, &Val2) -> Result, -) -> Relation { +pub(crate) fn join_into_relation( + input1: &Relation, + input2: &Relation, + mut logic: impl FnMut(P, A::Suffix, B::Suffix) -> O, +) -> Relation +where + P: Ord, + A: Copy + Split

, + B: Copy + Split

, + O: Ord, +{ let mut results = Vec::new(); join_helper(&input1.elements, &input2.elements, |k, v1, v2| { @@ -78,48 +98,57 @@ pub(crate) fn join_into_relation<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: Or } /// Moves all recent tuples from `input1` that are not present in `input2` into `output`. -pub(crate) fn antijoin( - input1: &Relation<(Key, Val)>, - input2: &Relation, - mut logic: impl FnMut(&Key, &Val) -> Result, -) -> Relation { +pub(crate) fn antijoin( + input1: &Relation, + input2: &Relation

, + mut logic: impl FnMut(A) -> O, +) -> Relation +where + A: Copy + Split

, + P: Ord, + O: Ord, +{ let mut tuples2 = &input2[..]; let results = input1 .elements .iter() - .filter(|(ref key, _)| { - tuples2 = gallop(tuples2, |k| k < key); - tuples2.first() != Some(key) + .filter(|el| { + tuples2 = gallop(tuples2, |p| p < &el.prefix()); + tuples2.first() != Some(&el.prefix()) }) - .map(|(ref key, ref val)| logic(key, val)) + .map(|&el| logic(el)) .collect::>(); Relation::from_vec(results) } -fn join_helper( - mut slice1: &[(K, V1)], - mut slice2: &[(K, V2)], - mut result: impl FnMut(&K, &V1, &V2), -) { +fn join_helper( + mut slice1: &[A], + mut slice2: &[B], + mut result: impl FnMut(P, A::Suffix, B::Suffix), +) where + A: Copy + Split

, + B: Copy + Split

, + P: Ord, +{ while !slice1.is_empty() && !slice2.is_empty() { use std::cmp::Ordering; // If the keys match produce tuples, else advance the smaller key until they might. - match slice1[0].0.cmp(&slice2[0].0) { + match slice1[0].prefix().cmp(&slice2[0].prefix()) { Ordering::Less => { - slice1 = gallop(slice1, |x| x.0 < slice2[0].0); + slice1 = gallop(slice1, |x| x.prefix() < slice2[0].prefix()); } Ordering::Equal => { // Determine the number of matching keys in each slice. - let count1 = slice1.iter().take_while(|x| x.0 == slice1[0].0).count(); - let count2 = slice2.iter().take_while(|x| x.0 == slice2[0].0).count(); + let count1 = slice1.iter().take_while(|x| x.prefix() == slice1[0].prefix()).count(); + let count2 = slice2.iter().take_while(|x| x.prefix() == slice2[0].prefix()).count(); // Produce results from the cross-product of matches. for index1 in 0..count1 { for s2 in slice2[..count2].iter() { - result(&slice1[0].0, &slice1[index1].1, &s2.1); + result(slice1[0].prefix(), slice1[index1].suffix(), s2.suffix()); } } @@ -128,7 +157,7 @@ fn join_helper( slice2 = &slice2[count2..]; } Ordering::Greater => { - slice2 = gallop(slice2, |x| x.0 < slice1[0].0); + slice2 = gallop(slice2, |x| x.prefix() < slice1[0].prefix()); } } } @@ -158,7 +187,7 @@ pub(crate) fn gallop(mut slice: &[T], mut cmp: impl FnMut(&T) -> bool) -> &[T } /// An input that can be used with `from_join`; either a `Variable` or a `Relation`. -pub trait JoinInput<'me, Tuple: Ord>: Copy { +pub trait JoinInput<'me, Tuple>: Copy { /// If we are on iteration N of the loop, these are the tuples /// added on iteration N-1. (For a `Relation`, this is always an /// empty slice.) @@ -171,7 +200,7 @@ pub trait JoinInput<'me, Tuple: Ord>: Copy { fn for_each_stable_set(self, f: impl FnMut(&[Tuple])); } -impl<'me, Tuple: Ord> JoinInput<'me, Tuple> for &'me Variable { +impl<'me, Tuple> JoinInput<'me, Tuple> for &'me Variable { type RecentTuples = Ref<'me, [Tuple]>; fn recent(self) -> Self::RecentTuples { @@ -185,7 +214,7 @@ impl<'me, Tuple: Ord> JoinInput<'me, Tuple> for &'me Variable { } } -impl<'me, Tuple: Ord> JoinInput<'me, Tuple> for &'me Relation { +impl<'me, Tuple> JoinInput<'me, Tuple> for &'me Relation { type RecentTuples = &'me [Tuple]; fn recent(self) -> Self::RecentTuples { @@ -196,29 +225,3 @@ impl<'me, Tuple: Ord> JoinInput<'me, Tuple> for &'me Relation { f(&self.elements) } } - -impl<'me, Tuple: Ord> JoinInput<'me, (Tuple, ())> for &'me Relation { - type RecentTuples = &'me [(Tuple, ())]; - - fn recent(self) -> Self::RecentTuples { - &[] - } - - fn for_each_stable_set(self, mut f: impl FnMut(&[(Tuple, ())])) { - use std::mem; - assert_eq!(mem::size_of::<(Tuple, ())>(), mem::size_of::()); - assert_eq!(mem::align_of::<(Tuple, ())>(), mem::align_of::()); - - // SAFETY: https://rust-lang.github.io/unsafe-code-guidelines/layout/structs-and-tuples.html#structs-with-1-zst-fields - // guarantees that `T` is layout compatible with `(T, ())`, since `()` is a 1-ZST. We use - // `slice::from_raw_parts` because the layout compatibility guarantee does not extend to - // containers like `&[T]`. - let elements: &'me [Tuple] = self.elements.as_slice(); - let len = elements.len(); - - let elements: &'me [(Tuple, ())] = - unsafe { std::slice::from_raw_parts(elements.as_ptr() as *const _, len) }; - - f(elements) - } -} diff --git a/src/lib.rs b/src/lib.rs index 682a7df..eae6be7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ mod iteration; mod join; mod map; mod merge; +mod prefix; mod relation; mod test; mod treefrog; @@ -23,6 +24,7 @@ mod variable; pub use crate::{ iteration::Iteration, join::JoinInput, + prefix::Split, relation::Relation, treefrog::{ extend_anti::ExtendAnti, @@ -30,7 +32,7 @@ pub use crate::{ filter_anti::FilterAnti, filter_with::FilterWith, filters::{passthrough, PrefixFilter, ValueFilter}, - Leaper, Leapers, RelationLeaper, + Leaper, Leapers, }, variable::Variable, }; diff --git a/src/prefix.rs b/src/prefix.rs new file mode 100644 index 0000000..a961e47 --- /dev/null +++ b/src/prefix.rs @@ -0,0 +1,101 @@ +/// Trait for splitting a tuple into two at some index. +/// +/// `P` is a tuple containing only the desired fields prior to the index. For some type `(A, B)`, +/// `(A, B)`, `(A,)`, and `()` are all valid choices for `P`, but `(B,)` is not. `A` is also not a +/// valid prefix, since `P` must be a tuple. +pub trait Split

: Sized { + /// The remaining fields after the input has been split. + /// + /// As a convenience, we unwrap unary tuples, so `<(A, B) as Split<(A,)>::Suffix` is `B`, + /// **not** `(B,)`. We cannot do this for `P` itself though, due to overlapping impls. + type Suffix: Sized; + + /// Splits a tuple into a prefix and a suffix. + fn split(self) -> (P, Self::Suffix); + + /// Returns the prefix of the tuple. + fn prefix(self) -> P { + self.split().0 + } + + /// Returns the suffix of the tuple. + fn suffix(self) -> Self::Suffix { + self.split().1 + } +} + +macro_rules! fwd { + () => { () }; + ($T:ident) => { $T }; + ($($T:ident)*) => { ($($T),*) }; +} + +macro_rules! rev { + ($($T:ident)*) => { + rev!(@REV [$($T)*]) + }; + (@REV [$H:ident $($T:ident)*] $($R:ident)*) => { + rev!(@REV [$($T)*] $H $($R)*) + }; + (@REV [$H:ident] $($R:ident)*) => { + rev!(@REV [] $H $($R)*) + }; + (@REV [] $($R:ident)*) => { ($($R,)*) }; +} + +macro_rules! impls { + ($($t:ident)*) => { + impls!(@outer [$($t)*]); + }; + + (@outer [$h:ident $($t:ident)+]) => { + impls!(@inner [$h $($t)*] [$h $($t)*] []); + impls!(@outer [$($t)*]); + }; + (@outer [$h:ident]) => { + impls!(@inner [$h] [$h] []); + impls!(@outer []); + }; + (@outer []) => {}; + + (@inner [$($t:ident)+] [$p:ident $($pt:ident)*] [$($r:ident)*]) => { + impls!(@imp [$($t)*] [$p $($pt)*] [$($r)*]); + impls!(@inner [$($t)*] [$($pt)*] [$p $($r)*]); + }; + (@inner [$($t:ident)+] [$p:ident] [$($r:ident)*]) => { + impls!(@imp [$($t)*] [$p] [$($r)*]); + impls!(@inner [$($t)*] [] [$p $($r)*]); + }; + (@inner [$($t:ident)+] [] [$($r:ident)*]) => { + impls!(@imp [$($t)*] [] [$($r)*]); + }; + + (@imp [$($t:ident)?] [$($p:ident)*] [$($r:ident)*]) => {}; + + (@imp [$($t:ident)+] [$($p:ident)*] [$($r:ident)*]) => { + impl<$($t),*> Split for rev!($($t)*) { + type Suffix = fwd!($($r)*); + + fn split(self) -> (rev!($($p)*), Self::Suffix) { + let rev!($($t)*) = self; + (rev!($($p)*), fwd!($($r)*)) + } + } + }; + (@imp [$($t:ident),+] [] []) => { + impl<$($t),*> Split<()> for rev!($($t),*) { + type Suffix = Self; + + fn split(self) -> ((), Self) { + ((), self) + } + } + }; +} + +#[allow(non_snake_case)] +mod impls { + use super::*; + + impls!(F E D C B A); +} diff --git a/src/relation.rs b/src/relation.rs index b671fe0..60e4e00 100644 --- a/src/relation.rs +++ b/src/relation.rs @@ -4,6 +4,7 @@ use crate::{ join, merge, treefrog::{self, Leapers}, + Split, }; /// A static, ordered list of key-value pairs. @@ -11,8 +12,8 @@ use crate::{ /// A relation represents a fixed set of key-value pairs. In many places in a /// Datalog computation we want to be sure that certain relations are not able /// to vary (for example, in antijoins). -#[derive(Clone)] -pub struct Relation { +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Relation { /// Sorted list of distinct tuples. pub elements: Vec, } @@ -36,9 +37,9 @@ impl Relation { /// Creates a `Relation` using the `leapjoin` logic; /// see [`Variable::from_leapjoin`] - pub fn from_leapjoin<'leap, SourceTuple: Ord, Val: Ord + 'leap>( + pub fn from_leapjoin( source: &Relation, - leapers: impl Leapers<'leap, SourceTuple, Val>, + leapers: impl Leapers, logic: impl FnMut(&SourceTuple, &Val) -> Tuple, ) -> Self { treefrog::leapjoin(&source.elements, leapers, logic) @@ -48,24 +49,52 @@ impl Relation { /// `input2` and then applying `logic`. Like /// [`Variable::from_join`] except for use where the inputs are /// not varying across iterations. - pub fn from_join( - input1: &Relation<(Key, Val1)>, - input2: &Relation<(Key, Val2)>, - logic: impl FnMut(&Key, &Val1, &Val2) -> Tuple, - ) -> Self { + pub fn from_join( + input1: &Relation, + input2: &Relation, + logic: impl FnMut(P, A::Suffix, B::Suffix) -> Tuple, + ) -> Self + where + P: Ord, + A: Copy + Split

, + B: Copy + Split

, + { join::join_into_relation(input1, input2, logic) } + /// An small wrapper around [`Relation::from_join`] that uses the first element of `A` and `B` + /// as the shared prefix. + /// + /// This is useful because `Split` needs a tuple, and working with 1-tuples is a pain. + /// It can also help with inference in cases where `logic` does not make use of the shared + /// prefix. + pub fn from_join_first( + input1: &Relation, + input2: &Relation, + mut logic: impl FnMut(P, A::Suffix, B::Suffix) -> Tuple, + ) -> Self + where + P: Ord, + A: Copy + Split<(P,)>, + B: Copy + Split<(P,)>, + { + join::join_into_relation(input1, input2, |(p,), a, b| logic(p, a, b)) + } + /// Creates a `Relation` by removing all values from `input1` that /// share a key with `input2`, and then transforming the resulting /// tuples with the `logic` closure. Like /// [`Variable::from_antijoin`] except for use where the inputs /// are not varying across iterations. - pub fn from_antijoin( - input1: &Relation<(Key, Val1)>, - input2: &Relation, - logic: impl FnMut(&Key, &Val1) -> Tuple, - ) -> Self { + pub fn from_antijoin( + input1: &Relation, + input2: &Relation

, + logic: impl FnMut(A) -> Tuple, + ) -> Self + where + P: Ord, + A: Copy + Split

+ { join::antijoin(input1, input2, logic) } @@ -99,7 +128,7 @@ impl FromIterator for Relation { } } -impl<'tuple, Tuple: 'tuple + Copy + Ord> FromIterator<&'tuple Tuple> for Relation { +impl<'tuple, Tuple: 'tuple + Clone + Ord> FromIterator<&'tuple Tuple> for Relation { fn from_iter(iterator: I) -> Self where I: IntoIterator, @@ -108,7 +137,7 @@ impl<'tuple, Tuple: 'tuple + Copy + Ord> FromIterator<&'tuple Tuple> for Relatio } } -impl std::ops::Deref for Relation { +impl std::ops::Deref for Relation { type Target = [Tuple]; fn deref(&self) -> &Self::Target { &self.elements[..] diff --git a/src/test.rs b/src/test.rs index 381468b..c729e17 100644 --- a/src/test.rs +++ b/src/test.rs @@ -2,7 +2,6 @@ use crate::Iteration; use crate::Relation; -use crate::RelationLeaper; use proptest::prelude::*; use proptest::{proptest, proptest_helper}; @@ -12,7 +11,7 @@ fn inputs() -> impl Strategy> { /// The original way to use datafrog -- computes reachable nodes from a set of edges fn reachable_with_var_join(edges: &[(u32, u32)]) -> Relation<(u32, u32)> { - let edges: Relation<_> = edges.iter().collect(); + let edges: Relation<(u32, u32)> = edges.iter().collect(); let mut iteration = Iteration::new(); let edges_by_successor = iteration.variable::<(u32, u32)>("edges_invert"); @@ -23,7 +22,7 @@ fn reachable_with_var_join(edges: &[(u32, u32)]) -> Relation<(u32, u32)> { while iteration.changed() { // reachable(N1, N3) :- edges(N1, N2), reachable(N2, N3). - reachable.from_join(&reachable, &edges_by_successor, |&_, &n3, &n1| (n1, n3)); + reachable.from_join_first(&reachable, &edges_by_successor, |_, n3, n1| (n1, n3)); } reachable.complete() @@ -31,7 +30,7 @@ fn reachable_with_var_join(edges: &[(u32, u32)]) -> Relation<(u32, u32)> { /// Like `reachable`, but using a relation as an input to `from_join` fn reachable_with_relation_join(edges: &[(u32, u32)]) -> Relation<(u32, u32)> { - let edges: Relation<_> = edges.iter().collect(); + let edges: Relation<(u32, u32)> = edges.iter().collect(); let mut iteration = Iteration::new(); // NB. Changed from `reachable_with_var_join`: @@ -42,7 +41,7 @@ fn reachable_with_relation_join(edges: &[(u32, u32)]) -> Relation<(u32, u32)> { while iteration.changed() { // reachable(N1, N3) :- edges(N1, N2), reachable(N2, N3). - reachable.from_join(&reachable, &edges_by_successor, |&_, &n3, &n1| (n1, n3)); + reachable.from_join_first(&reachable, &edges_by_successor, |_, n3, n1| (n1, n3)); } reachable.complete() @@ -61,7 +60,7 @@ fn reachable_with_leapfrog(edges: &[(u32, u32)]) -> Relation<(u32, u32)> { // reachable(N1, N3) :- edges(N1, N2), reachable(N2, N3). reachable.from_leapjoin( &reachable, - edges_by_successor.extend_with(|&(n2, _)| n2), + edges_by_successor.extend_with(|&(n2, _)| (n2,)), |&(_, n3), &n1| (n1, n3), ); } @@ -87,7 +86,7 @@ fn sum_join_via_var( while iteration.changed() { // output(K1, V1 * 100 + V2) :- input1(K1, V1), input2(K1, V2). - output.from_join(&input1, &input2, |&k1, &v1, &v2| (k1, v1 * 100 + v2)); + output.from_join_first(&input1, &input2, |k1, v1, v2| (k1, v1 * 100 + v2)); } output.complete() @@ -99,9 +98,9 @@ fn sum_join_via_relation( input1_slice: &[(u32, u32)], input2_slice: &[(u32, u32)], ) -> Relation<(u32, u32)> { - let input1: Relation<_> = input1_slice.iter().collect(); - let input2: Relation<_> = input2_slice.iter().collect(); - Relation::from_join(&input1, &input2, |&k1, &v1, &v2| (k1, v1 * 100 + v2)) + let input1: Relation<(u32, u32)> = input1_slice.iter().collect(); + let input2: Relation<(u32, u32)> = input2_slice.iter().collect(); + Relation::from_join_first(&input1, &input2, |k1, v1, v2| (k1, v1 * 100 + v2)) } proptest! { @@ -184,7 +183,7 @@ fn leapjoin_from_extend() { while iteration.changed() { variable.from_leapjoin( &variable, - doubles.extend_with(|&(i, _)| i), + doubles.extend_with(|&(i, _)| (i,)), |&(i, _), &j| (i, j), ); } @@ -222,11 +221,11 @@ fn passthrough_leaper() { #[test] fn relation_from_antijoin() { - let lhs: Relation<_> = (0 .. 10).map(|x| (x, x)).collect(); - let rhs: Relation<_> = (0 .. 10).filter(|x| x % 2 == 0).collect(); + let lhs: Relation<(u32, u32)> = (0 .. 10).map(|x| (x, x)).collect(); + let rhs: Relation<(u32,)> = (0 .. 10).filter(|x| x % 2 == 0).map(|x| (x,)).collect(); let expected: Relation<_> = (0 .. 10).filter(|x| x % 2 == 1).map(|x| (x, x)).collect(); - let result = Relation::from_antijoin(&lhs, &rhs, |a, b| (*a, *b)); + let result = Relation::from_antijoin(&lhs, &rhs, |x| x); assert_eq!(result.elements, expected.elements); } diff --git a/src/treefrog.rs b/src/treefrog.rs index e473436..baae016 100644 --- a/src/treefrog.rs +++ b/src/treefrog.rs @@ -3,9 +3,9 @@ use super::Relation; /// Performs treefrog leapjoin using a list of leapers. -pub(crate) fn leapjoin<'leap, Tuple: Ord, Val: Ord + 'leap, Result: Ord>( +pub(crate) fn leapjoin( source: &[Tuple], - mut leapers: impl Leapers<'leap, Tuple, Val>, + mut leapers: impl Leapers, mut logic: impl FnMut(&Tuple, &Val) -> Result, ) -> Relation { let mut result = Vec::new(); // temp output storage. @@ -36,7 +36,7 @@ pub(crate) fn leapjoin<'leap, Tuple: Ord, Val: Ord + 'leap, Result: Ord>( // Push remaining items into result. for val in values.drain(..) { - result.push(logic(tuple, val)); + result.push(logic(tuple, &val)); } } } @@ -45,23 +45,23 @@ pub(crate) fn leapjoin<'leap, Tuple: Ord, Val: Ord + 'leap, Result: Ord>( } /// Implemented for a tuple of leapers -pub trait Leapers<'leap, Tuple, Val> { +pub trait Leapers { /// Internal method: fn for_each_count(&mut self, tuple: &Tuple, op: impl FnMut(usize, usize)); /// Internal method: - fn propose(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec<&'leap Val>); + fn propose(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec); /// Internal method: - fn intersect(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec<&'leap Val>); + fn intersect(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec); } macro_rules! tuple_leapers { ($($Ty:ident)*) => { #[allow(unused_assignments, non_snake_case)] - impl<'leap, Tuple, Val, $($Ty),*> Leapers<'leap, Tuple, Val> for ($($Ty,)*) + impl Leapers for ($($Ty,)*) where - $($Ty: Leaper<'leap, Tuple, Val>,)* + $($Ty: Leaper,)* { fn for_each_count(&mut self, tuple: &Tuple, mut op: impl FnMut(usize, usize)) { let ($($Ty,)*) = self; @@ -73,7 +73,7 @@ macro_rules! tuple_leapers { )* } - fn propose(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec<&'leap Val>) { + fn propose(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec) { let ($($Ty,)*) = self; let mut index = 0; $( @@ -85,7 +85,7 @@ macro_rules! tuple_leapers { panic!("no match found for min_index={}", min_index); } - fn intersect(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec<&'leap Val>) { + fn intersect(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec) { let ($($Ty,)*) = self; let mut index = 0; $( @@ -107,13 +107,13 @@ tuple_leapers!(A B C D E F); tuple_leapers!(A B C D E F G); /// Methods to support treefrog leapjoin. -pub trait Leaper<'leap, Tuple, Val> { +pub trait Leaper { /// Estimates the number of proposed values. fn count(&mut self, prefix: &Tuple) -> usize; /// Populates `values` with proposed values. - fn propose(&mut self, prefix: &Tuple, values: &mut Vec<&'leap Val>); + fn propose(&mut self, prefix: &Tuple, values: &mut Vec); /// Restricts `values` to proposed values. - fn intersect(&mut self, prefix: &Tuple, values: &mut Vec<&'leap Val>); + fn intersect(&mut self, prefix: &Tuple, values: &mut Vec); } pub(crate) mod filters { @@ -131,7 +131,7 @@ pub(crate) mod filters { predicate: Func, } - impl<'leap, Tuple, Func> PrefixFilter + impl PrefixFilter where Func: Fn(&Tuple) -> bool, { @@ -144,7 +144,7 @@ pub(crate) mod filters { } } - impl<'leap, Tuple, Val, Func> Leaper<'leap, Tuple, Val> for PrefixFilter + impl Leaper for PrefixFilter where Func: Fn(&Tuple) -> bool, { @@ -157,21 +157,21 @@ pub(crate) mod filters { } } /// Populates `values` with proposed values. - fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val>) { + fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec) { panic!("PrefixFilter::propose(): variable apparently unbound"); } /// Restricts `values` to proposed values. - fn intersect(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val>) { + fn intersect(&mut self, _prefix: &Tuple, _values: &mut Vec) { // We can only be here if we returned max_value() above. } } - impl<'leap, Tuple, Func> Leapers<'leap, Tuple, ()> for PrefixFilter + impl Leapers for PrefixFilter where Func: Fn(&Tuple) -> bool, { fn for_each_count(&mut self, tuple: &Tuple, mut op: impl FnMut(usize, usize)) { - if >::count(self, tuple) == 0 { + if >::count(self, tuple) == 0 { op(0, 0) } else { // we will "propose" the `()` value if the predicate applies @@ -179,12 +179,12 @@ pub(crate) mod filters { } } - fn propose(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + fn propose(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<()>) { assert_eq!(min_index, 0); - values.push(&()); + values.push(()); } - fn intersect(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + fn intersect(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<()>) { assert_eq!(min_index, 0); assert_eq!(values.len(), 1); } @@ -202,17 +202,17 @@ pub(crate) mod filters { } } - impl<'leap, Tuple> Leaper<'leap, Tuple, ()> for Passthrough { + impl Leaper for Passthrough { /// Estimates the number of proposed values. fn count(&mut self, _prefix: &Tuple) -> usize { 1 } /// Populates `values` with proposed values. - fn propose(&mut self, _prefix: &Tuple, values: &mut Vec<&'leap ()>) { - values.push(&()) + fn propose(&mut self, _prefix: &Tuple, values: &mut Vec<()>) { + values.push(()) } /// Restricts `values` to proposed values. - fn intersect(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap ()>) { + fn intersect(&mut self, _prefix: &Tuple, _values: &mut Vec<()>) { // `Passthrough` never removes values (although if we're here it indicates that the user // didn't need a `Passthrough` in the first place) } @@ -250,7 +250,7 @@ pub(crate) mod filters { predicate: Func, } - impl<'leap, Tuple, Val, Func> ValueFilter + impl ValueFilter where Func: Fn(&Tuple, &Val) -> bool, { @@ -263,7 +263,7 @@ pub(crate) mod filters { } } - impl<'leap, Tuple, Val, Func> Leaper<'leap, Tuple, Val> for ValueFilter + impl Leaper for ValueFilter where Func: Fn(&Tuple, &Val) -> bool, { @@ -272,90 +272,82 @@ pub(crate) mod filters { usize::max_value() } /// Populates `values` with proposed values. - fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val>) { + fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec) { panic!("PrefixFilter::propose(): variable apparently unbound"); } /// Restricts `values` to proposed values. - fn intersect(&mut self, prefix: &Tuple, values: &mut Vec<&'leap Val>) { + fn intersect(&mut self, prefix: &Tuple, values: &mut Vec) { values.retain(|val| (self.predicate)(prefix, val)); } } } -/// Extension method for relations. -pub trait RelationLeaper { - /// Extend with `Val` using the elements of the relation. - fn extend_with<'leap, Tuple: Ord, Func: Fn(&Tuple) -> Key>( - &'leap self, - key_func: Func, - ) -> extend_with::ExtendWith<'leap, Key, Val, Tuple, Func> - where - Key: 'leap, - Val: 'leap; - /// Extend with `Val` using the complement of the relation. - fn extend_anti<'leap, Tuple: Ord, Func: Fn(&Tuple) -> Key>( - &'leap self, - key_func: Func, - ) -> extend_anti::ExtendAnti<'leap, Key, Val, Tuple, Func> - where - Key: 'leap, - Val: 'leap; - /// Extend with any value if tuple is present in relation. - fn filter_with<'leap, Tuple: Ord, Func: Fn(&Tuple) -> (Key, Val)>( - &'leap self, - key_func: Func, - ) -> filter_with::FilterWith<'leap, Key, Val, Tuple, Func> - where - Key: 'leap, - Val: 'leap; - /// Extend with any value if tuple is absent from relation. - fn filter_anti<'leap, Tuple: Ord, Func: Fn(&Tuple) -> (Key, Val)>( - &'leap self, - key_func: Func, - ) -> filter_anti::FilterAnti<'leap, Key, Val, Tuple, Func> - where - Key: 'leap, - Val: 'leap; -} - -impl RelationLeaper for Relation<(Key, Val)> { - fn extend_with<'leap, Tuple: Ord, Func: Fn(&Tuple) -> Key>( - &'leap self, - key_func: Func, - ) -> extend_with::ExtendWith<'leap, Key, Val, Tuple, Func> - where - Key: 'leap, - Val: 'leap, +impl Relation { + /// Extend with `>::Suffix` using the elements of the relation. + /// + /// This leaper proposes all tuples in `self` that have as a prefix the key extracted from the + /// source tuple via `key_func`. + /// + /// This leaper is analagous to a join: it finds all sets of tuples in the source and in + /// the underlying relation that have a shared prefix of type `P`, and for each shared prefix + /// generates the cartesian product of the two sets. + pub fn extend_with(&self, key_func: F) -> extend_with::ExtendWith<'_, P, T, F> + where F: Fn(&S) -> P // These bounds aren't necessary and could be deferred. + // They help with closure inference, however (see rust#41078). { extend_with::ExtendWith::from(self, key_func) } - fn extend_anti<'leap, Tuple: Ord, Func: Fn(&Tuple) -> Key>( - &'leap self, - key_func: Func, - ) -> extend_anti::ExtendAnti<'leap, Key, Val, Tuple, Func> - where - Key: 'leap, - Val: 'leap, + + /// Extend with `>::Suffix` using the complement of the relation. + /// + /// This leaper *removes* proposed values when + /// * `key_func(src)` matches the prefix (`P`) of a tuple in this relation, + /// * *AND* the proposed value matches the suffix of that same tuple. + /// + /// It is used when a negative atom depends on a variable that is proposed by another leaper. + /// For example: + /// + /// ```prolog + /// var_init_at(V, Q) :- + /// var_init_at(V, P), /* leapjoin source */ + /// cfg_edge(P, Q), /* extend_with */ + /// !var_moved_at(V, Q). /* extend_anti */ + /// ``` + /// + /// For each source tuple in `var_init_at`, `cfg_edge` will propose some number of CFG nodes + /// (`Q`). The `!var_moved_at` atom should be expressed as `extend_anti(|(v, _p)| v)`. That is, + /// it extracts `V` from the source tuple (the prefix), and eliminates proposed tuples with + /// that prefix whose suffix is `Q`. + /// + /// **FIXME:** The fact that `P` determines both the prefix (in the source) *and* the suffix (the + /// proposed value) is more restrictive than necessary. You could imagine a more complex program + /// where the proposed value contains more information than we need for the negative atom. + /// + /// ```prolog + /// x(A, B2, C2) :- + /// x(A, B1, C1), /* leapjoin source */ + /// t(B1, C1, B2, C2) /* Proposes `(B2, C2)` */ + /// !f(A, B2). /* Doesn't use `C2`! */ + /// ``` + /// + /// That would require a separate `val_func` (in addition to `key_func`) to extract the + /// relevant part of the proposed value. + pub fn extend_anti(&self, key_func: F) -> extend_anti::ExtendAnti<'_, P, T, F> + where F: Fn(&S) -> P { extend_anti::ExtendAnti::from(self, key_func) } - fn filter_with<'leap, Tuple: Ord, Func: Fn(&Tuple) -> (Key, Val)>( - &'leap self, - key_func: Func, - ) -> filter_with::FilterWith<'leap, Key, Val, Tuple, Func> - where - Key: 'leap, - Val: 'leap, + + /// Extend with any value if tuple is present in relation. + pub fn filter_with(&self, key_func: F) -> filter_with::FilterWith<'_, T, F> + where F: Fn(&S) -> T { filter_with::FilterWith::from(self, key_func) } - fn filter_anti<'leap, Tuple: Ord, Func: Fn(&Tuple) -> (Key, Val)>( - &'leap self, - key_func: Func, - ) -> filter_anti::FilterAnti<'leap, Key, Val, Tuple, Func> - where - Key: 'leap, - Val: 'leap, + + /// Extend with any value if tuple is absent from relation. + pub fn filter_anti(&self, key_func: F) -> filter_anti::FilterAnti<'_, T, F> + where F: Fn(&S) -> T { filter_anti::FilterAnti::from(self, key_func) } @@ -364,57 +356,38 @@ impl RelationLeaper for Relation<(Key, Val)> { pub(crate) mod extend_with { use super::{binary_search, Leaper, Leapers, Relation}; use crate::join::gallop; + use crate::Split; - /// Wraps a Relation as a leaper. - pub struct ExtendWith<'leap, Key, Val, Tuple, Func> - where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> Key, - { - relation: &'leap Relation<(Key, Val)>, + /// Wraps a `Relation` as a leaper that proposes all values who have as a prefix the key + /// extracted from the source tuple. + pub struct ExtendWith<'a, P, T, F> { + relation: &'a Relation, start: usize, end: usize, - key_func: Func, - old_key: Option, - phantom: ::std::marker::PhantomData, + old_key: Option

, + key_func: F, } - impl<'leap, Key, Val, Tuple, Func> ExtendWith<'leap, Key, Val, Tuple, Func> - where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> Key, - { - /// Constructs a ExtendWith from a relation and key and value function. - pub fn from(relation: &'leap Relation<(Key, Val)>, key_func: Func) -> Self { - ExtendWith { - relation, - start: 0, - end: 0, - key_func, - old_key: None, - phantom: ::std::marker::PhantomData, - } + impl<'a, P, T, F> ExtendWith<'a, P, T, F> { + /// Constructs an `ExtendWith` from a `Relation` and a key function. + pub fn from(relation: &'a Relation, key_func: F) -> Self { + ExtendWith { relation, start: 0, end: 0, old_key: None, key_func } } } - impl<'leap, Key, Val, Tuple, Func> Leaper<'leap, Tuple, Val> - for ExtendWith<'leap, Key, Val, Tuple, Func> + impl Leaper for ExtendWith<'_, P, T, F> where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> Key, + T: Copy + Split

, + P: Ord, + T::Suffix: Ord, + F: Fn(&S) -> P, { - fn count(&mut self, prefix: &Tuple) -> usize { - let key = (self.key_func)(prefix); + fn count(&mut self, src: &S) -> usize { + let key = (self.key_func)(src); if self.old_key.as_ref() != Some(&key) { - self.start = binary_search(&self.relation.elements, |x| &x.0 < &key); + self.start = binary_search(&self.relation.elements, |x| &x.prefix() < &key); let slice1 = &self.relation[self.start..]; - let slice2 = gallop(slice1, |x| &x.0 <= &key); + let slice2 = gallop(slice1, |x| &x.prefix() <= &key); self.end = self.relation.len() - slice2.len(); self.old_key = Some(key); @@ -422,37 +395,36 @@ pub(crate) mod extend_with { self.end - self.start } - fn propose(&mut self, _prefix: &Tuple, values: &mut Vec<&'leap Val>) { + + fn propose(&mut self, _src: &S, values: &mut Vec) { let slice = &self.relation[self.start..self.end]; - values.extend(slice.iter().map(|&(_, ref val)| val)); + values.extend(slice.iter().map(|val| val.suffix())); } - fn intersect(&mut self, _prefix: &Tuple, values: &mut Vec<&'leap Val>) { + + fn intersect(&mut self, _src: &S, values: &mut Vec) { let mut slice = &self.relation[self.start..self.end]; values.retain(|v| { - slice = gallop(slice, |kv| &kv.1 < v); - slice.get(0).map(|kv| &kv.1) == Some(v) + slice = gallop(slice, |kv| &kv.suffix() < v); + slice.get(0).map(|kv| kv.suffix()).as_ref() == Some(v) }); } } - impl<'leap, Key, Val, Tuple, Func> Leapers<'leap, Tuple, Val> - for ExtendWith<'leap, Key, Val, Tuple, Func> + impl Leapers for ExtendWith<'_, P, T, F> where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> Key, + T: Split

, + Self: Leaper, { - fn for_each_count(&mut self, tuple: &Tuple, mut op: impl FnMut(usize, usize)) { + fn for_each_count(&mut self, tuple: &S, mut op: impl FnMut(usize, usize)) { op(0, self.count(tuple)) } - fn propose(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec<&'leap Val>) { + fn propose(&mut self, tuple: &S, min_index: usize, values: &mut Vec) { assert_eq!(min_index, 0); Leaper::propose(self, tuple, values); } - fn intersect(&mut self, _: &Tuple, min_index: usize, _: &mut Vec<&'leap Val>) { + fn intersect(&mut self, _: &S, min_index: usize, _: &mut Vec) { assert_eq!(min_index, 0); } } @@ -463,63 +435,45 @@ pub(crate) mod extend_anti { use super::{binary_search, Leaper, Relation}; use crate::join::gallop; + use crate::Split; /// Wraps a Relation as a leaper. - pub struct ExtendAnti<'leap, Key, Val, Tuple, Func> - where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> Key, - { - relation: &'leap Relation<(Key, Val)>, - key_func: Func, - old_key: Option<(Key, Range)>, - phantom: ::std::marker::PhantomData, + pub struct ExtendAnti<'a, P, T, F> { + relation: &'a Relation, + key_func: F, + old_key: Option<(P, Range)>, } - impl<'leap, Key, Val, Tuple, Func> ExtendAnti<'leap, Key, Val, Tuple, Func> - where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> Key, - { + impl<'a, P, T, F> ExtendAnti<'a, P, T, F> { /// Constructs a ExtendAnti from a relation and key and value function. - pub fn from(relation: &'leap Relation<(Key, Val)>, key_func: Func) -> Self { - ExtendAnti { - relation, - key_func, - old_key: None, - phantom: ::std::marker::PhantomData, - } + pub fn from(relation: &'a Relation, key_func: F) -> Self { + ExtendAnti { relation, key_func, old_key: None } } } - impl<'leap, Key: Ord, Val: Ord + 'leap, Tuple: Ord, Func> Leaper<'leap, Tuple, Val> - for ExtendAnti<'leap, Key, Val, Tuple, Func> + impl Leaper for ExtendAnti<'_, P, T, F> where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> Key, + T: Copy + Split

, + P: Ord, + T::Suffix: Ord, + F: Fn(&S) -> P, { - fn count(&mut self, _prefix: &Tuple) -> usize { + fn count(&mut self, _prefix: &S) -> usize { usize::max_value() } - fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val>) { + fn propose(&mut self, _prefix: &S, _values: &mut Vec) { panic!("ExtendAnti::propose(): variable apparently unbound."); } - fn intersect(&mut self, prefix: &Tuple, values: &mut Vec<&'leap Val>) { + fn intersect(&mut self, prefix: &S, values: &mut Vec) { let key = (self.key_func)(prefix); let range = match self.old_key.as_ref() { Some((old, range)) if old == &key => range.clone(), _ => { - let start = binary_search(&self.relation.elements, |x| &x.0 < &key); + let start = binary_search(&self.relation.elements, |x| &x.prefix() < &key); let slice1 = &self.relation[start..]; - let slice2 = gallop(slice1, |x| &x.0 <= &key); + let slice2 = gallop(slice1, |x| &x.prefix() <= &key); let range = start..self.relation.len()-slice2.len(); self.old_key = Some((key, range.clone())); @@ -531,8 +485,8 @@ pub(crate) mod extend_anti { let mut slice = &self.relation[range]; if !slice.is_empty() { values.retain(|v| { - slice = gallop(slice, |kv| &kv.1 < v); - slice.get(0).map(|kv| &kv.1) != Some(v) + slice = gallop(slice, |kv| &kv.suffix() < v); + slice.get(0).map(|kv| kv.suffix()).as_ref() != Some(v) }); } } @@ -544,46 +498,26 @@ pub(crate) mod filter_with { use super::{Leaper, Leapers, Relation}; /// Wraps a Relation as a leaper. - pub struct FilterWith<'leap, Key, Val, Tuple, Func> - where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> (Key, Val), - { - relation: &'leap Relation<(Key, Val)>, - key_func: Func, - old_key_val: Option<((Key, Val), bool)>, - phantom: ::std::marker::PhantomData, + pub struct FilterWith<'a, T, F> { + relation: &'a Relation, + key_func: F, + old_key_val: Option<(T, bool)>, } - impl<'leap, Key, Val, Tuple, Func> FilterWith<'leap, Key, Val, Tuple, Func> - where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> (Key, Val), - { + impl<'a, T, F> FilterWith<'a, T, F> { /// Constructs a FilterWith from a relation and key and value function. - pub fn from(relation: &'leap Relation<(Key, Val)>, key_func: Func) -> Self { - FilterWith { - relation, - key_func, - old_key_val: None, - phantom: ::std::marker::PhantomData, - } + pub fn from(relation: &'a Relation, key_func: F) -> Self { + FilterWith { relation, key_func, old_key_val: None } } } - impl<'leap, Key, Val, Val2, Tuple, Func> Leaper<'leap, Tuple, Val2> - for FilterWith<'leap, Key, Val, Tuple, Func> + impl Leaper for FilterWith<'_, T, F> where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> (Key, Val), + T: Ord, + S: Ord, + F: Fn(&S) -> T, { - fn count(&mut self, prefix: &Tuple) -> usize { + fn count(&mut self, prefix: &S) -> usize { let key_val = (self.key_func)(prefix); if let Some((ref old_key_val, is_present)) = self.old_key_val { @@ -596,36 +530,32 @@ pub(crate) mod filter_with { self.old_key_val = Some((key_val, is_present)); if is_present { usize::MAX } else { 0 } } - fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val2>) { + fn propose(&mut self, _prefix: &S, _values: &mut Vec) { panic!("FilterWith::propose(): variable apparently unbound."); } - fn intersect(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val2>) { + fn intersect(&mut self, _prefix: &S, _values: &mut Vec) { // Only here because we didn't return zero above, right? } } - impl<'leap, Key, Val, Tuple, Func> Leapers<'leap, Tuple, ()> - for FilterWith<'leap, Key, Val, Tuple, Func> + impl Leapers for FilterWith<'_, T, F> where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> (Key, Val), + Self: Leaper, { - fn for_each_count(&mut self, tuple: &Tuple, mut op: impl FnMut(usize, usize)) { - if >::count(self, tuple) == 0 { + fn for_each_count(&mut self, tuple: &S, mut op: impl FnMut(usize, usize)) { + if >::count(self, tuple) == 0 { op(0, 0) } else { op(0, 1) } } - fn propose(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + fn propose(&mut self, _: &S, min_index: usize, values: &mut Vec<()>) { assert_eq!(min_index, 0); - values.push(&()); + values.push(()); } - fn intersect(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + fn intersect(&mut self, _: &S, min_index: usize, values: &mut Vec<()>) { assert_eq!(min_index, 0); assert_eq!(values.len(), 1); } @@ -633,50 +563,33 @@ pub(crate) mod filter_with { } pub(crate) mod filter_anti { - use super::{Leaper, Leapers, Relation}; /// Wraps a Relation as a leaper. - pub struct FilterAnti<'leap, Key, Val, Tuple, Func> - where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> (Key, Val), - { - relation: &'leap Relation<(Key, Val)>, - key_func: Func, - old_key_val: Option<((Key, Val), bool)>, - phantom: ::std::marker::PhantomData, + pub struct FilterAnti<'a, T, F> { + relation: &'a Relation, + key_func: F, + old_key_val: Option<(T, bool)>, } - impl<'leap, Key, Val, Tuple, Func> FilterAnti<'leap, Key, Val, Tuple, Func> - where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> (Key, Val), - { + impl<'a, T, F> FilterAnti<'a, T, F> { /// Constructs a FilterAnti from a relation and key and value function. - pub fn from(relation: &'leap Relation<(Key, Val)>, key_func: Func) -> Self { + pub fn from(relation: &'a Relation, key_func: F) -> Self { FilterAnti { relation, key_func, old_key_val: None, - phantom: ::std::marker::PhantomData, } } } - impl<'leap, Key: Ord, Val: Ord + 'leap, Val2, Tuple: Ord, Func> Leaper<'leap, Tuple, Val2> - for FilterAnti<'leap, Key, Val, Tuple, Func> + impl Leaper for FilterAnti<'_, T, F> where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> (Key, Val), + T: Ord, + S: Ord, + F: Fn(&S) -> T, { - fn count(&mut self, prefix: &Tuple) -> usize { + fn count(&mut self, prefix: &S) -> usize { let key_val = (self.key_func)(prefix); if let Some((ref old_key_val, is_present)) = self.old_key_val { @@ -689,37 +602,33 @@ pub(crate) mod filter_anti { self.old_key_val = Some((key_val, is_present)); if is_present { 0 } else { usize::MAX } } - fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val2>) { + fn propose(&mut self, _prefix: &S, _values: &mut Vec) { panic!("FilterAnti::propose(): variable apparently unbound."); } - fn intersect(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val2>) { + fn intersect(&mut self, _prefix: &S, _values: &mut Vec) { // Only here because we didn't return zero above, right? } } - impl<'leap, Key, Val, Tuple, Func> Leapers<'leap, Tuple, ()> - for FilterAnti<'leap, Key, Val, Tuple, Func> + impl Leapers for FilterAnti<'_, T, F> where - Key: Ord + 'leap, - Val: Ord + 'leap, - Tuple: Ord, - Func: Fn(&Tuple) -> (Key, Val), + Self: Leaper, { - fn for_each_count(&mut self, tuple: &Tuple, mut op: impl FnMut(usize, usize)) { - if >::count(self, tuple) == 0 { + fn for_each_count(&mut self, tuple: &S, mut op: impl FnMut(usize, usize)) { + if >::count(self, tuple) == 0 { op(0, 0) } else { op(0, 1) } } - fn propose(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + fn propose(&mut self, _: &S, min_index: usize, values: &mut Vec<()>) { // We only get here if `tuple` is *not* a member of `self.relation` assert_eq!(min_index, 0); - values.push(&()); + values.push(()); } - fn intersect(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + fn intersect(&mut self, _: &S, min_index: usize, values: &mut Vec<()>) { // We only get here if `tuple` is not a member of `self.relation` assert_eq!(min_index, 0); assert_eq!(values.len(), 1); diff --git a/src/variable.rs b/src/variable.rs index 4950488..6e88025 100644 --- a/src/variable.rs +++ b/src/variable.rs @@ -8,6 +8,7 @@ use crate::{ map, relation::Relation, treefrog::{self, Leapers}, + Split, }; /// A type that can report on whether it has changed. @@ -36,7 +37,7 @@ pub(crate) trait VariableTrait { /// of performance. Such a variable cannot be relied on to terminate iterative computation, /// and it is important that any cycle of derivations have at least one de-duplicating /// variable on it. -pub struct Variable { +pub struct Variable { /// Should the variable be maintained distinctly. pub(crate) distinct: bool, /// A useful name for the variable. @@ -83,21 +84,44 @@ impl Variable { /// variable.extend((0 .. 10).map(|x| (x + 1, x))); /// /// while iteration.changed() { - /// variable.from_join(&variable, &variable, |&key, &val1, &val2| (val1, val2)); + /// variable.from_join(&variable, &variable, |key: (usize,), val1, val2| (val1, val2)); /// } /// /// let result = variable.complete(); /// assert_eq!(result.len(), 121); /// ``` - pub fn from_join<'me, K: Ord, V1: Ord, V2: Ord>( + pub fn from_join<'me, P, A, B>( &self, - input1: &'me Variable<(K, V1)>, - input2: impl JoinInput<'me, (K, V2)>, - logic: impl FnMut(&K, &V1, &V2) -> Tuple, - ) { + input1: &'me Variable, + input2: impl JoinInput<'me, B>, + logic: impl FnMut(P, A::Suffix, B::Suffix) -> Tuple, + ) where + P: Ord, + A: Copy + Split

, + B: Copy + Split

, + { join::join_into(input1, input2, self, logic) } + /// An small wrapper around [`Variable::from_join`] that uses the first element of `A` and `B` + /// as the shared prefix. + /// + /// This is useful because `Split` needs a tuple, and working with 1-tuples is a pain. + /// It can also help with inference in cases where `logic` does not make use of the shared + /// prefix. + pub fn from_join_first<'me, P, A, B>( + &self, + input1: &'me Variable, + input2: impl JoinInput<'me, B>, + mut logic: impl FnMut(P, A::Suffix, B::Suffix) -> Tuple, + ) where + P: Ord, + A: Copy + Split<(P,)>, + B: Copy + Split<(P,)>, + { + join::join_into(input1, input2, self, |(p,), a, b| logic(p, a, b)) + } + /// Same as [`Variable::from_join`], but lets you ignore some of the resulting tuples. /// /// # Examples @@ -115,7 +139,7 @@ impl Variable { /// variable.extend((0 .. 10).map(|x| (x + 1, x))); /// /// while iteration.changed() { - /// variable.from_join_filtered(&variable, &variable, |&key, &val1, &val2| { + /// variable.from_join_filtered(&variable, &variable, |key: (isize,), val1, val2| { /// ((val1 - val2).abs() <= 3).then(|| (val1, val2)) /// }); /// } @@ -133,12 +157,16 @@ impl Variable { /// /// assert_eq!(result.len(), expected_cnt); /// ``` - pub fn from_join_filtered<'me, K: Ord, V1: Ord, V2: Ord>( + pub fn from_join_filtered<'me, P, A, B>( &self, - input1: &'me Variable<(K, V1)>, - input2: impl JoinInput<'me, (K, V2)>, - logic: impl FnMut(&K, &V1, &V2) -> Option, - ) { + input1: &'me Variable, + input2: impl JoinInput<'me, B>, + logic: impl FnMut(P, A::Suffix, B::Suffix) -> Option, + ) where + P: Ord, + A: Copy + Split

, + B: Copy + Split

, + { join::join_and_filter_into(input1, input2, self, logic) } @@ -162,21 +190,24 @@ impl Variable { /// let variable = iteration.variable::<(usize, usize)>("source"); /// variable.extend((0 .. 10).map(|x| (x, x + 1))); /// - /// let relation: Relation<_> = (0 .. 10).filter(|x| x % 3 == 0).collect(); + /// let relation: Relation<_> = (0 .. 10).filter(|x| x % 3 == 0).map(|x| (x,)).collect(); /// /// while iteration.changed() { - /// variable.from_antijoin(&variable, &relation, |&key, &val| (val, key)); + /// variable.from_antijoin(&variable, &relation, |(key, val)| (val, key)); /// } /// /// let result = variable.complete(); /// assert_eq!(result.len(), 16); /// ``` - pub fn from_antijoin( + pub fn from_antijoin( &self, - input1: &Variable<(K, V)>, - input2: &Relation, - logic: impl FnMut(&K, &V) -> Tuple, - ) { + input1: &Variable, + input2: &Relation

, + logic: impl FnMut(A) -> Tuple, + ) where + A: Copy + Split

, + P: Ord, + { self.insert(join::antijoin(&input1.recent.borrow(), input2, logic)) } @@ -241,17 +272,17 @@ impl Variable { /// - Finally, you get a callback `logic` that accepts each `(SourceTuple, Val)` /// that was successfully joined (and not filtered) and which maps to the /// type of this variable. - pub fn from_leapjoin<'leap, SourceTuple: Ord, Val: Ord + 'leap>( + pub fn from_leapjoin( &self, source: &Variable, - leapers: impl Leapers<'leap, SourceTuple, Val>, + leapers: impl Leapers, logic: impl FnMut(&SourceTuple, &Val) -> Tuple, ) { self.insert(treefrog::leapjoin(&source.recent.borrow(), leapers, logic)); } } -impl Clone for Variable { +impl Clone for Variable { fn clone(&self) -> Self { Variable { distinct: self.distinct,