1
#![forbid(unsafe_code)]
2

            
3
use std::fmt;
4

            
5
use merc_lts::StateIndex;
6

            
7
use crate::BlockIndex;
8
use crate::Partition;
9

            
10
/// Defines a partition based on an explicit indexing of elements to their block
11
/// number.
12
#[derive(Debug)]
13
pub struct IndexedPartition {
14
    partition: Vec<BlockIndex>,
15

            
16
    num_of_blocks: usize,
17
}
18

            
19
impl IndexedPartition {
20
    /// Create a new partition where all elements are in a single block.
21
2201
    pub fn new(num_of_elements: usize) -> IndexedPartition {
22
2201
        IndexedPartition {
23
2201
            partition: vec![BlockIndex::new(0); num_of_elements],
24
2201
            num_of_blocks: 1,
25
2201
        }
26
2201
    }
27

            
28
    /// Create a new partition with the given partitioning.
29
    pub fn with_partition(partition: Vec<BlockIndex>, num_of_blocks: usize) -> IndexedPartition {
30
        IndexedPartition {
31
            partition,
32
            num_of_blocks,
33
        }
34
    }
35

            
36
    /// Iterates over the blocks in the partition.
37
    pub fn iter(&self) -> impl Iterator<Item = BlockIndex> + '_ {
38
        self.partition.iter().copied()
39
    }
40

            
41
    /// Sets the block number of the given element
42
38971
    pub fn set_block(&mut self, element_index: StateIndex, block_number: BlockIndex) {
43
        // TODO: This assumes that the blocks are dense, otherwise it overestimates the number of blocks.
44
38971
        self.num_of_blocks = self.num_of_blocks.max(block_number.value() + 1);
45

            
46
38971
        self.partition[element_index] = block_number;
47
38971
    }
48
}
49

            
50
/// Combines two partitions into a new partition.
51
pub fn combine_partition(left: IndexedPartition, right: &impl Partition) -> IndexedPartition {
52
    let mut combined_partition = IndexedPartition::new(left.partition.len());
53

            
54
    for (element_index, block) in left.partition.iter().enumerate() {
55
        let new_block = right.block_number(StateIndex::new(block.value()));
56

            
57
        combined_partition.set_block(StateIndex::new(element_index), new_block);
58
    }
59

            
60
    combined_partition
61
}
62

            
63
/// Reorders the blocks of the given partition according to the given permutation.
64
pub fn reorder_partition<P>(partition: IndexedPartition, permutation: P) -> IndexedPartition
65
where
66
    P: Fn(BlockIndex) -> BlockIndex,
67
{
68
    let mut new_partition = IndexedPartition::new(partition.len());
69

            
70
    for (element_index, block) in partition.iter().enumerate() {
71
        new_partition.set_block(StateIndex::new(element_index), permutation(block));
72
    }
73

            
74
    new_partition
75
}
76

            
77
impl fmt::Display for IndexedPartition {
78
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79
        write!(f, "{{ ")?;
80

            
81
        let mut first = true;
82

            
83
        for block_index in 0..self.partition.len() {
84
            // Print all elements with the same block number.
85
            let mut first_block = true;
86
            for (element_index, _) in self.iter().enumerate().filter(|(_, value)| *value == block_index) {
87
                if !first_block {
88
                    write!(f, ", ")?;
89
                } else {
90
                    if !first {
91
                        write!(f, ", ")?;
92
                    }
93

            
94
                    write!(f, "{{")?;
95
                }
96

            
97
                write!(f, "{element_index}")?;
98
                first_block = false;
99
            }
100

            
101
            if !first_block {
102
                write!(f, "}}")?;
103
                first = false;
104
            }
105
        }
106

            
107
        write!(f, " }}")
108
    }
109
}
110

            
111
impl Partition for IndexedPartition {
112
511844
    fn block_number(&self, state_index: StateIndex) -> BlockIndex {
113
511844
        self.partition[state_index.value()]
114
511844
    }
115

            
116
16464
    fn num_of_blocks(&self) -> usize {
117
16464
        self.num_of_blocks
118
16464
    }
119

            
120
    fn len(&self) -> usize {
121
        self.partition.len()
122
    }
123
}