use std::path::Path;
#[derive(Debug, Clone)]
pub struct CitlResult {
pub success: bool,
pub compilation_rate: f64,
pub files_processed: usize,
pub iterations_used: usize,
pub fixes_applied: usize,
}
impl CitlResult {
pub fn empty_success() -> Self {
Self {
success: true,
compilation_rate: 1.0,
files_processed: 0,
iterations_used: 0,
fixes_applied: 0,
}
}
pub fn from_stats(compiled: usize, total: usize, iterations: usize, fixes: usize) -> Self {
let rate = if total == 0 {
1.0
} else {
compiled as f64 / total as f64
};
Self {
success: rate >= 1.0,
compilation_rate: rate,
files_processed: total,
iterations_used: iterations,
fixes_applied: fixes,
}
}
pub fn meets_target(&self, target_rate: f64) -> bool {
self.compilation_rate >= target_rate
}
pub fn failures(&self) -> usize {
self.files_processed.saturating_sub(self.compiled_count())
}
pub fn compiled_count(&self) -> usize {
(self.files_processed as f64 * self.compilation_rate).round() as usize
}
}
#[derive(Debug, Clone)]
pub struct CitlConfig {
pub max_iterations: usize,
pub confidence_threshold: f64,
pub verbose: bool,
}
impl Default for CitlConfig {
fn default() -> Self {
Self {
max_iterations: 10,
confidence_threshold: 0.8,
verbose: false,
}
}
}
impl CitlConfig {
pub fn validate(&self) -> Result<(), &'static str> {
if self.max_iterations == 0 {
return Err("Max iterations must be at least 1");
}
if self.max_iterations > 100 {
return Err("Max iterations cannot exceed 100");
}
if self.confidence_threshold < 0.0 || self.confidence_threshold > 1.0 {
return Err("Confidence threshold must be between 0.0 and 1.0");
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct QualityTargets {
pub min_tdg: f64,
pub max_tdg: f64,
pub max_complexity: u32,
pub min_coverage: u32,
}
impl Default for QualityTargets {
fn default() -> Self {
Self {
min_tdg: 0.0,
max_tdg: 2.0,
max_complexity: 10,
min_coverage: 80,
}
}
}
impl QualityTargets {
pub fn validate(&self) -> Result<(), &'static str> {
if self.min_tdg < 0.0 {
return Err("Min TDG cannot be negative");
}
if self.max_tdg < self.min_tdg {
return Err("Max TDG must be >= min TDG");
}
if self.max_complexity == 0 {
return Err("Max complexity must be at least 1");
}
if self.min_coverage > 100 {
return Err("Min coverage cannot exceed 100%");
}
Ok(())
}
pub fn tdg_ok(&self, tdg: f64) -> bool {
tdg >= self.min_tdg && tdg <= self.max_tdg
}
pub fn complexity_ok(&self, complexity: u32) -> bool {
complexity <= self.max_complexity
}
pub fn coverage_ok(&self, coverage_percent: f64) -> bool {
coverage_percent >= self.min_coverage as f64
}
}
#[derive(Debug, Clone)]
pub struct DoctestConfig {
pub module_prefix: String,
pub include_classes: bool,
pub include_pytest: bool,
}
impl Default for DoctestConfig {
fn default() -> Self {
Self {
module_prefix: String::new(),
include_classes: true,
include_pytest: false,
}
}
}
impl DoctestConfig {
pub fn module_name(&self, relative_path: &Path) -> String {
let base = relative_path
.with_extension("")
.to_string_lossy()
.replace(std::path::MAIN_SEPARATOR, ".");
if self.module_prefix.is_empty() {
base
} else {
format!("{}.{}", self.module_prefix, base)
}
}
}
#[derive(Debug, Clone, Default)]
pub struct DoctestSummary {
pub files_processed: usize,
pub files_with_examples: usize,
pub functions_with_examples: usize,
pub total_doctests: usize,
pub total_pytest: usize,
}
impl DoctestSummary {
pub fn total_examples(&self) -> usize {
self.total_doctests + self.total_pytest
}
pub fn has_examples(&self) -> bool {
self.total_examples() > 0
}
pub fn extraction_rate(&self) -> f64 {
if self.files_processed == 0 {
0.0
} else {
self.files_with_examples as f64 / self.files_processed as f64 * 100.0
}
}
}
#[derive(Debug, Clone, Default)]
pub struct FileFilter {
pub skip_hidden: bool,
pub skip_pycache: bool,
pub skip_venv: bool,
pub skip_test_files: bool,
pub extensions: Vec<String>,
}
impl FileFilter {
pub fn python() -> Self {
Self {
skip_hidden: true,
skip_pycache: true,
skip_venv: true,
skip_test_files: false,
extensions: vec!["py".to_string()],
}
}
pub fn python_no_tests() -> Self {
Self {
skip_test_files: true,
..Self::python()
}
}
pub fn should_skip_dir(&self, name: &str) -> bool {
if self.skip_hidden && name.starts_with('.') {
return true;
}
if self.skip_pycache && name == "__pycache__" {
return true;
}
if self.skip_venv && (name == "venv" || name == ".venv" || name == "env") {
return true;
}
false
}
pub fn matches_file(&self, path: &Path) -> bool {
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
if !self.extensions.is_empty() && !self.extensions.contains(&ext.to_string()) {
return false;
}
if self.skip_test_files {
let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
if name.starts_with("test_") || name.ends_with("_test.py") {
return false;
}
}
true
}
pub fn is_test_file(path: &Path) -> bool {
let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
name.starts_with("test_") || name.ends_with("_test.py")
}
}
#[derive(Debug, Clone)]
pub struct LambdaConfig {
pub runtime: LambdaRuntime,
pub architecture: Architecture,
pub memory_mb: u32,
pub timeout_secs: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LambdaRuntime {
ProvidedAl2,
#[default]
ProvidedAl2023,
}
impl LambdaRuntime {
pub fn as_str(&self) -> &'static str {
match self {
Self::ProvidedAl2 => "provided.al2",
Self::ProvidedAl2023 => "provided.al2023",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Architecture {
#[default]
X86_64,
Arm64,
}
impl Architecture {
pub fn as_str(&self) -> &'static str {
match self {
Self::X86_64 => "x86_64",
Self::Arm64 => "arm64",
}
}
pub fn target_triple(&self) -> &'static str {
match self {
Self::X86_64 => "x86_64-unknown-linux-musl",
Self::Arm64 => "aarch64-unknown-linux-musl",
}
}
}
impl Default for LambdaConfig {
fn default() -> Self {
Self {
runtime: LambdaRuntime::default(),
architecture: Architecture::default(),
memory_mb: 128,
timeout_secs: 30,
}
}
}
impl LambdaConfig {
pub fn validate(&self) -> Result<(), &'static str> {
if self.memory_mb < 128 {
return Err("Memory must be at least 128 MB");
}
if self.memory_mb > 10240 {
return Err("Memory cannot exceed 10240 MB");
}
if self.timeout_secs == 0 {
return Err("Timeout must be at least 1 second");
}
if self.timeout_secs > 900 {
return Err("Timeout cannot exceed 900 seconds (15 minutes)");
}
Ok(())
}
pub fn estimated_cost_per_million(&self, avg_duration_ms: u64) -> f64 {
let gb_seconds = (self.memory_mb as f64 / 1024.0) * (avg_duration_ms as f64 / 1000.0);
gb_seconds * 0.0000166667 * 1_000_000.0
}
}
#[derive(Debug, Clone)]
pub struct BatchProgress {
pub total: usize,
pub completed: usize,
pub succeeded: usize,
pub failed: usize,
pub skipped: usize,
}
impl BatchProgress {
pub fn new(total: usize) -> Self {
Self {
total,
completed: 0,
succeeded: 0,
failed: 0,
skipped: 0,
}
}
pub fn record_success(&mut self) {
self.completed += 1;
self.succeeded += 1;
}
pub fn record_failure(&mut self) {
self.completed += 1;
self.failed += 1;
}
pub fn record_skip(&mut self) {
self.completed += 1;
self.skipped += 1;
}
pub fn percent_complete(&self) -> f64 {
if self.total == 0 {
100.0
} else {
self.completed as f64 / self.total as f64 * 100.0
}
}
pub fn success_rate(&self) -> f64 {
let processed = self.succeeded + self.failed;
if processed == 0 {
0.0
} else {
self.succeeded as f64 / processed as f64 * 100.0
}
}
pub fn is_complete(&self) -> bool {
self.completed >= self.total
}
pub fn remaining(&self) -> usize {
self.total.saturating_sub(self.completed)
}
}
pub fn format_duration_ms(ms: u64) -> String {
if ms < 1000 {
format!("{}ms", ms)
} else if ms < 60000 {
format!("{:.2}s", ms as f64 / 1000.0)
} else {
let minutes = ms / 60000;
let seconds = (ms % 60000) / 1000;
format!("{}m {}s", minutes, seconds)
}
}
pub fn format_bytes(bytes: u64) -> String {
if bytes < 1024 {
format!("{} B", bytes)
} else if bytes < 1024 * 1024 {
format!("{:.1} KB", bytes as f64 / 1024.0)
} else if bytes < 1024 * 1024 * 1024 {
format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0))
} else {
format!("{:.2} GB", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
}
}
pub fn format_rate(rate: f64, threshold: f64) -> (String, bool) {
let formatted = format!("{:.1}%", rate);
let passed = rate >= threshold;
(formatted, passed)
}
pub fn improvement_needed(current: f64, target: f64) -> f64 {
if current >= target {
0.0
} else {
target - current
}
}
pub fn files_to_fix(total: usize, current_passed: usize, target_rate: f64) -> usize {
let target_passed = (total as f64 * target_rate / 100.0).ceil() as usize;
target_passed.saturating_sub(current_passed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_citl_result_empty_success() {
let result = CitlResult::empty_success();
assert!(result.success);
assert_eq!(result.compilation_rate, 1.0);
assert_eq!(result.files_processed, 0);
}
#[test]
fn test_citl_result_from_stats_perfect() {
let result = CitlResult::from_stats(10, 10, 5, 3);
assert!(result.success);
assert_eq!(result.compilation_rate, 1.0);
assert_eq!(result.files_processed, 10);
assert_eq!(result.fixes_applied, 3);
}
#[test]
fn test_citl_result_from_stats_partial() {
let result = CitlResult::from_stats(8, 10, 5, 2);
assert!(!result.success);
assert!((result.compilation_rate - 0.8).abs() < 0.001);
}
#[test]
fn test_citl_result_from_stats_empty() {
let result = CitlResult::from_stats(0, 0, 0, 0);
assert!(result.success);
assert_eq!(result.compilation_rate, 1.0);
}
#[test]
fn test_citl_result_meets_target() {
let result = CitlResult::from_stats(85, 100, 10, 5);
assert!(result.meets_target(0.8));
assert!(result.meets_target(0.85));
assert!(!result.meets_target(0.9));
}
#[test]
fn test_citl_result_failures() {
let result = CitlResult::from_stats(70, 100, 10, 5);
assert_eq!(result.failures(), 30);
}
#[test]
fn test_citl_result_compiled_count() {
let result = CitlResult::from_stats(75, 100, 10, 5);
assert_eq!(result.compiled_count(), 75);
}
#[test]
fn test_citl_config_default() {
let config = CitlConfig::default();
assert_eq!(config.max_iterations, 10);
assert_eq!(config.confidence_threshold, 0.8);
assert!(!config.verbose);
}
#[test]
fn test_citl_config_validate_valid() {
let config = CitlConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_citl_config_validate_zero_iterations() {
let config = CitlConfig {
max_iterations: 0,
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_citl_config_validate_too_many_iterations() {
let config = CitlConfig {
max_iterations: 101,
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_citl_config_validate_invalid_threshold() {
let config = CitlConfig {
confidence_threshold: 1.5,
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_citl_config_validate_negative_threshold() {
let config = CitlConfig {
confidence_threshold: -0.1,
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_quality_targets_default() {
let targets = QualityTargets::default();
assert_eq!(targets.min_tdg, 0.0);
assert_eq!(targets.max_tdg, 2.0);
assert_eq!(targets.max_complexity, 10);
assert_eq!(targets.min_coverage, 80);
}
#[test]
fn test_quality_targets_validate_valid() {
let targets = QualityTargets::default();
assert!(targets.validate().is_ok());
}
#[test]
fn test_quality_targets_validate_negative_tdg() {
let targets = QualityTargets {
min_tdg: -1.0,
..Default::default()
};
assert!(targets.validate().is_err());
}
#[test]
fn test_quality_targets_validate_invalid_range() {
let targets = QualityTargets {
min_tdg: 3.0,
max_tdg: 2.0,
..Default::default()
};
assert!(targets.validate().is_err());
}
#[test]
fn test_quality_targets_validate_zero_complexity() {
let targets = QualityTargets {
max_complexity: 0,
..Default::default()
};
assert!(targets.validate().is_err());
}
#[test]
fn test_quality_targets_validate_high_coverage() {
let targets = QualityTargets {
min_coverage: 101,
..Default::default()
};
assert!(targets.validate().is_err());
}
#[test]
fn test_quality_targets_tdg_ok() {
let targets = QualityTargets::default();
assert!(targets.tdg_ok(1.0));
assert!(targets.tdg_ok(0.0));
assert!(targets.tdg_ok(2.0));
assert!(!targets.tdg_ok(2.5));
assert!(!targets.tdg_ok(-0.1));
}
#[test]
fn test_quality_targets_complexity_ok() {
let targets = QualityTargets::default();
assert!(targets.complexity_ok(5));
assert!(targets.complexity_ok(10));
assert!(!targets.complexity_ok(11));
}
#[test]
fn test_quality_targets_coverage_ok() {
let targets = QualityTargets::default();
assert!(targets.coverage_ok(80.0));
assert!(targets.coverage_ok(95.0));
assert!(!targets.coverage_ok(79.9));
}
#[test]
fn test_doctest_config_default() {
let config = DoctestConfig::default();
assert!(config.module_prefix.is_empty());
assert!(config.include_classes);
assert!(!config.include_pytest);
}
#[test]
fn test_doctest_config_module_name_no_prefix() {
let config = DoctestConfig::default();
let path = Path::new("src/utils/helpers.py");
let name = config.module_name(path);
assert!(name.contains("helpers"));
}
#[test]
fn test_doctest_config_module_name_with_prefix() {
let config = DoctestConfig {
module_prefix: "mypackage".to_string(),
..Default::default()
};
let path = Path::new("utils.py");
let name = config.module_name(path);
assert!(name.starts_with("mypackage."));
}
#[test]
fn test_doctest_summary_default() {
let summary = DoctestSummary::default();
assert_eq!(summary.total_examples(), 0);
assert!(!summary.has_examples());
}
#[test]
fn test_doctest_summary_total_examples() {
let summary = DoctestSummary {
total_doctests: 10,
total_pytest: 5,
..Default::default()
};
assert_eq!(summary.total_examples(), 15);
assert!(summary.has_examples());
}
#[test]
fn test_doctest_summary_extraction_rate() {
let summary = DoctestSummary {
files_processed: 10,
files_with_examples: 4,
..Default::default()
};
assert!((summary.extraction_rate() - 40.0).abs() < 0.001);
}
#[test]
fn test_doctest_summary_extraction_rate_zero() {
let summary = DoctestSummary::default();
assert_eq!(summary.extraction_rate(), 0.0);
}
#[test]
fn test_file_filter_python() {
let filter = FileFilter::python();
assert!(filter.skip_hidden);
assert!(filter.skip_pycache);
assert!(!filter.skip_test_files);
}
#[test]
fn test_file_filter_python_no_tests() {
let filter = FileFilter::python_no_tests();
assert!(filter.skip_test_files);
}
#[test]
fn test_file_filter_should_skip_dir_hidden() {
let filter = FileFilter::python();
assert!(filter.should_skip_dir(".git"));
assert!(filter.should_skip_dir(".hidden"));
}
#[test]
fn test_file_filter_should_skip_dir_pycache() {
let filter = FileFilter::python();
assert!(filter.should_skip_dir("__pycache__"));
}
#[test]
fn test_file_filter_should_skip_dir_venv() {
let filter = FileFilter::python();
assert!(filter.should_skip_dir("venv"));
assert!(filter.should_skip_dir(".venv"));
assert!(filter.should_skip_dir("env"));
}
#[test]
fn test_file_filter_should_not_skip_normal() {
let filter = FileFilter::python();
assert!(!filter.should_skip_dir("src"));
assert!(!filter.should_skip_dir("lib"));
}
#[test]
fn test_file_filter_matches_file_extension() {
let filter = FileFilter::python();
assert!(filter.matches_file(Path::new("test.py")));
assert!(!filter.matches_file(Path::new("test.rs")));
}
#[test]
fn test_file_filter_matches_file_test() {
let filter = FileFilter::python_no_tests();
assert!(!filter.matches_file(Path::new("test_utils.py")));
assert!(!filter.matches_file(Path::new("utils_test.py")));
assert!(filter.matches_file(Path::new("utils.py")));
}
#[test]
fn test_file_filter_is_test_file() {
assert!(FileFilter::is_test_file(Path::new("test_utils.py")));
assert!(FileFilter::is_test_file(Path::new("utils_test.py")));
assert!(!FileFilter::is_test_file(Path::new("utils.py")));
}
#[test]
fn test_lambda_runtime_default() {
let runtime = LambdaRuntime::default();
assert_eq!(runtime, LambdaRuntime::ProvidedAl2023);
}
#[test]
fn test_lambda_runtime_as_str() {
assert_eq!(LambdaRuntime::ProvidedAl2.as_str(), "provided.al2");
assert_eq!(LambdaRuntime::ProvidedAl2023.as_str(), "provided.al2023");
}
#[test]
fn test_architecture_default() {
let arch = Architecture::default();
assert_eq!(arch, Architecture::X86_64);
}
#[test]
fn test_architecture_as_str() {
assert_eq!(Architecture::X86_64.as_str(), "x86_64");
assert_eq!(Architecture::Arm64.as_str(), "arm64");
}
#[test]
fn test_architecture_target_triple() {
assert_eq!(
Architecture::X86_64.target_triple(),
"x86_64-unknown-linux-musl"
);
assert_eq!(
Architecture::Arm64.target_triple(),
"aarch64-unknown-linux-musl"
);
}
#[test]
fn test_lambda_config_default() {
let config = LambdaConfig::default();
assert_eq!(config.memory_mb, 128);
assert_eq!(config.timeout_secs, 30);
}
#[test]
fn test_lambda_config_validate_valid() {
let config = LambdaConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_lambda_config_validate_low_memory() {
let config = LambdaConfig {
memory_mb: 64,
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_lambda_config_validate_high_memory() {
let config = LambdaConfig {
memory_mb: 20000,
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_lambda_config_validate_zero_timeout() {
let config = LambdaConfig {
timeout_secs: 0,
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_lambda_config_validate_high_timeout() {
let config = LambdaConfig {
timeout_secs: 1000,
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_lambda_config_estimated_cost() {
let config = LambdaConfig {
memory_mb: 1024,
..Default::default()
};
let cost = config.estimated_cost_per_million(100);
assert!(cost > 0.0);
}
#[test]
fn test_batch_progress_new() {
let progress = BatchProgress::new(100);
assert_eq!(progress.total, 100);
assert_eq!(progress.completed, 0);
assert_eq!(progress.succeeded, 0);
}
#[test]
fn test_batch_progress_record_success() {
let mut progress = BatchProgress::new(10);
progress.record_success();
assert_eq!(progress.completed, 1);
assert_eq!(progress.succeeded, 1);
assert_eq!(progress.failed, 0);
}
#[test]
fn test_batch_progress_record_failure() {
let mut progress = BatchProgress::new(10);
progress.record_failure();
assert_eq!(progress.completed, 1);
assert_eq!(progress.succeeded, 0);
assert_eq!(progress.failed, 1);
}
#[test]
fn test_batch_progress_record_skip() {
let mut progress = BatchProgress::new(10);
progress.record_skip();
assert_eq!(progress.completed, 1);
assert_eq!(progress.skipped, 1);
}
#[test]
fn test_batch_progress_percent_complete() {
let mut progress = BatchProgress::new(10);
progress.record_success();
progress.record_success();
assert!((progress.percent_complete() - 20.0).abs() < 0.001);
}
#[test]
fn test_batch_progress_percent_complete_zero() {
let progress = BatchProgress::new(0);
assert_eq!(progress.percent_complete(), 100.0);
}
#[test]
fn test_batch_progress_success_rate() {
let mut progress = BatchProgress::new(10);
progress.record_success();
progress.record_success();
progress.record_failure();
assert!((progress.success_rate() - 66.67).abs() < 0.1);
}
#[test]
fn test_batch_progress_success_rate_zero() {
let progress = BatchProgress::new(10);
assert_eq!(progress.success_rate(), 0.0);
}
#[test]
fn test_batch_progress_is_complete() {
let mut progress = BatchProgress::new(2);
assert!(!progress.is_complete());
progress.record_success();
assert!(!progress.is_complete());
progress.record_success();
assert!(progress.is_complete());
}
#[test]
fn test_batch_progress_remaining() {
let mut progress = BatchProgress::new(10);
assert_eq!(progress.remaining(), 10);
progress.record_success();
progress.record_failure();
assert_eq!(progress.remaining(), 8);
}
#[test]
fn test_format_duration_ms_milliseconds() {
assert_eq!(format_duration_ms(500), "500ms");
assert_eq!(format_duration_ms(0), "0ms");
}
#[test]
fn test_format_duration_ms_seconds() {
assert_eq!(format_duration_ms(1500), "1.50s");
assert_eq!(format_duration_ms(30000), "30.00s");
}
#[test]
fn test_format_duration_ms_minutes() {
assert_eq!(format_duration_ms(90000), "1m 30s");
assert_eq!(format_duration_ms(120000), "2m 0s");
}
#[test]
fn test_format_bytes_bytes() {
assert_eq!(format_bytes(500), "500 B");
assert_eq!(format_bytes(0), "0 B");
}
#[test]
fn test_format_bytes_kilobytes() {
assert_eq!(format_bytes(2048), "2.0 KB");
assert_eq!(format_bytes(1536), "1.5 KB");
}
#[test]
fn test_format_bytes_megabytes() {
assert_eq!(format_bytes(1048576), "1.0 MB");
assert_eq!(format_bytes(5242880), "5.0 MB");
}
#[test]
fn test_format_bytes_gigabytes() {
assert_eq!(format_bytes(1073741824), "1.00 GB");
}
#[test]
fn test_format_rate_passed() {
let (formatted, passed) = format_rate(85.5, 80.0);
assert_eq!(formatted, "85.5%");
assert!(passed);
}
#[test]
fn test_format_rate_failed() {
let (formatted, passed) = format_rate(75.0, 80.0);
assert_eq!(formatted, "75.0%");
assert!(!passed);
}
#[test]
fn test_improvement_needed_above_target() {
assert_eq!(improvement_needed(90.0, 80.0), 0.0);
}
#[test]
fn test_improvement_needed_below_target() {
assert!((improvement_needed(70.0, 80.0) - 10.0).abs() < 0.001);
}
#[test]
fn test_files_to_fix_above_target() {
assert_eq!(files_to_fix(100, 85, 80.0), 0);
}
#[test]
fn test_files_to_fix_below_target() {
assert_eq!(files_to_fix(100, 70, 80.0), 10);
}
#[test]
fn test_files_to_fix_zero_total() {
assert_eq!(files_to_fix(0, 0, 80.0), 0);
}
}