amari_gpu/
adaptive.rs

1//! Adaptive Verification Framework for Cross-Platform GPU Operations
2//!
3//! This module implements platform detection and adaptive verification
4//! strategies that automatically adjust verification approaches based on
5//! the execution environment and performance constraints.
6
7use crate::{verification::*, GpuCliffordAlgebra};
8use std::time::{Duration, Instant};
9use thiserror::Error;
10
11#[derive(Error, Debug)]
12pub enum AdaptiveVerificationError {
13    #[error("Platform detection failed: {0}")]
14    PlatformDetection(String),
15
16    #[error("GPU verification failed: {0}")]
17    GpuVerification(#[from] GpuVerificationError),
18
19    #[error("No suitable verification strategy available")]
20    NoSuitableStrategy,
21
22    #[error("Performance constraint violation: {constraint}")]
23    PerformanceConstraint { constraint: String },
24}
25
26/// Platform-specific execution environment
27#[derive(Debug, Clone, PartialEq)]
28pub enum VerificationPlatform {
29    /// Native CPU with full phantom type support
30    NativeCpu { features: CpuFeatures },
31    /// GPU with boundary verification constraints
32    Gpu {
33        backend: GpuBackend,
34        memory_mb: u64,
35        compute_units: u32,
36    },
37    /// WebAssembly with runtime verification
38    Wasm { env: WasmEnvironment },
39}
40
41#[derive(Debug, Clone, PartialEq)]
42pub struct CpuFeatures {
43    pub supports_simd: bool,
44    pub core_count: usize,
45    pub cache_size_kb: u64,
46}
47
48#[derive(Debug, Clone, PartialEq)]
49pub enum GpuBackend {
50    Vulkan,
51    Metal,
52    Dx12,
53    OpenGL,
54    WebGpu,
55}
56
57#[derive(Debug, Clone, PartialEq)]
58pub enum WasmEnvironment {
59    Browser { engine: String },
60    NodeJs { version: String },
61    Standalone,
62}
63
64/// Verification level that adapts to platform constraints
65#[derive(Debug, Clone, PartialEq)]
66pub enum AdaptiveVerificationLevel {
67    /// Maximum verification (CPU only)
68    Maximum,
69    /// High verification with performance awareness
70    High,
71    /// Balanced verification for production workloads
72    Balanced,
73    /// Minimal verification for performance-critical paths
74    Minimal,
75    /// Debug-only verification
76    Debug,
77}
78
79/// Adaptive verifier that selects optimal strategy per platform
80pub struct AdaptiveVerifier {
81    platform: VerificationPlatform,
82    verification_level: AdaptiveVerificationLevel,
83    performance_budget: Duration,
84    boundary_verifier: Option<GpuBoundaryVerifier>,
85    gpu_instance: Option<GpuCliffordAlgebra>,
86}
87
88impl AdaptiveVerifier {
89    /// Create adaptive verifier with automatic platform detection
90    pub async fn new() -> Result<Self, AdaptiveVerificationError> {
91        let platform = Self::detect_platform().await?;
92        let verification_level = Self::determine_verification_level(&platform);
93        let performance_budget = Self::determine_performance_budget(&platform);
94
95        let (boundary_verifier, gpu_instance) = match &platform {
96            VerificationPlatform::Gpu { .. } => {
97                let config = Self::create_gpu_verification_config(&platform, &verification_level);
98                let verifier = GpuBoundaryVerifier::new(config);
99                let gpu = GpuCliffordAlgebra::new::<3, 0, 0>().await.ok();
100                (Some(verifier), gpu)
101            }
102            _ => (None, None),
103        };
104
105        Ok(Self {
106            platform,
107            verification_level,
108            performance_budget,
109            boundary_verifier,
110            gpu_instance,
111        })
112    }
113
114    /// Create adaptive verifier with explicit configuration
115    pub async fn with_config(
116        level: AdaptiveVerificationLevel,
117        budget: Duration,
118    ) -> Result<Self, AdaptiveVerificationError> {
119        let platform = Self::detect_platform().await?;
120
121        let (boundary_verifier, gpu_instance) = match &platform {
122            VerificationPlatform::Gpu { .. } => {
123                let config = Self::create_gpu_verification_config(&platform, &level);
124                let verifier = GpuBoundaryVerifier::new(config);
125                let gpu = GpuCliffordAlgebra::new::<3, 0, 0>().await.ok();
126                (Some(verifier), gpu)
127            }
128            _ => (None, None),
129        };
130
131        Ok(Self {
132            platform,
133            verification_level: level,
134            performance_budget: budget,
135            boundary_verifier,
136            gpu_instance,
137        })
138    }
139
140    /// Perform verified operation with platform-appropriate strategy
141    pub async fn verified_geometric_product<const P: usize, const Q: usize, const R: usize>(
142        &mut self,
143        a: &VerifiedMultivector<P, Q, R>,
144        b: &VerifiedMultivector<P, Q, R>,
145    ) -> Result<VerifiedMultivector<P, Q, R>, AdaptiveVerificationError> {
146        let start_time = Instant::now();
147
148        let result = match &self.platform {
149            VerificationPlatform::NativeCpu { .. } => {
150                // Full phantom type verification available
151                self.cpu_verification(a, b).await?
152            }
153            VerificationPlatform::Gpu { .. } => {
154                // Single operations typically use CPU for efficiency
155                self.cpu_verification(a, b).await?
156            }
157            VerificationPlatform::Wasm { .. } => {
158                // Runtime contract verification
159                self.wasm_runtime_verification(a, b).await?
160            }
161        };
162
163        let elapsed = start_time.elapsed();
164        if elapsed > self.performance_budget {
165            return Err(AdaptiveVerificationError::PerformanceConstraint {
166                constraint: format!(
167                    "Operation exceeded budget: {:?} > {:?}",
168                    elapsed, self.performance_budget
169                ),
170            });
171        }
172
173        Ok(result)
174    }
175
176    /// Perform verified batch operation with optimal GPU/CPU dispatch
177    pub async fn verified_batch_geometric_product<
178        const P: usize,
179        const Q: usize,
180        const R: usize,
181    >(
182        &mut self,
183        a_batch: &[VerifiedMultivector<P, Q, R>],
184        b_batch: &[VerifiedMultivector<P, Q, R>],
185    ) -> Result<Vec<VerifiedMultivector<P, Q, R>>, AdaptiveVerificationError> {
186        if a_batch.is_empty() {
187            return Ok(Vec::new());
188        }
189
190        match &self.platform {
191            VerificationPlatform::NativeCpu { .. } => {
192                // CPU batch processing with full verification
193                self.cpu_batch_verification(a_batch, b_batch).await
194            }
195            VerificationPlatform::Gpu { .. } => {
196                // GPU boundary verification strategy
197                self.gpu_batch_verification(a_batch, b_batch).await
198            }
199            VerificationPlatform::Wasm { .. } => {
200                // WASM runtime verification with progressive enhancement
201                self.wasm_batch_verification(a_batch, b_batch).await
202            }
203        }
204    }
205
206    /// Get platform information
207    pub fn platform(&self) -> &VerificationPlatform {
208        &self.platform
209    }
210
211    /// Get current verification level
212    pub fn verification_level(&self) -> &AdaptiveVerificationLevel {
213        &self.verification_level
214    }
215
216    /// Get performance budget
217    pub fn performance_budget(&self) -> Duration {
218        self.performance_budget
219    }
220
221    /// Check if GPU acceleration should be used for given batch size
222    pub fn should_use_gpu(&self, batch_size: usize) -> bool {
223        match &self.platform {
224            VerificationPlatform::Gpu {
225                compute_units,
226                memory_mb,
227                ..
228            } => {
229                // Heuristic based on GPU capabilities and batch size
230                let min_batch_size = match &self.verification_level {
231                    AdaptiveVerificationLevel::Maximum => 500,
232                    AdaptiveVerificationLevel::High => 200,
233                    AdaptiveVerificationLevel::Balanced => 100,
234                    AdaptiveVerificationLevel::Minimal => 50,
235                    AdaptiveVerificationLevel::Debug => 1000, // Prefer CPU for debugging
236                };
237
238                // Scale threshold by GPU capabilities
239                let capability_factor = (*compute_units as f64 / 16.0).clamp(0.5, 4.0);
240                let memory_factor = (*memory_mb as f64 / 1024.0).clamp(0.5, 2.0);
241                let adjusted_threshold =
242                    (min_batch_size as f64 / (capability_factor * memory_factor)) as usize;
243
244                batch_size >= adjusted_threshold
245            }
246            _ => false,
247        }
248    }
249
250    /// Update verification level dynamically
251    pub fn set_verification_level(&mut self, level: AdaptiveVerificationLevel) {
252        // Update GPU verifier config if present
253        if let Some(ref mut verifier) = self.boundary_verifier {
254            let new_config = Self::create_gpu_verification_config(&self.platform, &level);
255            *verifier = GpuBoundaryVerifier::new(new_config);
256        }
257
258        self.verification_level = level;
259    }
260
261    // Private implementation methods
262
263    /// Detect current execution platform
264    async fn detect_platform() -> Result<VerificationPlatform, AdaptiveVerificationError> {
265        // Try GPU detection first
266        if GpuCliffordAlgebra::new::<3, 0, 0>().await.is_ok() {
267            let backend = Self::detect_gpu_backend();
268            let (memory_mb, compute_units) = Self::estimate_gpu_capabilities().await;
269
270            return Ok(VerificationPlatform::Gpu {
271                backend,
272                memory_mb,
273                compute_units,
274            });
275        }
276
277        // Check for WASM environment
278        if cfg!(target_arch = "wasm32") {
279            let env = Self::detect_wasm_environment();
280            return Ok(VerificationPlatform::Wasm { env });
281        }
282
283        // Default to native CPU
284        let features = Self::detect_cpu_features();
285        Ok(VerificationPlatform::NativeCpu { features })
286    }
287
288    /// Detect GPU backend type
289    fn detect_gpu_backend() -> GpuBackend {
290        // Platform-specific detection logic
291        if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
292            GpuBackend::Metal
293        } else if cfg!(target_os = "windows") {
294            GpuBackend::Dx12
295        } else if cfg!(target_arch = "wasm32") {
296            GpuBackend::WebGpu
297        } else {
298            GpuBackend::Vulkan
299        }
300    }
301
302    /// Estimate GPU capabilities
303    async fn estimate_gpu_capabilities() -> (u64, u32) {
304        // Conservative estimates for broad compatibility
305        // In production, these would query actual GPU capabilities
306        (1024, 16) // 1GB memory, 16 compute units
307    }
308
309    /// Detect WASM execution environment
310    fn detect_wasm_environment() -> WasmEnvironment {
311        // Simplified detection - in practice would check JavaScript globals
312        WasmEnvironment::Browser {
313            engine: "Unknown".to_string(),
314        }
315    }
316
317    /// Detect CPU features
318    fn detect_cpu_features() -> CpuFeatures {
319        CpuFeatures {
320            supports_simd: true, // Assume SIMD support
321            core_count: std::thread::available_parallelism()
322                .map(|n| n.get())
323                .unwrap_or(4),
324            cache_size_kb: 8192, // 8MB L3 cache estimate
325        }
326    }
327
328    /// Determine optimal verification level for platform
329    fn determine_verification_level(platform: &VerificationPlatform) -> AdaptiveVerificationLevel {
330        match platform {
331            VerificationPlatform::NativeCpu { features } => {
332                if features.core_count >= 8 {
333                    AdaptiveVerificationLevel::High
334                } else {
335                    AdaptiveVerificationLevel::Balanced
336                }
337            }
338            VerificationPlatform::Gpu { compute_units, .. } => {
339                if *compute_units >= 32 {
340                    AdaptiveVerificationLevel::Balanced
341                } else {
342                    AdaptiveVerificationLevel::Minimal
343                }
344            }
345            VerificationPlatform::Wasm { .. } => {
346                // WASM has limited debugging capabilities
347                AdaptiveVerificationLevel::Minimal
348            }
349        }
350    }
351
352    /// Determine performance budget for platform
353    fn determine_performance_budget(platform: &VerificationPlatform) -> Duration {
354        match platform {
355            VerificationPlatform::NativeCpu { .. } => Duration::from_millis(50),
356            VerificationPlatform::Gpu { .. } => Duration::from_millis(20),
357            VerificationPlatform::Wasm { .. } => Duration::from_millis(100),
358        }
359    }
360
361    /// Create GPU verification configuration
362    fn create_gpu_verification_config(
363        platform: &VerificationPlatform,
364        level: &AdaptiveVerificationLevel,
365    ) -> VerificationConfig {
366        let strategy = match level {
367            AdaptiveVerificationLevel::Maximum => VerificationStrategy::Strict,
368            AdaptiveVerificationLevel::High => {
369                VerificationStrategy::Statistical { sample_rate: 0.2 }
370            }
371            AdaptiveVerificationLevel::Balanced => {
372                VerificationStrategy::Statistical { sample_rate: 0.1 }
373            }
374            AdaptiveVerificationLevel::Minimal => VerificationStrategy::Boundary,
375            AdaptiveVerificationLevel::Debug => VerificationStrategy::Strict,
376        };
377
378        let budget = Self::determine_performance_budget(platform);
379
380        VerificationConfig {
381            strategy,
382            performance_budget: budget,
383            tolerance: 1e-12,
384            enable_invariant_checking: !matches!(level, AdaptiveVerificationLevel::Minimal),
385        }
386    }
387
388    /// CPU verification implementation
389    async fn cpu_verification<const P: usize, const Q: usize, const R: usize>(
390        &self,
391        a: &VerifiedMultivector<P, Q, R>,
392        b: &VerifiedMultivector<P, Q, R>,
393    ) -> Result<VerifiedMultivector<P, Q, R>, AdaptiveVerificationError> {
394        // Full verification with phantom types
395        let result = a.inner().geometric_product(b.inner());
396        let verified_result = VerifiedMultivector::new(result);
397
398        // Verify mathematical properties based on level
399        match self.verification_level {
400            AdaptiveVerificationLevel::Maximum | AdaptiveVerificationLevel::Debug => {
401                verified_result.verify_invariants()?;
402                // Additional checks for maximum verification
403                self.verify_geometric_product_properties(a, b, &verified_result)?;
404            }
405            AdaptiveVerificationLevel::High => {
406                verified_result.verify_invariants()?;
407            }
408            _ => {
409                // Minimal verification
410            }
411        }
412
413        Ok(verified_result)
414    }
415
416    /// CPU batch verification implementation
417    async fn cpu_batch_verification<const P: usize, const Q: usize, const R: usize>(
418        &self,
419        a_batch: &[VerifiedMultivector<P, Q, R>],
420        b_batch: &[VerifiedMultivector<P, Q, R>],
421    ) -> Result<Vec<VerifiedMultivector<P, Q, R>>, AdaptiveVerificationError> {
422        let mut results = Vec::with_capacity(a_batch.len());
423
424        for (a, b) in a_batch.iter().zip(b_batch.iter()) {
425            let result = self.cpu_verification(a, b).await?;
426            results.push(result);
427        }
428
429        Ok(results)
430    }
431
432    /// GPU batch verification implementation
433    async fn gpu_batch_verification<const P: usize, const Q: usize, const R: usize>(
434        &mut self,
435        a_batch: &[VerifiedMultivector<P, Q, R>],
436        b_batch: &[VerifiedMultivector<P, Q, R>],
437    ) -> Result<Vec<VerifiedMultivector<P, Q, R>>, AdaptiveVerificationError> {
438        if !self.should_use_gpu(a_batch.len()) {
439            return self.cpu_batch_verification(a_batch, b_batch).await;
440        }
441
442        if let (Some(ref mut verifier), Some(ref gpu)) =
443            (&mut self.boundary_verifier, &self.gpu_instance)
444        {
445            verifier
446                .verified_batch_geometric_product(gpu, a_batch, b_batch)
447                .await
448                .map_err(AdaptiveVerificationError::GpuVerification)
449        } else {
450            // Fallback to CPU
451            self.cpu_batch_verification(a_batch, b_batch).await
452        }
453    }
454
455    /// WASM runtime verification implementation
456    async fn wasm_runtime_verification<const P: usize, const Q: usize, const R: usize>(
457        &self,
458        a: &VerifiedMultivector<P, Q, R>,
459        b: &VerifiedMultivector<P, Q, R>,
460    ) -> Result<VerifiedMultivector<P, Q, R>, AdaptiveVerificationError> {
461        // Runtime signature verification
462        if VerifiedMultivector::<P, Q, R>::signature() != (P, Q, R) {
463            return Err(AdaptiveVerificationError::GpuVerification(
464                GpuVerificationError::SignatureMismatch {
465                    expected: (P, Q, R),
466                    actual: VerifiedMultivector::<P, Q, R>::signature(),
467                },
468            ));
469        }
470
471        // Perform operation with runtime checking
472        let result = a.inner().geometric_product(b.inner());
473        let verified_result = VerifiedMultivector::new(result);
474
475        // Basic runtime validation
476        if !verified_result.inner().magnitude().is_finite() {
477            return Err(AdaptiveVerificationError::GpuVerification(
478                GpuVerificationError::InvariantViolation {
479                    invariant: "Result magnitude must be finite".to_string(),
480                },
481            ));
482        }
483
484        Ok(verified_result)
485    }
486
487    /// WASM batch verification implementation
488    async fn wasm_batch_verification<const P: usize, const Q: usize, const R: usize>(
489        &self,
490        a_batch: &[VerifiedMultivector<P, Q, R>],
491        b_batch: &[VerifiedMultivector<P, Q, R>],
492    ) -> Result<Vec<VerifiedMultivector<P, Q, R>>, AdaptiveVerificationError> {
493        let mut results = Vec::with_capacity(a_batch.len());
494
495        for (a, b) in a_batch.iter().zip(b_batch.iter()) {
496            let result = self.wasm_runtime_verification(a, b).await?;
497            results.push(result);
498        }
499
500        Ok(results)
501    }
502
503    /// Verify geometric product mathematical properties
504    fn verify_geometric_product_properties<const P: usize, const Q: usize, const R: usize>(
505        &self,
506        a: &VerifiedMultivector<P, Q, R>,
507        b: &VerifiedMultivector<P, Q, R>,
508        result: &VerifiedMultivector<P, Q, R>,
509    ) -> Result<(), AdaptiveVerificationError> {
510        // Verify magnitude inequality: |a * b| <= |a| * |b|
511        let result_mag = result.inner().magnitude();
512        let a_mag = a.inner().magnitude();
513        let b_mag = b.inner().magnitude();
514
515        if result_mag > a_mag * b_mag + 1e-12 {
516            return Err(AdaptiveVerificationError::GpuVerification(
517                GpuVerificationError::InvariantViolation {
518                    invariant: format!(
519                        "Magnitude inequality violated: {} > {} * {}",
520                        result_mag, a_mag, b_mag
521                    ),
522                },
523            ));
524        }
525
526        Ok(())
527    }
528}
529
530/// Platform capabilities interface for adaptive optimization
531pub trait PlatformCapabilities {
532    /// Get maximum recommended batch size for the platform
533    fn max_batch_size(&self) -> usize;
534
535    /// Get optimal verification strategy for given workload
536    fn optimal_strategy(&self, workload_size: usize) -> VerificationStrategy;
537
538    /// Check if platform supports concurrent verification
539    fn supports_concurrent_verification(&self) -> bool;
540
541    /// Get platform-specific performance metrics
542    fn performance_characteristics(&self) -> PlatformPerformanceProfile;
543}
544
545#[derive(Debug, Clone)]
546pub struct PlatformPerformanceProfile {
547    pub verification_overhead_percent: f64,
548    pub memory_bandwidth_gbps: f64,
549    pub compute_throughput_gflops: f64,
550    pub latency_microseconds: f64,
551}
552
553impl PlatformCapabilities for VerificationPlatform {
554    fn max_batch_size(&self) -> usize {
555        match self {
556            VerificationPlatform::NativeCpu { features } => features.core_count * 1000,
557            VerificationPlatform::Gpu { memory_mb, .. } => {
558                (*memory_mb as usize * 1024 * 1024) / (8 * 64) // Rough estimate
559            }
560            VerificationPlatform::Wasm { .. } => {
561                10000 // Conservative for browser memory limits
562            }
563        }
564    }
565
566    fn optimal_strategy(&self, workload_size: usize) -> VerificationStrategy {
567        match self {
568            VerificationPlatform::NativeCpu { .. } => {
569                if workload_size < 100 {
570                    VerificationStrategy::Strict
571                } else {
572                    VerificationStrategy::Statistical { sample_rate: 0.1 }
573                }
574            }
575            VerificationPlatform::Gpu { .. } => {
576                if workload_size < 50 {
577                    VerificationStrategy::Boundary
578                } else {
579                    VerificationStrategy::Statistical { sample_rate: 0.05 }
580                }
581            }
582            VerificationPlatform::Wasm { .. } => {
583                VerificationStrategy::Statistical { sample_rate: 0.02 }
584            }
585        }
586    }
587
588    fn supports_concurrent_verification(&self) -> bool {
589        match self {
590            VerificationPlatform::NativeCpu { features } => features.core_count > 1,
591            VerificationPlatform::Gpu { .. } => true,
592            VerificationPlatform::Wasm { .. } => false, // Limited by JS single-threading
593        }
594    }
595
596    fn performance_characteristics(&self) -> PlatformPerformanceProfile {
597        match self {
598            VerificationPlatform::NativeCpu { features } => PlatformPerformanceProfile {
599                verification_overhead_percent: 5.0,
600                memory_bandwidth_gbps: 50.0,
601                compute_throughput_gflops: features.core_count as f64 * 100.0,
602                latency_microseconds: 1.0,
603            },
604            VerificationPlatform::Gpu { compute_units, .. } => PlatformPerformanceProfile {
605                verification_overhead_percent: 15.0,
606                memory_bandwidth_gbps: 200.0,
607                compute_throughput_gflops: *compute_units as f64 * 50.0,
608                latency_microseconds: 100.0,
609            },
610            VerificationPlatform::Wasm { .. } => PlatformPerformanceProfile {
611                verification_overhead_percent: 25.0,
612                memory_bandwidth_gbps: 10.0,
613                compute_throughput_gflops: 10.0,
614                latency_microseconds: 1000.0,
615            },
616        }
617    }
618}
619
620#[cfg(test)]
621mod tests {
622    use super::*;
623
624    #[test]
625    fn test_cpu_features_detection() {
626        let features = AdaptiveVerifier::detect_cpu_features();
627        assert!(features.core_count > 0);
628        assert!(features.cache_size_kb > 0);
629    }
630
631    #[test]
632    fn test_verification_level_determination() {
633        let cpu_platform = VerificationPlatform::NativeCpu {
634            features: CpuFeatures {
635                supports_simd: true,
636                core_count: 8,
637                cache_size_kb: 8192,
638            },
639        };
640
641        let level = AdaptiveVerifier::determine_verification_level(&cpu_platform);
642        assert_eq!(level, AdaptiveVerificationLevel::High);
643
644        let gpu_platform = VerificationPlatform::Gpu {
645            backend: GpuBackend::Vulkan,
646            memory_mb: 2048,
647            compute_units: 16,
648        };
649
650        let level = AdaptiveVerifier::determine_verification_level(&gpu_platform);
651        assert_eq!(level, AdaptiveVerificationLevel::Minimal);
652    }
653
654    #[test]
655    fn test_platform_capabilities() {
656        let platform = VerificationPlatform::NativeCpu {
657            features: CpuFeatures {
658                supports_simd: true,
659                core_count: 4,
660                cache_size_kb: 8192,
661            },
662        };
663
664        assert_eq!(platform.max_batch_size(), 4000);
665        assert!(platform.supports_concurrent_verification());
666
667        let profile = platform.performance_characteristics();
668        assert_eq!(profile.verification_overhead_percent, 5.0);
669        assert_eq!(profile.compute_throughput_gflops, 400.0);
670    }
671
672    #[tokio::test]
673    async fn test_adaptive_verifier_creation() {
674        // This test may fail in environments without GPU access
675        match AdaptiveVerifier::new().await {
676            Ok(verifier) => {
677                assert!(verifier.performance_budget() > Duration::ZERO);
678            }
679            Err(AdaptiveVerificationError::PlatformDetection(_)) => {
680                // Expected in limited environments
681            }
682            Err(e) => panic!("Unexpected error: {:?}", e),
683        }
684    }
685
686    #[tokio::test]
687    async fn test_verification_with_config() {
688        match AdaptiveVerifier::with_config(
689            AdaptiveVerificationLevel::Minimal,
690            Duration::from_millis(5),
691        )
692        .await
693        {
694            Ok(verifier) => {
695                assert_eq!(
696                    *verifier.verification_level(),
697                    AdaptiveVerificationLevel::Minimal
698                );
699                assert_eq!(verifier.performance_budget(), Duration::from_millis(5));
700            }
701            Err(_) => {
702                // Expected in limited environments
703            }
704        }
705    }
706}