use scirs2_core::ndarray::Array1;
use scirs2_interpolate::{
make_antisymmetric_boundary, make_cubic_extrapolator, make_exponential_extrapolator,
make_linear_extrapolator, make_linear_gradient_boundary, make_periodic_boundary,
make_periodic_extrapolator, make_reflection_extrapolator, make_symmetric_boundary,
make_zero_gradient_boundary, make_zero_value_boundary, BoundaryResult, ExtrapolationMethod,
Extrapolator,
};
#[allow(dead_code)]
fn demonstrate_extrapolation<F>(name: &str, extrapvalues: &[(f64, f64)], f: F)
where
F: Fn(f64) -> Result<f64, String>,
{
println!("--- {} Extrapolation ---", name);
for &(x, expected) in extrapvalues {
match f(x) {
Ok(value) => println!(
" f({:.2}) = {:.6} {}",
x,
value,
if f64::abs(value - expected) < 1e-6 {
"(correct)".to_string()
} else {
format!("(expected: {:.6})", expected)
}
),
Err(e) => println!(" f({:.2}) = Error: {}", x, e),
}
}
println!();
}
#[allow(dead_code)]
fn main() {
println!("Advanced Extrapolation and Boundary Handling Examples");
println!("====================================================\n");
println!("Example 1: Comparing Different Extrapolation Methods");
println!("--------------------------------------------------");
let lower_bound = 0.0;
let upper_bound = 1.0;
let lower_value = 0.0;
let upper_value = 1.0;
let lower_derivative = 0.0;
let upper_derivative = 2.0;
let constant_extrap = Extrapolator::new(
lower_bound,
upper_bound,
lower_value,
upper_value,
ExtrapolationMethod::Constant,
ExtrapolationMethod::Constant,
);
let linear_extrap = make_linear_extrapolator(
lower_bound,
upper_bound,
lower_value,
upper_value,
lower_derivative,
upper_derivative,
);
let periodic_extrap = make_periodic_extrapolator(lower_bound, upper_bound, None);
let reflection_extrap = make_reflection_extrapolator(lower_bound, upper_bound);
let constant_fn = |x: f64| -> Result<f64, String> {
constant_extrap.extrapolate(x).map_err(|e| e.to_string())
};
let linear_fn =
|x: f64| -> Result<f64, String> { linear_extrap.extrapolate(x).map_err(|e| e.to_string()) };
let periodic_fn = |x: f64| -> Result<f64, String> {
match periodic_extrap.extrapolate(x) {
Ok(val) => Ok(val),
Err(e) => {
if let scirs2_interpolate::InterpolateError::MappedPoint(mapped_x) = e {
Ok(f64::powi(mapped_x, 2))
} else {
Err(e.to_string())
}
}
}
};
let reflection_fn = |x: f64| -> Result<f64, String> {
match reflection_extrap.extrapolate(x) {
Ok(val) => Ok(val),
Err(e) => {
if let scirs2_interpolate::InterpolateError::MappedPoint(mapped_x) = e {
Ok(f64::powi(mapped_x, 2))
} else {
Err(e.to_string())
}
}
}
};
let inside_domain = vec![(0.25, 0.0625), (0.5, 0.25), (0.75, 0.5625)];
let below_domain = vec![(-0.5, 0.0), (-1.0, 0.0), (-1.5, 0.0)];
let above_domain = vec![(1.5, 1.0), (2.0, 1.0), (2.5, 1.0)];
let linear_below = vec![(-0.5, -0.0), (-1.0, -0.0), (-1.5, -0.0)];
let linear_above = vec![(1.5, 2.0), (2.0, 3.0), (2.5, 4.0)];
let periodic_below = vec![(-0.5, 0.25), (-1.0, 0.0), (-1.5, 0.25)];
let periodic_above = vec![(1.5, 0.25), (2.0, 0.0), (2.5, 0.25)];
let reflection_below = vec![(-0.5, 0.25), (-1.0, 1.0), (-1.5, 0.25)];
let reflection_above = vec![(1.5, 0.25), (2.0, 0.0), (2.5, 0.25)];
println!("Points inside domain (f(x) = x^2):");
demonstrate_extrapolation("Inside Domain", &inside_domain, |x| Ok(f64::powi(x, 2)));
println!("Extrapolation below domain (x < 0):");
demonstrate_extrapolation("Constant", &below_domain, constant_fn);
demonstrate_extrapolation("Linear", &linear_below, linear_fn);
demonstrate_extrapolation("Periodic", &periodic_below, periodic_fn);
demonstrate_extrapolation("Reflection", &reflection_below, reflection_fn);
println!("Extrapolation above domain (x > 1):");
demonstrate_extrapolation("Constant", &above_domain, constant_fn);
demonstrate_extrapolation("Linear", &linear_above, linear_fn);
demonstrate_extrapolation("Periodic", &periodic_above, periodic_fn);
demonstrate_extrapolation("Reflection", &reflection_above, reflection_fn);
println!("\nExample 2: Advanced Polynomial Extrapolation");
println!("------------------------------------------");
let lower_bound = 0.0;
let upper_bound = 1.0;
let lower_value = 0.0;
let upper_value = 1.0;
let lower_derivative = 0.0;
let upper_derivative = 3.0;
let lower_second_derivative = 0.0;
let upper_second_derivative = 6.0;
let cubic_extrap = make_cubic_extrapolator(
lower_bound,
upper_bound,
lower_value,
upper_value,
lower_derivative,
upper_derivative,
lower_second_derivative,
upper_second_derivative,
);
let cubic_fn =
|x: f64| -> Result<f64, String> { cubic_extrap.extrapolate(x).map_err(|e| e.to_string()) };
let cubic_below = vec![(-0.5, -0.125), (-1.0, -1.0), (-1.5, -3.375)];
let cubic_above = vec![(1.5, 3.375), (2.0, 8.0), (2.5, 15.625)];
println!("Cubic extrapolation of f(x) = x^3:");
demonstrate_extrapolation("Below domain", &cubic_below, cubic_fn);
demonstrate_extrapolation("Above domain", &cubic_above, cubic_fn);
println!("Exact values of f(x) = x^3 for comparison:");
demonstrate_extrapolation("Below domain", &cubic_below, |x| Ok(f64::powi(x, 3)));
demonstrate_extrapolation("Above domain", &cubic_above, |x| Ok(f64::powi(x, 3)));
println!("\nExample 3: Physics-Based Extrapolation");
println!("---------------------------------");
let lower_bound = 0.0;
let upper_bound = 1.0;
let lower_value = 1.0;
let upper_value = std::f64::consts::E; let lower_derivative = 1.0;
let upper_derivative = std::f64::consts::E;
let exp_extrap = make_exponential_extrapolator(
lower_bound,
upper_bound,
lower_value,
upper_value,
lower_derivative,
upper_derivative,
1.0,
1.0, );
let exp_fn =
|x: f64| -> Result<f64, String> { exp_extrap.extrapolate(x).map_err(|e| e.to_string()) };
let exp_points = vec![
(-1.0, 0.36787944117144233), (-2.0, 0.1353352832366127), (2.0, 7.3890560989306495), (3.0, 20.085536923187668), ];
println!("Exponential extrapolation compared to f(x) = e^x:");
demonstrate_extrapolation("Exponential extrapolation", &exp_points, exp_fn);
demonstrate_extrapolation("Exact exponential", &exp_points, |x| Ok(f64::exp(x)));
println!("\nExample 4: Boundary Handling for PDEs");
println!("---------------------------------");
let domain_points = Array1::linspace(0.0, 10.0, 11);
let values = domain_points.mapv(f64::sin);
let zero_gradient = make_zero_gradient_boundary(0.0, 10.0);
let zero_value = make_zero_value_boundary(0.0, 10.0);
let periodic = make_periodic_boundary(0.0, 10.0);
let symmetric = make_symmetric_boundary(0.0, 10.0);
let antisymmetric = make_antisymmetric_boundary(0.0, 10.0);
let linear_gradient = make_linear_gradient_boundary(0.0, 10.0, 0.5, -0.3);
let test_points = vec![-2.0, -1.0, -0.5, 10.5, 11.0, 12.0];
println!("Zero Gradient boundaries (Neumann condition):");
for &x in &test_points {
match zero_gradient.map_point(x, Some(&values.view()), Some(&domain_points.view())) {
Ok(BoundaryResult::MappedPoint(mapped_x)) => {
let idx = if mapped_x <= 0.0 { 0 } else { 10 };
println!(
" f({:.2}) → mapped to boundary: f({:.2}) = {:.6}",
x, mapped_x, values[idx]
);
}
Ok(BoundaryResult::InsideDomain(_)) => {
println!(" f({:.2}) is inside domain", x);
}
_ => println!(" f({:.2}) has unexpected result", x),
}
}
println!();
println!("Zero Value boundaries (Dirichlet condition):");
for &x in &test_points {
match zero_value.map_point(x, Some(&values.view()), Some(&domain_points.view())) {
Ok(BoundaryResult::DirectValue(value)) => {
println!(" f({:.2}) = {:.6}", x, value);
}
_ => println!(" f({:.2}) has unexpected result", x),
}
}
println!();
println!("Periodic boundaries:");
for &x in &test_points {
match periodic.map_point(x, Some(&values.view()), Some(&domain_points.view())) {
Ok(BoundaryResult::MappedPoint(mapped_x)) => {
let nearest_idx = domain_points
.iter()
.enumerate()
.min_by(|(_, a), (_, b)| {
let diff_a = f64::abs(mapped_x - *a);
let diff_b = f64::abs(mapped_x - *b);
diff_a.partial_cmp(&diff_b).expect("Test: operation failed")
})
.map(|(idx_, _)| idx_)
.expect("Test: operation failed");
println!(
" f({:.2}) → mapped to: f({:.2}) ≈ {:.6}",
x, mapped_x, values[nearest_idx]
);
}
_ => println!(" f({:.2}) has unexpected result", x),
}
}
println!();
println!("Symmetric boundaries (even function reflection):");
for &x in &test_points {
match symmetric.map_point(x, Some(&values.view()), Some(&domain_points.view())) {
Ok(BoundaryResult::MappedPoint(mapped_x)) => {
let nearest_idx = domain_points
.iter()
.enumerate()
.min_by(|(_, a), (_, b)| {
let diff_a = f64::abs(mapped_x - *a);
let diff_b = f64::abs(mapped_x - *b);
diff_a.partial_cmp(&diff_b).expect("Test: operation failed")
})
.map(|(idx_, _)| idx_)
.expect("Test: operation failed");
println!(
" f({:.2}) → mapped to: f({:.2}) ≈ {:.6}",
x, mapped_x, values[nearest_idx]
);
}
_ => println!(" f({:.2}) has unexpected result", x),
}
}
println!();
println!("Antisymmetric boundaries (odd function reflection):");
for &x in &test_points {
match antisymmetric.map_point(x, Some(&values.view()), Some(&domain_points.view())) {
Ok(BoundaryResult::DirectValue(value)) => {
println!(" f({:.2}) = {:.6}", x, value);
}
Ok(BoundaryResult::MappedPointWithSignChange(mapped_x)) => {
println!(" f({:.2}) → mapped to: -f({:.2})", x, mapped_x);
}
_ => println!(" f({:.2}) has unexpected result", x),
}
}
println!();
println!("Linear Gradient boundaries:");
for &x in &test_points {
match linear_gradient.map_point(x, Some(&values.view()), Some(&domain_points.view())) {
Ok(BoundaryResult::DirectValue(value)) => {
println!(" f({:.2}) = {:.6}", x, value);
}
_ => println!(" f({:.2}) has unexpected result", x),
}
}
println!("\nAll examples completed successfully!");
}