use crate::types::{Edge, Graph, Node};
use serde_json::Value;
use std::collections::HashMap;
pub type GeneratorFn = fn(&HashMap<String, Value>) -> Result<Graph, String>;
pub fn get_generator(name: &str) -> Option<GeneratorFn> {
match name {
"complete" => Some(generate_complete),
"path" => Some(generate_path),
"cycle" => Some(generate_cycle),
"grid" => Some(generate_grid),
"star" => Some(generate_star),
"tree" => Some(generate_tree),
"barabasi_albert" => Some(generate_barabasi_albert),
_ => None,
}
}
fn get_param_int(params: &HashMap<String, Value>, key: &str) -> Result<usize, String> {
params
.get(key)
.ok_or_else(|| format!("Missing required parameter: '{key}'"))
.and_then(|v| {
v.as_i64()
.map(|n| if n < 0 { 0 } else { n as usize })
.ok_or_else(|| format!("Invalid integer for parameter '{key}'"))
})
}
fn get_param_string(
params: &HashMap<String, Value>,
key: &str,
default: &str,
) -> String {
params
.get(key)
.and_then(|v| v.as_str())
.map(|s| s.to_string())
.unwrap_or_else(|| default.to_string())
}
fn get_param_bool(params: &HashMap<String, Value>, key: &str, default: bool) -> bool {
params
.get(key)
.and_then(|v| v.as_bool())
.unwrap_or(default)
}
pub fn generate_complete(params: &HashMap<String, Value>) -> Result<Graph, String> {
let n = get_param_int(params, "nodes")?;
let prefix = get_param_string(params, "prefix", "n");
let directed = get_param_bool(params, "directed", false);
let mut graph = Graph::new();
let nodes: Vec<_> = (0..n).map(|i| format!("{prefix}{i}")).collect();
for node_id in &nodes {
graph.add_node(node_id.clone(), Node::new());
}
for i in 0..n {
for j in 0..n {
if i == j {
continue;
}
if !directed && i > j {
continue;
}
let source = &nodes[i];
let target = &nodes[j];
let edge_id = format!("e_{source}_{target}");
graph.add_edge(
edge_id,
Edge::new(source.clone(), target.clone(), directed),
);
}
}
Ok(graph)
}
pub fn generate_path(params: &HashMap<String, Value>) -> Result<Graph, String> {
let n = get_param_int(params, "nodes")?;
let prefix = get_param_string(params, "prefix", "n");
let directed = get_param_bool(params, "directed", false);
let mut graph = Graph::new();
if n == 0 {
return Ok(graph);
}
for i in 0..n {
graph.add_node(format!("{prefix}{i}"), Node::new());
}
for i in 0..n - 1 {
let source = format!("{prefix}{i}");
let target = format!("{prefix}{}", i + 1);
let edge_id = format!("e{i}_{}", i + 1);
graph.add_edge(edge_id, Edge::new(source, target, directed));
}
Ok(graph)
}
pub fn generate_cycle(params: &HashMap<String, Value>) -> Result<Graph, String> {
let n = get_param_int(params, "nodes")?;
let prefix = get_param_string(params, "prefix", "n");
let directed = get_param_bool(params, "directed", false);
let mut graph = Graph::new();
if n == 0 {
return Ok(graph);
}
for i in 0..n {
graph.add_node(format!("{prefix}{i}"), Node::new());
}
for i in 0..n {
let source = format!("{prefix}{i}");
let target = format!("{prefix}{}", (i + 1) % n);
let edge_id = format!("e{}_{}", i, (i + 1) % n);
graph.add_edge(edge_id, Edge::new(source, target, directed));
}
Ok(graph)
}
pub fn generate_grid(params: &HashMap<String, Value>) -> Result<Graph, String> {
let rows = get_param_int(params, "rows")?;
let cols = get_param_int(params, "cols")?;
let prefix = get_param_string(params, "prefix", "n");
let periodic = get_param_bool(params, "periodic", false);
let mut graph = Graph::new();
for r in 0..rows {
for c in 0..cols {
graph.add_node(format!("{prefix}{r}_{c}"), Node::new());
}
}
for r in 0..rows {
for c in 0..cols {
let source = format!("{prefix}{r}_{c}");
if c < cols - 1 || periodic {
let target_c = (c + 1) % cols;
let target = format!("{prefix}{r}_{target_c}");
let edge_id = format!("eh_{r}_{c}");
graph.add_edge(edge_id, Edge::new(source.clone(), target, false));
}
if r < rows - 1 || periodic {
let target_r = (r + 1) % rows;
let target = format!("{prefix}{target_r}_{c}");
let edge_id = format!("ev_{r}_{c}");
graph.add_edge(edge_id, Edge::new(source.clone(), target, false));
}
}
}
Ok(graph)
}
pub fn generate_star(params: &HashMap<String, Value>) -> Result<Graph, String> {
let n = get_param_int(params, "nodes")?;
let prefix = get_param_string(params, "prefix", "n");
let directed = get_param_bool(params, "directed", false);
let mut graph = Graph::new();
if n == 0 {
return Ok(graph);
}
let center_id = format!("{prefix}0");
graph.add_node(center_id.clone(), Node::new());
for i in 1..n {
let spoke_id = format!("{prefix}{i}");
graph.add_node(spoke_id.clone(), Node::new());
let (source, target) = (center_id.clone(), spoke_id);
let edge_id = format!("e_center_{i}");
graph.add_edge(edge_id, Edge::new(source, target, directed));
}
Ok(graph)
}
pub fn generate_tree(params: &HashMap<String, Value>) -> Result<Graph, String> {
let branching = get_param_int(params, "branching")?;
let depth = get_param_int(params, "depth")?;
let prefix = get_param_string(params, "prefix", "n");
let mut graph = Graph::new();
graph.add_node(format!("{prefix}0"), Node::new());
if depth <= 1 {
return Ok(graph);
}
let mut parent_queue = vec![0];
let mut id_counter = 1;
for _d in 0..depth - 1 {
let mut next_level_parents = Vec::new();
for parent_id_val in parent_queue.drain(..) {
for _b in 0..branching {
let parent_id = format!("{prefix}{parent_id_val}");
let child_id = format!("{prefix}{id_counter}");
graph.add_node(child_id.clone(), Node::new());
let edge_id = format!("e{parent_id_val}_{id_counter}");
graph.add_edge(edge_id, Edge::new(parent_id, child_id, true));
next_level_parents.push(id_counter);
id_counter += 1;
}
}
parent_queue = next_level_parents;
}
Ok(graph)
}
pub fn generate_barabasi_albert(
params: &HashMap<String, Value>,
) -> Result<Graph, String> {
let n = get_param_int(params, "nodes")?;
let m = get_param_int(params, "edges_per_node")?;
let prefix = get_param_string(params, "prefix", "n");
if m == 0 || n == 0 {
return Ok(Graph::new());
}
if m >= n {
return Err("Parameter 'edges_per_node' must be less than 'nodes'".to_string());
}
let mut graph = Graph::new();
for i in 0..m {
graph.add_node(format!("{prefix}{i}"), Node::new());
}
for i in 0..m {
for j in i + 1..m {
let edge_id = format!("e{i}_{j}");
graph.add_edge(
edge_id,
Edge::new(format!("{prefix}{i}"), format!("{prefix}{j}"), false),
);
}
}
let mut degrees: Vec<String> = graph
.edges
.values()
.flat_map(|e| vec![e.source.clone(), e.target.clone()])
.collect();
for i in m..n {
let new_node_id = format!("{prefix}{i}");
graph.add_node(new_node_id.clone(), Node::new());
let mut selected_targets = std::collections::HashSet::new();
let mut attempts = 0;
while selected_targets.len() < m && attempts < 100 {
if !degrees.is_empty() {
let idx = fastrand::usize(..degrees.len());
selected_targets.insert(degrees[idx].clone());
}
attempts += 1;
}
if selected_targets.len() < m {
for j in 0..i {
let node_id = format!("{prefix}{j}");
selected_targets.insert(node_id);
if selected_targets.len() >= m {
break;
}
}
}
for target_id in selected_targets.iter().take(m) {
let edge_id = format!("e{i}_{}", target_id.strip_prefix(&prefix).unwrap_or("?"));
graph.add_edge(
edge_id,
Edge::new(new_node_id.clone(), target_id.clone(), false),
);
degrees.push(new_node_id.clone());
degrees.push(target_id.clone());
}
}
Ok(graph)
}