use ebcc::{ebcc_decode_into, ebcc_encode, EBCCConfig, EBCCResult};
use ndarray::Array;
use ::{ebcc_sys as _, thiserror as _};
#[expect(clippy::cast_precision_loss, clippy::suboptimal_flops)]
fn main() -> EBCCResult<()> {
println!("EBCC Basic Compression Example");
println!("=============================");
let height = 721;
let width = 1440;
let frames = 1;
let data = Array::from_shape_fn((frames, height, width), |(_f, i, j)| {
let lat = -90.0 + ((i as f32) / (height as f32)) * 180.0;
let lon = -180.0 + ((j as f32) / (width as f32)) * 360.0;
#[allow(clippy::let_and_return)]
let temp = 273.15
+ 30.0 * (1.0 - lat.abs() / 90.0)
+ 5.0 * (lon / 180.0).sin()
+ 2.0 * (lat / 90.0 * 4.0).sin();
temp
});
let total_elements = data.len();
println!("Generated {total_elements} climate data points");
println!(
"Data range: {:.2} to {:.2} K",
data.iter().fold(f32::INFINITY, |a, &b| a.min(b)),
data.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b))
);
let configs = vec![
("JPEG2000 only (CR=10)", EBCCConfig::jpeg2000_only(10.0)),
("JPEG2000 only (CR=30)", EBCCConfig::jpeg2000_only(30.0)),
(
"Max error bound (0.1K)",
EBCCConfig::max_absolute_error_bounded(20.0, 0.1),
),
(
"Max error bound (0.01K)",
EBCCConfig::max_absolute_error_bounded(20.0, 0.01),
),
(
"Relative error (0.1%)",
EBCCConfig::relative_error_bounded(20.0, 0.001),
),
];
let original_size = total_elements * std::mem::size_of::<f32>();
for (name, config) in configs {
println!("\n--- {name} ---");
let start = std::time::Instant::now();
let compressed = ebcc_encode(data.view(), &config)?;
let compress_time = start.elapsed();
let start = std::time::Instant::now();
let mut decompressed = Array::zeros(data.dim());
ebcc_decode_into(&compressed, decompressed.view_mut())?;
let decompress_time = start.elapsed();
let compression_ratio = (original_size as f64) / (compressed.len() as f64);
let compressed_size_mb = (compressed.len() as f64) / (1024.0 * 1024.0);
let original_size_mb = (original_size as f64) / (1024.0 * 1024.0);
let max_error = data
.iter()
.zip(decompressed.iter())
.map(|(&orig, &decomp)| (orig - decomp).abs())
.fold(0.0f32, f32::max);
let mse: f64 = data
.iter()
.zip(decompressed.iter())
.map(|(&orig, &decomp)| f64::from(orig - decomp).powi(2))
.sum::<f64>()
/ (total_elements as f64);
let rmse = mse.sqrt();
let data_range = data
.iter()
.fold(f32::INFINITY, |a, &b| a.min(b))
.max(data.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b)))
- data.iter().fold(f32::INFINITY, |a, &b| a.min(b));
let max_relative_error = max_error / data_range * 100.0;
println!();
println!(" Original size: {original_size_mb:.2} MiB");
println!(" Compressed size: {compressed_size_mb:.2} MiB");
println!(" Compression ratio: {compression_ratio:.2}:1");
println!(
" Compression time: {:.2} ms",
compress_time.as_secs_f64() * 1000.0
);
println!(
" Decompression time: {:.2} ms",
decompress_time.as_secs_f64() * 1000.0
);
println!(" Max error: {max_error:.4} K");
println!(" RMSE: {rmse:.4} K");
println!(" Max relative error: {max_relative_error:.4}%");
}
println!("\nCompression example completed successfully!");
Ok(())
}