use crate::expressions::{nil, ExpOp, ExpType, Expression, ExpressionArgument, MODIFY};
use crate::operations::cdt_context::{CdtContext, CtxType};
use crate::operations::maps::{map_write_op, CdtMapOpType, ToMapReturnTypeBitmask};
use crate::{MapPolicy, MapReturnType, Value};
pub(crate) const MODULE: i64 = 0;
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn put(
policy: &MapPolicy,
key: Expression,
value: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let op = map_write_op(policy, false);
let args: Vec<ExpressionArgument> = if op as u8 == CdtMapOpType::Replace as u8 {
vec![
ExpressionArgument::Context(ctx.to_vec()),
ExpressionArgument::Value(Value::from(op as u8)),
ExpressionArgument::FilterExpression(key),
ExpressionArgument::FilterExpression(value),
]
} else {
vec![
ExpressionArgument::Context(ctx.to_vec()),
ExpressionArgument::Value(Value::from(op as u8)),
ExpressionArgument::FilterExpression(key),
ExpressionArgument::FilterExpression(value),
ExpressionArgument::Value(Value::from(policy.order as u8)),
]
};
add_write(bin, ctx, args)
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn put_items(
policy: &MapPolicy,
map: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let op = map_write_op(policy, true);
let args: Vec<ExpressionArgument> = if op as u8 == CdtMapOpType::Replace as u8 {
vec![
ExpressionArgument::Context(ctx.to_vec()),
ExpressionArgument::Value(Value::from(op as u8)),
ExpressionArgument::FilterExpression(map),
]
} else {
vec![
ExpressionArgument::Context(ctx.to_vec()),
ExpressionArgument::Value(Value::from(op as u8)),
ExpressionArgument::FilterExpression(map),
ExpressionArgument::Value(Value::from(policy.order as u8)),
]
};
add_write(bin, ctx, args)
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn increment(
policy: &MapPolicy,
key: Expression,
incr: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::Increment as u8)),
ExpressionArgument::FilterExpression(key),
ExpressionArgument::FilterExpression(incr),
ExpressionArgument::Context(ctx.to_vec()),
ExpressionArgument::Value(Value::from(policy.order as u8)),
];
add_write(bin, ctx, args)
}
pub fn clear(bin: Expression, ctx: &[CdtContext]) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::Clear as u8)),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_key<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
key: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByKey as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(key),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_key_list<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
keys: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveKeyList as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(keys),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_key_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
key_begin: Option<Expression>,
key_end: Option<Expression>,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let mut args = vec![
ExpressionArgument::Context(ctx.to_vec()),
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByKeyInterval as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
];
if let Some(val_beg) = key_begin {
args.push(ExpressionArgument::FilterExpression(val_beg));
} else {
args.push(ExpressionArgument::FilterExpression(nil()));
}
if let Some(val_end) = key_end {
args.push(ExpressionArgument::FilterExpression(val_end));
}
add_write(bin, ctx, args)
}
pub fn remove_by_key_relative_index_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
key: Expression,
index: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByKeyRelIndexRange as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(key),
ExpressionArgument::FilterExpression(index),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_key_relative_index_range_count<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
key: Expression,
index: Expression,
count: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByKeyRelIndexRange as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(key),
ExpressionArgument::FilterExpression(index),
ExpressionArgument::FilterExpression(count),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_value<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
value: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByValue as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(value),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_value_list<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
values: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveValueList as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(values),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_value_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
value_begin: Option<Expression>,
value_end: Option<Expression>,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let mut args = vec![
ExpressionArgument::Context(ctx.to_vec()),
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByValueInterval as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
];
if let Some(val_beg) = value_begin {
args.push(ExpressionArgument::FilterExpression(val_beg));
} else {
args.push(ExpressionArgument::FilterExpression(nil()));
}
if let Some(val_end) = value_end {
args.push(ExpressionArgument::FilterExpression(val_end));
}
add_write(bin, ctx, args)
}
pub fn remove_by_value_relative_rank_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
value: Expression,
rank: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByValueRelRankRange as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(value),
ExpressionArgument::FilterExpression(rank),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_value_relative_rank_range_count<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
value: Expression,
rank: Expression,
count: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByValueRelRankRange as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(value),
ExpressionArgument::FilterExpression(rank),
ExpressionArgument::FilterExpression(count),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_index<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
index: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByIndex as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(index),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_index_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
index: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByIndexRange as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(index),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_index_range_count<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
index: Expression,
count: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByIndexRange as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(index),
ExpressionArgument::FilterExpression(count),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_rank<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
rank: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByRank as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(rank),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_rank_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
rank: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByRankRange as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(rank),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn remove_by_rank_range_count<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
rank: Expression,
count: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::RemoveByRankRange as u8)),
ExpressionArgument::Value(Value::from(return_type.to_bitmask())),
ExpressionArgument::FilterExpression(rank),
ExpressionArgument::FilterExpression(count),
ExpressionArgument::Context(ctx.to_vec()),
];
add_write(bin, ctx, args)
}
pub fn size(bin: Expression, ctx: &[CdtContext]) -> Expression {
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::Size as u8)),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, ExpType::INT, args)
}
pub fn get_by_key<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
value_type: ExpType,
key: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByKey as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(key),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, value_type, args)
}
pub fn get_by_key_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
key_begin: Option<Expression>,
key_end: Option<Expression>,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let mut args = vec![
ExpressionArgument::Context(ctx.to_vec()),
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByKeyInterval as u8)),
ExpressionArgument::Value(Value::from(return_type)),
];
if let Some(val_beg) = key_begin {
args.push(ExpressionArgument::FilterExpression(val_beg));
} else {
args.push(ExpressionArgument::FilterExpression(nil()));
}
if let Some(val_end) = key_end {
args.push(ExpressionArgument::FilterExpression(val_end));
}
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_key_list<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
keys: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByKeyList as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(keys),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_key_relative_index_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
key: Expression,
index: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByKeyRelIndexRange as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(key),
ExpressionArgument::FilterExpression(index),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_key_relative_index_range_count<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
key: Expression,
index: Expression,
count: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByKeyRelIndexRange as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(key),
ExpressionArgument::FilterExpression(index),
ExpressionArgument::FilterExpression(count),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_value<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
value: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByValue as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(value),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_value_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
value_begin: Option<Expression>,
value_end: Option<Expression>,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let mut args = vec![
ExpressionArgument::Context(ctx.to_vec()),
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByValueInterval as u8)),
ExpressionArgument::Value(Value::from(return_type)),
];
if let Some(val_beg) = value_begin {
args.push(ExpressionArgument::FilterExpression(val_beg));
} else {
args.push(ExpressionArgument::FilterExpression(nil()));
}
if let Some(val_end) = value_end {
args.push(ExpressionArgument::FilterExpression(val_end));
}
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_value_list<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
values: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByValueList as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(values),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_value_relative_rank_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
value: Expression,
rank: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByValueRelRankRange as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(value),
ExpressionArgument::FilterExpression(rank),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_value_relative_rank_range_count<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
value: Expression,
rank: Expression,
count: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByValueRelRankRange as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(value),
ExpressionArgument::FilterExpression(rank),
ExpressionArgument::FilterExpression(count),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_index<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
value_type: ExpType,
index: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByIndex as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(index),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, value_type, args)
}
pub fn get_by_index_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
index: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByIndexRange as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(index),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_index_range_count<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
index: Expression,
count: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByIndexRange as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(index),
ExpressionArgument::FilterExpression(count),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_rank<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
value_type: ExpType,
rank: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByRank as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(rank),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, value_type, args)
}
pub fn get_by_rank_range<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
rank: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByRankRange as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(rank),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, get_value_type(return_type), args)
}
pub fn get_by_rank_range_count<TMR: ToMapReturnTypeBitmask>(
return_type: TMR,
rank: Expression,
count: Expression,
bin: Expression,
ctx: &[CdtContext],
) -> Expression {
let return_type = return_type.to_bitmask();
let args = vec![
ExpressionArgument::Value(Value::from(CdtMapOpType::GetByRankRange as u8)),
ExpressionArgument::Value(Value::from(return_type)),
ExpressionArgument::FilterExpression(rank),
ExpressionArgument::FilterExpression(count),
ExpressionArgument::Context(ctx.to_vec()),
];
add_read(bin, get_value_type(return_type), args)
}
pub(crate) fn add_read(
bin: Expression,
return_type: ExpType,
arguments: Vec<ExpressionArgument>,
) -> Expression {
Expression {
cmd: Some(ExpOp::Call),
val: None,
bin: Some(Box::new(bin)),
flags: Some(MODULE),
module: Some(return_type),
exps: None,
arguments: Some(arguments),
bytes: None,
}
}
pub(crate) fn add_write(
bin: Expression,
ctx: &[CdtContext],
arguments: Vec<ExpressionArgument>,
) -> Expression {
let return_type = if ctx.is_empty() || (ctx[0].id & CtxType::ListIndex as u8) == 0 {
ExpType::MAP
} else {
ExpType::LIST
};
Expression {
cmd: Some(ExpOp::Call),
val: None,
bin: Some(Box::new(bin)),
flags: Some(MODULE | MODIFY),
module: Some(return_type),
exps: None,
arguments: Some(arguments),
bytes: None,
}
}
pub(crate) fn get_value_type(return_type: i64) -> ExpType {
let t = return_type & !(MapReturnType::Inverted as i64);
match t {
t if t == MapReturnType::Index as i64
|| t == MapReturnType::ReverseIndex as i64
|| t == MapReturnType::Rank as i64
|| t == MapReturnType::ReverseRank as i64 =>
{
ExpType::LIST
}
t if t == MapReturnType::Count as i64 => ExpType::INT,
t if t == MapReturnType::Key as i64 || t == MapReturnType::Value as i64 => ExpType::LIST,
t if t == MapReturnType::KeyValue as i64
|| t == MapReturnType::OrderedMap as i64
|| t == MapReturnType::UnorderedMap as i64 =>
{
ExpType::MAP
}
t if t == MapReturnType::Exists as i64 => ExpType::BOOL,
_ => panic!("Invalid MapReturnType: {}", return_type),
}
}