diff --git a/Cargo.toml b/Cargo.toml
index 986a382..08505b4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,14 +22,17 @@ hyper = { version = "0.14", features = ["full"] }
serde_json = "1.0"
bincode = "1.3"
rand = "0.8"
+futures = "0.3"
rustls = "0.21"
tokio-util = { version = "0.7", features = ["full"] }
async-trait = "0.1"
tokio-rustls = "0.24"
# Bellande Architecture
-bellande_step = "0.1.0"
+bellande_step = "0.1.1"
bellande_limit = "0.1.0"
bellande_node_importance = "0.1.0"
bellande_particle = "0.1.1"
bellande_probability = "0.1.1"
+# bellande_tree = "0.1.0"
+# bellande_segment = "0.1.0"
diff --git a/src/algorithm/bellande_search_path.rs b/src/algorithm/bellande_search_path.rs
deleted file mode 100644
index 3441dd2..0000000
--- a/src/algorithm/bellande_search_path.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (C) 2025 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande
-
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
diff --git a/src/algorithm/bellande_step.rs b/src/algorithm/bellande_step.rs
index 3441dd2..e2dd2e9 100644
--- a/src/algorithm/bellande_step.rs
+++ b/src/algorithm/bellande_step.rs
@@ -12,3 +12,640 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+
+use bellande_step::make_bellande_step_request;
+use futures::future::join_all;
+use serde::{Deserialize, Serialize};
+use serde_json::Value;
+use std::fmt;
+
+/// Error types specific to this library
+#[derive(Debug)]
+pub enum BellandeArchError {
+ DimensionMismatch(String),
+ InvalidCoordinates(String),
+ AlgorithmError(String),
+ NetworkError(String),
+}
+
+impl fmt::Display for BellandeArchError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ BellandeArchError::DimensionMismatch(msg) => write!(f, "Dimension mismatch: {}", msg),
+ BellandeArchError::InvalidCoordinates(msg) => write!(f, "Invalid coordinates: {}", msg),
+ BellandeArchError::AlgorithmError(msg) => write!(f, "Algorithm error: {}", msg),
+ BellandeArchError::NetworkError(msg) => write!(f, "Network error: {}", msg),
+ }
+ }
+}
+
+impl std::error::Error for BellandeArchError {}
+
+/// Configuration for spatial coordinate transformation
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct SpatialTransformConfig {
+ pub start_coordinates: Vec,
+ pub end_coordinates: Vec,
+ pub iterations: i32,
+ pub use_local_executable: bool,
+}
+
+/// Result of a spatial transformation operation
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct TransformationResult {
+ pub path_coordinates: Vec>,
+ pub convergence_metrics: Option>,
+ pub execution_time_ms: Option,
+}
+
+/// Extended configuration for advanced transformations
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct AdvancedTransformConfig {
+ pub basic_config: SpatialTransformConfig,
+ pub obstacles: Option>>,
+ pub constraint_regions: Option>>>,
+ pub preferred_path_bias: Option,
+ pub smoothing_factor: Option,
+}
+
+/// Configuration for coordinate system operations
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct CoordinateSystemConfig {
+ pub source_system: String,
+ pub target_system: String,
+ pub dimension_map: Option>,
+}
+
+/// Main spatial transformation module that leverages the Bellande Step algorithm
+pub struct SpatialTransformer;
+
+impl SpatialTransformer {
+ /// Performs a spatial coordinate transformation using the Bellande Step algorithm
+ pub async fn transform(
+ config: SpatialTransformConfig,
+ ) -> Result {
+ // Validate input coordinates
+ if config.start_coordinates.is_empty() || config.end_coordinates.is_empty() {
+ return Err(BellandeArchError::InvalidCoordinates(
+ "Coordinate vectors cannot be empty".to_string(),
+ ));
+ }
+ let dimensions = config.start_coordinates.len();
+
+ // Verify that both coordinate sets have the same dimensions
+ if dimensions != config.end_coordinates.len() {
+ return Err(BellandeArchError::DimensionMismatch(format!(
+ "Start coordinates have {} dimensions but end coordinates have {}",
+ dimensions,
+ config.end_coordinates.len()
+ )));
+ }
+ // Convert coordinates to JSON Values
+ let node0 = serde_json::json!(config.start_coordinates);
+ let node1 = serde_json::json!(config.end_coordinates);
+
+ // Run the Bellande Step algorithm using the API
+ let result = if !config.use_local_executable {
+ make_bellande_step_request(node0, node1, config.iterations, dimensions as i32)
+ .await
+ .map_err(|e| BellandeArchError::AlgorithmError(e.to_string()))?
+ } else {
+ return Err(BellandeArchError::AlgorithmError(
+ "Start and end coordinates cannot be empty".to_string(),
+ ));
+ };
+
+ // Process and transform the results
+ Self::process_result(result)
+ }
+
+ /// Process the raw Bellande Step result into our domain-specific format
+ fn process_result(result: Value) -> Result {
+ // Extract the path data from the result
+ let path = result
+ .get("path")
+ .and_then(|p| p.as_array())
+ .ok_or_else(|| {
+ BellandeArchError::AlgorithmError(
+ "Missing path data in algorithm result".to_string(),
+ )
+ })?;
+
+ // Convert the path to our format
+ let mut path_coordinates = Vec::new();
+ for point in path {
+ let coords = point
+ .as_array()
+ .ok_or_else(|| {
+ BellandeArchError::AlgorithmError(
+ "Invalid point format in algorithm result".to_string(),
+ )
+ })?
+ .iter()
+ .map(|v| {
+ v.as_f64().ok_or_else(|| {
+ BellandeArchError::AlgorithmError(
+ "Non-numeric coordinate in algorithm result".to_string(),
+ )
+ })
+ })
+ .collect::, _>>()?;
+
+ path_coordinates.push(coords);
+ }
+
+ // Extract any metrics if available
+ let convergence_metrics = result
+ .get("convergence_metrics")
+ .and_then(|m| m.as_array())
+ .map(|metrics| {
+ metrics
+ .iter()
+ .filter_map(|v| v.as_f64())
+ .collect::>()
+ });
+
+ // Extract execution time if available
+ let execution_time_ms = result
+ .get("execution_time_ms")
+ .and_then(|t| t.as_i64())
+ .map(|t| t as i32);
+
+ Ok(TransformationResult {
+ path_coordinates,
+ convergence_metrics,
+ execution_time_ms,
+ })
+ }
+
+ /// Optimizes a path between two points using advanced parameters
+ pub async fn optimize_path(
+ start: Vec,
+ end: Vec,
+ obstacles: Vec>,
+ precision: f64,
+ ) -> Result {
+ // Validate inputs
+ if start.is_empty() || end.is_empty() {
+ return Err(BellandeArchError::InvalidCoordinates(
+ "Start and end coordinates cannot be empty".to_string(),
+ ));
+ }
+
+ if start.len() != end.len() {
+ return Err(BellandeArchError::DimensionMismatch(format!(
+ "Start coordinates have {} dimensions but end coordinates have {}",
+ start.len(),
+ end.len()
+ )));
+ }
+
+ // Determine appropriate iteration count based on precision
+ let iterations = match precision {
+ p if p < 0.01 => 500,
+ p if p < 0.1 => 200,
+ _ => 100,
+ };
+
+ // Basic configuration
+ let config = SpatialTransformConfig {
+ start_coordinates: start.clone(),
+ end_coordinates: end.clone(),
+ iterations,
+ use_local_executable: false,
+ };
+
+ // Perform initial transformation
+ let initial_result = Self::transform(config.clone()).await?;
+
+ // If there are obstacles, we need more sophisticated processing
+ if !obstacles.is_empty() {
+ return Self::optimize_path_with_obstacles(config, obstacles).await;
+ }
+
+ Ok(initial_result)
+ }
+
+ /// Advanced path optimization that handles obstacles
+ async fn optimize_path_with_obstacles(
+ config: SpatialTransformConfig,
+ obstacles: Vec>,
+ ) -> Result {
+ // Validate obstacle data
+ for obstacle in &obstacles {
+ if obstacle.len() != config.start_coordinates.len() {
+ return Err(BellandeArchError::DimensionMismatch(format!(
+ "Obstacle has {} dimensions but path has {} dimensions",
+ obstacle.len(),
+ config.start_coordinates.len()
+ )));
+ }
+ }
+
+ // Define a safety radius around obstacles
+ let safety_radius = 1.0;
+
+ // Create a list of waypoints, starting with the start point
+ let mut waypoints = vec![config.start_coordinates.clone()];
+
+ // For each obstacle, determine if we need to route around it
+ for obstacle in &obstacles {
+ // Create a waypoint that avoids this obstacle
+
+ // For simplicity, we'll just offset in the first dimension
+ let mut waypoint = obstacle.clone();
+ waypoint[0] += safety_radius * 2.0;
+
+ // Only add unique waypoints that aren't too close to existing ones
+ if !waypoints
+ .iter()
+ .any(|wp| euclidean_distance(wp, &waypoint).unwrap_or(f64::MAX) < safety_radius)
+ {
+ waypoints.push(waypoint);
+ }
+ }
+
+ // Add the end point as final waypoint
+ waypoints.push(config.end_coordinates.clone());
+
+ // Now transform between each successive pair of waypoints
+ let mut segments = Vec::new();
+ for i in 0..waypoints.len() - 1 {
+ let segment_config = SpatialTransformConfig {
+ start_coordinates: waypoints[i].clone(),
+ end_coordinates: waypoints[i + 1].clone(),
+ iterations: config.iterations / waypoints.len() as i32,
+ use_local_executable: config.use_local_executable,
+ };
+
+ let segment_result = Self::transform(segment_config).await?;
+ segments.push(segment_result);
+ }
+
+ // Combine the segments into a single path
+ let mut combined_path = Vec::new();
+ let mut combined_metrics = Vec::new();
+ let mut total_time = 0;
+
+ for (i, segment) in segments.iter().enumerate() {
+ if i == 0 {
+ // Include the entire first segment
+ combined_path.extend(segment.path_coordinates.clone());
+ } else {
+ // For subsequent segments, skip the first point to avoid duplicates
+ combined_path.extend(segment.path_coordinates[1..].to_vec());
+ }
+
+ // Combine metrics if available
+ if let Some(metrics) = &segment.convergence_metrics {
+ combined_metrics.extend(metrics.clone());
+ }
+
+ // Add execution time
+ if let Some(time) = segment.execution_time_ms {
+ total_time += time;
+ }
+ }
+
+ Ok(TransformationResult {
+ path_coordinates: combined_path,
+ convergence_metrics: if combined_metrics.is_empty() {
+ None
+ } else {
+ Some(combined_metrics)
+ },
+ execution_time_ms: Some(total_time),
+ })
+ }
+
+ /// Batch processes multiple transformations in parallel
+ pub async fn batch_transform(
+ configs: Vec,
+ ) -> Vec> {
+ // Process each configuration in parallel
+ let futures = configs.into_iter().map(|config| Self::transform(config));
+
+ // Collect all results
+ join_all(futures).await
+ }
+
+ /// Advanced batch processing with custom parameters for each transformation
+ pub async fn advanced_batch_transform(
+ configs: Vec,
+ ) -> Vec> {
+ let futures = configs.into_iter().map(|config| {
+ let basic_config = config.basic_config.clone();
+
+ async move {
+ let obstacles = config.obstacles.unwrap_or_default();
+
+ if obstacles.is_empty() {
+ // Use basic transformation if no obstacles
+ Self::transform(basic_config).await
+ } else {
+ // Otherwise, use path optimization with obstacles
+ Self::optimize_path_with_obstacles(basic_config, obstacles).await
+ }
+ }
+ });
+
+ join_all(futures).await
+ }
+
+ /// Calculates the quality of a transformation path
+ pub fn calculate_path_quality(
+ path: &[Vec],
+ obstacles: &[Vec],
+ obstacle_radius: f64,
+ ) -> Result {
+ if path.is_empty() {
+ return Err(BellandeArchError::InvalidCoordinates(
+ "Path cannot be empty for quality calculation".to_string(),
+ ));
+ }
+
+ // Calculate path length
+ let mut path_length = 0.0;
+ for i in 0..path.len() - 1 {
+ path_length += euclidean_distance(&path[i], &path[i + 1])?;
+ }
+
+ // Calculate minimum distance to obstacles
+ let mut min_obstacle_distance = f64::MAX;
+ if !obstacles.is_empty() {
+ for point in path {
+ for obstacle in obstacles {
+ let distance = euclidean_distance(point, obstacle)?;
+ if distance < min_obstacle_distance {
+ min_obstacle_distance = distance;
+ }
+ }
+ }
+ } else {
+ min_obstacle_distance = f64::MAX; // No obstacles, so distance is "infinite"
+ }
+
+ // Calculate path smoothness (using angle between segments)
+ let mut smoothness_penalty = 0.0;
+ if path.len() >= 3 {
+ for i in 0..path.len() - 2 {
+ let v1 = vector_subtract(&path[i + 1], &path[i])?;
+ let v2 = vector_subtract(&path[i + 2], &path[i + 1])?;
+
+ let dot_product = vector_dot_product(&v1, &v2)?;
+ let v1_len = vector_length(&v1)?;
+ let v2_len = vector_length(&v2)?;
+
+ if v1_len > 0.0 && v2_len > 0.0 {
+ let cos_angle = dot_product / (v1_len * v2_len);
+ let angle = cos_angle.acos();
+
+ // Penalty increases with sharper turns
+ smoothness_penalty += angle;
+ }
+ }
+ }
+
+ // Obstacle proximity penalty (increases as we get closer to obstacle_radius)
+ let obstacle_penalty = if min_obstacle_distance < obstacle_radius * 2.0 {
+ (obstacle_radius * 2.0 - min_obstacle_distance) / obstacle_radius
+ } else {
+ 0.0
+ };
+
+ // Combine factors into an overall quality score (higher is better)
+ // We normalize the path length by dividing by the number of segments
+ let normalized_path_length = path_length / (path.len() - 1) as f64;
+ let normalized_smoothness = smoothness_penalty / (path.len() - 2).max(1) as f64;
+
+ // Quality formula: We want shorter paths, smoother paths, and paths that avoid obstacles
+ let quality =
+ 10.0 - normalized_path_length - normalized_smoothness * 2.0 - obstacle_penalty * 5.0;
+
+ Ok(quality.max(0.0)) // Ensure quality is non-negative
+ }
+}
+
+/// Subtracts one vector from another
+fn vector_subtract(a: &[f64], b: &[f64]) -> Result, BellandeArchError> {
+ if a.len() != b.len() {
+ return Err(BellandeArchError::DimensionMismatch(format!(
+ "Cannot subtract vectors with different dimensions: {} and {}",
+ a.len(),
+ b.len()
+ )));
+ }
+
+ Ok(a.iter().zip(b.iter()).map(|(x, y)| x - y).collect())
+}
+
+/// Calculates the dot product of two vectors
+fn vector_dot_product(a: &[f64], b: &[f64]) -> Result {
+ if a.len() != b.len() {
+ return Err(BellandeArchError::DimensionMismatch(format!(
+ "Cannot calculate dot product of vectors with different dimensions: {} and {}",
+ a.len(),
+ b.len()
+ )));
+ }
+
+ Ok(a.iter().zip(b.iter()).map(|(x, y)| x * y).sum())
+}
+
+/// Calculates the Euclidean length of a vector
+fn vector_length(v: &[f64]) -> Result {
+ Ok(v.iter().map(|x| x * x).sum::().sqrt())
+}
+
+/// Calculates the Euclidean distance between two points
+pub fn euclidean_distance(point1: &[f64], point2: &[f64]) -> Result {
+ if point1.len() != point2.len() {
+ return Err(BellandeArchError::DimensionMismatch(format!(
+ "Points have different dimensions: {} and {}",
+ point1.len(),
+ point2.len()
+ )));
+ }
+
+ let sum_squared: f64 = point1
+ .iter()
+ .zip(point2.iter())
+ .map(|(a, b)| (a - b).powi(2))
+ .sum();
+
+ Ok(sum_squared.sqrt())
+}
+
+/// Validates if a point is within the bounds of a given space
+pub fn is_point_valid(point: &[f64], min_bounds: &[f64], max_bounds: &[f64]) -> bool {
+ if point.len() != min_bounds.len() || point.len() != max_bounds.len() {
+ return false;
+ }
+
+ point
+ .iter()
+ .zip(min_bounds.iter().zip(max_bounds.iter()))
+ .all(|(p, (min, max))| p >= min && p <= max)
+}
+
+/// Converts between different coordinate representations
+pub fn convert_coordinate_system(
+ coordinates: &[f64],
+ from_system: &str,
+ to_system: &str,
+) -> Result, BellandeArchError> {
+ match (from_system, to_system) {
+ ("cartesian", "cartesian") => Ok(coordinates.to_vec()),
+
+ ("cartesian", "polar") => {
+ if coordinates.len() != 2 {
+ return Err(BellandeArchError::DimensionMismatch(
+ "Cartesian to polar conversion requires exactly 2 dimensions".to_string(),
+ ));
+ }
+
+ let x = coordinates[0];
+ let y = coordinates[1];
+ let r = (x.powi(2) + y.powi(2)).sqrt();
+ let theta = y.atan2(x);
+
+ Ok(vec![r, theta])
+ }
+
+ ("polar", "cartesian") => {
+ if coordinates.len() != 2 {
+ return Err(BellandeArchError::DimensionMismatch(
+ "Polar to Cartesian conversion requires exactly 2 dimensions".to_string(),
+ ));
+ }
+
+ let r = coordinates[0];
+ let theta = coordinates[1];
+ let x = r * theta.cos();
+ let y = r * theta.sin();
+
+ Ok(vec![x, y])
+ }
+
+ ("cartesian", "cylindrical") => {
+ if coordinates.len() != 3 {
+ return Err(BellandeArchError::DimensionMismatch(
+ "Cartesian to cylindrical conversion requires exactly 3 dimensions".to_string(),
+ ));
+ }
+
+ let x = coordinates[0];
+ let y = coordinates[1];
+ let z = coordinates[2];
+
+ let rho = (x.powi(2) + y.powi(2)).sqrt();
+ let phi = y.atan2(x);
+
+ Ok(vec![rho, phi, z])
+ }
+
+ ("cylindrical", "cartesian") => {
+ if coordinates.len() != 3 {
+ return Err(BellandeArchError::DimensionMismatch(
+ "Cylindrical to Cartesian conversion requires exactly 3 dimensions".to_string(),
+ ));
+ }
+
+ let rho = coordinates[0];
+ let phi = coordinates[1];
+ let z = coordinates[2];
+
+ let x = rho * phi.cos();
+ let y = rho * phi.sin();
+
+ Ok(vec![x, y, z])
+ }
+
+ ("cartesian", "spherical") => {
+ if coordinates.len() != 3 {
+ return Err(BellandeArchError::DimensionMismatch(
+ "Cartesian to spherical conversion requires exactly 3 dimensions".to_string(),
+ ));
+ }
+
+ let x = coordinates[0];
+ let y = coordinates[1];
+ let z = coordinates[2];
+
+ let r = (x.powi(2) + y.powi(2) + z.powi(2)).sqrt();
+ let theta = if r > 0.0 { (z / r).acos() } else { 0.0 };
+ let phi = y.atan2(x);
+
+ Ok(vec![r, theta, phi])
+ }
+
+ ("spherical", "cartesian") => {
+ if coordinates.len() != 3 {
+ return Err(BellandeArchError::DimensionMismatch(
+ "Spherical to Cartesian conversion requires exactly 3 dimensions".to_string(),
+ ));
+ }
+
+ let r = coordinates[0];
+ let theta = coordinates[1];
+ let phi = coordinates[2];
+
+ let x = r * theta.sin() * phi.cos();
+ let y = r * theta.sin() * phi.sin();
+ let z = r * theta.cos();
+
+ Ok(vec![x, y, z])
+ }
+
+ _ => Err(BellandeArchError::InvalidCoordinates(format!(
+ "Unsupported coordinate system conversion: {} to {}",
+ from_system, to_system
+ ))),
+ }
+}
+
+/// Interpolates between two points to create a sequence of intermediate points
+pub fn interpolate_points(
+ start: &[f64],
+ end: &[f64],
+ num_points: usize,
+) -> Result>, BellandeArchError> {
+ if start.len() != end.len() {
+ return Err(BellandeArchError::DimensionMismatch(format!(
+ "Points have different dimensions: {} and {}",
+ start.len(),
+ end.len()
+ )));
+ }
+
+ if num_points < 2 {
+ return Err(BellandeArchError::InvalidCoordinates(
+ "Number of points must be at least 2 (start and end)".to_string(),
+ ));
+ }
+
+ let mut result = Vec::with_capacity(num_points);
+
+ // Add the start point
+ result.push(start.to_vec());
+
+ // Add intermediate points
+ if num_points > 2 {
+ for i in 1..num_points - 1 {
+ let t = i as f64 / (num_points - 1) as f64;
+ let mut point = Vec::with_capacity(start.len());
+
+ for j in 0..start.len() {
+ let value = start[j] + t * (end[j] - start[j]);
+ point.push(value);
+ }
+
+ result.push(point);
+ }
+ }
+
+ // Add the end point
+ result.push(end.to_vec());
+
+ Ok(result)
+}
diff --git a/src/algorithm/mod.rs b/src/algorithm/mod.rs
index 445a749..f8bd354 100644
--- a/src/algorithm/mod.rs
+++ b/src/algorithm/mod.rs
@@ -17,7 +17,6 @@ pub mod bellande_limit;
pub mod bellande_node_importance;
pub mod bellande_particle;
pub mod bellande_probability;
-pub mod bellande_search_path;
pub mod bellande_segment;
pub mod bellande_step;
pub mod bellande_tree;
diff --git a/src/utilities/utilities.rs b/src/utilities/utilities.rs
index 645d241..1c14c40 100644
--- a/src/utilities/utilities.rs
+++ b/src/utilities/utilities.rs
@@ -12,25 +12,3 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-
-use ed25519_dalek::PublicKey;
-use serde::{Deserialize, Serialize};
-
-impl Serialize for PublicKey {
- fn serialize(&self, serializer: S) -> Result
- where
- S: serde::Serializer,
- {
- serializer.serialize_bytes(&self.to_bytes())
- }
-}
-
-impl<'de> Deserialize<'de> for PublicKey {
- fn deserialize(deserializer: D) -> Result
- where
- D: serde::Deserializer<'de>,
- {
- let bytes: [u8; 32] = Deserialize::deserialize(deserializer)?;
- PublicKey::from_bytes(&bytes).map_err(serde::de::Error::custom)
- }
-}