1use std::sync::Arc;
16
17#[cfg(feature = "scalar_type")]
18use crate::Scalar;
19use crate::enums::error::{KernelError, MinarrowError};
20use crate::enums::operators::ArithmeticOperator;
21use crate::kernels::broadcast::array::broadcast_array_add;
22use crate::kernels::broadcast::broadcast_value;
23use crate::kernels::broadcast::table_view::broadcast_tableview_to_arrayview;
24use crate::kernels::routing::arithmetic::resolve_binary_arithmetic;
25use crate::structs::field_array::create_field_for_array;
26use crate::{Array, ArrayV, Bitmask, Field, FieldArray, Table, TableV, Value};
27#[cfg(feature = "chunked")]
28use crate::{SuperArray, SuperArrayV, SuperTable, SuperTableV};
29
30pub fn broadcast_table_with_operator(
32 op: ArithmeticOperator,
33 table_l: impl Into<TableV>,
34 table_r: impl Into<TableV>,
35) -> Result<Table, MinarrowError> {
36 let table_l: TableV = table_l.into();
37 let table_r: TableV = table_r.into();
38
39 if table_l.cols.len() != table_r.cols.len() {
41 return Err(MinarrowError::ShapeError {
42 message: format!(
43 "Table column count mismatch: {} vs {}",
44 table_l.cols.len(),
45 table_r.cols.len()
46 ),
47 });
48 }
49
50 let mut result_field_arrays = Vec::new();
51
52 for (i, (col_l, col_r)) in table_l.cols.iter().zip(table_r.cols.iter()).enumerate() {
53 let result_array = resolve_binary_arithmetic(op, col_l.clone(), col_r.clone(), None)?;
55
56 let result_field_array = FieldArray::new(table_l.fields[i].as_ref().clone(), result_array);
58 result_field_arrays.push(result_field_array);
59 }
60
61 Ok(Table::new(table_l.name.clone(), Some(result_field_arrays)))
62}
63
64pub fn broadcast_table_add(
69 lhs: impl Into<TableV>,
70 rhs: impl Into<TableV>,
71 null_mask: Option<Arc<Bitmask>>,
72) -> Result<Table, KernelError> {
73 let lhs_table: TableV = lhs.into();
74 let rhs_table: TableV = rhs.into();
75
76 if lhs_table.cols.len() != rhs_table.cols.len() {
78 return Err(KernelError::BroadcastingError(format!(
79 "Table column count mismatch: LHS {} cols, RHS {} cols",
80 lhs_table.cols.len(),
81 rhs_table.cols.len()
82 )));
83 }
84
85 if lhs_table.len != rhs_table.len {
86 return Err(KernelError::BroadcastingError(format!(
87 "Table row count mismatch: LHS {} rows, RHS {} rows",
88 lhs_table.len, rhs_table.len
89 )));
90 }
91
92 let mut result_cols = Vec::with_capacity(lhs_table.cols.len());
94
95 for (i, (lhs_col, rhs_col)) in lhs_table.cols.iter().zip(rhs_table.cols.iter()).enumerate() {
96 let result_array =
97 broadcast_array_add(lhs_col.clone(), rhs_col.clone(), null_mask.as_deref()).map_err(
98 |e| KernelError::BroadcastingError(format!("Column {} addition failed: {}", i, e)),
99 )?;
100
101 let field_name = if i < lhs_table.fields.len() {
103 lhs_table.fields[i].name.clone()
104 } else {
105 format!("col_{}", i)
106 };
107
108 let field_dtype = if i < lhs_table.fields.len() {
109 lhs_table.fields[i].dtype.clone()
110 } else {
111 result_array.arrow_type()
112 };
113
114 let field = Field::new(
115 field_name,
116 field_dtype,
117 result_array.null_mask().is_some(), None, );
120 let field_array = FieldArray::new(field, result_array);
121
122 result_cols.push(field_array);
123 }
124
125 Ok(Table::new(lhs_table.name.clone(), Some(result_cols)))
127}
128
129#[cfg(feature = "chunked")]
134pub fn broadcast_super_table_add(
135 lhs: impl Into<SuperTableV>,
136 rhs: impl Into<SuperTableV>,
137 null_mask: Option<Arc<Bitmask>>,
138) -> Result<SuperTable, KernelError> {
139 let lhs_table: SuperTableV = lhs.into();
140 let rhs_table: SuperTableV = rhs.into();
141
142 if lhs_table.slices.len() != rhs_table.slices.len() {
144 return Err(KernelError::BroadcastingError(format!(
145 "SuperTable chunk count mismatch: LHS {} chunks, RHS {} chunks",
146 lhs_table.slices.len(),
147 rhs_table.slices.len()
148 )));
149 }
150
151 let mut result_tables = Vec::with_capacity(lhs_table.slices.len());
153
154 for (i, (lhs_chunk, rhs_chunk)) in lhs_table
155 .slices
156 .iter()
157 .zip(rhs_table.slices.iter())
158 .enumerate()
159 {
160 let result_table =
161 broadcast_table_add(lhs_chunk.clone(), rhs_chunk.clone(), null_mask.clone()).map_err(
162 |e| KernelError::BroadcastingError(format!("Chunk {} addition failed: {}", i, e)),
163 )?;
164
165 result_tables.push(Arc::new(result_table));
166 }
167
168 let name = if !lhs_table.slices.is_empty() && !lhs_table.slices[0].name.is_empty() {
170 lhs_table.slices[0].name.clone()
171 } else {
172 "SuperTable".to_string()
173 };
174 Ok(SuperTable::from_batches(result_tables, Some(name)))
175}
176
177pub fn broadcast_table_to_array(
179 op: ArithmeticOperator,
180 table: &Table,
181 array: &Array,
182) -> Result<Table, MinarrowError> {
183 let new_cols: Result<Vec<_>, _> = table
184 .cols
185 .iter()
186 .map(|field_array| {
187 let col_array = &field_array.array;
188 let result_array = match (
189 Value::Array(Arc::new(col_array.clone())),
190 Value::Array(Arc::new(array.clone())),
191 ) {
192 (a, b) => broadcast_value(op, a, b)?,
193 };
194
195 match result_array {
196 Value::Array(result_array) => {
197 let result_array = Arc::unwrap_or_clone(result_array);
198 let new_field = create_field_for_array(
200 &field_array.field.name,
201 &result_array,
202 Some(&array),
203 Some(field_array.field.metadata.clone()),
204 );
205 Ok(FieldArray::new(new_field, result_array))
206 }
207 _ => Err(MinarrowError::TypeError {
208 from: "table-array broadcasting",
209 to: "Array result",
210 message: Some("Expected Array result from broadcasting".to_string()),
211 }),
212 }
213 })
214 .collect();
215
216 let table_out = Table::new(table.name.clone(), Some(new_cols?));
217 #[cfg(feature = "table_metadata")]
218 {
219 let mut t = table_out;
220 t.metadata = table.metadata.clone();
221 return Ok(t);
222 }
223 #[cfg(not(feature = "table_metadata"))]
224 Ok(table_out)
225}
226
227#[cfg(feature = "scalar_type")]
229pub fn broadcast_table_to_scalar(
230 op: ArithmeticOperator,
231 table: &Table,
232 scalar: &Scalar,
233) -> Result<Table, MinarrowError> {
234 let new_cols: Result<Vec<_>, _> = table
235 .cols
236 .iter()
237 .map(|field_array| {
238 let col_array = &field_array.array;
239 let result_array = match (
240 Value::Array(Arc::new(col_array.clone())),
241 Value::Scalar(scalar.clone()),
242 ) {
243 (a, b) => broadcast_value(op, a, b)?,
244 };
245
246 match result_array {
247 Value::Array(result_array) => {
248 let result_array = Arc::unwrap_or_clone(result_array);
249 let new_field = create_field_for_array(
251 &field_array.field.name,
252 &result_array,
253 Some(&col_array),
254 Some(field_array.field.metadata.clone()),
255 );
256 Ok(FieldArray::new(new_field, result_array))
257 }
258 _ => Err(MinarrowError::TypeError {
259 from: "table-scalar broadcasting",
260 to: "Array result",
261 message: Some("Expected Array result from broadcasting".to_string()),
262 }),
263 }
264 })
265 .collect();
266
267 let table_out = Table::new(table.name.clone(), Some(new_cols?));
268 #[cfg(feature = "table_metadata")]
269 {
270 let mut t = table_out;
271 t.metadata = table.metadata.clone();
272 return Ok(t);
273 }
274 #[cfg(not(feature = "table_metadata"))]
275 Ok(table_out)
276}
277
278#[cfg(feature = "views")]
280pub fn broadcast_table_to_arrayview(
281 op: ArithmeticOperator,
282 table: &Table,
283 array_view: &ArrayV,
284) -> Result<Table, MinarrowError> {
285 let new_cols: Result<Vec<_>, _> = table
287 .cols
288 .iter()
289 .map(|field_array| {
290 let col_array = &field_array.array;
291 let col_view = ArrayV::new(col_array.clone(), array_view.offset, array_view.len());
293 let result_array = match (
294 Value::ArrayView(Arc::new(col_view)),
295 Value::ArrayView(Arc::new(array_view.clone())),
296 ) {
297 (a, b) => broadcast_value(op, a, b)?,
298 };
299
300 match result_array {
301 Value::Array(result_array) => {
302 let result_array = Arc::unwrap_or_clone(result_array);
303 let new_field = create_field_for_array(
304 &field_array.field.name,
305 &result_array,
306 Some(&array_view.array),
307 Some(field_array.field.metadata.clone()),
308 );
309 Ok(FieldArray::new(new_field, result_array))
310 }
311 _ => Err(MinarrowError::TypeError {
312 from: "table-arrayview broadcasting",
313 to: "Array result",
314 message: Some("Expected Array result from view broadcasting".to_string()),
315 }),
316 }
317 })
318 .collect();
319
320 let table_out = Table::new(table.name.clone(), Some(new_cols?));
321 #[cfg(feature = "table_metadata")]
322 {
323 let mut t = table_out;
324 t.metadata = table.metadata.clone();
325 return Ok(t);
326 }
327 #[cfg(not(feature = "table_metadata"))]
328 Ok(table_out)
329}
330
331#[cfg(all(feature = "chunked", feature = "views"))]
333pub fn broadcast_table_to_superarrayview(
334 op: ArithmeticOperator,
335 table: &Table,
336 super_array_view: &SuperArrayV,
337) -> Result<SuperTableV, MinarrowError> {
338 if table.n_rows != super_array_view.len {
340 return Err(MinarrowError::ShapeError {
341 message: format!(
342 "Table rows ({}) does not match SuperArrayView length ({})",
343 table.n_rows, super_array_view.len
344 ),
345 });
346 }
347
348 let mut current_offset = 0;
350 let mut table_slices = Vec::new();
351
352 for array_slice in super_array_view.slices.iter() {
353 let chunk_len = array_slice.len();
354 let table_slice = TableV::from_table(table.clone(), current_offset, chunk_len);
355 table_slices.push(table_slice);
356 current_offset += chunk_len;
357 }
358
359 let aligned_super_table = SuperTableV {
360 slices: table_slices,
361 len: table.n_rows,
362 };
363
364 let mut result_slices = Vec::new();
366 for i in 0..aligned_super_table.slices.len() {
367 let table_slice = &aligned_super_table.slices[i];
368 let array_slice = &super_array_view.slices[i];
369 let slice_result = broadcast_tableview_to_arrayview(op, table_slice, array_slice)?;
370 result_slices.push(slice_result);
371 }
372
373 Ok(SuperTableV {
374 slices: result_slices,
375 len: super_array_view.len,
376 })
377}
378
379#[cfg(feature = "chunked")]
381pub fn broadcast_table_to_superarray(
382 op: ArithmeticOperator,
383 table: &Table,
384 super_array: &SuperArray,
385) -> Result<SuperArray, MinarrowError> {
386 let new_chunks: Result<Vec<_>, _> = super_array
387 .chunks()
388 .iter()
389 .map(|chunk| {
390 let chunk_array = chunk;
391 let result_table = broadcast_table_to_array(op, table, chunk_array)?;
392 if result_table.cols.len() == 1 {
394 Ok(result_table.cols[0].clone())
395 } else {
396 Err(MinarrowError::ShapeError {
397 message: "Table-SuperArray broadcasting should result in single column"
398 .to_string(),
399 })
400 }
401 })
402 .collect();
403
404 Ok(SuperArray::from_chunks(new_chunks?))
405}
406
407#[cfg(test)]
408mod tests {
409 use super::*;
410 use crate::traits::selection::ColumnSelection;
411 use crate::{Array, FieldArray, IntegerArray, vec64};
412
413 fn create_test_table(name: &str, data1: &[i32], data2: &[i32]) -> Table {
414 let col1 = FieldArray::from_arr(
415 "col1",
416 Array::from_int32(IntegerArray::from_slice(&vec64![
417 data1[0], data1[1], data1[2]
418 ])),
419 );
420 let col2 = FieldArray::from_arr(
421 "col2",
422 Array::from_int32(IntegerArray::from_slice(&vec64![
423 data2[0], data2[1], data2[2]
424 ])),
425 );
426
427 Table::new(name.to_string(), Some(vec![col1, col2]))
428 }
429
430 #[test]
431 fn test_table_plus_table() {
432 let table1 = create_test_table("table1", &[1, 2, 3], &[10, 20, 30]);
433 let table2 = create_test_table("table2", &[4, 5, 6], &[40, 50, 60]);
434
435 let result = broadcast_table_add(table1, table2, None).unwrap();
436
437 assert_eq!(result.n_cols(), 2);
438 assert_eq!(result.n_rows(), 3);
439 assert_eq!(result.name, "table1"); if let Some(col1) = result.col_ix(0) {
443 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &col1.array {
444 assert_eq!(arr.data.as_slice(), &[5, 7, 9]);
445 } else {
446 panic!("Expected Int32 array in first column");
447 }
448 } else {
449 panic!("Could not get first column");
450 }
451
452 if let Some(col2) = result.col_ix(1) {
454 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &col2.array {
455 assert_eq!(arr.data.as_slice(), &[50, 70, 90]);
456 } else {
457 panic!("Expected Int32 array in second column");
458 }
459 } else {
460 panic!("Could not get second column");
461 }
462 }
463
464 #[test]
465 #[should_panic(expected = "column count mismatch")]
466 fn test_mismatched_column_count() {
467 let col1 = FieldArray::from_arr(
468 "col1",
469 Array::from_int32(IntegerArray::from_slice(&vec64![1, 2, 3])),
470 );
471 let table1 = Table::new("table1".to_string(), Some(vec![col1])); let table2 = create_test_table("table2", &[4, 5, 6], &[40, 50, 60]); let _ = broadcast_table_add(table1, table2, None).unwrap();
476 }
477
478 #[test]
479 #[should_panic(expected = "row count mismatch")]
480 fn test_mismatched_row_count() {
481 let col1 = FieldArray::from_arr(
482 "col1",
483 Array::from_int32(IntegerArray::from_slice(&vec64![1, 2])),
484 );
485 let col2 = FieldArray::from_arr(
486 "col2",
487 Array::from_int32(IntegerArray::from_slice(&vec64![10, 20])),
488 );
489 let table1 = Table::new("table1".to_string(), Some(vec![col1, col2])); let table2 = create_test_table("table2", &[4, 5, 6], &[40, 50, 60]); let _ = broadcast_table_add(table1, table2, None).unwrap();
494 }
495
496 #[test]
497 fn test_broadcast_table_with_operator_multiply() {
498 let table1 = create_test_table("table1", &[2, 3, 4], &[5, 6, 7]);
499 let table2 = create_test_table("table2", &[10, 10, 10], &[2, 2, 2]);
500
501 let result =
502 broadcast_table_with_operator(ArithmeticOperator::Multiply, table1, table2).unwrap();
503
504 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &result.cols[0].array {
506 assert_eq!(arr.data.as_slice(), &[20, 30, 40]);
507 } else {
508 panic!("Expected Int32 array");
509 }
510
511 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &result.cols[1].array {
513 assert_eq!(arr.data.as_slice(), &[10, 12, 14]);
514 } else {
515 panic!("Expected Int32 array");
516 }
517 }
518
519 #[test]
520 fn test_broadcast_table_to_array() {
521 let table = create_test_table("table1", &[10, 20, 30], &[100, 200, 300]);
522 let arr = Array::from_int32(IntegerArray::from_slice(&vec64![1, 2, 3]));
523
524 let result = broadcast_table_to_array(ArithmeticOperator::Add, &table, &arr).unwrap();
525
526 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &result.cols[0].array {
528 assert_eq!(arr.data.as_slice(), &[11, 22, 33]);
529 } else {
530 panic!("Expected Int32 array");
531 }
532
533 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &result.cols[1].array {
535 assert_eq!(arr.data.as_slice(), &[101, 202, 303]);
536 } else {
537 panic!("Expected Int32 array");
538 }
539 }
540
541 #[cfg(feature = "scalar_type")]
542 #[test]
543 fn test_broadcast_table_to_scalar() {
544 let table = create_test_table("table1", &[10, 20, 30], &[100, 200, 300]);
545 let scalar = Scalar::Int32(5);
546
547 let result =
548 broadcast_table_to_scalar(ArithmeticOperator::Multiply, &table, &scalar).unwrap();
549
550 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &result.cols[0].array {
552 assert_eq!(arr.data.as_slice(), &[50, 100, 150]);
553 } else {
554 panic!("Expected Int32 array");
555 }
556
557 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &result.cols[1].array {
559 assert_eq!(arr.data.as_slice(), &[500, 1000, 1500]);
560 } else {
561 panic!("Expected Int32 array");
562 }
563 }
564
565 #[cfg(feature = "views")]
566 #[test]
567 fn test_broadcast_table_to_arrayview() {
568 let table = create_test_table("table1", &[10, 20, 30], &[100, 200, 300]);
569 let arr = Array::from_int32(IntegerArray::from_slice(&vec64![2, 3, 4]));
570 let array_view = ArrayV::from(arr);
571
572 let result =
573 broadcast_table_to_arrayview(ArithmeticOperator::Subtract, &table, &array_view)
574 .unwrap();
575
576 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &result.cols[0].array {
578 assert_eq!(arr.data.as_slice(), &[8, 17, 26]);
579 } else {
580 panic!("Expected Int32 array");
581 }
582
583 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &result.cols[1].array {
585 assert_eq!(arr.data.as_slice(), &[98, 197, 296]);
586 } else {
587 panic!("Expected Int32 array");
588 }
589 }
590
591 #[cfg(feature = "chunked")]
592 #[test]
593 fn test_broadcast_super_table_add() {
594 let table1 = create_test_table("table1", &[1, 2, 3], &[10, 20, 30]);
595 let table2 = create_test_table("table2", &[4, 5, 6], &[40, 50, 60]);
596 let table3 = create_test_table("table3", &[7, 8, 9], &[70, 80, 90]);
597 let table4 = create_test_table("table4", &[1, 1, 1], &[2, 2, 2]);
598
599 let super_table1 = SuperTableV {
600 slices: vec![
601 TableV::from_table(table1, 0, 3),
602 TableV::from_table(table2, 0, 3),
603 ],
604 len: 6,
605 };
606
607 let super_table2 = SuperTableV {
608 slices: vec![
609 TableV::from_table(table3, 0, 3),
610 TableV::from_table(table4, 0, 3),
611 ],
612 len: 6,
613 };
614
615 let result = broadcast_super_table_add(super_table1, super_table2, None).unwrap();
616
617 assert_eq!(result.batches.len(), 2);
618
619 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) =
622 &result.batches[0].cols[0].array
623 {
624 assert_eq!(arr.data.as_slice(), &[8, 10, 12]);
625 } else {
626 panic!("Expected Int32 array");
627 }
628
629 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) =
631 &result.batches[0].cols[1].array
632 {
633 assert_eq!(arr.data.as_slice(), &[80, 100, 120]);
634 } else {
635 panic!("Expected Int32 array");
636 }
637
638 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) =
641 &result.batches[1].cols[0].array
642 {
643 assert_eq!(arr.data.as_slice(), &[5, 6, 7]);
644 } else {
645 panic!("Expected Int32 array");
646 }
647
648 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) =
650 &result.batches[1].cols[1].array
651 {
652 assert_eq!(arr.data.as_slice(), &[42, 52, 62]);
653 } else {
654 panic!("Expected Int32 array");
655 }
656 }
657
658 #[cfg(feature = "chunked")]
659 #[test]
660 fn test_broadcast_table_to_superarray() {
661 use crate::ffi::arrow_dtype::ArrowType;
662
663 let table = Table::build(
665 vec![FieldArray::new(
666 Field::new("col1".to_string(), ArrowType::Int32, false, None),
667 Array::from_int32(IntegerArray::from_slice(&vec64![2, 3, 4])),
668 )],
669 3,
670 "test".to_string(),
671 );
672
673 let field = Field::new("data".to_string(), ArrowType::Int32, false, None);
674 let chunks = vec![
675 FieldArray::new(
676 field.clone(),
677 Array::from_int32(IntegerArray::from_slice(&vec64![10, 20, 30])),
678 ),
679 FieldArray::new(
680 field.clone(),
681 Array::from_int32(IntegerArray::from_slice(&vec64![40, 50, 60])),
682 ),
683 ];
684 let super_array = SuperArray::from_chunks(chunks);
685
686 let result =
687 broadcast_table_to_superarray(ArithmeticOperator::Add, &table, &super_array).unwrap();
688
689 assert_eq!(result.chunks().len(), 2);
690
691 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &result.chunks()[0] {
693 assert_eq!(arr.data.as_slice(), &[12, 23, 34]);
694 } else {
695 panic!("Expected Int32 array");
696 }
697
698 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &result.chunks()[1] {
699 assert_eq!(arr.data.as_slice(), &[42, 53, 64]);
700 } else {
701 panic!("Expected Int32 array");
702 }
703 }
704
705 #[cfg(all(feature = "chunked", feature = "views"))]
706 #[test]
707 fn test_broadcast_table_to_superarrayview() {
708 use crate::ffi::arrow_dtype::ArrowType;
709
710 let table = Table::build(
711 vec![FieldArray::new(
712 Field::new("col1".to_string(), ArrowType::Int32, false, None),
713 Array::from_int32(IntegerArray::from_slice(&vec64![1, 2, 3, 4, 5, 6])),
714 )],
715 6,
716 "test".to_string(),
717 );
718
719 let arr = Array::from_int32(IntegerArray::from_slice(&vec64![10, 20, 30, 40, 50, 60]));
720 let field = Field::new("data".to_string(), ArrowType::Int32, false, None);
721
722 let slices = vec![
723 ArrayV::from(arr.clone()).slice(0, 3),
724 ArrayV::from(arr.clone()).slice(3, 3),
725 ];
726 let super_array_view = SuperArrayV {
727 slices,
728 field: Arc::new(field),
729 len: 6,
730 };
731
732 let result = broadcast_table_to_superarrayview(
733 ArithmeticOperator::Multiply,
734 &table,
735 &super_array_view,
736 )
737 .unwrap();
738
739 assert_eq!(result.slices.len(), 2);
740 assert_eq!(result.len, 6);
741
742 let slice1 = result.slices[0].to_table();
744 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &slice1.cols[0].array {
745 assert_eq!(arr.data.as_slice(), &[10, 40, 90]);
746 } else {
747 panic!("Expected Int32 array");
748 }
749
750 let slice2 = result.slices[1].to_table();
752 if let crate::Array::NumericArray(crate::NumericArray::Int32(arr)) = &slice2.cols[0].array {
753 assert_eq!(arr.data.as_slice(), &[160, 250, 360]);
754 } else {
755 panic!("Expected Int32 array");
756 }
757 }
758}