1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use std::{
    collections::{BTreeMap, HashMap},
    convert::identity,
    fmt::Debug,
    iter,
    ops::Neg,
};

use crate::{pair::Pair, Op, Op_, Relation};

pub type KVMap<K, V> = HashMap<K, HashMap<V, isize>>;
pub type ExtremaMap<K, V> = BTreeMap<K, HashMap<V, isize>>;

impl<C: Op_> Relation<C> {
    pub fn map_<Y, F: Fn(C::T) -> Y>(self, f: F) -> Relation<impl Op_<T = Y>> {
        self.flat_map_(move |x| iter::once(f(x)))
    }
    pub fn debug(self, name: &'static str) -> Relation<impl Op_<T = C::T>>
    where
        C::T: Debug,
    {
        self.map_(move |x| {
            log::debug!("{}: {:?}", name, x);
            x
        })
        .hidden()
    }
}

impl<C: Op> Relation<C> {
    pub fn flat_map<I: IntoIterator>(
        self,
        f: impl Fn(C::D) -> I,
    ) -> Relation<impl Op<D = I::Item>> {
        self.flat_map_(move |(x, count)| f(x).into_iter().map(move |y| (y, count)))
    }
    pub fn flatten(self) -> Relation<impl Op<D = <C::D as IntoIterator>::Item>>
    where
        C::D: IntoIterator,
    {
        self.flat_map(identity).type_named("flatten")
    }
    pub fn map<Y>(self, f: impl Fn(C::D) -> Y) -> Relation<impl Op<D = Y>> {
        self.flat_map(move |x| iter::once(f(x))).type_named("map")
    }

    pub fn map_h<Y>(self, f: impl Fn(C::D) -> Y) -> Relation<impl Op<D = Y>> {
        self.map(f).hidden()
    }

    pub fn filter(self, f: impl Fn(&C::D) -> bool) -> Relation<impl Op<D = C::D>> {
        self.flat_map(move |x| if f(&x) { Some(x) } else { None })
            .type_named("filter")
    }

    pub fn map_counts(self, f: impl Fn(isize) -> isize) -> Relation<impl Op<D = C::D>> {
        self.flat_map_(move |(x, count)| iter::once((x, f(count))))
            .type_named("map_counts")
    }

    pub fn negate(self) -> Relation<impl Op<D = C::D>> {
        self.map_counts(Neg::neg).type_named("negate")
    }
}

impl<A, B, C: Op<D = (A, B)>> Relation<C> {
    pub fn fsts(self) -> Relation<impl Op<D = A>> {
        self.map_h(Pair::fst)
    }
    pub fn snds(self) -> Relation<impl Op<D = B>> {
        self.map_h(Pair::snd)
    }
    pub fn swaps(self) -> Relation<impl Op<D = (B, A)>> {
        self.map_h(Pair::swap)
    }
}