Skip to main content

nord_format/
fields.rs

1//! Field-level introspection over a `#[bitbody]`'s fields.
2//!
3//! Generated registries let callers inspect and edit declared fields without a
4//! second, manually synchronized list of names.
5
6use std::fmt::{self, Debug, Display, Formatter};
7
8use crate::bits::Packed;
9
10/// One decoded field of a panel.
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct FieldValue {
13    /// The field's full registry path, e.g. `center_panel.transpose`.
14    pub name: String,
15    /// Where the bits sit, as `LO..=HI` over the declaring body's bytes.
16    pub placement: &'static str,
17    /// The field's bits as they were *read*, shifted down to bit 0. Carries no type, so
18    /// it stays comparable across a retype.
19    pub raw: u64,
20    /// The bits the field's current value would *write*.
21    ///
22    /// Equal to [`raw`](Self::raw) on a panel decoded from bytes and not edited since —
23    /// decode and encode are inverses — so the two diverging is exactly the set of
24    /// pending changes. A `Default`-built panel has all-zero raw bytes, so every default
25    /// that encodes non-zero reads as pending.
26    pub bits: u64,
27    /// The decoded value's `Debug` rendering.
28    pub value: String,
29}
30
31impl Display for FieldValue {
32    /// `lower_part  0..=2  raw 0  Organ`
33    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
34        write!(
35            f,
36            "{:<22} {:<12} raw {:<11} {}",
37            self.name, self.placement, self.raw, self.value
38        )
39    }
40}
41
42/// What a field is, without an instance of the panel to read it from.
43#[derive(Clone)]
44pub struct FieldSpec {
45    /// The field's full registry path, e.g. `center_panel.transpose`.
46    pub name: String,
47    pub placement: &'static str,
48    /// Width of the field in bits.
49    pub width: u32,
50    /// Every value the field's type accepts, rendered as `set_field` spells them. Empty for a field too wide to enumerate — see [`ENUMERABLE_BITS`].
51    pub legal: fn() -> Vec<String>,
52    /// Which panel control this field is, from its type's
53    /// [`CONTROL`](crate::bits::Packed::CONTROL).
54    pub control: ControlKind,
55}
56
57impl FieldSpec {
58    /// The full path of the parameter this field morphs, for a [`ControlKind::Morph`]
59    /// that names one.
60    ///
61    /// The kind carries the parent's *sibling name*, since that is all the declaring body
62    /// knows; the path is this field's path with its last segment replaced, so a nested
63    /// body's prefix rides along.
64    pub fn morph_parent(&self) -> Option<String> {
65        let ControlKind::Morph { of: Some(parent) } = self.control else {
66            return None;
67        };
68        Some(match self.name.rsplit_once('.') {
69            Some((prefix, _)) => format!("{prefix}.{parent}"),
70            None => parent.to_string(),
71        })
72    }
73}
74
75/// What the panel puts under a reader's finger.
76///
77/// The registry already says where a field sits and which values it takes; this says what
78/// *kind* of thing it is, so a caller can choose a widget without a table of field names
79/// beside it. It comes from the field's type, so a field gets it right by being declared
80/// with the type that matches the control — a `bool` is a button, a [`Level`] is a knob.
81///
82/// [`Level`]: crate::components::Level
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
84pub enum ControlKind {
85    /// A two-state button. Its two states may have names — see the field's `legal` values.
86    Toggle,
87    /// A selector over a fixed set of named values.
88    Selector,
89    /// A continuous knob or slider, reading in `unit`.
90    Knob(Unit),
91    /// A knob whose musical zero is its centre, reading in `unit` either side.
92    Bipolar(Unit),
93    /// One or more drawbars, each `0..=8`, drawn as bars rather than numbers.
94    Drawbar {
95        /// How many bars the field holds, in register order. The Stage models give each
96        /// bar its own field and the Electro 5 packs a whole register into one, so this
97        /// is what tells a caller which it is holding.
98        bars: u8,
99        /// Where the field's **first** bar sits in the register: 1 is the leftmost bar,
100        /// 9 the rightmost of a nine-bar manual. A whole register starts at 1 and a
101        /// single Stage bar carries its own position.
102        ///
103        /// ⚠️ A position, not a pitch. Which harmonic each position draws is the organ
104        /// model's business — the B3's 16'/5⅓'/8' series is not the Vox's or the
105        /// Farfisa's, and the same nine positions serve all of them here — so labelling
106        /// them is for a caller that knows which model the field belongs to.
107        ///
108        /// `None` where the declaration does not place the bar in a register at all: the
109        /// Electro 5's bass manual, whose two bars nothing establishes the position of.
110        rank: Option<u8>,
111        /// Bits one bar occupies.
112        bits_per_bar: u8,
113        /// Which end of the field the first bar sits at. Only meaningful above one bar,
114        /// and the reason it is here: the Electro 5 packs its nine nibbles high-first
115        /// while the arpeggiator packs its steps low-first, so a caller reading one by
116        /// the other's convention draws the register mirrored.
117        order: PackedOrder,
118    },
119    /// The value a performance control morphs its parent parameter *to*. Belongs on that
120    /// parent's control, not on one of its own.
121    Morph {
122        /// The parent parameter's field name, as a sibling of this field — the full path
123        /// is this field's path with its last segment replaced, which is what
124        /// [`FieldSpec::morph_parent`] does.
125        ///
126        /// `None` where the body declares no parameter under the name this slot's own
127        /// name implies, so the slot stands alone until one is placed beside it.
128        of: Option<&'static str>,
129    },
130    /// A per-step pattern grid: `steps` steps of `bits_per_step` bits, the first step at
131    /// the `order` end.
132    Pattern {
133        steps: u8,
134        bits_per_step: u8,
135        order: PackedOrder,
136    },
137    /// An opaque id into one of the instrument's libraries.
138    Reference(Library),
139    /// A signed shift, reading in `unit`.
140    Shift(Unit),
141    /// An integer nothing has been claimed about — the default, and a standing invitation
142    /// to give the field a type that says more.
143    Number,
144}
145
146impl ControlKind {
147    /// Name the parent a morph slot morphs — the sibling field, not a path.
148    ///
149    /// `#[bitbody]` applies this from the field's own name, and only where the body
150    /// really declares that sibling. Every other kind is returned unchanged, so a field
151    /// named like a morph slot but typed as something else keeps what its type said.
152    pub const fn morphing(self, parent: &'static str) -> ControlKind {
153        match self {
154            ControlKind::Morph { .. } => ControlKind::Morph { of: Some(parent) },
155            other => other,
156        }
157    }
158
159    /// Place a drawbar in its register: `rank` 1 is the leftmost bar.
160    ///
161    /// Applied by `#[bitbody]` from a `…_N` field name, and ignored by every other kind
162    /// — a field whose name happens to end in a digit is not a drawbar unless its type
163    /// says so.
164    pub const fn ranked(self, rank: u8) -> ControlKind {
165        match self {
166            ControlKind::Drawbar {
167                bars,
168                bits_per_bar,
169                order,
170                ..
171            } => ControlKind::Drawbar {
172                bars,
173                rank: Some(rank),
174                bits_per_bar,
175                order,
176            },
177            other => other,
178        }
179    }
180}
181
182/// Which end of a field the first of its packed values sits at.
183///
184/// Only a field holding several values in one slot needs it — a drawbar register, a
185/// pattern row — and such a field cannot be drawn without it: read from the wrong end,
186/// the register comes out mirrored and looks like a plausible registration.
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
188pub enum PackedOrder {
189    /// The first value occupies the most significant bits.
190    HighFirst,
191    /// The first value occupies the least significant bits.
192    LowFirst,
193}
194
195/// One of the instrument's stored libraries — what a [`ControlKind::Reference`] id is an
196/// id *into*.
197///
198/// A file carries the id alone, so nothing but this says which catalogue resolves it.
199/// Listed here are the libraries something in a decoded body actually refers to; the
200/// instruments hold others (the live slots, the settings singleton) that no reference
201/// points at, and they are not here.
202#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
203pub enum Library {
204    /// Piano instruments (`.npno`).
205    Piano,
206    /// Sample instruments (`.nsmp`).
207    Sample,
208    /// The instrument's own programs.
209    Program,
210    /// Set lists, which name programs in turn.
211    SetList,
212}
213
214impl Library {
215    /// The library's numeric code.
216    ///
217    /// It exists because a const generic parameter cannot be an enum: a type that carries
218    /// its library — [`LibraryRefOf`](crate::components::LibraryRefOf) — carries this
219    /// instead and turns it back with [`from_code`](Self::from_code).
220    ///
221    /// The numbers are the object-class codes the instruments use on the wire, and
222    /// `nord-usb`'s `ObjectClass` takes its library codes from here, so a caller holding
223    /// both has one table.
224    pub const fn code(self) -> u8 {
225        match self {
226            Library::Piano => 1,
227            Library::Sample => 3,
228            Library::Program => 4,
229            Library::SetList => 5,
230        }
231    }
232
233    /// The library a [`code`](Self::code) names, or `None` — most bytes name none.
234    pub const fn from_code(code: u8) -> Option<Library> {
235        match code {
236            1 => Some(Library::Piano),
237            3 => Some(Library::Sample),
238            4 => Some(Library::Program),
239            5 => Some(Library::SetList),
240            _ => None,
241        }
242    }
243
244    /// The library a [`code`](Self::code) names, for the type-level parameter this
245    /// vocabulary exists to carry.
246    ///
247    /// ⚠️ Panics on a code naming none. That is a build failure only where the value is
248    /// *forced* at compile time, which the aliases in [`components`](crate::components)
249    /// are — a `LibraryRefOf<7>` nobody places compiles clean and fails when a field
250    /// declared with it asks for its control kind. Use [`from_code`](Self::from_code)
251    /// anywhere a code arrives at runtime.
252    pub const fn expect_code(code: u8) -> Library {
253        match Library::from_code(code) {
254            Some(library) => library,
255            None => panic!("no library has this code"),
256        }
257    }
258
259    /// The catalogue's name, singular, as a caller would put it in front of "id".
260    pub fn label(&self) -> &'static str {
261        match self {
262            Library::Piano => "piano",
263            Library::Sample => "sample",
264            Library::Program => "program",
265            Library::SetList => "set list",
266        }
267    }
268}
269
270/// What a control's reading is *in*.
271///
272/// ⚠️ Naming a unit is not a promise that the stored value converts to it. Several Nord
273/// knobs read in milliseconds or hertz over a curve no manual publishes; the unit says
274/// what the panel shows, and the type's own `Display` prints a converted reading only
275/// where the transform is known. See [`Unit::describes_a_known_transform`].
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
277pub enum Unit {
278    /// The panel's own `0..10`, which most Nord knobs read in.
279    Panel10,
280    Decibels,
281    Milliseconds,
282    Hertz,
283    /// Beats per minute, quarter-note.
284    Bpm,
285    /// A subdivision of the master clock — `1/8`, `1/4 T`.
286    ClockDivision,
287    Semitones,
288    Octaves,
289    /// A stereo position, left through centre to right.
290    Pan,
291    /// No unit: a count, an index, or a raw byte.
292    None,
293}
294
295impl Unit {
296    /// The unit's numeric code.
297    ///
298    /// It exists for the same reason [`Library::code`] does: a const generic parameter
299    /// cannot be an enum, so a type that carries its unit —
300    /// [`BipolarOf`](crate::components::BipolarOf) — carries this and turns it back with
301    /// [`expect_code`](Self::expect_code).
302    pub const fn code(self) -> u8 {
303        match self {
304            Unit::Panel10 => 0,
305            Unit::Decibels => 1,
306            Unit::Milliseconds => 2,
307            Unit::Hertz => 3,
308            Unit::Bpm => 4,
309            Unit::ClockDivision => 5,
310            Unit::Semitones => 6,
311            Unit::Octaves => 7,
312            Unit::Pan => 8,
313            Unit::None => 9,
314        }
315    }
316
317    /// The unit a [`code`](Self::code) names, for the type-level parameter this
318    /// vocabulary exists to carry.
319    ///
320    /// ⚠️ Panics on a code naming none, which is a build failure where the value is
321    /// forced at compile time — as the aliases in [`components`](crate::components) are.
322    pub const fn expect_code(code: u8) -> Unit {
323        match code {
324            0 => Unit::Panel10,
325            1 => Unit::Decibels,
326            2 => Unit::Milliseconds,
327            3 => Unit::Hertz,
328            4 => Unit::Bpm,
329            5 => Unit::ClockDivision,
330            6 => Unit::Semitones,
331            7 => Unit::Octaves,
332            8 => Unit::Pan,
333            9 => Unit::None,
334            _ => panic!("no unit has this code"),
335        }
336    }
337
338    /// Whether a value in this unit can be *computed* from the stored one.
339    ///
340    /// False for the units where the panel's curve is not published — a caller that wants
341    /// to label an axis may still use the unit, but must print the stored value.
342    pub fn describes_a_known_transform(&self) -> bool {
343        matches!(
344            self,
345            Unit::Panel10 | Unit::Decibels | Unit::Semitones | Unit::Octaves | Unit::Pan
346        )
347    }
348}
349
350/// The widest field whose legal values are enumerated. Above it a field is spelled by its
351/// stored bits, since walking every pattern would mean millions of strings.
352pub const ENUMERABLE_BITS: u32 = 12;
353
354/// Why a field could not be set.
355#[derive(Debug, Clone, PartialEq, Eq)]
356pub enum FieldError {
357    UnknownField {
358        panel: &'static str,
359        name: String,
360    },
361    BadValue {
362        field: &'static str,
363        given: String,
364        legal: Vec<String>,
365    },
366}
367
368impl Display for FieldError {
369    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
370        match self {
371            FieldError::UnknownField { panel, name } => {
372                write!(f, "{panel} has no field {name:?}")
373            }
374            FieldError::BadValue {
375                field,
376                given,
377                legal,
378            } => {
379                write!(f, "{given:?} is not a value of {field}")?;
380                match legal.len() {
381                    // Too wide to have named values; the stored bits are its only
382                    // spelling.
383                    0 => write!(f, " (accepts the stored bits, decimal or 0x…)"),
384                    n if n > 12 => write!(f, " (accepts {} .. {})", legal[0], legal[n - 1]),
385                    _ => write!(f, " (accepts {})", legal.join(", ")),
386                }
387            }
388        }
389    }
390}
391
392impl std::error::Error for FieldError {}
393
394/// The generated field registry behind an entity, where its body declares one.
395///
396/// `#[bitbody]` generates these three methods on every body with public
397/// fields; this trait is the same surface behind one name, so a caller can
398/// list and set fields without naming the body type.
399/// [`Entity::registry`](crate::Entity::registry) is where one comes from —
400/// a body joins by being declared there, and every consumer sees it at once.
401pub trait Registry {
402    /// Every settable field, described under its full path.
403    fn fields(&self) -> Vec<Field>;
404    /// Every registered field's current value, in declaration order.
405    fn field_values(&self) -> Vec<FieldValue>;
406    /// Set one field by its full path.
407    fn set_field(&mut self, path: &str, value: &str) -> Result<(), FieldError>;
408}
409
410/// One settable field of a body, addressed the way `--set` addresses it.
411pub struct Field {
412    /// The field's full registry path, e.g. `center_panel.transpose`.
413    pub path: String,
414    pub spec: FieldSpec,
415    /// What the field currently holds, spelled the way `set_field` takes it.
416    /// Feeding this straight back is always a no-op.
417    pub value: String,
418    /// The same value as `nord inspect` renders it. Differs from `value` only for a
419    /// field too wide to have named values, where the rendering is a list and the
420    /// spelling is the stored bits.
421    pub display: String,
422}
423
424/// Every value of `T` that fits a `LO..=HI` field, in stored order, asked of the type
425/// itself rather than kept in a second list beside it.
426pub fn legal_values<T: Packed + Debug>(width: u32) -> Vec<String> {
427    if width > ENUMERABLE_BITS {
428        return Vec::new();
429    }
430    let mut seen = Vec::new();
431    for bits in 0..(1u64 << width) {
432        if let Ok(v) = T::from_bits(bits) {
433            let rendered = format!("{v:?}");
434            if !seen.contains(&rendered) {
435                seen.push(rendered);
436            }
437        }
438    }
439    seen
440}
441
442/// Parse a field's value out of the way the field prints it.
443///
444/// **The rendering is the vocabulary**: this walks the field's own bit patterns and takes
445/// the one whose `Debug` matches, so a type gets string parsing from its `Debug` alone,
446/// and a value outside its range has no pattern to match and fails here rather than being
447/// clamped.
448///
449/// ⚠️ An unexplained value can therefore only be written by *naming* it as unexplained: a
450/// sparse enum renders an unrecognized `9` as `Unknown(9)`, so a bare `9` matches nothing
451/// and `Unknown(9)` is the only spelling.
452pub fn parse_field<T: Packed + Debug>(width: u32, given: &str) -> Result<T, FieldError> {
453    let wanted = normalize(given);
454    // A truth word for a `bool` field: its `Debug` is `true`/`false`, which no numeric
455    // field renders, so trying the canonical spelling second cannot collide.
456    let alias = match wanted.as_str() {
457        "on" | "yes" | "1" => Some("true"),
458        "off" | "no" | "0" => Some("false"),
459        _ => None,
460    };
461
462    if width <= ENUMERABLE_BITS {
463        for bits in 0..(1u64 << width) {
464            let Ok(v) = T::from_bits(bits) else { continue };
465            let rendered = normalize(&format!("{v:?}"));
466            if rendered == wanted || Some(rendered.as_str()) == alias {
467                return Ok(v);
468            }
469        }
470    } else if let Some(bits) = stored_value(&wanted) {
471        // Wide fields use stored bits; for a drawbar block the hex digits are its bars.
472        // Check before decoding: storage-backed implementations may cast and truncate.
473        if width >= 64 || bits < (1u64 << width) {
474            if let Ok(v) = T::from_bits(bits) {
475                return Ok(v);
476            }
477        }
478    }
479    Err(FieldError::BadValue {
480        // Filled in by the caller, which knows the field's name.
481        field: "",
482        given: given.to_string(),
483        legal: legal_values::<T>(width),
484    })
485}
486
487/// Case-folded, `+`-stripped, whitespace-trimmed: `+5`, `5` and ` 5 ` are one value, and
488/// so are `Organ` and `organ`.
489fn normalize(s: &str) -> String {
490    s.trim()
491        .trim_start_matches('+')
492        .to_ascii_lowercase()
493        .to_string()
494}
495
496/// A field's stored bits, written decimal or `0x`-prefixed. Already normalized.
497fn stored_value(s: &str) -> Option<u64> {
498    match s.strip_prefix("0x") {
499        Some(hex) => u64::from_str_radix(hex, 16).ok(),
500        None => s.parse().ok(),
501    }
502}
503
504/// How a field of this width spells its current value back to a caller.
505///
506/// Narrow fields are named — `Organ`, `-5`, `true` — and that name is what `--set` takes.
507/// A field too wide to enumerate has no name, so its stored bits are the spelling, and
508/// `raw` is exactly those bits.
509pub fn settable_form(width: u32, debug: &str, raw: u64) -> String {
510    if width <= ENUMERABLE_BITS {
511        debug.to_string()
512    } else {
513        format!("{raw:#x}")
514    }
515}
516
517impl FieldError {
518    /// Attach the field's name to an error raised before it was known.
519    pub fn at(self, field: &'static str) -> Self {
520        match self {
521            FieldError::BadValue { given, legal, .. } => FieldError::BadValue {
522                field,
523                given,
524                legal,
525            },
526            other => other,
527        }
528    }
529}
530
531#[cfg(test)]
532mod tests {
533    use super::*;
534    use crate::components::MorphTarget;
535    use crate::formats::ne5::{Level, Transpose};
536
537    /// A refinement carries what the declaration site knows and the type cannot. It is
538    /// keyed on the kind, so a field whose *name* looks like a morph slot or a drawbar
539    /// but whose type says otherwise keeps what its type said.
540    #[test]
541    fn a_refinement_only_reaches_the_kind_it_is_for() {
542        assert_eq!(
543            ControlKind::Morph { of: None }.morphing("organ_a_volume"),
544            ControlKind::Morph {
545                of: Some("organ_a_volume")
546            }
547        );
548        let bar = |rank| ControlKind::Drawbar {
549            bars: 1,
550            rank,
551            bits_per_bar: 4,
552            order: PackedOrder::HighFirst,
553        };
554        assert_eq!(bar(None).ranked(7), bar(Some(7)));
555
556        let knob = ControlKind::Knob(Unit::Panel10);
557        assert_eq!(knob.morphing("delay_tempo"), knob);
558        assert_eq!(knob.ranked(2), knob);
559    }
560
561    /// The kind names the parent as a sibling; the path is the field's own, one segment
562    /// swapped, so a nested body's prefix rides along.
563    #[test]
564    fn a_morph_slot_resolves_its_parents_full_path() {
565        let spec = |name: &str| FieldSpec {
566            name: name.to_string(),
567            placement: "0..=7",
568            width: 8,
569            legal: || Vec::new(),
570            control: <MorphTarget as Packed>::CONTROL.morphing("drawbar_1"),
571        };
572        assert_eq!(
573            spec("organ_a.drawbar_1_wheel").morph_parent().as_deref(),
574            Some("organ_a.drawbar_1"),
575        );
576        assert_eq!(
577            spec("drawbar_1_wheel").morph_parent().as_deref(),
578            Some("drawbar_1"),
579        );
580        // A slot with no parameter beside it stands alone.
581        let mut orphan = spec("drawbar_1_wheel");
582        orphan.control = <MorphTarget as Packed>::CONTROL;
583        assert_eq!(orphan.morph_parent(), None);
584    }
585
586    #[test]
587    fn a_value_is_parsed_out_of_the_way_it_prints() {
588        let v: Transpose = parse_field(4, "-5").unwrap();
589        assert_eq!(v.inner(), -5);
590        // The bias is the type's business, not the caller's: -5 stores as 1.
591        assert_eq!(<Transpose as Packed>::to_bits(&v), 1);
592    }
593
594    #[test]
595    fn a_leading_plus_and_stray_space_are_the_same_value() {
596        for spelling in ["+3", "3", " 3 "] {
597            assert_eq!(parse_field::<Transpose>(4, spelling).unwrap().inner(), 3);
598        }
599    }
600
601    /// Out of range has no bit pattern to match, so it cannot reach an encode.
602    #[test]
603    fn a_value_outside_the_types_range_is_refused() {
604        let err = parse_field::<Transpose>(4, "9")
605            .unwrap_err()
606            .at("transpose");
607        assert!(
608            err.to_string().contains("not a value of transpose"),
609            "{err}"
610        );
611    }
612
613    #[test]
614    fn a_bool_takes_the_words_people_actually_type() {
615        for yes in ["true", "on", "yes", "1"] {
616            assert!(parse_field::<bool>(1, yes).unwrap(), "{yes}");
617        }
618        for no in ["false", "off", "no", "0"] {
619            assert!(!parse_field::<bool>(1, no).unwrap(), "{no}");
620        }
621    }
622
623    #[test]
624    fn a_wide_numeric_value_must_fit_its_declared_width() {
625        assert!(parse_field::<u16>(16, "70000").is_err());
626        assert!(parse_field::<u32>(32, "4294967296").is_err());
627        assert_eq!(
628            parse_field::<u64>(64, "18446744073709551615").unwrap(),
629            u64::MAX
630        );
631    }
632
633    /// A wide numeric field enumerates, so `--fields` can still say what it takes.
634    #[test]
635    fn legal_values_come_from_the_type() {
636        assert_eq!(legal_values::<bool>(1), vec!["false", "true"]);
637        let levels = legal_values::<Level>(7);
638        assert_eq!(levels.len(), 128);
639        assert_eq!(levels.last().unwrap(), "127");
640    }
641
642    /// The message has to name a way forward, or it is just a rejection.
643    #[test]
644    fn the_error_lists_a_short_value_set_and_ranges_a_long_one() {
645        let short = FieldError::BadValue {
646            field: "split",
647            given: "maybe".into(),
648            legal: vec!["false".into(), "true".into()],
649        };
650        assert!(short.to_string().contains("accepts false, true"));
651
652        let long = FieldError::BadValue {
653            field: "gain",
654            given: "200".into(),
655            legal: (0..128).map(|n| n.to_string()).collect(),
656        };
657        assert!(long.to_string().contains("accepts 0 .. 127"), "{long}");
658    }
659}