use scirs2_core::random::rngs::StdRng;
use scirs2_core::random::{Rng, RngExt, SeedableRng};
use std::collections::HashSet;
pub fn generate_graph(
n_nodes: usize,
edge_probability: f64,
) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
if !(0.0..=1.0).contains(&edge_probability) {
return Err("Edge probability must be between 0 and 1".into());
}
let mut rng = StdRng::seed_from_u64(42);
let mut edges = Vec::new();
for i in 0..n_nodes {
for j in (i + 1)..n_nodes {
if rng.random::<f64>() < edge_probability {
edges.push((i, j));
}
}
}
Ok(edges)
}
pub fn generate_complete_graph(n_nodes: usize) -> Vec<(usize, usize)> {
let mut edges = Vec::new();
for i in 0..n_nodes {
for j in (i + 1)..n_nodes {
edges.push((i, j));
}
}
edges
}
pub fn generate_cycle_graph(n_nodes: usize) -> Vec<(usize, usize)> {
let mut edges = Vec::new();
if n_nodes < 3 {
return edges;
}
for i in 0..n_nodes {
edges.push((i, (i + 1) % n_nodes));
}
edges
}
pub fn generate_grid_graph(rows: usize, cols: usize) -> Vec<(usize, usize)> {
let mut edges = Vec::new();
let node_index = |r, c| r * cols + c;
for r in 0..rows {
for c in 0..(cols - 1) {
edges.push((node_index(r, c), node_index(r, c + 1)));
}
}
for r in 0..(rows - 1) {
for c in 0..cols {
edges.push((node_index(r, c), node_index(r + 1, c)));
}
}
edges
}
pub fn generate_bipartite_graph(
left_nodes: usize,
right_nodes: usize,
edge_probability: f64,
) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
if !(0.0..=1.0).contains(&edge_probability) {
return Err("Edge probability must be between 0 and 1".into());
}
let mut rng = StdRng::seed_from_u64(42);
let mut edges = Vec::new();
for i in 0..left_nodes {
for j in 0..right_nodes {
if rng.random::<f64>() < edge_probability {
edges.push((i, left_nodes + j));
}
}
}
Ok(edges)
}
pub fn generate_regular_graph(
n_nodes: usize,
degree: usize,
) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
if degree >= n_nodes {
return Err("Degree must be less than number of nodes".into());
}
if (n_nodes * degree) % 2 != 0 {
return Err("n_nodes * degree must be even for regular graph".into());
}
let mut rng = StdRng::seed_from_u64(42);
let mut edges = HashSet::new();
let mut node_degrees = vec![0; n_nodes];
let max_attempts = n_nodes * degree * 10;
let mut attempts = 0;
while node_degrees.iter().any(|&d| d < degree) && attempts < max_attempts {
attempts += 1;
let available: Vec<usize> = (0..n_nodes).filter(|&i| node_degrees[i] < degree).collect();
if available.len() < 2 {
continue;
}
let i = available[rng.random_range(0..available.len())];
let j = available[rng.random_range(0..available.len())];
if i != j && !edges.contains(&(i.min(j), i.max(j))) {
edges.insert((i.min(j), i.max(j)));
node_degrees[i] += 1;
node_degrees[j] += 1;
}
}
if node_degrees.iter().any(|&d| d < degree) {
return Err("Failed to generate regular graph with given parameters".into());
}
Ok(edges.into_iter().collect())
}
pub fn generate_tree_graph(n_nodes: usize) -> Vec<(usize, usize)> {
let mut rng = StdRng::seed_from_u64(42);
let mut edges = Vec::new();
if n_nodes == 0 {
return edges;
}
for i in 1..n_nodes {
let parent = rng.random_range(0..i);
edges.push((parent, i));
}
edges
}
pub struct GraphProperties {
pub n_nodes: usize,
pub n_edges: usize,
pub avg_degree: f64,
pub max_degree: usize,
pub min_degree: usize,
pub is_connected: bool,
pub density: f64,
}
pub fn analyze_graph(n_nodes: usize, edges: &[(usize, usize)]) -> GraphProperties {
let mut degrees = vec![0; n_nodes];
for (i, j) in edges {
degrees[*i] += 1;
degrees[*j] += 1;
}
let avg_degree = degrees.iter().sum::<usize>() as f64 / n_nodes as f64;
let max_degree = *degrees.iter().max().unwrap_or(&0);
let min_degree = *degrees.iter().min().unwrap_or(&0);
let is_connected = if n_nodes > 0 {
let mut visited = vec![false; n_nodes];
let mut queue = vec![0];
visited[0] = true;
while let Some(node) = queue.pop() {
for (i, j) in edges {
if *i == node && !visited[*j] {
visited[*j] = true;
queue.push(*j);
} else if *j == node && !visited[*i] {
visited[*i] = true;
queue.push(*i);
}
}
}
visited.iter().all(|&v| v)
} else {
true
};
let max_edges = n_nodes * (n_nodes - 1) / 2;
let density = if max_edges > 0 {
edges.len() as f64 / max_edges as f64
} else {
0.0
};
GraphProperties {
n_nodes,
n_edges: edges.len(),
avg_degree,
max_degree,
min_degree,
is_connected,
density,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_complete_graph() {
let edges = generate_complete_graph(5);
assert_eq!(edges.len(), 10); }
#[test]
fn test_cycle_graph() {
let edges = generate_cycle_graph(5);
assert_eq!(edges.len(), 5);
}
#[test]
fn test_grid_graph() {
let edges = generate_grid_graph(3, 3);
assert_eq!(edges.len(), 12);
}
#[test]
fn test_tree_graph() {
let edges = generate_tree_graph(10);
assert_eq!(edges.len(), 9); }
}