ff_structure/
pair_table.rs1use std::ops::{Deref, DerefMut};
2use std::convert::TryFrom;
3use crate::StructureError;
4use crate::{DotBracket, DotBracketVec};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct PairTable(pub Vec<Option<usize>>);
8
9impl PairTable {
10 pub fn is_well_formed(&self, i: usize, j: usize) -> bool {
13 assert!(j <= self.len(), "Invalid interval: j must be <= length");
14
15 for k in i..j {
16 if let Some(l) = self[k] {
17 if l < i || l >= j {
18 return false; }
20 }
21 }
22 true
23 }
24}
25
26impl Deref for PairTable {
27 type Target = [Option<usize>];
28
29 fn deref(&self) -> &Self::Target {
30 &self.0
31 }
32}
33
34impl DerefMut for PairTable {
35 fn deref_mut(&mut self) -> &mut Self::Target {
36 &mut self.0
37 }
38}
39
40impl TryFrom<&str> for PairTable {
41 type Error = StructureError;
42
43 fn try_from(s: &str) -> Result<Self, Self::Error> {
44 let mut stack = Vec::new();
45 let mut table = vec![None; s.len()];
46
47 for (i, c) in s.chars().enumerate() {
48 match c {
49 '(' => stack.push(i),
50 ')' => {
51 let j = stack.pop().ok_or(StructureError::UnmatchedClose(i))?;
52 table[i] = Some(j);
53 table[j] = Some(i);
54 }
55 '.' => (),
56 _ => return Err(StructureError::InvalidToken(format!("character '{}'", c), "structure".to_string(), i)),
57 }
58 }
59
60 if let Some(i) = stack.pop() {
61 return Err(StructureError::UnmatchedOpen(i));
62 }
63 Ok(PairTable(table))
64 }
65}
66
67impl TryFrom<&DotBracketVec> for PairTable {
68 type Error = StructureError;
69
70 fn try_from(db: &DotBracketVec) -> Result<Self, Self::Error> {
71 let mut stack: Vec<usize> = Vec::new();
72 let mut table = vec![None; db.len()];
73
74 for (i, dot) in db.iter().enumerate() {
75 match dot {
76 DotBracket::Open => stack.push(i),
77 DotBracket::Close => {
78 let j = stack.pop().ok_or(StructureError::UnmatchedClose(i))?;
79 table[i] = Some(j);
80 table[j] = Some(i);
81 }
82 DotBracket::Unpaired => {}
83 DotBracket::Break => unreachable!("unexpected Break in single-stranded case"),
84 }
85 }
86
87 if let Some(i) = stack.pop() {
88 return Err(StructureError::UnmatchedOpen(i));
89 }
90
91 Ok(PairTable(table))
92 }
93}
94
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_valid_pair_table() {
102 let pt = PairTable::try_from("((..))").unwrap();
103 assert_eq!(pt.len(), 6);
104 assert_eq!(pt[0], Some(5));
105 assert_eq!(pt[1], Some(4));
106 assert_eq!(pt[2], None);
107 assert_eq!(pt[3], None);
108 assert_eq!(pt[4], Some(1));
109 assert_eq!(pt[5], Some(0));
110 }
111
112 #[test]
113 fn test_unmatched_open() {
114 let err = PairTable::try_from("(()").unwrap_err();
115 assert_eq!(format!("{}", err), "Unmatched '(' at position 0");
116 }
117
118 #[test]
119 fn test_unmatched_close() {
120 let err = PairTable::try_from("())").unwrap_err();
121 assert_eq!(format!("{}", err), "Unmatched ')' at position 2");
122 }
123
124 #[test]
125 fn test_invalid_token() {
126 let err = PairTable::try_from("(x)").unwrap_err();
127 assert_eq!(format!("{}", err), "Invalid character 'x' in structure at position 1");
128 }
129
130 #[test]
131 fn test_well_formed_empty_interval() {
132 let pt= PairTable::try_from("...").unwrap();
133 assert!(pt.is_well_formed(0, 0));
134 assert!(pt.is_well_formed(0, 1));
135 assert!(pt.is_well_formed(0, 2));
136 assert!(pt.is_well_formed(0, 3));
137 assert!(pt.is_well_formed(1, 3));
138 assert!(pt.is_well_formed(2, 3));
139 assert!(pt.is_well_formed(3, 3));
140 }
141
142 #[test]
143 fn test_well_formed_pairings_within_interval() {
144 let pt = PairTable::try_from(".(.).").unwrap();
145 assert!(pt.is_well_formed(0, 5)); assert!(pt.is_well_formed(0, 4));
147 assert!(pt.is_well_formed(1, 5));
148 assert!(pt.is_well_formed(1, 4));
149 assert!(pt.is_well_formed(1, 4));
150 assert!(pt.is_well_formed(2, 3));
151 assert!(!pt.is_well_formed(0, 3));
152 assert!(!pt.is_well_formed(1, 3));
153 assert!(!pt.is_well_formed(2, 4));
154 }
155
156 #[test]
157 #[should_panic(expected = "Invalid interval: j must be <= length")]
158 fn test_well_formed_out_of_bounds_assert() {
159 let pt = PairTable::try_from("..").unwrap();
160 pt.is_well_formed(0, 3); }
162}
163
164
165