///|
/// A compact index over observations used by audit and export layers.
pub(all) struct EvidenceIndex {
observation_count : Int
unique_feature_count : Int
duplicate_feature_count : Int
sample_count : Int
feature_ids : Array[String]
}
///|
fn evidence_contains(ids : Array[String], value : String) -> Bool {
for id in ids {
if id == value {
return true
}
}
false
}
///|
/// Build a deterministic feature-to-observation index.
pub fn build_evidence_index(observations : Array[Observation]) -> EvidenceIndex {
let feature_ids = []
let mut sample_count = 0
let mut duplicate_feature_count = 0
for observation in observations {
sample_count += if observation.sample_count > 0 {
observation.sample_count
} else {
0
}
if evidence_contains(feature_ids, observation.feature_id) {
duplicate_feature_count += 1
} else {
feature_ids.push(observation.feature_id)
}
}
{
observation_count: observations.length(),
unique_feature_count: feature_ids.length(),
duplicate_feature_count,
sample_count,
feature_ids,
}
}
///|
/// Return expected feature IDs that have no observation in the index.
pub fn missing_feature_ids(
index : EvidenceIndex,
expected : Array[String],
) -> Array[String] {
let result = []
for id in expected {
if !evidence_contains(index.feature_ids, id) {
result.push(id)
}
}
result
}
///|
/// Return whether the index contains useful, non-empty evidence.
pub fn evidence_index_is_usable(index : EvidenceIndex) -> Bool {
index.observation_count > 0 &&
index.unique_feature_count > 0 &&
index.sample_count > 0
}
///|
/// Calculate the accepted-observation ratio against indexed samples.
pub fn evidence_index_pass_rate(
index : EvidenceIndex,
passed_observation_count : Int,
) -> Double {
if index.sample_count <= 0 || passed_observation_count <= 0 {
0.0
} else {
let bounded = if passed_observation_count > index.sample_count {
index.sample_count
} else {
passed_observation_count
}
bounded.to_double() / index.sample_count.to_double()
}
}
///|
/// Return the number of indexed features represented in expected IDs.
pub fn matched_feature_count(
index : EvidenceIndex,
expected : Array[String],
) -> Int {
expected.length() - missing_feature_ids(index, expected).length()
}
///|
/// Return whether every expected feature has evidence.
pub fn evidence_is_complete(
index : EvidenceIndex,
expected : Array[String],
) -> Bool {
missing_feature_ids(index, expected).length() == 0
}
///|
/// Return the number of observations with non-positive sample counts.
pub fn invalid_sample_observation_count(
observations : Array[Observation],
) -> Int {
let mut result = 0
for observation in observations {
if observation.sample_count <= 0 {
result += 1
}
}
result
}
///|
/// Return a stable evidence-density ratio per unique feature.
pub fn evidence_density(index : EvidenceIndex) -> Double {
if index.unique_feature_count == 0 {
0.0
} else {
index.observation_count.to_double() / index.unique_feature_count.to_double()
}
}
///|
/// Return a concise evidence index label for reports.
pub fn render_evidence_index(index : EvidenceIndex) -> String {
"features=" +
index.unique_feature_count.to_string() +
" observations=" +
index.observation_count.to_string() +
" samples=" +
index.sample_count.to_string()
}