use ::rand::{rngs::StdRng};
use std::fmt::Debug;
use std::convert::TryInto;
use quantifiable_derive::Quantifiable;use super::{
Topology,TopologyBuilderArgument,CartesianData,Location,
};
use crate::{
config_parser::ConfigurationValue,
quantify::Quantifiable,
};
trait FlatRing : Debug + Quantifiable
{
fn size(&self) -> usize;
fn index_from_i32(&self, integer: i32) -> usize;
fn add(&self, a:usize, b:usize) -> usize;
fn sub(&self, a:usize, b:usize) -> usize;
fn mul(&self, a:usize, b:usize) -> usize;
fn pow(&self, a:usize, exp:u32) -> usize
{
let mut current=1;
let mut rem_exp=exp;
let mut factor=a;
while rem_exp > 0
{
if rem_exp % 2 == 1
{
current=self.mul(current,factor);
}
rem_exp/=2;
factor = self.mul(factor,factor);
}
current
}
fn order(&self, a:usize) -> (usize,usize)
{
let mut ret=(1,a);
if a==0
{
return ret;
}
let mut current = a;
let mut exp=1;
loop{
current=self.mul(current,a);
if current==ret.1
{
return ret;
}
exp+=1;
if current==1
{
return (exp,current);
}
if current<ret.1
{
ret=(exp,current);
}
}
}
fn is_primitive(&self, a:usize) -> bool
{
let n = self.size();
let half:u32 = ((n-1)/2).try_into().unwrap();
let prev = self.pow(a,half);
prev!=1 && self.mul(prev,prev)==1
}
}
#[derive(Debug,Quantifiable)]
struct IntegerIdealRing
{
modulo: usize
}
impl FlatRing for IntegerIdealRing
{
fn size(&self) -> usize
{
self.modulo
}
fn index_from_i32(&self, integer: i32) -> usize
{
let mut ret:i32 = integer;
let m :i32 = self.modulo as i32;
while ret<0
{
ret += m;
}
ret %= m;
ret as usize
}
fn add(&self, a:usize, b:usize) -> usize
{
(a+b) % self.modulo
}
fn sub(&self, a:usize, b:usize) -> usize
{
(self.modulo + a -b) % self.modulo
}
fn mul(&self, a:usize, b:usize) -> usize
{
(a*b) % self.modulo
}
}
struct SlimFlyCoordinates
{
local: usize,
global: usize,
block: usize,
}
impl SlimFlyCoordinates
{
fn unpack(index:usize,size:usize) -> SlimFlyCoordinates
{
let local = index % size;
let other = index / size;
let global = other % size;
let block = other / size;
SlimFlyCoordinates{
local,
global,
block,
}
}
fn pack(&self,size:usize) -> usize
{
(self.block * size + self.global)*size + self.local
}
}
#[derive(Debug,Quantifiable)]
pub struct SlimFly
{
field: Box<dyn FlatRing>,
primitive: usize,
servers_per_router: usize,
paley_sets: [Vec<usize>;2],
neg_paley_sets: [Vec<usize>;2],
}
impl Topology for SlimFly
{
fn num_routers(&self) -> usize
{
let n=self.field.size();
n*n*2
}
fn num_servers(&self) -> usize
{
self.servers_per_router * self.num_routers()
}
fn neighbour(&self, router_index:usize, port:usize) -> (Location,usize)
{
let n = self.field.size();
let router_coords = SlimFlyCoordinates::unpack(router_index,n);
if port < self.paley_sets[0].len()
{
let neighbour_local= self.field.add(router_coords.local,self.paley_sets[router_coords.block][port]);
return (Location::RouterPort{
router_index: SlimFlyCoordinates{local:neighbour_local,..router_coords}.pack(n),
router_port: self.neg_paley_sets[router_coords.block][port],
},0);
}
let offset=port - self.paley_sets[0].len();
if offset < self.field.size()
{
let global_product = self.field.mul(router_coords.global,offset);
let neighbour_local = if router_coords.block==0
{
self.field.sub(router_coords.local,global_product)
}
else
{
self.field.add(router_coords.local,global_product)
};
return (Location::RouterPort{
router_index: SlimFlyCoordinates{local:neighbour_local,global:offset,block:1-router_coords.block}.pack(n),
router_port: self.paley_sets[0].len() + router_coords.global,
},1);
}
let offset = offset - self.field.size();
(Location::ServerPort(offset+router_index*self.servers_per_router),2)
}
fn server_neighbour(&self, server_index:usize) -> (Location,usize)
{
let router_index = server_index/self.servers_per_router;
let router_port = (server_index % self.servers_per_router) + self.degree(router_index);
(Location::RouterPort{
router_index,
router_port,
},2)
}
fn diameter(&self) -> usize
{
2
}
fn distance(&self,origin:usize,destination:usize) -> usize
{
if origin==destination
{
0
} else
{
let origin_coords = SlimFlyCoordinates::unpack(origin,self.field.size());
let destination_coords = SlimFlyCoordinates::unpack(destination,self.field.size());
if origin_coords.block==destination_coords.block && origin_coords.global==destination_coords.global
{
let local_diff=self.field.sub(origin_coords.local,destination_coords.local);
if self.paley_sets[origin_coords.block].contains(&local_diff)
{
return 1;
}
}
if origin_coords.block!=destination_coords.block
{
let (left,right) = if origin_coords.block==0 {(origin_coords,destination_coords)} else {(destination_coords,origin_coords)};
let local_diff = self.field.sub(left.local,right.local);
let global_prod = self.field.mul(left.global,right.global);
if local_diff==global_prod
{
return 1;
}
}
2
}
}
fn amount_shortest_paths(&self,_origin:usize,_destination:usize) -> usize
{
todo!();
}
fn average_amount_shortest_paths(&self) -> f32
{
todo!();
}
fn maximum_degree(&self) -> usize
{
self.paley_sets[0].len() + self.field.size()
}
fn minimum_degree(&self) -> usize
{
self.paley_sets[0].len() + self.field.size()
}
fn degree(&self, _router_index: usize) -> usize
{
self.paley_sets[0].len() + self.field.size()
}
fn ports(&self, router_index: usize) -> usize
{
self.degree(router_index) + self.servers_per_router
}
fn cartesian_data(&self) -> Option<&CartesianData>
{
None
}
fn coordinated_routing_record(&self, _coordinates_a:&[usize], _coordinates_b:&[usize], _rng: Option<&mut StdRng>)->Vec<i32>
{
unimplemented!()
}
fn is_direction_change(&self, _router_index:usize, _input_port: usize, _output_port: usize) -> bool
{
false
}
fn up_down_distance(&self,_origin:usize,_destination:usize) -> Option<(usize,usize)>
{
None
}
}
impl SlimFly
{
pub fn new(arg:TopologyBuilderArgument) -> SlimFly
{
let mut prime=None;
let mut primitive=None;
let mut servers_per_router=None;
if let &ConfigurationValue::Object(ref cv_name, ref cv_pairs)=arg.cv
{
if cv_name!="SlimFly"
{
panic!("A SlimFly must be created from a `SlimFly` object not `{}`",cv_name);
}
for &(ref name,ref value) in cv_pairs
{
match name.as_ref()
{
"prime" => match value
{
&ConfigurationValue::Number(f) => prime=Some(f as usize),
_ => panic!("bad value for prime"),
},
"primitive" => match value
{
&ConfigurationValue::Number(f) => primitive=Some(f as usize),
_ => panic!("bad value for primitive"),
},
"servers_per_router" => match value
{
&ConfigurationValue::Number(f) => servers_per_router=Some(f as usize),
_ => panic!("bad value for servers_per_router"),
},
"legend_name" => (),
_ => panic!("Nothing to do with field {} in RandomRegularGraph",name),
}
}
}
else
{
panic!("Trying to create a NeighboursLists from a non-Object");
}
let prime=prime.expect("There were no prime");
let servers_per_router=servers_per_router.expect("There were no servers_per_router");
let field = IntegerIdealRing{modulo:prime};
let primitive=primitive.unwrap_or_else(||{
let n=field.size();
(2..n).find(|x|field.is_primitive(*x)).unwrap_or_else(||panic!("Could not find a primtive element in the ring {:?}",field))
});
let epsilon:i32 = {
let p4 = prime % 4;
if p4==3 {-1} else {1}
};
let paley_set:Vec<usize>=match epsilon
{
1 =>
{
let limit :u32 = (prime as u32-1)/2;
(0..limit).map(|k|field.pow(primitive,2*k)).collect()
},
-1 =>
{
let limit :u32= (prime as u32-3)/4;
(0..=limit).map(|k|2*k).chain( (0..=limit).map(|k|(prime as u32-1)/2 + 2*k) ).map(|exp|field.pow(primitive,exp)).collect()
},
0 =>
{
let limit :u32= prime as u32/2;
(0..limit).map(|k|field.pow(primitive,2*k)).collect()
},
_ => panic!("{} cannot be a prime",prime),
};
println!("primitive={} paley_set={:?} len={} (q-eps)/2={}",primitive,paley_set,paley_set.len(),(prime as i32-epsilon)/2);
let second_paley_set=paley_set.iter().map(|x|field.mul(*x,primitive)).collect();
let paley_sets:[Vec<usize>;2]=[paley_set,second_paley_set];
let neg_paley_sets=
{
let builder=|b:usize|{
(0..paley_sets[b].len()).map(|k|{
let elem=paley_sets[b][k];
let neg_elem=field.sub(0,elem);
paley_sets[b].iter().enumerate().find(|(_,x)|**x==neg_elem).expect("the Paley should be circulant").0
}).collect()};
[builder(0),builder(1)]
};
SlimFly{
field: Box::new(field),
primitive,
servers_per_router,
paley_sets,
neg_paley_sets,
}
}
}