1use std::fmt;
18use std::iter::FusedIterator;
19use std::ops::Range;
20
21use crate::analysis::inner::Node;
22use crate::analysis::lattice::Lattice;
23use crate::dic::lexicon_set::LexiconSet;
24use crate::dic::subset::InfoSubset;
25use crate::dic::word_id::WordId;
26use crate::dic::word_info::{WordInfo, WordInfoResolver};
27use crate::input_text::InputBuffer;
28use crate::prelude::*;
29
30pub trait RightId {
32 fn right_id(&self) -> u16;
33}
34
35pub trait PathCost {
37 fn total_cost(&self) -> i32;
38
39 #[inline]
40 fn is_connected_to_bos(&self) -> bool {
41 self.total_cost() != i32::MAX
42 }
43}
44
45pub trait LatticeNode: RightId {
46 fn begin(&self) -> usize;
47 fn end(&self) -> usize;
48 fn cost(&self) -> i16;
49 fn word_id(&self) -> WordId;
50 fn left_id(&self) -> u16;
51
52 #[inline]
55 fn is_oov(&self) -> bool {
56 self.word_id().is_oov()
57 }
58
59 #[inline]
62 fn is_special_node(&self) -> bool {
63 self.word_id().is_special()
64 }
65
66 #[inline]
68 fn num_codepts(&self) -> usize {
69 self.end() - self.begin()
70 }
71
72 #[inline]
74 fn char_range(&self) -> Range<usize> {
75 self.begin()..self.end()
76 }
77}
78
79#[derive(Clone)]
80pub struct ResultNode {
84 inner: Node,
85 total_cost: i32,
86 begin_bytes: u16,
87 end_bytes: u16,
88
89 word_info: WordInfo,
90}
91
92impl ResultNode {
93 pub fn new(
94 inner: Node,
95 total_cost: i32,
96 begin_bytes: u16,
97 end_bytes: u16,
98 word_info: WordInfo,
99 ) -> ResultNode {
100 ResultNode {
101 inner,
102 total_cost,
103 begin_bytes,
104 end_bytes,
105 word_info,
106 }
107 }
108}
109
110impl RightId for ResultNode {
111 fn right_id(&self) -> u16 {
112 self.inner.right_id()
113 }
114}
115
116impl PathCost for ResultNode {
117 fn total_cost(&self) -> i32 {
118 self.total_cost
119 }
120}
121
122impl LatticeNode for ResultNode {
123 fn begin(&self) -> usize {
124 self.inner.begin()
125 }
126
127 fn end(&self) -> usize {
128 self.inner.end()
129 }
130
131 fn cost(&self) -> i16 {
132 self.inner.cost()
133 }
134
135 fn word_id(&self) -> WordId {
136 self.inner.word_id()
137 }
138
139 fn left_id(&self) -> u16 {
140 self.inner.left_id()
141 }
142}
143
144impl ResultNode {
145 pub fn word_info(&self) -> &WordInfo {
146 &self.word_info
147 }
148
149 pub fn begin_bytes(&self) -> usize {
151 self.begin_bytes as usize
152 }
153
154 pub fn end_bytes(&self) -> usize {
156 self.end_bytes as usize
157 }
158
159 pub fn bytes_range(&self) -> Range<usize> {
161 self.begin_bytes()..self.end_bytes()
162 }
163
164 pub fn set_bytes_range(&mut self, begin: u16, end: u16) {
165 self.begin_bytes = begin;
166 self.end_bytes = end;
167 }
168
169 pub fn set_char_range(&mut self, begin: u16, end: u16) {
170 self.inner.set_range(begin, end)
171 }
172
173 pub fn num_splits(&self, mode: Mode) -> usize {
175 match mode {
176 Mode::A => self.word_info.a_unit_split().len(),
177 Mode::B => self.word_info.b_unit_split().len(),
178 Mode::C => 0,
179 }
180 }
181
182 pub fn split<'a>(
184 &'a self,
185 mode: Mode,
186 lexicon_set: &'a LexiconSet<'a>,
187 subset: InfoSubset,
188 text: &'a InputBuffer,
189 ) -> NodeSplitIterator<'a> {
190 let splits: &[WordId] = match mode {
191 Mode::A => self.word_info.a_unit_split(),
192 Mode::B => self.word_info.b_unit_split(),
193 Mode::C => panic!("splitting Node with Mode::C is not supported"),
194 };
195
196 NodeSplitIterator {
197 splits,
198 index: 0,
199 lexicon_set,
200 subset,
201 text,
202 byte_offset: self.begin_bytes,
203 byte_end: self.end_bytes,
204 char_offset: self.begin() as u16,
205 char_end: self.end() as u16,
206 }
207 }
208}
209
210impl fmt::Display for ResultNode {
211 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212 write!(
213 f,
214 "{} {} {} {} {} {} {}",
215 self.begin(),
216 self.end(),
217 self.word_id(),
218 self.word_info().pos_id(),
219 self.left_id(),
220 self.right_id(),
221 self.cost()
222 )
223 }
224}
225
226pub struct NodeSplitIterator<'a> {
227 splits: &'a [WordId],
228 lexicon_set: &'a LexiconSet<'a>,
229 index: usize,
230 subset: InfoSubset,
231 text: &'a InputBuffer,
232 char_offset: u16,
233 byte_offset: u16,
234 char_end: u16,
235 byte_end: u16,
236}
237
238impl<'a> Iterator for NodeSplitIterator<'a> {
239 type Item = ResultNode;
240
241 #[inline]
242 fn next(&mut self) -> Option<Self::Item> {
243 let idx = self.index;
244 if idx >= self.splits.len() {
245 return None;
246 }
247
248 let char_start = self.char_offset;
249 let byte_start = self.byte_offset;
250
251 let word_id = self.splits[idx];
252 let word_info = self
254 .lexicon_set
255 .get_word_info_subset(word_id, self.subset)
256 .unwrap();
257
258 let (char_end, byte_end) = if idx + 1 == self.splits.len() {
259 (self.char_end, self.byte_end)
260 } else {
261 let byte_end = byte_start as usize + word_info.index_form_length();
262 let char_end = self.text.ch_idx(byte_end);
263 (char_end as u16, byte_end as u16)
264 };
265
266 self.char_offset = char_end;
267 self.byte_offset = byte_end;
268
269 let inner = Node::new(char_start, char_end, u16::MAX, u16::MAX, i16::MAX, word_id);
270
271 let node = ResultNode::new(inner, i32::MAX, byte_start, byte_end, word_info);
272
273 self.index += 1;
274 Some(node)
275 }
276
277 #[inline]
278 fn size_hint(&self) -> (usize, Option<usize>) {
279 (self.splits.len(), Some(self.splits.len()))
280 }
281}
282
283impl FusedIterator for NodeSplitIterator<'_> {}
284
285pub fn concat_nodes(
287 mut path: Vec<ResultNode>,
288 begin: usize,
289 end: usize,
290 normalized_form: Option<String>,
291 resolver: &dyn WordInfoResolver,
292) -> SudachiResult<Vec<ResultNode>> {
293 if begin >= end {
294 return Err(SudachiError::InvalidRange(begin, end));
295 }
296
297 let end_bytes = path[end - 1].end_bytes();
298 let beg_bytes = path[begin].begin_bytes();
299
300 let mut headword = String::with_capacity(end_bytes - beg_bytes);
301 let mut reading_form = String::with_capacity(end_bytes - beg_bytes);
302 let mut dictionary_form = String::with_capacity(end_bytes - beg_bytes);
303 let mut index_form_length = 0;
304
305 for node in path[begin..end].iter() {
306 headword.push_str(node.word_info().headword(resolver));
307 reading_form.push_str(node.word_info().reading_form(resolver));
308 dictionary_form.push_str(node.word_info().dictionary_form(resolver));
309 index_form_length += node.word_info().index_form_length();
310 }
311
312 let normalized_form = normalized_form.unwrap_or_else(|| {
313 let mut norm = String::with_capacity(end_bytes - beg_bytes);
314 for node in path[begin..end].iter() {
315 norm.push_str(node.word_info().normalized_form(resolver));
316 }
317 norm
318 });
319
320 let pos_id = path[begin].word_info().pos_id() as i16;
321
322 let wid = WordId::oov(pos_id as u32);
323 let new_wi = WordInfo::new_with_strings(
324 pos_id,
325 index_form_length as i16,
326 wid,
327 headword,
328 reading_form,
329 normalized_form,
330 dictionary_form,
331 );
332
333 let inner = Node::new(
334 path[begin].begin() as u16,
335 path[end - 1].end() as u16,
336 u16::MAX,
337 u16::MAX,
338 i16::MAX,
339 wid,
340 );
341
342 let node = ResultNode::new(
343 inner,
344 path[end - 1].total_cost,
345 path[begin].begin_bytes,
346 path[end - 1].end_bytes,
347 new_wi,
348 );
349
350 path[begin] = node;
351 path.drain(begin + 1..end);
352 Ok(path)
353}
354
355pub fn concat_oov_nodes(
357 mut path: Vec<ResultNode>,
358 begin: usize,
359 end: usize,
360 pos_id: u16,
361 text: &InputBuffer,
362 lattice: &Lattice,
363 resolver: &dyn WordInfoResolver,
364) -> SudachiResult<Vec<ResultNode>> {
365 if begin >= end {
366 return Err(SudachiError::InvalidRange(begin, end));
367 }
368
369 let byte_begin = path[begin].begin_bytes;
370 let byte_end = path[end - 1].end_bytes;
371 let node_begin = path[begin].begin();
372 let node_end = path[end - 1].end();
373
374 if let Some((existing_node, cost)) = lattice.get_minimum_node(node_begin, node_end) {
376 if !existing_node.is_special_node() {
377 let word_info = if existing_node.is_oov() {
378 let surface = text
379 .curr_slice_c(existing_node.begin()..existing_node.end())
380 .to_owned();
381 WordInfo::new_oov(
382 existing_node.word_id().entry().as_raw() as u16,
383 surface.len() as i16,
384 existing_node.word_id(),
385 surface,
386 )
387 } else {
388 resolver
389 .lexicon()
390 .get_word_info_subset(existing_node.word_id(), InfoSubset::all())?
391 };
392 let node =
393 ResultNode::new(existing_node.clone(), cost, byte_begin, byte_end, word_info);
394 path[begin] = node;
395 path.drain(begin + 1..end);
396 return Ok(path);
397 }
398 }
399
400 let capa = path[end - 1].end_bytes() - path[begin].begin_bytes();
402
403 let mut headword = String::with_capacity(capa);
404 let mut index_form_length = 0;
405 for node in path[begin..end].iter() {
406 headword.push_str(node.word_info().headword(resolver));
407 index_form_length += node.word_info().index_form_length();
408 }
409
410 let wid = WordId::oov(pos_id as u32);
413
414 let new_wi = WordInfo::new_oov(pos_id, index_form_length as i16, wid, headword);
415
416 let inner = Node::new(
417 path[begin].begin() as u16,
418 path[end - 1].end() as u16,
419 u16::MAX,
420 u16::MAX,
421 i16::MAX,
422 wid,
423 );
424
425 let node = ResultNode::new(
426 inner,
427 path[end - 1].total_cost,
428 byte_begin,
429 byte_end,
430 new_wi,
431 );
432
433 path[begin] = node;
434 path.drain(begin + 1..end);
435 Ok(path)
436}