use std::{
collections::{HashMap, HashSet},
convert::TryFrom,
fmt::Display,
fs::File,
io::{BufRead, BufReader},
path::Path,
};
use getset::{CopyGetters, Getters, MutGetters};
use crate::error::ParseTspError;
static K_NAME: &str = "NAME";
static K_TYPE: &str = "TYPE";
static K_DIM: &str = "DIMENSION";
static K_CAP: &str = "CAPACITY";
static K_WEIGHT_TYPE: &str = "EDGE_WEIGHT_TYPE";
static K_WEIGHT_FORMAT: &str = "EDGE_WEIGHT_FORMAT";
static K_EDGE_FORMAT: &str = "EDGE_DATA_FORMAT";
static K_NODE_COORD_TYPE: &str = "NODE_COORD_TYPE";
static K_DISP_TYPE: &str = "DISPLAY_DATA_TYPE";
static K_NODE_COORD_SEC: &str = "NODE_COORD_SECTION";
static K_EDGE_WEIGHT_SEC: &str = "EDGE_WEIGHT_SECTION";
static K_TOUR_SEC: &str = "TOUR_SECTION";
#[derive(Debug, CopyGetters, Getters, MutGetters)]
pub struct Tsp {
#[getset(get = "pub")]
name: String,
#[getset(get_copy = "pub")]
kind: TspKind,
#[getset(get = "pub")]
comment: String,
#[getset(get_copy = "pub")]
dim: usize,
#[getset(get_copy = "pub")]
capacity: f64,
#[getset(get_copy = "pub")]
weight_kind: WeightKind,
#[getset(get_copy = "pub")]
weight_format: WeightFormat,
#[getset(get = "pub")]
edge_format: EdgeFormat,
#[getset(get_copy = "pub")]
coord_kind: CoordKind,
#[getset(get_copy = "pub")]
disp_kind: DisplayKind,
#[getset(get = "pub", get_mut = "pub")]
node_coords: HashMap<usize, Point>,
#[getset(get = "pub", get_mut = "pub")]
depots: HashSet<usize>,
#[getset(get = "pub", get_mut = "pub")]
demands: HashMap<usize, f64>,
#[getset(get = "pub", get_mut = "pub")]
fixed_edges: Vec<(usize, usize)>,
#[getset(get = "pub", get_mut = "pub")]
disp_coords: Vec<Point>,
#[getset(get = "pub", get_mut = "pub")]
edge_weights: Vec<Vec<f64>>,
#[getset(get = "pub", get_mut = "pub")]
tours: Vec<Vec<usize>>,
}
impl Tsp {
pub fn weight(&self, a: usize, b: usize) -> f64 {
match self.weight_kind {
WeightKind::Explicit => match self.weight_format {
WeightFormat::Function => 0.,
WeightFormat::FullMatrix => self.edge_weights[a][b],
WeightFormat::UpperRow | WeightFormat::LowerCol => match a.cmp(&b) {
std::cmp::Ordering::Less => self.edge_weights[a][b - a - 1],
std::cmp::Ordering::Equal => 0.,
std::cmp::Ordering::Greater => self.edge_weights[b][a - b - 1],
},
WeightFormat::UpperDiagRow | WeightFormat::LowerDiagCol => {
if a < b {
self.edge_weights[a][b - a]
} else {
self.edge_weights[b][a - b]
}
}
WeightFormat::LowerRow | WeightFormat::UpperCol => match a.cmp(&b) {
std::cmp::Ordering::Less => self.edge_weights[b - 1][a],
std::cmp::Ordering::Equal => 0.,
std::cmp::Ordering::Greater => self.edge_weights[a - 1][b],
},
WeightFormat::LowerDiagRow | WeightFormat::UpperDiagCol => {
if a < b {
self.edge_weights[b][a]
} else {
self.edge_weights[a][b]
}
}
WeightFormat::Undefined => 0.,
},
_ => {
if let (Some(na), Some(nb)) = (self.node_coords.get(&a), self.node_coords.get(&b)) {
self.weight_kind.cost(na.pos(), nb.pos())
} else {
0.
}
}
}
}
}
impl Display for Tsp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Spec: {name} {kind} {dim} {wkind} {wformat} {eformat} {ckind} {dkind}\n
Data: {coord:?} {eweights:?} {dcoord:?} {fedges:?}",
name = self.name,
kind = self.kind,
dim = self.dim,
wkind = self.weight_kind,
wformat = self.weight_format,
eformat = self.edge_format,
ckind = self.coord_kind,
dkind = self.disp_kind,
coord = self.node_coords,
eweights = self.edge_weights,
dcoord = self.disp_coords,
fedges = self.fixed_edges,
)
}
}
#[derive(Debug, Default)]
pub struct TspBuilder {
name: Option<String>,
kind: Option<TspKind>,
comment: Option<String>,
dim: Option<usize>,
capacity: Option<f64>,
weight_kind: Option<WeightKind>,
weight_format: Option<WeightFormat>,
edge_format: Option<EdgeFormat>,
coord_kind: Option<CoordKind>,
disp_kind: Option<DisplayKind>,
coords: Option<HashMap<usize, Point>>,
depots: Option<HashSet<usize>>,
demands: Option<HashMap<usize, f64>>,
edge_weights: Option<Vec<Vec<f64>>>,
disp_coords: Option<Vec<Point>>,
fixed_edges: Option<Vec<(usize, usize)>>,
tours: Option<Vec<Vec<usize>>>,
}
impl TspBuilder {
pub fn new() -> Self {
TspBuilder {
..Default::default()
}
}
pub fn parse_str<S>(s: S) -> Result<Tsp, ParseTspError>
where
S: AsRef<str>,
{
let mut itr = s.as_ref().lines();
Self::parse_it(&mut itr)
}
pub fn parse_path<P>(path: P) -> Result<Tsp, ParseTspError>
where
P: AsRef<Path>,
{
if path.as_ref().is_dir() {
return Err(ParseTspError::Other("Path is a directory"));
}
let file = File::open(path)?;
let reader = BufReader::new(file);
let mut lines_it = reader.lines().map(|l| l.unwrap());
Self::parse_it(&mut lines_it)
}
fn parse_it<I>(itr: &mut I) -> Result<Tsp, ParseTspError>
where
I: Iterator,
<I as Iterator>::Item: AsRef<str>,
{
let splitter = |s: &str| {
let val = s.split(':').collect::<Vec<&str>>();
String::from(val[1].trim())
};
let mut builder = TspBuilder::new();
while let Some(line) = itr.next() {
let line = line.as_ref().trim();
if line.is_empty() {
continue;
}
if line.starts_with("EOF") {
break;
}
if line.starts_with(K_NAME) {
builder.name = Some(splitter(&line));
} else if line.starts_with(K_TYPE) {
builder.kind = Some(TspKind::try_from(InputWrapper(splitter(&line).as_str()))?);
} else if line.starts_with("COMMENT") {
builder.comment = Some(splitter(&line));
} else if line.starts_with(K_DIM) {
builder.dim = Some(splitter(&line).parse::<usize>().unwrap());
} else if line.starts_with("CAPACITY") {
builder.capacity = Some(splitter(&line).parse::<f64>().unwrap());
} else if line.starts_with(K_WEIGHT_TYPE) {
let kind = WeightKind::try_from(InputWrapper(splitter(&line).as_str()))?;
builder.weight_kind = Some(kind);
builder.coord_kind = Some(CoordKind::from(kind));
} else if line.starts_with(K_WEIGHT_FORMAT) {
builder.weight_format = Some(WeightFormat::try_from(InputWrapper(
splitter(&line).as_str(),
))?);
} else if line.starts_with(K_EDGE_FORMAT) {
builder.edge_format = Some(EdgeFormat::try_from(InputWrapper(
splitter(&line).as_str(),
))?);
} else if line.starts_with(K_NODE_COORD_TYPE) {
builder.coord_kind =
Some(CoordKind::try_from(InputWrapper(splitter(&line).as_str()))?);
} else if line.starts_with(K_DISP_TYPE) {
builder.disp_kind = Some(DisplayKind::try_from(InputWrapper(
splitter(&line).as_str(),
))?);
} else if line.starts_with(K_NODE_COORD_SEC) {
builder.parse_node_coord_section(itr)?;
} else if line.starts_with("DEPOT_SECTION") {
builder.parse_depot_section(itr)?;
} else if line.starts_with("DEMAND_SECTION") {
builder.parse_demand_section(itr)?;
} else if line.starts_with("EDGE_DATA_SECTION") {
builder.parse_edge_data_section(itr)?;
} else if line.starts_with("FIXED_EDGES_SECTION") {
builder.parse_fixed_edges_section(itr)?;
} else if line.starts_with("DISPLAY_DATA_SECTION") {
builder.parse_display_data_section(itr)?;
} else if line.starts_with(K_TOUR_SEC) {
builder.parse_tour_section(itr)?;
} else if line.starts_with(K_EDGE_WEIGHT_SEC) {
builder.parse_edge_weight_section(itr)?;
} else {
return Err(ParseTspError::InvalidEntry(String::from(line)));
}
}
builder.build()
}
fn parse_node_coord_section<I>(&mut self, lines_it: &mut I) -> Result<(), ParseTspError>
where
I: Iterator,
<I as Iterator>::Item: AsRef<str>,
{
self.validate_spec()?;
let func: Box<dyn Fn(&Vec<&str>) -> Point> = match &self.coord_kind.unwrap() {
CoordKind::Coord2d => {
let f = |v: &Vec<&str>| {
Point::new2(
v[0].parse::<usize>().unwrap(),
v[1].parse::<f64>().unwrap(),
v[2].parse::<f64>().unwrap(),
)
};
Box::new(f)
}
CoordKind::Coord3d => {
let f = |v: &Vec<&str>| {
Point::new3(
v[0].parse::<usize>().unwrap(),
v[1].parse::<f64>().unwrap(),
v[2].parse::<f64>().unwrap(),
v[3].parse::<f64>().unwrap(),
)
};
Box::new(f)
}
CoordKind::NoCoord | CoordKind::Undefined => {
unimplemented!()
}
};
let mut count = 0;
let dim = self.dim.unwrap();
let mut dta = HashMap::with_capacity(dim);
while count < dim {
let line = lines_it.next().unwrap();
let pt = func(
&line
.as_ref()
.trim()
.split_whitespace()
.collect::<Vec<&str>>(),
);
dta.insert(pt.id, pt);
count += 1;
}
self.coords = Some(dta);
Ok(())
}
fn parse_depot_section<I>(&mut self, lines_it: &mut I) -> Result<(), ParseTspError>
where
I: Iterator,
<I as Iterator>::Item: AsRef<str>,
{
self.validate_spec()?;
let mut dta = HashSet::new();
loop {
let line = lines_it.next().unwrap();
if line.as_ref().trim().starts_with("-1") {
break;
}
dta.insert(line.as_ref().trim().parse::<usize>().unwrap());
}
self.depots = Some(dta);
Ok(())
}
fn parse_demand_section<I>(&mut self, lines_it: &mut I) -> Result<(), ParseTspError>
where
I: Iterator,
<I as Iterator>::Item: AsRef<str>,
{
self.validate_spec()?;
let mut dta = HashMap::new();
for _ in 0..self.dim.unwrap() {
let line = lines_it.next().unwrap();
let mut it = line.as_ref().trim().split_whitespace();
if let (Some(id), Some(de)) = (it.next(), it.next()) {
dta.insert(id.parse::<usize>().unwrap(), de.parse::<f64>().unwrap());
}
}
self.demands = Some(dta);
Ok(())
}
fn parse_edge_data_section<I>(&mut self, lines_it: &mut I) -> Result<(), ParseTspError>
where
I: Iterator,
<I as Iterator>::Item: AsRef<str>,
{
let mut dta = Vec::new();
match self.edge_format.as_mut().unwrap() {
EdgeFormat::EdgeList(v) => {
loop {
let line = lines_it.next().unwrap();
if line.as_ref().trim().starts_with("-1") {
break;
}
let mut it = line.as_ref().trim().split_whitespace();
if let (Some(f), Some(l)) = (it.next(), it.next()) {
dta.push((f.parse::<usize>().unwrap(), l.parse::<usize>().unwrap()));
}
}
v.append(&mut dta);
}
EdgeFormat::AdjList => todo!(),
EdgeFormat::Undefined => {
return Err(ParseTspError::InvalidEntry(String::from(K_EDGE_FORMAT)))
}
}
Ok(())
}
fn parse_fixed_edges_section<I>(&mut self, lines_it: &mut I) -> Result<(), ParseTspError>
where
I: Iterator,
<I as Iterator>::Item: AsRef<str>,
{
let mut dta = Vec::new();
loop {
let line = lines_it.next().unwrap();
if line.as_ref().trim().starts_with("-1") {
break;
}
let mut it = line.as_ref().trim().split_whitespace();
if let (Some(f), Some(l)) = (it.next(), it.next()) {
dta.push((f.parse::<usize>().unwrap(), l.parse::<usize>().unwrap()));
}
}
self.fixed_edges = Some(dta);
Ok(())
}
fn parse_tour_section<I>(&mut self, lines_it: &mut I) -> Result<(), ParseTspError>
where
I: Iterator,
<I as Iterator>::Item: AsRef<str>,
{
self.validate_spec()?;
let mut dta = Vec::new();
let mut v = Vec::new();
loop {
let line = lines_it.next().unwrap();
let s = line.as_ref().trim();
if s.starts_with("-1") {
let tmp = v.drain(0..).collect();
dta.push(tmp);
match lines_it.peekable().peek() {
Some(peek) => {
let s = peek.as_ref().trim();
if s.starts_with("-1") {
break;
}
let ch = s.chars().next().unwrap();
if ch.is_digit(10) {
v = Vec::new();
v.append(
&mut s
.split_whitespace()
.map(|s| s.parse::<usize>().unwrap())
.collect(),
);
} else {
break;
}
}
None => break,
};
continue;
}
v.append(
&mut s
.split_whitespace()
.map(|s| s.parse::<usize>().unwrap())
.collect(),
);
}
self.tours = Some(dta);
Ok(())
}
fn parse_edge_weight_section<I>(&mut self, lines_it: &mut I) -> Result<(), ParseTspError>
where
I: Iterator,
<I as Iterator>::Item: AsRef<str>,
{
self.validate_spec()?;
let dim = self.dim.unwrap();
let (len_vec, cnt, it): (usize, usize, Box<dyn Iterator<Item = usize>>) =
match self.weight_format.unwrap() {
WeightFormat::Function => (0, 0, Box::new(std::iter::empty::<usize>())),
WeightFormat::FullMatrix => {
(dim, dim * dim, Box::new(std::iter::repeat(dim).take(dim)))
}
WeightFormat::UpperRow | WeightFormat::LowerCol => {
(dim - 1, dim * (dim - 1) / 2, Box::new((1..dim).rev()))
}
WeightFormat::LowerRow | WeightFormat::UpperCol => {
(dim - 1, dim * (dim - 1) / 2, Box::new(1..dim))
}
WeightFormat::UpperDiagRow | WeightFormat::LowerDiagCol => {
(dim, dim * (dim + 1) / 2, Box::new((1..=dim).rev()))
}
WeightFormat::LowerDiagRow | WeightFormat::UpperDiagCol => {
(dim, dim * (dim + 1) / 2, Box::new(1..=dim))
}
WeightFormat::Undefined => (0, 0, Box::new(std::iter::empty::<usize>())),
};
let mut dta = Vec::with_capacity(len_vec);
let mut v = Vec::with_capacity(cnt);
while v.len() < cnt {
let line = lines_it.next().unwrap();
let mut tmp: Vec<f64> = line
.as_ref()
.trim()
.split_whitespace()
.map(|s| s.parse::<f64>().unwrap())
.collect();
v.append(&mut tmp);
}
if v.len() == dim + 1 {
v.remove(0);
}
for len_row in it {
dta.push(v.drain(0..len_row).collect());
}
self.edge_weights = Some(dta);
Ok(())
}
fn parse_display_data_section<I>(&mut self, lines_it: &mut I) -> Result<(), ParseTspError>
where
I: Iterator,
<I as Iterator>::Item: AsRef<str>,
{
self.validate_spec()?;
let dim = self.dim.unwrap();
let mut dta = Vec::with_capacity(dim);
let mut count = 0;
while count < dim {
let line = lines_it.next().unwrap();
let v = line
.as_ref()
.trim()
.split_whitespace()
.collect::<Vec<&str>>();
dta.push(Point::new2(
v[0].parse::<usize>().unwrap(),
v[1].parse::<f64>().unwrap(),
v[2].parse::<f64>().unwrap(),
));
count += 1;
}
self.disp_coords = Some(dta);
Ok(())
}
fn validate_spec(&self) -> Result<(), ParseTspError> {
if self.name.is_none() {
return Err(ParseTspError::MissingEntry(String::from(K_NAME)));
}
match self.kind {
Some(kind) => {
match kind {
TspKind::Tsp | TspKind::Atsp | TspKind::Cvrp | TspKind::Sop => {
match self.weight_kind {
Some(wk) => {
if wk == WeightKind::Undefined {
return Err(ParseTspError::InvalidEntry(String::from(
K_WEIGHT_TYPE,
)));
}
}
None => {
return Err(ParseTspError::MissingEntry(String::from(
K_WEIGHT_TYPE,
)))
}
}
if kind == TspKind::Cvrp && self.capacity.is_none() {
return Err(ParseTspError::MissingEntry(String::from(K_CAP)));
}
}
TspKind::Hcp => match self.edge_format {
Some(ref ef) => {
if ef == &EdgeFormat::Undefined {
return Err(ParseTspError::InvalidEntry(String::from(
K_EDGE_FORMAT,
)));
}
}
None => {
return Err(ParseTspError::MissingEntry(String::from(K_EDGE_FORMAT)))
}
},
TspKind::Tour => {}
TspKind::Undefined => {
return Err(ParseTspError::InvalidEntry(String::from(K_TYPE)))
}
}
if kind != TspKind::Tour && self.dim.is_none() {
return Err(ParseTspError::MissingEntry(String::from(K_DIM)));
}
}
None => return Err(ParseTspError::MissingEntry(String::from(K_TYPE))),
}
Ok(())
}
fn validate_data(&self) -> Result<(), ParseTspError> {
match self.kind.unwrap() {
TspKind::Tsp | TspKind::Atsp | TspKind::Cvrp => match self.weight_kind.unwrap() {
WeightKind::Explicit => {
if self.edge_weights.is_none() {
return Err(ParseTspError::MissingEntry(String::from(K_EDGE_WEIGHT_SEC)));
}
}
_ => {
if self.coords.is_none() {
return Err(ParseTspError::MissingEntry(String::from(K_NODE_COORD_SEC)));
}
}
},
TspKind::Sop => {}
TspKind::Hcp => {}
TspKind::Tour => {
if self.tours.is_none() {
return Err(ParseTspError::MissingEntry(String::from(K_TOUR_SEC)));
}
}
TspKind::Undefined => {}
}
if self.weight_kind.is_some() {
match self.weight_kind.unwrap() {
WeightKind::Explicit => {
if self.edge_weights.is_none() {
return Err(ParseTspError::MissingEntry(String::from(K_EDGE_WEIGHT_SEC)));
}
}
_ => {
if self.coords.is_none() {
return Err(ParseTspError::MissingEntry(String::from(K_NODE_COORD_SEC)));
}
}
}
}
Ok(())
}
pub fn build(self) -> Result<Tsp, ParseTspError> {
self.validate_spec()?;
self.validate_data()?;
let tsp = Tsp {
name: self.name.unwrap(),
kind: self.kind.unwrap(),
comment: self.comment.unwrap_or_else(String::new),
dim: self.dim.unwrap_or(0),
capacity: self.capacity.unwrap_or(0.),
weight_kind: self.weight_kind.unwrap_or(WeightKind::Undefined),
weight_format: self.weight_format.unwrap_or(WeightFormat::Undefined),
edge_format: self.edge_format.unwrap_or(EdgeFormat::Undefined),
coord_kind: self.coord_kind.unwrap_or(CoordKind::Undefined),
disp_kind: self.disp_kind.unwrap_or(DisplayKind::Undefined),
node_coords: self.coords.unwrap_or_else(|| HashMap::with_capacity(0)),
demands: self.demands.unwrap_or_else(|| HashMap::with_capacity(0)),
depots: self.depots.unwrap_or_else(|| HashSet::with_capacity(0)),
edge_weights: self.edge_weights.unwrap_or_else(|| Vec::with_capacity(0)),
disp_coords: self.disp_coords.unwrap_or_else(|| Vec::with_capacity(0)),
fixed_edges: self.fixed_edges.unwrap_or_else(|| Vec::with_capacity(0)),
tours: self.tours.unwrap_or_else(|| Vec::with_capacity(0)),
};
Ok(tsp)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
struct InputWrapper<T>(T);
#[derive(Clone, Debug)]
pub struct Point {
id: usize,
pos: Vec<f64>,
}
impl Point {
pub fn id(&self) -> usize {
self.id
}
pub fn pos(&self) -> &Vec<f64> {
&self.pos
}
pub fn into_value(self) -> (usize, Vec<f64>) {
(self.id, self.pos)
}
pub fn new(id: usize, pos: Vec<f64>) -> Self {
Self { id, pos }
}
pub fn new2(id: usize, x: f64, y: f64) -> Self {
Self::new(id, vec![x, y])
}
pub fn new3(id: usize, x: f64, y: f64, z: f64) -> Self {
Self::new(id, vec![x, y, z])
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum TspKind {
Tsp,
Atsp,
Sop,
Hcp,
Cvrp,
Tour,
Undefined,
}
impl_disp_enum!(TspKind);
impl<T> TryFrom<InputWrapper<T>> for TspKind
where
T: AsRef<str>,
{
type Error = ParseTspError;
fn try_from(value: InputWrapper<T>) -> Result<Self, Self::Error> {
match value.0.as_ref() {
"TSP" => Ok(Self::Tsp),
"ATSP" => Ok(Self::Atsp),
"SOP" => Ok(Self::Sop),
"HCP" => Ok(Self::Hcp),
"CVRP" => Ok(Self::Cvrp),
"TOUR" => Ok(Self::Tour),
_ => Err(ParseTspError::InvalidInput {
key: K_TYPE.to_string(),
val: value.0.as_ref().to_string(),
}),
}
}
}
impl From<&str> for TspKind {
fn from(s: &str) -> Self {
match s {
"TSP" => Self::Tsp,
"ATSP" => Self::Atsp,
"SOP" => Self::Sop,
"HCP" => Self::Hcp,
"CVRP" => Self::Cvrp,
"TOUR" => Self::Tour,
_ => Self::Undefined,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum WeightKind {
Explicit,
Euc2d,
Euc3d,
Max2d,
Max3d,
Man2d,
Man3d,
Ceil2d,
Geo,
Att,
Xray1,
Xray2,
Custom,
Undefined,
}
impl_disp_enum!(WeightKind);
impl From<&str> for WeightKind {
fn from(s: &str) -> Self {
match s {
"EXPLICIT" => Self::Explicit,
"EUC_2D" => Self::Euc2d,
"EUC_3D" => Self::Euc3d,
"MAX_2D" => Self::Max2d,
"MAX_3D" => Self::Max3d,
"MAN_2D" => Self::Man2d,
"MAN_3D" => Self::Man3d,
"CEIL_2D" => Self::Ceil2d,
"GEO" => Self::Geo,
"ATT" => Self::Att,
"XRAY1" => Self::Xray1,
"XRAY2" => Self::Xray2,
"SPECIAL" => Self::Custom,
_ => Self::Undefined,
}
}
}
impl<T> TryFrom<InputWrapper<T>> for WeightKind
where
T: AsRef<str>,
{
type Error = ParseTspError;
fn try_from(value: InputWrapper<T>) -> Result<Self, Self::Error> {
match value.0.as_ref() {
"EXPLICIT" => Ok(Self::Explicit),
"EUC_2D" => Ok(Self::Euc2d),
"EUC_3D" => Ok(Self::Euc3d),
"MAX_2D" => Ok(Self::Max2d),
"MAX_3D" => Ok(Self::Max3d),
"MAN_2D" => Ok(Self::Man2d),
"MAN_3D" => Ok(Self::Man3d),
"CEIL_2D" => Ok(Self::Ceil2d),
"GEO" => Ok(Self::Geo),
"ATT" => Ok(Self::Att),
"XRAY1" => Ok(Self::Xray1),
"XRAY2" => Ok(Self::Xray2),
"SPECIAL" => Ok(Self::Custom),
_ => Err(ParseTspError::InvalidInput {
key: K_WEIGHT_TYPE.to_string(),
val: value.0.as_ref().to_string(),
}),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum WeightFormat {
Function,
FullMatrix,
UpperRow,
LowerRow,
UpperDiagRow,
LowerDiagRow,
UpperCol,
LowerCol,
UpperDiagCol,
LowerDiagCol,
Undefined,
}
impl WeightFormat {
#[allow(dead_code)]
pub(crate) fn tsp_str(&self) -> &'static str {
match self {
WeightFormat::Function => "FUNCTION",
WeightFormat::FullMatrix => "FULL_MATRIX",
WeightFormat::UpperRow => "UPPER_ROW",
WeightFormat::LowerRow => "LOWER_ROW",
WeightFormat::UpperDiagRow => "UPPER_DIAG_ROW",
WeightFormat::LowerDiagRow => "LOWER_DIAG_ROW",
WeightFormat::UpperCol => "UPPER_COL",
WeightFormat::LowerCol => "LOWER_COL",
WeightFormat::UpperDiagCol => "UPPER_DIAG_COL",
WeightFormat::LowerDiagCol => "LOWER_DIAG_COL",
WeightFormat::Undefined => "UNDEFINED",
}
}
}
impl From<&str> for WeightFormat {
fn from(s: &str) -> Self {
match s {
"FUNCTION" => Self::Function,
"FULL_MATRIX" => Self::FullMatrix,
"UPPER_ROW" => Self::UpperRow,
"LOWER_ROW" => Self::LowerRow,
"UPPER_DIAG_ROW" => Self::UpperDiagRow,
"LOWER_DIAG_ROW" => Self::LowerDiagRow,
"UPPER_COL" => Self::UpperCol,
"LOWER_COL" => Self::LowerCol,
"UPPER_DIAG_COL" => Self::UpperDiagCol,
"LOWER_DIAG_COL" => Self::LowerDiagCol,
_ => Self::Undefined,
}
}
}
impl<T> TryFrom<InputWrapper<T>> for WeightFormat
where
T: AsRef<str>,
{
type Error = ParseTspError;
fn try_from(value: InputWrapper<T>) -> Result<Self, Self::Error> {
match value.0.as_ref() {
"FUNCTION" => Ok(Self::Function),
"FULL_MATRIX" => Ok(Self::FullMatrix),
"UPPER_ROW" => Ok(Self::UpperRow),
"LOWER_ROW" => Ok(Self::LowerRow),
"UPPER_DIAG_ROW" => Ok(Self::UpperDiagRow),
"LOWER_DIAG_ROW" => Ok(Self::LowerDiagRow),
"UPPER_COL" => Ok(Self::UpperCol),
"LOWER_COL" => Ok(Self::LowerCol),
"UPPER_DIAG_COL" => Ok(Self::UpperDiagCol),
"LOWER_DIAG_COL" => Ok(Self::LowerDiagCol),
_ => Err(ParseTspError::InvalidInput {
key: K_WEIGHT_FORMAT.to_string(),
val: value.0.as_ref().to_string(),
}),
}
}
}
impl_disp_enum!(WeightFormat);
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum EdgeFormat {
EdgeList(Vec<(usize, usize)>),
AdjList,
Undefined,
}
impl From<&str> for EdgeFormat {
fn from(s: &str) -> Self {
match s {
"EDGE_LIST" => Self::EdgeList(Vec::new()),
"ADJ_LIST" => Self::AdjList,
_ => Self::Undefined,
}
}
}
impl<T> TryFrom<InputWrapper<T>> for EdgeFormat
where
T: AsRef<str>,
{
type Error = ParseTspError;
fn try_from(value: InputWrapper<T>) -> Result<Self, Self::Error> {
match value.0.as_ref() {
"EDGE_LIST" => Ok(Self::EdgeList(Vec::new())),
"ADJ_LIST" => Ok(Self::AdjList),
_ => Err(ParseTspError::InvalidInput {
key: K_EDGE_FORMAT.to_string(),
val: value.0.as_ref().to_string(),
}),
}
}
}
impl_disp_enum!(EdgeFormat);
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum CoordKind {
Coord2d,
Coord3d,
NoCoord,
Undefined,
}
impl From<&str> for CoordKind {
fn from(s: &str) -> Self {
match s {
"TWOD_COORDS" => Self::Coord2d,
"THREED_COORDS" => Self::Coord3d,
"NO_COORDS" => Self::NoCoord,
_ => Self::Undefined,
}
}
}
impl<T> TryFrom<InputWrapper<T>> for CoordKind
where
T: AsRef<str>,
{
type Error = ParseTspError;
fn try_from(value: InputWrapper<T>) -> Result<Self, Self::Error> {
match value.0.as_ref() {
"TWOD_COORDS" => Ok(Self::Coord2d),
"THREED_COORDS" => Ok(Self::Coord3d),
"NO_COORDS" => Ok(Self::NoCoord),
_ => Err(ParseTspError::InvalidInput {
key: K_NODE_COORD_TYPE.to_string(),
val: value.0.as_ref().to_string(),
}),
}
}
}
impl From<WeightKind> for CoordKind {
fn from(kind: WeightKind) -> Self {
match kind {
WeightKind::Euc2d
| WeightKind::Max2d
| WeightKind::Man2d
| WeightKind::Ceil2d
| WeightKind::Geo
| WeightKind::Att => Self::Coord2d,
WeightKind::Euc3d | WeightKind::Max3d | WeightKind::Man3d => Self::Coord3d,
_ => Self::Undefined,
}
}
}
impl_disp_enum!(CoordKind);
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum DisplayKind {
DispCoo,
Disp2d,
NoDisp,
Undefined,
}
impl From<&str> for DisplayKind {
fn from(s: &str) -> Self {
match s {
"COORD_DISPLAY" => Self::DispCoo,
"TWOD_DISPLAY" => Self::Disp2d,
"NO_DISPLAY" => Self::NoDisp,
_ => Self::Undefined,
}
}
}
impl<T> TryFrom<InputWrapper<T>> for DisplayKind
where
T: AsRef<str>,
{
type Error = ParseTspError;
fn try_from(value: InputWrapper<T>) -> Result<Self, Self::Error> {
match value.0.as_ref() {
"COORD_DISPLAY" => Ok(Self::DispCoo),
"TWOD_DISPLAY" => Ok(Self::Disp2d),
"NO_DISPLAY" => Ok(Self::NoDisp),
_ => Err(ParseTspError::InvalidInput {
key: K_DISP_TYPE.to_string(),
val: value.0.as_ref().to_string(),
}),
}
}
}
impl_disp_enum!(DisplayKind);