arrow_array/builder/
generic_bytes_dictionary_builder.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::builder::{ArrayBuilder, GenericByteBuilder, PrimitiveBuilder};
19use crate::types::{ArrowDictionaryKeyType, ByteArrayType, GenericBinaryType, GenericStringType};
20use crate::{Array, ArrayRef, DictionaryArray, GenericByteArray};
21use arrow_buffer::ArrowNativeType;
22use arrow_schema::{ArrowError, DataType};
23use hashbrown::HashTable;
24use std::any::Any;
25use std::sync::Arc;
26
27/// Builder for [`DictionaryArray`] of [`GenericByteArray`]
28///
29/// For example to map a set of byte indices to String values. Note that
30/// the use of a `HashMap` here will not scale to very large arrays or
31/// result in an ordered dictionary.
32#[derive(Debug)]
33pub struct GenericByteDictionaryBuilder<K, T>
34where
35    K: ArrowDictionaryKeyType,
36    T: ByteArrayType,
37{
38    state: ahash::RandomState,
39    dedup: HashTable<usize>,
40
41    keys_builder: PrimitiveBuilder<K>,
42    values_builder: GenericByteBuilder<T>,
43}
44
45impl<K, T> Default for GenericByteDictionaryBuilder<K, T>
46where
47    K: ArrowDictionaryKeyType,
48    T: ByteArrayType,
49{
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55impl<K, T> GenericByteDictionaryBuilder<K, T>
56where
57    K: ArrowDictionaryKeyType,
58    T: ByteArrayType,
59{
60    /// Creates a new `GenericByteDictionaryBuilder`
61    pub fn new() -> Self {
62        let keys_builder = PrimitiveBuilder::new();
63        let values_builder = GenericByteBuilder::<T>::new();
64        Self {
65            state: Default::default(),
66            dedup: HashTable::with_capacity(keys_builder.capacity()),
67            keys_builder,
68            values_builder,
69        }
70    }
71
72    /// Creates a new `GenericByteDictionaryBuilder` with the provided capacities
73    ///
74    /// `keys_capacity`: the number of keys, i.e. length of array to build
75    /// `value_capacity`: the number of distinct dictionary values, i.e. size of dictionary
76    /// `data_capacity`: the total number of bytes of all distinct bytes in the dictionary
77    pub fn with_capacity(
78        keys_capacity: usize,
79        value_capacity: usize,
80        data_capacity: usize,
81    ) -> Self {
82        Self {
83            state: Default::default(),
84            dedup: Default::default(),
85            keys_builder: PrimitiveBuilder::with_capacity(keys_capacity),
86            values_builder: GenericByteBuilder::<T>::with_capacity(value_capacity, data_capacity),
87        }
88    }
89
90    /// Creates a new `GenericByteDictionaryBuilder` from a keys capacity and a dictionary
91    /// which is initialized with the given values.
92    /// The indices of those dictionary values are used as keys.
93    ///
94    /// # Example
95    ///
96    /// ```
97    /// # use arrow_array::builder::StringDictionaryBuilder;
98    /// # use arrow_array::{Int16Array, StringArray};
99    ///
100    /// let dictionary_values = StringArray::from(vec![None, Some("abc"), Some("def")]);
101    ///
102    /// let mut builder = StringDictionaryBuilder::new_with_dictionary(3, &dictionary_values).unwrap();
103    /// builder.append("def").unwrap();
104    /// builder.append_null();
105    /// builder.append("abc").unwrap();
106    ///
107    /// let dictionary_array = builder.finish();
108    ///
109    /// let keys = dictionary_array.keys();
110    ///
111    /// assert_eq!(keys, &Int16Array::from(vec![Some(2), None, Some(1)]));
112    /// ```
113    pub fn new_with_dictionary(
114        keys_capacity: usize,
115        dictionary_values: &GenericByteArray<T>,
116    ) -> Result<Self, ArrowError> {
117        let state = ahash::RandomState::default();
118        let dict_len = dictionary_values.len();
119
120        let mut dedup = HashTable::with_capacity(dict_len);
121
122        let values_len = dictionary_values.value_data().len();
123        let mut values_builder = GenericByteBuilder::<T>::with_capacity(dict_len, values_len);
124
125        K::Native::from_usize(dictionary_values.len())
126            .ok_or(ArrowError::DictionaryKeyOverflowError)?;
127
128        for (idx, maybe_value) in dictionary_values.iter().enumerate() {
129            match maybe_value {
130                Some(value) => {
131                    let value_bytes: &[u8] = value.as_ref();
132                    let hash = state.hash_one(value_bytes);
133
134                    dedup
135                        .entry(
136                            hash,
137                            |idx: &usize| value_bytes == get_bytes(&values_builder, *idx),
138                            |idx: &usize| state.hash_one(get_bytes(&values_builder, *idx)),
139                        )
140                        .or_insert(idx);
141
142                    values_builder.append_value(value);
143                }
144                None => values_builder.append_null(),
145            }
146        }
147
148        Ok(Self {
149            state,
150            dedup,
151            keys_builder: PrimitiveBuilder::with_capacity(keys_capacity),
152            values_builder,
153        })
154    }
155}
156
157impl<K, T> ArrayBuilder for GenericByteDictionaryBuilder<K, T>
158where
159    K: ArrowDictionaryKeyType,
160    T: ByteArrayType,
161{
162    /// Returns the builder as an non-mutable `Any` reference.
163    fn as_any(&self) -> &dyn Any {
164        self
165    }
166
167    /// Returns the builder as an mutable `Any` reference.
168    fn as_any_mut(&mut self) -> &mut dyn Any {
169        self
170    }
171
172    /// Returns the boxed builder as a box of `Any`.
173    fn into_box_any(self: Box<Self>) -> Box<dyn Any> {
174        self
175    }
176
177    /// Returns the number of array slots in the builder
178    fn len(&self) -> usize {
179        self.keys_builder.len()
180    }
181
182    /// Builds the array and reset this builder.
183    fn finish(&mut self) -> ArrayRef {
184        Arc::new(self.finish())
185    }
186
187    /// Builds the array without resetting the builder.
188    fn finish_cloned(&self) -> ArrayRef {
189        Arc::new(self.finish_cloned())
190    }
191}
192
193impl<K, T> GenericByteDictionaryBuilder<K, T>
194where
195    K: ArrowDictionaryKeyType,
196    T: ByteArrayType,
197{
198    fn get_or_insert_key(&mut self, value: impl AsRef<T::Native>) -> Result<K::Native, ArrowError> {
199        let value_native: &T::Native = value.as_ref();
200        let value_bytes: &[u8] = value_native.as_ref();
201
202        let state = &self.state;
203        let storage = &mut self.values_builder;
204        let hash = state.hash_one(value_bytes);
205
206        let idx = *self
207            .dedup
208            .entry(
209                hash,
210                |idx| value_bytes == get_bytes(storage, *idx),
211                |idx| state.hash_one(get_bytes(storage, *idx)),
212            )
213            .or_insert_with(|| {
214                let idx = storage.len();
215                storage.append_value(value);
216                idx
217            })
218            .get();
219
220        let key = K::Native::from_usize(idx).ok_or(ArrowError::DictionaryKeyOverflowError)?;
221
222        Ok(key)
223    }
224
225    /// Append a value to the array. Return an existing index
226    /// if already present in the values array or a new index if the
227    /// value is appended to the values array.
228    ///
229    /// Returns an error if the new index would overflow the key type.
230    pub fn append(&mut self, value: impl AsRef<T::Native>) -> Result<K::Native, ArrowError> {
231        let key = self.get_or_insert_key(value)?;
232        self.keys_builder.append_value(key);
233        Ok(key)
234    }
235
236    /// Append a value multiple times to the array.
237    /// This is the same as `append` but allows to append the same value multiple times without doing multiple lookups.
238    ///
239    /// Returns an error if the new index would overflow the key type.
240    pub fn append_n(
241        &mut self,
242        value: impl AsRef<T::Native>,
243        count: usize,
244    ) -> Result<K::Native, ArrowError> {
245        let key = self.get_or_insert_key(value)?;
246        self.keys_builder.append_value_n(key, count);
247        Ok(key)
248    }
249
250    /// Infallibly append a value to this builder
251    ///
252    /// # Panics
253    ///
254    /// Panics if the resulting length of the dictionary values array would exceed `T::Native::MAX`
255    pub fn append_value(&mut self, value: impl AsRef<T::Native>) {
256        self.append(value).expect("dictionary key overflow");
257    }
258
259    /// Infallibly append a value to this builder repeatedly `count` times.
260    /// This is the same as `append_value` but allows to append the same value multiple times without doing multiple lookups.
261    ///
262    /// # Panics
263    ///
264    /// Panics if the resulting length of the dictionary values array would exceed `T::Native::MAX`
265    pub fn append_values(&mut self, value: impl AsRef<T::Native>, count: usize) {
266        self.append_n(value, count)
267            .expect("dictionary key overflow");
268    }
269
270    /// Appends a null slot into the builder
271    #[inline]
272    pub fn append_null(&mut self) {
273        self.keys_builder.append_null()
274    }
275
276    /// Infallibly append `n` null slots into the builder
277    #[inline]
278    pub fn append_nulls(&mut self, n: usize) {
279        self.keys_builder.append_nulls(n)
280    }
281
282    /// Append an `Option` value into the builder
283    ///
284    /// # Panics
285    ///
286    /// Panics if the resulting length of the dictionary values array would exceed `T::Native::MAX`
287    #[inline]
288    pub fn append_option(&mut self, value: Option<impl AsRef<T::Native>>) {
289        match value {
290            None => self.append_null(),
291            Some(v) => self.append_value(v),
292        };
293    }
294
295    /// Append an `Option` value into the builder repeatedly `count` times.
296    /// This is the same as `append_option` but allows to append the same value multiple times without doing multiple lookups.
297    ///
298    /// # Panics
299    ///
300    /// Panics if the resulting length of the dictionary values array would exceed `T::Native::MAX`
301    pub fn append_options(&mut self, value: Option<impl AsRef<T::Native>>, count: usize) {
302        match value {
303            None => self.keys_builder.append_nulls(count),
304            Some(v) => self.append_values(v, count),
305        };
306    }
307
308    /// Builds the `DictionaryArray` and reset this builder.
309    pub fn finish(&mut self) -> DictionaryArray<K> {
310        self.dedup.clear();
311        let values = self.values_builder.finish();
312        let keys = self.keys_builder.finish();
313
314        let data_type = DataType::Dictionary(Box::new(K::DATA_TYPE), Box::new(T::DATA_TYPE));
315
316        let builder = keys
317            .into_data()
318            .into_builder()
319            .data_type(data_type)
320            .child_data(vec![values.into_data()]);
321
322        DictionaryArray::from(unsafe { builder.build_unchecked() })
323    }
324
325    /// Builds the `DictionaryArray` without resetting the builder.
326    pub fn finish_cloned(&self) -> DictionaryArray<K> {
327        let values = self.values_builder.finish_cloned();
328        let keys = self.keys_builder.finish_cloned();
329
330        let data_type = DataType::Dictionary(Box::new(K::DATA_TYPE), Box::new(T::DATA_TYPE));
331
332        let builder = keys
333            .into_data()
334            .into_builder()
335            .data_type(data_type)
336            .child_data(vec![values.into_data()]);
337
338        DictionaryArray::from(unsafe { builder.build_unchecked() })
339    }
340
341    /// Returns the current null buffer as a slice
342    pub fn validity_slice(&self) -> Option<&[u8]> {
343        self.keys_builder.validity_slice()
344    }
345}
346
347impl<K: ArrowDictionaryKeyType, T: ByteArrayType, V: AsRef<T::Native>> Extend<Option<V>>
348    for GenericByteDictionaryBuilder<K, T>
349{
350    #[inline]
351    fn extend<I: IntoIterator<Item = Option<V>>>(&mut self, iter: I) {
352        for v in iter {
353            self.append_option(v)
354        }
355    }
356}
357
358fn get_bytes<T: ByteArrayType>(values: &GenericByteBuilder<T>, idx: usize) -> &[u8] {
359    let offsets = values.offsets_slice();
360    let values = values.values_slice();
361
362    let end_offset = offsets[idx + 1].as_usize();
363    let start_offset = offsets[idx].as_usize();
364
365    &values[start_offset..end_offset]
366}
367
368/// Builder for [`DictionaryArray`] of [`StringArray`](crate::array::StringArray)
369///
370/// ```
371/// // Create a dictionary array indexed by bytes whose values are Strings.
372/// // It can thus hold up to 256 distinct string values.
373///
374/// # use arrow_array::builder::StringDictionaryBuilder;
375/// # use arrow_array::{Int8Array, StringArray};
376/// # use arrow_array::types::Int8Type;
377///
378/// let mut builder = StringDictionaryBuilder::<Int8Type>::new();
379///
380/// // The builder builds the dictionary value by value
381/// builder.append("abc").unwrap();
382/// builder.append_null();
383/// builder.append_n("def", 2).unwrap();  // appends "def" twice with a single lookup
384/// builder.append("abc").unwrap();
385/// let array = builder.finish();
386///
387/// assert_eq!(
388///   array.keys(),
389///   &Int8Array::from(vec![Some(0), None, Some(1), Some(1), Some(0)])
390/// );
391///
392/// // Values are polymorphic and so require a downcast.
393/// let av = array.values();
394/// let ava: &StringArray = av.as_any().downcast_ref::<StringArray>().unwrap();
395///
396/// assert_eq!(ava.value(0), "abc");
397/// assert_eq!(ava.value(1), "def");
398///
399/// ```
400pub type StringDictionaryBuilder<K> = GenericByteDictionaryBuilder<K, GenericStringType<i32>>;
401
402/// Builder for [`DictionaryArray`] of [`LargeStringArray`](crate::array::LargeStringArray)
403pub type LargeStringDictionaryBuilder<K> = GenericByteDictionaryBuilder<K, GenericStringType<i64>>;
404
405/// Builder for [`DictionaryArray`] of [`BinaryArray`](crate::array::BinaryArray)
406///
407/// ```
408/// // Create a dictionary array indexed by bytes whose values are binary.
409/// // It can thus hold up to 256 distinct binary values.
410///
411/// # use arrow_array::builder::BinaryDictionaryBuilder;
412/// # use arrow_array::{BinaryArray, Int8Array};
413/// # use arrow_array::types::Int8Type;
414///
415/// let mut builder = BinaryDictionaryBuilder::<Int8Type>::new();
416///
417/// // The builder builds the dictionary value by value
418/// builder.append(b"abc").unwrap();
419/// builder.append_null();
420/// builder.append(b"def").unwrap();
421/// builder.append(b"def").unwrap();
422/// builder.append(b"abc").unwrap();
423/// let array = builder.finish();
424///
425/// assert_eq!(
426///   array.keys(),
427///   &Int8Array::from(vec![Some(0), None, Some(1), Some(1), Some(0)])
428/// );
429///
430/// // Values are polymorphic and so require a downcast.
431/// let av = array.values();
432/// let ava: &BinaryArray = av.as_any().downcast_ref::<BinaryArray>().unwrap();
433///
434/// assert_eq!(ava.value(0), b"abc");
435/// assert_eq!(ava.value(1), b"def");
436///
437/// ```
438pub type BinaryDictionaryBuilder<K> = GenericByteDictionaryBuilder<K, GenericBinaryType<i32>>;
439
440/// Builder for [`DictionaryArray`] of [`LargeBinaryArray`](crate::array::LargeBinaryArray)
441pub type LargeBinaryDictionaryBuilder<K> = GenericByteDictionaryBuilder<K, GenericBinaryType<i64>>;
442
443#[cfg(test)]
444mod tests {
445    use super::*;
446
447    use crate::array::Int8Array;
448    use crate::types::{Int16Type, Int32Type, Int8Type, Utf8Type};
449    use crate::{BinaryArray, StringArray};
450
451    fn test_bytes_dictionary_builder<T>(values: Vec<&T::Native>)
452    where
453        T: ByteArrayType,
454        <T as ByteArrayType>::Native: PartialEq,
455        <T as ByteArrayType>::Native: AsRef<<T as ByteArrayType>::Native>,
456    {
457        let mut builder = GenericByteDictionaryBuilder::<Int8Type, T>::new();
458        builder.append(values[0]).unwrap();
459        builder.append_null();
460        builder.append(values[1]).unwrap();
461        builder.append(values[1]).unwrap();
462        builder.append(values[0]).unwrap();
463        let array = builder.finish();
464
465        assert_eq!(
466            array.keys(),
467            &Int8Array::from(vec![Some(0), None, Some(1), Some(1), Some(0)])
468        );
469
470        // Values are polymorphic and so require a downcast.
471        let av = array.values();
472        let ava: &GenericByteArray<T> = av.as_any().downcast_ref::<GenericByteArray<T>>().unwrap();
473
474        assert_eq!(*ava.value(0), *values[0]);
475        assert_eq!(*ava.value(1), *values[1]);
476    }
477
478    #[test]
479    fn test_string_dictionary_builder() {
480        test_bytes_dictionary_builder::<GenericStringType<i32>>(vec!["abc", "def"]);
481    }
482
483    #[test]
484    fn test_binary_dictionary_builder() {
485        test_bytes_dictionary_builder::<GenericBinaryType<i32>>(vec![b"abc", b"def"]);
486    }
487
488    fn test_bytes_dictionary_builder_finish_cloned<T>(values: Vec<&T::Native>)
489    where
490        T: ByteArrayType,
491        <T as ByteArrayType>::Native: PartialEq,
492        <T as ByteArrayType>::Native: AsRef<<T as ByteArrayType>::Native>,
493    {
494        let mut builder = GenericByteDictionaryBuilder::<Int8Type, T>::new();
495
496        builder.append(values[0]).unwrap();
497        builder.append_null();
498        builder.append(values[1]).unwrap();
499        builder.append(values[1]).unwrap();
500        builder.append(values[0]).unwrap();
501        let mut array = builder.finish_cloned();
502
503        assert_eq!(
504            array.keys(),
505            &Int8Array::from(vec![Some(0), None, Some(1), Some(1), Some(0)])
506        );
507
508        // Values are polymorphic and so require a downcast.
509        let av = array.values();
510        let ava: &GenericByteArray<T> = av.as_any().downcast_ref::<GenericByteArray<T>>().unwrap();
511
512        assert_eq!(ava.value(0), values[0]);
513        assert_eq!(ava.value(1), values[1]);
514
515        builder.append(values[0]).unwrap();
516        builder.append(values[2]).unwrap();
517        builder.append(values[1]).unwrap();
518
519        array = builder.finish();
520
521        assert_eq!(
522            array.keys(),
523            &Int8Array::from(vec![
524                Some(0),
525                None,
526                Some(1),
527                Some(1),
528                Some(0),
529                Some(0),
530                Some(2),
531                Some(1)
532            ])
533        );
534
535        // Values are polymorphic and so require a downcast.
536        let av2 = array.values();
537        let ava2: &GenericByteArray<T> =
538            av2.as_any().downcast_ref::<GenericByteArray<T>>().unwrap();
539
540        assert_eq!(ava2.value(0), values[0]);
541        assert_eq!(ava2.value(1), values[1]);
542        assert_eq!(ava2.value(2), values[2]);
543    }
544
545    #[test]
546    fn test_string_dictionary_builder_finish_cloned() {
547        test_bytes_dictionary_builder_finish_cloned::<GenericStringType<i32>>(vec![
548            "abc", "def", "ghi",
549        ]);
550    }
551
552    #[test]
553    fn test_binary_dictionary_builder_finish_cloned() {
554        test_bytes_dictionary_builder_finish_cloned::<GenericBinaryType<i32>>(vec![
555            b"abc", b"def", b"ghi",
556        ]);
557    }
558
559    fn test_bytes_dictionary_builder_with_existing_dictionary<T>(
560        dictionary: GenericByteArray<T>,
561        values: Vec<&T::Native>,
562    ) where
563        T: ByteArrayType,
564        <T as ByteArrayType>::Native: PartialEq,
565        <T as ByteArrayType>::Native: AsRef<<T as ByteArrayType>::Native>,
566    {
567        let mut builder =
568            GenericByteDictionaryBuilder::<Int8Type, T>::new_with_dictionary(6, &dictionary)
569                .unwrap();
570        builder.append(values[0]).unwrap();
571        builder.append_null();
572        builder.append(values[1]).unwrap();
573        builder.append(values[1]).unwrap();
574        builder.append(values[0]).unwrap();
575        builder.append(values[2]).unwrap();
576        let array = builder.finish();
577
578        assert_eq!(
579            array.keys(),
580            &Int8Array::from(vec![Some(2), None, Some(1), Some(1), Some(2), Some(3)])
581        );
582
583        // Values are polymorphic and so require a downcast.
584        let av = array.values();
585        let ava: &GenericByteArray<T> = av.as_any().downcast_ref::<GenericByteArray<T>>().unwrap();
586
587        assert!(!ava.is_valid(0));
588        assert_eq!(ava.value(1), values[1]);
589        assert_eq!(ava.value(2), values[0]);
590        assert_eq!(ava.value(3), values[2]);
591    }
592
593    #[test]
594    fn test_string_dictionary_builder_with_existing_dictionary() {
595        test_bytes_dictionary_builder_with_existing_dictionary::<GenericStringType<i32>>(
596            StringArray::from(vec![None, Some("def"), Some("abc")]),
597            vec!["abc", "def", "ghi"],
598        );
599    }
600
601    #[test]
602    fn test_binary_dictionary_builder_with_existing_dictionary() {
603        let values: Vec<Option<&[u8]>> = vec![None, Some(b"def"), Some(b"abc")];
604        test_bytes_dictionary_builder_with_existing_dictionary::<GenericBinaryType<i32>>(
605            BinaryArray::from(values),
606            vec![b"abc", b"def", b"ghi"],
607        );
608    }
609
610    fn test_bytes_dictionary_builder_with_reserved_null_value<T>(
611        dictionary: GenericByteArray<T>,
612        values: Vec<&T::Native>,
613    ) where
614        T: ByteArrayType,
615        <T as ByteArrayType>::Native: PartialEq,
616        <T as ByteArrayType>::Native: AsRef<<T as ByteArrayType>::Native>,
617    {
618        let mut builder =
619            GenericByteDictionaryBuilder::<Int16Type, T>::new_with_dictionary(4, &dictionary)
620                .unwrap();
621        builder.append(values[0]).unwrap();
622        builder.append_null();
623        builder.append(values[1]).unwrap();
624        builder.append(values[0]).unwrap();
625        let array = builder.finish();
626
627        assert!(array.is_null(1));
628        assert!(!array.is_valid(1));
629
630        let keys = array.keys();
631
632        assert_eq!(keys.value(0), 1);
633        assert!(keys.is_null(1));
634        // zero initialization is currently guaranteed by Buffer allocation and resizing
635        assert_eq!(keys.value(1), 0);
636        assert_eq!(keys.value(2), 2);
637        assert_eq!(keys.value(3), 1);
638    }
639
640    #[test]
641    fn test_string_dictionary_builder_with_reserved_null_value() {
642        let v: Vec<Option<&str>> = vec![None];
643        test_bytes_dictionary_builder_with_reserved_null_value::<GenericStringType<i32>>(
644            StringArray::from(v),
645            vec!["abc", "def"],
646        );
647    }
648
649    #[test]
650    fn test_binary_dictionary_builder_with_reserved_null_value() {
651        let values: Vec<Option<&[u8]>> = vec![None];
652        test_bytes_dictionary_builder_with_reserved_null_value::<GenericBinaryType<i32>>(
653            BinaryArray::from(values),
654            vec![b"abc", b"def"],
655        );
656    }
657
658    #[test]
659    fn test_extend() {
660        let mut builder = GenericByteDictionaryBuilder::<Int32Type, Utf8Type>::new();
661        builder.extend(["a", "b", "c", "a", "b", "c"].into_iter().map(Some));
662        builder.extend(["c", "d", "a"].into_iter().map(Some));
663        let dict = builder.finish();
664        assert_eq!(dict.keys().values(), &[0, 1, 2, 0, 1, 2, 2, 3, 0]);
665        assert_eq!(dict.values().len(), 4);
666    }
667}