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