1use std::fmt::{Display, Formatter, Result as FmtResult};
2
3use nalgebra::DMatrix;
4use polars::{datatypes::DataType, frame::DataFrame};
5
6use crate::display_table::DisplayTable;
7
8#[derive(Clone, Debug)]
9pub struct ModelMatrix {
10 data: ModelMatrixData,
11 schema: Vec<ModelMatrixSchema>,
12}
13
14#[derive(Clone, Debug)]
15pub enum ModelMatrixData {
16 Vec {
17 name: String,
18 data: Vec<f64>,
19 },
20 Matrix {
21 names: Vec<String>,
22 data: DMatrix<f64>,
23 },
24 DataFrame(DataFrame),
25}
26
27#[derive(Copy, Clone, Debug, PartialEq, Eq)]
28pub enum ModelMatrixSchema {
29 Level,
30 Factor,
31 Value,
32 RowName,
33}
34
35fn write_table_bars(
36 column_widths: &[usize],
37 formatter: &mut Formatter,
38 left_bar: &str,
39 middle_blank: &str,
40 middle_bar: &str,
41 right_bar: &str,
42) -> FmtResult {
43 write!(formatter, "{left_bar}")?;
44 for (index, &len) in column_widths.iter().enumerate() {
45 if len > 0 {
46 for _ in 0..len + 2 {
47 write!(formatter, "{middle_blank}")?;
48 }
49 if index != column_widths.len() - 1 {
50 write!(formatter, "{middle_bar}")?;
51 }
52 }
53 }
54 writeln!(formatter, "{right_bar}")?;
55
56 Ok(())
57}
58
59fn write_data(
60 column_widths: &[usize],
61 formatter: &mut Formatter,
62 bar: &str,
63 row_names: &[String],
64 data: &Vec<Vec<String>>,
65) -> FmtResult {
66 for (row_index, row) in data.into_iter().enumerate() {
67 for (column_index, value) in row.iter().enumerate() {
68 write!(formatter, "{bar} ")?;
69 let column_index = if !row_names.is_empty() && column_index == 0 {
70 row_names[row_index].fmt(formatter)?;
71 for _ in 0..column_widths[column_index] - row_names[row_index].len() {
72 write!(formatter, " ")?;
73 }
74 write!(formatter, " {bar} ")?;
75 value.fmt(formatter)?;
76 column_index + 1
77 } else {
78 value.fmt(formatter)?;
79 if !row_names.is_empty() {
80 column_index + 1
81 } else {
82 column_index
83 }
84 };
85 if column_widths[column_index] > value.to_string().len() {
86 for _ in 0..column_widths[column_index] - value.to_string().len() {
87 write!(formatter, " ")?;
88 }
89 }
90 write!(formatter, " ")?;
91 }
92 writeln!(formatter, "{bar}")?;
93 }
94
95 Ok(())
96}
97
98fn as_factor<T: PartialOrd + PartialEq + ToString + Clone + Display>(
99 name: &str,
100 v: &[T],
101) -> (Vec<String>, DMatrix<f64>) {
102 let mut ret_names = Vec::new();
103 let mut ret_matrix = DMatrix::<f64>::zeros(v.len(), 0);
104
105 let mut unique_c = v.to_vec();
106 unique_c.sort_by(|u_c1, u_c2| u_c1.partial_cmp(u_c2).unwrap());
107 unique_c = unique_c.into_iter().fold(Vec::new(), |mut acc, v| {
108 if let Some(a_last) = acc.last() {
109 if a_last == &v {
110 acc
111 } else {
112 acc.push(v);
113 acc
114 }
115 } else {
116 acc.push(v);
117 acc
118 }
119 });
120 unique_c.sort_by(|a, b| a.partial_cmp(b).unwrap());
121
122 for j in 0..unique_c.len() {
123 ret_matrix = ret_matrix.insert_column(j, 0.0);
124 ret_names.push(format!("{} ({})", name, unique_c[j]));
125 }
126
127 for (j, c_j) in v.iter().enumerate() {
128 let column_index = unique_c
129 .binary_search_by(|a| a.partial_cmp(c_j).unwrap())
130 .unwrap();
131 ret_matrix[(j, column_index)] = 1.0;
132 }
133
134 (ret_names, ret_matrix)
135}
136
137fn as_level<T: PartialOrd + PartialEq + ToString + Clone>(v: &[T]) -> DMatrix<f64> {
138 let mut ret = DMatrix::<f64>::zeros(v.len(), 1);
139
140 let mut unique_c = v.to_vec();
141 unique_c.sort_by(|u_c1, u_c2| u_c1.partial_cmp(u_c2).unwrap());
142 unique_c = unique_c.into_iter().fold(Vec::new(), |mut acc, v| {
143 if let Some(a_last) = acc.last() {
144 if a_last == &v {
145 acc
146 } else {
147 acc.push(v);
148 acc
149 }
150 } else {
151 acc.push(v);
152 acc
153 }
154 });
155 unique_c.sort_by(|a, b| a.partial_cmp(b).unwrap());
156
157 for (i, c_t) in v.iter().enumerate() {
158 let index = unique_c
159 .binary_search_by(|u_c| u_c.partial_cmp(c_t).unwrap())
160 .unwrap();
161 ret[(i, 0)] = index as f64 + 1.0;
162 }
163
164 ret
165}
166
167impl ModelMatrix {
168 pub fn matrix(&self) -> DMatrix<f64> {
169 match &self.data {
170 ModelMatrixData::Vec { data, .. } => match self.schema[0] {
171 ModelMatrixSchema::RowName => DMatrix::<f64>::zeros(data.len(), 0),
172 ModelMatrixSchema::Level => as_level(data),
173 ModelMatrixSchema::Factor => as_factor("", data).1,
174 ModelMatrixSchema::Value => DMatrix::<f64>::from_column_slice(data.len(), 1, data),
175 },
176 ModelMatrixData::Matrix { data, .. } => {
177 let mut matrix = DMatrix::<f64>::zeros(data.nrows(), 0);
178
179 let schemas = &self.schema;
180
181 for i in 0..data.ncols() {
182 let column = data.column(i);
183 let schema = &schemas[i];
184
185 match schema {
186 ModelMatrixSchema::RowName => {}
187 ModelMatrixSchema::Level => {
188 let mut ncols = matrix.ncols();
189 matrix = matrix.insert_column(ncols, 0.0);
190 let x = column.as_slice();
191 for (j, &v) in as_level(x).iter().enumerate() {
192 ncols = matrix.ncols();
193 matrix[(j, ncols - 1)] = v;
194 }
195 }
196 ModelMatrixSchema::Factor => {
197 let x = column.as_slice();
198 let factor_matrix = as_factor("", x).1;
199 for ii in 0..factor_matrix.ncols() {
200 let mut ncols = matrix.ncols();
201 matrix = matrix.insert_column(ncols, 0.0);
202 for j in 0..factor_matrix.nrows() {
203 ncols = matrix.ncols();
204 matrix[(j, ncols - 1)] = factor_matrix[(j, ii)];
205 }
206 }
207 }
208 ModelMatrixSchema::Value => {
209 let mut ncols = matrix.ncols();
210 matrix = matrix.insert_column(ncols, 0.0);
211 for (j, &v) in column.iter().enumerate() {
212 ncols = matrix.ncols();
213 matrix[(j, ncols - 1)] = v;
214 }
215 }
216 }
217 }
218
219 matrix
220 }
221 ModelMatrixData::DataFrame(df) => {
222 let mut matrix = DMatrix::<f64>::zeros(df.height(), 0);
223
224 let data_names = df.get_column_names();
225 let data_types = df.dtypes();
226 let schemas = &self.schema;
227
228 for i in 0..df.width() {
229 let column = &df[data_names[i]];
230 let data_type = &data_types[i];
231 let schema = &schemas[i];
232
233 match schema {
234 ModelMatrixSchema::RowName => {}
235 ModelMatrixSchema::Level => match data_type {
236 DataType::Boolean => {
237 let mut c = Vec::new();
238 column
239 .bool()
240 .unwrap()
241 .for_each(|c_opt| c.push(c_opt.unwrap()));
242 let mat = as_level(&c);
243
244 let column_index_start = matrix.ncols();
245 for column_number in 0..mat.ncols() {
246 let ncols = matrix.ncols();
247 matrix = matrix.insert_column(ncols, 0.0);
248 for j in 0..mat.nrows() {
249 matrix[(j, column_number + column_index_start)] =
250 mat[(j, column_number)];
251 }
252 }
253 }
254 DataType::UInt8
255 | DataType::UInt16
256 | DataType::UInt32
257 | DataType::UInt64
258 | DataType::Int8
259 | DataType::Int16
260 | DataType::Int32
261 | DataType::Int64
262 | DataType::Float32
263 | DataType::Float64 => {
264 let mut c = Vec::new();
265 column
266 .cast(&DataType::Float64)
267 .unwrap()
268 .f64()
269 .unwrap()
270 .for_each(|c_opt| c.push(c_opt.unwrap()));
271 let mat = as_level(&c);
272
273 let column_index_start = matrix.ncols();
274 for column_number in 0..mat.ncols() {
275 let ncols = matrix.ncols();
276 matrix = matrix.insert_column(ncols, 0.0);
277 for j in 0..mat.nrows() {
278 matrix[(j, column_number + column_index_start)] =
279 mat[(j, column_number)];
280 }
281 }
282 }
283 DataType::String => {
284 let mut c = Vec::new();
285 column
286 .str()
287 .unwrap()
288 .for_each(|c_opt| c.push(c_opt.unwrap()));
289 let mat = as_level(&c);
290
291 let column_index_start = matrix.ncols();
292 for column_number in 0..mat.ncols() {
293 let ncols = matrix.ncols();
294 matrix = matrix.insert_column(ncols, 0.0);
295 for j in 0..mat.nrows() {
296 matrix[(j, column_number + column_index_start)] =
297 mat[(j, column_number)];
298 }
299 }
300 }
301 DataType::Binary | DataType::BinaryOffset => {}
302 DataType::Date
303 | DataType::Datetime(_, _)
304 | DataType::Duration(_)
305 | DataType::Time => {}
306 DataType::List(_) | DataType::Null | DataType::Unknown => {}
307 },
308 ModelMatrixSchema::Factor => match data_type {
309 DataType::Boolean => {
310 let mut c = Vec::new();
311 column
312 .bool()
313 .unwrap()
314 .for_each(|c_opt| c.push(c_opt.unwrap()));
315 let (_, mat) = as_factor("", &c);
316
317 let column_index_start = matrix.ncols();
318 for column_number in 0..mat.ncols() {
319 let ncols = matrix.ncols();
320 matrix = matrix.insert_column(ncols, 0.0);
321 for j in 0..mat.nrows() {
322 matrix[(j, column_number + column_index_start)] =
323 mat[(j, column_number)];
324 }
325 }
326 }
327 DataType::UInt8
328 | DataType::UInt16
329 | DataType::UInt32
330 | DataType::UInt64
331 | DataType::Int8
332 | DataType::Int16
333 | DataType::Int32
334 | DataType::Int64
335 | DataType::Float32
336 | DataType::Float64 => {
337 let mut c = Vec::new();
338 column
339 .cast(&DataType::Float64)
340 .unwrap()
341 .f64()
342 .unwrap()
343 .for_each(|c_opt| c.push(c_opt.unwrap()));
344 let (_, mat) = as_factor("", &c);
345
346 let column_index_start = matrix.ncols();
347 for column_number in 0..mat.ncols() {
348 let ncols = matrix.ncols();
349 matrix = matrix.insert_column(ncols, 0.0);
350 for j in 0..mat.nrows() {
351 matrix[(j, column_number + column_index_start)] =
352 mat[(j, column_number)];
353 }
354 }
355 }
356 DataType::String => {
357 let mut c = Vec::new();
358 column
359 .str()
360 .unwrap()
361 .for_each(|c_opt| c.push(c_opt.unwrap()));
362 let (_, mat) = as_factor("", &c);
363
364 let column_index_start = matrix.ncols();
365 for column_number in 0..mat.ncols() {
366 let ncols = matrix.ncols();
367 matrix = matrix.insert_column(ncols, 0.0);
368 for j in 0..mat.nrows() {
369 matrix[(j, column_number + column_index_start)] =
370 mat[(j, column_number)];
371 }
372 }
373 }
374 DataType::Binary | DataType::BinaryOffset => {}
375 DataType::Date
376 | DataType::Datetime(_, _)
377 | DataType::Duration(_)
378 | DataType::Time => {}
379 DataType::List(_) | DataType::Null | DataType::Unknown => {}
380 },
381 ModelMatrixSchema::Value => {
382 let column_index = matrix.ncols();
383 matrix = matrix.insert_column(column_index, 0.0);
384
385 let mut c = Vec::new();
386 match data_type {
387 DataType::Boolean => {
388 column.bool().unwrap().for_each(|c_opt| {
389 c.push(if c_opt.unwrap() { 1.0 } else { 0.0 })
390 });
391 }
392 DataType::UInt8
393 | DataType::UInt16
394 | DataType::UInt32
395 | DataType::UInt64
396 | DataType::Int8
397 | DataType::Int16
398 | DataType::Int32
399 | DataType::Int64
400 | DataType::Float32
401 | DataType::Float64 => {
402 column
403 .cast(&DataType::Float64)
404 .unwrap()
405 .f64()
406 .unwrap()
407 .for_each(|c_opt| c.push(c_opt.unwrap()));
408 }
409 DataType::String => {
410 column.str().unwrap().for_each(|c_opt| {
411 let c_unwrapped = c_opt.unwrap().to_string();
412 c.push(c_unwrapped.parse::<f64>().unwrap());
413 });
414 }
415 DataType::Binary | DataType::BinaryOffset => {}
416 DataType::Date
417 | DataType::Datetime(_, _)
418 | DataType::Duration(_)
419 | DataType::Time => {}
420 DataType::List(_) | DataType::Null | DataType::Unknown => {}
421 }
422
423 for (j, c_j) in c.into_iter().enumerate() {
424 matrix[(j, column_index)] = c_j;
425 }
426 }
427 }
428 }
429
430 matrix
431 }
432 }
433 }
434
435 pub fn set_schema_index(&mut self, index: usize, schema: ModelMatrixSchema) {
436 self.schema[index] = schema;
437 }
438
439 pub fn set_schema_name(&mut self, name: &str, schema: ModelMatrixSchema) {
440 match &self.data {
441 ModelMatrixData::Vec { .. } => self.schema[0] = schema,
442 ModelMatrixData::Matrix { .. } => {
443 let index = name.replace("x", "").parse::<usize>().unwrap();
444 self.schema[index] = schema;
445 }
446 ModelMatrixData::DataFrame(df) => {
447 let name_index = df
448 .get_column_names()
449 .iter()
450 .enumerate()
451 .find(|(_, &n)| n == name)
452 .unwrap()
453 .0;
454 self.schema[name_index] = schema;
455 }
456 }
457 }
458
459 pub fn set_name(&mut self, old_name: &str, new_name: &str) {
460 match &mut self.data {
461 ModelMatrixData::Vec { name, .. } => {
462 *name = new_name.to_string();
463 }
464 ModelMatrixData::Matrix { names, .. } => {
465 for name in names {
466 if name == old_name {
467 *name = new_name.to_string();
468 }
469 }
470 }
471 ModelMatrixData::DataFrame(df) => {
472 df.rename(old_name, new_name).unwrap();
473 }
474 }
475 }
476
477 pub fn set_name_index(&mut self, index: usize, name: &str) {
478 match &mut self.data {
479 ModelMatrixData::Vec { name, .. } => {
480 *name = name.to_string();
481 }
482 ModelMatrixData::Matrix { names, .. } => {
483 names[index] = name.to_string();
484 }
485 ModelMatrixData::DataFrame(df) => {
486 let old_name = df.get_column_names()[index].to_string();
487 df.rename(&old_name, name).unwrap();
488 }
489 }
490 }
491
492 pub fn column_names(&self) -> Vec<String> {
493 match &self.data {
494 ModelMatrixData::Vec { name, data } => match self.schema[0] {
495 ModelMatrixSchema::RowName => Vec::new(),
496 ModelMatrixSchema::Level | ModelMatrixSchema::Value => vec![name.clone()],
497 ModelMatrixSchema::Factor => as_factor(name, data).0,
498 },
499 ModelMatrixData::Matrix {
500 names: base_names,
501 data,
502 } => {
503 let mut names = Vec::new();
504 for i in 0..data.ncols() {
505 match self.schema[i] {
506 ModelMatrixSchema::RowName => {}
507 ModelMatrixSchema::Level | ModelMatrixSchema::Value => {
508 names.push(base_names[i].clone())
509 }
510 ModelMatrixSchema::Factor => {
511 for name in as_factor(&base_names[i], data.column(i).as_slice()).0 {
512 names.push(name);
513 }
514 }
515 }
516 }
517 names
518 }
519 ModelMatrixData::DataFrame(df) => {
520 let mut names = Vec::new();
521
522 let data_names = df.get_column_names();
523 let data_types = df.dtypes();
524 let schemas = &self.schema;
525
526 for i in 0..df.width() {
527 let column_name = data_names[i];
528 let column = &df[column_name];
529 let data_type = &data_types[i];
530 let schema = &schemas[i];
531
532 match schema {
533 ModelMatrixSchema::RowName => {}
534 ModelMatrixSchema::Value | ModelMatrixSchema::Level => {
535 names.push(data_names[i].to_string())
536 }
537 ModelMatrixSchema::Factor => match data_type {
538 DataType::Boolean => {
539 let mut c = Vec::new();
540 column
541 .bool()
542 .unwrap()
543 .for_each(|c_opt| c.push(c_opt.unwrap()));
544 let (mut new_names, _) = as_factor(column_name, &c);
545
546 names.append(&mut new_names);
547 }
548 DataType::UInt8
549 | DataType::UInt16
550 | DataType::UInt32
551 | DataType::UInt64
552 | DataType::Int8
553 | DataType::Int16
554 | DataType::Int32
555 | DataType::Int64
556 | DataType::Float32
557 | DataType::Float64 => {
558 let mut c = Vec::new();
559 column
560 .cast(&DataType::Float64)
561 .unwrap()
562 .f64()
563 .unwrap()
564 .for_each(|c_opt| c.push(c_opt.unwrap()));
565 let (mut new_names, _) = as_factor(column_name, &c);
566
567 names.append(&mut new_names);
568 }
569 DataType::String => {
570 let mut c = Vec::new();
571 column
572 .str()
573 .unwrap()
574 .for_each(|c_opt| c.push(c_opt.unwrap()));
575 let (mut new_names, _) = as_factor(column_name, &c);
576
577 names.append(&mut new_names);
578 }
579 DataType::Binary | DataType::BinaryOffset => {}
580 DataType::Date
581 | DataType::Datetime(_, _)
582 | DataType::Duration(_)
583 | DataType::Time => {}
584 DataType::List(_) | DataType::Null | DataType::Unknown => {}
585 },
586 }
587 }
588
589 names
590 }
591 }
592 }
593
594 pub fn row_names(&self) -> Vec<String> {
595 match &self.data {
596 ModelMatrixData::Vec { data, .. } => {
597 if self.schema[0] == ModelMatrixSchema::RowName {
598 data.iter().map(|d| d.to_string()).collect::<Vec<_>>()
599 } else {
600 Vec::new()
601 }
602 }
603 ModelMatrixData::Matrix { data, .. } => {
604 let mut names = Vec::new();
605 for i in 0..data.ncols() {
606 if self.schema[i] == ModelMatrixSchema::RowName {
607 names = data.column(i).iter().map(|d| d.to_string()).collect();
608 break;
609 }
610 }
611 names
612 }
613 ModelMatrixData::DataFrame(df) => {
614 let mut names = Vec::new();
615
616 let data_names = df.get_column_names();
617 let data_types = df.dtypes();
618 let schemas = &self.schema;
619
620 for i in 0..df.width() {
621 let column_name = data_names[i];
622 let column = &df[column_name];
623 let data_type = &data_types[i];
624 let schema = schemas[i];
625
626 if schema == ModelMatrixSchema::RowName {
627 match data_type {
628 DataType::Boolean => {
629 let mut c = Vec::new();
630 column
631 .bool()
632 .unwrap()
633 .for_each(|c_opt| c.push(c_opt.unwrap()));
634 names = c.into_iter().map(|b| b.to_string()).collect();
635 }
636 DataType::UInt8
637 | DataType::UInt16
638 | DataType::UInt32
639 | DataType::UInt64
640 | DataType::Int8
641 | DataType::Int16
642 | DataType::Int32
643 | DataType::Int64
644 | DataType::Float32
645 | DataType::Float64 => {
646 let mut c = Vec::new();
647 column
648 .cast(&DataType::Float64)
649 .unwrap()
650 .f64()
651 .unwrap()
652 .for_each(|c_opt| c.push(c_opt.unwrap()));
653 names = c.into_iter().map(|f| f.to_string()).collect();
654 }
655 DataType::String => {
656 let mut c = Vec::new();
657 column
658 .str()
659 .unwrap()
660 .for_each(|c_opt| c.push(c_opt.unwrap()));
661 names = c.into_iter().map(|s| s.to_string()).collect();
662 }
663 DataType::Binary | DataType::BinaryOffset => {}
664 DataType::Date
665 | DataType::Datetime(_, _)
666 | DataType::Duration(_)
667 | DataType::Time => {}
668 DataType::List(_) | DataType::Null | DataType::Unknown => {}
669 }
670 break;
671 }
672 }
673
674 names
675 }
676 }
677 }
678}
679
680impl Display for ModelMatrix {
681 fn fmt(&self, formatter: &mut Formatter) -> FmtResult {
682 writeln!(
683 formatter,
684 "({}, {})",
685 self.matrix().nrows(),
686 self.matrix().ncols()
687 )?;
688
689 let dp = DisplayTable::new(
690 self.column_names(),
691 self.row_names(),
692 self.matrix()
693 .row_iter()
694 .map(|r| r.iter().cloned().collect())
695 .collect(),
696 None,
697 );
698
699 writeln!(formatter, "{dp}")
700 }
701}
702
703impl From<&DataFrame> for ModelMatrix {
704 fn from(value: &DataFrame) -> Self {
705 Self::from(value.clone())
706 }
707}
708
709impl From<DataFrame> for ModelMatrix {
710 fn from(value: DataFrame) -> Self {
711 Self {
712 schema: value
713 .dtypes()
714 .into_iter()
715 .enumerate()
716 .filter_map(|(i, dtype)| {
717 if i == 0 && value.get_column_names()[i].is_empty() {
718 Some(ModelMatrixSchema::RowName)
719 } else {
720 match dtype {
721 DataType::Boolean => Some(ModelMatrixSchema::Factor),
722 DataType::UInt8
723 | DataType::UInt16
724 | DataType::UInt32
725 | DataType::UInt64
726 | DataType::Int8
727 | DataType::Int16
728 | DataType::Int32
729 | DataType::Int64
730 | DataType::Float32
731 | DataType::Float64 => Some(ModelMatrixSchema::Value),
732 DataType::String => Some(ModelMatrixSchema::Factor),
733 DataType::Binary | DataType::BinaryOffset => None,
734 DataType::Date
735 | DataType::Datetime(_, _)
736 | DataType::Duration(_)
737 | DataType::Time => None,
738 DataType::List(_) | DataType::Null | DataType::Unknown => None,
739 }
740 }
741 })
742 .collect(),
743 data: ModelMatrixData::DataFrame(value),
744 }
745 }
746}
747
748impl From<&DMatrix<f64>> for ModelMatrix {
749 fn from(value: &DMatrix<f64>) -> Self {
750 Self::from(value.clone())
751 }
752}
753
754impl From<DMatrix<f64>> for ModelMatrix {
755 fn from(value: DMatrix<f64>) -> Self {
756 Self {
757 schema: (0..value.ncols())
758 .map(|_| ModelMatrixSchema::Value)
759 .collect(),
760 data: ModelMatrixData::Matrix {
761 names: (0..value.ncols()).map(|i| format!("x{i}")).collect(),
762 data: value,
763 },
764 }
765 }
766}
767
768impl<T: Clone> From<Vec<T>> for ModelMatrix
769where
770 f64: From<T>,
771{
772 fn from(value: Vec<T>) -> Self {
773 Self::from(value.as_slice())
774 }
775}
776
777impl<T: Clone> From<&Vec<T>> for ModelMatrix
778where
779 f64: From<T>,
780{
781 fn from(value: &Vec<T>) -> Self {
782 Self::from(value.as_slice())
783 }
784}
785
786impl<T: Clone> From<&[T]> for ModelMatrix
787where
788 f64: From<T>,
789{
790 fn from(value: &[T]) -> Self {
791 Self {
792 data: ModelMatrixData::Vec {
793 name: "x".to_string(),
794 data: value.iter().map(|t| f64::from(t.clone())).collect(),
795 },
796 schema: vec![ModelMatrixSchema::Value],
797 }
798 }
799}