use crate::bijective::BijectiveDictionary;
use crate::dynamic_dawg_char::DynamicDawgChar;
use crate::sync_compat::RwLock;
use crate::value::DictionaryValue;
use crate::{Dictionary, MappedDictionary};
use std::collections::HashMap;
use std::hash::Hash;
use std::sync::Arc;
#[derive(Debug)]
pub struct BijectiveMap<V: DictionaryValue + Eq + Hash> {
forward: DynamicDawgChar<V>,
reverse: Arc<RwLock<HashMap<V, String>>>,
}
impl<V: DictionaryValue + Eq + Hash> Clone for BijectiveMap<V> {
fn clone(&self) -> Self {
Self {
forward: self.forward.clone(),
reverse: Arc::new(RwLock::new(self.reverse.read().clone())),
}
}
}
impl<V: DictionaryValue + Eq + Hash> Default for BijectiveMap<V> {
fn default() -> Self {
Self::new()
}
}
impl<V: DictionaryValue + Eq + Hash> BijectiveMap<V> {
pub fn new() -> Self {
Self {
forward: DynamicDawgChar::new(),
reverse: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
forward: DynamicDawgChar::new(),
reverse: Arc::new(RwLock::new(HashMap::with_capacity(capacity))),
}
}
pub fn from_pairs<I, S>(pairs: I) -> Self
where
I: IntoIterator<Item = (S, V)>,
S: AsRef<str>,
{
let bimap = Self::new();
for (term, value) in pairs {
bimap.insert(term.as_ref(), value);
}
bimap
}
pub fn insert(&self, term: &str, value: V) {
if self.forward.get_value(term).is_some() {
panic!(
"BijectiveMap::insert: duplicate term '{}' violates bijection invariant",
term
);
}
{
let reverse = self.reverse.read();
if reverse.contains_key(&value) {
panic!("BijectiveMap::insert: duplicate value violates bijection invariant");
}
}
self.forward.insert_with_value(term, value.clone());
{
let mut reverse = self.reverse.write();
reverse.insert(value, term.to_string());
}
}
pub fn try_insert(&self, term: &str, value: V) -> Result<(), InsertError> {
if self.forward.get_value(term).is_some() {
return Err(InsertError::DuplicateTerm);
}
{
let reverse = self.reverse.read();
if reverse.contains_key(&value) {
return Err(InsertError::DuplicateValue);
}
}
self.forward.insert_with_value(term, value.clone());
{
let mut reverse = self.reverse.write();
reverse.insert(value, term.to_string());
}
Ok(())
}
#[inline]
pub fn get_value(&self, term: &str) -> Option<V> {
self.forward.get_value(term)
}
#[inline]
pub fn get_term(&self, value: &V) -> Option<String> {
self.reverse.read().get(value).cloned()
}
#[inline]
pub fn contains_term(&self, term: &str) -> bool {
self.forward.get_value(term).is_some()
}
#[inline]
pub fn contains_value(&self, value: &V) -> bool {
self.reverse.read().contains_key(value)
}
#[inline]
pub fn len(&self) -> usize {
self.reverse.read().len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.reverse.read().is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (String, V)> + '_ {
let reverse = self.reverse.read();
reverse
.iter()
.map(|(v, t)| (t.clone(), v.clone()))
.collect::<Vec<_>>()
.into_iter()
}
pub fn terms(&self) -> impl Iterator<Item = String> + '_ {
let reverse = self.reverse.read();
reverse.values().cloned().collect::<Vec<_>>().into_iter()
}
pub fn values(&self) -> impl Iterator<Item = V> + '_ {
let reverse = self.reverse.read();
reverse.keys().cloned().collect::<Vec<_>>().into_iter()
}
#[inline]
pub fn forward(&self) -> &DynamicDawgChar<V> {
&self.forward
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InsertError {
DuplicateTerm,
DuplicateValue,
}
impl std::fmt::Display for InsertError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InsertError::DuplicateTerm => write!(f, "duplicate term violates bijection invariant"),
InsertError::DuplicateValue => {
write!(f, "duplicate value violates bijection invariant")
}
}
}
}
impl std::error::Error for InsertError {}
impl<V: DictionaryValue + Eq + Hash> Dictionary for BijectiveMap<V> {
type Node = <DynamicDawgChar<V> as Dictionary>::Node;
fn root(&self) -> Self::Node {
self.forward.root()
}
fn contains(&self, term: &str) -> bool {
self.forward.contains(term)
}
fn len(&self) -> Option<usize> {
Some(self.reverse.read().len())
}
}
impl<V: DictionaryValue + Eq + Hash> MappedDictionary for BijectiveMap<V> {
type Value = V;
fn get_value(&self, term: &str) -> Option<Self::Value> {
Self::get_value(self, term)
}
fn contains_with_value<F>(&self, term: &str, predicate: F) -> bool
where
F: Fn(&Self::Value) -> bool,
{
self.forward.get_value(term).is_some_and(|v| predicate(&v))
}
}
impl<V: DictionaryValue + Eq + Hash> BijectiveDictionary for BijectiveMap<V> {
fn get_term(&self, value: &Self::Value) -> Option<std::borrow::Cow<'_, str>> {
let reverse = self.reverse.read();
reverse
.get(value)
.map(|s| std::borrow::Cow::Owned(s.clone()))
}
fn contains_value(&self, value: &Self::Value) -> bool {
Self::contains_value(self, value)
}
fn bijection_len(&self) -> usize {
self.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_map() {
let bimap: BijectiveMap<i32> = BijectiveMap::new();
assert!(bimap.is_empty());
assert_eq!(bimap.len(), 0);
assert_eq!(bimap.get_value("test"), None);
assert_eq!(bimap.get_term(&0), None);
}
#[test]
fn test_single_pair() {
let bimap: BijectiveMap<i32> = BijectiveMap::new();
bimap.insert("hello", 42);
assert_eq!(bimap.len(), 1);
assert_eq!(bimap.get_value("hello"), Some(42));
assert_eq!(bimap.get_term(&42), Some("hello".to_string()));
}
#[test]
fn test_multiple_pairs() {
let bimap = BijectiveMap::from_pairs([("a", 1), ("b", 2), ("c", 3)]);
assert_eq!(bimap.len(), 3);
assert_eq!(bimap.get_value("a"), Some(1));
assert_eq!(bimap.get_value("b"), Some(2));
assert_eq!(bimap.get_value("c"), Some(3));
assert_eq!(bimap.get_term(&1), Some("a".to_string()));
assert_eq!(bimap.get_term(&2), Some("b".to_string()));
assert_eq!(bimap.get_term(&3), Some("c".to_string()));
}
#[test]
#[should_panic(expected = "duplicate term")]
fn test_duplicate_term_panics() {
let bimap: BijectiveMap<i32> = BijectiveMap::new();
bimap.insert("hello", 1);
bimap.insert("hello", 2); }
#[test]
#[should_panic(expected = "duplicate value")]
fn test_duplicate_value_panics() {
let bimap: BijectiveMap<i32> = BijectiveMap::new();
bimap.insert("one", 1);
bimap.insert("uno", 1); }
#[test]
fn test_try_insert() {
let bimap: BijectiveMap<i32> = BijectiveMap::new();
assert!(bimap.try_insert("one", 1).is_ok());
assert_eq!(bimap.try_insert("one", 2), Err(InsertError::DuplicateTerm));
assert_eq!(bimap.try_insert("uno", 1), Err(InsertError::DuplicateValue));
assert_eq!(bimap.len(), 1);
assert_eq!(bimap.get_value("one"), Some(1));
}
#[test]
fn test_contains() {
let bimap = BijectiveMap::from_pairs([("hello", 42)]);
assert!(bimap.contains_term("hello"));
assert!(!bimap.contains_term("world"));
assert!(bimap.contains_value(&42));
assert!(!bimap.contains_value(&999));
}
#[test]
fn test_unicode_terms() {
let bimap = BijectiveMap::from_pairs([("café", 1), ("日本語", 2), ("🎉", 3), ("naïve", 4)]);
assert_eq!(bimap.get_value("café"), Some(1));
assert_eq!(bimap.get_value("日本語"), Some(2));
assert_eq!(bimap.get_value("🎉"), Some(3));
assert_eq!(bimap.get_value("naïve"), Some(4));
assert_eq!(bimap.get_term(&1), Some("café".to_string()));
assert_eq!(bimap.get_term(&2), Some("日本語".to_string()));
assert_eq!(bimap.get_term(&3), Some("🎉".to_string()));
assert_eq!(bimap.get_term(&4), Some("naïve".to_string()));
}
#[test]
fn test_string_values() {
let bimap: BijectiveMap<String> = BijectiveMap::new();
bimap.insert("key1", "value1".to_string());
bimap.insert("key2", "value2".to_string());
assert_eq!(bimap.get_value("key1"), Some("value1".to_string()));
assert_eq!(
bimap.get_term(&"value1".to_string()),
Some("key1".to_string())
);
}
#[test]
fn test_bijection_invariant() {
let bimap = BijectiveMap::from_pairs([("a", 1), ("b", 2), ("c", 3)]);
for (term, value) in bimap.iter() {
assert_eq!(bimap.get_value(&term), Some(value.clone()));
assert_eq!(bimap.get_term(&value), Some(term));
}
}
#[test]
fn test_iter() {
let bimap = BijectiveMap::from_pairs([("x", 1), ("y", 2), ("z", 3)]);
let pairs: Vec<_> = bimap.iter().collect();
assert_eq!(pairs.len(), 3);
assert!(pairs.contains(&("x".to_string(), 1)));
assert!(pairs.contains(&("y".to_string(), 2)));
assert!(pairs.contains(&("z".to_string(), 3)));
}
#[test]
fn test_terms_and_values() {
let bimap = BijectiveMap::from_pairs([("a", 1), ("b", 2)]);
let terms: Vec<_> = bimap.terms().collect();
let values: Vec<_> = bimap.values().collect();
assert_eq!(terms.len(), 2);
assert!(terms.contains(&"a".to_string()));
assert!(terms.contains(&"b".to_string()));
assert_eq!(values.len(), 2);
assert!(values.contains(&1));
assert!(values.contains(&2));
}
#[test]
fn test_mapped_dictionary_trait() {
use crate::MappedDictionary;
let bimap = BijectiveMap::from_pairs([("test", 42)]);
assert_eq!(MappedDictionary::get_value(&bimap, "test"), Some(42));
assert_eq!(MappedDictionary::get_value(&bimap, "missing"), None);
}
#[test]
fn test_bijective_dictionary_trait() {
use crate::bijective::BijectiveDictionary;
let bimap = BijectiveMap::from_pairs([("test", 42)]);
let term = BijectiveDictionary::get_term(&bimap, &42);
assert_eq!(term.as_deref(), Some("test"));
assert!(BijectiveDictionary::get_term(&bimap, &99).is_none());
assert!(BijectiveDictionary::contains_value(&bimap, &42));
assert!(!BijectiveDictionary::contains_value(&bimap, &99));
assert_eq!(BijectiveDictionary::bijection_len(&bimap), 1);
}
#[test]
fn test_with_capacity() {
let bimap: BijectiveMap<i32> = BijectiveMap::with_capacity(1000);
assert!(bimap.is_empty());
bimap.insert("test", 1);
assert_eq!(bimap.get_value("test"), Some(1));
}
}