1use std::fmt::Debug;
12use std::ops::Range;
13
14use itertools::Itertools as _;
15use vortex_buffer::BitBuffer;
16use vortex_error::VortexExpect as _;
17use vortex_error::VortexResult;
18use vortex_error::vortex_bail;
19use vortex_error::vortex_err;
20use vortex_mask::Mask;
21use vortex_mask::MaskValues;
22
23use crate::ArrayRef;
24use crate::Canonical;
25use crate::ExecutionCtx;
26use crate::IntoArray;
27use crate::VortexSessionExecute;
28use crate::arrays::BoolArray;
29use crate::arrays::ChunkedArray;
30use crate::arrays::ConstantArray;
31use crate::arrays::scalar_fn::ScalarFnFactoryExt;
32use crate::builtins::ArrayBuiltins;
33use crate::dtype::DType;
34use crate::dtype::Nullability;
35use crate::legacy_session;
36use crate::optimizer::ArrayOptimizer;
37use crate::patches::Patches;
38use crate::scalar::Scalar;
39use crate::scalar_fn::fns::binary::Binary;
40use crate::scalar_fn::fns::operators::Operator;
41
42#[derive(Clone)]
44pub enum Validity {
45 NonNullable,
47 AllValid,
49 AllInvalid,
51 Array(ArrayRef),
55}
56
57impl Debug for Validity {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 match self {
60 Self::NonNullable => write!(f, "NonNullable"),
61 Self::AllValid => write!(f, "AllValid"),
62 Self::AllInvalid => write!(f, "AllInvalid"),
63 Self::Array(arr) => write!(f, "SomeValid({})", arr.display_values()),
64 }
65 }
66}
67
68impl Validity {
69 pub fn execute(self, ctx: &mut ExecutionCtx) -> VortexResult<Validity> {
71 match self {
72 v @ Validity::NonNullable | v @ Validity::AllValid | v @ Validity::AllInvalid => Ok(v),
73 Validity::Array(a) => Ok(Validity::Array(a.execute::<Canonical>(ctx)?.into_array())),
74 }
75 }
76}
77
78impl Validity {
79 pub const DTYPE: DType = DType::Bool(Nullability::NonNullable);
81
82 pub fn to_array(&self, len: usize) -> ArrayRef {
84 match self {
85 Self::NonNullable | Self::AllValid => ConstantArray::new(true, len).into_array(),
86 Self::AllInvalid => ConstantArray::new(false, len).into_array(),
87 Self::Array(a) => a.clone(),
88 }
89 }
90
91 #[inline]
93 pub fn into_array(self) -> Option<ArrayRef> {
94 if let Self::Array(a) = self {
95 Some(a)
96 } else {
97 None
98 }
99 }
100
101 #[inline]
103 pub fn as_array(&self) -> Option<&ArrayRef> {
104 if let Self::Array(a) = self {
105 Some(a)
106 } else {
107 None
108 }
109 }
110
111 #[inline]
112 pub fn nullability(&self) -> Nullability {
113 if matches!(self, Self::NonNullable) {
114 Nullability::NonNullable
115 } else {
116 Nullability::Nullable
117 }
118 }
119
120 #[inline]
127 pub fn definitely_no_nulls(&self) -> bool {
128 matches!(self, Self::NonNullable | Self::AllValid)
129 }
130
131 #[inline]
140 pub fn definitely_all_null(&self) -> bool {
141 matches!(self, Self::AllInvalid)
142 }
143
144 pub fn execute_no_nulls(&self, length: usize, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
150 match self {
151 Self::NonNullable | Self::AllValid => Ok(true),
152 Self::AllInvalid => Ok(length == 0),
153 Self::Array(_) => Ok(self.execute_mask(length, ctx)?.all_true()),
154 }
155 }
156
157 #[inline]
159 pub fn union_nullability(self, nullability: Nullability) -> Self {
160 match nullability {
161 Nullability::NonNullable => self,
162 Nullability::Nullable => self.into_nullable(),
163 }
164 }
165
166 #[inline]
168 pub fn execute_is_valid(&self, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
169 Ok(match self {
170 Self::NonNullable | Self::AllValid => true,
171 Self::AllInvalid => false,
172 Self::Array(a) => a
173 .execute_scalar(index, ctx)?
174 .as_bool()
175 .value()
176 .ok_or_else(|| vortex_err!("validity value at index {index} is null"))?,
177 })
178 }
179
180 #[inline]
182 pub fn execute_is_null(&self, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
183 Ok(!self.execute_is_valid(index, ctx)?)
184 }
185
186 #[deprecated(note = "use `execute_is_valid` with an explicit `ExecutionCtx`")]
188 #[inline]
189 #[allow(clippy::disallowed_methods)]
190 pub fn is_valid(&self, index: usize) -> VortexResult<bool> {
191 self.execute_is_valid(index, &mut legacy_session().create_execution_ctx())
192 }
193
194 #[deprecated(note = "use `execute_is_null` with an explicit `ExecutionCtx`")]
196 #[inline]
197 #[allow(clippy::disallowed_methods)]
198 pub fn is_null(&self, index: usize) -> VortexResult<bool> {
199 self.execute_is_null(index, &mut legacy_session().create_execution_ctx())
200 }
201
202 #[inline]
203 pub fn slice(&self, range: Range<usize>) -> VortexResult<Self> {
204 match self {
205 Self::Array(a) => Ok(Self::Array(a.slice(range)?)),
206 Self::NonNullable | Self::AllValid | Self::AllInvalid => Ok(self.clone()),
207 }
208 }
209
210 pub fn take(&self, indices: &ArrayRef) -> VortexResult<Self> {
211 match self {
212 Self::NonNullable => indices.validity(),
213 Self::AllValid => Ok(match indices.validity()? {
214 Self::NonNullable => Self::AllValid,
215 v => v,
216 }),
217 Self::AllInvalid => Ok(Self::AllInvalid),
218 Self::Array(is_valid) => {
219 let maybe_is_valid = is_valid.take(indices.clone())?;
220 let is_valid = maybe_is_valid.fill_null(Scalar::from(false))?;
222 Ok(Self::Array(is_valid))
223 }
224 }
225 }
226
227 pub fn not(&self) -> VortexResult<Self> {
229 match self {
230 Validity::NonNullable => Ok(Validity::NonNullable),
231 Validity::AllValid => Ok(Validity::AllInvalid),
232 Validity::AllInvalid => Ok(Validity::AllValid),
233 Validity::Array(arr) => Ok(Validity::Array(arr.not()?)),
234 }
235 }
236
237 pub fn filter(&self, mask: &Mask) -> VortexResult<Self> {
245 match self {
248 v @ (Validity::NonNullable | Validity::AllValid | Validity::AllInvalid) => {
249 Ok(v.clone())
250 }
251 Validity::Array(arr) => Ok(Validity::Array(arr.filter(mask.clone())?)),
252 }
253 }
254
255 #[deprecated(note = "Use execute_mask")]
259 pub fn to_mask(&self, length: usize, ctx: &mut ExecutionCtx) -> VortexResult<Mask> {
260 match self {
261 Self::NonNullable | Self::AllValid => Ok(Mask::new_true(length)),
262 Self::AllInvalid => Ok(Mask::new_false(length)),
263 Self::Array(arr) => arr.clone().execute::<Mask>(ctx),
264 }
265 }
266
267 #[inline]
268 pub fn execute_mask(&self, length: usize, ctx: &mut ExecutionCtx) -> VortexResult<Mask> {
269 match self {
270 Self::NonNullable | Self::AllValid => Ok(Mask::AllTrue(length)),
271 Self::AllInvalid => Ok(Mask::AllFalse(length)),
272 Self::Array(arr) => {
273 assert_eq!(
274 arr.len(),
275 length,
276 "Validity::Array length must equal to_logical's argument: {}, {}.",
277 arr.len(),
278 length,
279 );
280 arr.clone().execute::<Mask>(ctx)
283 }
284 }
285 }
286
287 pub fn mask_eq(
290 &self,
291 other: &Validity,
292 length: usize,
293 ctx: &mut ExecutionCtx,
294 ) -> VortexResult<bool> {
295 match (self, other) {
296 (
298 Validity::NonNullable | Validity::AllValid,
299 Validity::NonNullable | Validity::AllValid,
300 )
301 | (Validity::AllInvalid, Validity::AllInvalid) => Ok(true),
302 _ => Ok(self.execute_mask(length, ctx)? == other.execute_mask(length, ctx)?),
303 }
304 }
305
306 #[inline]
308 pub fn and(self, rhs: Validity) -> VortexResult<Validity> {
309 Ok(match (self, rhs) {
310 (Validity::NonNullable, Validity::NonNullable) => Validity::NonNullable,
312 (Validity::AllInvalid, _) | (_, Validity::AllInvalid) => Validity::AllInvalid,
314 (Validity::Array(a), Validity::AllValid)
316 | (Validity::Array(a), Validity::NonNullable)
317 | (Validity::NonNullable, Validity::Array(a))
318 | (Validity::AllValid, Validity::Array(a)) => Validity::Array(a),
319 (Validity::NonNullable, Validity::AllValid)
321 | (Validity::AllValid, Validity::NonNullable)
322 | (Validity::AllValid, Validity::AllValid) => Validity::AllValid,
323 (Validity::Array(lhs), Validity::Array(rhs)) => Validity::Array(
325 Binary
326 .try_new_array(lhs.len(), Operator::And, [lhs, rhs])?
327 .optimize()?,
328 ),
329 })
330 }
331
332 pub fn patch(
333 self,
334 len: usize,
335 indices_offset: usize,
336 indices: &ArrayRef,
337 patches: &Validity,
338 ctx: &mut ExecutionCtx,
339 ) -> VortexResult<Self> {
340 match (&self, patches) {
341 (Validity::NonNullable, Validity::NonNullable) => return Ok(Validity::NonNullable),
342 (Validity::NonNullable, _) => {
343 vortex_bail!("Can't patch a non-nullable validity with nullable validity")
344 }
345 (_, Validity::NonNullable) => {
346 vortex_bail!("Can't patch a nullable validity with non-nullable validity")
347 }
348 (Validity::AllValid, Validity::AllValid) => return Ok(Validity::AllValid),
349 (Validity::AllInvalid, Validity::AllInvalid) => return Ok(Validity::AllInvalid),
350 _ => {}
351 };
352
353 if matches!(self, Validity::NonNullable) {
354 return Ok(Self::NonNullable);
355 }
356
357 let source = match self {
359 Validity::NonNullable => BoolArray::from(BitBuffer::new_set(len)),
360 Validity::AllValid => BoolArray::from(BitBuffer::new_set(len)),
361 Validity::AllInvalid => BoolArray::from(BitBuffer::new_unset(len)),
362 Validity::Array(a) => a.execute::<BoolArray>(ctx)?,
363 };
364
365 let patch_values = match patches {
366 Validity::NonNullable => BoolArray::from(BitBuffer::new_set(indices.len())),
367 Validity::AllValid => BoolArray::from(BitBuffer::new_set(indices.len())),
368 Validity::AllInvalid => BoolArray::from(BitBuffer::new_unset(indices.len())),
369 Validity::Array(a) => a.clone().execute::<BoolArray>(ctx)?,
370 };
371
372 let patches = Patches::new(
373 len,
374 indices_offset,
375 indices.clone(),
376 patch_values.into_array(),
377 None,
379 )?;
380
381 Ok(Self::Array(source.patch(&patches, ctx)?.into_array()))
382 }
383
384 #[inline]
386 pub fn into_nullable(self) -> Validity {
387 match self {
388 Self::NonNullable => Self::AllValid,
389 Self::AllValid | Self::AllInvalid | Self::Array(_) => self,
390 }
391 }
392
393 #[inline]
399 pub fn into_non_nullable(self, len: usize, ctx: &mut ExecutionCtx) -> Option<Validity> {
400 match self {
401 _ if len == 0 => Some(Validity::NonNullable),
402 Self::NonNullable => Some(Self::NonNullable),
403 Self::AllValid => Some(Self::NonNullable),
404 Self::AllInvalid => None,
405 Self::Array(is_valid) => {
406 is_valid
407 .statistics()
408 .compute_min::<bool>(ctx)
409 .vortex_expect("validity array must support min")
410 .then(|| {
411 Self::NonNullable
413 })
414 }
415 }
416 }
417
418 #[inline]
429 pub fn trivial_into_non_nullable(self, len: usize) -> VortexResult<Option<Validity>> {
430 match self {
431 _ if len == 0 => Ok(Some(Validity::NonNullable)),
432 Self::NonNullable => Ok(Some(Self::NonNullable)),
433 Self::AllValid => Ok(Some(Self::NonNullable)),
434 Self::AllInvalid => {
435 Err(vortex_err!(InvalidArgument: "Cannot cast AllInvalid to NonNullable"))
436 }
437 Self::Array(_) => Ok(None),
438 }
439 }
440
441 #[inline]
457 pub fn cast_nullability(
458 self,
459 nullability: Nullability,
460 len: usize,
461 ctx: &mut ExecutionCtx,
462 ) -> VortexResult<Validity> {
463 match nullability {
464 Nullability::NonNullable => self.into_non_nullable(len, ctx).ok_or_else(|| {
465 vortex_err!(InvalidArgument: "Cannot cast array with invalid values to non-nullable type.")
466 }),
467 Nullability::Nullable => Ok(self.into_nullable()),
468 }
469 }
470
471 #[inline]
496 pub fn trivially_cast_nullability(
497 self,
498 nullability: Nullability,
499 len: usize,
500 ) -> VortexResult<Option<Validity>> {
501 match nullability {
502 Nullability::NonNullable => self.trivial_into_non_nullable(len),
503 Nullability::Nullable => Ok(Some(self.into_nullable())),
504 }
505 }
506
507 #[inline]
509 pub fn maybe_len(&self) -> Option<usize> {
510 match self {
511 Self::NonNullable | Self::AllValid | Self::AllInvalid => None,
512 Self::Array(a) => Some(a.len()),
513 }
514 }
515}
516
517impl From<BitBuffer> for Validity {
518 #[inline]
519 fn from(value: BitBuffer) -> Self {
520 let true_count = value.true_count();
521 if true_count == value.len() {
522 Self::AllValid
523 } else if true_count == 0 {
524 Self::AllInvalid
525 } else {
526 Self::Array(BoolArray::from(value).into_array())
527 }
528 }
529}
530
531impl FromIterator<Mask> for Validity {
532 #[inline]
533 fn from_iter<T: IntoIterator<Item = Mask>>(iter: T) -> Self {
534 Validity::from_mask(iter.into_iter().collect(), Nullability::Nullable)
535 }
536}
537
538impl FromIterator<bool> for Validity {
539 #[inline]
540 fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
541 Validity::from(BitBuffer::from_iter(iter))
542 }
543}
544
545impl From<Nullability> for Validity {
546 #[inline]
547 fn from(value: Nullability) -> Self {
548 Validity::from(&value)
549 }
550}
551
552impl From<&Nullability> for Validity {
553 #[inline]
554 fn from(value: &Nullability) -> Self {
555 match *value {
556 Nullability::NonNullable => Validity::NonNullable,
557 Nullability::Nullable => Validity::AllValid,
558 }
559 }
560}
561
562impl Validity {
563 pub fn concat(validities: Vec<(Validity, usize)>) -> Option<Self> {
567 let mut validity_kinds = validities
568 .iter()
569 .map(|(v, _)| std::mem::discriminant(v))
570 .unique();
571 let validity_kind = validity_kinds.next()?;
572 if validity_kinds.next().is_none() {
573 if validity_kind == std::mem::discriminant(&Validity::AllValid) {
576 return Some(Validity::AllValid);
577 }
578 if validity_kind == std::mem::discriminant(&Validity::AllInvalid) {
579 return Some(Validity::AllInvalid);
580 }
581 if validity_kind == std::mem::discriminant(&Validity::NonNullable) {
582 return Some(Validity::NonNullable);
583 }
584 }
585
586 Some(Validity::Array(
587 unsafe {
588 ChunkedArray::new_unchecked(
589 validities
590 .into_iter()
591 .map(|(v, len)| v.to_array(len))
592 .collect(),
593 DType::Bool(Nullability::NonNullable),
594 )
595 }
596 .into_array(),
597 ))
598 }
599}
600
601impl Validity {
602 pub fn from_bit_buffer(buffer: BitBuffer, nullability: Nullability) -> Self {
603 if buffer.true_count() == buffer.len() {
604 nullability.into()
605 } else if buffer.true_count() == 0 {
606 Validity::AllInvalid
607 } else {
608 Validity::Array(BoolArray::new(buffer, Validity::NonNullable).into_array())
609 }
610 }
611
612 pub fn from_mask(mask: Mask, nullability: Nullability) -> Self {
613 assert!(
614 nullability == Nullability::Nullable || matches!(mask, Mask::AllTrue(_)),
615 "NonNullable validity must be AllValid",
616 );
617 match mask {
618 Mask::AllTrue(_) => match nullability {
619 Nullability::NonNullable => Validity::NonNullable,
620 Nullability::Nullable => Validity::AllValid,
621 },
622 Mask::AllFalse(_) => Validity::AllInvalid,
623 Mask::Values(values) => Validity::Array(values.into_array()),
624 }
625 }
626}
627
628impl IntoArray for Mask {
629 #[inline]
630 fn into_array(self) -> ArrayRef {
631 match self {
632 Self::AllTrue(len) => ConstantArray::new(true, len).into_array(),
633 Self::AllFalse(len) => ConstantArray::new(false, len).into_array(),
634 Self::Values(a) => a.into_array(),
635 }
636 }
637}
638
639impl IntoArray for &MaskValues {
640 #[inline]
641 fn into_array(self) -> ArrayRef {
642 BoolArray::new(self.bit_buffer().clone(), Validity::NonNullable).into_array()
643 }
644}
645
646#[cfg(test)]
647mod tests {
648 use rstest::rstest;
649 use vortex_buffer::Buffer;
650 use vortex_buffer::buffer;
651 use vortex_mask::Mask;
652
653 use crate::ArrayRef;
654 use crate::IntoArray;
655 use crate::VortexSessionExecute;
656 use crate::array_session;
657 use crate::arrays::PrimitiveArray;
658 use crate::dtype::Nullability;
659 use crate::validity::BoolArray;
660 use crate::validity::Validity;
661
662 #[rstest]
663 #[case(Validity::AllValid, 5, &[2, 4], Validity::AllValid, Validity::AllValid)]
664 #[case(
665 Validity::AllValid,
666 5,
667 &[2, 4],
668 Validity::AllInvalid,
669 Validity::Array(BoolArray::from_iter([true, true, false, true, false]).into_array())
670 )]
671 #[case(
672 Validity::AllValid,
673 5,
674 &[2, 4],
675 Validity::Array(BoolArray::from_iter([true, false]).into_array()),
676 Validity::Array(BoolArray::from_iter([true, true, true, true, false]).into_array())
677 )]
678 #[case(
679 Validity::AllInvalid,
680 5,
681 &[2, 4],
682 Validity::AllValid,
683 Validity::Array(BoolArray::from_iter([false, false, true, false, true]).into_array())
684 )]
685 #[case(Validity::AllInvalid, 5, &[2, 4], Validity::AllInvalid, Validity::AllInvalid)]
686 #[case(
687 Validity::AllInvalid,
688 5,
689 &[2, 4],
690 Validity::Array(BoolArray::from_iter([true, false]).into_array()),
691 Validity::Array(BoolArray::from_iter([false, false, true, false, false]).into_array())
692 )]
693 #[case(
694 Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array()),
695 5,
696 &[2, 4],
697 Validity::AllValid,
698 Validity::Array(BoolArray::from_iter([false, true, true, true, true]).into_array())
699 )]
700 #[case(
701 Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array()),
702 5,
703 &[2, 4],
704 Validity::AllInvalid,
705 Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array())
706 )]
707 #[case(
708 Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array()),
709 5,
710 &[2, 4],
711 Validity::Array(BoolArray::from_iter([true, false]).into_array()),
712 Validity::Array(BoolArray::from_iter([false, true, true, true, false]).into_array())
713 )]
714
715 fn patch_validity(
716 #[case] validity: Validity,
717 #[case] len: usize,
718 #[case] positions: &[u64],
719 #[case] patches: Validity,
720 #[case] expected: Validity,
721 ) {
722 let indices =
723 PrimitiveArray::new(Buffer::copy_from(positions), Validity::NonNullable).into_array();
724
725 let mut ctx = array_session().create_execution_ctx();
726
727 assert!(
728 validity
729 .patch(len, 0, &indices, &patches, &mut ctx,)
730 .unwrap()
731 .mask_eq(&expected, len, &mut ctx)
732 .unwrap()
733 );
734 }
735
736 #[test]
737 #[should_panic]
738 fn out_of_bounds_patch() {
739 let mut ctx = array_session().create_execution_ctx();
740 Validity::NonNullable
741 .patch(
742 2,
743 0,
744 &buffer![4].into_array(),
745 &Validity::AllInvalid,
746 &mut ctx,
747 )
748 .unwrap();
749 }
750
751 #[test]
752 #[should_panic]
753 fn into_validity_nullable() {
754 Validity::from_mask(Mask::AllFalse(10), Nullability::NonNullable);
755 }
756
757 #[test]
758 #[should_panic]
759 fn into_validity_nullable_array() {
760 Validity::from_mask(Mask::from_iter(vec![true, false]), Nullability::NonNullable);
761 }
762
763 #[rstest]
764 #[case(
765 Validity::AllValid,
766 PrimitiveArray::new(buffer![0, 1], Validity::from_iter(vec![true, false])).into_array(),
767 Validity::from_iter(vec![true, false])
768 )]
769 #[case(Validity::AllValid, buffer![0, 1].into_array(), Validity::AllValid)]
770 #[case(
771 Validity::AllValid,
772 PrimitiveArray::new(buffer![0, 1], Validity::AllInvalid).into_array(),
773 Validity::AllInvalid
774 )]
775 #[case(
776 Validity::NonNullable,
777 PrimitiveArray::new(buffer![0, 1], Validity::from_iter(vec![true, false])).into_array(),
778 Validity::from_iter(vec![true, false])
779 )]
780 #[case(Validity::NonNullable, buffer![0, 1].into_array(), Validity::NonNullable)]
781 #[case(
782 Validity::NonNullable,
783 PrimitiveArray::new(buffer![0, 1], Validity::AllInvalid).into_array(),
784 Validity::AllInvalid
785 )]
786 fn validity_take(
787 #[case] validity: Validity,
788 #[case] indices: ArrayRef,
789 #[case] expected: Validity,
790 ) {
791 let mut ctx = array_session().create_execution_ctx();
792 assert!(
793 validity
794 .take(&indices)
795 .unwrap()
796 .mask_eq(&expected, indices.len(), &mut ctx)
797 .unwrap()
798 );
799 }
800
801 #[rstest]
802 #[case(Validity::NonNullable, Validity::AllValid, true)]
804 #[case(Validity::AllValid, Validity::NonNullable, true)]
805 #[case(Validity::AllValid, Validity::AllInvalid, false)]
806 #[case(Validity::NonNullable, Validity::AllInvalid, false)]
807 #[case(
809 Validity::Array(BoolArray::from_iter([true, true, true]).into_array()),
810 Validity::AllValid,
811 true
812 )]
813 #[case(
814 Validity::NonNullable,
815 Validity::Array(BoolArray::from_iter([true, true, true]).into_array()),
816 true
817 )]
818 #[case(
819 Validity::Array(BoolArray::from_iter([false, false, false]).into_array()),
820 Validity::AllInvalid,
821 true
822 )]
823 #[case(
824 Validity::Array(BoolArray::from_iter([true, false, true]).into_array()),
825 Validity::AllValid,
826 false
827 )]
828 #[case(
829 Validity::Array(BoolArray::from_iter([true, false, true]).into_array()),
830 Validity::AllInvalid,
831 false
832 )]
833 fn mask_eq_mixed_variants(
834 #[case] lhs: Validity,
835 #[case] rhs: Validity,
836 #[case] expected: bool,
837 ) -> vortex_error::VortexResult<()> {
838 let mut ctx = array_session().create_execution_ctx();
839 assert_eq!(lhs.mask_eq(&rhs, 3, &mut ctx)?, expected);
840 Ok(())
841 }
842}