use crate::engine;
use crate::types::{max, min, min1, Dmp};
use std::collections::HashMap;
#[allow(clippy::ptr_arg)]
impl Dmp {
pub fn match_main(&mut self, text1: &str, patern1: &str, mut loc: i32) -> i32 {
loc = max(0, min(loc, text1.len() as i32));
if patern1.is_empty() {
return loc;
}
if text1.is_empty() {
return -1;
}
let text: Vec<char> = text1.chars().collect();
let patern: Vec<char> = patern1.chars().collect();
match_clamped(self, &text, &patern, loc)
}
pub fn match_bitap(&mut self, text: &Vec<char>, patern: &Vec<char>, loc: i32) -> i32 {
bitap(self, text, patern, loc)
}
pub fn match_bitap_score(&mut self, e: i32, x: i32, loc: i32, patern: &Vec<char>) -> f32 {
bitap_score(self, e, x, loc, patern.len())
}
pub fn match_alphabet(&mut self, patern: &Vec<char>) -> HashMap<char, i32> {
alphabet(patern)
.into_iter()
.map(|(ch, mask)| (ch, mask as u32 as i32))
.collect()
}
}
pub(crate) fn match_chars(dmp: &mut Dmp, text: &[char], patern: &[char], loc: i32) -> i32 {
let loc = max(0, min(loc, text.len() as i32));
if patern.is_empty() {
return loc;
}
if text.is_empty() {
return -1;
}
match_clamped(dmp, text, patern, loc)
}
fn match_clamped(dmp: &mut Dmp, text: &[char], patern: &[char], loc: i32) -> i32 {
if text == patern {
return 0;
} else if loc as usize + patern.len() <= text.len()
&& text[(loc as usize)..(loc as usize + patern.len())] == *patern
{
return loc;
}
bitap(dmp, text, patern, loc)
}
fn bitap(dmp: &mut Dmp, text: &[char], patern: &[char], loc: i32) -> i32 {
if !(dmp.match_maxbits == 0 || patern.len() as i32 <= dmp.match_maxbits) {
panic!("patern too long for this application");
}
if patern.len() > 64 {
panic!("patern too long for this application");
}
let s: HashMap<char, u64> = alphabet(patern);
let mut score_threshold: f32 = dmp.match_threshold;
let mut best_loc = match engine::find_sub(text, patern, loc as usize) {
Some(i) => i as i32,
None => -1,
};
if best_loc != -1 {
score_threshold = min1(
bitap_score(dmp, 0, best_loc, loc, patern.len()),
score_threshold,
);
best_loc = match engine::rfind_sub(text, patern, loc as usize + patern.len()) {
Some(i) => i as i32,
None => -1,
};
if best_loc != -1 {
score_threshold = min1(
score_threshold,
bitap_score(dmp, 0, best_loc, loc, patern.len()),
);
}
}
let matchmask: u64 = 1 << (patern.len() - 1);
best_loc = -1;
let mut bin_min: i32;
let mut bin_mid: i32;
let mut bin_max: i32 = (patern.len() + text.len()) as i32;
let mut last_rd: Vec<u64> = vec![];
for d in 0..patern.len() {
let mut rd: Vec<u64> = vec![];
bin_min = 0;
bin_mid = bin_max;
while bin_min < bin_mid {
if bitap_score(dmp, d as i32, loc + bin_mid, loc, patern.len()) <= score_threshold {
bin_min = bin_mid;
} else {
bin_max = bin_mid;
}
bin_mid = bin_min + (bin_max - bin_min) / 2;
}
bin_max = bin_mid;
let mut start = max(1, loc - bin_mid + 1);
let finish = min(loc + bin_mid, text.len() as i32) + patern.len() as i32;
rd.resize((finish + 2) as usize, 0);
rd[(finish + 1) as usize] = (1u64 << d) - 1;
let mut j = finish;
while j >= start {
let char_match: u64;
if text.len() < j as usize {
char_match = 0;
} else {
match s.get(&(text[j as usize - 1])) {
Some(num) => {
char_match = *num;
}
None => {
char_match = 0;
}
}
}
if d == 0 {
rd[j as usize] = ((rd[j as usize + 1] << 1) | 1) & char_match;
} else {
rd[j as usize] = (((rd[j as usize + 1] << 1) | 1) & char_match)
| (((last_rd[j as usize + 1] | last_rd[j as usize]) << 1) | 1)
| last_rd[j as usize + 1];
}
if (rd[j as usize] & matchmask) != 0 {
let score: f32 = bitap_score(dmp, d as i32, j - 1, loc, patern.len());
if score <= score_threshold {
score_threshold = score;
best_loc = j - 1;
if best_loc > loc {
start = max(1, 2 * loc - best_loc);
} else {
break;
}
}
}
j -= 1;
}
if bitap_score(dmp, d as i32 + 1, loc, loc, patern.len()) > score_threshold {
break;
}
last_rd = rd;
}
best_loc
}
fn bitap_score(dmp: &Dmp, e: i32, x: i32, loc: i32, patern_len: usize) -> f32 {
let accuracy: f32 = (e as f32) / (patern_len as f32);
let proximity: i32 = (loc - x).abs();
if dmp.match_distance == 0 {
if proximity == 0 {
return accuracy;
} else {
return 1.0;
}
}
accuracy + ((proximity as f32) / (dmp.match_distance as f32))
}
fn alphabet(patern: &[char]) -> HashMap<char, u64> {
let mut s: HashMap<char, u64> = HashMap::new();
for &ch in patern {
s.insert(ch, 0);
}
for (i, &ch) in patern.iter().enumerate() {
let mask = s[&ch] | (1u64 << (patern.len() - i - 1));
s.insert(ch, mask);
}
s
}