use std::cmp::{max, min, Ordering};
use std::ops::{Add, Div, Mul, Sub};
use num::traits::Zero;
use crate::div;
use crate::XY;
pub type Vec2 = XY<usize>;
pub type Vec2i = XY<isize>;
impl<T: PartialOrd> PartialOrd for XY<T> {
fn partial_cmp(&self, other: &XY<T>) -> Option<Ordering> {
if self == other {
Some(Ordering::Equal)
} else if self.x < other.x && self.y < other.y {
Some(Ordering::Less)
} else if self.x > other.x && self.y > other.y {
Some(Ordering::Greater)
} else {
None
}
}
}
impl XY<usize> {
#[must_use]
pub fn max_value() -> Self {
Self::new(usize::max_value(), usize::max_value())
}
#[must_use]
pub fn saturating_sub<O: Into<Self>>(&self, other: O) -> Self {
let other = other.into();
self.zip_map(other, usize::saturating_sub)
}
#[must_use]
pub fn saturating_add<O: Into<XY<isize>>>(&self, other: O) -> Self {
let other = other.into();
self.zip_map(other, |s, o| {
if o > 0 {
s.saturating_add(o as usize)
} else {
s.saturating_sub((-o) as usize)
}
})
}
pub fn checked_add<O: Into<XY<isize>>>(&self, other: O) -> Option<Self> {
let other = other.into();
self.zip_map(other, |s, o| {
if o > 0 {
s.checked_add(o as usize)
} else {
s.checked_sub((-o) as usize)
}
})
.both()
}
#[must_use]
pub fn div_up<O>(&self, other: O) -> Self
where
O: Into<Self>,
{
self.zip_map(other.into(), div::div_up)
}
pub fn checked_sub<O: Into<Self>>(&self, other: O) -> Option<Self> {
let other = other.into();
if self.fits(other) {
Some(*self - other)
} else {
None
}
}
pub fn signed(self) -> XY<isize> {
self.into()
}
}
impl<T: Ord> XY<T> {
pub fn fits_in<O: Into<Self>>(&self, other: O) -> bool {
let other = other.into();
self.x <= other.x && self.y <= other.y
}
pub fn fits<O: Into<Self>>(&self, other: O) -> bool {
let other = other.into();
self.x >= other.x && self.y >= other.y
}
pub fn strictly_lt<O: Into<Self>>(&self, other: O) -> bool {
let other = other.into();
self < &other
}
pub fn strictly_gt<O: Into<Self>>(&self, other: O) -> bool {
let other = other.into();
self > &other
}
#[must_use]
pub fn max<A: Into<XY<T>>, B: Into<XY<T>>>(a: A, b: B) -> Self {
let a = a.into();
let b = b.into();
a.zip_map(b, max)
}
#[must_use]
pub fn min<A: Into<XY<T>>, B: Into<XY<T>>>(a: A, b: B) -> Self {
let a = a.into();
let b = b.into();
a.zip_map(b, min)
}
#[must_use]
pub fn or_min<O: Into<XY<T>>>(self, other: O) -> Self {
Self::min(self, other)
}
#[must_use]
pub fn or_max<O: Into<XY<T>>>(self, other: O) -> Self {
Self::max(self, other)
}
}
impl<T: Ord + Add<Output = T> + Clone> XY<T> {
#[must_use]
pub fn stack_vertical(&self, other: &Self) -> Self {
Self::new(
max(self.x.clone(), other.x.clone()),
self.y.clone() + other.y.clone(),
)
}
#[must_use]
pub fn stack_horizontal(&self, other: &Self) -> Self {
Self::new(
self.x.clone() + other.x.clone(),
max(self.y.clone(), other.y.clone()),
)
}
pub fn fits_in_rect<O1, O2>(&self, top_left: O1, size: O2) -> bool
where
O1: Into<Self>,
O2: Into<Self>,
{
let top_left = top_left.into();
self.fits(top_left.clone()) && self < &(top_left + size)
}
}
impl<T: Add> XY<T> {
pub fn sum(self) -> T::Output {
self.fold(std::ops::Add::add)
}
}
impl<T: Mul> XY<T> {
pub fn product(self) -> T::Output {
self.fold(std::ops::Mul::mul)
}
}
impl<T: Zero + Clone> XY<T> {
#[must_use]
pub fn keep_x(&self) -> Self {
Self::new(self.x.clone(), T::zero())
}
#[must_use]
pub fn keep_y(&self) -> Self {
Self::new(T::zero(), self.y.clone())
}
#[must_use]
pub fn zero() -> Self {
Self::new(T::zero(), T::zero())
}
}
impl<'a, T> From<&'a XY<T>> for XY<T>
where
T: Clone,
{
fn from(t: &'a XY<T>) -> Self {
t.clone()
}
}
impl<T> From<T> for XY<isize>
where
T: Into<XY<usize>>,
{
fn from(t: T) -> Self {
let other = t.into();
Self::new(other.x as isize, other.y as isize)
}
}
impl From<(i32, i32)> for XY<usize> {
fn from((x, y): (i32, i32)) -> Self {
(x as usize, y as usize).into()
}
}
impl From<(u32, u32)> for XY<usize> {
fn from((x, y): (u32, u32)) -> Self {
(x as usize, y as usize).into()
}
}
impl From<(u8, u8)> for XY<usize> {
fn from((x, y): (u8, u8)) -> Self {
(x as usize, y as usize).into()
}
}
impl From<(u16, u16)> for XY<usize> {
fn from((x, y): (u16, u16)) -> Self {
(x as usize, y as usize).into()
}
}
impl<T, O> Add<O> for XY<T>
where
T: Add<Output = T>,
O: Into<XY<T>>,
{
type Output = Self;
fn add(self, other: O) -> Self {
self.zip_map(other.into(), Add::add)
}
}
impl<T, O> Sub<O> for XY<T>
where
T: Sub<Output = T>,
O: Into<XY<T>>,
{
type Output = Self;
fn sub(self, other: O) -> Self {
self.zip_map(other.into(), Sub::sub)
}
}
impl<T: Clone + Div<Output = T>> Div<T> for XY<T> {
type Output = Self;
fn div(self, other: T) -> Self {
self.map(|s| s / other.clone())
}
}
impl Mul<usize> for XY<usize> {
type Output = Vec2;
fn mul(self, other: usize) -> Vec2 {
self.map(|s| s * other)
}
}
impl<T> Mul<XY<T>> for XY<T>
where
T: Mul<T>,
{
type Output = XY<T::Output>;
fn mul(self, other: XY<T>) -> Self::Output {
self.zip_map(other, |s, o| s * o)
}
}
impl<T> Div<XY<T>> for XY<T>
where
T: Div<T>,
{
type Output = XY<T::Output>;
fn div(self, other: XY<T>) -> Self::Output {
self.zip_map(other, |s, o| s / o)
}
}
#[cfg(test)]
mod tests {
use super::Vec2;
#[test]
fn test_from() {
let vi32 = Vec2::from((4i32, 5i32));
let vu32 = Vec2::from((4u32, 5u32));
let vusize = Vec2::from((4usize, 5usize));
let vvec = Vec2::new(4, 5);
assert_eq!(vi32 - vu32, vusize - vvec);
}
}