1use crate::error::Result;
8use crate::geometry::Rectangle;
9use crate::objects::{Dictionary, Object};
10use crate::page::Page;
11
12#[derive(Debug, Clone)]
14pub enum PageTreeNode {
15 Pages(PagesNode),
17 Page(PageNode),
19}
20
21#[derive(Debug, Clone)]
23pub struct PagesNode {
24 pub kids: Vec<PageTreeNode>,
26 pub count: usize,
28 pub attributes: InheritableAttributes,
30 pub parent: Option<Box<PageTreeNode>>,
32}
33
34#[derive(Debug, Clone)]
36pub struct PageNode {
37 pub contents: Vec<Object>,
39 pub attributes: InheritableAttributes,
41 pub parent: Option<Box<PagesNode>>,
43 pub annotations: Vec<Object>,
45 pub metadata: Option<Dictionary>,
47}
48
49#[derive(Debug, Clone, Default)]
51pub struct InheritableAttributes {
52 pub resources: Option<Dictionary>,
54 pub media_box: Option<Rectangle>,
56 pub crop_box: Option<Rectangle>,
58 pub rotate: Option<i32>,
60}
61
62impl InheritableAttributes {
63 pub fn new() -> Self {
65 Self::default()
66 }
67
68 pub fn merge_with_parent(&self, parent: &InheritableAttributes) -> InheritableAttributes {
70 InheritableAttributes {
71 resources: self.resources.clone().or_else(|| parent.resources.clone()),
72 media_box: self.media_box.or(parent.media_box),
73 crop_box: self.crop_box.or(parent.crop_box),
74 rotate: self.rotate.or(parent.rotate),
75 }
76 }
77
78 pub fn to_dict(&self) -> Dictionary {
80 let mut dict = Dictionary::new();
81
82 if let Some(ref resources) = self.resources {
83 dict.set("Resources", Object::Dictionary(resources.clone()));
84 }
85
86 if let Some(media_box) = self.media_box {
87 dict.set(
88 "MediaBox",
89 Object::Array(vec![
90 Object::Real(media_box.lower_left.x),
91 Object::Real(media_box.lower_left.y),
92 Object::Real(media_box.upper_right.x),
93 Object::Real(media_box.upper_right.y),
94 ]),
95 );
96 }
97
98 if let Some(crop_box) = self.crop_box {
99 dict.set(
100 "CropBox",
101 Object::Array(vec![
102 Object::Real(crop_box.lower_left.x),
103 Object::Real(crop_box.lower_left.y),
104 Object::Real(crop_box.upper_right.x),
105 Object::Real(crop_box.upper_right.y),
106 ]),
107 );
108 }
109
110 if let Some(rotate) = self.rotate {
111 dict.set("Rotate", Object::Integer(rotate as i64));
112 }
113
114 dict
115 }
116}
117
118pub struct PageTree {
120 root: PagesNode,
122 max_kids: usize,
124}
125
126impl PageTree {
127 pub fn new() -> Self {
129 Self {
130 root: PagesNode {
131 kids: Vec::new(),
132 count: 0,
133 attributes: InheritableAttributes::new(),
134 parent: None,
135 },
136 max_kids: 10, }
138 }
139
140 pub fn set_max_kids(&mut self, max: usize) {
142 self.max_kids = max.max(2); }
144
145 pub fn add_page(&mut self, page: Page) -> Result<()> {
147 use crate::geometry::Point;
148
149 let media_box = Rectangle::new(
151 Point::new(0.0, 0.0),
152 Point::new(page.width(), page.height()),
153 );
154
155 let page_node = PageNode {
156 contents: vec![],
157 attributes: InheritableAttributes {
158 media_box: Some(media_box),
159 crop_box: None, rotate: Some(0), resources: None, },
163 parent: None,
164 annotations: Vec::new(),
165 metadata: None,
166 };
167
168 self.add_page_node(page_node);
169 self.root.count += 1;
170 Ok(())
171 }
172
173 fn add_page_node(&mut self, page: PageNode) {
175 self.root.kids.push(PageTreeNode::Page(page));
178 }
179
180 pub fn page_count(&self) -> usize {
182 self.root.count
183 }
184
185 pub fn get_page(&self, index: usize) -> Option<PageNode> {
187 self.get_page_from_node_clone(&self.root, index)
188 }
189
190 #[allow(clippy::only_used_in_recursion)]
192 fn get_page_from_node_clone(&self, pages: &PagesNode, index: usize) -> Option<PageNode> {
193 let mut current_index = index;
194 for kid in &pages.kids {
195 match kid {
196 PageTreeNode::Page(page) => {
197 if current_index == 0 {
198 return Some(page.clone());
199 }
200 current_index -= 1;
201 }
202 PageTreeNode::Pages(pages) => {
203 let kid_count = pages.count;
204 if current_index < kid_count {
205 return self.get_page_from_node_clone(pages, current_index);
206 }
207 current_index -= kid_count;
208 }
209 }
210 }
211 None
212 }
213
214 #[allow(dead_code)]
216 fn get_node_count(&self, node: &PageTreeNode) -> usize {
217 match node {
218 PageTreeNode::Page(_) => 1,
219 PageTreeNode::Pages(pages) => pages.count,
220 }
221 }
222
223 pub fn to_dict(&self) -> Dictionary {
225 self.node_to_dict(&PageTreeNode::Pages(self.root.clone()))
226 }
227
228 #[allow(clippy::only_used_in_recursion)]
230 fn node_to_dict(&self, node: &PageTreeNode) -> Dictionary {
231 let mut dict = Dictionary::new();
232
233 match node {
234 PageTreeNode::Page(page) => {
235 dict.set("Type", Object::Name("Page".to_string()));
236
237 let attrs = page.attributes.to_dict();
239 for (key, value) in attrs.iter() {
240 dict.set(key, value.clone());
241 }
242
243 if !page.contents.is_empty() {
245 if page.contents.len() == 1 {
246 dict.set("Contents", page.contents[0].clone());
247 } else {
248 dict.set("Contents", Object::Array(page.contents.clone()));
249 }
250 }
251
252 if !page.annotations.is_empty() {
254 dict.set("Annots", Object::Array(page.annotations.clone()));
255 }
256 }
257 PageTreeNode::Pages(pages) => {
258 dict.set("Type", Object::Name("Pages".to_string()));
259 dict.set("Count", Object::Integer(pages.count as i64));
260
261 let attrs = pages.attributes.to_dict();
263 for (key, value) in attrs.iter() {
264 dict.set(key, value.clone());
265 }
266
267 let kids: Vec<Object> = pages
269 .kids
270 .iter()
271 .map(|kid| Object::Dictionary(self.node_to_dict(kid)))
272 .collect();
273 dict.set("Kids", Object::Array(kids));
274 }
275 }
276
277 dict
278 }
279
280 pub fn balance(&mut self) {
282 }
285
286 pub fn set_default_media_box(&mut self, rect: Rectangle) {
288 self.root.attributes.media_box = Some(rect);
289 }
290
291 pub fn set_default_resources(&mut self, resources: Dictionary) {
293 self.root.attributes.resources = Some(resources);
294 }
295
296 pub fn find_pages<F>(&self, predicate: F) -> Vec<usize>
298 where
299 F: Fn(&PageNode) -> bool,
300 {
301 let mut results = Vec::new();
302 self.find_pages_in_node(&self.root, &predicate, 0, &mut results);
303 results
304 }
305
306 #[allow(clippy::only_used_in_recursion)]
307 fn find_pages_in_node<F>(
308 &self,
309 pages: &PagesNode,
310 predicate: &F,
311 base_index: usize,
312 results: &mut Vec<usize>,
313 ) -> usize
314 where
315 F: Fn(&PageNode) -> bool,
316 {
317 let mut current_index = base_index;
318 let mut count = 0;
319
320 for kid in &pages.kids {
321 match kid {
322 PageTreeNode::Page(page) => {
323 if predicate(page) {
324 results.push(current_index);
325 }
326 current_index += 1;
327 count += 1;
328 }
329 PageTreeNode::Pages(inner_pages) => {
330 let inner_count =
331 self.find_pages_in_node(inner_pages, predicate, current_index, results);
332 current_index += inner_count;
333 count += inner_count;
334 }
335 }
336 }
337
338 count
339 }
340}
341
342impl Default for PageTree {
343 fn default() -> Self {
344 Self::new()
345 }
346}
347
348pub struct PageTreeBuilder {
350 tree: PageTree,
351 default_attributes: InheritableAttributes,
352}
353
354impl Default for PageTreeBuilder {
355 fn default() -> Self {
356 Self::new()
357 }
358}
359
360impl PageTreeBuilder {
361 pub fn new() -> Self {
363 Self {
364 tree: PageTree::new(),
365 default_attributes: InheritableAttributes::new(),
366 }
367 }
368
369 pub fn with_media_box(mut self, rect: Rectangle) -> Self {
371 self.default_attributes.media_box = Some(rect);
372 self.tree.set_default_media_box(rect);
373 self
374 }
375
376 pub fn with_resources(mut self, resources: Dictionary) -> Self {
378 self.default_attributes.resources = Some(resources.clone());
379 self.tree.set_default_resources(resources);
380 self
381 }
382
383 pub fn with_rotation(mut self, degrees: i32) -> Self {
385 self.default_attributes.rotate = Some(degrees);
386 self
387 }
388
389 pub fn add_page(mut self, page: Page) -> Self {
391 self.tree.add_page(page).unwrap_or_else(|e| {
392 tracing::debug!("Warning: Failed to add page: {}", e);
393 });
394 self
395 }
396
397 pub fn build(mut self) -> PageTree {
399 self.tree.balance();
400 self.tree
401 }
402}
403
404#[cfg(test)]
405mod tests {
406 use super::*;
407 use crate::geometry::Point;
408
409 #[test]
410 fn test_inheritable_attributes_merge() {
411 let parent = InheritableAttributes {
412 resources: Some(Dictionary::new()),
413 media_box: Some(Rectangle::new(
414 Point::new(0.0, 0.0),
415 Point::new(612.0, 792.0),
416 )),
417 crop_box: None,
418 rotate: Some(0),
419 };
420
421 let child = InheritableAttributes {
422 resources: None,
423 media_box: None,
424 crop_box: Some(Rectangle::new(
425 Point::new(10.0, 10.0),
426 Point::new(602.0, 782.0),
427 )),
428 rotate: Some(90),
429 };
430
431 let merged = child.merge_with_parent(&parent);
432 assert!(merged.resources.is_some());
433 assert!(merged.media_box.is_some());
434 assert!(merged.crop_box.is_some());
435 assert_eq!(merged.rotate, Some(90));
436 }
437
438 #[test]
439 fn test_page_tree_creation() {
440 let tree = PageTree::new();
441 assert_eq!(tree.page_count(), 0);
442 }
443
444 #[test]
445 fn test_page_tree_add_page() {
446 let mut tree = PageTree::new();
447 let page = Page::new(612.0, 792.0);
448 tree.add_page(page).unwrap();
449 assert_eq!(tree.page_count(), 1);
450 }
451
452 #[test]
453 fn test_page_tree_builder() {
454 let rect = Rectangle::new(Point::new(0.0, 0.0), Point::new(612.0, 792.0));
455 let tree = PageTreeBuilder::new()
456 .with_media_box(rect)
457 .with_rotation(0)
458 .add_page(Page::new(612.0, 792.0))
459 .add_page(Page::new(612.0, 792.0))
460 .build();
461
462 assert_eq!(tree.page_count(), 2);
463 }
464
465 #[test]
466 fn test_page_tree_to_dict() {
467 let mut tree = PageTree::new();
468 tree.add_page(Page::new(612.0, 792.0)).unwrap();
469
470 let dict = tree.to_dict();
471 assert_eq!(dict.get("Type"), Some(&Object::Name("Pages".to_string())));
472 assert_eq!(dict.get("Count"), Some(&Object::Integer(1)));
473 assert!(dict.get("Kids").is_some());
474 }
475
476 #[test]
477 fn test_find_pages() {
478 let mut tree = PageTree::new();
479
480 tree.add_page(Page::new(612.0, 792.0)).unwrap(); tree.add_page(Page::new(595.0, 842.0)).unwrap(); tree.add_page(Page::new(612.0, 792.0)).unwrap(); let letter_pages = tree.find_pages(|page| {
487 page.attributes
488 .media_box
489 .map(|mb| mb.upper_right.x == 612.0 && mb.upper_right.y == 792.0)
490 .unwrap_or(false)
491 });
492
493 assert_eq!(letter_pages.len(), 2);
494 assert_eq!(letter_pages[0], 0);
495 assert_eq!(letter_pages[1], 2);
496 }
497
498 #[test]
499 fn test_attributes_to_dict() {
500 let attrs = InheritableAttributes {
501 resources: Some(Dictionary::new()),
502 media_box: Some(Rectangle::new(
503 Point::new(0.0, 0.0),
504 Point::new(612.0, 792.0),
505 )),
506 crop_box: None,
507 rotate: Some(90),
508 };
509
510 let dict = attrs.to_dict();
511 assert!(dict.get("Resources").is_some());
512 assert!(dict.get("MediaBox").is_some());
513 assert_eq!(dict.get("Rotate"), Some(&Object::Integer(90)));
514 }
515
516 #[test]
517 fn test_max_kids_setting() {
518 let mut tree = PageTree::new();
519 tree.set_max_kids(5);
520 assert_eq!(tree.max_kids, 5);
521
522 tree.set_max_kids(1); assert_eq!(tree.max_kids, 2);
524 }
525
526 #[test]
527 fn test_page_tree_get_page() {
528 let mut tree = PageTree::new();
529
530 tree.add_page(Page::new(612.0, 792.0)).unwrap();
532 tree.add_page(Page::new(595.0, 842.0)).unwrap();
533 tree.add_page(Page::new(420.0, 595.0)).unwrap();
534
535 let page0 = tree.get_page(0);
537 assert!(page0.is_some());
538 let page0 = page0.unwrap();
539 assert_eq!(page0.attributes.media_box.unwrap().upper_right.x, 612.0);
540
541 let page1 = tree.get_page(1);
542 assert!(page1.is_some());
543 let page1 = page1.unwrap();
544 assert_eq!(page1.attributes.media_box.unwrap().upper_right.x, 595.0);
545
546 let page2 = tree.get_page(2);
547 assert!(page2.is_some());
548 let page2 = page2.unwrap();
549 assert_eq!(page2.attributes.media_box.unwrap().upper_right.x, 420.0);
550
551 assert!(tree.get_page(3).is_none());
553 assert!(tree.get_page(100).is_none());
554 }
555
556 #[test]
557 fn test_page_tree_empty() {
558 let tree = PageTree::new();
559 assert_eq!(tree.page_count(), 0);
560 assert!(tree.get_page(0).is_none());
561
562 let dict = tree.to_dict();
563 assert_eq!(dict.get("Type"), Some(&Object::Name("Pages".to_string())));
564 assert_eq!(dict.get("Count"), Some(&Object::Integer(0)));
565 }
566
567 #[test]
568 fn test_page_tree_large_number_of_pages() {
569 let mut tree = PageTree::new();
570
571 for _ in 0..100 {
573 tree.add_page(Page::new(612.0, 792.0)).unwrap();
574 }
575
576 assert_eq!(tree.page_count(), 100);
577
578 assert!(tree.get_page(0).is_some());
580 assert!(tree.get_page(49).is_some());
581 assert!(tree.get_page(99).is_some());
582 assert!(tree.get_page(100).is_none());
583 }
584
585 #[test]
586 fn test_page_node_creation() {
587 let page_node = PageNode {
588 contents: vec![Object::Integer(1), Object::Integer(2)],
589 attributes: InheritableAttributes::new(),
590 parent: None,
591 annotations: vec![Object::Name("Annot".to_string())],
592 metadata: Some(Dictionary::new()),
593 };
594
595 assert_eq!(page_node.contents.len(), 2);
596 assert_eq!(page_node.annotations.len(), 1);
597 assert!(page_node.metadata.is_some());
598 }
599
600 #[test]
601 fn test_pages_node_creation() {
602 let pages_node = PagesNode {
603 kids: vec![],
604 count: 0,
605 attributes: InheritableAttributes::new(),
606 parent: None,
607 };
608
609 assert_eq!(pages_node.kids.len(), 0);
610 assert_eq!(pages_node.count, 0);
611 }
612
613 #[test]
614 fn test_inheritable_attributes_default() {
615 let attrs = InheritableAttributes::default();
616 assert!(attrs.resources.is_none());
617 assert!(attrs.media_box.is_none());
618 assert!(attrs.crop_box.is_none());
619 assert!(attrs.rotate.is_none());
620 }
621
622 #[test]
623 fn test_inheritable_attributes_complete() {
624 let attrs = InheritableAttributes {
625 resources: Some(Dictionary::new()),
626 media_box: Some(Rectangle::new(
627 Point::new(0.0, 0.0),
628 Point::new(612.0, 792.0),
629 )),
630 crop_box: Some(Rectangle::new(
631 Point::new(36.0, 36.0),
632 Point::new(576.0, 756.0),
633 )),
634 rotate: Some(90),
635 };
636
637 let dict = attrs.to_dict();
638 assert!(dict.get("Resources").is_some());
639 assert!(dict.get("MediaBox").is_some());
640 assert!(dict.get("CropBox").is_some());
641 assert_eq!(dict.get("Rotate"), Some(&Object::Integer(90)));
642 }
643
644 #[test]
645 fn test_merge_with_parent_override() {
646 let parent = InheritableAttributes {
647 resources: Some(Dictionary::new()),
648 media_box: Some(Rectangle::new(
649 Point::new(0.0, 0.0),
650 Point::new(612.0, 792.0),
651 )),
652 crop_box: Some(Rectangle::new(
653 Point::new(0.0, 0.0),
654 Point::new(612.0, 792.0),
655 )),
656 rotate: Some(0),
657 };
658
659 let mut child_resources = Dictionary::new();
660 child_resources.set("Font", Object::Name("F1".to_string()));
661
662 let child = InheritableAttributes {
663 resources: Some(child_resources.clone()),
664 media_box: Some(Rectangle::new(
665 Point::new(0.0, 0.0),
666 Point::new(595.0, 842.0),
667 )),
668 crop_box: None,
669 rotate: Some(180),
670 };
671
672 let merged = child.merge_with_parent(&parent);
673
674 assert_eq!(
676 merged.resources.unwrap().get("Font"),
677 Some(&Object::Name("F1".to_string()))
678 );
679 assert_eq!(merged.media_box.unwrap().upper_right.x, 595.0);
680 assert_eq!(merged.crop_box.unwrap().upper_right.x, 612.0); assert_eq!(merged.rotate, Some(180));
682 }
683
684 #[test]
685 fn test_merge_with_parent_inherit_all() {
686 let parent = InheritableAttributes {
687 resources: Some(Dictionary::new()),
688 media_box: Some(Rectangle::new(
689 Point::new(0.0, 0.0),
690 Point::new(612.0, 792.0),
691 )),
692 crop_box: Some(Rectangle::new(
693 Point::new(36.0, 36.0),
694 Point::new(576.0, 756.0),
695 )),
696 rotate: Some(90),
697 };
698
699 let child = InheritableAttributes::new();
700 let merged = child.merge_with_parent(&parent);
701
702 assert!(merged.resources.is_some());
704 assert_eq!(merged.media_box.unwrap().upper_right.x, 612.0);
705 assert_eq!(merged.crop_box.unwrap().upper_right.x, 576.0);
706 assert_eq!(merged.rotate, Some(90));
707 }
708
709 #[test]
710 fn test_page_tree_builder_comprehensive() {
711 let rect = Rectangle::new(Point::new(0.0, 0.0), Point::new(595.0, 842.0));
712 let mut resources = Dictionary::new();
713 resources.set("Font", Object::Name("F1".to_string()));
714
715 let tree = PageTreeBuilder::new()
716 .with_media_box(rect)
717 .with_resources(resources.clone())
718 .with_rotation(90)
719 .add_page(Page::new(595.0, 842.0))
720 .add_page(Page::new(595.0, 842.0))
721 .add_page(Page::new(595.0, 842.0))
722 .build();
723
724 assert_eq!(tree.page_count(), 3);
725 assert_eq!(tree.root.attributes.media_box.unwrap().upper_right.x, 595.0);
726 assert!(tree.root.attributes.resources.is_some());
728 }
729
730 #[test]
731 fn test_find_pages_complex_predicate() {
732 let mut tree = PageTree::new();
733
734 tree.add_page(Page::new(612.0, 792.0)).unwrap(); tree.add_page(Page::new(595.0, 842.0)).unwrap(); tree.add_page(Page::new(420.0, 595.0)).unwrap(); tree.add_page(Page::new(612.0, 792.0)).unwrap(); tree.add_page(Page::new(595.0, 842.0)).unwrap(); let a4_pages = tree.find_pages(|page| {
743 page.attributes
744 .media_box
745 .map(|mb| mb.upper_right.x == 595.0 && mb.upper_right.y == 842.0)
746 .unwrap_or(false)
747 });
748
749 assert_eq!(a4_pages.len(), 2);
750 assert_eq!(a4_pages[0], 1);
751 assert_eq!(a4_pages[1], 4);
752
753 let small_pages = tree.find_pages(|page| {
755 page.attributes
756 .media_box
757 .map(|mb| mb.upper_right.x < 595.0 || mb.upper_right.y < 842.0)
758 .unwrap_or(false)
759 });
760
761 assert_eq!(small_pages.len(), 3); }
763
764 #[test]
765 fn test_page_tree_node_to_dict_page() {
766 let tree = PageTree::new();
767 let page_node = PageNode {
768 contents: vec![Object::Integer(1)],
769 attributes: InheritableAttributes {
770 media_box: Some(Rectangle::new(
771 Point::new(0.0, 0.0),
772 Point::new(612.0, 792.0),
773 )),
774 crop_box: None,
775 rotate: Some(0),
776 resources: None,
777 },
778 parent: None,
779 annotations: vec![Object::Name("Annot1".to_string())],
780 metadata: None,
781 };
782
783 let dict = tree.node_to_dict(&PageTreeNode::Page(page_node));
784 assert_eq!(dict.get("Type"), Some(&Object::Name("Page".to_string())));
785 assert_eq!(dict.get("Contents"), Some(&Object::Integer(1)));
786 assert!(dict.get("Annots").is_some());
787 assert!(dict.get("MediaBox").is_some());
788 }
789
790 #[test]
791 fn test_page_tree_node_to_dict_pages() {
792 let tree = PageTree::new();
793 let pages_node = PagesNode {
794 kids: vec![],
795 count: 5,
796 attributes: InheritableAttributes::new(),
797 parent: None,
798 };
799
800 let dict = tree.node_to_dict(&PageTreeNode::Pages(pages_node));
801 assert_eq!(dict.get("Type"), Some(&Object::Name("Pages".to_string())));
802 assert_eq!(dict.get("Count"), Some(&Object::Integer(5)));
803 assert!(dict.get("Kids").is_some());
804 }
805
806 #[test]
807 fn test_rotation_values() {
808 let attrs = InheritableAttributes {
809 resources: None,
810 media_box: None,
811 crop_box: None,
812 rotate: Some(270), };
814
815 let dict = attrs.to_dict();
816 assert_eq!(dict.get("Rotate"), Some(&Object::Integer(270)));
817
818 for rotation in &[0, 90, 180, 270] {
820 let attrs = InheritableAttributes {
821 resources: None,
822 media_box: None,
823 crop_box: None,
824 rotate: Some(*rotation),
825 };
826 let dict = attrs.to_dict();
827 assert_eq!(dict.get("Rotate"), Some(&Object::Integer(*rotation as i64)));
828 }
829 }
830
831 #[test]
832 fn test_balance_method() {
833 let mut tree = PageTree::new();
834
835 for _ in 0..50 {
837 tree.add_page(Page::new(612.0, 792.0)).unwrap();
838 }
839
840 let count_before = tree.page_count();
841 tree.balance();
842 let count_after = tree.page_count();
843
844 assert_eq!(count_before, count_after);
846 assert_eq!(count_after, 50);
847 }
848
849 #[test]
850 fn test_set_default_methods() {
851 let mut tree = PageTree::new();
852
853 let rect = Rectangle::new(Point::new(0.0, 0.0), Point::new(420.0, 595.0));
854 tree.set_default_media_box(rect);
855 assert_eq!(tree.root.attributes.media_box.unwrap().upper_right.x, 420.0);
856
857 let mut resources = Dictionary::new();
858 resources.set(
859 "ProcSet",
860 Object::Array(vec![Object::Name("PDF".to_string())]),
861 );
862 tree.set_default_resources(resources.clone());
863 assert!(tree.root.attributes.resources.is_some());
864 assert_eq!(
865 tree.root
866 .attributes
867 .resources
868 .as_ref()
869 .unwrap()
870 .get("ProcSet"),
871 Some(&Object::Array(vec![Object::Name("PDF".to_string())]))
872 );
873 }
874}