use lling_llang::semiring::{LogWeight, Semiring, TropicalWeight};
use lling_llang::wfst::{MutableWfst, VectorWfst, Wfst};
use proptest::prelude::*;
fn build_simple_fst() -> VectorWfst<char, TropicalWeight> {
let mut fst: VectorWfst<char, TropicalWeight> = VectorWfst::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
let s2 = fst.add_state();
fst.set_start(s0);
fst.set_final(s2, TropicalWeight::one());
fst.add_arc(s0, Some('a'), Some('a'), s1, TropicalWeight::new(1.0));
fst.add_arc(s1, Some('b'), Some('b'), s2, TropicalWeight::new(2.0));
fst
}
fn build_epsilon_fst() -> VectorWfst<char, TropicalWeight> {
let mut fst: VectorWfst<char, TropicalWeight> = VectorWfst::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
let s2 = fst.add_state();
let s3 = fst.add_state();
fst.set_start(s0);
fst.set_final(s3, TropicalWeight::one());
fst.add_arc(s0, Some('a'), Some('a'), s1, TropicalWeight::new(1.0));
fst.add_arc(s1, None, None, s2, TropicalWeight::new(0.5)); fst.add_arc(s2, Some('b'), Some('b'), s3, TropicalWeight::new(2.0));
fst
}
fn build_nondet_fst() -> VectorWfst<char, TropicalWeight> {
let mut fst: VectorWfst<char, TropicalWeight> = VectorWfst::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
let s2 = fst.add_state();
let s3 = fst.add_state();
fst.set_start(s0);
fst.set_final(s2, TropicalWeight::one());
fst.set_final(s3, TropicalWeight::one());
fst.add_arc(s0, Some('a'), Some('x'), s1, TropicalWeight::new(1.0));
fst.add_arc(s0, Some('a'), Some('y'), s2, TropicalWeight::new(2.0));
fst.add_arc(s1, Some('b'), Some('z'), s3, TropicalWeight::new(1.0));
fst
}
fn build_disconnected_fst() -> VectorWfst<char, TropicalWeight> {
let mut fst: VectorWfst<char, TropicalWeight> = VectorWfst::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
let s2 = fst.add_state(); let s3 = fst.add_state();
fst.set_start(s0);
fst.set_final(s1, TropicalWeight::one());
fst.add_arc(s0, Some('a'), Some('a'), s1, TropicalWeight::new(1.0));
fst.add_arc(s2, Some('b'), Some('b'), s1, TropicalWeight::new(2.0)); fst.add_arc(s0, Some('c'), Some('c'), s3, TropicalWeight::new(3.0));
fst
}
fn build_chain_fst(length: usize) -> VectorWfst<u32, TropicalWeight> {
let mut fst: VectorWfst<u32, TropicalWeight> = VectorWfst::new();
if length == 0 {
let s = fst.add_state();
fst.set_start(s);
fst.set_final(s, TropicalWeight::one());
return fst;
}
let mut prev = fst.add_state();
fst.set_start(prev);
for i in 0..length {
let next = fst.add_state();
fst.add_arc(
prev,
Some(i as u32),
Some(i as u32),
next,
TropicalWeight::new(1.0),
);
prev = next;
}
fst.set_final(prev, TropicalWeight::one());
fst
}
fn build_identity_fst(alphabet_size: usize) -> VectorWfst<u32, TropicalWeight> {
let mut fst: VectorWfst<u32, TropicalWeight> = VectorWfst::new();
let s0 = fst.add_state();
fst.set_start(s0);
fst.set_final(s0, TropicalWeight::one());
for label in 0..alphabet_size {
fst.add_arc(
s0,
Some(label as u32),
Some(label as u32),
s0,
TropicalWeight::one(),
);
}
fst
}
#[test]
fn test_connect_simple() {
use lling_llang::algorithms::{connect, ConnectConfig};
let mut fst: VectorWfst<char, TropicalWeight> = VectorWfst::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
let _s2 = fst.add_state();
fst.set_start(s0);
fst.set_final(s1, TropicalWeight::one());
fst.add_arc(s0, Some('a'), Some('a'), s1, TropicalWeight::new(1.0));
let original_states = fst.num_states();
let removed = connect(&mut fst, ConnectConfig::default());
assert!(removed > 0 || fst.num_states() <= original_states);
}
#[test]
fn test_connect_removes_unreachable() {
use lling_llang::algorithms::{
compute_accessible, connect, count_useful_states, ConnectConfig,
};
let mut fst = build_disconnected_fst();
let original_useful = count_useful_states(&fst);
let removed = connect(&mut fst, ConnectConfig::default());
assert!(
removed > 0 || original_useful == fst.num_states(),
"Should remove at least one state or all were already useful"
);
let accessible = compute_accessible(&fst);
assert!(
accessible.contains(&fst.start()),
"Start state should be accessible"
);
}
#[test]
fn test_connect_removes_non_coaccessible() {
use lling_llang::algorithms::{
compute_coaccessible, connect, count_useful_states, ConnectConfig,
};
let mut fst = build_disconnected_fst();
let useful_before = count_useful_states(&fst);
let _removed = connect(&mut fst, ConnectConfig::default());
let useful_after = count_useful_states(&fst);
assert!(
useful_after <= useful_before || useful_after == fst.num_states(),
"Useful states should not increase after connect"
);
let coaccessible = compute_coaccessible(&fst);
for state in 0..fst.num_states() {
if fst.is_final(state as u32) {
assert!(
coaccessible.contains(&(state as u32)),
"Final state {} should be coaccessible",
state
);
}
}
}
#[test]
fn test_is_connected() {
use lling_llang::algorithms::is_connected;
let simple = build_simple_fst();
assert!(is_connected(&simple), "Simple FST should be connected");
let chain = build_chain_fst(3);
assert!(is_connected(&chain), "Chain FST should be connected");
let disconnected = build_disconnected_fst();
let is_conn = is_connected(&disconnected);
assert!(!is_conn, "Disconnected FST should not be connected");
}
#[test]
fn test_count_useful_states() {
use lling_llang::algorithms::count_useful_states;
let fst = build_simple_fst();
let count = count_useful_states(&fst);
assert_eq!(
count,
fst.num_states(),
"All states should be useful in simple FST"
);
let disconnected = build_disconnected_fst();
let count_disconnected = count_useful_states(&disconnected);
assert!(
count_disconnected < disconnected.num_states(),
"Some states should be non-useful in disconnected FST"
);
}
#[test]
fn test_connect_idempotent() {
use lling_llang::algorithms::{connect, count_useful_states, ConnectConfig};
let mut fst = build_disconnected_fst();
let removed_first = connect(&mut fst, ConnectConfig::default());
let useful_after_first = count_useful_states(&fst);
let removed_second = connect(&mut fst, ConnectConfig::default());
let useful_after_second = count_useful_states(&fst);
assert_eq!(
useful_after_first, useful_after_second,
"Connect should be idempotent on useful states"
);
assert!(
removed_second <= removed_first,
"Second connect should remove <= first"
);
}
#[test]
fn test_determinize_simple() {
use lling_llang::algorithms::{determinize, DeterminizeConfig};
let mut fst: VectorWfst<char, TropicalWeight> = VectorWfst::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
let s2 = fst.add_state();
fst.set_start(s0);
fst.set_final(s1, TropicalWeight::one());
fst.set_final(s2, TropicalWeight::one());
fst.add_arc(s0, Some('a'), Some('a'), s1, TropicalWeight::new(1.0));
fst.add_arc(s0, Some('a'), Some('a'), s2, TropicalWeight::new(2.0));
let result = determinize(&fst, DeterminizeConfig::default());
match result {
Ok(det) => {
assert!(det.num_states() >= 1);
}
Err(_) => {
}
}
}
#[test]
fn test_determinize_output_is_deterministic() {
use lling_llang::algorithms::{determinize, is_deterministic, DeterminizeConfig};
let fst = build_nondet_fst();
let original_is_det = is_deterministic(&fst);
let result = determinize(&fst, DeterminizeConfig::default());
match result {
Ok(det) => {
assert!(
is_deterministic(&det),
"Determinized FST should be deterministic"
);
if !original_is_det {
}
}
Err(_) => {
}
}
}
#[test]
fn test_determinize_idempotent() {
use lling_llang::algorithms::{determinize, is_deterministic, DeterminizeConfig};
let fst = build_nondet_fst();
let result1 = determinize(&fst, DeterminizeConfig::default());
if let Ok(det1) = result1 {
let result2 = determinize(&det1, DeterminizeConfig::default());
if let Ok(det2) = result2 {
assert!(
is_deterministic(&det1),
"First result should be deterministic"
);
assert!(
is_deterministic(&det2),
"Second result should be deterministic"
);
assert_eq!(
det1.num_states(),
det2.num_states(),
"Determinization should be idempotent on state count"
);
}
}
}
#[test]
fn test_is_deterministic_predicate() {
use lling_llang::algorithms::is_deterministic;
let simple = build_simple_fst();
let nondet = build_nondet_fst();
assert!(
is_deterministic(&simple),
"Simple FST should be deterministic"
);
assert!(
!is_deterministic(&nondet),
"Non-deterministic FST should not be deterministic"
);
}
#[test]
fn test_non_determinism_degree() {
use lling_llang::algorithms::non_determinism_degree;
let simple = build_simple_fst();
let nondet = build_nondet_fst();
let simple_degree = non_determinism_degree(&simple);
let nondet_degree = non_determinism_degree(&nondet);
assert_eq!(simple_degree, 1, "Deterministic FST has degree 1");
assert!(nondet_degree > 1, "Non-deterministic FST has degree > 1");
}
#[test]
fn test_determinize_chain() {
use lling_llang::algorithms::{determinize, is_deterministic, DeterminizeConfig};
let chain = build_chain_fst(5);
assert!(
is_deterministic(&chain),
"Chain FST should be deterministic"
);
let result = determinize(&chain, DeterminizeConfig::default());
match result {
Ok(det) => {
assert!(is_deterministic(&det));
assert!(
det.num_states() <= chain.num_states() + 1,
"Determinized chain should have similar state count"
);
}
Err(_) => {
panic!("Determinization should succeed on deterministic input");
}
}
}
#[test]
fn test_minimize_simple() {
use lling_llang::algorithms::{minimize, MinimizeConfig};
let fst = build_simple_fst();
let result = minimize(&fst, MinimizeConfig::default());
match result {
Ok(min) => {
assert!(
min.num_states() <= fst.num_states() + 1,
"Minimized FST should not have more states"
);
}
Err(_) => {
}
}
}
#[test]
fn test_minimize_idempotent() {
use lling_llang::algorithms::{minimize, MinimizeConfig};
let fst = build_simple_fst();
let result1 = minimize(&fst, MinimizeConfig::default());
if let Ok(min1) = result1 {
let result2 = minimize(&min1, MinimizeConfig::default());
if let Ok(min2) = result2 {
assert_eq!(
min1.num_states(),
min2.num_states(),
"Minimization should be idempotent on state count"
);
}
}
}
#[test]
fn test_minimize_state_count_bound() {
use lling_llang::algorithms::{minimize, MinimizeConfig};
let mut fst: VectorWfst<u32, TropicalWeight> = VectorWfst::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
let s2 = fst.add_state();
let s3 = fst.add_state();
fst.set_start(s0);
fst.set_final(s2, TropicalWeight::one());
fst.set_final(s3, TropicalWeight::one());
fst.add_arc(s0, Some(0), Some(0), s1, TropicalWeight::new(1.0));
fst.add_arc(s1, Some(1), Some(1), s2, TropicalWeight::new(1.0));
fst.add_arc(s1, Some(2), Some(2), s3, TropicalWeight::new(1.0));
let original_states = fst.num_states();
let result = minimize(&fst, MinimizeConfig::default());
if let Ok(min) = result {
assert!(
min.num_states() <= original_states,
"Minimized should have <= original states"
);
}
}
#[test]
fn test_estimate_reduction() {
use lling_llang::algorithms::estimate_reduction;
let simple = build_simple_fst();
let estimate = estimate_reduction(&simple);
assert!(
estimate <= simple.num_states(),
"Reduction estimate should be <= num_states"
);
}
#[test]
fn test_epsilon_removal() {
use lling_llang::algorithms::{has_epsilon_transitions, remove_epsilon, EpsilonRemovalConfig};
let mut fst = build_epsilon_fst();
assert!(
has_epsilon_transitions(&fst),
"Original FST should have epsilons"
);
let result = remove_epsilon(&mut fst, EpsilonRemovalConfig::default());
match result {
Ok(()) => {
assert!(
!has_epsilon_transitions(&fst),
"Epsilon-removed FST should have no epsilons"
);
}
Err(_) => {
}
}
}
#[test]
fn test_has_epsilon_transitions() {
use lling_llang::algorithms::has_epsilon_transitions;
let simple = build_simple_fst();
let epsilon = build_epsilon_fst();
assert!(
!has_epsilon_transitions(&simple),
"Simple FST should have no epsilons"
);
assert!(
has_epsilon_transitions(&epsilon),
"Epsilon FST should have epsilons"
);
}
#[test]
fn test_epsilon_removal_no_epsilons() {
use lling_llang::algorithms::{has_epsilon_transitions, remove_epsilon, EpsilonRemovalConfig};
let mut fst = build_simple_fst();
let original_states = fst.num_states();
assert!(!has_epsilon_transitions(&fst));
let result = remove_epsilon(&mut fst, EpsilonRemovalConfig::default());
match result {
Ok(()) => {
assert!(!has_epsilon_transitions(&fst));
assert!(fst.num_states() <= original_states + 1);
}
Err(_) => {
}
}
}
#[test]
fn test_epsilon_removal_idempotent() {
use lling_llang::algorithms::{has_epsilon_transitions, remove_epsilon, EpsilonRemovalConfig};
let mut fst = build_epsilon_fst();
let result1 = remove_epsilon(&mut fst, EpsilonRemovalConfig::default());
if result1.is_ok() {
assert!(!has_epsilon_transitions(&fst));
let states_after_first = fst.num_states();
let result2 = remove_epsilon(&mut fst, EpsilonRemovalConfig::default());
if result2.is_ok() {
assert!(!has_epsilon_transitions(&fst));
assert_eq!(
states_after_first,
fst.num_states(),
"Epsilon removal should be idempotent on state count"
);
}
}
}
#[test]
fn test_remove_epsilon_star() {
use lling_llang::algorithms::{
has_epsilon_transitions, remove_epsilon_star, EpsilonRemovalConfig,
};
let mut fst = build_epsilon_fst();
let result = remove_epsilon_star(&mut fst, EpsilonRemovalConfig::default());
match result {
Ok(()) => {
assert!(
!has_epsilon_transitions(&fst),
"remove_epsilon_star should remove all epsilons"
);
}
Err(_) => {
}
}
}
#[test]
fn test_single_source_shortest_distance() {
use lling_llang::algorithms::{single_source_shortest_distance, ShortestDistanceConfig};
let fst = build_chain_fst(5);
let config = ShortestDistanceConfig::default();
let result = single_source_shortest_distance(&fst, config);
match result {
Some(distances) => {
assert_eq!(
distances.len(),
fst.num_states(),
"Should have distance for each state"
);
let start = fst.start();
assert!(
distances[start as usize].value() < f64::INFINITY,
"Start state should be reachable"
);
}
None => {
}
}
}
#[test]
fn test_all_pairs_shortest_distance() {
use lling_llang::algorithms::all_pairs_shortest_distance;
let fst = build_simple_fst();
let result = all_pairs_shortest_distance(&fst);
match result {
Some(distances) => {
let n = fst.num_states();
assert_eq!(distances.len(), n, "Should have n rows");
for row in &distances {
assert_eq!(row.len(), n, "Each row should have n columns");
}
for (i, row) in distances.iter().enumerate().take(n) {
assert!(
(row[i].value() - TropicalWeight::one().value()).abs() < 1e-10,
"Distance from state to itself should be one()"
);
}
}
None => {
}
}
}
#[test]
fn test_shortest_distance_to_final() {
use lling_llang::algorithms::{reverse_shortest_distance, ShortestDistanceConfig};
let fst = build_chain_fst(5);
let config = ShortestDistanceConfig::default();
let result = reverse_shortest_distance(&fst, config);
match result {
Some(distances) => {
assert_eq!(distances.len(), fst.num_states());
let reachable = distances.iter().any(|d| d.value() < f64::INFINITY);
assert!(reachable, "At least one state should reach final");
}
None => {
}
}
}
#[test]
fn test_sample_path() {
use lling_llang::algorithms::{sample_path, SampleConfig};
let fst = build_simple_fst();
let config = SampleConfig::default();
let result = sample_path(&fst, config);
match result {
Ok(path) => {
let _ = path.input_labels.len(); }
Err(_) => {
}
}
}
#[test]
fn test_sample_paths() {
use lling_llang::algorithms::{sample_paths, SampleConfig};
let fst = build_simple_fst();
let config = SampleConfig::default();
let results = sample_paths(&fst, 5, config);
let successful: Vec<_> = results.into_iter().filter_map(|r| r.ok()).collect();
assert!(successful.len() <= 5, "Should have at most 5 paths");
}
#[test]
fn test_push_weights() {
use lling_llang::algorithms::{
push_weights, PushConfig, PushDirection, ShortestDistanceConfig,
};
let mut fst = build_simple_fst();
let original_states = fst.num_states();
let config = PushConfig {
direction: PushDirection::Backward,
remove_non_coaccessible: true,
distance_config: ShortestDistanceConfig::default(),
};
let result = push_weights(&mut fst, config);
match result {
Ok(()) => {
assert!(
fst.num_states() <= original_states + 1,
"Pushing should preserve or reduce state count"
);
}
Err(_) => {
}
}
}
#[test]
fn test_is_stochastic() {
use lling_llang::algorithms::is_stochastic;
let fst = build_simple_fst();
let _is_stoch = is_stochastic(&fst, 1e-6);
}
#[test]
fn test_compose_with_identity() {
use lling_llang::composition::{compose, materialize};
let fst = build_chain_fst(3);
let identity = build_identity_fst(3);
let composed = compose(fst.clone(), identity.clone());
let materialized: VectorWfst<u32, TropicalWeight> = materialize(composed);
assert!(
materialized.num_states() >= 1,
"Composed FST should have states"
);
}
#[test]
fn test_compose_preserves_accepting() {
use lling_llang::algorithms::{connect, ConnectConfig};
use lling_llang::composition::{compose, materialize};
let fst = build_chain_fst(2);
let identity = build_identity_fst(2);
let original_states = fst.num_states();
let composed = compose(fst, identity);
let mut materialized: VectorWfst<u32, TropicalWeight> = materialize(composed);
connect(&mut materialized, ConnectConfig::default());
if original_states > 0 {
assert!(materialized.num_states() >= 1);
}
}
proptest! {
#[test]
fn prop_connect_idempotent(length in 1usize..10) {
use lling_llang::algorithms::{connect, ConnectConfig};
let mut fst = build_chain_fst(length);
connect(&mut fst, ConnectConfig::default());
let states_after_first = fst.num_states();
connect(&mut fst, ConnectConfig::default());
let states_after_second = fst.num_states();
prop_assert_eq!(states_after_first, states_after_second);
}
#[test]
fn prop_determinize_is_deterministic(length in 1usize..10) {
use lling_llang::algorithms::{determinize, is_deterministic, DeterminizeConfig};
let fst = build_chain_fst(length);
if let Ok(det) = determinize(&fst, DeterminizeConfig::default()) {
prop_assert!(is_deterministic(&det));
}
}
#[test]
fn prop_minimize_reduces_states(length in 1usize..10) {
use lling_llang::algorithms::{minimize, MinimizeConfig};
let fst = build_chain_fst(length);
let original_states = fst.num_states();
if let Ok(min) = minimize(&fst, MinimizeConfig::default()) {
prop_assert!(min.num_states() <= original_states + 1);
}
}
#[test]
fn prop_epsilon_removal_complete(length in 1usize..10) {
use lling_llang::algorithms::{remove_epsilon, has_epsilon_transitions, EpsilonRemovalConfig};
let mut fst = build_chain_fst(length);
if remove_epsilon(&mut fst, EpsilonRemovalConfig::default()).is_ok() {
prop_assert!(!has_epsilon_transitions(&fst));
}
}
#[test]
fn prop_chain_is_deterministic(length in 1usize..20) {
use lling_llang::algorithms::is_deterministic;
let fst = build_chain_fst(length);
prop_assert!(is_deterministic(&fst));
}
#[test]
fn prop_sssd_dimensions(length in 1usize..10) {
use lling_llang::algorithms::{single_source_shortest_distance, ShortestDistanceConfig};
let fst = build_chain_fst(length);
if let Some(distances) = single_source_shortest_distance(&fst, ShortestDistanceConfig::default()) {
prop_assert_eq!(distances.len(), fst.num_states());
}
}
}
#[test]
fn test_algorithms_with_log_weight() {
use lling_llang::algorithms::{
connect, determinize, minimize, remove_epsilon, single_source_shortest_distance,
ConnectConfig, DeterminizeConfig, EpsilonRemovalConfig, MinimizeConfig,
ShortestDistanceConfig,
};
let mut fst: VectorWfst<u32, LogWeight> = VectorWfst::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
let s2 = fst.add_state();
fst.set_start(s0);
fst.set_final(s2, LogWeight::one());
fst.add_arc(s0, Some(0), Some(0), s1, LogWeight::new(1.0));
fst.add_arc(s1, Some(1), Some(1), s2, LogWeight::new(2.0));
let original_states = fst.num_states();
let mut fst_clone = fst.clone();
connect(&mut fst_clone, ConnectConfig::default());
assert!(fst_clone.num_states() <= fst.num_states());
if let Ok(det) = determinize(&fst, DeterminizeConfig::default()) {
assert!(det.num_states() >= 1);
}
if let Ok(min) = minimize(&fst, MinimizeConfig::default()) {
assert!(min.num_states() <= fst.num_states() + 1);
}
let mut fst_for_eps = fst.clone();
if remove_epsilon(&mut fst_for_eps, EpsilonRemovalConfig::default()).is_ok() {
assert!(fst_for_eps.num_states() >= 1);
}
if let Some(distances) =
single_source_shortest_distance(&fst, ShortestDistanceConfig::default())
{
assert_eq!(distances.len(), original_states);
}
}
#[test]
fn test_algorithms_empty_fst() {
use lling_llang::algorithms::{
connect, has_epsilon_transitions, is_connected, is_deterministic, ConnectConfig,
};
let mut fst: VectorWfst<u32, TropicalWeight> = VectorWfst::new();
let s = fst.add_state();
fst.set_start(s);
connect(&mut fst, ConnectConfig::default());
let _is_conn = is_connected(&fst);
let _is_det = is_deterministic(&fst);
let _has_eps = has_epsilon_transitions(&fst);
}
#[test]
fn test_algorithms_single_state() {
use lling_llang::algorithms::{
connect, determinize, minimize, ConnectConfig, DeterminizeConfig, MinimizeConfig,
};
let mut fst: VectorWfst<u32, TropicalWeight> = VectorWfst::new();
let s = fst.add_state();
fst.set_start(s);
fst.set_final(s, TropicalWeight::one());
let mut fst_clone = fst.clone();
connect(&mut fst_clone, ConnectConfig::default());
assert_eq!(fst_clone.num_states(), 1);
if let Ok(det) = determinize(&fst, DeterminizeConfig::default()) {
assert!(det.num_states() >= 1);
}
if let Ok(min) = minimize(&fst, MinimizeConfig::default()) {
assert!(min.num_states() >= 1);
}
}
#[test]
fn test_algorithms_with_cycles() {
use lling_llang::algorithms::{
connect, is_deterministic, single_source_shortest_distance, ConnectConfig,
ShortestDistanceConfig,
};
let mut fst: VectorWfst<u32, TropicalWeight> = VectorWfst::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
fst.set_start(s0);
fst.set_final(s1, TropicalWeight::one());
fst.add_arc(s0, Some(0), Some(0), s0, TropicalWeight::new(0.5));
fst.add_arc(s0, Some(1), Some(1), s1, TropicalWeight::new(1.0));
let mut fst_clone = fst.clone();
connect(&mut fst_clone, ConnectConfig::default());
assert_eq!(fst_clone.num_states(), 2);
assert!(is_deterministic(&fst));
if let Some(distances) =
single_source_shortest_distance(&fst, ShortestDistanceConfig::default())
{
assert_eq!(distances.len(), 2);
}
}