dyck 0.1.0

A crate for running Dyck and InterDyck algorithms over languages of generic <T> token enum instances or string slice types.
Documentation
use dyck::{Language, Word};

#[cfg(test)]
mod core_tests {
    use super::*;

    #[test]
    fn new_from_vec_no_duplicates() {
        let pairs = vec![("(", ")"), ("[", "]")];
        let language = Language::new_from_vec(&pairs).expect("failed to create language");
        assert_eq!(language.get_k(), 2);
    }

    #[test]
    fn new_from_arr_no_duplicates() {
        let pairs = [("(", ")"), ("[", "]")];
        let language = Language::new_from_arr(&pairs).expect("failed to create language");
        assert_eq!(language.get_k(), 2);
    }

    #[test]
    fn new_from_vec_with_duplicate_opening_tokens() {
        let pairs = vec![("(", ")"), ("(", "]")];
        assert!(
            Language::new_from_vec(&pairs).is_err(),
            "constructor should fail with duplicate opening tokens"
        );
    }

    #[test]
    fn new_from_vec_with_duplicate_closing_tokens() {
        let pairs = vec![("(", ")"), ("{", ")")];
        assert!(
            Language::new_from_vec(&pairs).is_err(),
            "constructor should fail with duplicate closing tokens"
        );
    }

    #[test]
    fn new_from_vec_with_duplicate_pairs() {
        let pairs = vec![("(", ")"), ("(", ")")];
        assert!(
            Language::new_from_vec(&pairs).is_err(),
            "constructor should fail with duplicate pairs"
        );
    }

    #[test]
    fn get_k_with_single_pair() {
        let pairs = vec![("(", ")")];
        let language =
            Language::new_from_vec(&pairs).expect("failed to create language with single pair");
        assert_eq!(
            language.get_k(),
            1,
            "get_k should return 1 for a single pair of parentheses"
        );
    }

    #[test]
    fn get_k_with_multiple_pairs() {
        let pairs = vec![("(", ")"), ("[", "]"), ("{", "}")];
        let language =
            Language::new_from_vec(&pairs).expect("failed to create language with multiple pairs");
        assert_eq!(
            language.get_k(),
            3,
            "get_k should return the correct number of parenthesis types"
        );
    }

    #[test]
    fn get_k_with_no_pairs() {
        let pairs = Vec::new();
        let language: Language<&str> =
            Language::new_from_vec(&pairs).expect("failed to create language with no pairs");
        assert_eq!(
            language.get_k(),
            0,
            "get_k should return 0 when no pairs are defined"
        );
    }

    #[test]
    fn is_open() {
        let pairs = vec![("{", "}")];
        let language = Language::new_from_vec(&pairs).unwrap();
        assert!(language.is_open("{"));
        assert!(!language.is_open("}"));
    }

    #[test]
    fn is_close() {
        let pairs = vec![("{", "}")];
        let language = Language::new_from_vec(&pairs).unwrap();
        assert!(language.is_close("}"));
        assert!(!language.is_close("{"));
    }

