diff --git a/examples/practice2_j_segment_tree.rs b/examples/practice2_j_segment_tree.rs new file mode 100644 index 0000000..ed5d043 --- /dev/null +++ b/examples/practice2_j_segment_tree.rs @@ -0,0 +1,35 @@ +use ac_library_rs::{Max, Segtree}; +use std::io::Read; + +fn main() { + let mut buf = String::new(); + std::io::stdin().read_to_string(&mut buf).unwrap(); + let mut input = buf.split_whitespace(); + + let n: usize = input.next().unwrap().parse().unwrap(); + let q: usize = input.next().unwrap().parse().unwrap(); + let mut segtree = Segtree::>::new(n + 1); + for i in 1..=n { + segtree.set(i, input.next().unwrap().parse().unwrap()); + } + for _ in 0..q { + match input.next().unwrap().parse().unwrap() { + 1 => { + let x = input.next().unwrap().parse().unwrap(); + let v = input.next().unwrap().parse().unwrap(); + segtree.set(x, v); + } + 2 => { + let l = input.next().unwrap().parse().unwrap(); + let r: usize = input.next().unwrap().parse().unwrap(); + println!("{}", segtree.prod(l, r + 1)); + } + 3 => { + let x = input.next().unwrap().parse().unwrap(); + let v = input.next().unwrap().parse().unwrap(); + println!("{}", segtree.max_right(x, |a| a < &v)) + } + _ => {} + } + } +} diff --git a/examples/practice2_k_range_affine_range_sum.rs b/examples/practice2_k_range_affine_range_sum.rs new file mode 100644 index 0000000..e07c542 --- /dev/null +++ b/examples/practice2_k_range_affine_range_sum.rs @@ -0,0 +1,67 @@ +use ac_library_rs::{LazySegtree, MapMonoid, ModInt998244353, Monoid}; +use std::io::Read; + +type Mint = ModInt998244353; +struct Sum; +impl Monoid for Sum { + type S = (Mint, usize); + + fn identity() -> Self::S { + (0.into(), 0) + } + + fn binary_operation(&(a, n): &Self::S, &(b, m): &Self::S) -> Self::S { + (a + b, n + m) + } +} +struct Affine; +impl MapMonoid for Affine { + type M = Sum; + type F = (Mint, Mint); + + fn identity_map() -> Self::F { + (1.into(), 0.into()) + } + + fn mapping(&(a, b): &Self::F, &(x, n): &::S) -> ::S { + (a * x + b * Mint::new(n), n) + } + + // a(cx + d) + b = (ac)x + (ad+b) + fn composition(&(a, b): &Self::F, &(c, d): &Self::F) -> Self::F { + (a * c, a * d + b) + } +} + +#[allow(clippy::many_single_char_names)] +fn main() { + let mut buf = String::new(); + std::io::stdin().read_to_string(&mut buf).unwrap(); + let mut input = buf.split_whitespace(); + + let n = input.next().unwrap().parse().unwrap(); + let q = input.next().unwrap().parse().unwrap(); + let mut segtree: LazySegtree = input + .by_ref() + .take(n) + .map(|s| (s.parse().unwrap(), 1)) + .collect::>() + .into(); + for _ in 0..q { + match input.next().unwrap().parse().unwrap() { + 0 => { + let l = input.next().unwrap().parse().unwrap(); + let r = input.next().unwrap().parse().unwrap(); + let b = input.next().unwrap().parse().unwrap(); + let c = input.next().unwrap().parse().unwrap(); + segtree.apply_range(l, r, (b, c)); + } + 1 => { + let l = input.next().unwrap().parse().unwrap(); + let r = input.next().unwrap().parse().unwrap(); + println!("{}", segtree.prod(l, r).0); + } + _ => {} + } + } +} diff --git a/examples/practice2_l_lazy_segment_tree.rs b/examples/practice2_l_lazy_segment_tree.rs new file mode 100644 index 0000000..61469d4 --- /dev/null +++ b/examples/practice2_l_lazy_segment_tree.rs @@ -0,0 +1,63 @@ +#![allow(clippy::many_single_char_names)] +use ac_library_rs::{LazySegtree, MapMonoid, Monoid}; +use std::io::Read; +use std::iter; + +struct M; +impl Monoid for M { + type S = (u64, u64, u64); + fn identity() -> Self::S { + (0, 0, 0) + } + fn binary_operation(&(a, b, c): &Self::S, &(d, e, f): &Self::S) -> Self::S { + (a + d, b + e, c + f + b * d) + } +} +struct F; +impl MapMonoid for F { + type M = M; + type F = bool; + + fn identity_map() -> Self::F { + false + } + fn mapping(&f: &Self::F, &(a, b, c): &::S) -> ::S { + if f { + // (a + b) * (a + b - 1) / 2 - a * (a - 1) / 2 - b * (b - 1) / 2 - c + // = a * b - c + (b, a, a * b - c) + } else { + (a, b, c) + } + } + fn composition(&f: &Self::F, &g: &Self::F) -> Self::F { + f ^ g + } +} + +fn main() { + let mut buf = String::new(); + std::io::stdin().read_to_string(&mut buf).unwrap(); + let mut input = buf.split_whitespace(); + + let n = input.next().unwrap().parse().unwrap(); + let q = input.next().unwrap().parse().unwrap(); + let mut segtree: LazySegtree = iter::once((0, 0, 0)) + .chain(input.by_ref().take(n).map(|s| match s { + "0" => (1, 0, 0), + "1" => (0, 1, 0), + _ => panic!(), + })) + .collect::>() + .into(); + for _ in 0..q { + let t = input.next().unwrap().parse().unwrap(); + let l = input.next().unwrap().parse().unwrap(); + let r: usize = input.next().unwrap().parse().unwrap(); + match t { + 1 => segtree.apply_range(l, r + 1, true), + 2 => println!("{}", segtree.prod(l, r + 1).2), + _ => {} + } + } +} diff --git a/src/internal_type_traits.rs b/src/internal_type_traits.rs index 73ba2e6..88a52a9 100644 --- a/src/internal_type_traits.rs +++ b/src/internal_type_traits.rs @@ -52,37 +52,65 @@ pub trait Integral: + fmt::Debug + fmt::Binary + fmt::Octal + + Zero + + One + + BoundedBelow + + BoundedAbove { +} + +/// Class that has additive identity element +pub trait Zero { + /// The additive identity element fn zero() -> Self; +} + +/// Class that has multiplicative identity element +pub trait One { + /// The multiplicative identity element fn one() -> Self; +} + +pub trait BoundedBelow { fn min_value() -> Self; +} + +pub trait BoundedAbove { fn max_value() -> Self; } macro_rules! impl_integral { ($($ty:ty),*) => { $( - impl Integral for $ty { + impl Zero for $ty { #[inline] fn zero() -> Self { 0 } + } + impl One for $ty { #[inline] fn one() -> Self { 1 } + } + impl BoundedBelow for $ty { #[inline] fn min_value() -> Self { Self::min_value() } + } + impl BoundedAbove for $ty { #[inline] fn max_value() -> Self { Self::max_value() } } + + impl Integral for $ty {} )* }; } diff --git a/src/lazysegtree.rs b/src/lazysegtree.rs index 8b13789..47020a8 100644 --- a/src/lazysegtree.rs +++ b/src/lazysegtree.rs @@ -1 +1,416 @@ +use crate::internal_bit::ceil_pow2; +use crate::Monoid; +pub trait MapMonoid { + type M: Monoid; + type F: Clone; + // type S = ::S; + fn identity_element() -> ::S { + Self::M::identity() + } + fn binary_operation( + a: &::S, + b: &::S, + ) -> ::S { + Self::M::binary_operation(a, b) + } + fn identity_map() -> Self::F; + fn mapping(f: &Self::F, x: &::S) -> ::S; + fn composition(f: &Self::F, g: &Self::F) -> Self::F; +} + +impl Default for LazySegtree { + fn default() -> Self { + Self::new(0) + } +} +impl LazySegtree { + pub fn new(n: usize) -> Self { + vec![F::identity_element(); n].into() + } +} +impl From::S>> for LazySegtree { + fn from(v: Vec<::S>) -> Self { + let n = v.len(); + let log = ceil_pow2(n as u32) as usize; + let size = 1 << log; + let mut d = vec![F::identity_element(); 2 * size]; + let lz = vec![F::identity_map(); size]; + d[size..(size + n)].clone_from_slice(&v); + let mut ret = LazySegtree { + n, + size, + log, + d, + lz, + }; + for i in (1..size).rev() { + ret.update(i); + } + ret + } +} + +impl LazySegtree { + pub fn set(&mut self, mut p: usize, x: ::S) { + assert!(p < self.n); + p += self.size; + for i in (1..=self.log).rev() { + self.push(p >> i); + } + self.d[p] = x; + for i in 1..=self.log { + self.update(p >> i); + } + } + + pub fn get(&mut self, mut p: usize) -> ::S { + assert!(p < self.n); + p += self.size; + for i in (1..=self.log).rev() { + self.push(p >> i); + } + self.d[p].clone() + } + + pub fn prod(&mut self, mut l: usize, mut r: usize) -> ::S { + assert!(l <= r && r <= self.n); + if l == r { + return F::identity_element(); + } + + l += self.size; + r += self.size; + + for i in (1..=self.log).rev() { + if ((l >> i) << i) != l { + self.push(l >> i); + } + if ((r >> i) << i) != r { + self.push(r >> i); + } + } + + let mut sml = F::identity_element(); + let mut smr = F::identity_element(); + while l < r { + if l & 1 != 0 { + sml = F::binary_operation(&sml, &self.d[l]); + l += 1; + } + if r & 1 != 0 { + r -= 1; + smr = F::binary_operation(&self.d[r], &smr); + } + l >>= 1; + r >>= 1; + } + + F::binary_operation(&sml, &smr) + } + + pub fn all_prod(&self) -> ::S { + self.d[1].clone() + } + + pub fn apply(&mut self, mut p: usize, f: F::F) { + assert!(p < self.n); + p += self.size; + for i in (1..=self.log).rev() { + self.push(p >> i); + } + self.d[p] = F::mapping(&f, &self.d[p]); + for i in 1..=self.log { + self.update(p >> i); + } + } + pub fn apply_range(&mut self, mut l: usize, mut r: usize, f: F::F) { + assert!(l <= r && r <= self.n); + if l == r { + return; + } + + l += self.size; + r += self.size; + + for i in (1..=self.log).rev() { + if ((l >> i) << i) != l { + self.push(l >> i); + } + if ((r >> i) << i) != r { + self.push((r - 1) >> i); + } + } + + { + let l2 = l; + let r2 = r; + while l < r { + if l & 1 != 0 { + self.all_apply(l, f.clone()); + l += 1; + } + if r & 1 != 0 { + r -= 1; + self.all_apply(r, f.clone()); + } + l >>= 1; + r >>= 1; + } + l = l2; + r = r2; + } + + for i in 1..=self.log { + if ((l >> i) << i) != l { + self.update(l >> i); + } + if ((r >> i) << i) != r { + self.update((r - 1) >> i); + } + } + } + + pub fn max_right(&mut self, mut l: usize, g: G) -> usize + where + G: Fn(::S) -> bool, + { + assert!(l <= self.n); + assert!(g(F::identity_element())); + if l == self.n { + return self.n; + } + l += self.size; + for i in (1..=self.log).rev() { + self.push(l >> i); + } + let mut sm = F::identity_element(); + while { + // do + while l % 2 == 0 { + l >>= 1; + } + if !g(F::binary_operation(&sm, &self.d[l])) { + while l < self.size { + self.push(l); + l *= 2; + let res = F::binary_operation(&sm, &self.d[l]); + if g(res.clone()) { + sm = res; + l += 1; + } + } + return l - self.size; + } + sm = F::binary_operation(&sm, &self.d[l]); + l += 1; + //while + { + let l = l as isize; + (l & -l) != l + } + } {} + self.n + } + + pub fn min_left(&mut self, mut r: usize, g: G) -> usize + where + G: Fn(::S) -> bool, + { + assert!(r <= self.n); + assert!(g(F::identity_element())); + if r == 0 { + return 0; + } + r += self.size; + for i in (1..=self.log).rev() { + self.push((r - 1) >> i); + } + let mut sm = F::identity_element(); + while { + // do + r -= 1; + while r > 1 && r % 2 != 0 { + r >>= 1; + } + if !g(F::binary_operation(&self.d[r], &sm)) { + while r < self.size { + self.push(r); + r = 2 * r + 1; + let res = F::binary_operation(&self.d[r], &sm); + if g(res.clone()) { + sm = res; + r -= 1; + } + } + return r + 1 - self.size; + } + sm = F::binary_operation(&self.d[r], &sm); + // while + { + let r = r as isize; + (r & -r) != r + } + } {} + 0 + } +} + +pub struct LazySegtree +where + F: MapMonoid, +{ + n: usize, + size: usize, + log: usize, + d: Vec<::S>, + lz: Vec, +} +impl LazySegtree +where + F: MapMonoid, +{ + fn update(&mut self, k: usize) { + self.d[k] = F::binary_operation(&self.d[2 * k], &self.d[2 * k + 1]); + } + fn all_apply(&mut self, k: usize, f: F::F) { + self.d[k] = F::mapping(&f, &self.d[k]); + if k < self.size { + self.lz[k] = F::composition(&f, &self.lz[k]); + } + } + fn push(&mut self, k: usize) { + self.all_apply(2 * k, self.lz[k].clone()); + self.all_apply(2 * k + 1, self.lz[k].clone()); + self.lz[k] = F::identity_map(); + } +} + +// TODO is it useful? +use std::fmt::{Debug, Error, Formatter, Write}; +impl Debug for LazySegtree +where + F: MapMonoid, + F::F: Debug, + ::S: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + for i in 0..self.log { + for j in 0..1 << i { + f.write_fmt(format_args!( + "{:?}[{:?}]\t", + self.d[(1 << i) + j], + self.lz[(1 << i) + j] + ))?; + } + f.write_char('\n')?; + } + for i in 0..self.size { + f.write_fmt(format_args!("{:?}\t", self.d[self.size + i]))?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::{LazySegtree, MapMonoid, Max}; + + struct MaxAdd; + impl MapMonoid for MaxAdd { + type M = Max; + type F = i32; + + fn identity_map() -> Self::F { + 0 + } + + fn mapping(&f: &i32, &x: &i32) -> i32 { + f + x + } + + fn composition(&f: &i32, &g: &i32) -> i32 { + f + g + } + } + + #[test] + fn test_max_add_lazy_segtree() { + let base = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3]; + let n = base.len(); + let mut segtree: LazySegtree = base.clone().into(); + check_segtree(&base, &mut segtree); + + let mut segtree = LazySegtree::::new(n); + let mut internal = vec![i32::min_value(); n]; + for i in 0..n { + segtree.set(i, base[i]); + internal[i] = base[i]; + check_segtree(&internal, &mut segtree); + } + + segtree.set(6, 5); + internal[6] = 5; + check_segtree(&internal, &mut segtree); + + segtree.apply(5, 1); + internal[5] += 1; + check_segtree(&internal, &mut segtree); + + segtree.set(6, 0); + internal[6] = 0; + check_segtree(&internal, &mut segtree); + + segtree.apply_range(3, 8, 2); + internal[3..8].iter_mut().for_each(|e| *e += 2); + check_segtree(&internal, &mut segtree); + } + + //noinspection DuplicatedCode + fn check_segtree(base: &[i32], segtree: &mut LazySegtree) { + let n = base.len(); + #[allow(clippy::needless_range_loop)] + for i in 0..n { + assert_eq!(segtree.get(i), base[i]); + } + for i in 0..=n { + for j in i..=n { + assert_eq!( + segtree.prod(i, j), + base[i..j].iter().max().copied().unwrap_or(i32::min_value()) + ); + } + } + assert_eq!( + segtree.all_prod(), + base.iter().max().copied().unwrap_or(i32::min_value()) + ); + for k in 0..=10 { + let f = |x| x < k; + for i in 0..=n { + assert_eq!( + Some(segtree.max_right(i, f)), + (i..=n) + .filter(|&j| f(base[i..j] + .iter() + .max() + .copied() + .unwrap_or(i32::min_value()))) + .max() + ); + } + for j in 0..=n { + assert_eq!( + Some(segtree.min_left(j, f)), + (0..=j) + .filter(|&i| f(base[i..j] + .iter() + .max() + .copied() + .unwrap_or(i32::min_value()))) + .min() + ); + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index e95c70c..77961cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ pub(crate) mod internal_type_traits; pub use dsu::Dsu; pub use fenwicktree::FenwickTree; +pub use lazysegtree::{LazySegtree, MapMonoid}; pub use math::{crt, floor_sum, inv_mod, pow_mod}; pub use maxflow::{Edge, MfGraph}; pub use mincostflow::MinCostFlowGraph; @@ -28,6 +29,7 @@ pub use modint::{ ModInt998244353, Modulus, RemEuclidU32, StaticModInt, }; pub use scc::SccGraph; +pub use segtree::{Additive, Max, Min, Monoid, Multiplicative, Segtree}; pub use string::{ lcp_array, lcp_array_arbitrary, suffix_array, suffix_array_arbitrary, suffix_array_manual, z_algorithm, z_algorithm_arbitrary, diff --git a/src/segtree.rs b/src/segtree.rs index 8b13789..b543aa3 100644 --- a/src/segtree.rs +++ b/src/segtree.rs @@ -1 +1,315 @@ +use crate::internal_bit::ceil_pow2; +use crate::internal_type_traits::{BoundedAbove, BoundedBelow, One, Zero}; +use std::cmp::{max, min}; +use std::convert::Infallible; +use std::marker::PhantomData; +use std::ops::{Add, Mul}; +// TODO Should I split monoid-related traits to another module? +pub trait Monoid { + type S: Clone; + fn identity() -> Self::S; + fn binary_operation(a: &Self::S, b: &Self::S) -> Self::S; +} + +pub struct Max(Infallible, PhantomData S>); +impl Monoid for Max +where + S: Copy + Ord + BoundedBelow, +{ + type S = S; + fn identity() -> Self::S { + S::min_value() + } + fn binary_operation(a: &Self::S, b: &Self::S) -> Self::S { + max(*a, *b) + } +} + +pub struct Min(Infallible, PhantomData S>); +impl Monoid for Min +where + S: Copy + Ord + BoundedAbove, +{ + type S = S; + fn identity() -> Self::S { + S::max_value() + } + fn binary_operation(a: &Self::S, b: &Self::S) -> Self::S { + min(*a, *b) + } +} + +pub struct Additive(Infallible, PhantomData S>); +impl Monoid for Additive +where + S: Copy + Add + Zero, +{ + type S = S; + fn identity() -> Self::S { + S::zero() + } + fn binary_operation(a: &Self::S, b: &Self::S) -> Self::S { + *a + *b + } +} + +pub struct Multiplicative(Infallible, PhantomData S>); +impl Monoid for Multiplicative +where + S: Copy + Mul + One, +{ + type S = S; + fn identity() -> Self::S { + S::one() + } + fn binary_operation(a: &Self::S, b: &Self::S) -> Self::S { + *a * *b + } +} + +impl Default for Segtree { + fn default() -> Self { + Segtree::new(0) + } +} +impl Segtree { + pub fn new(n: usize) -> Segtree { + vec![M::identity(); n].into() + } +} +impl From> for Segtree { + fn from(v: Vec) -> Self { + let n = v.len(); + let log = ceil_pow2(n as u32) as usize; + let size = 1 << log; + let mut d = vec![M::identity(); 2 * size]; + d[size..(size + n)].clone_from_slice(&v); + let mut ret = Segtree { n, size, log, d }; + for i in (1..size).rev() { + ret.update(i); + } + ret + } +} +impl Segtree { + pub fn set(&mut self, mut p: usize, x: M::S) { + assert!(p < self.n); + p += self.size; + self.d[p] = x; + for i in 1..=self.log { + self.update(p >> i); + } + } + + pub fn get(&self, p: usize) -> M::S { + assert!(p < self.n); + self.d[p + self.size].clone() + } + + pub fn prod(&self, mut l: usize, mut r: usize) -> M::S { + assert!(l <= r && r <= self.n); + let mut sml = M::identity(); + let mut smr = M::identity(); + l += self.size; + r += self.size; + + while l < r { + if l & 1 != 0 { + sml = M::binary_operation(&sml, &self.d[l]); + l += 1; + } + if r & 1 != 0 { + r -= 1; + smr = M::binary_operation(&self.d[r], &smr); + } + l >>= 1; + r >>= 1; + } + + M::binary_operation(&sml, &smr) + } + + pub fn all_prod(&self) -> M::S { + self.d[1].clone() + } + + pub fn max_right(&self, mut l: usize, f: F) -> usize + where + F: Fn(&M::S) -> bool, + { + assert!(l <= self.n); + assert!(f(&M::identity())); + if l == self.n { + return self.n; + } + l += self.size; + let mut sm = M::identity(); + while { + // do + while l % 2 == 0 { + l >>= 1; + } + if !f(&M::binary_operation(&sm, &self.d[l])) { + while l < self.size { + l *= 2; + let res = M::binary_operation(&sm, &self.d[l]); + if f(&res) { + sm = res; + l += 1; + } + } + return l - self.size; + } + sm = M::binary_operation(&sm, &self.d[l]); + l += 1; + // while + { + let l = l as isize; + (l & -l) != l + } + } {} + self.n + } + + pub fn min_left(&self, mut r: usize, f: F) -> usize + where + F: Fn(&M::S) -> bool, + { + assert!(r <= self.n); + assert!(f(&M::identity())); + if r == 0 { + return 0; + } + r += self.size; + let mut sm = M::identity(); + while { + // do + r -= 1; + while r > 1 && r % 2 == 1 { + r >>= 1; + } + if !f(&M::binary_operation(&self.d[r], &sm)) { + while r < self.size { + r = 2 * r + 1; + let res = M::binary_operation(&self.d[r], &sm); + if f(&res) { + sm = res; + r -= 1; + } + } + return r + 1 - self.size; + } + sm = M::binary_operation(&self.d[r], &sm); + // while + { + let r = r as isize; + (r & -r) != r + } + } {} + 0 + } + + fn update(&mut self, k: usize) { + self.d[k] = M::binary_operation(&self.d[2 * k], &self.d[2 * k + 1]); + } +} + +// Maybe we can use this someday +// ``` +// for i in 0..=self.log { +// for j in 0..1 << i { +// print!("{}\t", self.d[(1 << i) + j]); +// } +// println!(); +// } +// ``` + +pub struct Segtree +where + M: Monoid, +{ + // variable name is _n in original library + n: usize, + size: usize, + log: usize, + d: Vec, +} + +#[cfg(test)] +mod tests { + use crate::segtree::Max; + use crate::Segtree; + + #[test] + fn test_max_segtree() { + let base = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3]; + let n = base.len(); + let segtree: Segtree> = base.clone().into(); + check_segtree(&base, &segtree); + + let mut segtree = Segtree::>::new(n); + let mut internal = vec![i32::min_value(); n]; + for i in 0..n { + segtree.set(i, base[i]); + internal[i] = base[i]; + check_segtree(&internal, &segtree); + } + + segtree.set(6, 5); + internal[6] = 5; + check_segtree(&internal, &segtree); + + segtree.set(6, 0); + internal[6] = 0; + check_segtree(&internal, &segtree); + } + + //noinspection DuplicatedCode + fn check_segtree(base: &[i32], segtree: &Segtree>) { + let n = base.len(); + #[allow(clippy::needless_range_loop)] + for i in 0..n { + assert_eq!(segtree.get(i), base[i]); + } + for i in 0..=n { + for j in i..=n { + assert_eq!( + segtree.prod(i, j), + base[i..j].iter().max().copied().unwrap_or(i32::min_value()) + ); + } + } + assert_eq!( + segtree.all_prod(), + base.iter().max().copied().unwrap_or(i32::min_value()) + ); + for k in 0..=10 { + let f = |&x: &i32| x < k; + for i in 0..=n { + assert_eq!( + Some(segtree.max_right(i, f)), + (i..=n) + .filter(|&j| f(&base[i..j] + .iter() + .max() + .copied() + .unwrap_or(i32::min_value()))) + .max() + ); + } + for j in 0..=n { + assert_eq!( + Some(segtree.min_left(j, f)), + (0..=j) + .filter(|&i| f(&base[i..j] + .iter() + .max() + .copied() + .unwrap_or(i32::min_value()))) + .min() + ); + } + } + } +}