1
- use std:: fmt:: { self , Display , Formatter } ;
1
+ use std:: {
2
+ fmt:: { self , Display , Formatter } ,
3
+ ops:: { Index , IndexMut } ,
4
+ } ;
2
5
6
+ use ahash:: { HashSet , HashSetExt } ;
3
7
use itertools:: Itertools ;
4
8
use polychem:: { BondInfo , Polymer , ResidueGroup , ResidueId } ;
5
9
6
10
// FIXME: Consider using newtype? Especially if this is made public!
7
11
type BondAbbr < ' p > = & ' p str ;
8
12
9
- #[ derive( Copy , Clone , Debug ) ]
13
+ #[ derive( Copy , Clone , Eq , PartialEq , Hash , Debug ) ]
10
14
enum Terminal < ' p > {
11
15
Donor ( BondAbbr < ' p > ) ,
12
16
Acceptor ( BondAbbr < ' p > ) ,
@@ -21,37 +25,37 @@ impl Display for Terminal<'_> {
21
25
}
22
26
}
23
27
24
- #[ derive( Clone , Debug , Default ) ]
28
+ #[ derive( Clone , Eq , PartialEq , Hash , Debug , Default ) ]
25
29
struct Residue < ' p > {
26
30
terminals : Vec < Terminal < ' p > > ,
27
31
bonds : Vec < Bond < ' p > > ,
28
32
}
29
33
30
- #[ derive( Copy , Clone , Debug ) ]
34
+ #[ derive( Copy , Clone , Eq , PartialEq , Hash , Debug ) ]
31
35
struct Bond < ' p > {
32
36
// FIXME: The naming `end` doesn't fit well with the `Terminal` here...
33
37
end : Terminal < ' p > ,
34
- target : usize ,
38
+ target : NodeId ,
35
39
}
36
40
37
41
impl < ' p > Bond < ' p > {
38
- const fn donating_to ( abbr : BondAbbr < ' p > , acceptor : usize ) -> Self {
42
+ const fn donating_to ( abbr : BondAbbr < ' p > , acceptor : NodeId ) -> Self {
39
43
let end = Terminal :: Donor ( abbr) ;
40
44
Self {
41
45
end,
42
46
target : acceptor,
43
47
}
44
48
}
45
49
46
- const fn accepting_from ( abbr : BondAbbr < ' p > , donor : usize ) -> Self {
50
+ const fn accepting_from ( abbr : BondAbbr < ' p > , donor : NodeId ) -> Self {
47
51
let end = Terminal :: Acceptor ( abbr) ;
48
52
Self { end, target : donor }
49
53
}
50
54
}
51
55
52
56
// PERF: Not sold on this "tombstone" approach with `Option` — might be better to re-index the sub-graphs so their
53
57
// vectors can be shrunk?
54
- #[ derive( Clone , Debug ) ]
58
+ #[ derive( Clone , Eq , PartialEq , Hash , Debug ) ]
55
59
struct Fragment < ' p > ( Vec < Option < Residue < ' p > > > ) ;
56
60
57
61
// FIXME: This was written way too quickly... Take a look back at this...
@@ -84,6 +88,8 @@ impl Display for Fragment<'_> {
84
88
}
85
89
}
86
90
91
+ type NodeId = usize ;
92
+
87
93
#[ derive( Clone , Debug ) ]
88
94
struct NodeMapping ( Vec < ResidueId > ) ;
89
95
@@ -99,7 +105,7 @@ impl NodeMapping {
99
105
// NOTE: Keeping `id` as a ref, since it needs to be one for `binary_search()` and because it comes from
100
106
// `bond_refs()` to begin with!
101
107
#[ allow( clippy:: trivially_copy_pass_by_ref) ]
102
- fn index ( & self , id : & ResidueId ) -> usize {
108
+ fn index ( & self , id : & ResidueId ) -> NodeId {
103
109
// SAFETY: Panics if the `id` isn't found
104
110
self . 0 . binary_search ( id) . unwrap ( )
105
111
}
@@ -124,6 +130,9 @@ pub trait Dissociable: Sized {
124
130
let node_mapping = NodeMapping :: new ( polymer) ;
125
131
let fragment = Fragment :: new ( & node_mapping, polymer) ;
126
132
eprintln ! ( "{fragment}" ) ;
133
+ for piece in fragment. fragment ( Some ( 1 ) ) {
134
+ eprintln ! ( "{piece}" ) ;
135
+ }
127
136
}
128
137
}
129
138
@@ -140,24 +149,81 @@ impl<'p> Fragment<'p> {
140
149
let acceptor = node_mapping. index ( acceptor) ;
141
150
142
151
let mut push_bond =
143
- // FIXME: Shocking failure of type inference here with that `usize `...
144
- |residue : usize , bond| residues[ residue ] . as_mut ( ) . unwrap ( ) . bonds . push ( bond) ;
152
+ // FIXME: Shocking failure of type inference here with that `NodeId `...
153
+ |node : NodeId , bond| residues[ node ] . as_mut ( ) . unwrap ( ) . bonds . push ( bond) ;
145
154
146
155
push_bond ( donor, Bond :: donating_to ( abbr, acceptor) ) ;
147
156
push_bond ( acceptor, Bond :: accepting_from ( abbr, donor) ) ;
148
157
}
149
158
150
159
Self ( residues)
151
160
}
161
+
162
+ fn fragment ( & self , max_depth : Option < usize > ) -> HashSet < Self > {
163
+ let mut processing_queue = vec ! [ self . clone( ) ] ;
164
+ // PERF: Any clever `with_capacity` pre-allocation I could do?
165
+ let mut fragments = HashSet :: new ( ) ;
166
+
167
+ let mut depth = 0 ;
168
+ while let Some ( next) = processing_queue. pop ( ) {
169
+ // FIXME: Can I avoid that clone?
170
+ if fragments. insert ( next. clone ( ) ) {
171
+ continue ;
172
+ }
173
+
174
+ if depth < max_depth. unwrap_or ( usize:: MAX ) {
175
+ depth += 1 ;
176
+ fragments. extend ( next. cut_each_bond ( ) . map ( |( _, piece) | piece) ) ;
177
+ }
178
+ }
179
+
180
+ fragments
181
+ }
182
+
183
+ // FIXME: Clarify type with some aliases?
184
+ // FIXME: Remove the `+ '_` once Rust 2024 is released!
185
+ fn cut_each_bond ( & self ) -> impl Iterator < Item = ( NodeId , Self ) > + ' _ {
186
+ self . 0
187
+ . iter ( )
188
+ . enumerate ( )
189
+ . filter_map ( |( node, opt_residue) | opt_residue. as_ref ( ) . map ( |residue| ( node, residue) ) )
190
+ . flat_map ( |( node, residue) | residue. bonds . iter ( ) . map ( move |bond| ( node, bond. target ) ) )
191
+ // NOTE: Ensures that the same bonds aren't cut twice
192
+ . filter ( |( a, b) | a < b)
193
+ . map ( |( a, b) | {
194
+ let mut fragment = self . clone ( ) ;
195
+ let mut remove_edge = |from, to| {
196
+ swap_remove_first ( & mut fragment[ from] . bonds , |bond| bond. target == to) . unwrap ( )
197
+ } ;
198
+
199
+ let terminal_a = remove_edge ( a, b) . end ;
200
+ let terminal_b = remove_edge ( b, a) . end ;
201
+ fragment[ a] . terminals . push ( terminal_a) ;
202
+ fragment[ b] . terminals . push ( terminal_b) ;
203
+
204
+ ( a, fragment)
205
+ } )
206
+ }
207
+ }
208
+
209
+ impl < ' p > Index < NodeId > for Fragment < ' p > {
210
+ type Output = Residue < ' p > ;
211
+
212
+ fn index ( & self , index : NodeId ) -> & Self :: Output {
213
+ self . 0 [ index] . as_ref ( ) . unwrap ( )
214
+ }
152
215
}
153
216
154
- // impl<'p> Index<usize> for Fragment<'p> {
155
- // type Output = Residue<'p>;
217
+ impl < ' p > IndexMut < NodeId > for Fragment < ' p > {
218
+ fn index_mut ( & mut self , index : NodeId ) -> & mut Self :: Output {
219
+ self . 0 [ index] . as_mut ( ) . unwrap ( )
220
+ }
221
+ }
156
222
157
- // fn index(&self, index: usize) -> &Self::Output {
158
- // self.0[index].as_ref().unwrap()
159
- // }
160
- // }
223
+ // FIXME: Should this just unwrap things here? Assuming there will always be one match?
224
+ fn swap_remove_first < T > ( vec : & mut Vec < T > , f : impl FnMut ( & T ) -> bool ) -> Option < T > {
225
+ vec . iter ( ) . position ( f ) . map ( |i| vec . swap_remove ( i ) )
226
+ }
161
227
162
228
// SEE NOTES FROM APRIL 8TH!
163
229
// use DashMap or quick-cache for a global fragment cache
0 commit comments