Skip to content

Commit ec8a639

Browse files
Support joins on arbitrary prefixes
1 parent ccaca5b commit ec8a639

File tree

3 files changed

+90
-51
lines changed

3 files changed

+90
-51
lines changed

src/join.rs

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Join functionality.
22
3-
use super::{Relation, Variable};
3+
use super::{Relation, Split, Variable};
44
use std::cell::Ref;
55
use std::ops::Deref;
66

@@ -9,28 +9,38 @@ use std::ops::Deref;
99
/// because relations have no "recent" tuples, so the fn would be a
1010
/// guaranteed no-op if both arguments were relations. See also
1111
/// `join_into_relation`.
12-
pub(crate) fn join_into<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: Ord>(
13-
input1: &Variable<(Key, Val1)>,
14-
input2: impl JoinInput<'me, (Key, Val2)>,
15-
output: &Variable<Result>,
16-
mut logic: impl FnMut(&Key, &Val1, &Val2) -> Result,
17-
) {
12+
pub(crate) fn join_into<'me, P, A, B, O>(
13+
input1: &Variable<A>,
14+
input2: impl JoinInput<'me, B>,
15+
output: &Variable<O>,
16+
mut logic: impl FnMut(P, A::Suffix, B::Suffix) -> O,
17+
) where
18+
P: Ord,
19+
A: Copy + Split<P>,
20+
B: Copy + Split<P>,
21+
O: Ord,
22+
{
1823
let mut results = Vec::new();
19-
let push_result = |k: &Key, v1: &Val1, v2: &Val2| results.push(logic(k, v1, v2));
24+
let push_result = |k, v1, v2| results.push(logic(k, v1, v2));
2025

2126
join_delta(input1, input2, push_result);
2227

2328
output.insert(Relation::from_vec(results));
2429
}
2530

