///|
fn common_prefix_length(left : String, right : String, maximum : Int) -> Int {
let shared = if left.length() < right.length() {
left.length()
} else {
right.length()
}
let limit = if shared < maximum { shared } else { maximum }
let mut prefix = 0
while prefix < limit && left[prefix] == right[prefix] {
prefix = prefix + 1
}
prefix
}
///|
/// Computes Jaro-Winkler similarity with a validated prefix scaling factor.
pub fn jaro_winkler_similarity(
left : String,
right : String,
scaling : Double,
) -> Result[Double, SimilarityError] {
if scaling.is_nan() || scaling.is_inf() || scaling < 0.0 || scaling > 0.25 {
return Err(InvalidWinklerScaling(scaling))
}
let base = jaro_similarity(left, right)
if base < 0.7 {
return Ok(base)
}
let prefix = common_prefix_length(left, right, 4)
let score = base + prefix.to_double() * scaling * (1.0 - base)
Ok(if score > 1.0 { 1.0 } else { score })
}