<commit_before>use num::{self, Float, Num, NumCast};
use ordered_float::OrderedFloat;

use graph::{Attribute, Geometry};

pub trait Unit: Copy + Num {
    fn unit_radius() -> (Self, Self);
    fn unit_width() -> (Self, Self);
}

macro_rules! unit {
    (integer => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one() + Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one())
            }
        }
    )*};
    (real => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::One;
                (-Self::one(), Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::One;
                let half = Self::one() / (Self::one() + Self::one());
                (-half, half)
            }
        }
    )*};
}
unit!(integer => i8, i16, i32, i64, u8, u16, u32, u64);
unit!(real => f32, f64);

impl<T> Attribute for (T, T, T)
where
    T: Default + Unit,
{
}

impl<T> Attribute for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Default + Float + Unit,
{
}

impl<T> Geometry for (T, T, T)
where
    T: Default + Unit,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

impl<T> Geometry for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Default + Float + Unit,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

pub trait Interpolate<T = Self>: Sized {
    type Output;

    fn lerp(&self, other: &T, f: f32) -> Self::Output;

    fn midpoint(&self, other: &T) -> Self::Output {
        self.lerp(other, 0.5)
    }
}

impl<T> Interpolate for (T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(&self, other: &Self, f: f32) -> Self::Output {
        (lerp(self.0, other.0, f), lerp(self.1, other.1, f))
    }
}

impl<T> Interpolate for (T, T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(&self, other: &Self, f: f32) -> Self::Output {
        (
            lerp(self.0, other.0, f),
            lerp(self.1, other.1, f),
            lerp(self.2, other.2, f),
        )
    }
}

fn lerp<T>(a: T, b: T, f: f32) -> T
where
    T: Num + NumCast,
{
    let f = num::clamp(f, 0.0, 1.0);
    let af = <f32 as NumCast>::from(a).unwrap() * (1.0 - f);
    let bf = <f32 as NumCast>::from(b).unwrap() * f;
    <T as NumCast>::from(af + bf).unwrap()
}
<commit_msg>Use `f64` instead of `f32` for `lerp`.<commit_after>use num::{self, Float, Num, NumCast};
use ordered_float::OrderedFloat;

use graph::{Attribute, Geometry};

pub trait Unit: Copy + Num {
    fn unit_radius() -> (Self, Self);
    fn unit_width() -> (Self, Self);
}

macro_rules! unit {
    (integer => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one() + Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one())
            }
        }
    )*};
    (real => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::One;
                (-Self::one(), Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::One;
                let half = Self::one() / (Self::one() + Self::one());
                (-half, half)
            }
        }
    )*};
}
unit!(integer => i8, i16, i32, i64, u8, u16, u32, u64);
unit!(real => f32, f64);

impl<T> Attribute for (T, T, T)
where
    T: Default + Unit,
{
}

impl<T> Attribute for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Default + Float + Unit,
{
}

impl<T> Geometry for (T, T, T)
where
    T: Default + Unit,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

impl<T> Geometry for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Default + Float + Unit,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

pub trait Interpolate<T = Self>: Sized {
    type Output;

    fn lerp(&self, other: &T, f: f64) -> Self::Output;

    fn midpoint(&self, other: &T) -> Self::Output {
        self.lerp(other, 0.5)
    }
}

impl<T> Interpolate for (T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(&self, other: &Self, f: f64) -> Self::Output {
        (lerp(self.0, other.0, f), lerp(self.1, other.1, f))
    }
}

impl<T> Interpolate for (T, T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(&self, other: &Self, f: f64) -> Self::Output {
        (
            lerp(self.0, other.0, f),
            lerp(self.1, other.1, f),
            lerp(self.2, other.2, f),
        )
    }
}

fn lerp<T>(a: T, b: T, f: f64) -> T
where
    T: Num + NumCast,
{
    let f = num::clamp(f, 0.0, 1.0);
    let af = <f64 as NumCast>::from(a).unwrap() * (1.0 - f);
    let bf = <f64 as NumCast>::from(b).unwrap() * f;
    <T as NumCast>::from(af + bf).unwrap()
}
<|endoftext|>


<commit_before>use num::{self, Float, Num, NumCast};
use ordered_float::OrderedFloat;

pub trait Unit: Copy + Num {
    fn unit_radius() -> (Self, Self);
    fn unit_width() -> (Self, Self);
}

macro_rules! unit {
    (integer => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one() + Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one())
            }
        }
    )*};
    (real => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::One;
                (-Self::one(), Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::One;
                let half = Self::one() / (Self::one() + Self::one());
                (-half, half)
            }
        }
    )*};
}
unit!(integer => i8, i16, i32, i64, u8, u16, u32, u64);
unit!(real => f32, f64);

pub trait Vector {
    type Scalar;
}

impl<T> Vector for (T, T) {
    type Scalar = T;
}

impl<T> Vector for (T, T, T) {
    type Scalar = T;
}

pub trait FromUnorderedFloat<T>: Vector<Scalar = OrderedFloat<T::Scalar>>
where
    T: Vector,
    T::Scalar: Float + Unit,
{
    fn from_unordered_float(vector: T) -> Self;
}

pub trait IntoUnorderedFloat<T>
    : Sized + Vector<Scalar = OrderedFloat<T::Scalar>>
where
    T: Vector,
    T::Scalar: Float + Unit,
{
    fn into_unordered_float(self) -> T;
}

impl<T, U> IntoUnorderedFloat<U> for T
where
    T: Vector<Scalar = OrderedFloat<U::Scalar>>,
    U: FromOrderedFloat<T> + Vector,
    U::Scalar: Float + Unit,
{
    fn into_unordered_float(self) -> U {
        U::from_ordered_float(self)
    }
}

impl<T> FromUnorderedFloat<(T, T)> for (OrderedFloat<T>, OrderedFloat<T>)
where
    T: Float + Unit,
{
    fn from_unordered_float(vector: (T, T)) -> Self {
        (OrderedFloat::from(vector.0), OrderedFloat::from(vector.1))
    }
}

impl<T> FromUnorderedFloat<(T, T, T)> for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Float + Unit,
{
    fn from_unordered_float(vector: (T, T, T)) -> Self {
        (
            OrderedFloat::from(vector.0),
            OrderedFloat::from(vector.1),
            OrderedFloat::from(vector.2),
        )
    }
}

pub trait FromOrderedFloat<T>: Vector
where
    Self::Scalar: Float + Unit,
    T: Vector<Scalar = OrderedFloat<Self::Scalar>>,
{
    fn from_ordered_float(vector: T) -> Self;
}

pub trait IntoOrderedFloat<T>: Sized + Vector
where
    Self::Scalar: Float + Unit,
    T: Vector<Scalar = OrderedFloat<Self::Scalar>>,
{
    fn into_ordered_float(self) -> T;
}

impl<T, U> IntoOrderedFloat<U> for T
where
    T: Vector,
    T::Scalar: Float + Unit,
    U: FromUnorderedFloat<T> + Vector<Scalar = OrderedFloat<T::Scalar>>,
{
    fn into_ordered_float(self) -> U {
        U::from_unordered_float(self)
    }
}

impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>)> for (T, T)
where
    T: Float + Unit,
{
    fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>)) -> Self {
        ((vector.0).0, (vector.1).0)
    }
}

impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)> for (T, T, T)
where
    T: Float + Unit,
{
    fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)) -> Self {
        ((vector.0).0, (vector.1).0, (vector.2).0)
    }
}

pub trait Interpolate<T = Self>: Sized {
    type Output;

    fn lerp(self, other: T, f: f64) -> Self::Output;

    fn midpoint(self, other: T) -> Self::Output {
        self.lerp(other, 0.5)
    }
}

impl<T> Interpolate for (T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(self, other: Self, f: f64) -> Self::Output {
        (lerp(self.0, other.0, f), lerp(self.1, other.1, f))
    }
}

impl<T> Interpolate for (T, T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(self, other: Self, f: f64) -> Self::Output {
        (
            lerp(self.0, other.0, f),
            lerp(self.1, other.1, f),
            lerp(self.2, other.2, f),
        )
    }
}

fn lerp<T>(a: T, b: T, f: f64) -> T
where
    T: Num + NumCast,
{
    let f = num::clamp(f, 0.0, 1.0);
    let af = <f64 as NumCast>::from(a).unwrap() * (1.0 - f);
    let bf = <f64 as NumCast>::from(b).unwrap() * f;
    <T as NumCast>::from(af + bf).unwrap()
}

#[cfg(feature = "geometry-nalgebra")]
mod feature {
    use nalgebra::{Point2, Point3, Scalar, Vector2, Vector3};

    use super::*;

    impl<T> Vector for Point2<T>
    where
        T: Scalar,
    {
        type Scalar = T;
    }

    impl<T> Vector for Point3<T>
    where
        T: Scalar,
    {
        type Scalar = T;
    }

    impl<T> Vector for Vector2<T>
    where
        T: Scalar,
    {
        type Scalar = T;
    }

    impl<T> Vector for Vector3<T>
    where
        T: Scalar,
    {
        type Scalar = T;
    }

    impl<T> FromUnorderedFloat<Point2<T>> for (OrderedFloat<T>, OrderedFloat<T>)
    where
        T: Float + Scalar + Unit,
    {
        fn from_unordered_float(point: Point2<T>) -> Self {
            (OrderedFloat::from(point.x), OrderedFloat::from(point.y))
        }
    }

    impl<T> FromUnorderedFloat<Point3<T>> for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
    where
        T: Float + Scalar + Unit,
    {
        fn from_unordered_float(point: Point3<T>) -> Self {
            (
                OrderedFloat::from(point.x),
                OrderedFloat::from(point.y),
                OrderedFloat::from(point.z),
            )
        }
    }

    impl<T> FromUnorderedFloat<Vector2<T>> for (OrderedFloat<T>, OrderedFloat<T>)
    where
        T: Float + Scalar + Unit,
    {
        fn from_unordered_float(vector: Vector2<T>) -> Self {
            (OrderedFloat::from(vector.x), OrderedFloat::from(vector.y))
        }
    }

    impl<T> FromUnorderedFloat<Vector3<T>> for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
    where
        T: Float + Scalar + Unit,
    {
        fn from_unordered_float(vector: Vector3<T>) -> Self {
            (
                OrderedFloat::from(vector.x),
                OrderedFloat::from(vector.y),
                OrderedFloat::from(vector.z),
            )
        }
    }

    impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>)> for Point2<T>
    where
        T: Float + Scalar + Unit,
    {
        fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>)) -> Self {
            Point2::new((vector.0).0, (vector.1).0)
        }
    }

    impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)> for Point3<T>
    where
        T: Float + Scalar + Unit,
    {
        fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)) -> Self {
            Point3::new((vector.0).0, (vector.1).0, (vector.2).0)
        }
    }

    impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>)> for Vector2<T>
    where
        T: Float + Scalar + Unit,
    {
        fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>)) -> Self {
            Vector2::new((vector.0).0, (vector.1).0)
        }
    }

    impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)> for Vector3<T>
    where
        T: Float + Scalar + Unit,
    {
        fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)) -> Self {
            Vector3::new((vector.0).0, (vector.1).0, (vector.2).0)
        }
    }

    impl<T> Interpolate for Point2<T>
    where
        T: NumCast + Scalar + Unit,
    {
        type Output = Self;

        fn lerp(self, other: Self, f: f64) -> Self::Output {
            Point2::new(lerp(self.x, other.x, f), lerp(self.y, other.y, f))
        }
    }

    impl<T> Interpolate for Point3<T>
    where
        T: NumCast + Scalar + Unit,
    {
        type Output = Self;

        fn lerp(self, other: Self, f: f64) -> Self::Output {
            Point3::new(
                lerp(self.x, other.x, f),
                lerp(self.y, other.y, f),
                lerp(self.z, other.z, f),
            )
        }
    }

    impl<T> Interpolate for Vector2<T>
    where
        T: NumCast + Scalar + Unit,
    {
        type Output = Self;

        fn lerp(self, other: Self, f: f64) -> Self::Output {
            Vector2::new(lerp(self.x, other.x, f), lerp(self.y, other.y, f))
        }
    }

    impl<T> Interpolate for Vector3<T>
    where
        T: NumCast + Scalar + Unit,
    {
        type Output = Self;

        fn lerp(self, other: Self, f: f64) -> Self::Output {
            Vector3::new(
                lerp(self.x, other.x, f),
                lerp(self.y, other.y, f),
                lerp(self.z, other.z, f),
            )
        }
    }
}

#[cfg(not(feature = "geometry-nalgebra"))]
mod feature {}

pub use self::feature::*;
<commit_msg>Provide `OrderedFloatConjugate` for canonical mapping between ordered and unordered types.<commit_after>use num::{self, Float, Num, NumCast};
use ordered_float::OrderedFloat;

pub trait Unit: Copy + Num {
    fn unit_radius() -> (Self, Self);
    fn unit_width() -> (Self, Self);
}

macro_rules! unit {
    (integer => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one() + Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::{One, Zero};
                (Self::zero(), Self::one())
            }
        }
    )*};
    (real => $($t:ty),*) => {$(
        impl Unit for $t {
            fn unit_radius() -> (Self, Self) {
                use num::One;
                (-Self::one(), Self::one())
            }

            fn unit_width() -> (Self, Self) {
                use num::One;
                let half = Self::one() / (Self::one() + Self::one());
                (-half, half)
            }
        }
    )*};
}
unit!(integer => i8, i16, i32, i64, u8, u16, u32, u64);
unit!(real => f32, f64);

pub trait Vector {
    type Scalar;
}

impl<T> Vector for (T, T) {
    type Scalar = T;
}

impl<T> Vector for (T, T, T) {
    type Scalar = T;
}

pub trait FromUnorderedFloat<T>: Vector<Scalar = OrderedFloat<T::Scalar>>
where
    T: Vector,
    T::Scalar: Float + Unit,
{
    fn from_unordered_float(vector: T) -> Self;
}

pub trait IntoUnorderedFloat<T>
    : Sized + Vector<Scalar = OrderedFloat<T::Scalar>>
where
    T: Vector,
    T::Scalar: Float + Unit,
{
    fn into_unordered_float(self) -> T;
}

impl<T, U> IntoUnorderedFloat<U> for T
where
    T: Vector<Scalar = OrderedFloat<U::Scalar>>,
    U: FromOrderedFloat<T> + Vector,
    U::Scalar: Float + Unit,
{
    fn into_unordered_float(self) -> U {
        U::from_ordered_float(self)
    }
}

impl<T> FromUnorderedFloat<(T, T)> for (OrderedFloat<T>, OrderedFloat<T>)
where
    T: Float + Unit,
{
    fn from_unordered_float(vector: (T, T)) -> Self {
        (OrderedFloat::from(vector.0), OrderedFloat::from(vector.1))
    }
}

impl<T> FromUnorderedFloat<(T, T, T)> for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
where
    T: Float + Unit,
{
    fn from_unordered_float(vector: (T, T, T)) -> Self {
        (
            OrderedFloat::from(vector.0),
            OrderedFloat::from(vector.1),
            OrderedFloat::from(vector.2),
        )
    }
}

pub trait FromOrderedFloat<T>: Vector
where
    Self::Scalar: Float + Unit,
    T: Vector<Scalar = OrderedFloat<Self::Scalar>>,
{
    fn from_ordered_float(vector: T) -> Self;
}

pub trait IntoOrderedFloat<T>: Sized + Vector
where
    Self::Scalar: Float + Unit,
    T: Vector<Scalar = OrderedFloat<Self::Scalar>>,
{
    fn into_ordered_float(self) -> T;
}

impl<T, U> IntoOrderedFloat<U> for T
where
    T: Vector,
    T::Scalar: Float + Unit,
    U: FromUnorderedFloat<T> + Vector<Scalar = OrderedFloat<T::Scalar>>,
{
    fn into_ordered_float(self) -> U {
        U::from_unordered_float(self)
    }
}

impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>)> for (T, T)
where
    T: Float + Unit,
{
    fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>)) -> Self {
        ((vector.0).0, (vector.1).0)
    }
}

impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)> for (T, T, T)
where
    T: Float + Unit,
{
    fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)) -> Self {
        ((vector.0).0, (vector.1).0, (vector.2).0)
    }
}

pub trait OrderedFloatConjugate: Sized + Vector
where
    Self::Scalar: Float + Unit,
{
    type Ordered: FromUnorderedFloat<Self> + IntoUnorderedFloat<Self>;
}

impl<T> OrderedFloatConjugate for (T, T)
where
    T: Float + Unit,
{
    type Ordered = (OrderedFloat<T>, OrderedFloat<T>);
}

impl<T> OrderedFloatConjugate for (T, T, T)
where
    T: Float + Unit,
{
    type Ordered = (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>);
}

pub trait Interpolate<T = Self>: Sized {
    type Output;

    fn lerp(self, other: T, f: f64) -> Self::Output;

    fn midpoint(self, other: T) -> Self::Output {
        self.lerp(other, 0.5)
    }
}

impl<T> Interpolate for (T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(self, other: Self, f: f64) -> Self::Output {
        (lerp(self.0, other.0, f), lerp(self.1, other.1, f))
    }
}

impl<T> Interpolate for (T, T, T)
where
    T: Copy + Num + NumCast,
{
    type Output = Self;

    fn lerp(self, other: Self, f: f64) -> Self::Output {
        (
            lerp(self.0, other.0, f),
            lerp(self.1, other.1, f),
            lerp(self.2, other.2, f),
        )
    }
}

fn lerp<T>(a: T, b: T, f: f64) -> T
where
    T: Num + NumCast,
{
    let f = num::clamp(f, 0.0, 1.0);
    let af = <f64 as NumCast>::from(a).unwrap() * (1.0 - f);
    let bf = <f64 as NumCast>::from(b).unwrap() * f;
    <T as NumCast>::from(af + bf).unwrap()
}

#[cfg(feature = "geometry-nalgebra")]
mod feature {
    use nalgebra::{Point2, Point3, Scalar, Vector2, Vector3};

    use super::*;

    impl<T> Vector for Point2<T>
    where
        T: Scalar,
    {
        type Scalar = T;
    }

    impl<T> Vector for Point3<T>
    where
        T: Scalar,
    {
        type Scalar = T;
    }

    impl<T> Vector for Vector2<T>
    where
        T: Scalar,
    {
        type Scalar = T;
    }

    impl<T> Vector for Vector3<T>
    where
        T: Scalar,
    {
        type Scalar = T;
    }

    impl<T> FromUnorderedFloat<Point2<T>> for (OrderedFloat<T>, OrderedFloat<T>)
    where
        T: Float + Scalar + Unit,
    {
        fn from_unordered_float(point: Point2<T>) -> Self {
            (OrderedFloat::from(point.x), OrderedFloat::from(point.y))
        }
    }

    impl<T> FromUnorderedFloat<Point3<T>> for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
    where
        T: Float + Scalar + Unit,
    {
        fn from_unordered_float(point: Point3<T>) -> Self {
            (
                OrderedFloat::from(point.x),
                OrderedFloat::from(point.y),
                OrderedFloat::from(point.z),
            )
        }
    }

    impl<T> FromUnorderedFloat<Vector2<T>> for (OrderedFloat<T>, OrderedFloat<T>)
    where
        T: Float + Scalar + Unit,
    {
        fn from_unordered_float(vector: Vector2<T>) -> Self {
            (OrderedFloat::from(vector.x), OrderedFloat::from(vector.y))
        }
    }

    impl<T> FromUnorderedFloat<Vector3<T>> for (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)
    where
        T: Float + Scalar + Unit,
    {
        fn from_unordered_float(vector: Vector3<T>) -> Self {
            (
                OrderedFloat::from(vector.x),
                OrderedFloat::from(vector.y),
                OrderedFloat::from(vector.z),
            )
        }
    }

    impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>)> for Point2<T>
    where
        T: Float + Scalar + Unit,
    {
        fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>)) -> Self {
            Point2::new((vector.0).0, (vector.1).0)
        }
    }

    impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)> for Point3<T>
    where
        T: Float + Scalar + Unit,
    {
        fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)) -> Self {
            Point3::new((vector.0).0, (vector.1).0, (vector.2).0)
        }
    }

    impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>)> for Vector2<T>
    where
        T: Float + Scalar + Unit,
    {
        fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>)) -> Self {
            Vector2::new((vector.0).0, (vector.1).0)
        }
    }

    impl<T> FromOrderedFloat<(OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)> for Vector3<T>
    where
        T: Float + Scalar + Unit,
    {
        fn from_ordered_float(vector: (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>)) -> Self {
            Vector3::new((vector.0).0, (vector.1).0, (vector.2).0)
        }
    }

    impl<T> OrderedFloatConjugate for Point2<T>
    where
        T: Float + Scalar + Unit,
    {
        type Ordered = (OrderedFloat<T>, OrderedFloat<T>);
    }

    impl<T> OrderedFloatConjugate for Point3<T>
    where
        T: Float + Scalar + Unit,
    {
        type Ordered = (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>);
    }

    impl<T> OrderedFloatConjugate for Vector2<T>
    where
        T: Float + Scalar + Unit,
    {
        type Ordered = (OrderedFloat<T>, OrderedFloat<T>);
    }

    impl<T> OrderedFloatConjugate for Vector3<T>
    where
        T: Float + Scalar + Unit,
    {
        type Ordered = (OrderedFloat<T>, OrderedFloat<T>, OrderedFloat<T>);
    }

    impl<T> Interpolate for Point2<T>
    where
        T: NumCast + Scalar + Unit,
    {
        type Output = Self;

        fn lerp(self, other: Self, f: f64) -> Self::Output {
            Point2::new(lerp(self.x, other.x, f), lerp(self.y, other.y, f))
        }
    }

    impl<T> Interpolate for Point3<T>
    where
        T: NumCast + Scalar + Unit,
    {
        type Output = Self;

        fn lerp(self, other: Self, f: f64) -> Self::Output {
            Point3::new(
                lerp(self.x, other.x, f),
                lerp(self.y, other.y, f),
                lerp(self.z, other.z, f),
            )
        }
    }

    impl<T> Interpolate for Vector2<T>
    where
        T: NumCast + Scalar + Unit,
    {
        type Output = Self;

        fn lerp(self, other: Self, f: f64) -> Self::Output {
            Vector2::new(lerp(self.x, other.x, f), lerp(self.y, other.y, f))
        }
    }

    impl<T> Interpolate for Vector3<T>
    where
        T: NumCast + Scalar + Unit,
    {
        type Output = Self;

        fn lerp(self, other: Self, f: f64) -> Self::Output {
            Vector3::new(
                lerp(self.x, other.x, f),
                lerp(self.y, other.y, f),
                lerp(self.z, other.z, f),
            )
        }
    }
}

#[cfg(not(feature = "geometry-nalgebra"))]
mod feature {}

pub use self::feature::*;
<|endoftext|>


<commit_before>use num_traits::ToPrimitive;
use num_bigint::BigInt;
use std::str::FromStr;
use std::cmp::max;

mod precision;
use self::precision::*;

pub mod asset_symbol;
pub use self::asset_symbol::*;

pub mod cmp;
pub use self::cmp::*;

pub mod error;
pub use self::error::*;

#[derive(Debug, Clone)]
pub struct Asset {
    pub amount: i64,
    pub decimals: u8,
    pub symbol: AssetSymbol
}

impl Asset {