    #[test]
    fn is_valid() {
        let pairs = vec![("[", "]"), ("{", "}")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let valid_word: Word<&str> = vec!["[", "{", "}", "]"];
        assert!(language.is_valid(&valid_word));
        let invalid_word: Word<&str> = vec!["[", "{", "]"];
        assert!(!language.is_valid(&invalid_word));
    }

    #[test]
    fn is_valid_empty_word() {
        let pairs = vec![("(", ")")];
        let language = Language::new_from_vec(&pairs).unwrap();
        assert!(
            language.is_valid(&Vec::new()),
            "an empty word should be considered valid"
        );
    }

    #[test]
    fn is_valid_incorrect_closing_order() {
        let pairs = vec![("(", ")"), ("[", "]")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let invalid_word: Word<&str> = vec!["[", "(", "]", ")"];
        assert!(
            !language.is_valid(&invalid_word),
            "word with incorrect closing order should be invalid"
        );
    }

    #[test]
    fn is_balanced() {
        let pairs = vec![("[", "]")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let balanced_word: Word<&str> = vec!["[", "]"];
        assert!(language.is_balanced(&balanced_word));
        let unbalanced_word: Word<&str> = vec!["[", "["];
        assert!(!language.is_balanced(&unbalanced_word));
    }

    #[test]
    fn appending_to_valid_sequence_returns_same_sequence() {
        let pairs = vec![("(", ")"), ("[", "]")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let word: Word<&str> = vec!["(", "[", "]", ")"]; // already valid.

        let result = language.shortest_validating_appendage(&word).unwrap();

        assert_eq!(
            result, word,
            "appending to an already valid sequence should return the sequence unchanged"
        );
    }

    #[test]
    fn appending_completes_sequence_needing_closures() {
        let pairs = vec![("(", ")"), ("[", "]")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let word: Word<&str> = vec!["(", "[", "["]; // needs "]])" to be complete.

        let result = language.shortest_validating_appendage(&word).unwrap();
        let expected_completion: Word<&str> = vec!["(", "[", "[", "]", "]", ")"];

        assert_eq!(
            result, expected_completion,
            "the completion should correctly close all open brackets"
        );
    }

    #[test]
    fn appending_error_for_leading_close_token() {
        let pairs = vec![("(", ")"), ("[", "]")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let word: Word<&str> = vec![")", "(", "[", "]"]; // invalid start.

        let result = language.shortest_validating_appendage(&word);

        assert!(
            result.is_none(),
            "should return None for words starting with a closing token"
        );
    }

    #[test]
    fn appending_to_empty_sequence_returns_empty() {
        let pairs = vec![("(", ")"), ("[", "]")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let word: Word<&str> = vec![];

        let result = language.shortest_validating_appendage(&word).unwrap();

        assert!(
            result.is_empty(),
            "appending to an empty sequence should return an empty sequence"
        );
    }

    #[test]
    fn appending_corrects_sequence_with_multiple_unmatched_opens() {
        let pairs = vec![("(", ")"), ("[", "]")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let word: Word<&str> = vec!["(", "[", "(", "["]; // needs "])])" to be complete.

        let result = language.shortest_validating_appendage(&word).unwrap();
        let expected_completion: Word<&str> = vec!["(", "[", "(", "[", "]", ")", "]", ")"];

        assert_eq!(
            result, expected_completion,
            "the completion should correctly close all open brackets in reverse order"
        );
    }

    #[test]
    fn longest_valid_prefix() {
        let pairs = vec![("(", ")")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let word: Word<&str> = vec!["(", "(", ")", ")", "(", ")"];
        assert_eq!(language.longest_valid_prefix(&word), 6);
    }

    #[test]
    fn longest_valid_prefix_empty_word() {
        let pairs = vec![("(", ")")];
        let language = Language::new_from_vec(&pairs).unwrap();
        assert_eq!(
            language.longest_valid_prefix(&Vec::new()),
            0,
            "the longest valid prefix of an empty word should be 0"
        );
    }

    #[test]
    fn longest_valid_prefix_incorrect_nesting() {
        let pairs = vec![("(", ")")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let word: Word<&str> = vec!["(", ")", "(", ")", ")"];
        assert_eq!(
            language.longest_valid_prefix(&word),
            4,
            "incorrect nesting should limit valid prefix length"
        );
    }

    #[test]
    fn longest_valid_prefix_entire_word() {
        let pairs = vec![("[", "]"), ("{", "}")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let word: Word<&str> = vec!["[", "{", "}", "]"];
        assert_eq!(
            language.longest_valid_prefix(&word),
            word.len(),
            "the entire word should be a valid prefix"
        );
    }

    #[test]
    fn longest_valid_prefix_with_unmatched_open() {
        let pairs = vec![("(", ")")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let word: Word<&str> = vec!["(", "("];
        assert_eq!(
            language.longest_valid_prefix(&word),
            0,
            "word with unmatched open should have 0 as longest valid prefix"
        );
    }

    #[test]
    fn longest_valid_prefix_with_unmatched_close() {
        let pairs = vec![("(", ")")];
        let language = Language::new_from_vec(&pairs).unwrap();
        let word: Word<&str> = vec![")", "("];
        assert_eq!(
            language.longest_valid_prefix(&word),
            0,
            "word with unmatched close should have 0 as longest valid prefix"
        );
    }

    #[test]
    fn get_close() {
        let pairs = vec![("A", "B")];
        let language = Language::new_from_vec(&pairs).unwrap();
        assert_eq!(language.get_close("A"), Some("B"));
        assert_eq!(language.get_close("B"), None);
    }

    #[test]
    fn get_open() {
        let pairs = vec![("A", "B")];
        let language = Language::new_from_vec(&pairs).unwrap();
        assert_eq!(language.get_open("B"), Some("A"));
        assert_eq!(language.get_open("A"), None);
    }

    #[test]
    fn inverse_operations_get_open_get_close() {
        let pairs = vec![("A", "B"), ("X", "Y")];
        let language = Language::new_from_vec(&pairs).unwrap();

        for (open, close) in pairs {
            assert_eq!(
                language.get_close(open),
                Some(close),
                "get_close did not return the correct closing token for {}",
                open
            );
            assert_eq!(
                language.get_open(close),
                Some(open),
                "get_open did not return the correct opening token for {}",
                close
            );
        }
    }
}