## Coverage Report

#### Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/realizar/src/tui.rs

    Line
    Count
    Source
    1
    //! TUI Monitoring for LLM Inference
    2
    //!
    3
    //! Real-time terminal UI for monitoring inference performance.
    4
    //! Provides visual feedback on throughput, latency, and GPU utilization.
    5
    //!
    6
    //! # Usage
    7
    //!
    8
    //! ```rust,ignore
    9
    //! use realizar::tui::{InferenceTui, TuiConfig, InferenceMetrics};
    10
    //!
    11
    //! let config = TuiConfig::default();
    12
    //! let mut tui = InferenceTui::new(config);
    13
    //!
    14
    //! // Update with metrics during inference
    15
    //! tui.update(&metrics);
    16
    //!
    17
    //! // Render to string (for testing)
    18
    //! let output = tui.render_to_string();
    19
    //! ```
    20
    //!
    21
    //! # Visual Elements
    22
    //!
    23
    //! ```text
    24
    //! ╭─────────────────────────────────────────────────────────────╮
    25
    //! │             realizar Inference Monitor                       │
    26
    //! ├─────────────────────────────────────────────────────────────┤
    27
    //! │  Throughput: 64.2 tok/s   Target: 192 tok/s (M4)           │
    28
    //! │  Latency:    15.6 ms/tok  P95: 23.4 ms                     │
    29
    //! │  GPU Memory: 4.2 GB / 24 GB                                │
    30
    //! │  Batch Size: 4            Queue: 12 pending                │
    31
    //! ├─────────────────────────────────────────────────────────────┤
    32
    //! │  Throughput: ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁▂▃▄▅▆▇█                       │
    33
    //! │  Latency:    ▇▆▅▄▃▂▁▂▃▄▅▆▇█▇▆▅▄▃▂▁                         │
    34
    //! ├─────────────────────────────────────────────────────────────┤
    35
    //! │  Status: ● Running   Tokens: 1,234   Requests: 42          │
    36
    //! ╰─────────────────────────────────────────────────────────────╯
    37
    //! ```
    38
    39
    use std::collections::VecDeque;
    40
    41
    /// TUI configuration
    42
    #[derive(Debug, Clone)]
    43
    pub struct TuiConfig {
    44
        /// Refresh rate in milliseconds
    45
        pub refresh_rate_ms: u64,
    46
        /// Show throughput sparkline
    47
        pub show_throughput_sparkline: bool,
    48
        /// Show latency sparkline
    49
        pub show_latency_sparkline: bool,
    50
        /// Show GPU memory usage
    51
        pub show_gpu_memory: bool,
    52
        /// Title for the TUI window
    53
        pub title: String,
    54
        /// Target throughput for M4 parity
    55
        pub m4_target_tok_per_sec: f64,
    56
        /// Width of the TUI display
    57
        pub width: usize,
    58
    }
    59
    60
    impl Default for TuiConfig {
    61
    5
        fn default() -> Self {
    62
    5
            Self {
    63
    5
                refresh_rate_ms: 100,
    64
    5
                show_throughput_sparkline: true,
    65
    5
                show_latency_sparkline: true,
    66
    5
                show_gpu_memory: true,
    67
    5
                title: "realizar Inference Monitor".to_string(),
    68
    5
                m4_target_tok_per_sec: 192.0,
    69
    5
                width: 65,
    70
    5
            }
    71
    5
        }
    72
    }
    73
    74
    /// Real-time inference metrics
    75
    #[derive(Debug, Clone, Default)]
    76
    pub struct InferenceMetrics {
    77
        /// Current throughput (tokens/second)
    78
        pub throughput_tok_per_sec: f64,
    79
        /// Mean latency per token (milliseconds)
    80
        pub latency_ms: f64,
    81
        /// P95 latency (milliseconds)
    82
        pub latency_p95_ms: f64,
    83
        /// GPU memory used (bytes)
    84
        pub gpu_memory_bytes: u64,
    85
        /// GPU memory total (bytes)
    86
        pub gpu_memory_total_bytes: u64,
    87
        /// Current batch size
    88
        pub batch_size: usize,
    89
        /// Pending requests in queue
    90
        pub queue_size: usize,
    91
        /// Total tokens generated
    92
        pub total_tokens: u64,
    93
        /// Total requests processed
    94
        pub total_requests: u64,
    95
        /// Is currently running
    96
        pub running: bool,
    97
        /// Is using GPU
    98
        pub using_gpu: bool,
    99
    }
    100
    101
    impl InferenceMetrics {
    102
        /// Create new metrics with defaults
    103
        #[must_use]
    104
    0
        pub fn new() -> Self {
    105
    0
            Self::default()
    106
    0
        }
    107
    108
        /// Check if throughput achieves M4 parity (192 tok/s)
    109
        #[must_use]
    110
    8
        pub fn achieves_m4_parity(&self) -> bool {
    111
    8
            self.throughput_tok_per_sec >= 192.0
    112
    8
        }
    113
    114
        /// Calculate gap to M4 target
    115
        #[must_use]
    116
    2
        pub fn gap_to_m4(&self) -> f64 {
    117
    2
            if self.throughput_tok_per_sec > 0.0 {
    118
    2
                192.0 / self.throughput_tok_per_sec
    119
            } else {
    120
    0
                f64::INFINITY
    121
            }
    122
    2
        }
    123
    124
        /// Format GPU memory as human-readable string
    125
        #[must_use]
    126
    4
        pub fn format_gpu_memory(&self) -> String {
    127
    4
            let used_gb = self.gpu_memory_bytes as f64 / 1e9;
    128
    4
            let total_gb = self.gpu_memory_total_bytes as f64 / 1e9;
    129
    4
            format!("{:.1} GB / {:.1} GB", used_gb, total_gb)
    130
    4
        }
    131
    }
    132
    133
    /// TUI state for rendering
    134
    #[derive(Debug, Clone)]
    135
    pub struct InferenceTui {
    136
        /// Configuration
    137
        config: TuiConfig,
    138
        /// Current metrics
    139
        metrics: InferenceMetrics,
    140
        /// Throughput history (for sparkline)
    141
        throughput_history: VecDeque<f64>,
    142
        /// Latency history (for sparkline)
    143
        latency_history: VecDeque<f64>,
    144
        /// Maximum history size
    145
        max_history: usize,
    146
    }
    147
    148
    impl InferenceTui {
    149
        /// Create new TUI with configuration
    150
        #[must_use]
    151
    4
        pub fn new(config: TuiConfig) -> Self {
    152
    4
            Self {
    153
    4
                config,
    154
    4
                metrics: InferenceMetrics::default(),
    155
    4
                throughput_history: VecDeque::new(),
    156
    4
                latency_history: VecDeque::new(),
    157
    4
                max_history: 40,
    158
    4
            }
    159
    4
        }
    160
    161
        /// Update TUI with new metrics
    162
    62
        pub fn update(&mut self, metrics: &InferenceMetrics) {
    163
    62
            self.metrics = metrics.clone();
    164
    165
            // Add to history
    166
    62
            self.throughput_history
    167
    62
                .push_back(metrics.throughput_tok_per_sec);
    168
    62
            self.latency_history.push_back(metrics.latency_ms);
    169
    170
            // Trim history
    171
    72
            while self.throughput_history.len() > self.max_history {
    172
    10
                self.throughput_history.pop_front();
    173
    10
            }
    174
    72
            while self.latency_history.len() > self.max_history {
    175
    10
                self.latency_history.pop_front();
    176
    10
            }
    177
    62
        }
    178
    179
        /// Generate sparkline string from values
    180
    6
        fn sparkline(values: &VecDeque<f64>, width: usize) -> String {
    181
            const BLOCKS: [char; 8] = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
    182
    183
    6
            if values.is_empty() {
    184
    1
                return " ".repeat(width);
    185
    5
            }
    186
    187
    5
            let max = values.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
    188
    5
            let min = values.iter().cloned().fold(f64::INFINITY, f64::min);
    189
    5
            let range = (max - min).max(0.001);
    190
    191
    5
            let mut result: String = values
    192
    5
                .iter()
    193
    5
                .take(width)
    194
    42
                .map5(|&v| {
    195
    42
                    let normalized = (v - min) / range;
    196
    42
                    let level = (normalized * 7.0).round().clamp(0.0, 7.0) as usize;
    197
    42
                    BLOCKS[level]
    198
    42
                })
    199
    5
                .collect();
    200
    201
            // Pad to width
    202
    143
            while result.chars().count() < width {
    203
    138
                result.push(' ');
    204
    138
            }
    205
    206
    5
            result
    207
    6
        }
    208
    209
        /// Render TUI to string (for testing and display)
    210
        #[must_use]
    211
    2
        pub fn render_to_string(&self) -> String {
    212
    2
            let w = self.config.width;
    213
    2
            let inner_w = w - 2; // Account for │ borders on each side
    214
    215
    2
            let mut lines = Vec::new();
    216
    217
            // Top border
    218
    2
            lines.push(format!("╭{}╮", "─".repeat(w - 2)));
    219
    220
            // Title
    221
    2
            let title = &self.config.title;
    222
    2
            let padding = (inner_w - title.len()) / 2;
    223
    2
            lines.push(format!(
    224
    2
                "│{}{}{}│",
    225
    2
                " ".repeat(padding),
    226
                title,
    227
    2
                " ".repeat(inner_w - padding - title.len())
    228
            ));
    229
    230
            // Separator
    231
    2
            lines.push(format!("├{}┤", "─".repeat(w - 2)));
    232
    233
            // Throughput line
    234
    2
            let status_icon = if self.metrics.achieves_m4_parity() {
    235
    0
                "✓"
    236
            } else {
    237
    2
                "○"
    238
            };
    239
    2
            let throughput_line = format!(
    240
    2
                "  Throughput: {:.1} tok/s {} Target: {:.0} tok/s (M4)",
    241
                self.metrics.throughput_tok_per_sec, status_icon, self.config.m4_target_tok_per_sec
    242
            );
    243
    2
            lines.push(Self::pad_line(&throughput_line, inner_w));
    244
    245
            // Latency line
    246
    2
            let latency_line = format!(
    247
    2
                "  Latency:    {:.1} ms/tok  P95: {:.1} ms",
    248
                self.metrics.latency_ms, self.metrics.latency_p95_ms
    249
            );
    250
    2
            lines.push(Self::pad_line(&latency_line, inner_w));
    251
    252
            // GPU memory line
    253
    2
            if self.config.show_gpu_memory {
    254
    2
                let gpu_line = format!("  GPU Memory: {}", self.metrics.format_gpu_memory());
    255
    2
                lines.push(Self::pad_line(&gpu_line, inner_w));
    256
    2
            }0
    257
    258
            // Batch info line
    259
    2
            let batch_line = format!(
    260
    2
                "  Batch Size: {}            Queue: {} pending",
    261
                self.metrics.batch_size, self.metrics.queue_size
    262
            );
    263
    2
            lines.push(Self::pad_line(&batch_line, inner_w));
    264
    265
            // Separator
    266
    2
            lines.push(format!("├{}┤", "─".repeat(w - 2)));
    267
    268
            // Sparklines
    269
    2
            if self.config.show_throughput_sparkline {
    270
    2
                let sparkline = Self::sparkline(&self.throughput_history, 40);
    271
    2
                let spark_line = format!("  Throughput: {}", sparkline);
    272
    2
                lines.push(Self::pad_line(&spark_line, inner_w));
    273
    2
            }0
    274
    275
    2
            if self.config.show_latency_sparkline {
    276
    2
                let sparkline = Self::sparkline(&self.latency_history, 40);
    277
    2
                let spark_line = format!("  Latency:    {}", sparkline);
    278
    2
                lines.push(Self::pad_line(&spark_line, inner_w));
    279
    2
            }0
    280
    281
            // Separator
    282
    2
            lines.push(format!("├{}┤", "─".repeat(w - 2)));
    283
    284
            // Status line
    285
    2
            let status = if self.metrics.running {
    286
    2
                "● Running"
    287
            } else {
    288
    0
                "○ Stopped"
    289
            };
    290
    2
            let gpu_status = if self.metrics.using_gpu { "GPU" } else { "CPU"0 };
    291
    2
            let status_line = format!(
    292
    2
                "  Status: {}  [{:>3}]  Tokens: {:>6}  Requests: {:>4}",
    293
                status, gpu_status, self.metrics.total_tokens, self.metrics.total_requests
    294
            );
    295
    2
            lines.push(Self::pad_line(&status_line, inner_w));
    296
    297
            // Bottom border
    298
    2
            lines.push(format!("╰{}╯", "─".repeat(w - 2)));
    299
    300
    2
            lines.join("\n")
    301
    2
        }
    302
    303
        /// Pad line to fit within borders
    304
    14
        fn pad_line(content: &str, width: usize) -> String {
    305
    14
            let content_len = content.chars().count();
    306
    14
            if content_len >= width {
    307
    0
                format!("│{}│", &content[..width])
    308
            } else {
    309
    14
                format!("│{}{}│", content, " ".repeat(width - content_len))
    310
            }
    311
    14
        }
    312
    313
        /// Get current metrics
    314
        #[must_use]
    315
    1
        pub fn metrics(&self) -> &InferenceMetrics {
    316
    1
            &self.metrics
    317
    1
        }
    318
    319
        /// Get throughput history for testing
    320
        #[must_use]
    321
    3
        pub fn throughput_history(&self) -> &VecDeque<f64> {
    322
    3
            &self.throughput_history
    323
    3
        }
    324
    325
        /// Get latency history for testing
    326
        #[must_use]
    327
    1
        pub fn latency_history(&self) -> &VecDeque<f64> {
    328
    1
            &self.latency_history
    329
    1
        }
    330
    }
    331
    332
    #[cfg(test)]
    333
    mod tests {
    334
        use super::*;
    335
    336
        // =========================================================================
    337
        // PARITY-090: TUI Configuration Tests
    338
        // =========================================================================
    339
    340
        /// PARITY-090a: Test TuiConfig defaults
    341
        #[test]
    342
    1
        fn test_parity_090a_tui_config_defaults() {
    343
    1
            println!("PARITY-090a: TuiConfig Default Values");
    344
    345
    1
            let config = TuiConfig::default();
    346
    347
    1
            println!("  refresh_rate_ms: {}", config.refresh_rate_ms);
    348
    1
            println!("  m4_target_tok_per_sec: {}", config.m4_target_tok_per_sec);
    349
    1
            println!("  width: {}", config.width);
    350
    351
    1
            assert_eq!(config.refresh_rate_ms, 100);
    352
    1
            assert_eq!(config.m4_target_tok_per_sec, 192.0);
    353
    1
            assert!(config.show_throughput_sparkline);
    354
    1
            assert!(config.show_latency_sparkline);
    355
    1
        }
    356
    357
        /// PARITY-090b: Test InferenceMetrics creation
    358
        #[test]
    359
    1
        fn test_parity_090b_inference_metrics() {
    360
    1
            println!("PARITY-090b: InferenceMetrics");
    361
    362
    1
            let metrics = InferenceMetrics {
    363
    1
                throughput_tok_per_sec: 64.0,
    364
    1
                latency_ms: 15.6,
    365
    1
                latency_p95_ms: 23.4,
    366
    1
                gpu_memory_bytes: 4_200_000_000,
    367
    1
                gpu_memory_total_bytes: 24_000_000_000,
    368
    1
                batch_size: 4,
    369
    1
                queue_size: 12,
    370
    1
                total_tokens: 1234,
    371
    1
                total_requests: 42,
    372
    1
                running: true,
    373
    1
                using_gpu: true,
    374
    1
            };
    375
    376
    1
            println!("  throughput: {:.1} tok/s", metrics.throughput_tok_per_sec);
    377
    1
            println!("  achieves_m4: {}", metrics.achieves_m4_parity());
    378
    1
            println!("  gap_to_m4: {:.2}x", metrics.gap_to_m4());
    379
    1
            println!("  gpu_memory: {}", metrics.format_gpu_memory());
    380
    381
    1
            assert!(!metrics.achieves_m4_parity());
    382
    1
            assert!((metrics.gap_to_m4() - 3.0).abs() < 0.1);
    383
    1
            assert!(metrics.format_gpu_memory().contains("4.2 GB"));
    384
    1
        }
    385
    386
        /// PARITY-090c: Test M4 parity detection
    387
        #[test]
    388
    1
        fn test_parity_090c_m4_parity_detection() {
    389
    1
            println!("PARITY-090c: M4 Parity Detection");
    390
    391
    1
            let test_cases = [
    392
    1
                (64.0, false, "Baseline - not M4"),
    393
    1
                (150.0, false, "Batch threshold - not M4"),
    394
    1
                (192.0, true, "Exactly M4"),
    395
    1
                (256.0, true, "Above M4"),
    396
    1
            ];
    397
    398
    5
            for (throughput4, expected4, description4) in test_cases {
    399
    4
                let metrics = InferenceMetrics {
    400
    4
                    throughput_tok_per_sec: throughput,
    401
    4
                    ..Default::default()
    402
    4
                };
    403
    4
                let achieves = metrics.achieves_m4_parity();
    404
    4
                println!("  {}: {} tok/s → M4={}", description, throughput, achieves);
    405
    4
                assert_eq!(achieves, expected, "{}"0, description);
    406
            }
    407
    1
        }
    408
    409
        // =========================================================================
    410
        // PARITY-091: TUI Rendering Tests
    411
        // =========================================================================
    412
    413
        /// PARITY-091a: Test TUI creation and update
    414
        #[test]
    415
    1
        fn test_parity_091a_tui_creation_update() {
    416
    1
            println!("PARITY-091a: TUI Creation and Update");
    417
    418
    1
            let config = TuiConfig::default();
    419
    1
            let mut tui = InferenceTui::new(config);
    420
    421
    1
            let metrics = InferenceMetrics {
    422
    1
                throughput_tok_per_sec: 64.0,
    423
    1
                latency_ms: 15.6,
    424
    1
                running: true,
    425
    1
                ..Default::default()
    426
    1
            };
    427
    428
    1
            tui.update(&metrics);
    429
    430
    1
            assert_eq!(tui.metrics().throughput_tok_per_sec, 64.0);
    431
    1
            assert_eq!(tui.throughput_history().len(), 1);
    432
    1
        }
    433
    434
        /// PARITY-091b: Test sparkline generation
    435
        #[test]
    436
    1
        fn test_parity_091b_sparkline_generation() {
    437
    1
            println!("PARITY-091b: Sparkline Generation");
    438
    439
    1
            let mut history = VecDeque::new();
    440
    21
            for i20 in 0..20 {
    441
    20
                history.push_back((i as f64) * 10.0);
    442
    20
            }
    443
    444
    1
            let sparkline = InferenceTui::sparkline(&history, 20);
    445
    1
            println!("  Sparkline: {}", sparkline);
    446
    447
    1
            assert_eq!(sparkline.chars().count(), 20);
    448
    1
            assert!(sparkline.contains('▁')); // Low values
    449
    1
            assert!(sparkline.contains('█')); // High values
    450
    1
        }
    451
    452
        /// PARITY-091c: Test TUI render output structure
    453
        #[test]
    454
    1
        fn test_parity_091c_tui_render_structure() {
    455
    1
            println!("PARITY-091c: TUI Render Output Structure");
    456
    457
    1
            let config = TuiConfig::default();
    458
    1
            let mut tui = InferenceTui::new(config);
    459
    460
            // Add some history
    461
    11
            for i10 in 0..10 {
    462
    10
                let metrics = InferenceMetrics {
    463
    10
                    throughput_tok_per_sec: 50.0 + (i as f64) * 5.0,
    464
    10
                    latency_ms: 20.0 - (i as f64),
    465
    10
                    batch_size: 4,
    466
    10
                    queue_size: 12,
    467
    10
                    total_tokens: 1234,
    468
    10
                    total_requests: 42,
    469
    10
                    running: true,
    470
    10
                    using_gpu: true,
    471
    10
                    ..Default::default()
    472
    10
                };
    473
    10
                tui.update(&metrics);
    474
    10
            }
    475
    476
    1
            let output = tui.render_to_string();
    477
    1
            println!("{}", output);
    478
    479
            // Verify structure
    480
    1
            assert!(output.contains("╭"), "Should have top border"0);
    481
    1
            assert!(output.contains("╰"), "Should have bottom border"0);
    482
    1
            assert!(
    483
    1
                output.contains("realizar Inference Monitor"),
    484
    0
                "Should have title"
    485
            );
    486
    1
            assert!(output.contains("Throughput:"), "Should show throughput"0);
    487
    1
            assert!(output.contains("Latency:"), "Should show latency"0);
    488
    1
            assert!(output.contains("tok/s"), "Should show tok/s unit"0);
    489
    1
            assert!(output.contains("● Running"), "Should show running status"0);
    490
    1
        }
    491
    492
        /// PARITY-091d: Test TUI visual regression baseline
    493
        #[test]
    494
    1
        fn test_parity_091d_visual_regression_baseline() {
    495
    1
            println!("PARITY-091d: Visual Regression Baseline");
    496
    497
    1
            let config = TuiConfig {
    498
    1
                width: 65,
    499
    1
                ..Default::default()
    500
    1
            };
    501
    1
            let mut tui = InferenceTui::new(config);
    502
    503
    1
            let metrics = InferenceMetrics {
    504
    1
                throughput_tok_per_sec: 64.2,
    505
    1
                latency_ms: 15.6,
    506
    1
                latency_p95_ms: 23.4,
    507
    1
                gpu_memory_bytes: 4_200_000_000,
    508
    1
                gpu_memory_total_bytes: 24_000_000_000,
    509
    1
                batch_size: 4,
    510
    1
                queue_size: 12,
    511
    1
                total_tokens: 1234,
    512
    1
                total_requests: 42,
    513
    1
                running: true,
    514
    1
                using_gpu: true,
    515
    1
            };
    516
    517
    1
            tui.update(&metrics);
    518
    1
            let output = tui.render_to_string();
    519
    520
    1
            println!("=== GOLDEN BASELINE ===");
    521
    1
            println!("{}", output);
    522
    1
            println!("=== END BASELINE ===");
    523
    524
            // Verify key visual elements
    525
    1
            let lines: Vec<&str> = output.lines().collect();
    526
    1
            assert!(lines.len() >= 10, "Should have at least 10 lines"0);
    527
    528
            // Check border consistency
    529
    1
            assert!(lines[0].starts_with('╭'));
    530
    1
            assert!(lines[0].ends_with('╮'));
    531
    1
            assert!(lines.last().expect("test").starts_with('╰'));
    532
    1
            assert!(lines.last().expect("test").ends_with('╯'));
    533
    534
            // Check content
    535
    1
            assert!(
    536
    1
                output.contains("64.2 tok/s"),
    537
    0
                "Should show throughput value"
    538
            );
    539
    1
            assert!(output.contains("15.6 ms/tok"), "Should show latency value"0);
    540
    1
            assert!(output.contains("1234"), "Should show token count"0);
    541
    1
        }
    542
    543
        /// PARITY-091e: Test history accumulation
    544
        #[test]
    545
    1
        fn test_parity_091e_history_accumulation() {
    546
    1
            println!("PARITY-091e: History Accumulation");
    547
    548
    1
            let config = TuiConfig::default();
    549
    1
            let mut tui = InferenceTui::new(config);
    550
    551
            // Add more than max_history items
    552
    51
            for i50 in 0..50 {
    553
    50
                let metrics = InferenceMetrics {
    554
    50
                    throughput_tok_per_sec: (i as f64) * 2.0,
    555
    50
                    latency_ms: 100.0 - (i as f64),
    556
    50
                    ..Default::default()
    557
    50
                };
    558
    50
                tui.update(&metrics);
    559
    50
            }
    560
    561
            // Should be capped at max_history (40)
    562
    1
            assert_eq!(tui.throughput_history().len(), 40);
    563
    1
            assert_eq!(tui.latency_history().len(), 40);
    564
    565
            // Most recent should be last
    566
    1
            assert!((tui.throughput_history().back().expect("test") - 98.0).abs() < 0.1);
    567
    1
        }
    568
    569
        /// PARITY-091f: Test empty sparkline handling
    570
        #[test]
    571
    1
        fn test_parity_091f_empty_sparkline() {
    572
    1
            println!("PARITY-091f: Empty Sparkline Handling");
    573
    574
    1
            let empty: VecDeque<f64> = VecDeque::new();
    575
    1
            let sparkline = InferenceTui::sparkline(&empty, 20);
    576
    577
    1
            println!("  Empty sparkline: '{}'", sparkline);
    578
    1
            assert_eq!(sparkline.len(), 20);
    579
    20
            assert!1(sparkline.chars()1.all1(|c| c == ' '));
    580
    1
        }
    581
    }