    pub fn add(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_add(b)?, decimals, symbol: self.symbol })
    }

    pub fn sub(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_sub(b)?, decimals, symbol: self.symbol })
    }

    pub fn mul(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = self.decimals + other.decimals;

        let mul = i128::from(self.amount).checked_mul(i128::from(other.amount))?;
        let final_mul = set_decimals_i128(mul, decimals, precision)?;
        if final_mul > i128::from(::std::i64::MAX) { return None }
        Some(Asset {
            amount: final_mul as i64,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn div(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol || other.amount == 0 { return None }
        let decimals = max(max(self.decimals, other.decimals), precision);
        let a = set_decimals_i64(self.amount, self.decimals, decimals * 2)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset {
            amount: set_decimals_i64(a.checked_div(b)?, decimals, precision)?,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn pow(&self, num: u16, precision: u8) -> Option<Self> {
        if num == 0 {
            return Some(Asset {
                amount: set_decimals_i64(1, 0, precision)?,
                decimals: precision,
                symbol: self.symbol
            })
        }
        let big_zero = BigInt::from(0);
        let big_one = BigInt::from(1);

        let decimals = u16::from(self.decimals).checked_mul(num)?;
        let mut res = BigInt::from(1);
        {
            let mut base = BigInt::from(self.amount);
            let mut exp = BigInt::from(num);
            loop {
                if &exp & &big_one == big_one { res = &res * &base; }
                exp >>= 1;
                if exp == big_zero { break };
                base = &base * &base;
            }
        }

        res = set_decimals_big(&res, decimals, u16::from(precision));
        Some(Asset {
            amount: res.to_i64()?,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn set_decimals(&self, decimals: u8) -> Option<Self> {
        let amount = set_decimals_i64(self.amount, self.decimals, decimals)?;
        Some(Asset {
            amount,
            decimals,
            symbol: self.symbol
        })
    }

    pub fn to_str(&self) -> Box<str> {
        let mut s = self.amount.to_string();
        if self.decimals > 0 {
            let len = s.len();
            if len < self.decimals as usize {
                let start = if self.amount < 0 { 1 } else { 0 };
                let diff = self.decimals as usize - len + start;
                s.insert_str(start, "0.");
                s.insert_str(start + 2, &"0".repeat(diff));
            } else if len == self.decimals as usize {
                s.insert_str(0, "0.");
            } else {
                s.insert(len - (self.decimals as usize), '.');
            }
        }
        s.push(' ');
        s.push_str(self.symbol.as_str());
        s.into_boxed_str()
    }
}

impl FromStr for Asset {
    type Err = AssetError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() > 32 {
            return Err(AssetError { kind: AssetErrorKind::StrTooLarge })
        }
        let mut split = s.trim().splitn(2, ' ');

        let amount: i64;
        let mut decimals: u8 = 0;
        match split.next() {
            Some(x) => {
                if let Some(pos) = x.find('.') {
                    let len = x.len() - 1;
                    if pos > 0 { decimals = (len - pos) as u8; }
                    else { decimals = len as u8; }

                    if decimals > 8 {
                        return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }

                    amount = match x.replace('.', "").parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                } else {
                    amount = match x.parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        let symbol = match split.next() {
            Some(x) => {
                match AssetSymbol::parse_str(x) {
                    Some(x) => x,
                    None => return Err(AssetError { kind: AssetErrorKind::InvalidAssetType })
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        Ok(Asset { amount, decimals, symbol })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_valid_input() {
        let c = |asset: Asset, amount: &str, decimals: u8, symbol: AssetSymbol| {
            assert_eq!(asset.amount.to_string(), amount);
            assert_eq!(asset.decimals, decimals);
            assert_eq!(asset.symbol, symbol);
        };

        c(get_asset("1 GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset("1. GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset(".1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("-.1 GOLD"), "-1", 1, AssetSymbol::GOLD);
        c(get_asset("0.1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("1.0 SILVER"), "10", 1, AssetSymbol::SILVER);
        c(get_asset("0 SILVER"), "0", 0, AssetSymbol::SILVER);
        c(get_asset("-0.0 SILVER"), "0", 1, AssetSymbol::SILVER);
        c(get_asset("-1.0 SILVER"), "-10", 1, AssetSymbol::SILVER);
    }

    #[test]
    fn test_asset_to_str() {
        let c = |asset: Asset, s: &str| {
            assert_eq!(&*asset.to_str(), s);
        };
        c(get_asset("1.00001 GOLD"), "1.00001 GOLD");
        c(get_asset("0.00001 GOLD"), "0.00001 GOLD");
        c(get_asset("-0.00001 GOLD"), "-0.00001 GOLD");
        c(get_asset(".00001 GOLD"), "0.00001 GOLD");
        c(get_asset(".1 GOLD"), "0.1 GOLD");
        c(get_asset("1.0 GOLD"), "1.0 GOLD");
    }

    #[test]
    fn test_fail_parsing_invalid_input() {
        let c = |asset: &str, err: AssetErrorKind| {
            let e = Asset::from_str(asset).err().unwrap();
            assert_eq!(e.kind, err);
        };

        c("1e10 GOLD", AssetErrorKind::InvalidAmount);
        c("a100 GOLD", AssetErrorKind::InvalidAmount);
        c("100a GOLD", AssetErrorKind::InvalidAmount);

        c("1234567890123456789012345678 GOLD", AssetErrorKind::StrTooLarge);
        c("1", AssetErrorKind::InvalidFormat);

        c("1.0 GOLD a", AssetErrorKind::InvalidAssetType);
        c("1.0 gold", AssetErrorKind::InvalidAssetType);
    }

    #[test]
    fn test_set_precision() {
        let a = get_asset("1.5678 GOLD");
        assert_eq!(a.decimals, 4);
        assert_eq!(a.amount.to_string(), "15678");

        let a = a.set_decimals(2).unwrap();

        assert_eq!(a.decimals, 2);
        assert_eq!(a.amount.to_string(), "156");

        let a = a.set_decimals(0).unwrap();
        assert_eq!(a.decimals, 0);
        assert_eq!(a.amount.to_string(), "1");

        let a = a.set_decimals(4).unwrap();
        assert_eq!(a.decimals, 4);
        assert_eq!(a.amount.to_string(), "10000");
    }

    #[test]
    fn test_perform_arithmetic() {
        let c = |asset: &Asset, amount: &str| {
            assert_eq!(&*asset.to_str(), amount);
        };

        let a = get_asset("123.456 GOLD");
        c(&a.add(&get_asset("2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.add(&get_asset("-2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.add(&get_asset(".00000001 GOLD")).unwrap(), "123.45600001 GOLD");
        c(&a.sub(&get_asset("2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.sub(&get_asset("-2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.mul(&get_asset("100000.11111111 GOLD"), 8).unwrap(), "12345613.71733319 GOLD");
        c(&a.mul(&get_asset("-100000.11111111 GOLD"), 8).unwrap(), "-12345613.71733319 GOLD");
        c(&a.div(&get_asset("23 GOLD"), 3).unwrap(), "5.367 GOLD");
        c(&a.div(&get_asset("-23 GOLD"), 8).unwrap(), "-5.36765217 GOLD");
        c(&a.pow(2, 8).unwrap(), "15241.38393600 GOLD");
        c(&a.pow(3, 8).unwrap(), "1881640.29520281 GOLD");
        c(&a, "123.456 GOLD");

        c(&get_asset("1.0002 GOLD").pow(1000, 8).unwrap(), "1.22137833 GOLD");
        c(&get_asset("10 GOLD").div(&get_asset("2 GOLD"), 0).unwrap(), "5 GOLD");
        c(&get_asset("5 GOLD").div(&get_asset("10 GOLD"), 1).unwrap(), "0.5 GOLD");

        assert!(&a.div(&get_asset("0 GOLD"), 1).is_none());
    }

    #[test]
    fn test_invalid_arithmetic() {
        let a = &get_asset("10 GOLD");
        let b = &get_asset("10 SILVER");

        assert!(a.add(b).is_none());
        assert!(a.sub(b).is_none());
        assert!(a.div(b, 8).is_none());
        assert!(a.mul(b, 8).is_none());
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}

<commit_msg>Declare constants for max asset string and precision<commit_after>use num_traits::ToPrimitive;
use num_bigint::BigInt;
use std::str::FromStr;
use std::cmp::max;

mod precision;
use self::precision::*;

pub mod asset_symbol;
pub use self::asset_symbol::*;

pub mod cmp;
pub use self::cmp::*;

pub mod error;
pub use self::error::*;

pub const MAX_STR_LEN: usize = 32;
pub const MAX_PRECISION: u8 = 8;

#[derive(Debug, Clone)]
pub struct Asset {
    pub amount: i64,
    pub decimals: u8,
    pub symbol: AssetSymbol
}

impl Asset {

    pub fn add(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_add(b)?, decimals, symbol: self.symbol })
    }

    pub fn sub(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_sub(b)?, decimals, symbol: self.symbol })
    }

    pub fn mul(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = self.decimals + other.decimals;

        let mul = i128::from(self.amount).checked_mul(i128::from(other.amount))?;
        let final_mul = set_decimals_i128(mul, decimals, precision)?;
        if final_mul > i128::from(::std::i64::MAX) { return None }
        Some(Asset {
            amount: final_mul as i64,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn div(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol || other.amount == 0 { return None }
        let decimals = max(max(self.decimals, other.decimals), precision);
        let a = set_decimals_i64(self.amount, self.decimals, decimals * 2)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset {
            amount: set_decimals_i64(a.checked_div(b)?, decimals, precision)?,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn pow(&self, num: u16, precision: u8) -> Option<Self> {
        if num == 0 {
            return Some(Asset {
                amount: set_decimals_i64(1, 0, precision)?,
                decimals: precision,
                symbol: self.symbol
            })
        }
        let big_zero = BigInt::from(0);
        let big_one = BigInt::from(1);

        let decimals = u16::from(self.decimals).checked_mul(num)?;
        let mut res = BigInt::from(1);
        {
            let mut base = BigInt::from(self.amount);
            let mut exp = BigInt::from(num);
            loop {
                if &exp & &big_one == big_one { res = &res * &base; }
                exp >>= 1;
                if exp == big_zero { break };
                base = &base * &base;
            }
        }

        res = set_decimals_big(&res, decimals, u16::from(precision));
        Some(Asset {
            amount: res.to_i64()?,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn set_decimals(&self, decimals: u8) -> Option<Self> {
        let amount = set_decimals_i64(self.amount, self.decimals, decimals)?;
        Some(Asset {
            amount,
            decimals,
            symbol: self.symbol
        })
    }

    pub fn to_str(&self) -> Box<str> {
        let mut s = self.amount.to_string();
        if self.decimals > 0 {
            let len = s.len();
            if len < self.decimals as usize {
                let start = if self.amount < 0 { 1 } else { 0 };
                let diff = self.decimals as usize - len + start;
                s.insert_str(start, "0.");
                s.insert_str(start + 2, &"0".repeat(diff));
            } else if len == self.decimals as usize {
                s.insert_str(0, "0.");
            } else {
                s.insert(len - (self.decimals as usize), '.');
            }
        }
        s.push(' ');
        s.push_str(self.symbol.as_str());
        s.into_boxed_str()
    }
}

impl FromStr for Asset {
    type Err = AssetError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() > MAX_STR_LEN {
            return Err(AssetError { kind: AssetErrorKind::StrTooLarge })
        }
        let mut split = s.trim().splitn(2, ' ');

        let amount: i64;
        let mut decimals: u8 = 0;
        match split.next() {
            Some(x) => {
                if let Some(pos) = x.find('.') {
                    let len = x.len() - 1;
                    if pos > 0 { decimals = (len - pos) as u8; }
                    else { decimals = len as u8; }

                    if decimals > MAX_PRECISION {
                        return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }

                    amount = match x.replace('.', "").parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                } else {
                    amount = match x.parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        let symbol = match split.next() {
            Some(x) => {
                match AssetSymbol::parse_str(x) {
                    Some(x) => x,
                    None => return Err(AssetError { kind: AssetErrorKind::InvalidAssetType })
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        Ok(Asset { amount, decimals, symbol })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_valid_input() {
        let c = |asset: Asset, amount: &str, decimals: u8, symbol: AssetSymbol| {
            assert_eq!(asset.amount.to_string(), amount);
            assert_eq!(asset.decimals, decimals);
            assert_eq!(asset.symbol, symbol);
        };

        c(get_asset("1 GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset("1. GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset(".1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("-.1 GOLD"), "-1", 1, AssetSymbol::GOLD);
        c(get_asset("0.1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("1.0 SILVER"), "10", 1, AssetSymbol::SILVER);
        c(get_asset("0 SILVER"), "0", 0, AssetSymbol::SILVER);
        c(get_asset("-0.0 SILVER"), "0", 1, AssetSymbol::SILVER);
        c(get_asset("-1.0 SILVER"), "-10", 1, AssetSymbol::SILVER);
    }

    #[test]
    fn test_asset_to_str() {
        let c = |asset: Asset, s: &str| {
            assert_eq!(&*asset.to_str(), s);
        };
        c(get_asset("1.00001 GOLD"), "1.00001 GOLD");
        c(get_asset("0.00001 GOLD"), "0.00001 GOLD");
        c(get_asset("-0.00001 GOLD"), "-0.00001 GOLD");
        c(get_asset(".00001 GOLD"), "0.00001 GOLD");
        c(get_asset(".1 GOLD"), "0.1 GOLD");
        c(get_asset("1.0 GOLD"), "1.0 GOLD");
    }

    #[test]
    fn test_fail_parsing_invalid_input() {
        let c = |asset: &str, err: AssetErrorKind| {
            let e = Asset::from_str(asset).err().unwrap();
            assert_eq!(e.kind, err);
        };

        c("1e10 GOLD", AssetErrorKind::InvalidAmount);
        c("a100 GOLD", AssetErrorKind::InvalidAmount);
        c("100a GOLD", AssetErrorKind::InvalidAmount);

        c("1234567890123456789012345678 GOLD", AssetErrorKind::StrTooLarge);
        c("1", AssetErrorKind::InvalidFormat);

        c("1.0 GOLD a", AssetErrorKind::InvalidAssetType);
        c("1.0 gold", AssetErrorKind::InvalidAssetType);
    }

    #[test]
    fn test_set_precision() {
        let a = get_asset("1.5678 GOLD");
        assert_eq!(a.decimals, 4);
        assert_eq!(a.amount.to_string(), "15678");

        let a = a.set_decimals(2).unwrap();

        assert_eq!(a.decimals, 2);
        assert_eq!(a.amount.to_string(), "156");

        let a = a.set_decimals(0).unwrap();
        assert_eq!(a.decimals, 0);
        assert_eq!(a.amount.to_string(), "1");

        let a = a.set_decimals(4).unwrap();
        assert_eq!(a.decimals, 4);
        assert_eq!(a.amount.to_string(), "10000");
    }

    #[test]
    fn test_perform_arithmetic() {
        let c = |asset: &Asset, amount: &str| {
            assert_eq!(&*asset.to_str(), amount);
        };

        let a = get_asset("123.456 GOLD");
        c(&a.add(&get_asset("2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.add(&get_asset("-2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.add(&get_asset(".00000001 GOLD")).unwrap(), "123.45600001 GOLD");
        c(&a.sub(&get_asset("2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.sub(&get_asset("-2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.mul(&get_asset("100000.11111111 GOLD"), 8).unwrap(), "12345613.71733319 GOLD");
        c(&a.mul(&get_asset("-100000.11111111 GOLD"), 8).unwrap(), "-12345613.71733319 GOLD");
        c(&a.div(&get_asset("23 GOLD"), 3).unwrap(), "5.367 GOLD");
        c(&a.div(&get_asset("-23 GOLD"), 8).unwrap(), "-5.36765217 GOLD");
        c(&a.pow(2, 8).unwrap(), "15241.38393600 GOLD");
        c(&a.pow(3, 8).unwrap(), "1881640.29520281 GOLD");
        c(&a, "123.456 GOLD");

        c(&get_asset("1.0002 GOLD").pow(1000, 8).unwrap(), "1.22137833 GOLD");
        c(&get_asset("10 GOLD").div(&get_asset("2 GOLD"), 0).unwrap(), "5 GOLD");
        c(&get_asset("5 GOLD").div(&get_asset("10 GOLD"), 1).unwrap(), "0.5 GOLD");

        assert!(&a.div(&get_asset("0 GOLD"), 1).is_none());
    }

    #[test]
    fn test_invalid_arithmetic() {
        let a = &get_asset("10 GOLD");
        let b = &get_asset("10 SILVER");

        assert!(a.add(b).is_none());
        assert!(a.sub(b).is_none());
        assert!(a.div(b, 8).is_none());
        assert!(a.mul(b, 8).is_none());
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}

<|endoftext|>


<commit_before>use grid::Grid;
use cell::Cell;

const NEIGHBORS_COUNT: usize = 8;

type Neighbors = [Option<usize>; NEIGHBORS_COUNT];

pub struct SquareGrid<C: Cell + Copy> {
    cells: Vec<C>,
    old_cells: Vec<C>,
    neighbors: Vec<Neighbors>,
    rows: i32,
    cols: i32,
}

impl<C: Cell + Copy> SquareGrid<C> {

    pub fn new(rows: i32, cols: i32, initial: C) -> Self {

        let len = (rows * cols) as usize;
        let cells = vec![initial; len];
        let old_cells = Vec::new();
        let neighbors = Vec::with_capacity(len);

        let mut grid = SquareGrid {
            cells: cells,
            old_cells: old_cells,
            neighbors: neighbors,
            rows: rows,
            cols: cols,
        };
        grid.init_neighbors();

        grid
    }

    #[inline]
    fn offset(&self, i: i32, j: i32) -> usize {
        (i * self.cols + j) as usize
    }

    #[inline]
    fn from_offset(&self, offset: i32) -> (i32, i32) {
        let col = offset % self.cols;
        let row = (offset - col) / self.rows;
        (row, col)
    }

    /// 0 | 1 | 2
    /// 3 | x | 4
    /// 5 | 6 | 7
    fn init_neighbors(&mut self) {

        let len = self.cells.len() as i32;

        for offset in 0 .. len {
            let (i, j) = self.from_offset(offset);

            let neighbors_coords = [
                (i - 1, j - 1), (i - 1, j), (i - 1, j + 1),
                (i, j - 1),     /* x */     (i, j + 1),
                (i + 1, j - 1), (i + 1, j), (i + 1, j + 1)
            ];

            let mut neighbors = [None; NEIGHBORS_COUNT];

            for (serial, coord) in neighbors_coords.iter().enumerate() {
                let (x, y) = *coord;

                if x >= 0 && x < self.cols &&
                   y >= 0 && y < self.rows {
                    neighbors[serial] = Some(self.offset(x, y));
                }
            }

            self.neighbors.push(neighbors);
        }
    }

    fn neighbors_iter<'a>(&self,
                          cells: &'a Vec<C>,
                          neighbors: Neighbors)
        -> SquareGridIterator<'a, C> {

        SquareGridIterator {
            cells: cells,
            neighbors: neighbors,
            index: 0,
        }
    }
}

impl<C: Cell + Copy> Grid for SquareGrid<C> {

    fn step(&mut self) {
        self.old_cells = self.cells.clone();

        for (cell_no, cell) in self.old_cells.iter().enumerate() {
            let neighbors = self.neighbors[cell_no];
            let neighbors_iter = self.neighbors_iter(&self.old_cells, neighbors);
            self.cells[cell_no] = cell.step(neighbors_iter);
        }
    }
}

struct SquareGridIterator<'a, C: Cell + 'a> {
    cells: &'a Vec<C>,
    neighbors: Neighbors,
    index: usize,
}

impl<'a, C: Cell> Iterator for SquareGridIterator<'a, C> {
    type Item = Option<&'a C>;

    fn next(&mut self) -> Option<Self::Item> {
        let next = match self.index {
            i @ _  if i < NEIGHBORS_COUNT => {
                let maybe_index = self.neighbors[i];
                match maybe_index {
                    Some(index) => Some(Some(&self.cells[index])),
                    None => Some(None)
                }
            },
            _ => None
        };
        self.index += 1;

        next
    }
}

mod test {

    use cell::Cell;
    use grid::Grid;

    #[derive(Copy, Clone, PartialEq, Debug)]
    struct MooreTestCell;

    impl Cell for MooreTestCell {

        fn step<'a, I>(&self, neighbors: I) -> Self
            where I: Iterator<Item=Option<&'a Self>> {

            // Initial value should be copied
            // There should be 3 neighbors and 5 None values
            // Overall count of neighbors should be equal to NEIGHBORS_COUNT
            let mut total = 0;
            let mut none_cnt = 0;
            let mut neighbors_cnt = 0;
            for neighbor in neighbors {
                match neighbor {
                    None => {
                        none_cnt += 1;
                    },
                    Some(n) => {
                        assert_eq!(self, n);
                        neighbors_cnt += 1;
                    }
                };
                total += 1;
            }

            assert_eq!(total, super::NEIGHBORS_COUNT);
            assert_eq!(none_cnt, 5);
            assert_eq!(neighbors_cnt, 3);

            self.clone()
        }
    }

    #[test]
    fn test_neighbors() {
        let initial = MooreTestCell;
        let mut grid = super::SquareGrid::new(2, 2, initial);
        grid.step();
    }
}
<commit_msg>Iterator is renamed.<commit_after>use grid::Grid;
use cell::Cell;

const NEIGHBORS_COUNT: usize = 8;

type Neighbors = [Option<usize>; NEIGHBORS_COUNT];

pub struct SquareGrid<C: Cell + Copy> {
    cells: Vec<C>,
    old_cells: Vec<C>,
    neighbors: Vec<Neighbors>,
    rows: i32,
    cols: i32,
}

impl<C: Cell + Copy> SquareGrid<C> {

    pub fn new(rows: i32, cols: i32, initial: C) -> Self {

        let len = (rows * cols) as usize;
        let cells = vec![initial; len];
        let old_cells = Vec::new();
        let neighbors = Vec::with_capacity(len);

        let mut grid = SquareGrid {
            cells: cells,
            old_cells: old_cells,
            neighbors: neighbors,
            rows: rows,
            cols: cols,
        };
        grid.init_neighbors();

        grid
    }

    #[inline]
    fn offset(&self, i: i32, j: i32) -> usize {
        (i * self.cols + j) as usize
    }

    #[inline]
    fn from_offset(&self, offset: i32) -> (i32, i32) {
        let col = offset % self.cols;
        let row = (offset - col) / self.rows;
        (row, col)
    }

    /// 0 | 1 | 2
    /// 3 | x | 4
    /// 5 | 6 | 7
    fn init_neighbors(&mut self) {

        let len = self.cells.len() as i32;

        for offset in 0 .. len {
            let (i, j) = self.from_offset(offset);

            let neighbors_coords = [
                (i - 1, j - 1), (i - 1, j), (i - 1, j + 1),
                (i, j - 1),     /* x */     (i, j + 1),
                (i + 1, j - 1), (i + 1, j), (i + 1, j + 1)
            ];

            let mut neighbors = [None; NEIGHBORS_COUNT];

            for (serial, coord) in neighbors_coords.iter().enumerate() {
                let (x, y) = *coord;

                if x >= 0 && x < self.cols &&
                   y >= 0 && y < self.rows {
                    neighbors[serial] = Some(self.offset(x, y));
                }
            }

            self.neighbors.push(neighbors);
        }
    }

    fn neighbors_iter<'a>(&self,
                          cells: &'a Vec<C>,
                          neighbors: Neighbors)
        -> MooreSquareGridIterator<'a, C> {

        MooreSquareGridIterator {
            cells: cells,
            neighbors: neighbors,
            index: 0,
        }
    }
}

impl<C: Cell + Copy> Grid for SquareGrid<C> {

    fn step(&mut self) {
        self.old_cells = self.cells.clone();

        for (cell_no, cell) in self.old_cells.iter().enumerate() {
            let neighbors = self.neighbors[cell_no];
            let neighbors_iter = self.neighbors_iter(&self.old_cells, neighbors);
            self.cells[cell_no] = cell.step(neighbors_iter);
        }
    }
}

struct MooreSquareGridIterator<'a, C: Cell + 'a> {
    cells: &'a Vec<C>,
    neighbors: Neighbors,
    index: usize,
}

impl<'a, C: Cell> Iterator for MooreSquareGridIterator<'a, C> {
    type Item = Option<&'a C>;

    fn next(&mut self) -> Option<Self::Item> {
        let next = match self.index {
            i @ _  if i < NEIGHBORS_COUNT => {
                let maybe_index = self.neighbors[i];
                match maybe_index {
                    Some(index) => Some(Some(&self.cells[index])),
                    None => Some(None)
                }
            },
            _ => None
        };
        self.index += 1;

        next
    }
}

mod test {

    use cell::Cell;
    use grid::Grid;

    #[derive(Copy, Clone, PartialEq, Debug)]
    struct MooreTestCell;

    impl Cell for MooreTestCell {

        fn step<'a, I>(&self, neighbors: I) -> Self
            where I: Iterator<Item=Option<&'a Self>> {

            // Initial value should be copied
            // There should be 3 neighbors and 5 None values
            // Overall count of neighbors should be equal to NEIGHBORS_COUNT
            let mut total = 0;
            let mut none_cnt = 0;
            let mut neighbors_cnt = 0;
            for neighbor in neighbors {
                match neighbor {
                    None => {
                        none_cnt += 1;
                    },
                    Some(n) => {
                        assert_eq!(self, n);
                        neighbors_cnt += 1;
                    }
                };
                total += 1;
            }

            assert_eq!(total, super::NEIGHBORS_COUNT);
            assert_eq!(none_cnt, 5);
            assert_eq!(neighbors_cnt, 3);

            self.clone()
        }
    }

    #[test]
    fn test_neighbors() {
        let initial = MooreTestCell;
        let mut grid = super::SquareGrid::new(2, 2, initial);
        grid.step();
    }
}
<|endoftext|>


<commit_before>extern crate bindgen;

use std::env;
use std::path::PathBuf;

fn main() {
    // Tell cargo to tell rustc to link the system pam
    // shared library.
    println!("cargo:rustc-link-lib=pam");
    // pam_misc is only supported on Linux afaik
    if cfg!(target_os = "linux") {
        println!("cargo:rustc-link-lib=pam_misc");
    }

    // Tell cargo to invalidate the built crate whenever the wrapper changes
    println!("cargo:rerun-if-changed=wrapper.h");

    // The bindgen::Builder is the main entry point
    // to bindgen, and lets you build up options for
    // the resulting bindings.
    let bindings = bindgen::Builder::default()
        // The input header we would like to generate
        // bindings for.
        .header("wrapper.h")
        // Import libc so our signatures are slightly nicer
        .raw_line("use libc::{uid_t, gid_t, group, passwd, spwd};")
        .ctypes_prefix("libc")
        // Set macro constants to signed int, as all functions that accept
        // these constants use signed int as the parameter type
        .default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
        // Blacklist varargs functions and related types for now
        // TODO: find a nice solution for this
        .blacklist_type("va_list")
        .blacklist_type("__builtin_va_list")
        .blacklist_type("__va_list_tag")
        .blacklist_function("pam_v.*")
        // Blacklist types we use from libc
        .blacklist_type(".*gid_t")
        .blacklist_type(".*uid_t")
        .blacklist_type("group")
        .blacklist_type("passwd")
        .blacklist_type("spwd")
        // Whitelist all PAM constants
        .whitelist_var("PAM_.*")
        // Whitelist all PAM functions..
        .whitelist_function("pam_.*")
        // ..except module related functions (pam_sm_*)
        .blacklist_function("pam_sm_.*")
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}
<commit_msg>Don't implement `Copy` on opaque type `pam_handle`<commit_after>extern crate bindgen;

use std::env;
use std::path::PathBuf;

fn main() {
    // Tell cargo to tell rustc to link the system pam
    // shared library.
    println!("cargo:rustc-link-lib=pam");
    // pam_misc is only supported on Linux afaik
    if cfg!(target_os = "linux") {
        println!("cargo:rustc-link-lib=pam_misc");
    }

    // Tell cargo to invalidate the built crate whenever the wrapper changes
    println!("cargo:rerun-if-changed=wrapper.h");

    // The bindgen::Builder is the main entry point
    // to bindgen, and lets you build up options for
    // the resulting bindings.
    let bindings = bindgen::Builder::default()
        // The input header we would like to generate
        // bindings for.
        .header("wrapper.h")
        // Import libc so our signatures are slightly nicer
        .raw_line("use libc::{uid_t, gid_t, group, passwd, spwd};")
        .ctypes_prefix("libc")
        // Set macro constants to signed int, as all functions that accept
        // these constants use signed int as the parameter type
        .default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
        // pam_handle is opaque, don't implement Copy
        .no_copy("pam_handle")
        // Blacklist varargs functions and related types for now
        // TODO: find a nice solution for this
        .blacklist_type("va_list")
        .blacklist_type("__builtin_va_list")
        .blacklist_type("__va_list_tag")
        .blacklist_function("pam_v.*")
        // Blacklist types we use from libc
        .blacklist_type(".*gid_t")
        .blacklist_type(".*uid_t")
        .blacklist_type("group")
        .blacklist_type("passwd")
        .blacklist_type("spwd")
        // Whitelist all PAM constants
        .whitelist_var("PAM_.*")
        // Whitelist all PAM functions..
        .whitelist_function("pam_.*")
        // ..except module related functions (pam_sm_*)
        .blacklist_function("pam_sm_.*")
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}
<|endoftext|>


<commit_before>// Copyright 2016 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Finds the required `libclang` libraries and links to them.
//!
//! # Environment Variables
//!
//! This build script can make use of several environment variables to help it find the required
//! static or dynamic libraries.
//!
//! * `LLVM_CONFIG_PATH` - provides a path to an `llvm-config` executable
//! * `LIBCLANG_PATH` - provides a path to a directory containing a `libclang` shared library
//! * `LIBCLANG_STATIC_PATH` - provides a path to a directory containing LLVM and Clang static libraries

#![allow(unused_attributes)]

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", warn(clippy))]

extern crate glob;

use std::env;
use std::fs::{self, File};
use std::io::{Read};
use std::path::{Path, PathBuf};
use std::process::{Command};

use glob::{MatchOptions};

/// Returns a path to one of the supplied files if such a file can be found in the supplied directory.
fn contains<D: AsRef<Path>>(directory: D, files: &[String]) -> Option<PathBuf> {
    files.iter().map(|file| directory.as_ref().join(file)).find(|file| file.exists())
}

/// Runs a console command, returning the output if the command was successfully executed.
fn run(command: &str, arguments: &[&str]) -> Option<String> {
    Command::new(command).args(arguments).output().map(|o| {
        String::from_utf8_lossy(&o.stdout).into_owned()
    }).ok()
}

/// Runs `llvm-config`, returning the output if the command was successfully executed.
fn run_llvm_config(arguments: &[&str]) -> Result<String, String> {
    match run(&env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into()), arguments) {
        Some(output) => Ok(output),
        None => {
            let message = format!(
                "couldn't execute `llvm-config {}`, set the LLVM_CONFIG_PATH environment variable \
                to a path to a valid `llvm-config` executable",
                arguments.join(" "),
            );
            Err(message)
        },
    }
}

/// Backup search directory globs for FreeBSD and Linux.
const SEARCH_LINUX: &'static [&'static str] = &[
    "/usr/lib*",
    "/usr/lib*/*",
    "/usr/lib*/*/*",
    "/usr/local/lib*",
    "/usr/local/lib*/*",
    "/usr/local/lib*/*/*",
    "/usr/local/llvm*/lib",
];

/// Backup search directory globs for OS X.
const SEARCH_OSX: &'static [&'static str] = &[
    "/usr/local/opt/llvm*/lib",
    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
    "/Library/Developer/CommandLineTools/usr/lib",
    "/usr/local/opt/llvm*/lib/llvm*/lib",
];

/// Backup search directory globs for Windows.
const SEARCH_WINDOWS: &'static [&'static str] = &[
    "C:\\LLVM\\lib",
    "C:\\Program Files*\\LLVM\\lib",
    "C:\\MSYS*\\MinGW*\\lib",
];

/// Indicates the type of library being searched for.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Library {
    Dynamic,
    Static,
}

impl Library {
    /// Checks whether the supplied file is a valid library for the architecture.
    fn check(&self, file: &PathBuf) -> Result<(), String> {
        if cfg!(any(target_os="freebsd", target_os="linux")) {
            if *self == Library::Static {
                return Ok(());
            }
            let mut file = try!(File::open(file).map_err(|e| e.to_string()));
            let mut elf = [0; 5];
            try!(file.read_exact(&mut elf).map_err(|e| e.to_string()));
            if elf[..4] != [127, 69, 76, 70] {
                return Err("invalid ELF header".into());
            }
            if cfg!(target_pointer_width="32") && elf[4] != 1 {
                return Err("invalid ELF class (64-bit)".into());
            }
            if cfg!(target_pointer_width="64") && elf[4] != 2 {
                return Err("invalid ELF class (32-bit)".into());
            }
            Ok(())
        } else {
            Ok(())
        }
    }
}

/// Searches for a library, returning the directory it can be found in if the search was successful.
fn find(library: Library, files: &[String], env: &str) -> Result<PathBuf, String> {
    let mut skipped = vec![];

    /// Attempts to return the supplied file.
    macro_rules! try_file {
        ($file:expr) => ({
            match library.check(&$file) {
                Ok(_) => return Ok($file),
                Err(message) => skipped.push(format!("({}: {})", $file.display(), message)),
            }
        });
    }

    /// Searches the supplied directory and, on Windows, any relevant sibling directories.
    macro_rules! search_directory {
        ($directory:ident) => {
            if let Some(file) = contains(&$directory, files) {
                try_file!(file);
            }

            // On Windows, `libclang.dll` is usually found in the LLVM `bin` directory while
            // `libclang.lib` is usually found in the LLVM `lib` directory. To keep things
            // consistent with other platforms, only LLVM `lib` directories are included in the
            // backup search directory globs so we need to search the LLVM `bin` directory here.
            if cfg!(target_os="windows") && $directory.ends_with("lib") {
                let sibling = $directory.parent().unwrap().join("bin");
                if let Some(file) = contains(&sibling, files) {
                    try_file!(file);
                }
            }
        }
    }

    // Search the directory provided by the relevant environment variable if it is set.
    if let Ok(directory) = env::var(env).map(|d| Path::new(&d).to_path_buf()) {
        search_directory!(directory);
    }

    // Search the `bin` and `lib` subdirectories in the directory returned by
    // `llvm-config --prefix` if `llvm-config` is available.
    if let Ok(output) = run_llvm_config(&["--prefix"]) {
        let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
        let bin = directory.join("bin");
        if let Some(file) = contains(&bin, files) {
            try_file!(file);
        }
        let lib = directory.join("lib");
        if let Some(file) = contains(&lib, files) {
            try_file!(file);
        }
    }

    // Search the backup directories.
    let search = if cfg!(any(target_os="freebsd", target_os="linux")) {
        SEARCH_LINUX
    } else if cfg!(target_os="macos") {
        SEARCH_OSX
    } else if cfg!(target_os="windows") {
        SEARCH_WINDOWS
    } else {
        &[]
    };
    for pattern in search {
        let mut options = MatchOptions::new();
        options.case_sensitive = false;
        options.require_literal_separator = true;
        if let Ok(paths) = glob::glob_with(pattern, &options) {
            for path in paths.filter_map(Result::ok).filter(|p| p.is_dir()) {
                search_directory!(path);
            }
        }
    }

    let message = format!(
        "couldn't find any of [{}], set the {} environment variable to a path where one of these \
         files can be found (skipped: [{}])",
        files.iter().map(|f| format!("'{}'", f)).collect::<Vec<_>>().join(", "),
        env,
        skipped.join(", "),
    );
    Err(message)
}

/// Searches for a `libclang` shared library, returning the path to such a shared library if the
/// search was successful.
pub fn find_shared_library() -> Result<PathBuf, String> {
    let mut files = vec![format!("{}clang{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX)];
    if cfg!(target_os="linux") {
        // Some Linux distributions don't create a `libclang.so` symlink.
        //
        // FIXME: We should improve our detection and selection of versioned libraries.
        files.push("libclang.so.1".into());
    }
    if cfg!(target_os="windows") {
        // The official LLVM build uses `libclang.dll` on Windows instead of `clang.dll`. However,
        // unofficial builds such as MinGW use `clang.dll`.
        files.push("libclang.dll".into());
    }
    find(Library::Dynamic, &files, "LIBCLANG_PATH")
}

/// Returns the name of an LLVM or Clang library from a path to such a library.
fn get_library_name(path: &Path) -> Option<String> {
    path.file_stem().map(|l| l.to_string_lossy()[3..].into())
}

/// Returns the LLVM libraries required to link to `libclang` statically.
fn get_llvm_libraries() -> Vec<String> {
    run_llvm_config(&["--libs"]).unwrap().split_whitespace().filter_map(|p| {
        // Depending on the version of `llvm-config` in use, listed libraries may be in one of two
        // forms, a full path to the library or simply prefixed with `-l`.
        if p.starts_with("-l") {
            Some(p[2..].into())
        } else {
            get_library_name(Path::new(p))
        }
    }).collect()
}

/// Clang libraries required to link to `libclang` 3.5 and later statically.
const CLANG_LIBRARIES: &'static [&'static str] = &[
    "clang",
    "clangAST",
    "clangAnalysis",
    "clangBasic",
    "clangDriver",
    "clangEdit",
    "clangFrontend",
    "clangIndex",
    "clangLex",
    "clangParse",
    "clangRewrite",
    "clangSema",
    "clangSerialization",
];

/// Returns the Clang libraries required to link to `libclang` statically.
fn get_clang_libraries<P: AsRef<Path>>(directory: P) -> Vec<String> {
    let pattern = directory.as_ref().join("libclang*.a").to_string_lossy().to_string();
    if let Ok(libraries) = glob::glob(&pattern) {
        libraries.filter_map(|l| l.ok().and_then(|l| get_library_name(&l))).collect()
    } else {
        CLANG_LIBRARIES.iter().map(|l| l.to_string()).collect()
    }
}

/// Find and link to `libclang` statically.
#[cfg_attr(feature="runtime", allow(dead_code))]
fn link_static() {
    let file = find(Library::Static, &["libclang.a".into()], "LIBCLANG_STATIC_PATH").unwrap();
    let directory = file.parent().unwrap();
    print!("cargo:rustc-flags=");

    // Specify required Clang static libraries.
    print!("-L {} ", directory.display());
    for library in get_clang_libraries(directory) {
        print!("-l static={} ", library);
    }

    // Specify required LLVM static libraries.
    print!("-L {} ", run_llvm_config(&["--libdir"]).unwrap().trim_right());
    for library in get_llvm_libraries() {
        print!("-l static={} ", library);
    }

    // Specify required system libraries.
    if cfg!(target_os="freebsd") {
        println!("-l ffi -l ncursesw -l c++ -l z");
    } else if cfg!(target_os="linux") {
        println!("-l ffi -l ncursesw -l stdc++ -l z");
    } else if cfg!(target_os="macos") {
        println!("-l ffi -l ncurses -l c++ -l z");
    } else {
        panic!("unsupported operating system for static linking");
    }
}

/// Find and link to `libclang` dynamically.
#[cfg_attr(feature="runtime", allow(dead_code))]
fn link_dynamic() {
    let file = find_shared_library().unwrap();
    let directory = file.parent().unwrap();
    println!("cargo:rustc-link-search={}", directory.display());

    if cfg!(all(target_os="windows", target_env="msvc")) {
        // Find the `libclang` stub static library required for the MSVC toolchain.
        let libdir = if !directory.ends_with("bin") {
            directory.to_owned()
        } else {
            directory.parent().unwrap().join("lib")
        };
        if libdir.join("libclang.lib").exists() {
            println!("cargo:rustc-link-search={}", libdir.display());
        } else if libdir.join("libclang.dll.a").exists() {
            // MSYS and MinGW use `libclang.dll.a` instead of `libclang.lib`. It is linkable with
            // the MSVC linker, but Rust doesn't recognize the `.a` suffix, so we need to copy it
            // with a different name.
            //
            // FIXME: Maybe we can just hardlink or symlink it?
            let out = env::var("OUT_DIR").unwrap();
            fs::copy(libdir.join("libclang.dll.a"), Path::new(&out).join("libclang.lib")).unwrap();
            println!("cargo:rustc-link-search=native={}", out);
        } else {
            panic!(
                "using '{}', so 'libclang.lib' or 'libclang.dll.a' must be available in {}",
                file.display(),
                libdir.display(),
            );
        }
        println!("cargo:rustc-link-lib=dylib=libclang");
    } else {
        println!("cargo:rustc-link-lib=dylib=clang");
    }
}

#[cfg_attr(feature="runtime", allow(dead_code))]
fn main() {
    if cfg!(feature="runtime") {
        if cfg!(feature="static") {
            panic!("`runtime` and `static` features can't be combined");
        }
    } else if cfg!(feature="static") {
        link_static();
    } else {
        link_dynamic();
    }
}
<commit_msg>Improve detection and selection of versioned libraries (#55)<commit_after>// Copyright 2016 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Finds the required `libclang` libraries and links to them.
//!
//! # Environment Variables
//!
//! This build script can make use of several environment variables to help it find the required
//! static or dynamic libraries.
//!
//! * `LLVM_CONFIG_PATH` - provides a path to an `llvm-config` executable
//! * `LIBCLANG_PATH` - provides a path to a directory containing a `libclang` shared library
//! * `LIBCLANG_STATIC_PATH` - provides a path to a directory containing LLVM and Clang static libraries

#![allow(unused_attributes)]

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", warn(clippy))]

extern crate glob;

use std::env;
use std::fs::{self, File};
use std::io::{Read};
use std::path::{Path, PathBuf};
use std::process::{Command};

use glob::{MatchOptions};

/// Returns a path to one of the supplied files if such a file can be found in the supplied directory.
fn contains<D: AsRef<Path>>(directory: D, files: &[String]) -> Option<PathBuf> {
    fn extract_version(path: &PathBuf) -> Vec<u32> {
        path.to_str()
            .unwrap_or("")
            .split('.')
            .map(|s| s.parse::<u32>().unwrap_or(0))
            .collect()
    }

    let mut options = MatchOptions::new();
    options.case_sensitive = false;
    options.require_literal_separator = true;

    let mut found: Vec<PathBuf> = Vec::new();

    for file in files {
        if let Some(file_pattern) = directory.as_ref().join(file).to_str() {
            if let Ok(paths) = glob::glob_with(file_pattern, &options) {
                for path in paths.filter_map(Result::ok) {
                    found.push(path);
                }
            }
        }
    };

    found.sort_by_key(|k| extract_version(k));
    found.pop()
}

/// Runs a console command, returning the output if the command was successfully executed.
fn run(command: &str, arguments: &[&str]) -> Option<String> {
    Command::new(command).args(arguments).output().map(|o| {
        String::from_utf8_lossy(&o.stdout).into_owned()
    }).ok()
}

/// Runs `llvm-config`, returning the output if the command was successfully executed.
fn run_llvm_config(arguments: &[&str]) -> Result<String, String> {
    match run(&env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into()), arguments) {
        Some(output) => Ok(output),
        None => {
            let message = format!(
                "couldn't execute `llvm-config {}`, set the LLVM_CONFIG_PATH environment variable \
                to a path to a valid `llvm-config` executable",
                arguments.join(" "),
            );
            Err(message)
        },
    }
}

/// Backup search directory globs for FreeBSD and Linux.
const SEARCH_LINUX: &'static [&'static str] = &[
    "/usr/lib*",
    "/usr/lib*/*",
    "/usr/lib*/*/*",
    "/usr/local/lib*",
    "/usr/local/lib*/*",
    "/usr/local/lib*/*/*",
    "/usr/local/llvm*/lib",
];

/// Backup search directory globs for OS X.
const SEARCH_OSX: &'static [&'static str] = &[
    "/usr/local/opt/llvm*/lib",
    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
    "/Library/Developer/CommandLineTools/usr/lib",
    "/usr/local/opt/llvm*/lib/llvm*/lib",
];

/// Backup search directory globs for Windows.
const SEARCH_WINDOWS: &'static [&'static str] = &[
    "C:\\LLVM\\lib",
    "C:\\Program Files*\\LLVM\\lib",
    "C:\\MSYS*\\MinGW*\\lib",
];

/// Indicates the type of library being searched for.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Library {
    Dynamic,
    Static,
}

impl Library {
    /// Checks whether the supplied file is a valid library for the architecture.
    fn check(&self, file: &PathBuf) -> Result<(), String> {
        if cfg!(any(target_os="freebsd", target_os="linux")) {
            if *self == Library::Static {
                return Ok(());
            }
            let mut file = try!(File::open(file).map_err(|e| e.to_string()));
            let mut elf = [0; 5];
            try!(file.read_exact(&mut elf).map_err(|e| e.to_string()));
            if elf[..4] != [127, 69, 76, 70] {
                return Err("invalid ELF header".into());
            }
            if cfg!(target_pointer_width="32") && elf[4] != 1 {
                return Err("invalid ELF class (64-bit)".into());
            }
            if cfg!(target_pointer_width="64") && elf[4] != 2 {
                return Err("invalid ELF class (32-bit)".into());
            }
            Ok(())
        } else {
            Ok(())
        }
    }
}

/// Searches for a library, returning the directory it can be found in if the search was successful.
fn find(library: Library, files: &[String], env: &str) -> Result<PathBuf, String> {
    let mut skipped = vec![];

    /// Attempts to return the supplied file.
    macro_rules! try_file {
        ($file:expr) => ({
            match library.check(&$file) {
                Ok(_) => return Ok($file),
                Err(message) => skipped.push(format!("({}: {})", $file.display(), message)),
            }
        });
    }

    /// Searches the supplied directory and, on Windows, any relevant sibling directories.
    macro_rules! search_directory {
        ($directory:ident) => {
            if let Some(file) = contains(&$directory, files) {
                try_file!(file);
            }

            // On Windows, `libclang.dll` is usually found in the LLVM `bin` directory while
            // `libclang.lib` is usually found in the LLVM `lib` directory. To keep things
            // consistent with other platforms, only LLVM `lib` directories are included in the
            // backup search directory globs so we need to search the LLVM `bin` directory here.
            if cfg!(target_os="windows") && $directory.ends_with("lib") {
                let sibling = $directory.parent().unwrap().join("bin");
                if let Some(file) = contains(&sibling, files) {
                    try_file!(file);
                }
            }
        }
    }

    // Search the directory provided by the relevant environment variable if it is set.
    if let Ok(directory) = env::var(env).map(|d| Path::new(&d).to_path_buf()) {
        search_directory!(directory);
    }

    // Search the `bin` and `lib` subdirectories in the directory returned by
    // `llvm-config --prefix` if `llvm-config` is available.
    if let Ok(output) = run_llvm_config(&["--prefix"]) {
        let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
        let bin = directory.join("bin");
        if let Some(file) = contains(&bin, files) {
            try_file!(file);
        }
        let lib = directory.join("lib");
        if let Some(file) = contains(&lib, files) {
            try_file!(file);
        }
    }

    // Search the backup directories.
    let search = if cfg!(any(target_os="freebsd", target_os="linux")) {
        SEARCH_LINUX
    } else if cfg!(target_os="macos") {
        SEARCH_OSX
    } else if cfg!(target_os="windows") {
        SEARCH_WINDOWS
    } else {
        &[]
    };
    for pattern in search {
        let mut options = MatchOptions::new();
        options.case_sensitive = false;
        options.require_literal_separator = true;
        if let Ok(paths) = glob::glob_with(pattern, &options) {
            for path in paths.filter_map(Result::ok).filter(|p| p.is_dir()) {
                search_directory!(path);
            }
        }
    }

    let message = format!(
        "couldn't find any of [{}], set the {} environment variable to a path where one of these \
         files can be found (skipped: [{}])",
        files.iter().map(|f| format!("'{}'", f)).collect::<Vec<_>>().join(", "),
        env,
        skipped.join(", "),
    );
    Err(message)
}

/// Searches for a `libclang` shared library, returning the path to such a shared library if the
/// search was successful.
pub fn find_shared_library() -> Result<PathBuf, String> {
    let mut files = vec![format!("{}clang{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX)];
    if cfg!(any(target_os="linux", target_os="openbsd")) {
        // Some Linux distributions don't create a `libclang.so` symlink.
        //
        files.push("libclang.so.*".into());
    }
    if cfg!(target_os="windows") {
        // The official LLVM build uses `libclang.dll` on Windows instead of `clang.dll`. However,
        // unofficial builds such as MinGW use `clang.dll`.
        files.push("libclang.dll".into());
    }
    find(Library::Dynamic, &files, "LIBCLANG_PATH")
}

/// Returns the name of an LLVM or Clang library from a path to such a library.
fn get_library_name(path: &Path) -> Option<String> {
    path.file_stem().map(|l| l.to_string_lossy()[3..].into())
}

/// Returns the LLVM libraries required to link to `libclang` statically.
fn get_llvm_libraries() -> Vec<String> {
    run_llvm_config(&["--libs"]).unwrap().split_whitespace().filter_map(|p| {
        // Depending on the version of `llvm-config` in use, listed libraries may be in one of two
        // forms, a full path to the library or simply prefixed with `-l`.
        if p.starts_with("-l") {
            Some(p[2..].into())
        } else {
            get_library_name(Path::new(p))
        }
    }).collect()
}

/// Clang libraries required to link to `libclang` 3.5 and later statically.
const CLANG_LIBRARIES: &'static [&'static str] = &[
    "clang",
    "clangAST",
    "clangAnalysis",
    "clangBasic",
    "clangDriver",
    "clangEdit",
    "clangFrontend",
    "clangIndex",
    "clangLex",
    "clangParse",
    "clangRewrite",
    "clangSema",
    "clangSerialization",
];

/// Returns the Clang libraries required to link to `libclang` statically.
fn get_clang_libraries<P: AsRef<Path>>(directory: P) -> Vec<String> {
    let pattern = directory.as_ref().join("libclang*.a").to_string_lossy().to_string();
    if let Ok(libraries) = glob::glob(&pattern) {
        libraries.filter_map(|l| l.ok().and_then(|l| get_library_name(&l))).collect()
    } else {
        CLANG_LIBRARIES.iter().map(|l| l.to_string()).collect()
    }
}

/// Find and link to `libclang` statically.
#[cfg_attr(feature="runtime", allow(dead_code))]
fn link_static() {
    let file = find(Library::Static, &["libclang.a".into()], "LIBCLANG_STATIC_PATH").unwrap();
    let directory = file.parent().unwrap();
    print!("cargo:rustc-flags=");

    // Specify required Clang static libraries.
    print!("-L {} ", directory.display());
    for library in get_clang_libraries(directory) {
        print!("-l static={} ", library);
    }

    // Specify required LLVM static libraries.
    print!("-L {} ", run_llvm_config(&["--libdir"]).unwrap().trim_right());
    for library in get_llvm_libraries() {
        print!("-l static={} ", library);
    }

    // Specify required system libraries.
    if cfg!(target_os="freebsd") {
        println!("-l ffi -l ncursesw -l c++ -l z");
    } else if cfg!(target_os="linux") {
        println!("-l ffi -l ncursesw -l stdc++ -l z");
    } else if cfg!(target_os="macos") {
        println!("-l ffi -l ncurses -l c++ -l z");
    } else {
        panic!("unsupported operating system for static linking");
    }
}

/// Find and link to `libclang` dynamically.
#[cfg_attr(feature="runtime", allow(dead_code))]
fn link_dynamic() {
    let file = find_shared_library().unwrap();
    let directory = file.parent().unwrap();
    println!("cargo:rustc-link-search={}", directory.display());

    if cfg!(all(target_os="windows", target_env="msvc")) {
        // Find the `libclang` stub static library required for the MSVC toolchain.
        let libdir = if !directory.ends_with("bin") {
            directory.to_owned()
        } else {
            directory.parent().unwrap().join("lib")
        };
        if libdir.join("libclang.lib").exists() {
            println!("cargo:rustc-link-search={}", libdir.display());
        } else if libdir.join("libclang.dll.a").exists() {
            // MSYS and MinGW use `libclang.dll.a` instead of `libclang.lib`. It is linkable with
            // the MSVC linker, but Rust doesn't recognize the `.a` suffix, so we need to copy it
            // with a different name.
            //
            // FIXME: Maybe we can just hardlink or symlink it?
            let out = env::var("OUT_DIR").unwrap();
            fs::copy(libdir.join("libclang.dll.a"), Path::new(&out).join("libclang.lib")).unwrap();
            println!("cargo:rustc-link-search=native={}", out);
        } else {
            panic!(
                "using '{}', so 'libclang.lib' or 'libclang.dll.a' must be available in {}",
                file.display(),
                libdir.display(),
            );
        }
        println!("cargo:rustc-link-lib=dylib=libclang");
    } else {
        println!("cargo:rustc-link-lib=dylib=clang");
    }
}

#[cfg_attr(feature="runtime", allow(dead_code))]
fn main() {
    if cfg!(feature="runtime") {
        if cfg!(feature="static") {
            panic!("`runtime` and `static` features can't be combined");
        }
    } else if cfg!(feature="static") {
        link_static();
    } else {
        link_dynamic();
    }
}
<|endoftext|>


<commit_before>extern crate cc;

use std::env;
use std::path::PathBuf;

fn main() {
    let mut config = cc::Build::new();
    let root_path: PathBuf = ["vendor", "tree-sitter"].iter().collect();

    config
        .define("UTF8PROC_STATIC", "")
        .flag_if_supported("-std=c99")
        .flag_if_supported("-Wno-unused-parameter")
        .include(root_path.join("src"))
        .include(root_path.join("include"))
        .include(root_path.join("externals").join("utf8proc"));

    let source_filenames = [
      "get_changed_ranges.c",
      "language.c",
      "lexer.c",
      "node.c",
      "parser.c",
      "stack.c",
      "subtree.c",
      "tree_cursor.c",
      "tree.c",
      "utf16.c",
    ];

    config.files(source_filenames.iter().map(|source_filename| {
        root_path
            .join("src")
            .join("runtime")
            .join(&source_filename)
    }));

    config.file(root_path.join("externals").join("utf8proc").join("utf8proc.c"));

    if env::var("RUST_TREE_SITTER_TEST").is_ok() {
        let parser_dir: PathBuf = ["fixtures", "tree-sitter-rust", "src"].iter().collect();
        config
            .file(parser_dir.join("parser.c"))
            .file(parser_dir.join("scanner.c"));
    }

    config.compile("tree-sitter-runtime");
}
<commit_msg>Add missing source file<commit_after>extern crate cc;

use std::env;
use std::path::PathBuf;

fn main() {
    let mut config = cc::Build::new();
    let root_path: PathBuf = ["vendor", "tree-sitter"].iter().collect();

    config
        .define("UTF8PROC_STATIC", "")
        .flag_if_supported("-std=c99")
        .flag_if_supported("-Wno-unused-parameter")
        .include(root_path.join("src"))
        .include(root_path.join("include"))
        .include(root_path.join("externals").join("utf8proc"));

    let source_filenames = [
      "get_changed_ranges.c",
      "language.c",
      "lexer.c",
      "node.c",
      "parser.c",
      "stack.c",
      "string_input.c",
      "subtree.c",
      "tree_cursor.c",
      "tree.c",
      "utf16.c",
    ];

    config.files(source_filenames.iter().map(|source_filename| {
        root_path
            .join("src")
            .join("runtime")
            .join(&source_filename)
    }));

    config.file(root_path.join("externals").join("utf8proc").join("utf8proc.c"));

    if env::var("RUST_TREE_SITTER_TEST").is_ok() {
        let parser_dir: PathBuf = ["fixtures", "tree-sitter-rust", "src"].iter().collect();
        config
            .file(parser_dir.join("parser.c"))
            .file(parser_dir.join("scanner.c"));
    }

    config.compile("tree-sitter-runtime");
}
<|endoftext|>


<commit_before>use std::env;
use std::ffi::OsStr;
use std::path::Path;

fn main() {
	let target = env::var_os("TARGET").expect("TARGET is not defined");
	if target
		.to_str()
		.expect("Invalid TARGET value")
		.ends_with("x86_64-pc-windows-msvc")
	{
		let current_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
		// TODO: this doesn't work, it's currently impossible to set variables for
		// upstream
		if env::var_os("BOX2D_INCLUDE_PATH") == None {
			eprintln!("BOX2D_INCLUDE_PATH was not set, try the provided cargo_wrapper.bat");
			let box2d_include_path = Path::new(&current_dir).join("include");
			env::set_var(OsStr::new("BOX2D_INCLUDE_PATH"), box2d_include_path.as_os_str());
			println!(
				"cargo:include={}",
				box2d_include_path.to_str().expect("Invalid library path")
			);
		}
		// Library paths can be set as linking is a downstream op
		println!(
			"cargo:rustc-link-search=native={}",
			Path::new(&current_dir)
				.join("lib/x64",)
				.to_str()
				.expect("Invalid library path",)
		)
	}
}
<commit_msg>Removed trailing commas<commit_after>use std::env;
use std::ffi::OsStr;
use std::path::Path;

fn main() {
	let target = env::var_os("TARGET").expect("TARGET is not defined");
	if target
		.to_str()
		.expect("Invalid TARGET value")
		.ends_with("x86_64-pc-windows-msvc")
	{
		let current_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
		// TODO: this doesn't work, it's currently impossible to set variables for
		// upstream
		if env::var_os("BOX2D_INCLUDE_PATH") == None {
			eprintln!("BOX2D_INCLUDE_PATH was not set, try the provided cargo_wrapper.bat");
			let box2d_include_path = Path::new(&current_dir).join("include");
			env::set_var(OsStr::new("BOX2D_INCLUDE_PATH"), box2d_include_path.as_os_str());
			println!(
				"cargo:include={}",
				box2d_include_path.to_str().expect("Invalid library path")
			);
		}
		// Library paths can be set as linking is a downstream op
		println!(
			"cargo:rustc-link-search=native={}",
			Path::new(&current_dir)
				.join("lib/x64")
				.to_str()
				.expect("Invalid library path")
		)
	}
}
<|endoftext|>