Skip to main content

aura_sync/core/
config.rs

1//! Unified configuration for aura-sync protocols
2//!
3//! This module provides a centralized configuration system that consolidates
4//! all timeout, retry, batch size, and peer management settings scattered
5//! across the aura-sync crate into a single, coherent structure.
6
7use aura_core::effects::RandomEffects;
8use serde::{Deserialize, Serialize};
9use std::env;
10use std::time::Duration;
11
12/// Master configuration for all aura-sync operations
13#[derive(Debug, Clone, Serialize, Deserialize, Default)]
14pub struct SyncConfig {
15    /// Network and transport configuration
16    pub network: NetworkConfig,
17    /// Retry and backoff configuration
18    pub retry: RetryConfig,
19    /// Batch processing configuration
20    pub batching: BatchConfig,
21    /// Peer management configuration
22    pub peer_management: PeerManagementConfig,
23    /// Protocol-specific configurations
24    pub protocols: ProtocolConfigs,
25    /// Performance and load balancing
26    pub performance: PerformanceConfig,
27}
28
29/// Network timing and connection configuration
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct NetworkConfig {
32    /// Base interval between sync rounds (default: 30s)
33    pub base_sync_interval: Duration,
34    /// Minimum interval between syncs with the same peer (default: 10s)
35    pub min_sync_interval: Duration,
36    /// Maximum time to wait for any sync operation (default: 120s)
37    pub sync_timeout: Duration,
38    /// Cleanup interval for stale sessions (default: 5 minutes)
39    pub cleanup_interval: Duration,
40}
41
42impl Default for NetworkConfig {
43    fn default() -> Self {
44        Self {
45            base_sync_interval: Duration::from_secs(30),
46            min_sync_interval: Duration::from_secs(10),
47            sync_timeout: Duration::from_secs(120),
48            cleanup_interval: Duration::from_secs(300),
49        }
50    }
51}
52
53/// Retry and exponential backoff configuration
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct RetryConfig {
56    /// Maximum number of retry attempts (default: 3)
57    pub max_retries: u32,
58    /// Base delay for exponential backoff (default: 500ms)
59    pub base_delay: Duration,
60    /// Maximum delay between retries (default: 30s)
61    pub max_delay: Duration,
62    /// Jitter factor for randomizing delays 0.0-1.0 (default: 0.1)
63    pub jitter_factor: f64,
64}
65
66impl Default for RetryConfig {
67    fn default() -> Self {
68        Self {
69            max_retries: 3,
70            base_delay: Duration::from_millis(500),
71            max_delay: Duration::from_secs(30),
72            jitter_factor: 0.1,
73        }
74    }
75}
76
77impl RetryConfig {
78    /// Calculate delay for attempt number with jitter
79    pub async fn delay_for_attempt<R: RandomEffects + ?Sized>(
80        &self,
81        attempt: u32,
82        random: &R,
83    ) -> Duration {
84        let base_ms = self.base_delay.as_millis() as f64;
85        let exponential_delay = base_ms * 2_f64.powi(attempt as i32);
86        let jitter_sample = random.random_range(0, 10_000).await as f64 / 10_000.0;
87        let jittered_delay = exponential_delay * (1.0 + self.jitter_factor * jitter_sample);
88        let clamped_delay = jittered_delay.min(self.max_delay.as_millis() as f64);
89        Duration::from_millis(clamped_delay as u64)
90    }
91
92    /// Check if should retry for given attempt number
93    pub fn should_retry(&self, attempt: u32) -> bool {
94        attempt < self.max_retries
95    }
96}
97
98/// Batch processing and throughput configuration
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct BatchConfig {
101    /// Default batch size for operations (default: 128)
102    pub default_batch_size: u32,
103    /// Maximum operations per synchronization round (default: 1000)
104    pub max_operations_per_round: u32,
105    /// Enable compression for large batches (default: true)
106    pub enable_compression: bool,
107    /// Minimum batch size before forcing processing (default: 10)
108    pub min_batch_size: u32,
109    /// Maximum time to wait before processing partial batch (default: 5s)
110    pub batch_timeout: Duration,
111}
112
113impl Default for BatchConfig {
114    fn default() -> Self {
115        Self {
116            default_batch_size: 128,
117            max_operations_per_round: 1000,
118            enable_compression: true,
119            min_batch_size: 10,
120            batch_timeout: Duration::from_secs(5),
121        }
122    }
123}
124
125/// Peer selection and management configuration
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct PeerManagementConfig {
128    /// Maximum concurrent synchronization sessions (default: 5)
129    pub max_concurrent_syncs: u32,
130    /// Minimum priority threshold for scheduling (default: 10)
131    pub min_priority_threshold: u32,
132    /// Priority boost for peers with pending operations (default: 20)
133    pub pending_operations_boost: u32,
134    /// Penalty for peers with recent failures (default: 15)
135    pub failure_penalty: u32,
136    /// Time to wait before retrying failed peer (default: 5 minutes)
137    pub failure_backoff_duration: Duration,
138}
139
140impl Default for PeerManagementConfig {
141    fn default() -> Self {
142        Self {
143            max_concurrent_syncs: 5,
144            min_priority_threshold: 10,
145            pending_operations_boost: 20,
146            failure_penalty: 15,
147            failure_backoff_duration: Duration::from_secs(300),
148        }
149    }
150}
151
152/// Protocol-specific configuration grouping
153#[derive(Debug, Clone, Serialize, Deserialize, Default)]
154pub struct ProtocolConfigs {
155    /// OTA upgrade protocol configuration
156    pub ota_upgrade: OTAConfig,
157    /// Receipt verification configuration
158    pub verification: VerificationConfig,
159    /// Anti-entropy protocol configuration
160    pub anti_entropy: AntiEntropyConfig,
161}
162
163/// OTA (Over-The-Air) upgrade protocol configuration
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct OTAConfig {
166    /// Timeout for soft fork proposals (default: 1 hour)
167    pub soft_fork_timeout: Duration,
168    /// Timeout for hard fork proposals (default: 1 week)
169    pub hard_fork_timeout: Duration,
170    /// Default readiness threshold (default: 2)
171    pub default_threshold: u32,
172    /// Maximum session duration (default: 24 hours)
173    pub max_session_duration: Duration,
174    /// Enable automatic validation (default: true)
175    pub enable_auto_validation: bool,
176}
177
178impl Default for OTAConfig {
179    fn default() -> Self {
180        Self {
181            soft_fork_timeout: Duration::from_secs(3600), // 1 hour
182            hard_fork_timeout: Duration::from_secs(7 * 24 * 3600), // 1 week
183            default_threshold: 2,
184            max_session_duration: Duration::from_secs(24 * 3600), // 24 hours
185            enable_auto_validation: true,
186        }
187    }
188}
189
190/// Receipt verification configuration
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct VerificationConfig {
193    /// Timeout for receipt verification (default: 30s)
194    pub verification_timeout: Duration,
195    /// Required confirmations for verification (default: 2)
196    pub required_confirmations: u32,
197    /// Maximum verification attempts (default: 3)
198    pub max_verification_attempts: u32,
199}
200
201impl Default for VerificationConfig {
202    fn default() -> Self {
203        Self {
204            verification_timeout: Duration::from_secs(30),
205            required_confirmations: 2,
206            max_verification_attempts: 3,
207        }
208    }
209}
210
211/// Anti-entropy protocol configuration
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct AntiEntropyConfig {
214    /// Minimum time between anti-entropy rounds (default: 60s)
215    pub min_sync_interval: Duration,
216    /// Digest comparison timeout (default: 10s)
217    pub digest_timeout: Duration,
218    /// Maximum digest entries per message (default: 1000)
219    pub max_digest_entries: u32,
220}
221
222impl Default for AntiEntropyConfig {
223    fn default() -> Self {
224        Self {
225            min_sync_interval: Duration::from_secs(60),
226            digest_timeout: Duration::from_secs(10),
227            max_digest_entries: 1000,
228        }
229    }
230}
231
232/// Performance tuning and resource management configuration
233#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct PerformanceConfig {
235    /// Maximum CPU usage percentage 0-100 (default: 80)
236    pub max_cpu_usage: u32,
237    /// Maximum network bandwidth usage in bytes/sec (default: 10MB/s)
238    pub max_network_bandwidth: u64,
239    /// Enable adaptive scheduling based on system load (default: true)
240    pub adaptive_scheduling: bool,
241    /// Memory limit for batching operations in bytes (default: 100MB)
242    pub memory_limit: u64,
243}
244
245impl Default for PerformanceConfig {
246    fn default() -> Self {
247        Self {
248            max_cpu_usage: 80,
249            max_network_bandwidth: 10 * 1024 * 1024, // 10 MB/s
250            adaptive_scheduling: true,
251            memory_limit: 100 * 1024 * 1024, // 100 MB
252        }
253    }
254}
255
256impl PerformanceConfig {
257    /// Validate performance configuration values
258    pub fn validate(&self) -> Result<(), String> {
259        if self.max_cpu_usage > 100 {
260            return Err("max_cpu_usage must be <= 100".to_string());
261        }
262        if self.max_network_bandwidth == 0 {
263            return Err("max_network_bandwidth must be > 0".to_string());
264        }
265        if self.memory_limit < 1024 * 1024 {
266            // 1MB minimum
267            return Err("memory_limit must be >= 1MB".to_string());
268        }
269        Ok(())
270    }
271}
272
273impl SyncConfig {
274    /// Create a configuration optimized for testing
275    pub fn for_testing() -> Self {
276        Self {
277            network: NetworkConfig {
278                base_sync_interval: Duration::from_millis(100),
279                min_sync_interval: Duration::from_millis(50),
280                sync_timeout: Duration::from_secs(5),
281                cleanup_interval: Duration::from_secs(10),
282            },
283            retry: RetryConfig {
284                max_retries: 2,
285                base_delay: Duration::from_millis(10),
286                max_delay: Duration::from_millis(100),
287                jitter_factor: 0.0, // No jitter in tests for predictability
288            },
289            batching: BatchConfig {
290                default_batch_size: 10,
291                max_operations_per_round: 50,
292                enable_compression: false, // Faster for tests
293                min_batch_size: 1,
294                batch_timeout: Duration::from_millis(100),
295            },
296            performance: PerformanceConfig {
297                max_cpu_usage: 100, // No limits in tests
298                max_network_bandwidth: u64::MAX,
299                adaptive_scheduling: false, // Predictable behavior
300                memory_limit: u64::MAX,
301            },
302            ..Self::default()
303        }
304    }
305
306    /// Create a configuration optimized for production
307    pub fn for_production() -> Self {
308        Self {
309            network: NetworkConfig {
310                base_sync_interval: Duration::from_secs(60), // Less frequent in prod
311                sync_timeout: Duration::from_secs(300),      // Longer timeout
312                ..NetworkConfig::default()
313            },
314            retry: RetryConfig {
315                max_retries: 5,                     // More retries in production
316                max_delay: Duration::from_secs(60), // Longer max delay
317                ..RetryConfig::default()
318            },
319            performance: PerformanceConfig {
320                max_cpu_usage: 60, // Conservative CPU usage
321                adaptive_scheduling: true,
322                ..PerformanceConfig::default()
323            },
324            ..Self::default()
325        }
326    }
327
328    /// Validate the entire configuration
329    pub fn validate(&self) -> Result<(), String> {
330        // Validate performance config
331        self.performance.validate()?;
332
333        // Validate network timeouts make sense
334        if self.network.min_sync_interval >= self.network.base_sync_interval {
335            return Err("min_sync_interval must be less than base_sync_interval".to_string());
336        }
337
338        // Validate retry config
339        if self.retry.jitter_factor < 0.0 || self.retry.jitter_factor > 1.0 {
340            return Err("jitter_factor must be between 0.0 and 1.0".to_string());
341        }
342
343        // Validate batch config
344        if self.batching.min_batch_size > self.batching.default_batch_size {
345            return Err("min_batch_size must be <= default_batch_size".to_string());
346        }
347
348        Ok(())
349    }
350
351    /// Load configuration from environment variables with fallbacks
352    pub fn from_env() -> Self {
353        let mut config = Self::default();
354
355        // Network
356        config.network.base_sync_interval = duration_secs(
357            "AURA_SYNC_BASE_SYNC_INTERVAL_SECS",
358            config.network.base_sync_interval,
359        );
360        config.network.min_sync_interval = duration_secs(
361            "AURA_SYNC_MIN_SYNC_INTERVAL_SECS",
362            config.network.min_sync_interval,
363        );
364        config.network.sync_timeout =
365            duration_secs("AURA_SYNC_TIMEOUT_SECS", config.network.sync_timeout);
366        config.network.cleanup_interval = duration_secs(
367            "AURA_SYNC_CLEANUP_INTERVAL_SECS",
368            config.network.cleanup_interval,
369        );
370
371        // Retry
372        config.retry.max_retries =
373            parse_u32("AURA_SYNC_RETRY_MAX_RETRIES", config.retry.max_retries);
374        config.retry.base_delay =
375            duration_millis("AURA_SYNC_RETRY_BASE_DELAY_MS", config.retry.base_delay);
376        config.retry.max_delay =
377            duration_millis("AURA_SYNC_RETRY_MAX_DELAY_MS", config.retry.max_delay);
378        config.retry.jitter_factor =
379            parse_f64("AURA_SYNC_RETRY_JITTER", config.retry.jitter_factor);
380
381        // Batching
382        config.batching.default_batch_size = parse_u32(
383            "AURA_SYNC_DEFAULT_BATCH_SIZE",
384            config.batching.default_batch_size,
385        );
386        config.batching.max_operations_per_round = parse_u32(
387            "AURA_SYNC_MAX_OPS_PER_ROUND",
388            config.batching.max_operations_per_round,
389        );
390        config.batching.enable_compression = parse_bool(
391            "AURA_SYNC_ENABLE_COMPRESSION",
392            config.batching.enable_compression,
393        );
394        config.batching.min_batch_size =
395            parse_u32("AURA_SYNC_MIN_BATCH_SIZE", config.batching.min_batch_size);
396        config.batching.batch_timeout =
397            duration_millis("AURA_SYNC_BATCH_TIMEOUT_MS", config.batching.batch_timeout);
398
399        // Peer management
400        config.peer_management.max_concurrent_syncs = parse_u32(
401            "AURA_SYNC_MAX_CONCURRENT_SYNCS",
402            config.peer_management.max_concurrent_syncs,
403        );
404        config.peer_management.min_priority_threshold = parse_u32(
405            "AURA_SYNC_MIN_PRIORITY_THRESHOLD",
406            config.peer_management.min_priority_threshold,
407        );
408        config.peer_management.pending_operations_boost = parse_u32(
409            "AURA_SYNC_PENDING_OPS_BOOST",
410            config.peer_management.pending_operations_boost,
411        );
412        config.peer_management.failure_penalty = parse_u32(
413            "AURA_SYNC_FAILURE_PENALTY",
414            config.peer_management.failure_penalty,
415        );
416        config.peer_management.failure_backoff_duration = duration_secs(
417            "AURA_SYNC_FAILURE_BACKOFF_SECS",
418            config.peer_management.failure_backoff_duration,
419        );
420
421        // Protocols
422        config.protocols.anti_entropy.min_sync_interval = duration_secs(
423            "AURA_SYNC_ANTI_ENTROPY_MIN_INTERVAL_SECS",
424            config.protocols.anti_entropy.min_sync_interval,
425        );
426        config.protocols.anti_entropy.digest_timeout = duration_secs(
427            "AURA_SYNC_ANTI_ENTROPY_DIGEST_TIMEOUT_SECS",
428            config.protocols.anti_entropy.digest_timeout,
429        );
430        config.protocols.anti_entropy.max_digest_entries = parse_u32(
431            "AURA_SYNC_ANTI_ENTROPY_MAX_DIGEST_ENTRIES",
432            config.protocols.anti_entropy.max_digest_entries,
433        );
434
435        config.protocols.verification.verification_timeout = duration_secs(
436            "AURA_SYNC_VERIFICATION_TIMEOUT_SECS",
437            config.protocols.verification.verification_timeout,
438        );
439        config.protocols.verification.required_confirmations = parse_u32(
440            "AURA_SYNC_VERIFICATION_CONFIRMATIONS",
441            config.protocols.verification.required_confirmations,
442        );
443        config.protocols.verification.max_verification_attempts = parse_u32(
444            "AURA_SYNC_VERIFICATION_MAX_ATTEMPTS",
445            config.protocols.verification.max_verification_attempts,
446        );
447
448        config.protocols.ota_upgrade.soft_fork_timeout = duration_secs(
449            "AURA_SYNC_OTA_SOFT_FORK_TIMEOUT_SECS",
450            config.protocols.ota_upgrade.soft_fork_timeout,
451        );
452        config.protocols.ota_upgrade.hard_fork_timeout = duration_secs(
453            "AURA_SYNC_OTA_HARD_FORK_TIMEOUT_SECS",
454            config.protocols.ota_upgrade.hard_fork_timeout,
455        );
456        config.protocols.ota_upgrade.default_threshold = parse_u32(
457            "AURA_SYNC_OTA_DEFAULT_THRESHOLD",
458            config.protocols.ota_upgrade.default_threshold,
459        );
460        config.protocols.ota_upgrade.max_session_duration = duration_secs(
461            "AURA_SYNC_OTA_MAX_SESSION_DURATION_SECS",
462            config.protocols.ota_upgrade.max_session_duration,
463        );
464        config.protocols.ota_upgrade.enable_auto_validation = parse_bool(
465            "AURA_SYNC_OTA_ENABLE_AUTO_VALIDATION",
466            config.protocols.ota_upgrade.enable_auto_validation,
467        );
468
469        // Performance
470        config.performance.max_cpu_usage =
471            parse_u32("AURA_SYNC_MAX_CPU_USAGE", config.performance.max_cpu_usage);
472        config.performance.max_network_bandwidth = parse_u64(
473            "AURA_SYNC_MAX_NETWORK_BANDWIDTH",
474            config.performance.max_network_bandwidth,
475        );
476        config.performance.adaptive_scheduling = parse_bool(
477            "AURA_SYNC_ADAPTIVE_SCHEDULING",
478            config.performance.adaptive_scheduling,
479        );
480        config.performance.memory_limit = parse_u64(
481            "AURA_SYNC_MEMORY_LIMIT_BYTES",
482            config.performance.memory_limit,
483        );
484
485        config
486    }
487
488    /// Get a configuration builder for custom setups
489    pub fn builder() -> SyncConfigBuilder {
490        SyncConfigBuilder::new()
491    }
492}
493
494/// Builder pattern for creating custom configurations
495pub struct SyncConfigBuilder {
496    config: SyncConfig,
497}
498
499impl SyncConfigBuilder {
500    /// Create a new configuration builder with default settings
501    pub fn new() -> Self {
502        Self {
503            config: SyncConfig::default(),
504        }
505    }
506
507    /// Set network configuration
508    pub fn network(mut self, network: NetworkConfig) -> Self {
509        self.config.network = network;
510        self
511    }
512
513    /// Set retry configuration
514    pub fn retry(mut self, retry: RetryConfig) -> Self {
515        self.config.retry = retry;
516        self
517    }
518
519    /// Set batching configuration
520    pub fn batching(mut self, batching: BatchConfig) -> Self {
521        self.config.batching = batching;
522        self
523    }
524
525    /// Set peer management configuration
526    pub fn peer_management(mut self, peer_management: PeerManagementConfig) -> Self {
527        self.config.peer_management = peer_management;
528        self
529    }
530
531    /// Set protocol configurations
532    pub fn protocols(mut self, protocols: ProtocolConfigs) -> Self {
533        self.config.protocols = protocols;
534        self
535    }
536
537    /// Set performance configuration
538    pub fn performance(mut self, performance: PerformanceConfig) -> Self {
539        self.config.performance = performance;
540        self
541    }
542
543    /// Build and validate the configuration
544    pub fn build(self) -> Result<SyncConfig, String> {
545        self.config.validate()?;
546        Ok(self.config)
547    }
548}
549
550fn parse_bool(key: &str, default: bool) -> bool {
551    env::var(key)
552        .ok()
553        .as_deref()
554        .map(|v| matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes" | "on"))
555        .unwrap_or(default)
556}
557
558fn parse_u32(key: &str, default: u32) -> u32 {
559    env::var(key)
560        .ok()
561        .and_then(|v| v.parse::<u32>().ok())
562        .unwrap_or(default)
563}
564
565fn parse_u64(key: &str, default: u64) -> u64 {
566    env::var(key)
567        .ok()
568        .and_then(|v| v.parse::<u64>().ok())
569        .unwrap_or(default)
570}
571
572fn parse_f64(key: &str, default: f64) -> f64 {
573    env::var(key)
574        .ok()
575        .and_then(|v| v.parse::<f64>().ok())
576        .unwrap_or(default)
577}
578
579fn duration_secs(key: &str, default: Duration) -> Duration {
580    env::var(key)
581        .ok()
582        .and_then(|v| v.parse::<u64>().ok())
583        .map(Duration::from_secs)
584        .unwrap_or(default)
585}
586
587fn duration_millis(key: &str, default: Duration) -> Duration {
588    env::var(key)
589        .ok()
590        .and_then(|v| v.parse::<u64>().ok())
591        .map(Duration::from_millis)
592        .unwrap_or(default)
593}
594
595impl Default for SyncConfigBuilder {
596    fn default() -> Self {
597        Self::new()
598    }
599}
600
601#[cfg(test)]
602mod tests {
603    use super::*;
604
605    #[test]
606    fn test_default_config_is_valid() {
607        let config = SyncConfig::default();
608        assert!(config.validate().is_ok());
609    }
610
611    #[test]
612    fn test_testing_config() {
613        let config = SyncConfig::for_testing();
614        assert!(config.validate().is_ok());
615        assert_eq!(config.retry.jitter_factor, 0.0);
616        assert!(!config.batching.enable_compression);
617    }
618
619    #[test]
620    fn test_production_config() {
621        let config = SyncConfig::for_production();
622        assert!(config.validate().is_ok());
623        assert_eq!(config.retry.max_retries, 5);
624        assert_eq!(config.performance.max_cpu_usage, 60);
625    }
626
627    #[tokio::test]
628    async fn test_retry_config_delay_calculation() {
629        let retry_config = RetryConfig::default();
630        let fixture = aura_testkit::create_test_fixture()
631            .await
632            .unwrap_or_else(|_| panic!("Failed to create test fixture"));
633        let device_id = fixture.device_id();
634        let composer = aura_testkit::foundation::TestEffectComposer::new(
635            aura_core::effects::ExecutionMode::Testing,
636            device_id,
637        );
638        let handler = composer
639            .build_mock_handler()
640            .unwrap_or_else(|err| panic!("build mock handler for retry tests: {err}"));
641
642        let delay1 = retry_config.delay_for_attempt(0, handler.as_ref()).await;
643        let delay2 = retry_config.delay_for_attempt(1, handler.as_ref()).await;
644
645        // Second attempt should have longer delay (exponential backoff)
646        assert!(delay2 > delay1);
647
648        // Should not exceed max delay
649        let delay_long = retry_config.delay_for_attempt(10, handler.as_ref()).await;
650        assert!(delay_long <= retry_config.max_delay);
651    }
652
653    #[test]
654    fn test_config_validation() {
655        let mut config = SyncConfig::default();
656
657        // Test invalid CPU usage
658        config.performance.max_cpu_usage = 150;
659        assert!(config.validate().is_err());
660
661        config.performance.max_cpu_usage = 80;
662
663        // Test invalid jitter factor
664        config.retry.jitter_factor = 2.0;
665        assert!(config.validate().is_err());
666
667        config.retry.jitter_factor = 0.1;
668        assert!(config.validate().is_ok());
669    }
670
671    #[test]
672    fn test_config_builder() {
673        let config = SyncConfig::builder()
674            .retry(RetryConfig {
675                max_retries: 10,
676                ..RetryConfig::default()
677            })
678            .build()
679            .unwrap();
680
681        assert_eq!(config.retry.max_retries, 10);
682    }
683
684    #[test]
685    fn test_builder_validation() {
686        let result = SyncConfig::builder()
687            .performance(PerformanceConfig {
688                max_cpu_usage: 150, // Invalid
689                ..PerformanceConfig::default()
690            })
691            .build();
692
693        assert!(result.is_err());
694    }
695}