use crate::core::exceptions::GraphinaException;
use crate::core::types::{BaseGraph, GraphConstructor};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
pub fn erdos_renyi_graph<Ty: GraphConstructor<u32, f32>>(
n: usize,
p: f64,
seed: u64,
) -> Result<BaseGraph<u32, f32, Ty>, GraphinaException> {
if n == 0 {
return Err(GraphinaException::new(
"Number of nodes must be greater than zero.",
));
}
if !(0.0..=1.0).contains(&p) {
return Err(GraphinaException::new(
"Probability p must be in the range [0.0, 1.0].",
));
}
let mut graph = BaseGraph::<u32, f32, Ty>::new();
let mut nodes = Vec::with_capacity(n);
for i in 0..n {
nodes.push(graph.add_node(i as u32));
}
let mut rng = StdRng::seed_from_u64(seed);
if <Ty as GraphConstructor<u32, f32>>::is_directed() {
for i in 0..n {
for j in 0..n {
if i != j && rng.random_bool(p) {
graph.add_edge(nodes[i], nodes[j], 1.0);
}
}
}
} else {
for i in 0..n {
for j in (i + 1)..n {
if rng.random_bool(p) {
graph.add_edge(nodes[i], nodes[j], 1.0);
}
}
}
}
Ok(graph)
}
pub fn complete_graph<Ty: GraphConstructor<u32, f32>>(
n: usize,
) -> Result<BaseGraph<u32, f32, Ty>, GraphinaException> {
if n == 0 {
return Err(GraphinaException::new(
"Number of nodes must be greater than zero.",
));
}
let mut graph = BaseGraph::<u32, f32, Ty>::new();
let mut nodes = Vec::with_capacity(n);
for i in 0..n {
nodes.push(graph.add_node(i as u32));
}
if <Ty as GraphConstructor<u32, f32>>::is_directed() {
for i in 0..n {
for j in 0..n {
if i != j {
graph.add_edge(nodes[i], nodes[j], 1.0);
}
}
}
} else {
for i in 0..n {
for j in (i + 1)..n {
graph.add_edge(nodes[i], nodes[j], 1.0);
}
}
}
Ok(graph)
}
pub fn bipartite_graph<Ty: GraphConstructor<u32, f32>>(
n1: usize,
n2: usize,
p: f64,
seed: u64,
) -> Result<BaseGraph<u32, f32, Ty>, GraphinaException> {
if n1 == 0 || n2 == 0 {
return Err(GraphinaException::new(
"Both partitions must have at least one node.",
));
}
if !(0.0..=1.0).contains(&p) {
return Err(GraphinaException::new(
"Probability p must be in the range [0.0, 1.0].",
));
}
let mut graph = BaseGraph::<u32, f32, Ty>::new();
let mut group1 = Vec::with_capacity(n1);
let mut group2 = Vec::with_capacity(n2);
for i in 0..n1 {
group1.push(graph.add_node(i as u32));
}
for j in 0..n2 {
group2.push(graph.add_node((n1 + j) as u32));
}
let mut rng = StdRng::seed_from_u64(seed);
for &u in &group1 {
for &v in &group2 {
if rng.random_bool(p) {
graph.add_edge(u, v, 1.0);
}
}
}
Ok(graph)
}
pub fn star_graph<Ty: GraphConstructor<u32, f32>>(
n: usize,
) -> Result<BaseGraph<u32, f32, Ty>, GraphinaException> {
let mut graph = BaseGraph::<u32, f32, Ty>::new();
if n == 0 {
return Err(GraphinaException::new(
"Star graph must have at least one node.",
));
}
let center = graph.add_node(0);
for i in 1..n {
let node = graph.add_node(i as u32);
graph.add_edge(center, node, 1.0);
}
Ok(graph)
}
pub fn cycle_graph<Ty: GraphConstructor<u32, f32>>(
n: usize,
) -> Result<BaseGraph<u32, f32, Ty>, GraphinaException> {
let mut graph = BaseGraph::<u32, f32, Ty>::new();
if n == 0 {
return Err(GraphinaException::new(
"Cycle graph must have at least one node.",
));
}
let mut nodes = Vec::with_capacity(n);
for i in 0..n {
nodes.push(graph.add_node(i as u32));
}
for i in 0..n {
let j = (i + 1) % n;
graph.add_edge(nodes[i], nodes[j], 1.0);
}
Ok(graph)
}
pub fn watts_strogatz_graph<Ty: GraphConstructor<u32, f32>>(
n: usize,
k: usize,
beta: f64,
seed: u64,
) -> Result<BaseGraph<u32, f32, Ty>, GraphinaException> {
if n == 0 {
return Err(GraphinaException::new(
"Number of nodes must be greater than zero.",
));
}
if k % 2 != 0 || k >= n {
return Err(GraphinaException::new("k must be even and less than n."));
}
if !(0.0..=1.0).contains(&beta) {
return Err(GraphinaException::new(
"Beta must be in the range [0.0, 1.0].",
));
}
let mut graph = BaseGraph::<u32, f32, Ty>::new();
let mut nodes = Vec::with_capacity(n);
for i in 0..n {
nodes.push(graph.add_node(i as u32));
}
let mut rng = StdRng::seed_from_u64(seed);
let half_k = k / 2;
for i in 0..n {
for j in 1..=half_k {
let neighbor = (i + j) % n;
graph.add_edge(nodes[i], nodes[neighbor], 1.0);
}
}
for i in 0..n {
for j in 1..=half_k {
if rng.random_bool(beta) {
let neighbor = (i + j) % n;
if let Some(eid) = graph.find_edge(nodes[i], nodes[neighbor]) {
let _ = graph.remove_edge(eid);
let mut new_target;
loop {
new_target = rng.random_range(0..n);
if new_target != i {
break;
}
}
graph.add_edge(nodes[i], nodes[new_target], 1.0);
}
}
}
}
Ok(graph)
}
pub fn barabasi_albert_graph<Ty: GraphConstructor<u32, f32>>(
n: usize,
m: usize,
seed: u64,
) -> Result<BaseGraph<u32, f32, Ty>, GraphinaException> {
if m == 0 || n < m {
return Err(GraphinaException::new(
"n must be at least m and m must be > 0.",
));
}
let mut graph = BaseGraph::<u32, f32, Ty>::new();
let mut nodes = Vec::with_capacity(n);
for i in 0..m {
nodes.push(graph.add_node(i as u32));
}
for i in 0..m {
for j in (i + 1)..m {
graph.add_edge(nodes[i], nodes[j], 1.0);
}
}
let mut rng = StdRng::seed_from_u64(seed);
let mut degrees: Vec<usize> = vec![m - 1; m];
let mut total_degree = m * (m - 1);
for i in m..n {
let new_node = graph.add_node(i as u32);
nodes.push(new_node);
let mut targets = Vec::new();
while targets.len() < m {
let r = rng.random_range(0..total_degree);
let mut cumulative = 0;
for (idx, °) in degrees.iter().enumerate() {
cumulative += deg;
if r < cumulative {
if !targets.contains(&nodes[idx]) {
targets.push(nodes[idx]);
}
break;
}
}
}
for target in &targets {
graph.add_edge(new_node, *target, 1.0);
let idx = nodes.iter().position(|&x| x == *target).unwrap();
degrees[idx] += 1;
}
degrees.push(m);
total_degree += 2 * m;
}
Ok(graph)
}