use crate::discovery::SymbolicRegression;
use crate::equation::Expression;
use crate::error::{LmmError, Result};
use std::collections::HashMap;
const REPLACEMENT_CHAR: char = '\u{FFFD}';
#[derive(Debug, Clone)]
pub struct EncodedMessage {
pub equation: Expression,
pub length: usize,
pub residuals: Vec<i64>,
}
impl EncodedMessage {
pub fn summary(&self) -> String {
self.equation.to_string()
}
pub fn to_data_string(&self) -> String {
format!("Length: {}, Residuals: {:?}", self.length, self.residuals)
}
}
pub fn encode_text(text: &str, iterations: usize, depth: usize) -> Result<EncodedMessage> {
if text.is_empty() {
return Err(LmmError::ParseError("Cannot encode empty text".into()));
}
let chars: Vec<f64> = text.chars().map(|c| c as u32 as f64).collect();
let n = chars.len();
let inputs: Vec<Vec<f64>> = (0..n).map(|i| vec![i as f64]).collect();
let targets: Vec<f64> = chars;
let sr = SymbolicRegression::new(iterations, depth).with_variables(vec!["x".into()]);
let equation = sr.fit(&inputs, &targets)?;
let mut residuals = Vec::with_capacity(text.len());
let mut bindings = HashMap::new();
for (i, c) in text.chars().enumerate() {
bindings.insert("x".to_string(), i as f64);
let base = equation.evaluate(&bindings).unwrap_or(65.0).round() as i64;
let char_val = c as u32 as i64;
residuals.push(char_val - base);
}
Ok(EncodedMessage {
equation,
length: n,
residuals,
})
}
pub fn decode_message(msg: &EncodedMessage) -> Result<String> {
let mut output = String::with_capacity(msg.length);
let mut bindings = HashMap::new();
for i in 0..msg.length {
bindings.insert("x".to_string(), i as f64);
let val = msg.equation.evaluate(&bindings).unwrap_or(65.0);
let residual = msg.residuals.get(i).copied().unwrap_or(0);
let adjusted = val.round() as i64 + residual;
let code_point = adjusted.clamp(0, 0x10FFFF) as u32;
let ch = char::from_u32(code_point).unwrap_or(REPLACEMENT_CHAR);
output.push(ch);
}
Ok(output)
}
pub fn decode_from_parts(equation: &str, length: usize, residuals: &str) -> Result<String> {
let expr: Expression = equation
.parse::<Expression>()
.map_err(|e: String| LmmError::ParseError(e))?;
let parsed_residuals: Vec<i64> = residuals
.split(',')
.filter_map(|s| s.trim().parse::<i64>().ok())
.collect();
let mut output = String::with_capacity(length);
let mut bindings = HashMap::new();
for i in 0..length {
bindings.insert("x".to_string(), i as f64);
let base = expr.evaluate(&bindings).unwrap_or(65.0);
let residual = parsed_residuals.get(i).copied().unwrap_or(0);
let adjusted = base.round() as i64 + residual;
let code_point = adjusted.clamp(0, 0x10FFFF) as u32;
let ch = char::from_u32(code_point).unwrap_or(REPLACEMENT_CHAR);
output.push(ch);
}
Ok(output)
}
pub fn text_fingerprint(text: &str) -> f64 {
if text.is_empty() {
return 0.0;
}
let sum: f64 = text.chars().map(|c| c as u32 as f64).sum();
sum / (text.chars().count() as f64 * 0x10FFFF as f64)
}