1
use std::fmt;
2

            
3
use merc_utilities::TagIndex;
4

            
5
/// A zero sized tag for the block.
6
pub struct BlockTag {}
7

            
8
/// The index for blocks.
9
pub type BlockIndex = TagIndex<usize, BlockTag>;
10

            
11
/// Special value used for elements that do not occur in the partition.
12
pub const NOT_IN_PARTITION: usize = usize::MAX;
13

            
14
/// Defines a partition based on an explicit indexing of elements to their block
15
/// number.
16
///
17
/// # Details
18
///
19
/// This partition always stores 0..max_value elements in the partition, but it
20
/// uses a special value to indicate that an element is not in the partition. So
21
/// this is not memory efficient.
22
#[derive(Clone, Debug)]
23
pub struct IndexedPartition {
24
    /// Stores a mapping from element index to block number.
25
    partition: Vec<BlockIndex>,
26

            
27
    /// Keeps track of the number of blocks defined.
28
    num_of_blocks: usize,
29
}
30

            
31
impl IndexedPartition {
32
    /// Create a new partition with 0 to `num_of_elements` in a single block.
33
144624
    pub fn new(num_of_elements: usize) -> IndexedPartition {
34
144624
        IndexedPartition {
35
144624
            partition: vec![BlockIndex::new(0); num_of_elements],
36
144624
            num_of_blocks: 1,
37
144624
        }
38
144624
    }
39

            
40
    /// Create a new partition where the given indices are in a single block.
41
6924
    pub fn with_subset<I>(num_of_elements: usize, included_indices: I) -> IndexedPartition
42
6924
    where
43
6924
        I: IntoIterator<Item = usize>,
44
    {
45
6924
        let mut partition = vec![BlockIndex::new(NOT_IN_PARTITION); num_of_elements];
46
6924
        let mut has_included_indices = false;
47

            
48
2029063
        for index in included_indices {
49
2029063
            partition[index] = BlockIndex::new(0);
50
2029063
            has_included_indices = true;
51
2029063
        }
52

            
53
6924
        IndexedPartition {
54
6924
            partition,
55
6924
            num_of_blocks: usize::from(has_included_indices),
56
6924
        }
57
6924
    }
58

            
59
    /// Create a new partition with the given partitioning.
60
    pub fn with_partition(partition: Vec<BlockIndex>, num_of_blocks: usize) -> IndexedPartition {
61
        debug_assert!(
62
            partition
63
                .iter()
64
                .all(|&block| block.value() < num_of_blocks || block.value() == NOT_IN_PARTITION),
65
            "Block numbers must be less than the number of blocks, or equal to NOT_IN_PARTITION"
66
        );
67

            
68
        IndexedPartition {
69
            partition,
70
            num_of_blocks,
71
        }
72
    }
73

            
74
    /// Iterates over the blocks in the partition, blocks can be repeated.
75
    pub fn iter(&self) -> impl Iterator<Item = BlockIndex> + '_ {
76
        self.iter_elements().map(|(_, block)| block)
77
    }
78

            
79
    /// Iterates over element-block pairs in the partition.
80
87116
    pub fn iter_elements(&self) -> impl Iterator<Item = (usize, BlockIndex)> + '_ {
81
87116
        self.partition
82
87116
            .iter()
83
87116
            .enumerate()
84
8690216
            .filter_map(|(element_index, &block)| (block.value() != NOT_IN_PARTITION).then_some((element_index, block)))
85
87116
    }
86

            
87
    /// Sets the block number of the given element
88
    ///
89
    /// # Details
90
    ///
91
    /// This assumes that the blocks numbers are dense, otherwise the partition
92
    /// overestimates the total number of blocks present returned from [Self::num_of_blocks].
93
213653500
    pub fn set_block(&mut self, element_index: usize, block_number: BlockIndex) {
94
213653500
        debug_assert!(
95
213653500
            block_number.value() != NOT_IN_PARTITION,
96
            "Block number cannot be NOT_IN_PARTITION"
97
        );
98

            
99
213653500
        self.num_of_blocks = self.num_of_blocks.max(block_number.value() + 1);
100
213653500
        self.partition[element_index] = block_number;
101
213653500
    }
102

            
103
    /// Returns the block number of the given element.
104
27305913224
    pub fn block(&self, element_index: usize) -> BlockIndex {
105
27305913224
        self.partition[element_index]
106
27305913224
    }
107

            
108
    /// Returns the number of elements in the partition.
109
1704480
    pub fn len(&self) -> usize {
110
1704480
        self.partition.len()
111
1704480
    }
112

            
113
    /// Returns whether the partition is empty.
114
    pub fn is_empty(&self) -> bool {
115
        self.partition.is_empty()
116
    }
117

            
118
    /// Returns the number of blocks in the partition.
119
44309044
    pub fn num_of_blocks(&self) -> usize {
120
44309044
        self.num_of_blocks
121
44309044
    }
122
}
123

            
124
/// Reorders the blocks of the given partition according to the given permutation.
125
pub fn reorder_partition<P>(partition: IndexedPartition, permutation: P) -> IndexedPartition
126
where
127
    P: Fn(BlockIndex) -> BlockIndex,
128
{
129
    let mut new_partition = partition.clone();
130

            
131
    for (element_index, block) in partition.iter_elements() {
132
        new_partition.set_block(element_index, permutation(block));
133
    }
134

            
135
    new_partition
136
}
137

            
138
impl fmt::Display for IndexedPartition {
139
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140
        write!(f, "{{ ")?;
141

            
142
        let mut first = true;
143

            
144
        for block_index in self.iter() {
145
            // Print all elements with the same block number.
146
            let mut first_block = true;
147
            for (element_index, _) in self.iter_elements().filter(|&(_, value)| value == block_index) {
148
                if !first_block {
149
                    write!(f, ", ")?;
150
                } else {
151
                    if !first {
152
                        write!(f, ", ")?;
153
                    }
154

            
155
                    write!(f, "{{")?;
156
                }
157

            
158
                write!(f, "{element_index}")?;
159
                first_block = false;
160
            }
161

            
162
            if !first_block {
163
                write!(f, "}}")?;
164
                first = false;
165
            }
166
        }
167

            
168
        write!(f, " }}")
169
    }
170
}