ff_structure/
pair_table.rs1use std::ops::{Deref, DerefMut};
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 TryFrom<&str> for PairTable {
46 type Error = StructureError;
47
48 fn try_from(s: &str) -> Result<Self, Self::Error> {
49 let mut stack = Vec::new();
50 let mut table = vec![None; s.len()];
51
52 for (i, c) in s.chars().enumerate() {
53 match c {
54 '(' => stack.push(i),
55 ')' => {
56 let j = stack.pop().ok_or(StructureError::UnmatchedClose(i))?;
57 table[i] = Some(j as NAIDX);
58 table[j] = Some(i as NAIDX);
59 }
60 '.' => (),
61 _ => return Err(StructureError::InvalidToken(format!("character '{}'", c), "structure".to_string(), i)),
62 }
63 }
64
65 if let Some(i) = stack.pop() {
66 return Err(StructureError::UnmatchedOpen(i));
67 }
68 Ok(PairTable(table))
69 }
70}
71
72impl TryFrom<&DotBracketVec> for PairTable {
73 type Error = StructureError;
74
75 fn try_from(db: &DotBracketVec) -> Result<Self, Self::Error> {
76 let mut stack = Vec::new();
77 let mut table = vec![None; db.len()];
78
79 for (i, dot) in db.iter().enumerate() {
80 match dot {
81 DotBracket::Open => stack.push(i),
82 DotBracket::Close => {
83 let j = stack.pop().ok_or(StructureError::UnmatchedClose(i))?;
84 table[i] = Some(j as NAIDX);
85 table[j] = Some(i as NAIDX);
86 }
87 DotBracket::Unpaired => {}
88 DotBracket::Break => unreachable!("unexpected Break in single-stranded case"),
89 }
90 }
91
92 if let Some(i) = stack.pop() {
93 return Err(StructureError::UnmatchedOpen(i));
94 }
95
96 Ok(PairTable(table))
97 }
98}
99
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn test_valid_pair_table() {
107 let pt = PairTable::try_from("((..))").unwrap();
108 assert_eq!(pt.len(), 6);
109 assert_eq!(pt[0], Some(5));
110 assert_eq!(pt[1], Some(4));
111 assert_eq!(pt[2], None);
112 assert_eq!(pt[3], None);
113 assert_eq!(pt[4], Some(1));
114 assert_eq!(pt[5], Some(0));
115 }
116
117 #[test]
118 fn test_unmatched_open() {
119 let err = PairTable::try_from("(()").unwrap_err();
120 assert_eq!(format!("{}", err), "Unmatched '(' at position 0");
121 }
122
123 #[test]
124 fn test_unmatched_close() {
125 let err = PairTable::try_from("())").unwrap_err();
126 assert_eq!(format!("{}", err), "Unmatched ')' at position 2");
127 }
128
129 #[test]
130 fn test_invalid_token() {
131 let err = PairTable::try_from("(x)").unwrap_err();
132 assert_eq!(format!("{}", err), "Invalid character 'x' in structure at position 1");
133 }
134
135 #[test]
136 fn test_well_formed_empty_interval() {
137 let pt= PairTable::try_from("...").unwrap();
138 assert!(pt.is_well_formed(0, 0));
139 assert!(pt.is_well_formed(0, 1));
140 assert!(pt.is_well_formed(0, 2));
141 assert!(pt.is_well_formed(0, 3));
142 assert!(pt.is_well_formed(1, 3));
143 assert!(pt.is_well_formed(2, 3));
144 assert!(pt.is_well_formed(3, 3));
145 }
146
147 #[test]
148 fn test_well_formed_pairings_within_interval() {
149 let pt = PairTable::try_from(".(.).").unwrap();
150 assert!(pt.is_well_formed(0, 5)); assert!(pt.is_well_formed(0, 4));
152 assert!(pt.is_well_formed(1, 5));
153 assert!(pt.is_well_formed(1, 4));
154 assert!(pt.is_well_formed(1, 4));
155 assert!(pt.is_well_formed(2, 3));
156 assert!(!pt.is_well_formed(0, 3));
157 assert!(!pt.is_well_formed(1, 3));
158 assert!(!pt.is_well_formed(2, 4));
159 }
160
161 #[test]
162 #[should_panic(expected = "Invalid interval: j must be <= length")]
163 fn test_well_formed_out_of_bounds_assert() {
164 let pt = PairTable::try_from("..").unwrap();
165 pt.is_well_formed(0, 3); }
167}
168
169
170