code never broke

never has ide reported "problems (43)"
This commit is contained in:
lda committed 2025-01-15 21:45:56 +07:00
1 parent de5d5cc62c
commit af82016d1c
3 files changed
+261 -119

No files matched your search

View File
Whitespace-only changes.
+260 -119
View File
@@ -7,12 +7,120 @@
/// position? idk /// position? idk
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum EditOperation<T> { pub enum EditOperation<T> {
Insert { elem: T }, // Insert a character from the target string Insert { elem: T },
Delete { elem: T }, // Delete a character from the source string Delete { elem: T },
Substitute { from: T, to: T }, // Replace one character with another Substitute { from: T, to: T },
Match { elem: T }, // Characters match, no operation Match { elem: T },
} }
#[derive(Debug, Clone, PartialEq)]
pub enum MiniEditOperation {
Insert,
Delete,
Substitute,
Match,
}
// trait EditOperationCoords {
// fn coord_offset(self) -> (i32, i32);
// }
// // impl<T> EditOperationCoords for EditOperation<T> {
// // fn coord_offset(&self) -> (i32, i32) {
// // use EditOperation::*;
// // match self {
// // Delete { .. } => (-1, 0),
// // Insert { .. } => (0, -1),
// // Substitute { .. } | Match { .. } => (-1, -1),
// // }
// // }
// // }
// impl<U> EditOperationCoords for U
// where
// MiniEditOperation: From<U>,
// {
// fn coord_offset(self) -> (i32, i32) {
// use MiniEditOperation::*;
// match MiniEditOperation::from(self) {
// Delete => (-1, 0),
// Insert => (0, -1),
// Substitute | Match => (-1, -1),
// }
// }
// }
impl<T> From<EditOperation<T>> for MiniEditOperation {
fn from(value: EditOperation<T>) -> Self {
match value {
EditOperation::Delete { .. } => Self::Delete,
EditOperation::Insert { .. } => Self::Insert,
EditOperation::Match { .. } => Self::Match,
EditOperation::Substitute { .. } => Self::Substitute,
}
}
}
impl<T> From<&EditOperation<T>> for MiniEditOperation {
fn from(value: &EditOperation<T>) -> Self {
match value {
EditOperation::Delete { .. } => Self::Delete,
EditOperation::Insert { .. } => Self::Insert,
EditOperation::Match { .. } => Self::Match,
EditOperation::Substitute { .. } => Self::Substitute,
}
}
}
trait ToEditOperation {
fn full<T>(
&self,
maybe_s1: Option<T>,
maybe_s2: Option<T>,
) -> Result<EditOperation<T>,()>;
}
impl ToEditOperation for MiniEditOperation {
fn full<T>(
&self, // edit_operation
maybe_s1: Option<T>,
maybe_s2: Option<T>,
) -> Result<EditOperation<T>, ()> {
match self {
MiniEditOperation::Insert => {
if let Some(s2) = maybe_s2 {
Ok(EditOperation::Insert { elem: s2 })
} else {
Err(())
}
}
MiniEditOperation::Delete => {
if let Some(s1) = maybe_s1 {
Ok(EditOperation::Delete { elem: s1 })
} else {
Err(())
}
}
MiniEditOperation::Substitute => {
if let Some(s1) = maybe_s1 {
if let Some(s2) = maybe_s2 {
Ok(EditOperation::Substitute { from: s1, to: s2 })
} else {
Err(())
}
} else {
Err(())
}
}
MiniEditOperation::Match => {
if let Some(s1) = maybe_s1 {
Ok(EditOperation::Match { elem: s1 })
} else if let Some(s2) = maybe_s2 {
Ok(EditOperation::Match { elem: s2 })
} else {
Err(())
}
}
}
}
}
pub struct EditCost { pub struct EditCost {
pub insert: i32, pub insert: i32,
pub delete: i32, pub delete: i32,
@@ -38,13 +146,47 @@ impl EditCost {
} }
} }
} }
/// custom cost for each [`EditOperation`]s operation
pub trait CustomCost<T> { /// custom cost for operations
fn cost(&self, operation: &EditOperation<T>) -> i32; ///
/// ```
/// use lda_strdist::levenshtein::general::{EditOperation, CustomCost};
/// struct MyCost {}
/// impl CustomCost<&EditOperation<char>> for MyCost{
/// fn cost(&self, operation: &EditOperation<char>) -> i32 {
/// use EditOperation::*;
/// match operation {
/// Substitute{from: a, to: b} =>{
/// if *a == 'b' && *b == 'n' {
/// println!("what he tryna pull");
/// -6
/// } else {1}
/// },
/// _ => 0 // do your own brih
/// }
/// }
/// }
/// ```
pub trait CustomCost<U>
where
MiniEditOperation: From<U>,
{
fn cost(&self, operation: U) -> i32;
} }
impl<T> CustomCost<T> for EditCost { impl CustomCost<MiniEditOperation> for EditCost {
fn cost(&self, operation: &EditOperation<T>) -> i32 { fn cost(&self, operation: MiniEditOperation) -> i32 {
match operation {
MiniEditOperation::Insert => self.insert,
MiniEditOperation::Delete => self.delete,
MiniEditOperation::Substitute => self.substitute,
MiniEditOperation::Match => 0,
}
}
}
impl<T> CustomCost<EditOperation<T>> for EditCost {
fn cost(&self, operation: EditOperation<T>) -> i32 {
match operation { match operation {
EditOperation::Insert { .. } => self.insert, EditOperation::Insert { .. } => self.insert,
EditOperation::Delete { .. } => self.delete, EditOperation::Delete { .. } => self.delete,
@@ -56,13 +198,18 @@ impl<T> CustomCost<T> for EditCost {
/// cost of operation and the operation itself /// cost of operation and the operation itself
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CostOperation<T> { pub struct CostOperation<U>
where
MiniEditOperation: From<U>,
{
pub cost: i32, pub cost: i32,
pub operation: EditOperation<T>, pub operation: U,
} }
// absolutely phenomenal naming // absolutely phenomenal naming
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
// use super::CostOperation;
impl<T: Debug> Display for EditOperation<T> { impl<T: Debug> Display for EditOperation<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
@@ -76,9 +223,9 @@ impl<T: Debug> Display for EditOperation<T> {
} }
} }
impl<T: Debug> Display for CostOperation<T> { impl<T: Debug> Display for CostOperation<EditOperation<T>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Cost: {}, Operation: {:#?}", self.cost, self.operation) write!(f, "Cost: {}, Operation: {}", self.cost, self.operation)
} }
} }
/// returns the distance + the dp table for who needs it /// returns the distance + the dp table for who needs it
@@ -88,11 +235,12 @@ pub fn generalized_levenshtein<T, F>(
seq1: &[T], seq1: &[T],
seq2: &[T], seq2: &[T],
cost_calc: &F, cost_calc: &F,
) -> (i32, Vec<Vec<Option<CostOperation<T>>>>) ) -> (i32, Vec<Vec<Option<CostOperation<MiniEditOperation>>>>)
where where
T: PartialEq + Clone + Debug, T: PartialEq + Clone + Debug,
F: CustomCost<T>, F: CustomCost<EditOperation<T>>,
{ {
use MiniEditOperation::*;
let len1 = seq1.len(); let len1 = seq1.len();
let len2 = seq2.len(); let len2 = seq2.len();
@@ -100,111 +248,115 @@ where
// Fill the base cases // Fill the base cases
for i in 1..=len1 { for i in 1..=len1 {
dp[i][0] = if i > 0 { dp[i][0] = Some(CostOperation {
Some(CostOperation { cost: i as i32,
cost: i as i32, operation: Delete,
operation: EditOperation::Delete { });
elem: seq1[i - 1].clone(),
},
})
} else {
None
}
} }
for j in 0..=len2 { for j in 1..=len2 {
dp[0][j] = if j > 0 { dp[0][j] = Some(CostOperation {
Some(CostOperation { cost: j as i32,
cost: j as i32, operation: Insert,
operation: EditOperation::Insert { });
elem: seq2[j - 1].clone(), }
},
}) // Handle the case where either sequence is empty
} else { if len1 == 0 || len2 == 0 {
None return (dp[len1][len2].as_ref().unwrap().cost, dp);
};
} }
// Fill the DP table // Fill the DP table
for i in 1..=len1 { for i in 1..=len1 {
for j in 1..=len2 { for j in 1..=len2 {
let cost = seq1[i - 1] == seq2[j - 1]; let cost = seq1[i - 1] == seq2[j - 1];
let delete_op = EditOperation::Delete { let delete_cost = dp[i - 1][j].as_ref().map(|s| {
elem: seq1[i - 1].clone(), s.cost
}; + cost_calc.cost(EditOperation::Delete {
let delete_cost = dp[i - 1][j] elem: seq1[i - 1].clone(),
.as_ref() })
.and_then(|s| Some(s.cost + cost_calc.cost(&delete_op))); });
let insert_op = EditOperation::Insert { let insert_cost = dp[i][j - 1].as_ref().map(|c| {
elem: seq2[j - 1].clone(), c.cost
}; + cost_calc.cost(EditOperation::Insert {
let insert_cost = dp[i][j - 1] elem: seq2[j - 1].clone(),
.as_ref() })
.and_then(|c| Some(c.cost + cost_calc.cost(&insert_op))); });
let sub_op = if cost {
EditOperation::Match {
elem: seq1[i - 1].clone(),
}
} else {
EditOperation::Substitute {
from: seq1[i - 1].clone(),
to: seq2[j - 1].clone(),
}
};
let substitute_cost = dp[i - 1][j - 1] let sub_op = if cost { Match } else { Substitute };
.as_ref() let substitute_cost = dp[i - 1][j - 1].as_ref().map(|c| {
.and_then(|c| Some(c.cost + cost_calc.cost(&sub_op))); c.cost
+ cost_calc.cost(if cost {
EditOperation::Match {
elem: seq1[i - 1].clone(),
}
} else {
EditOperation::Substitute {
from: seq1[i - 1].clone(),
to: seq2[j - 1].clone(),
}
})
});
dp[i][j] = [ dp[i][j] = [
(delete_cost, delete_op), (delete_cost, Delete),
(insert_cost, insert_op), (insert_cost, Insert),
(substitute_cost, sub_op), (substitute_cost, sub_op),
] ]
.into_iter() .into_iter()
.filter_map(|(c, o)| c.and_then(|c| Some((c, o)))) .filter_map(|(c, o)| c.and_then(|c| Some((c, o))))
.min_by_key(|c| c.0) .min_by_key(|c| c.0)
.and_then(|(c, o)| { .map(|(c, o)| CostOperation {
Some(CostOperation { cost: c,
cost: c, operation: o,
operation: o,
})
}); });
} }
} }
(dp[len1][len2].as_ref().unwrap().cost, dp) (dp[len1][len2].as_ref().unwrap().cost, dp)
} }
///extract the optimized edit sequence from the dp table ///extract the optimized edit sequence from the dp table
/// ///
/// strs there just for the len() lmfao /// strs there just for the len() lmfao
pub fn edit_sequence<T: Clone>(dp: &Vec<Vec<Option<CostOperation<T>>>>) -> Vec<EditOperation<T>> { pub fn edit_sequence<T: Clone>(
seq1: &[T],
seq2: &[T],
dp: &[Vec<Option<CostOperation<MiniEditOperation>>>],
) -> Vec<EditOperation<T>> {
let mut i = dp.len() - 1; let mut i = dp.len() - 1;
let mut j = dp[0].len() - 1; let mut j = dp[0].len() - 1;
let mut funny = Vec::new(); let mut funny = Vec::new();
while let Some(op) = dp while let Some(op) = dp
.get(i) .get(i)
.and_then(|row| row.get(j)) .and_then(|row| row.get(j))
.and_then(|op| Some(op.as_ref().unwrap().operation.clone())) .and_then(|op| op.as_ref())
{ {
match op { match op.operation {
EditOperation::Insert { .. } => j -= 1, MiniEditOperation::Insert => j -= 1,
EditOperation::Delete { .. } => i -= 1, MiniEditOperation::Delete => i -= 1,
EditOperation::Substitute { .. } | EditOperation::Match { .. } => { MiniEditOperation::Substitute | MiniEditOperation::Match => {
i -= 1; i -= 1;
j -= 1; j -= 1;
} }
} }
funny.push(op); funny.push(
MiniEditOperation::full(
&op.operation,
seq1.get(i).cloned(),
seq2.get(j).cloned(),
)
.unwrap(),
);
} }
funny.reverse(); funny.reverse();
funny funny
} }
pub fn generate_alignment<T: Clone + Debug>( pub fn generate_alignment<T: Clone + Debug>(
operations: &Vec<Vec<Option<CostOperation<T>>>>, operations: &[Vec<Option<CostOperation<EditOperation<T>>>>],
) -> (Vec<T>, Vec<T>) { ) -> (Vec<T>, Vec<T>) {
let mut aligned_seq1 = Vec::new(); let mut aligned_seq1 = Vec::new();
let mut aligned_seq2 = Vec::new(); let mut aligned_seq2 = Vec::new();
@@ -213,22 +365,22 @@ pub fn generate_alignment<T: Clone + Debug>(
let mut j = operations[0].len() - 1; let mut j = operations[0].len() - 1;
while let Some(op) = &operations[i][j] { while let Some(op) = &operations[i][j] {
match op.operation { match &op.operation {
EditOperation::Insert { ref elem } => { EditOperation::Insert { elem } => {
aligned_seq2.push(elem.clone()); aligned_seq2.push(elem.clone());
j -= 1; j -= 1;
} }
EditOperation::Delete { ref elem } => { EditOperation::Delete { elem } => {
aligned_seq1.push(elem.clone()); aligned_seq1.push(elem.clone());
i -= 1; i -= 1;
} }
EditOperation::Substitute { ref from, ref to } => { EditOperation::Substitute { from, to } => {
aligned_seq1.push(from.clone()); aligned_seq1.push(from.clone());
aligned_seq2.push(to.clone()); aligned_seq2.push(to.clone());
i -= 1; i -= 1;
j -= 1; j -= 1;
} }
EditOperation::Match { ref elem } => { EditOperation::Match { elem } => {
aligned_seq1.push(elem.clone()); aligned_seq1.push(elem.clone());
aligned_seq2.push(elem.clone()); aligned_seq2.push(elem.clone());
i -= 1; i -= 1;
@@ -250,47 +402,36 @@ pub fn generate_alignment<T: Clone + Debug>(
pub fn levenstein_distance<T: PartialEq + Clone + Debug>( pub fn levenstein_distance<T: PartialEq + Clone + Debug>(
seq1: &[T], seq1: &[T],
seq2: &[T], seq2: &[T],
cost: &impl CustomCost<T>, cost: &impl CustomCost<EditOperation<T>>,
) -> (i32, Vec<EditOperation<T>>) { ) -> (i32, Vec<EditOperation<T>>)
where
MiniEditOperation: From<EditOperation<T>>,
{
let (c, d) = generalized_levenshtein(seq1, seq2, cost); let (c, d) = generalized_levenshtein(seq1, seq2, cost);
(c, edit_sequence(&d)) (c, edit_sequence(seq1, seq2, &d))
} }
/// get source str from [`edit_sequence`] /// get source str from [`edit_sequence`]
pub fn reconstruct_from<T: Clone>(vector: &Vec<EditOperation<T>>) -> Vec<T> { pub fn reconstruct_from<T: Clone>(vector: &[EditOperation<T>]) -> Vec<T> {
let mut result = Vec::new(); vector
for operation in vector { .iter()
match operation { .filter_map(|operation| match operation {
EditOperation::Insert { .. } => {} EditOperation::Delete { elem } => Some(elem.clone()),
EditOperation::Delete { elem } => { EditOperation::Substitute { from, .. } => Some(from.clone()),
result.push(elem.clone()); EditOperation::Match { elem } => Some(elem.clone()),
} _ => None,
EditOperation::Substitute { from, .. } => { })
result.push(from.clone()); .collect()
}
EditOperation::Match { elem } => {
result.push(elem.clone());
}
}
}
result
} }
/// get changed str from [`edit_sequence`] /// get changed str from [`edit_sequence`]
pub fn reconstruct_to<T: Clone>(vector: &Vec<EditOperation<T>>) -> Vec<T> { pub fn reconstruct_to<T: Clone>(vector: &[EditOperation<T>]) -> Vec<T> {
let mut result = Vec::new(); vector
for operation in vector { .iter()
match operation { .filter_map(|operation| match operation {
EditOperation::Insert { elem } => { EditOperation::Insert { elem } => Some(elem.clone()),
result.push(elem.clone()); EditOperation::Substitute { to, .. } => Some(to.clone()),
} EditOperation::Match { elem } => Some(elem.clone()),
EditOperation::Delete { .. } => {} _ => None,
EditOperation::Substitute { to, .. } => { })
result.push(to.clone()); .collect()
}
EditOperation::Match { elem } => {
result.push(elem.clone());
}
}
}
result
} }
+1
View File
@@ -1,2 +1,3 @@
pub mod levenshtein; pub mod levenshtein;
pub mod common_subsequence;
pub mod wrapper_; pub mod wrapper_;