///|
/// One formal concept: extent' = intent and intent' = extent.
pub struct Concept {
extent : Array[Int32]
intent : Array[Int32]
} derive(Eq, Debug)
///|
pub extend Concept with Eq::{equal, not_equal}
///|
pub extend Concept with Debug::{to_repr}
///|
pub fn Context::concept_for(
self : Context,
attributes : Array[Int32],
) -> Concept raise FcaError {
let extent = self.extent(attributes)
{ extent, intent: self.intent(extent), }
}
///|
pub fn Context::valid_concept(
self : Context,
concept : Concept,
) -> Bool raise FcaError {
let a = checked_indices(concept.extent, self.object_count())
let b = checked_indices(concept.intent, self.attribute_count())
self.intent(a) == b && self.extent(b) == a
}
///|
/// NextClosure in the input attribute order. The supplied intent must already be closed.
pub fn Context::next_intent(
self : Context,
current : Array[Int32],
) -> Array[Int32]? raise FcaError {
let current = checked_indices(current, self.attribute_count())
if !self.is_closed(current) {
raise Invalid("NextClosure requires a closed intent")
}
for i = self.attribute_count() - 1; i >= 0; i = i - 1 {
if !current.contains(i) {
let seed : Array[Int32] = []
for j in current {
if j < i {
seed.push(j)
}
}
seed.push(i)
let candidate = self.closure(seed)
let mut canonical = true
for j = 0; j < i; j = j + 1 {
if candidate.contains(j) && !current.contains(j) {
canonical = false
break
}
}
if canonical {
return Some(candidate)
}
}
}
None
}
///|
/// Enumerate in lectic order; budget failure never returns a silently truncated result.
pub fn Context::concepts(
self : Context,
limit~ : Int32 = 4096,
) -> Array[Concept] raise FcaError {
if limit < 1 || limit > 65536 {
raise Invalid("concept limit must be 1..65536")
}
let result : Array[Concept] = []
let mut current = self.closure([])
while true {
if result.length() >= limit {
raise Budget("concept enumeration limit reached")
}
result.push({ extent: self.extent(current), intent: current.copy(), })
match self.next_intent(current) {
Some(next) => current = next
None => break
}
}
result
}