use crate::math::general::NumTools;
pub trait StringUtils {
#[must_use]
fn char_at(&self, index: usize) -> Option<char>;
#[must_use]
fn byte_at(&self, index: usize) -> u8;
fn validate_brackets(&self) -> Result<(), usize>;
fn is_alphanumeric(&self) -> Result<(), usize>;
fn is_numeric(&self) -> Result<(), usize>;
#[must_use]
fn similarity_with(&self, other: &str) -> f32;
#[must_use]
fn levenshtein_dist_with(&self, other: &str) -> usize;
#[must_use]
fn to_isize(&self) -> Option<isize>;
}
impl StringUtils for String {
#[inline]
fn char_at(&self, index: usize) -> Option<char> {
self.chars().nth(index)
}
#[inline]
fn byte_at(&self, index: usize) -> u8 {
self.as_bytes()[index]
}
#[inline]
fn validate_brackets(&self) -> Result<(), usize> {
backend_val_brackets(self)
}
#[inline]
fn is_alphanumeric(&self) -> Result<(), usize> {
backend_alphanumeric(self)
}
#[inline]
fn is_numeric(&self) -> Result<(), usize> {
backend_numeric(self)
}
#[inline]
fn similarity_with(&self, other: &str) -> f32 {
backend_sim(self, other)
}
#[inline]
fn levenshtein_dist_with(&self, other: &str) -> usize {
backend_levenshtein(self, other)
}
#[inline]
fn to_isize(&self) -> Option<isize> {
backend_to_isize(self)
}
}
impl StringUtils for str {
#[inline]
fn char_at(&self, index: usize) -> Option<char> {
self.chars().nth(index)
}
#[inline]
fn byte_at(&self, index: usize) -> u8 {
self.as_bytes()[index]
}
#[inline]
fn validate_brackets(&self) -> Result<(), usize> {
backend_val_brackets(self)
}
#[inline]
fn is_alphanumeric(&self) -> Result<(), usize> {
backend_alphanumeric(self)
}
#[inline]
fn is_numeric(&self) -> Result<(), usize> {
backend_numeric(self)
}
#[inline]
fn similarity_with(&self, other: &str) -> f32 {
backend_sim(self, other)
}
#[inline]
fn levenshtein_dist_with(&self, other: &str) -> usize {
backend_levenshtein(self, other)
}
#[inline]
fn to_isize(&self) -> Option<isize> {
backend_to_isize(self)
}
}
fn backend_val_brackets(s: &str) -> Result<(), usize> {
let mut res_vec: Vec<char> = Vec::with_capacity(s.len());
let mut i: usize = 0;
for c in s.chars() {
match c {
'[' | '{' | '(' => { res_vec.push(c); },
']' if res_vec.pop() != Some('[') => { return Err(i); }
'}' if res_vec.pop() != Some('{') => { return Err(i); }
')' if res_vec.pop() != Some('(') => { return Err(i); }
_ => { }
}
i.inc();
}
if !res_vec.is_empty()
{ return Err(i); }
Ok(())
}
fn backend_numeric(s: &str) -> Result<(), usize> {
for c in s.chars().enumerate() {
if !(c.1 as u8).is_in_range(48, 57)
{ return Err(c.0); }
}
Ok(())
}
fn backend_alphanumeric(s: &str) -> Result<(), usize> {
for c in s.chars().enumerate() {
if !((c.1 >= '0' && c.1 <= '9') ||
(c.1 >= 'a' && c.1 <= 'z') ||
(c.1 >= 'A' && c.1 <= 'Z'))
{ return Err(c.0); }
}
Ok(())
}
fn backend_sim(s1: &str, s2: &str) -> f32 {
let mut sum = 0;
match s1.len() > s2.len() {
true => {
for i in s2.chars().enumerate() {
if unsafe { s1.char_at(i.0).unwrap_unchecked() } == i.1
{ sum.inc(); }
}
}
false => {
for i in s1.chars().enumerate() {
if unsafe { s2.char_at(i.0).unwrap_unchecked() } == i.1
{ sum.inc(); }
}
}
}
sum as f32 / std::cmp::max(s1.len(), s2.len()) as f32
}
fn backend_levenshtein(str1: &str, str2: &str) -> usize {
if str1 == str2
{ return 0; }
let mut res = 0;
let str1_len = str1.chars().count();
let str2_len = str2.chars().count();
if str1_len == 0
{ return str2_len; }
if str2_len == 0
{ return str1_len; }
let mut buffer: Vec<usize> = (1..).take(str1_len).collect();
let mut str1_dist: usize;
let mut str2_dist: usize;
for (index_str2, char_str2) in str2.chars().enumerate() {
res = index_str2;
str1_dist = index_str2;
for (index_str1, char_str1) in str1.chars().enumerate() {
str2_dist =
match char_str1 == char_str2 {
true => { str1_dist }
false => { str1_dist + 1 }
};
str1_dist = buffer[index_str1];
res =
if str1_dist > res {
match str2_dist > res {
true => { res + 1 }
false => { str2_dist }
}
}
else if str1_dist < str2_dist
{ str1_dist + 1 }
else
{ str2_dist };
buffer[index_str1] = res;
}
}
res
}
fn backend_to_isize(s: &str) -> Option<isize> {
let _s = s.trim();
let mut res = 0;
let flag = _s.starts_with('-');
let mut power_of_ten = 1;
for (i, character) in _s.chars().rev().enumerate() {
if character == '-' && i != _s.len() - 1
{ return None; }
match character {
'1' => res.inc_by( power_of_ten),
'2' => res.inc_by(2 * power_of_ten),
'3' => res.inc_by(3 * power_of_ten),
'4' => res.inc_by(4 * power_of_ten),
'5' => res.inc_by(5 * power_of_ten),
'6' => res.inc_by(6 * power_of_ten),
'7' => res.inc_by(7 * power_of_ten),
'8' => res.inc_by(8 * power_of_ten),
'9' => res.inc_by(9 * power_of_ten),
_ => { }
}
if !character.is_whitespace()
{ power_of_ten *= 10; }
}
if flag
{ res *= -1; }
Some(res)
}
#[must_use]
pub const fn strcmp(s1: &str, s2: &str) -> i16 {
let mut i: usize = 0;
let mut flag: i16 = 0;
while flag == 0 {
flag = s1.as_bytes()[i] as i16 - s2.as_bytes()[i] as i16;
i += 1;
if i == s1.len()
{ break; }
}
flag
}