Skip to main content

caixa_ast/
span.rs

1//! Byte-offset spans — minimal and cheap. Line/column are computed on demand.
2
3use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7/// A half-open byte range `[start, end)` into some source string.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
9pub struct Span {
10    pub start: u32,
11    pub end: u32,
12}
13
14impl Span {
15    #[must_use]
16    pub const fn new(start: u32, end: u32) -> Self {
17        Self { start, end }
18    }
19
20    #[must_use]
21    pub const fn point(offset: u32) -> Self {
22        Self {
23            start: offset,
24            end: offset,
25        }
26    }
27
28    /// Byte-width of the half-open range `[start, end)`. `pub const fn` —
29    /// `u32::saturating_sub` is const-stable since Rust 1.47, well before
30    /// this workspace's 1.89 MSRV floor, so the promotion is a body-
31    /// preserving type-signature widening. Matches the sibling
32    /// [`Self::new`] / [`Self::point`] / [`Self::contains`] /
33    /// [`Self::union`] `pub const fn` shape on the same [`Span`] primitive
34    /// — every downstream consumer that wants a compile-time span-width
35    /// fixture (a `const WIDTH: u32 = SPAN.len();` LSP hover-registry
36    /// entry, a per-diagnostic const-context width oracle a future
37    /// admission webhook consults, a compile-time span-partition truth
38    /// table the caixa-fmt trivia-owner resolver keys off) now reads
39    /// through one substrate-primitive const dispatch rather than being
40    /// forced onto the runtime code path.
41    #[must_use]
42    pub const fn len(self) -> u32 {
43        self.end.saturating_sub(self.start)
44    }
45
46    /// Half-open emptiness predicate — `true` iff `self.start == self.end`.
47    /// `pub const fn` — folds onto the sibling [`Self::len`] `pub const
48    /// fn` promotion (integer equality is const in Rust since long before
49    /// this workspace's 1.89 MSRV floor). Matches every other accessor /
50    /// predicate on this [`Span`] primitive's const-eval surface; only
51    /// the fundamentally-runtime-only `slice(&str)` method (string-slice
52    /// indexing outside const-eval) remains `pub fn`.
53    #[must_use]
54    pub const fn is_empty(self) -> bool {
55        self.len() == 0
56    }
57
58    /// The smallest span covering both. Useful for building a list node's
59    /// span from its children — every parser code path that composes a
60    /// parent span from its immediate child boundaries reads through this
61    /// (`open.union(close)` on a delimited list, `head.union(target.span)`
62    /// on a quote-form target, and every downstream trivia-owner /
63    /// diagnostic-aggregator / fmt-region parent-span builder). `pub const
64    /// fn` — the body reaches for `u32::min` / `u32::max`, both const
65    /// stable since Rust 1.83 (well before this workspace's 1.89 MSRV
66    /// floor), so the promotion is a body-preserving type-signature
67    /// widening. Matches the sibling [`Self::new`] / [`Self::point`] /
68    /// [`Self::contains`] `pub const fn` shape on the same [`Span`]
69    /// primitive — every downstream consumer that wants a compile-time
70    /// span-composition fixture (a `const OUTER: Span = INNER1.union(
71    /// INNER2);` LSP hover-registry entry, a per-diagnostic const-context
72    /// parent-span oracle a future admission webhook consults, a
73    /// compile-time span-partition truth table the caixa-fmt trivia-owner
74    /// resolver keys off) now reads through one substrate-primitive const
75    /// dispatch rather than being forced onto the runtime code path.
76    #[must_use]
77    pub const fn union(self, other: Span) -> Span {
78        // Body-preserving `Ord::min` / `Ord::max` open-coding: the trait
79        // dispatch is not yet stable in `const` context (rust-lang/rust
80        // #143874), so the pub-const-fn promotion reads through inline
81        // `if`/`else` on the same `u32 < u32` / `u32 > u32` comparisons
82        // the primitive-integer inherent methods lower to.
83        let start = if self.start < other.start {
84            self.start
85        } else {
86            other.start
87        };
88        let end = if self.end > other.end {
89            self.end
90        } else {
91            other.end
92        };
93        Span { start, end }
94    }
95
96    #[must_use]
97    pub fn slice<'a>(self, src: &'a str) -> &'a str {
98        let start = self.start as usize;
99        let end = self.end as usize;
100        if start >= src.len() {
101            ""
102        } else {
103            let end = end.min(src.len());
104            &src[start..end]
105        }
106    }
107
108    /// Byte-offset half-open containment predicate every consumer that
109    /// keys off an author-authored source position (LSP hover
110    /// span-lookup at the cursor, per-diagnostic span-registry probe,
111    /// per-trivia leading/trailing-owner attachment gate) reads through
112    /// — returns `true` iff `offset` lies inside the half-open range
113    /// `[self.start, self.end)`. `pub const fn` — matches the sibling
114    /// [`Self::new`] / [`Self::point`] `pub const fn` shape on the same
115    /// [`Span`] primitive's construction axis, extending the const-eval
116    /// surface onto the primitive's containment-predicate axis without
117    /// a body change (integer comparison is const in Rust since long
118    /// before this workspace's 1.89 MSRV floor). Every downstream
119    /// consumer that wants a compile-time span-containment fixture —
120    /// a `const IS_INSIDE: bool = SPAN.contains(OFFSET);` LSP hover-
121    /// registry entry, a per-diagnostic const-context span oracle a
122    /// future admission webhook consults, a compile-time span-partition
123    /// truth table the caixa-fmt trivia-owner resolver keys off — now
124    /// reads through one substrate-primitive const dispatch rather than
125    /// being forced onto the runtime code path.
126    #[must_use]
127    pub const fn contains(self, offset: u32) -> bool {
128        offset >= self.start && offset < self.end
129    }
130}
131
132impl fmt::Display for Span {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        write!(f, "{}..{}", self.start, self.end)
135    }
136}
137
138/// 1-indexed line/column pair — what humans see in editors.
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140pub struct Position {
141    pub line: u32,
142    pub column: u32,
143}
144
145impl Position {
146    /// Substrate-primitive constructor every producer of a
147    /// 1-indexed line/column pair reads through — folds the two-slot
148    /// `Position { line, column }` struct-literal wire-up (the sole
149    /// production emitter [`line_column`]'s tail, plus every
150    /// per-fixture line/column literal in the sibling test module)
151    /// onto one `pub const fn` dispatch. Matches the sibling
152    /// [`Span::new`] / [`Span::point`] `pub const fn` shape on the
153    /// same caixa-ast source-position primitive family; every
154    /// downstream LSP hover-registry / diagnostic emitter / future
155    /// position-carrying trivia-registry that wants a compile-time
156    /// position fixture (a `const AT: Position = Position::new(1, 4);`
157    /// LSP hover-oracle default, a per-diagnostic const-context
158    /// leading-position fixture, a compile-time position-partition
159    /// truth table the caixa-fmt trivia-owner resolver keys off)
160    /// now reaches through one substrate-primitive const dispatch
161    /// rather than duplicating the two-slot struct literal at every
162    /// construction site.
163    #[must_use]
164    pub const fn new(line: u32, column: u32) -> Self {
165        Self { line, column }
166    }
167
168    /// Canonical origin — line 1, column 1, matching the
169    /// [`line_column`] convention (both axes 1-indexed). `pub const
170    /// fn` — the shape materialises at compile time so a future
171    /// const-context consumer (a substrate-wide `const ORIGIN:
172    /// Position = Position::origin();` LSP hover-oracle default, a
173    /// per-diagnostic const-context leading-position fixture that
174    /// today reaches for the `(1, 1)` magic pair inline) reads
175    /// through one substrate-primitive const dispatch rather than
176    /// duplicating the `(1, 1)` origin literal at every consumer.
177    #[must_use]
178    pub const fn origin() -> Self {
179        Self::new(1, 1)
180    }
181}
182
183impl fmt::Display for Position {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        write!(f, "{}:{}", self.line, self.column)
186    }
187}
188
189/// Compute (line, column) for a byte offset. Line and column are 1-indexed.
190/// O(offset); fine for diagnostics, not for hot paths.
191///
192/// `pub const fn` — closes the const-eval discipline on the caixa-ast
193/// source-position primitive family. The pre-lift body iterated
194/// [`str::char_indices`], whose iterator methods are not yet const-
195/// stable (rust-lang/rust #143874, the same tracking issue [`Span::union`]
196/// open-codes `Ord::min` / `Ord::max` around). The const-lift trades the
197/// char-boundary iterator for a manual `while` walk over the src's raw
198/// [`str::as_bytes`] (`pub const fn` since Rust 1.32, well before this
199/// workspace's 1.89 MSRV floor) that keys off two facts every `&str`
200/// carries by construction:
201///
202/// - **`\n` is ASCII.** Byte `0x0A` is a 1-byte UTF-8 codepoint and
203///   never appears as a continuation byte of a multi-byte codepoint (a
204///   continuation byte lies in `0x80..0xC0`). Every line boundary in a
205///   `&str` therefore surfaces as a lone `b'\n'` at exactly one byte
206///   position, so the line-count decision matches the pre-lift
207///   `char_indices` walk verbatim.
208/// - **A char boundary is a non-continuation byte.** Every codepoint
209///   start byte lies in `0x00..0x80` (ASCII) or `0xC0..` (multi-byte
210///   lead); continuation bytes lie in `0x80..0xC0`. Column-counting
211///   therefore matches the pre-lift walk by advancing `col` only on
212///   non-continuation bytes.
213///
214/// Semantic-preserving across every fixture the sibling
215/// [`tests::line_column_handles_newlines`] pins on ASCII input, plus
216/// the added [`tests::line_column_is_const_across_ascii_and_utf8`]
217/// UTF-8 sweep (a 2-byte `é` and a 4-byte `😀` fixture, both of which
218/// the byte-role decision correctly folds onto one column advance per
219/// codepoint start byte, matching the pre-lift `char_indices` walk).
220///
221/// The offset clamp mirrors the pre-lift `break` on `i >= offset`: when
222/// `offset` falls beyond the src end, the walk stops at the last byte
223/// rather than reading past it; when `offset` falls in the middle of a
224/// multi-byte codepoint, the walk stops before the codepoint's start
225/// byte would be counted, matching the pre-lift arm that consumed no
226/// char whose start byte index was `>= offset`.
227#[must_use]
228pub const fn line_column(src: &str, offset: u32) -> Position {
229    let mut line: u32 = 1;
230    let mut col: u32 = 1;
231    let offset = offset as usize;
232    let bytes = src.as_bytes();
233    let end = if offset < bytes.len() {
234        offset
235    } else {
236        bytes.len()
237    };
238    let mut i = 0;
239    while i < end {
240        let b = bytes[i];
241        if b == b'\n' {
242            line += 1;
243            col = 1;
244        } else if b < 0x80 || b >= 0xC0 {
245            // ASCII byte (0x00..0x80) or multi-byte codepoint lead
246            // (0xC0..) — advances the column by one, matching
247            // one char in the pre-lift `char_indices` walk. A
248            // continuation byte (0x80..0xC0) belongs to a codepoint
249            // that has already been counted at its lead byte and
250            // must not re-advance `col`.
251            col += 1;
252        }
253        i += 1;
254    }
255    Position::new(line, col)
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261
262    #[test]
263    fn point_span_is_empty() {
264        let s = Span::point(5);
265        assert!(s.is_empty());
266        assert_eq!(s.len(), 0);
267    }
268
269    #[test]
270    fn union_widens() {
271        let a = Span::new(2, 5);
272        let b = Span::new(4, 9);
273        let u = a.union(b);
274        assert_eq!(u.start, 2);
275        assert_eq!(u.end, 9);
276    }
277
278    #[test]
279    fn union_is_const() {
280        // Pin the const-eval surface: the substrate-primitive parent-span
281        // builder reaches into `const` context, so a future compile-time
282        // parser-fixture / diagnostic-aggregator / fmt-region template
283        // can build a `const OUTER: Span = INNER1.union(INNER2);` without
284        // being forced onto the runtime code path. The four
285        // `const _: () = assert!(…)` bindings resolve the composition at
286        // compile time — any regression that drops `pub const fn` back
287        // to `pub fn` (a body edit that reaches for a non-const
288        // operation) fails this test at compile time rather than at
289        // runtime, matching the sibling `Span::new` / `Span::point` /
290        // `Span::contains` `pub const fn` shape's const-eval discipline.
291        const A: Span = Span::new(2, 5);
292        const B: Span = Span::new(4, 9);
293        const U: Span = A.union(B);
294        const _: () = assert!(U.start == 2);
295        const _: () = assert!(U.end == 9);
296        // Half-open containment on the const-composed parent span keys
297        // through `Span::contains`'s own `pub const fn` promotion — so
298        // both const-eval surfaces (composition and containment) resolve
299        // in one compile-time expression, matching the [start, end)
300        // boundary discipline the sibling `contains_is_const` fixture
301        // already pins.
302        const _: () = assert!(U.contains(2));
303        const _: () = assert!(!U.contains(9));
304    }
305
306    #[test]
307    fn slice_extracts_substring() {
308        let src = "hello world";
309        assert_eq!(Span::new(6, 11).slice(src), "world");
310    }
311
312    #[test]
313    fn line_column_handles_newlines() {
314        let src = "abc\ndef\nghi";
315        assert_eq!(line_column(src, 0), Position::origin());
316        assert_eq!(line_column(src, 4), Position::new(2, 1));
317        assert_eq!(line_column(src, 9), Position::new(3, 2));
318    }
319
320    #[test]
321    fn contains_is_half_open() {
322        let s = Span::new(3, 7);
323        assert!(!s.contains(2));
324        assert!(s.contains(3));
325        assert!(s.contains(6));
326        assert!(!s.contains(7));
327    }
328
329    #[test]
330    fn len_and_is_empty_are_const() {
331        // Pin the const-eval surface: the substrate-primitive width
332        // accessor and emptiness predicate reach into `const` context, so
333        // a future compile-time span-registry / LSP hover-oracle /
334        // trivia-owner truth-table fixture can key off `Span::len` /
335        // `Span::is_empty` without being forced onto the runtime code
336        // path. Any regression that drops `pub const fn` back to `pub fn`
337        // (a body edit that reaches for a non-const operation) fails this
338        // test at compile time rather than at runtime, matching the
339        // sibling `Span::new` / `Span::point` / `Span::contains` /
340        // `Span::union` `pub const fn` shape's const-eval discipline.
341        const RANGE: Span = Span::new(3, 7);
342        const POINT: Span = Span::point(5);
343        const RANGE_LEN: u32 = RANGE.len();
344        const POINT_LEN: u32 = POINT.len();
345        const RANGE_EMPTY: bool = RANGE.is_empty();
346        const POINT_EMPTY: bool = POINT.is_empty();
347        const _: () = assert!(RANGE_LEN == 4);
348        const _: () = assert!(POINT_LEN == 0);
349        const _: () = assert!(!RANGE_EMPTY);
350        const _: () = assert!(POINT_EMPTY);
351        // Saturating-sub floor: an inverted (end < start) fixture must
352        // clamp to 0 at compile time, matching the runtime
353        // `u32::saturating_sub` semantics the pre-lift body carried.
354        const INVERTED: Span = Span::new(9, 2);
355        const INVERTED_LEN: u32 = INVERTED.len();
356        const INVERTED_EMPTY: bool = INVERTED.is_empty();
357        const _: () = assert!(INVERTED_LEN == 0);
358        const _: () = assert!(INVERTED_EMPTY);
359    }
360
361    #[test]
362    fn position_new_and_origin_are_const() {
363        // Pin the const-eval surface on the substrate-primitive
364        // 1-indexed line/column pair: the constructor and canonical
365        // origin reach into `const` context, so a future compile-time
366        // LSP hover-registry / diagnostic-emitter / trivia-owner
367        // truth-table fixture can key off `Position::new` /
368        // `Position::origin` without being forced onto the runtime
369        // code path. Any regression that drops `pub const fn` back to
370        // `pub fn` (a body edit that reaches for a non-const
371        // operation) fails this test at compile time rather than at
372        // runtime, matching the sibling `Span::new` / `Span::point` /
373        // `Span::contains` / `Span::union` / `Span::len` /
374        // `Span::is_empty` `pub const fn` shape's const-eval
375        // discipline on the same caixa-ast source-position primitive
376        // family.
377        //
378        // Also pins `Position::origin`'s canonical (1, 1) shape at
379        // compile time — a future accidental drift (an `origin` that
380        // returns `Position::new(0, 0)` on a well-meaning "zero-
381        // indexed origin" rewrite that forgets `line_column` emits
382        // 1-indexed positions) trips at build time rather than
383        // surfacing far from the origin declaration at some
384        // downstream diagnostic-emitter's off-by-one row report.
385        const AT: Position = Position::new(2, 4);
386        const ORIGIN: Position = Position::origin();
387        const AT_LINE: u32 = AT.line;
388        const AT_COLUMN: u32 = AT.column;
389        const ORIGIN_LINE: u32 = ORIGIN.line;
390        const ORIGIN_COLUMN: u32 = ORIGIN.column;
391        const _: () = assert!(AT_LINE == 2);
392        const _: () = assert!(AT_COLUMN == 4);
393        const _: () = assert!(ORIGIN_LINE == 1);
394        const _: () = assert!(ORIGIN_COLUMN == 1);
395    }
396
397    #[test]
398    fn line_column_is_const_across_ascii_and_utf8() {
399        // Pin the const-eval surface on the last unlifted method of the
400        // caixa-ast source-position primitive family: `line_column`
401        // reaches into `const` context, so a future compile-time
402        // diagnostic-emitter / LSP hover-registry / trivia-owner
403        // truth-table fixture can key off `line_column` without being
404        // forced onto the runtime code path. Any regression that drops
405        // `pub const fn` back to `pub fn` (a body edit that reaches for
406        // a non-const iterator like `str::char_indices`) fails this test
407        // at compile time rather than at runtime, matching the sibling
408        // `Span::new` / `Span::point` / `Span::contains` / `Span::union`
409        // / `Span::len` / `Span::is_empty` / `Position::new` /
410        // `Position::origin` `pub const fn` shape's const-eval discipline
411        // on the same caixa-ast source-position primitive family.
412        //
413        // The ASCII arm matches the sibling `line_column_handles_newlines`
414        // fixture verbatim, resolved at compile time.
415        const ASCII: &str = "abc\ndef\nghi";
416        const ASCII_ORIGIN: Position = line_column(ASCII, 0);
417        const ASCII_ROW_2: Position = line_column(ASCII, 4);
418        const ASCII_ROW_3: Position = line_column(ASCII, 9);
419        const _: () = assert!(ASCII_ORIGIN.line == 1 && ASCII_ORIGIN.column == 1);
420        const _: () = assert!(ASCII_ROW_2.line == 2 && ASCII_ROW_2.column == 1);
421        const _: () = assert!(ASCII_ROW_3.line == 3 && ASCII_ROW_3.column == 2);
422        // Offset clamp: an offset beyond the src end falls onto the last
423        // byte, matching the pre-lift `break` on the exhausted iterator.
424        const ASCII_PAST_END: Position = line_column(ASCII, 100);
425        const _: () = assert!(ASCII_PAST_END.line == 3 && ASCII_PAST_END.column == 4);
426
427        // UTF-8 arm: the byte-role logic (advance `col` only on non-
428        // continuation bytes, `b < 0x80 || b >= 0xC0`) folds every
429        // multi-byte codepoint onto exactly one column advance, matching
430        // the pre-lift `char_indices` walk. `é` is 2 bytes (`0xC3 0xA9`);
431        // `😀` is 4 bytes (`0xF0 0x9F 0x98 0x80`).
432        //
433        // Fixture: "h" (0x68) at byte 0, "é" (0xC3 0xA9) at bytes 1..3,
434        // "l" (0x6C) at byte 3, "l" (0x6C) at byte 4, "o" (0x6F) at byte 5.
435        const UTF8_TWO_BYTE: &str = "héllo";
436        const UTF8_TWO_BYTE_AFTER_H: Position = line_column(UTF8_TWO_BYTE, 1);
437        const UTF8_TWO_BYTE_MID_E_ACUTE: Position = line_column(UTF8_TWO_BYTE, 2);
438        const UTF8_TWO_BYTE_AFTER_E_ACUTE: Position = line_column(UTF8_TWO_BYTE, 3);
439        const UTF8_TWO_BYTE_END: Position = line_column(UTF8_TWO_BYTE, 100);
440        // After "h" (1 codepoint): column 2.
441        const _: () = assert!(UTF8_TWO_BYTE_AFTER_H.line == 1 && UTF8_TWO_BYTE_AFTER_H.column == 2);
442        // Offset 2 falls one byte after `é`'s lead byte (offset 1),
443        // so the codepoint has already been counted at its lead byte
444        // — matches the pre-lift `char_indices` arm that consumed
445        // every char whose start-index was `< offset`. Column 3
446        // = "after h and é" — the mid-codepoint continuation-byte
447        // position resolves to the same 1-indexed column the lead-
448        // byte-adjacent position would.
449        const _: () =
450            assert!(UTF8_TWO_BYTE_MID_E_ACUTE.line == 1 && UTF8_TWO_BYTE_MID_E_ACUTE.column == 3);
451        // After "hé" (2 codepoints): column 3, byte-equal to the mid-
452        // continuation-byte case above — the codepoint boundary at
453        // byte 3 lands on the same 1-indexed column the walk reached
454        // after crossing `é`'s lead byte at byte 1.
455        const _: () = assert!(
456            UTF8_TWO_BYTE_AFTER_E_ACUTE.line == 1 && UTF8_TWO_BYTE_AFTER_E_ACUTE.column == 3
457        );
458        // Past-end clamp on UTF-8 input: 5 codepoints total, column 6.
459        const _: () = assert!(UTF8_TWO_BYTE_END.line == 1 && UTF8_TWO_BYTE_END.column == 6);
460
461        // Fixture: "😀" (0xF0 0x9F 0x98 0x80) at bytes 0..4, space at 4,
462        // "n" at 5.
463        const UTF8_FOUR_BYTE: &str = "😀 nice";
464        const UTF8_FOUR_BYTE_AFTER_EMOJI: Position = line_column(UTF8_FOUR_BYTE, 4);
465        const UTF8_FOUR_BYTE_AFTER_SPACE: Position = line_column(UTF8_FOUR_BYTE, 5);
466        // After "😀" (1 codepoint on a 4-byte sequence): column 2.
467        const _: () =
468            assert!(UTF8_FOUR_BYTE_AFTER_EMOJI.line == 1 && UTF8_FOUR_BYTE_AFTER_EMOJI.column == 2);
469        // After "😀 " (2 codepoints): column 3.
470        const _: () =
471            assert!(UTF8_FOUR_BYTE_AFTER_SPACE.line == 1 && UTF8_FOUR_BYTE_AFTER_SPACE.column == 3);
472
473        // Newline crossing a multi-byte codepoint boundary: "😀\n😀".
474        // Byte layout: 0..4 = 😀, 4 = '\n', 5..9 = 😀. Offset 5 lands at
475        // line 2 column 1 (start of the second codepoint).
476        const UTF8_ACROSS_NEWLINE: &str = "😀\n😀";
477        const UTF8_ACROSS_NEWLINE_ROW_2: Position = line_column(UTF8_ACROSS_NEWLINE, 5);
478        const _: () =
479            assert!(UTF8_ACROSS_NEWLINE_ROW_2.line == 2 && UTF8_ACROSS_NEWLINE_ROW_2.column == 1);
480    }
481
482    #[test]
483    fn contains_is_const() {
484        // Pin the const-eval surface: the substrate-primitive
485        // containment predicate reaches into `const` context, so a
486        // future compile-time span-registry / LSP hover-oracle /
487        // trivia-owner truth-table fixture can key off `Span::contains`
488        // without being forced onto the runtime code path. The four
489        // `const _: () = assert!(…)` bindings resolve the predicate at
490        // compile time — any regression that drops `pub const fn` back
491        // to `pub fn` (a body edit that reaches for a non-const
492        // operation) fails this test at compile time rather than at
493        // runtime, matching the sibling `Span::new` / `Span::point`
494        // `pub const fn` shape's const-eval discipline.
495        const SPAN: Span = Span::new(3, 7);
496        const _: () = assert!(!SPAN.contains(2));
497        const _: () = assert!(SPAN.contains(3));
498        const _: () = assert!(SPAN.contains(5));
499        const _: () = assert!(!SPAN.contains(7));
500    }
501}