words words

words words

TODO words

use itertools::Itertools;
use std::collections::HashMap;

trait LocationTraits: std::cmp::Eq
	+ std::hash::Hash
	+ std::clone::Clone
	+ std::fmt::Debug
	+ std::fmt::Display {}
impl<T: std::cmp::Eq
	+ std::hash::Hash
	+ std::clone::Clone
	+ std::fmt::Debug
	+ std::fmt::Display> LocationTraits for T {}

trait DistanceTraits: std::default::Default + std::clone::Clone + std::ops::Add + std::cmp::Ord {}
impl<T: std::default::Default + std::clone::Clone+ std::ops::Add + std::cmp::Ord> DistanceTraits for T {}

#[derive(Debug, Clone)]
pub struct Graph<L, D> {
	distances: HashMap<[L; 2], D>,

	is_directed: bool
}

impl<Location, Distance> Graph<Location, Distance>
where
	Location: LocationTraits,
	Distance: DistanceTraits<Output = Distance>
{
	pub fn new_empty () -> Graph<Location, Distance> {
		return Graph {
			distances: HashMap::new(),
			is_directed: true
		};
	}

	pub fn new_from_iterator (iterator: impl Iterator<Item = (Location, Location, Distance)>) -> Graph<Location, Distance> {
		let mut graph = Graph::new_empty();
		iterator.for_each(|x| graph.add_path(x.0, x.1, x.2));
		return graph;
	}

	pub fn make_directed (&mut self) { self.is_directed = true; }
	pub fn make_undirected (&mut self) { self.is_directed = false; }

	pub fn add_path (&mut self, from: Location, to: Location, distance: Distance) {
		self.distances.insert([from, to], distance);
	}

	pub fn get_locations (&self) -> impl Iterator<Item = &Location> {
		return self.distances.keys().flatten().unique();
	}

	pub fn path_distance (&self, path_keys: Vec<&Location>) -> Distance {
		let mut total_distance = Distance::default();
		for i in 1..path_keys.len() {
			let from = path_keys[i - 1].clone();
			let to = path_keys[i].clone();

			let forward = self.distances.get(&[from.clone(), to.clone()]);
			let backward = self.distances.get(&[to.clone(), from.clone()]);

			total_distance = total_distance + if self.is_directed {
				forward.unwrap().clone()
			} else {
				forward.or_else(|| backward).unwrap().clone()
			};
		}
		
		return total_distance;
	}

	pub fn all_paths (&self) -> impl Iterator<Item = Vec<&Location>> {
		return self.get_locations().permutations(self.get_locations().count());
	}
}