1use forge_foundation::{PhaseType, ZoneType};
2use serde::{Deserialize, Serialize};
3use std::collections::{BTreeMap, HashMap};
4
5use crate::ability::AbilityKey;
6use crate::card::card_damage_map::CardDamageMap;
7use crate::card::card_zone_table::CardZoneTable;
8use crate::ids::{CardId, PlayerId};
9use strum_macros::Display;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ZoneChangeRecord {
16 pub origin: ZoneType,
17 pub destination: ZoneType,
18 pub card: CardId,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize, Display)]
22#[allow(clippy::large_enum_variant)]
23pub enum AbilityValue {
24 Card(CardId),
25 Player(PlayerId),
26 Cards(Vec<CardId>),
27 Players(Vec<PlayerId>),
28 VoteMap(Vec<(String, Vec<PlayerId>)>),
29 SpellAbility(crate::spellability::SpellAbility),
30 CardZoneTable(CardZoneTable),
31 DamageMap(CardDamageMap),
32 CounterMap(BTreeMap<String, i32>),
33 String(String),
34 Int(i32),
35 Bool(bool),
36 Zone(ZoneType),
37 Phase(PhaseType),
38}
39
40impl Default for AbilityValue {
41 fn default() -> Self {
42 AbilityValue::String(String::new())
43 }
44}
45
46impl AbilityValue {
47 pub fn as_str(&self) -> &str {
48 match self {
49 AbilityValue::String(value) => value.as_str(),
50 _ => "",
51 }
52 }
53}
54
55impl std::ops::Deref for AbilityValue {
56 type Target = str;
57
58 fn deref(&self) -> &Self::Target {
59 self.as_str()
60 }
61}
62
63impl From<CardId> for AbilityValue {
64 fn from(value: CardId) -> Self {
65 AbilityValue::Card(value)
66 }
67}
68
69impl From<PlayerId> for AbilityValue {
70 fn from(value: PlayerId) -> Self {
71 AbilityValue::Player(value)
72 }
73}
74
75impl From<Vec<CardId>> for AbilityValue {
76 fn from(value: Vec<CardId>) -> Self {
77 AbilityValue::Cards(value)
78 }
79}
80
81impl From<Vec<PlayerId>> for AbilityValue {
82 fn from(value: Vec<PlayerId>) -> Self {
83 AbilityValue::Players(value)
84 }
85}
86
87impl From<Vec<(String, Vec<PlayerId>)>> for AbilityValue {
88 fn from(value: Vec<(String, Vec<PlayerId>)>) -> Self {
89 AbilityValue::VoteMap(value)
90 }
91}
92
93impl From<crate::spellability::SpellAbility> for AbilityValue {
94 fn from(value: crate::spellability::SpellAbility) -> Self {
95 AbilityValue::SpellAbility(value)
96 }
97}
98
99impl From<CardZoneTable> for AbilityValue {
100 fn from(value: CardZoneTable) -> Self {
101 AbilityValue::CardZoneTable(value)
102 }
103}
104
105impl From<CardDamageMap> for AbilityValue {
106 fn from(value: CardDamageMap) -> Self {
107 AbilityValue::DamageMap(value)
108 }
109}
110
111impl From<BTreeMap<String, i32>> for AbilityValue {
112 fn from(value: BTreeMap<String, i32>) -> Self {
113 AbilityValue::CounterMap(value)
114 }
115}
116
117impl From<String> for AbilityValue {
118 fn from(value: String) -> Self {
119 AbilityValue::String(value)
120 }
121}
122
123impl From<&str> for AbilityValue {
124 fn from(value: &str) -> Self {
125 AbilityValue::String(value.to_string())
126 }
127}
128
129impl From<&String> for AbilityValue {
130 fn from(value: &String) -> Self {
131 AbilityValue::String(value.clone())
132 }
133}
134
135impl From<i32> for AbilityValue {
136 fn from(value: i32) -> Self {
137 AbilityValue::Int(value)
138 }
139}
140
141impl From<bool> for AbilityValue {
142 fn from(value: bool) -> Self {
143 AbilityValue::Bool(value)
144 }
145}
146
147impl From<ZoneType> for AbilityValue {
148 fn from(value: ZoneType) -> Self {
149 AbilityValue::Zone(value)
150 }
151}
152
153impl From<PhaseType> for AbilityValue {
154 fn from(value: PhaseType) -> Self {
155 AbilityValue::Phase(value)
156 }
157}
158
159#[derive(Debug, Clone, Default, Serialize, Deserialize)]
163pub struct RunParams {
164 pub card: Option<CardId>,
165 pub card_lki: Option<CardId>,
166 pub cards: Option<Vec<CardId>>,
168 pub zone_changes: Option<Vec<ZoneChangeRecord>>,
170 pub change_zone_table: Option<CardZoneTable>,
172 pub origin: Option<ZoneType>,
173 pub destination: Option<ZoneType>,
174 pub destinations: Option<String>,
176 pub activator: Option<PlayerId>,
178 pub cause_player: Option<PlayerId>,
179 pub player: Option<PlayerId>,
180 pub phase: Option<PhaseType>,
181 pub damage_source: Option<CardId>,
182 pub damage_target_player: Option<PlayerId>,
183 pub damage_target_card: Option<CardId>,
184 pub target_player: Option<PlayerId>,
186 pub target_card: Option<CardId>,
187 pub damage_amount: Option<i32>,
188 pub is_combat_damage: Option<bool>,
189 pub first_time: Option<bool>,
191 pub fizzle: Option<bool>,
193 pub valiant: Option<bool>,
195 pub attacker: Option<CardId>,
196 pub attacked_player: Option<PlayerId>,
198 pub attacked_card: Option<CardId>,
199 pub other_attacker_ids: Option<Vec<CardId>>,
201 pub defenders_player_ids: Option<Vec<PlayerId>>,
203 pub defenders_card_ids: Option<Vec<CardId>>,
204 pub attacking_player: Option<PlayerId>,
206 pub defending_player: Option<PlayerId>,
207 pub spell_card: Option<CardId>,
208 pub spell_controller: Option<PlayerId>,
209 pub card2: Option<CardId>,
211 pub explored: Option<CardId>,
213 pub spell_ability: Option<crate::spellability::SpellAbility>,
215 pub source_sa: Option<crate::spellability::SpellAbility>,
217 pub ability_mana: Option<crate::spellability::SpellAbility>,
219 pub cause: Option<crate::spellability::SpellAbility>,
221 pub causer: Option<CardId>,
223 pub produced: Option<String>,
225 pub mode: Option<String>,
227 pub num: Option<i32>,
229 pub number: Option<i32>,
231 pub blocker: Option<CardId>,
234 pub blocked_attacker: Option<CardId>,
236 pub life_amount: Option<i32>,
238 pub counter_type: Option<String>,
240 pub counter_amount: Option<i32>,
242 pub attacker_ids: Option<Vec<CardId>>,
245 pub blocker_ids: Option<Vec<CardId>>,
247 pub original_controller: Option<PlayerId>,
249 pub mana_expend_amount: Option<i32>,
251 pub enlisted: Option<CardId>,
253 pub cause_card: Option<CardId>,
255 pub coin_flip_won: Option<bool>,
257 pub die_result: Option<i32>,
259 pub die_results: Option<Vec<i32>>,
261 pub natural_result: Option<i32>,
263 pub die_sides: Option<i32>,
265 pub num_attackers: Option<usize>,
267 pub exploited_card: Option<CardId>,
269 pub lki_p1p1_counters: Option<i32>,
272 pub lki_power: Option<i32>,
275 pub lki_toughness: Option<i32>,
278 pub cumulative_upkeep_paid: Option<bool>,
280 pub echo_paid: Option<bool>,
282 pub class_level: Option<i32>,
284 pub room_name: Option<String>,
286 pub crew_cards: Option<Vec<CardId>>,
288 pub championed_card: Option<CardId>,
290 pub source_card: Option<CardId>,
292 pub source_player: Option<PlayerId>,
294 pub object_card: Option<CardId>,
296 pub object_player: Option<PlayerId>,
298 pub counter_map: Option<BTreeMap<String, i32>>,
300 pub damage_map: Option<CardDamageMap>,
302 pub clash_won: Option<bool>,
304 pub card_state_name: Option<String>,
306 pub drawn_this_turn_snapshot: Option<i32>,
310 pub first_time_players: Option<Vec<PlayerId>>,
312 pub all_votes: Option<Vec<(String, Vec<PlayerId>)>>,
314 pub discarded_before: Option<Vec<CardId>>,
316 pub rolled_to_visit_attractions: Option<bool>,
318}
319
320impl RunParams {
321 pub fn add_common_trigger_objects(&self, sa: &mut crate::spellability::SpellAbility) {
322 if let Some(card_id) = self.card {
323 sa.set_triggering_object(crate::ability::AbilityKey::Card, card_id.0.to_string());
324 sa.set_triggering_object(crate::ability::AbilityKey::NewCard, card_id.0.to_string());
325 }
326 if let Some(card_id) = self.card_lki {
327 sa.set_triggering_object(crate::ability::AbilityKey::CardLKI, card_id.0.to_string());
328 }
329 if let Some(player_id) = self.activator.or(self.cause_player) {
330 sa.set_triggering_object(
331 crate::ability::AbilityKey::Activator,
332 player_id.0.to_string(),
333 );
334 }
335 if let Some(player_id) = self.player {
336 sa.set_triggering_object(crate::ability::AbilityKey::Player, player_id.0.to_string());
337 }
338 if let Some(player_id) = self.attacking_player {
339 sa.set_triggering_object(
340 crate::ability::AbilityKey::AttackingPlayer,
341 player_id.0.to_string(),
342 );
343 }
344 if let Some(player_id) = self.defending_player {
345 sa.set_triggering_object(
346 crate::ability::AbilityKey::DefendingPlayer,
347 player_id.0.to_string(),
348 );
349 }
350 if let Some(card_id) = self.causer.or(self.cause_card) {
351 sa.set_triggering_object(crate::ability::AbilityKey::Causer, card_id.0.to_string());
352 }
353 if let Some(card_id) = self.source_card.or(self.spell_card) {
354 sa.set_triggering_object(crate::ability::AbilityKey::Source, card_id.0.to_string());
355 }
356 if let Some(card_id) = self.attacker {
357 sa.set_triggering_object(crate::ability::AbilityKey::Attacker, card_id.0.to_string());
358 }
359 if let Some(card_id) = self.blocker {
360 sa.set_triggering_object(crate::ability::AbilityKey::Blocker, card_id.0.to_string());
361 }
362 if let Some(card_id) = self.attacked_card {
363 sa.set_triggering_object(crate::ability::AbilityKey::Attacked, card_id.0.to_string());
364 }
365 if let Some(player_id) = self.attacked_player {
366 sa.set_triggering_object(
367 crate::ability::AbilityKey::AttackedTarget,
368 player_id.0.to_string(),
369 );
370 }
371 if let Some(card_id) = self.target_card {
372 let value = card_id.0.to_string();
373 sa.set_triggering_object(crate::ability::AbilityKey::Target, &value);
374 sa.set_triggering_object(crate::ability::AbilityKey::TargetCard, &value);
375 }
376 if let Some(player_id) = self.target_player {
377 let value = player_id.0.to_string();
378 sa.set_triggering_object(crate::ability::AbilityKey::Target, &value);
379 sa.set_triggering_object(crate::ability::AbilityKey::TargetPlayer, &value);
380 }
381 if self.target_player.is_none() {
382 if let Some(player_id) = self.damage_target_player {
383 let value = player_id.0.to_string();
384 sa.set_triggering_object(crate::ability::AbilityKey::Target, &value);
385 sa.set_triggering_object(crate::ability::AbilityKey::TargetPlayer, &value);
386 }
387 }
388 if self.target_card.is_none() {
389 if let Some(card_id) = self.damage_target_card {
390 let value = card_id.0.to_string();
391 sa.set_triggering_object(crate::ability::AbilityKey::Target, &value);
392 sa.set_triggering_object(crate::ability::AbilityKey::TargetCard, &value);
393 }
394 }
395 if let Some(card_id) = self.explored {
396 sa.set_triggering_object(crate::ability::AbilityKey::Explored, card_id.0.to_string());
397 }
398 if let Some(cards) = self.cards.as_deref() {
399 let csv = cards
400 .iter()
401 .map(|card_id| card_id.0.to_string())
402 .collect::<Vec<_>>()
403 .join(",");
404 if !csv.is_empty() {
405 sa.set_triggering_object(crate::ability::AbilityKey::Cards, &csv);
406 }
407 }
408 if let Some(cards) = self.attacker_ids.as_deref() {
409 let csv = cards
410 .iter()
411 .map(|card_id| card_id.0.to_string())
412 .collect::<Vec<_>>()
413 .join(",");
414 if !csv.is_empty() {
415 sa.set_triggering_object(crate::ability::AbilityKey::Attackers, &csv);
416 }
417 }
418 if let Some(value) = self.life_amount {
419 sa.set_triggering_object(crate::ability::AbilityKey::LifeAmount, value.to_string());
420 }
421 if let Some(value) = self.natural_result {
422 sa.set_triggering_object(crate::ability::AbilityKey::NaturalResult, value.to_string());
423 }
424 if let Some(value) = self.card_state_name.as_deref() {
425 sa.set_triggering_object(crate::ability::AbilityKey::CardState, value);
426 }
427 if let Some(value) = self.room_name.as_deref() {
428 sa.set_triggering_object(crate::ability::AbilityKey::RoomName, value);
429 }
430 if let Some(value) = self.spell_ability.as_ref() {
431 sa.set_triggering_spell_ability("SpellAbility", value.clone());
432 }
433 if let Some(value) = self.source_sa.as_ref() {
434 sa.set_triggering_spell_ability("SourceSA", value.clone());
435 }
436 if let Some(value) = self.ability_mana.as_ref() {
437 sa.set_triggering_spell_ability("AbilityMana", value.clone());
438 }
439 if let Some(value) = self.cause.as_ref() {
440 sa.set_triggering_spell_ability("Cause", value.clone());
441 }
442 if let Some(results) = self.die_results.as_deref() {
443 let csv = results
444 .iter()
445 .map(i32::to_string)
446 .collect::<Vec<_>>()
447 .join(",");
448 if !csv.is_empty() {
449 sa.set_triggering_object(crate::ability::AbilityKey::Result, &csv);
450 }
451 } else if let Some(value) = self.die_result {
452 sa.set_triggering_object(crate::ability::AbilityKey::Result, value.to_string());
453 }
454 if let Some(value) = self.die_sides {
455 sa.set_triggering_object(crate::ability::AbilityKey::Sides, value.to_string());
456 }
457 if let Some(value) = self.number {
458 sa.set_triggering_object(crate::ability::AbilityKey::Number, value.to_string());
459 }
460 }
461
462 pub fn get_value(&self, key: AbilityKey) -> Option<AbilityValue> {
463 use AbilityKey::*;
464 match key {
465 AbilityMana => self.ability_mana.clone().map(AbilityValue::SpellAbility),
466 AllVotes => self.all_votes.clone().map(AbilityValue::VoteMap),
467 Activator => self
468 .activator
469 .or(self.cause_player)
470 .map(AbilityValue::Player),
471 Amount => self
472 .cards
473 .as_ref()
474 .map(|cards| AbilityValue::Int(cards.len() as i32))
475 .or_else(|| self.counter_amount.map(AbilityValue::Int))
476 .or_else(|| self.damage_amount.map(AbilityValue::Int))
477 .or_else(|| self.life_amount.map(AbilityValue::Int)),
478 Attacked => self
479 .attacked_card
480 .map(AbilityValue::Card)
481 .or_else(|| self.attacked_player.map(AbilityValue::Player)),
482 Attacker => self.attacker.map(AbilityValue::Card),
483 Attackers => self.attacker_ids.clone().map(AbilityValue::Cards),
484 AttackingPlayer => self
485 .attacking_player
486 .or(self.spell_controller)
487 .map(AbilityValue::Player),
488 Blocker => self.blocker.map(AbilityValue::Card),
489 Blockers => self.blocker_ids.clone().map(AbilityValue::Cards),
490 Card => self.card.map(AbilityValue::Card),
491 CardState => self.card_state_name.clone().map(AbilityValue::String),
492 Cards => self
493 .change_zone_table
494 .clone()
495 .map(AbilityValue::CardZoneTable)
496 .or_else(|| self.cards.clone().map(AbilityValue::Cards)),
497 CardLKI => self.card_lki.map(AbilityValue::Card),
498 Causer => self
499 .causer
500 .map(AbilityValue::Card)
501 .or_else(|| self.cause_card.map(AbilityValue::Card))
502 .or_else(|| {
503 self.cause
504 .as_ref()
505 .and_then(|sa| sa.source)
506 .map(AbilityValue::Card)
507 }),
508 Cause => self
509 .cause
510 .clone()
511 .map(AbilityValue::SpellAbility)
512 .or_else(|| self.cause_card.map(AbilityValue::Card))
513 .or_else(|| self.cards.clone().map(AbilityValue::Cards)),
514 Championed => self.championed_card.map(AbilityValue::Card),
515 ClassLevel => self.class_level.map(AbilityValue::Int),
516 CounterAmount | CounterNum | NewCounterAmount => {
517 self.counter_amount.map(AbilityValue::Int)
518 }
519 CounterMap => self.counter_map.clone().map(AbilityValue::CounterMap),
520 CounterType => self.counter_type.clone().map(AbilityValue::String),
521 Crew => self.crew_cards.clone().map(AbilityValue::Cards),
522 CumulativeUpkeepPaid => self.cumulative_upkeep_paid.map(AbilityValue::Bool),
523 DamageAmount | LifeGained | LifeAmount | Num | Number | PreventedAmount => self
524 .damage_amount
525 .or(self.life_amount)
526 .or(self.num)
527 .or(self.number)
528 .or(self.drawn_this_turn_snapshot)
529 .map(AbilityValue::Int),
530 DamageMap => self.damage_map.clone().map(AbilityValue::DamageMap),
531 DamageSource => self.damage_source.map(AbilityValue::Card),
532 DamageTarget | Target => self
533 .damage_target_card
534 .map(AbilityValue::Card)
535 .or_else(|| self.damage_target_player.map(AbilityValue::Player))
536 .or_else(|| self.target_card.map(AbilityValue::Card))
537 .or_else(|| self.target_player.map(AbilityValue::Player)),
538 Defenders => self
539 .defenders_card_ids
540 .clone()
541 .map(AbilityValue::Cards)
542 .or_else(|| self.defenders_player_ids.clone().map(AbilityValue::Players)),
543 DefendingPlayer => self.defending_player.map(AbilityValue::Player),
544 Destination => self
545 .destinations
546 .clone()
547 .map(AbilityValue::String)
548 .or_else(|| self.destination.map(AbilityValue::Zone)),
549 EchoPaid => self.echo_paid.map(AbilityValue::Bool),
550 Enlisted => self.enlisted.map(AbilityValue::Card),
551 Exploited => self.exploited_card.map(AbilityValue::Card),
552 Explored => self.explored.map(AbilityValue::Card),
553 Explorer => self.card.map(AbilityValue::Card),
554 FirstTime => self
555 .first_time_players
556 .clone()
557 .map(AbilityValue::Players)
558 .or_else(|| self.first_time.map(AbilityValue::Bool)),
559 Fizzle => self.fizzle.map(AbilityValue::Bool),
560 IsCombat | IsCombatDamage => self.is_combat_damage.map(AbilityValue::Bool),
561 LastStateBattlefield => self
562 .change_zone_table
563 .as_ref()
564 .map(|table| AbilityValue::Cards(table.last_state_battlefield().to_vec())),
565 LastStateGraveyard => self
566 .change_zone_table
567 .as_ref()
568 .map(|table| AbilityValue::Cards(table.last_state_graveyard().to_vec())),
569 Mana | Produced => self.produced.clone().map(AbilityValue::String),
570 Mode => self.mode.clone().map(AbilityValue::String),
571 NewCard => self.card2.map(AbilityValue::Card),
572 Object => self
573 .object_card
574 .map(AbilityValue::Card)
575 .or_else(|| self.object_player.map(AbilityValue::Player))
576 .or_else(|| self.card.map(AbilityValue::Card)),
577 OtherAttackers => self.other_attacker_ids.clone().map(AbilityValue::Cards),
578 Origin => self.origin.map(AbilityValue::Zone),
579 OriginalController => self.original_controller.map(AbilityValue::Player),
580 Phase => self.phase.map(AbilityValue::Phase),
581 Player => self.player.map(AbilityValue::Player),
582 Result | Won => self
583 .coin_flip_won
584 .map(AbilityValue::Bool)
585 .or_else(|| self.clash_won.map(AbilityValue::Bool))
586 .or_else(|| self.mode.clone().map(AbilityValue::String))
587 .or_else(|| self.die_result.map(AbilityValue::Int)),
588 NaturalResult => self.natural_result.map(AbilityValue::Int),
589 RoomName => self.room_name.clone().map(AbilityValue::String),
590 RolledToVisitAttractions => self.rolled_to_visit_attractions.map(AbilityValue::Bool),
591 Scheme => self.card.map(AbilityValue::Card),
592 Sides => self.die_sides.map(AbilityValue::Int),
593 Source => self
594 .source_card
595 .map(AbilityValue::Card)
596 .or_else(|| self.source_player.map(AbilityValue::Player))
597 .or_else(|| self.spell_card.map(AbilityValue::Card)),
598 SourceSA | SpellAbility | StackSa => self
599 .source_sa
600 .clone()
601 .or_else(|| self.spell_ability.clone())
602 .map(AbilityValue::SpellAbility),
603 Valiant => self.valiant.map(AbilityValue::Bool),
604 InternalTriggerTable => self
605 .change_zone_table
606 .clone()
607 .map(AbilityValue::CardZoneTable),
608 _ => None,
609 }
610 }
611
612 pub fn as_ability_map(&self) -> HashMap<AbilityKey, AbilityValue> {
613 crate::ability::ability_key::all_ability_keys()
614 .iter()
615 .filter_map(|key| self.get_value(*key).map(|value| (*key, value)))
616 .collect()
617 }
618
619 pub fn get(&self, key: AbilityKey) -> Option<AbilityValue> {
621 self.get_value(key)
622 }
623
624 pub fn get_card(&self, key: crate::ability::AbilityKey) -> Option<CardId> {
628 match self.get_value(key) {
629 Some(AbilityValue::Card(card)) => Some(card),
630 _ => None,
631 }
632 }
633
634 pub fn get_player(&self, key: crate::ability::AbilityKey) -> Option<PlayerId> {
636 match self.get_value(key) {
637 Some(AbilityValue::Player(player)) => Some(player),
638 _ => None,
639 }
640 }
641
642 pub fn get_amount(&self, key: crate::ability::AbilityKey) -> Option<i32> {
644 match self.get_value(key) {
645 Some(AbilityValue::Int(value)) => Some(value),
646 _ => None,
647 }
648 }
649
650 pub fn get_bool(&self, key: crate::ability::AbilityKey) -> Option<bool> {
652 use crate::ability::AbilityKey;
653 match key {
654 AbilityKey::IsCombatDamage => self.is_combat_damage,
655 AbilityKey::FirstTime => self.first_time,
656 AbilityKey::Valiant => self.valiant,
657 _ => None,
658 }
659 }
660
661 pub fn get_spell_ability(
663 &self,
664 key: crate::ability::AbilityKey,
665 ) -> Option<&crate::spellability::SpellAbility> {
666 use crate::ability::AbilityKey;
667 match key {
668 AbilityKey::SpellAbility => self.spell_ability.as_ref(),
669 AbilityKey::SourceSA => self.source_sa.as_ref(),
670 AbilityKey::AbilityMana => self.ability_mana.as_ref(),
671 AbilityKey::Cause => self.cause.as_ref(),
672 _ => None,
673 }
674 }
675
676 pub fn get_cards(&self, key: crate::ability::AbilityKey) -> Option<&[CardId]> {
678 use crate::ability::AbilityKey;
679 match key {
680 AbilityKey::Cards => self.cards.as_deref(),
681 AbilityKey::DamageTargets => self.cards.as_deref(),
682 AbilityKey::Attackers => self.attacker_ids.as_deref(),
683 AbilityKey::Blockers => self.blocker_ids.as_deref(),
684 AbilityKey::OtherAttackers => self.other_attacker_ids.as_deref(),
685 AbilityKey::Defenders => self.defenders_card_ids.as_deref(),
686 _ => None,
687 }
688 }
689
690 pub fn get_string(&self, key: crate::ability::AbilityKey) -> Option<&str> {
692 use crate::ability::AbilityKey;
693 match key {
694 AbilityKey::Produced => self.produced.as_deref(),
695 AbilityKey::Mode => self.mode.as_deref(),
696 _ => None,
697 }
698 }
699}