///|
/// Clarification removes only duplicate incidence rows/columns, not reducible nonduplicates.
/// Groups map each representative back to every original index, so multiplicities are explicit.
pub struct Clarification {
context : Context
priv object_groups : Array[Array[Int32]]
priv attribute_groups : Array[Array[Int32]]
} derive(Debug)
///|
pub extend Clarification with Debug::{to_repr}
///|
pub fn Context::clarify(self : Context) -> Clarification raise FcaError {
let object_groups : Array[Array[Int32]] = []
let row_keys : Map[String, Int32] = Map([])
let object_representatives : Array[Int32] = []
for o = 0; o < self.object_count(); o = o + 1 {
let key = self.incidence[o]
.map(fn(present) { if present { "1" } else { "0" } })
.join("")
match row_keys.get(key) {
Some(group) => object_groups[group].push(o)
None => {
row_keys[key] = object_groups.length()
object_groups.push([o])
object_representatives.push(o)
}
}
}
let attribute_groups : Array[Array[Int32]] = []
let column_keys : Map[String, Int32] = Map([])
let attribute_representatives : Array[Int32] = []
for a = 0; a < self.attribute_count(); a = a + 1 {
let column : Array[String] = []
for o = 0; o < self.object_count(); o = o + 1 {
column.push(if self.incidence[o][a] { "1" } else { "0" })
}
let key = column.join("")
match column_keys.get(key) {
Some(group) => attribute_groups[group].push(a)
None => {
column_keys[key] = attribute_groups.length()
attribute_groups.push([a])
attribute_representatives.push(a)
}
}
}
{
context: self.project(object_representatives, attribute_representatives),
object_groups,
attribute_groups,
}
}
///|
pub fn Clarification::expand_extent(
self : Clarification,
extent : Array[Int32],
) -> Array[Int32] raise FcaError {
let selected = checked_indices(extent, self.object_groups.length())
let result : Array[Int32] = []
for group in selected {
for value in self.object_groups[group] {
result.push(value)
}
}
index_set(result)
}
///|
pub fn Clarification::expand_intent(
self : Clarification,
intent : Array[Int32],
) -> Array[Int32] raise FcaError {
let selected = checked_indices(intent, self.attribute_groups.length())
let result : Array[Int32] = []
for group in selected {
for value in self.attribute_groups[group] {
result.push(value)
}
}
index_set(result)
}
///|
pub fn Clarification::object_classes(
self : Clarification,
) -> Array[Array[Int32]] {
self.object_groups.map(fn(group) { group.copy() })
}
///|
pub fn Clarification::attribute_classes(
self : Clarification,
) -> Array[Array[Int32]] {
self.attribute_groups.map(fn(group) { group.copy() })
}