26-
pub(crate) fn join_and_filter_into<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: Ord>(
27-
input1: &Variable<(Key, Val1)>,
28-
input2: impl JoinInput<'me, (Key, Val2)>,
29-
output: &Variable<Result>,
30-
mut logic: impl FnMut(&Key, &Val1, &Val2) -> Option<Result>,
31-
) {
31+
pub(crate) fn join_and_filter_into<'me, P, A, B, O>(
32+
input1: &Variable<A>,
33+
input2: impl JoinInput<'me, B>,
34+
output: &Variable<O>,
35+
mut logic: impl FnMut(P, A::Suffix, B::Suffix) -> Option<O>,
36+
) where
37+
P: Ord,
38+
A: Copy + Split<P>,
39+
B: Copy + Split<P>,
40+
O: Ord,
41+
{
3242
let mut results = Vec::new();
33-
let push_result = |k: &Key, v1: &Val1, v2: &Val2| {
43+
let push_result = |k, v1, v2| {
3444
if let Some(result) = logic(k, v1, v2) {
3545
results.push(result);
3646
}
@@ -43,11 +53,15 @@ pub(crate) fn join_and_filter_into<'me, Key: Ord, Val1: Ord, Val2: Ord, Result:
4353

4454
/// Joins the `recent` tuples of each input with the `stable` tuples of the other, then the
4555
/// `recent` tuples of *both* inputs.
46-
fn join_delta<'me, Key: Ord, Val1: Ord, Val2: Ord>(
47-
input1: &Variable<(Key, Val1)>,
48-
input2: impl JoinInput<'me, (Key, Val2)>,
49-
mut result: impl FnMut(&Key, &Val1, &Val2),
50-
) {
56+
fn join_delta<'me, P, A, B>(
57+
input1: &Variable<A>,
58+
input2: impl JoinInput<'me, B>,
59+
mut result: impl FnMut(P, A::Suffix, B::Suffix),
60+
) where
61+
P: Ord,
62+
A: Copy + Split<P>,
63+
B: Copy + Split<P>,
64+
{
5165
let recent1 = input1.recent();
5266
let recent2 = input2.recent();
5367

@@ -63,11 +77,17 @@ fn join_delta<'me, Key: Ord, Val1: Ord, Val2: Ord>(
6377
}
6478

6579
/// Join, but for two relations.
66-
pub(crate) fn join_into_relation<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: Ord>(
67-
input1: &Relation<(Key, Val1)>,
68-
input2: &Relation<(Key, Val2)>,
69-
mut logic: impl FnMut(&Key, &Val1, &Val2) -> Result,
70-
) -> Relation<Result> {
80+
pub(crate) fn join_into_relation<P, A, B, O>(
81+
input1: &Relation<A>,
82+
input2: &Relation<B>,
83+
mut logic: impl FnMut(P, A::Suffix, B::Suffix) -> O,
84+
) -> Relation<O>
85+
where
86+
P: Ord,
87+
A: Copy + Split<P>,
88+
B: Copy + Split<P>,
89+
O: Ord,
90+
{
7191
let mut results = Vec::new();
7292

7393
join_helper(&input1.elements, &input2.elements, |k, v1, v2| {
@@ -98,28 +118,32 @@ pub(crate) fn antijoin<Key: Ord, Val: Ord, Result: Ord>(
98118
Relation::from_vec(results)
99119
}
100120

101-
fn join_helper<K: Ord, V1, V2>(
102-
mut slice1: &[(K, V1)],
103-
mut slice2: &[(K, V2)],
104-
mut result: impl FnMut(&K, &V1, &V2),
105-
) {
121+
fn join_helper<P, A, B>(
122+
mut slice1: &[A],
123+
mut slice2: &[B],
124+
mut result: impl FnMut(P, A::Suffix, B::Suffix),
125+
) where
126+
A: Copy + Split<P>,
127+
B: Copy + Split<P>,
128+
P: Ord,
129+
{
106130
while !slice1.is_empty() && !slice2.is_empty() {
107131
use std::cmp::Ordering;
108132

109133
// If the keys match produce tuples, else advance the smaller key until they might.
110-
match slice1[0].0.cmp(&slice2[0].0) {
134+
match slice1[0].prefix().cmp(&slice2[0].prefix()) {
111135
Ordering::Less => {
112-
slice1 = gallop(slice1, |x| x.0 < slice2[0].0);
136+
slice1 = gallop(slice1, |x| x.prefix() < slice2[0].prefix());
113137
}
114138
Ordering::Equal => {
115139
// Determine the number of matching keys in each slice.
116-
let count1 = slice1.iter().take_while(|x| x.0 == slice1[0].0).count();
117-
let count2 = slice2.iter().take_while(|x| x.0 == slice2[0].0).count();
140+
let count1 = slice1.iter().take_while(|x| x.prefix() == slice1[0].prefix()).count();
141+
let count2 = slice2.iter().take_while(|x| x.prefix() == slice2[0].prefix()).count();
118142

119143
// Produce results from the cross-product of matches.
120144
for index1 in 0..count1 {
121145
for s2 in slice2[..count2].iter() {
122-
result(&slice1[0].0, &slice1[index1].1, &s2.1);
146+
result(slice1[0].prefix(), slice1[index1].suffix(), s2.suffix());
123147
}
124148
}
125149

@@ -128,7 +152,7 @@ fn join_helper<K: Ord, V1, V2>(
128152
slice2 = &slice2[count2..];
129153
}
130154
Ordering::Greater => {
131-
slice2 = gallop(slice2, |x| x.0 < slice1[0].0);
155+
slice2 = gallop(slice2, |x| x.prefix() < slice1[0].prefix());
132156
}
133157
}
134158
}

src/relation.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
join,
55
merge,
66
treefrog::{self, Leapers},
7+
Split,
78
};
89

910
/// A static, ordered list of key-value pairs.
@@ -48,11 +49,16 @@ impl<Tuple: Ord> Relation<Tuple> {
4849
/// `input2` and then applying `logic`. Like
4950
/// [`Variable::from_join`] except for use where the inputs are
5051
/// not varying across iterations.
51-
pub fn from_join<Key: Ord, Val1: Ord, Val2: Ord>(
52-
input1: &Relation<(Key, Val1)>,
53-
input2: &Relation<(Key, Val2)>,
54-
logic: impl FnMut(&Key, &Val1, &Val2) -> Tuple,
55-
) -> Self {
52+
pub fn from_join<P, A, B>(
53+
input1: &Relation<A>,
54+
input2: &Relation<B>,
55+
logic: impl FnMut(P, A::Suffix, B::Suffix) -> Tuple,
56+
) -> Self
57+
where
58+
P: Ord,
59+
A: Copy + Split<P>,
60+
B: Copy + Split<P>,
61+
{
5662
join::join_into_relation(input1, input2, logic)
5763
}
5864

src/variable.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
map,
99
relation::Relation,
1010
treefrog::{self, Leapers},
11+
Split,
1112
};
1213

1314
/// A type that can report on whether it has changed.
@@ -89,12 +90,16 @@ impl<Tuple: Ord> Variable<Tuple> {
8990
/// let result = variable.complete();
9091
/// assert_eq!(result.len(), 121);
9192
/// ```
92-
pub fn from_join<'me, K: Ord, V1: Ord, V2: Ord>(
93+
pub fn from_join<'me, P, A, B>(
9394
&self,
94-
input1: &'me Variable<(K, V1)>,
95-
input2: impl JoinInput<'me, (K, V2)>,
96-
logic: impl FnMut(&K, &V1, &V2) -> Tuple,
97-
) {
95+
input1: &'me Variable<A>,
96+
input2: impl JoinInput<'me, B>,
97+
logic: impl FnMut(P, A::Suffix, B::Suffix) -> Tuple,
98+
) where
99+
P: Ord,
100+
A: Copy + Split<P>,
101+
B: Copy + Split<P>,
102+
{
98103
join::join_into(input1, input2, self, logic)
99104
}
100105

@@ -133,12 +138,16 @@ impl<Tuple: Ord> Variable<Tuple> {
133138
///
134139
/// assert_eq!(result.len(), expected_cnt);
135140
/// ```
136-
pub fn from_join_filtered<'me, K: Ord, V1: Ord, V2: Ord>(
141+
pub fn from_join_filtered<'me, P, A, B>(
137142
&self,
138-
input1: &'me Variable<(K, V1)>,
139-
input2: impl JoinInput<'me, (K, V2)>,
140-
logic: impl FnMut(&K, &V1, &V2) -> Option<Tuple>,
141-
) {
143+
input1: &'me Variable<A>,
144+
input2: impl JoinInput<'me, B>,
145+
logic: impl FnMut(P, A::Suffix, B::Suffix) -> Option<Tuple>,
146+
) where
147+
P: Ord,
148+
A: Copy + Split<P>,
149+
B: Copy + Split<P>,
150+
{
142151
join::join_and_filter_into(input1, input2, self, logic)
143152
}
144153

0 commit comments

Comments
 (0)