1use core::default::Default;
63use core::fmt;
64use itertools::Itertools;
65use partial_min_max::max;
66use strum::EnumIter;
67use writeable::{LengthHint, Writeable};
68
69#[derive(Clone, Debug, PartialEq, Default)]
70#[allow(clippy::exhaustive_structs)] pub struct Breakpoints {
72 pub breakpoints: Vec<usize>,
74 pub length: usize,
76}
77
78#[derive(Clone, Copy, Debug, PartialEq)]
79#[allow(clippy::exhaustive_structs)] pub struct BiesVector<F: fmt::Debug> {
81 pub b: F,
82 pub i: F,
83 pub e: F,
84 pub s: F,
85}
86
87#[derive(Clone, Debug, PartialEq)]
89#[allow(clippy::exhaustive_structs)] pub struct BiesMatrix(pub Vec<BiesVector<f32>>);
91
92#[derive(Clone, PartialEq)]
93#[allow(clippy::exhaustive_structs)] pub struct BiesString<'a>(&'a Breakpoints);
95
96#[derive(Clone, Copy, Debug, PartialEq, EnumIter)]
97#[non_exhaustive]
98pub enum Algorithm {
99 Alg1a,
101
102 Alg1b,
104
105 Alg2a,
107
108 Alg3a,
110}
111
112impl Breakpoints {
113 pub fn from_bies_matrix(
114 algorithm: Algorithm,
115 matrix: &BiesMatrix,
116 valid_breakpoints: impl Iterator<Item = usize>,
117 ) -> Self {
118 match algorithm {
119 Algorithm::Alg1a => Self::from_bies_matrix_1a(matrix, valid_breakpoints),
120 Algorithm::Alg1b => Self::from_bies_matrix_1b(matrix, valid_breakpoints),
121 Algorithm::Alg2a => Self::from_bies_matrix_2a(matrix, valid_breakpoints),
122 Algorithm::Alg3a => Self::from_bies_matrix_3a(matrix, valid_breakpoints),
123 }
124 }
125
126 #[expect(clippy::suspicious_operation_groupings)]
127 fn from_bies_matrix_1a(
128 matrix: &BiesMatrix,
129 valid_breakpoints: impl Iterator<Item = usize>,
130 ) -> Self {
131 let mut breakpoints = vec![];
132 for i in valid_breakpoints {
133 if i == 0 || i >= matrix.0.len() {
134 panic!("Invalid i value");
136 }
137 let bies1 = &matrix.0[i - 1];
138 let bies2 = &matrix.0[i];
139 let break_score =
140 bies1.e * bies2.b + bies1.e * bies2.s + bies1.s * bies2.b + bies1.s * bies2.s;
141 let nobrk_score =
142 bies1.i * bies2.i + bies1.i * bies2.e + bies1.b * bies2.i + bies1.b * bies2.e;
143 if break_score > nobrk_score {
144 breakpoints.push(i);
145 }
146 }
147 Self {
148 breakpoints,
149 length: matrix.0.len(),
150 }
151 }
152
153 fn from_bies_matrix_1b(
154 matrix: &BiesMatrix,
155 valid_breakpoints: impl Iterator<Item = usize>,
156 ) -> Self {
157 let mut breakpoints = vec![];
158 for i in valid_breakpoints {
159 if i == 0 || i >= matrix.0.len() {
160 panic!("Invalid i value");
162 }
163 let bies1 = &matrix.0[i - 1];
164 let bies2 = &matrix.0[i];
165 let mut candidate = (f32::NEG_INFINITY, false);
166 candidate = max(candidate, (bies1.e * bies2.b, true));
167 candidate = max(candidate, (bies1.e * bies2.s, true));
168 candidate = max(candidate, (bies1.s * bies2.b, true));
169 candidate = max(candidate, (bies1.s * bies2.s, true));
170 candidate = max(candidate, (bies1.i * bies2.i, false));
171 candidate = max(candidate, (bies1.i * bies2.e, false));
172 candidate = max(candidate, (bies1.b * bies2.i, false));
173 candidate = max(candidate, (bies1.b * bies2.e, false));
174 if candidate.1 {
175 breakpoints.push(i);
176 }
177 }
178 Self {
179 breakpoints,
180 length: matrix.0.len(),
181 }
182 }
183
184 fn from_bies_matrix_2a(
185 matrix: &BiesMatrix,
186 mut valid_breakpoints: impl Iterator<Item = usize>,
187 ) -> Self {
188 if matrix.0.len() <= 1 {
189 return Self::default();
190 }
191 let mut breakpoints = vec![];
192 let mut inside_word = false;
193 let mut next_valid_brkpt = valid_breakpoints.next();
194 for i in 0..(matrix.0.len() - 1) {
195 let bies1 = &matrix.0[i];
196 let bies2 = &matrix.0[i + 1];
197 let is_valid_brkpt = next_valid_brkpt == Some(i + 1);
198 let mut candidate = (f32::NEG_INFINITY, false);
199 if inside_word {
200 candidate = max(candidate, (bies1.i * bies2.e, false));
202 candidate = max(candidate, (bies1.i * bies2.i, false));
203 if is_valid_brkpt {
204 candidate = max(candidate, (bies1.e * bies2.b, true));
206 candidate = max(candidate, (bies1.e * bies2.s, true));
207 }
208 } else {
209 candidate = max(candidate, (bies1.b * bies2.i, false));
211 candidate = max(candidate, (bies1.b * bies2.e, false));
212 if is_valid_brkpt {
213 candidate = max(candidate, (bies1.s * bies2.b, true));
215 candidate = max(candidate, (bies1.s * bies2.s, true));
216 }
217 }
218 if candidate.1 {
219 breakpoints.push(i + 1);
220 }
221 inside_word = !candidate.1;
222 if is_valid_brkpt {
223 next_valid_brkpt = valid_breakpoints.next();
224 }
225 }
226 Self {
227 breakpoints,
228 length: matrix.0.len(),
229 }
230 }
231
232 fn from_bies_matrix_3a(
233 matrix: &BiesMatrix,
234 valid_breakpoints: impl Iterator<Item = usize>,
235 ) -> Self {
236 let valid_breakpoints: Vec<usize> = valid_breakpoints.collect();
237 let mut best_log_probability = f32::NEG_INFINITY;
238 let mut breakpoints: Vec<usize> = vec![];
239 for i in 0..=valid_breakpoints.len() {
240 for combo in valid_breakpoints.iter().combinations(i) {
241 let mut log_probability = 0.0;
242 let mut add_word = |i: usize, j: usize| {
243 if i == j - 1 {
244 log_probability += matrix.0[i].s.ln();
245 } else {
246 log_probability += matrix.0[i].b.ln();
247 for k in (i + 1)..(j - 1) {
248 log_probability += matrix.0[k].i.ln();
249 }
250 log_probability += matrix.0[j - 1].e.ln();
251 }
252 };
253 let mut i = 0;
254 for j in combo.iter().copied().copied() {
255 add_word(i, j);
256 i = j;
257 }
258 add_word(i, matrix.0.len());
259 if log_probability > best_log_probability {
260 best_log_probability = log_probability;
261 breakpoints = combo.iter().copied().copied().collect();
262 }
263 }
264 }
265 Self {
266 breakpoints,
267 length: matrix.0.len(),
268 }
269 }
270}
271
272impl<'a> From<&'a Breakpoints> for BiesString<'a> {
273 fn from(other: &'a Breakpoints) -> Self {
274 Self(other)
275 }
276}
277
278impl Writeable for BiesString<'_> {
279 fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
280 let mut write_bies_word = |i: usize, j: usize| -> fmt::Result {
281 if i == j - 1 {
282 sink.write_char('s')?;
283 } else {
284 sink.write_char('b')?;
285 for _ in (i + 1)..(j - 1) {
286 sink.write_char('i')?;
287 }
288 sink.write_char('e')?;
289 }
290 Ok(())
291 };
292 let mut i = 0;
293 for j in self.0.breakpoints.iter().copied() {
294 write_bies_word(i, j)?;
295 i = j;
296 }
297 write_bies_word(i, self.0.length)?;
298 Ok(())
299 }
300
301 fn writeable_length_hint(&self) -> LengthHint {
302 LengthHint::exact(self.0.length)
303 }
304}
305
306impl fmt::Debug for BiesString<'_> {
307 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308 self.write_to(f)
309 }
310}
311
312writeable::impl_display_with_writeable!(BiesString<'_>);