Skip to content

Commit a127afd

Browse files
committed
fix(type_length_limit): does this work in CI?
1 parent 584afb7 commit a127afd

File tree

4 files changed

+86
-17
lines changed

4 files changed

+86
-17
lines changed

crates/muropeptide/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! Responsible for parsing strings into meaningful `Muropeptide` structures
2+
#![type_length_limit = "254089318894795"]
23

34
mod parser;
45

crates/polychem/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! An abstraction for building chemically validated polymers
2+
#![type_length_limit = "18554191"]
23

34
pub mod atoms;
45
pub mod errors;

crates/smithereens/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7+
ahash = "0.8.11"
78
itertools = "0.13.0"
89
polychem = { path = "../polychem" }
910

crates/smithereens/src/lib.rs

+83-17
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
use std::fmt::{self, Display, Formatter};
1+
use std::{
2+
fmt::{self, Display, Formatter},
3+
ops::{Index, IndexMut},
4+
};
25

6+
use ahash::{HashSet, HashSetExt};
37
use itertools::Itertools;
48
use polychem::{BondInfo, Polymer, ResidueGroup, ResidueId};
59

610
// FIXME: Consider using newtype? Especially if this is made public!
711
type BondAbbr<'p> = &'p str;
812

9-
#[derive(Copy, Clone, Debug)]
13+
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
1014
enum Terminal<'p> {
1115
Donor(BondAbbr<'p>),
1216
Acceptor(BondAbbr<'p>),
@@ -21,37 +25,37 @@ impl Display for Terminal<'_> {
2125
}
2226
}
2327

24-
#[derive(Clone, Debug, Default)]
28+
#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)]
2529
struct Residue<'p> {
2630
terminals: Vec<Terminal<'p>>,
2731
bonds: Vec<Bond<'p>>,
2832
}
2933

30-
#[derive(Copy, Clone, Debug)]
34+
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
3135
struct Bond<'p> {
3236
// FIXME: The naming `end` doesn't fit well with the `Terminal` here...
3337
end: Terminal<'p>,
34-
target: usize,
38+
target: NodeId,
3539
}
3640

3741
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 {
3943
let end = Terminal::Donor(abbr);
4044
Self {
4145
end,
4246
target: acceptor,
4347
}
4448
}
4549

46-
const fn accepting_from(abbr: BondAbbr<'p>, donor: usize) -> Self {
50+
const fn accepting_from(abbr: BondAbbr<'p>, donor: NodeId) -> Self {
4751
let end = Terminal::Acceptor(abbr);
4852
Self { end, target: donor }
4953
}
5054
}
5155

5256
// PERF: Not sold on this "tombstone" approach with `Option` — might be better to re-index the sub-graphs so their
5357
// vectors can be shrunk?
54-
#[derive(Clone, Debug)]
58+
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
5559
struct Fragment<'p>(Vec<Option<Residue<'p>>>);
5660

5761
// FIXME: This was written way too quickly... Take a look back at this...
@@ -84,6 +88,8 @@ impl Display for Fragment<'_> {
8488
}
8589
}
8690

91+
type NodeId = usize;
92+
8793
#[derive(Clone, Debug)]
8894
struct NodeMapping(Vec<ResidueId>);
8995

@@ -99,7 +105,7 @@ impl NodeMapping {
99105
// NOTE: Keeping `id` as a ref, since it needs to be one for `binary_search()` and because it comes from
100106
// `bond_refs()` to begin with!
101107
#[allow(clippy::trivially_copy_pass_by_ref)]
102-
fn index(&self, id: &ResidueId) -> usize {
108+
fn index(&self, id: &ResidueId) -> NodeId {
103109
// SAFETY: Panics if the `id` isn't found
104110
self.0.binary_search(id).unwrap()
105111
}
@@ -124,6 +130,9 @@ pub trait Dissociable: Sized {
124130
let node_mapping = NodeMapping::new(polymer);
125131
let fragment = Fragment::new(&node_mapping, polymer);
126132
eprintln!("{fragment}");
133+
for piece in fragment.fragment(Some(1)) {
134+
eprintln!("{piece}");
135+
}
127136
}
128137
}
129138

@@ -140,24 +149,81 @@ impl<'p> Fragment<'p> {
140149
let acceptor = node_mapping.index(acceptor);
141150

142151
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);
145154

146155
push_bond(donor, Bond::donating_to(abbr, acceptor));
147156
push_bond(acceptor, Bond::accepting_from(abbr, donor));
148157
}
149158

150159
Self(residues)
151160
}
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+
}
152215
}
153216

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+
}
156222

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+
}
161227

162228
// SEE NOTES FROM APRIL 8TH!
163229
// use DashMap or quick-cache for a global fragment cache

0 commit comments

Comments
 (0)