use std::collections::HashSet;
use std::fmt::{Display, Formatter};
use crate::fuzzy::membership::piecewise::{LinearFunction, PiecewiseLinearFunction};
use crate::fuzzy::membership::Trapezoidal;
use crate::fuzzy::{label::get_labels_names, Label, LabelMembership};
use crate::utilities;
use super::Domain;
#[derive(Debug, PartialEq)]
pub struct Qualitative<T: LabelMembership> {
labels: Vec<Label<T>>,
}
#[derive(Debug, PartialEq)]
pub enum QualitativeError {
DuplicateName { name: String },
}
impl Display for QualitativeError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
use QualitativeError::*;
match &self {
DuplicateName { name } => {
write!(f, "Duplicate label name {}.", name)
}
}
}
}
impl<T: LabelMembership> Domain for Qualitative<T> {}
impl<T: LabelMembership + Display> Display for Qualitative<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"[{}]",
self.labels
.iter()
.map(|v| format!("{}", v))
.collect::<Vec<String>>()
.join(", ")
)
}
}
impl<T: LabelMembership> Qualitative<T> {
fn _find_duplicate(labels: &[&str]) -> Option<String> {
let mut set = HashSet::new();
for label in labels {
if set.contains(label) {
return Some(String::from(*label));
}
set.insert(label);
}
None
}
pub fn new(labels: Vec<Label<T>>) -> Result<Self, QualitativeError> {
use QualitativeError::*;
if let Some(name) = Qualitative::<T>::_find_duplicate(&get_labels_names(&labels)) {
Err(DuplicateName { name })
} else {
Ok(Self { labels })
}
}
pub fn cardinality(&self) -> usize {
self.labels.len()
}
pub fn contains_label(&self, name: &str) -> bool {
get_labels_names(&self.labels).contains(&name)
}
pub fn label_index(&self, name: &str) -> Option<usize> {
get_labels_names(&self.labels)
.iter()
.position(|&v| v.eq(name))
}
pub fn get_label_by_index(&self, index: usize) -> Option<&Label<T>> {
if index < self.labels.len() {
Some(&self.labels[index])
} else {
None
}
}
pub fn get_label_by_name(&self, name: &str) -> Option<&Label<T>> {
if let Some(index) = self.label_index(name) {
Some(&self.labels[index])
} else {
None
}
}
pub fn get_labels_names(&self) -> Vec<&str> {
get_labels_names(&self.labels)
}
pub fn is_odd(&self) -> bool {
self.cardinality() % 2 != 0
}
}
impl Qualitative<Trapezoidal> {
pub fn is_fuzzy_partition(&self) -> bool {
let mut fuzzy_partition = PiecewiseLinearFunction::new();
fuzzy_partition
.add(0.0, 1.0, LinearFunction::new(0.0, 1.0))
.unwrap();
PiecewiseLinearFunction::from(self) == fuzzy_partition
}
pub fn is_triangular(&self) -> bool {
for l in &self.labels {
if !l.membership().is_triangular() {
return false;
}
}
true
}
pub fn is_tor(&self) -> bool {
self.is_odd() && self.is_triangular() && self.is_fuzzy_partition()
}
pub fn is_symmetrical(&self) -> bool {
let cardinality = self.cardinality();
if cardinality == 0 {
return true;
}
let center_pos = cardinality / 2;
let centroid;
if self.is_odd() {
let center_label = &self.labels[center_pos];
if !center_label.membership().is_symmetrical() {
return false;
} else {
centroid = center_label.membership().centroid();
}
} else {
centroid = (self.labels[center_pos].membership().centroid()
+ self.labels[center_pos - 1].membership().centroid())
/ 2.;
}
for pos in 0..center_pos {
if !self.labels[pos].membership().is_symmetrical_respect_center(
self.labels[cardinality - 1 - pos].membership(),
centroid,
) {
return false;
}
}
true
}
pub fn is_uniform(&self) -> bool {
let cardinality = self.cardinality();
if cardinality <= 1 {
return true;
}
let compute_diff = |i: usize| {
let (a, b) = self.labels[i].membership().center();
let (c, d) = self.labels[i - 1].membership().center();
(a + b - c - d) / 2.
};
let diff = compute_diff(1);
for pos in 2..cardinality {
if !utilities::math::approx_equal_f32(diff, compute_diff(pos), 5) {
return false;
}
}
true
}
pub fn is_blts(&self) -> bool {
self.is_tor() && self.is_symmetrical() && self.is_uniform()
}
}
impl From<&Qualitative<Trapezoidal>> for PiecewiseLinearFunction {
fn from(domain: &Qualitative<Trapezoidal>) -> Self {
let mut result = PiecewiseLinearFunction::new();
domain
.labels
.iter()
.map(PiecewiseLinearFunction::from)
.for_each(|function| result = result.merge(&function));
result
}
}