<commit_before>//! All matrix multiplication in this module is in row-vector notation,
//! i.e. a vector `v` is transformed with `v * T`, and if you want to apply `T1`
//! before `T2` you use `T1 * T2`

use approxeq::ApproxEq;
use num_traits::Float;
use trig::Trig;
use {Rotation3D, Transform3D, Vector3D, UnknownUnit};

/// A rigid transformation. All lengths are preserved under such a transformation.
///
///
/// Internally, this is a rotation and a translation, with the rotation
/// applied first (i.e. `Rotation * Translation`, in row-vector notation)
///
/// This can be more efficient to use over full matrices, especially if you
/// have to deal with the decomposed quantities often.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
pub struct RigidTransform3D<T, Src, Dst> {
    pub rotation: Rotation3D<T, Src, Dst>,
    pub translation: Vector3D<T, Dst>,
}

impl<T, Src, Dst> RigidTransform3D<T, Src, Dst> {
    /// Construct a new rigid transformation, where the `rotation` applies first
    #[inline]
    pub const fn new(rotation: Rotation3D<T, Src, Dst>, translation: Vector3D<T, Dst>) -> Self {
        Self {
            rotation,
            translation,
        }
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
    /// Construct an identity transform
    #[inline]
    pub fn identity() -> Self {
        Self {
            rotation: Rotation3D::identity(),
            translation: Vector3D::zero(),
        }
    }

    /// Construct a new rigid transformation, where the `translation` applies first
    #[inline]
    pub fn new_from_reversed(
        translation: Vector3D<T, Src>,
        rotation: Rotation3D<T, Src, Dst>,
    ) -> Self {
        // T * R
        //   = (R * R^-1) * T * R
        //   = R * (R^-1 * T * R)
        //   = R * T'
        //
        // T' = (R^-1 * T * R) is also a translation matrix
        // It is equivalent to the translation matrix obtained by rotating the
        // translation by R

        let translation = rotation.transform_vector3d(translation);
        Self {
            rotation,
            translation,
        }
    }

    #[inline]
    pub fn from_rotation(rotation: Rotation3D<T, Src, Dst>) -> Self {
        Self {
            rotation,
            translation: Vector3D::zero(),
        }
    }

    #[inline]
    pub fn from_translation(translation: Vector3D<T, Dst>) -> Self {
        Self {
            translation,
            rotation: Rotation3D::identity(),
        }
    }

    /// Decompose this into a translation and an rotation to be applied in the opposite order
    ///
    /// i.e., the translation is applied _first_
    #[inline]
    pub fn decompose_reversed(&self) -> (Vector3D<T, Src>, Rotation3D<T, Src, Dst>) {
        // self = R * T
        //      = R * T * (R^-1 * R)
        //      = (R * T * R^-1) * R)
        //      = T' * R
        //
        // T' = (R^ * T * R^-1) is T rotated by R^-1

        let translation = self.rotation.inverse().transform_vector3d(self.translation);
        (translation, self.rotation)
    }

    /// Returns the multiplication of the two transforms such that
    /// other's transformation applies after self's transformation.
    ///
    /// i.e., this produces `self * other` in row-vector notation
    #[inline]
    pub fn post_transform<Dst2>(
        &self,
        other: &RigidTransform3D<T, Dst, Dst2>,
    ) -> RigidTransform3D<T, Src, Dst2> {
        // self = R1 * T1
        // other = R2 * T2
        // result = R1 * T1 * R2 * T2
        //        = R1 * (R2 * R2^-1) * T1 * R2 * T2
        //        = (R1 * R2) * (R2^-1 * T1 * R2) * T2
        //        = R' * T' * T2
        //        = R' * T''
        //
        // (R2^-1 * T2 * R2^) = T' = T2 rotated by R2
        // R1 * R2  = R'
        // T' * T2 = T'' = vector addition of translations T2 and T'

        let t_prime = other
            .rotation
            .transform_vector3d(self.translation);
        let r_prime = self.rotation.post_rotate(&other.rotation);
        let t_prime2 = t_prime + other.translation;
        RigidTransform3D {
            rotation: r_prime,
            translation: t_prime2,
        }
    }

    /// Returns the multiplication of the two transforms such that
    /// self's transformation applies after other's transformation.
    ///
    /// i.e., this produces `other * self` in row-vector notation
    #[inline]
    pub fn pre_transform<Src2>(
        &self,
        other: &RigidTransform3D<T, Src2, Src>,
    ) -> RigidTransform3D<T, Src2, Dst> {
        other.post_transform(&self)
    }

    /// Inverts the transformation
    #[inline]
    pub fn inverse(&self) -> RigidTransform3D<T, Dst, Src> {
        // result = (self)^-1
        //        = (R * T)^-1
        //        = T^-1 * R^-1
        //        = (R^-1 * R) * T^-1 * R^-1
        //        = R^-1 * (R * T^-1 * R^-1)
        //        = R' * T'
        //
        // T' = (R * T^-1 * R^-1) = (-T) rotated by R^-1
        // R' = R^-1
        //
        // An easier way of writing this is to use new_from_reversed() with R^-1 and T^-1

        RigidTransform3D::new_from_reversed(
            -self.translation,
            self.rotation.inverse(),
        )
    }

    pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
    where
        T: Trig,
    {
        self.translation
            .to_transform()
            .pre_transform(&self.rotation.to_transform())
    }

    /// Drop the units, preserving only the numeric value.
    #[inline]
    pub fn to_untyped(&self) -> RigidTransform3D<T, UnknownUnit, UnknownUnit> {
        RigidTransform3D {
            rotation: self.rotation.to_untyped(),
            translation: self.translation.to_untyped(),
        }
    }

    /// Tag a unitless value with units.
    #[inline]
    pub fn from_untyped(transform: &RigidTransform3D<T, UnknownUnit, UnknownUnit>) -> Self {
        RigidTransform3D {
            rotation: Rotation3D::from_untyped(&transform.rotation),
            translation: Vector3D::from_untyped(transform.translation),
        }
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> From<Rotation3D<T, Src, Dst>>
    for RigidTransform3D<T, Src, Dst>
{
    fn from(rot: Rotation3D<T, Src, Dst>) -> Self {
        Self::from_rotation(rot)
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> From<Vector3D<T, Dst>>
    for RigidTransform3D<T, Src, Dst>
{
    fn from(t: Vector3D<T, Dst>) -> Self {
        Self::from_translation(t)
    }
}

#[cfg(test)]
mod test {
    use super::RigidTransform3D;
    use default::{Rotation3D, Transform3D, Vector3D};

    #[test]
    fn test_rigid_construction() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        assert!(rigid
            .to_transform()
            .approx_eq(&translation.to_transform().pre_transform(&rotation.to_transform())));

        let rigid = RigidTransform3D::new_from_reversed(translation, rotation);
        assert!(rigid.to_transform().approx_eq(
            &translation
                .to_transform()
                .post_transform(&rotation.to_transform())
        ));
    }

    #[test]
    fn test_rigid_decomposition() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        let (t2, r2) = rigid.decompose_reversed();
        assert!(rigid
            .to_transform()
            .approx_eq(&t2.to_transform().post_transform(&r2.to_transform())));
    }

    #[test]
    fn test_rigid_inverse() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        let inverse = rigid.inverse();
        assert!(rigid
            .post_transform(&inverse)
            .to_transform()
            .approx_eq(&Transform3D::identity()));
        assert!(inverse
            .to_transform()
            .approx_eq(&rigid.to_transform().inverse().unwrap()));
    }

    #[test]
    fn test_rigid_multiply() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
        let translation2 = Vector3D::new(9.3, -3.9, 1.1);
        let rotation2 = Rotation3D::unit_quaternion(0.1, 0.2, 0.3, -0.4);
        let rigid = RigidTransform3D::new(rotation, translation);
        let rigid2 = RigidTransform3D::new(rotation2, translation2);

        assert!(rigid
            .post_transform(&rigid2)
            .to_transform()
            .approx_eq(&rigid.to_transform().post_transform(&rigid2.to_transform())));
        assert!(rigid
            .pre_transform(&rigid2)
            .to_transform()
            .approx_eq(&rigid.to_transform().pre_transform(&rigid2.to_transform())));
    }
}
<commit_msg>Add cast_unit for RigidTransform<commit_after>//! All matrix multiplication in this module is in row-vector notation,
//! i.e. a vector `v` is transformed with `v * T`, and if you want to apply `T1`
//! before `T2` you use `T1 * T2`

use approxeq::ApproxEq;
use num_traits::Float;
use trig::Trig;
use {Rotation3D, Transform3D, Vector3D, UnknownUnit};

/// A rigid transformation. All lengths are preserved under such a transformation.
///
///
/// Internally, this is a rotation and a translation, with the rotation
/// applied first (i.e. `Rotation * Translation`, in row-vector notation)
///
/// This can be more efficient to use over full matrices, especially if you
/// have to deal with the decomposed quantities often.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
pub struct RigidTransform3D<T, Src, Dst> {
    pub rotation: Rotation3D<T, Src, Dst>,
    pub translation: Vector3D<T, Dst>,
}

impl<T, Src, Dst> RigidTransform3D<T, Src, Dst> {
    /// Construct a new rigid transformation, where the `rotation` applies first
    #[inline]
    pub const fn new(rotation: Rotation3D<T, Src, Dst>, translation: Vector3D<T, Dst>) -> Self {
        Self {
            rotation,
            translation,
        }
    }
}

impl<T: Copy, Src, Dst> RigidTransform3D<T, Src, Dst> {
    pub fn cast_unit<Src2, Dst2>(&self) -> RigidTransform3D<T, Src2, Dst2> {
        RigidTransform3D {
            rotation: self.rotation.cast_unit(),
            translation: self.translation.cast_unit(),
        }
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
    /// Construct an identity transform
    #[inline]
    pub fn identity() -> Self {
        Self {
            rotation: Rotation3D::identity(),
            translation: Vector3D::zero(),
        }
    }

    /// Construct a new rigid transformation, where the `translation` applies first
    #[inline]
    pub fn new_from_reversed(
        translation: Vector3D<T, Src>,
        rotation: Rotation3D<T, Src, Dst>,
    ) -> Self {
        // T * R
        //   = (R * R^-1) * T * R
        //   = R * (R^-1 * T * R)
        //   = R * T'
        //
        // T' = (R^-1 * T * R) is also a translation matrix
        // It is equivalent to the translation matrix obtained by rotating the
        // translation by R

        let translation = rotation.transform_vector3d(translation);
        Self {
            rotation,
            translation,
        }
    }

    #[inline]
    pub fn from_rotation(rotation: Rotation3D<T, Src, Dst>) -> Self {
        Self {
            rotation,
            translation: Vector3D::zero(),
        }
    }

    #[inline]
    pub fn from_translation(translation: Vector3D<T, Dst>) -> Self {
        Self {
            translation,
            rotation: Rotation3D::identity(),
        }
    }

    /// Decompose this into a translation and an rotation to be applied in the opposite order
    ///
    /// i.e., the translation is applied _first_
    #[inline]
    pub fn decompose_reversed(&self) -> (Vector3D<T, Src>, Rotation3D<T, Src, Dst>) {
        // self = R * T
        //      = R * T * (R^-1 * R)
        //      = (R * T * R^-1) * R)
        //      = T' * R
        //
        // T' = (R^ * T * R^-1) is T rotated by R^-1

        let translation = self.rotation.inverse().transform_vector3d(self.translation);
        (translation, self.rotation)
    }

    /// Returns the multiplication of the two transforms such that
    /// other's transformation applies after self's transformation.
    ///
    /// i.e., this produces `self * other` in row-vector notation
    #[inline]
    pub fn post_transform<Dst2>(
        &self,
        other: &RigidTransform3D<T, Dst, Dst2>,
    ) -> RigidTransform3D<T, Src, Dst2> {
        // self = R1 * T1
        // other = R2 * T2
        // result = R1 * T1 * R2 * T2
        //        = R1 * (R2 * R2^-1) * T1 * R2 * T2
        //        = (R1 * R2) * (R2^-1 * T1 * R2) * T2
        //        = R' * T' * T2
        //        = R' * T''
        //
        // (R2^-1 * T2 * R2^) = T' = T2 rotated by R2
        // R1 * R2  = R'
        // T' * T2 = T'' = vector addition of translations T2 and T'

        let t_prime = other
            .rotation
            .transform_vector3d(self.translation);
        let r_prime = self.rotation.post_rotate(&other.rotation);
        let t_prime2 = t_prime + other.translation;
        RigidTransform3D {
            rotation: r_prime,
            translation: t_prime2,
        }
    }

    /// Returns the multiplication of the two transforms such that
    /// self's transformation applies after other's transformation.
    ///
    /// i.e., this produces `other * self` in row-vector notation
    #[inline]
    pub fn pre_transform<Src2>(
        &self,
        other: &RigidTransform3D<T, Src2, Src>,
    ) -> RigidTransform3D<T, Src2, Dst> {
        other.post_transform(&self)
    }

    /// Inverts the transformation
    #[inline]
    pub fn inverse(&self) -> RigidTransform3D<T, Dst, Src> {
        // result = (self)^-1
        //        = (R * T)^-1
        //        = T^-1 * R^-1
        //        = (R^-1 * R) * T^-1 * R^-1
        //        = R^-1 * (R * T^-1 * R^-1)
        //        = R' * T'
        //
        // T' = (R * T^-1 * R^-1) = (-T) rotated by R^-1
        // R' = R^-1
        //
        // An easier way of writing this is to use new_from_reversed() with R^-1 and T^-1

        RigidTransform3D::new_from_reversed(
            -self.translation,
            self.rotation.inverse(),
        )
    }

    pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
    where
        T: Trig,
    {
        self.translation
            .to_transform()
            .pre_transform(&self.rotation.to_transform())
    }

    /// Drop the units, preserving only the numeric value.
    #[inline]
    pub fn to_untyped(&self) -> RigidTransform3D<T, UnknownUnit, UnknownUnit> {
        RigidTransform3D {
            rotation: self.rotation.to_untyped(),
            translation: self.translation.to_untyped(),
        }
    }

    /// Tag a unitless value with units.
    #[inline]
    pub fn from_untyped(transform: &RigidTransform3D<T, UnknownUnit, UnknownUnit>) -> Self {
        RigidTransform3D {
            rotation: Rotation3D::from_untyped(&transform.rotation),
            translation: Vector3D::from_untyped(transform.translation),
        }
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> From<Rotation3D<T, Src, Dst>>
    for RigidTransform3D<T, Src, Dst>
{
    fn from(rot: Rotation3D<T, Src, Dst>) -> Self {
        Self::from_rotation(rot)
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> From<Vector3D<T, Dst>>
    for RigidTransform3D<T, Src, Dst>
{
    fn from(t: Vector3D<T, Dst>) -> Self {
        Self::from_translation(t)
    }
}

#[cfg(test)]
mod test {
    use super::RigidTransform3D;
    use default::{Rotation3D, Transform3D, Vector3D};

    #[test]
    fn test_rigid_construction() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        assert!(rigid
            .to_transform()
            .approx_eq(&translation.to_transform().pre_transform(&rotation.to_transform())));

        let rigid = RigidTransform3D::new_from_reversed(translation, rotation);
        assert!(rigid.to_transform().approx_eq(
            &translation
                .to_transform()
                .post_transform(&rotation.to_transform())
        ));
    }

    #[test]
    fn test_rigid_decomposition() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        let (t2, r2) = rigid.decompose_reversed();
        assert!(rigid
            .to_transform()
            .approx_eq(&t2.to_transform().post_transform(&r2.to_transform())));
    }

    #[test]
    fn test_rigid_inverse() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        let inverse = rigid.inverse();
        assert!(rigid
            .post_transform(&inverse)
            .to_transform()
            .approx_eq(&Transform3D::identity()));
        assert!(inverse
            .to_transform()
            .approx_eq(&rigid.to_transform().inverse().unwrap()));
    }

    #[test]
    fn test_rigid_multiply() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
        let translation2 = Vector3D::new(9.3, -3.9, 1.1);
        let rotation2 = Rotation3D::unit_quaternion(0.1, 0.2, 0.3, -0.4);
        let rigid = RigidTransform3D::new(rotation, translation);
        let rigid2 = RigidTransform3D::new(rotation2, translation2);

        assert!(rigid
            .post_transform(&rigid2)
            .to_transform()
            .approx_eq(&rigid.to_transform().post_transform(&rigid2.to_transform())));
        assert!(rigid
            .pre_transform(&rigid2)
            .to_transform()
            .approx_eq(&rigid.to_transform().pre_transform(&rigid2.to_transform())));
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ptr;
use std::sync::atomic;
use std::env;
use std::fmt;

use libc;

use memmap::{Mmap, MmapOptions, Protection};

/// A task's stack. The name "Stack" is a vestige of segmented stacks.
pub struct Stack {
    buf: Option<Mmap>,
    min_size: usize,
}

impl fmt::Debug for Stack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "Stack {} buf: ", "{"));
        match self.buf {
            Some(ref map) => try!(write!(f, "Some({:#x}), ", map.ptr() as libc::uintptr_t)),
            None => try!(write!(f, "None, ")),
        }
        write!(f, "min_size: {:?} {}", self.min_size, "}")
    }
}

impl Stack {
    /// Allocate a new stack of `size`. If size = 0, this will fail. Use
    /// `dummy_stack` if you want a zero-sized stack.
    pub fn new(size: usize) -> Stack {
        // Map in a stack. Eventually we might be able to handle stack
        // allocation failure, which would fail to spawn the task. But there's
        // not many sensible things to do on OOM.  Failure seems fine (and is
        // what the old stack allocation did).

        let options =
            if cfg!(all(not(windows), not(target_os = "freebsd"), not(target_os = "dragonfly"))) {
                MmapOptions { stack: true }
            } else if cfg!(any(target_os = "freebsd", target_os = "dragonfly")) {
                MmapOptions { stack: false }
            } else {
                MmapOptions { stack: false }
            };

        let stack = match Mmap::anonymous_with_options(size, Protection::ReadCopy, options) {
            Ok(map) => map,
            Err(e) => panic!("mmap for stack of size {} failed: {}", size, e)
        };

        // Change the last page to be inaccessible. This is to provide safety;
        // when an FFI function overflows it will (hopefully) hit this guard
        // page. It isn't guaranteed, but that's why FFI is unsafe. buf.data is
        // guaranteed to be aligned properly.
        if !protect_last_page(&stack) {
            panic!("Could not memory-protect guard page. stack={:?}",
                  stack.ptr());
        }

        Stack {
            buf: Some(stack),
            min_size: size,
        }
    }

    /// Create a 0-length stack which starts (and ends) at 0.
    #[allow(dead_code)]
    pub unsafe fn dummy_stack() -> Stack {
        Stack {
            buf: None,
            min_size: 0,
        }
    }

    #[allow(dead_code)]
    pub fn guard(&self) -> *const usize {
        (self.start() as usize + page_size()) as *const usize
    }

    /// Point to the low end of the allocated stack
    pub fn start(&self) -> *const usize {
        self.buf.as_ref()
            .map(|m| m.ptr() as *const usize)
            .unwrap_or(ptr::null())
    }

    /// Point one usize beyond the high end of the allocated stack
    pub fn end(&self) -> *const usize {
        self.buf
            .as_ref()
            .map(|buf| unsafe {
                buf.ptr().offset(buf.len() as isize) as *const usize
            })
            .unwrap_or(ptr::null())
    }
}

#[cfg(unix)]
fn protect_last_page(stack: &Mmap) -> bool {
    unsafe {
        // This may seem backwards: the start of the segment is the last page?
        // Yes! The stack grows from higher addresses (the end of the allocated
        // block) to lower addresses (the start of the allocated block).
        let last_page = stack.ptr() as *mut libc::c_void;
        libc::mprotect(last_page, page_size() as libc::size_t,
                       libc::PROT_NONE) != -1
    }
}

#[cfg(windows)]
fn protect_last_page(stack: &Mmap) -> bool {
    unsafe {
        // see above
        let last_page = stack.ptr() as *mut libc::c_void;
        let mut old_prot: libc::DWORD = 0;
        libc::VirtualProtect(last_page, page_size() as libc::SIZE_T,
                             libc::PAGE_NOACCESS,
                             &mut old_prot as libc::LPDWORD) != 0
    }
}

#[derive(Debug)]
pub struct StackPool {
    // Ideally this would be some data structure that preserved ordering on
    // Stack.min_size.
    stacks: Vec<Stack>,
}

impl StackPool {
    pub fn new() -> StackPool {
        StackPool {
            stacks: vec![],
        }
    }

    pub fn take_stack(&mut self, min_size: usize) -> Stack {
        // Ideally this would be a binary search
        match self.stacks.iter().position(|s| min_size <= s.min_size) {
            Some(idx) => self.stacks.swap_remove(idx),
            None => Stack::new(min_size)
        }
    }

    pub fn give_stack(&mut self, stack: Stack) {
        if self.stacks.len() <= max_cached_stacks() {
            self.stacks.push(stack)
        }
    }
}

fn max_cached_stacks() -> usize {
    static mut AMT: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT;
    match unsafe { AMT.load(atomic::Ordering::SeqCst) } {
        0 => {}
        n => return n - 1,
    }
    let amt = env::var("RUST_MAX_CACHED_STACKS").ok().and_then(|s| s.parse().ok());
    // This default corresponds to 20M of cache per scheduler (at the
    // default size).
    let amt = amt.unwrap_or(10);
    // 0 is our sentinel value, so ensure that we'll never see 0 after
    // initialization has run
    unsafe { AMT.store(amt + 1, atomic::Ordering::SeqCst); }
    return amt;
}

#[cfg(unix)]
fn page_size() -> usize {
    unsafe {
        libc::sysconf(libc::_SC_PAGESIZE) as usize
    }
}

#[cfg(windows)]
fn page_size() -> usize {
    unsafe {
        let mut info = mem::zeroed();
        libc::GetSystemInfo(&mut info);
        info.dwPageSize as usize
    }
}

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

    #[test]
    fn stack_pool_caches() {
        let mut p = StackPool::new();
        let s = p.take_stack(10);
        p.give_stack(s);
        let s = p.take_stack(4);
        assert_eq!(s.min_size, 10);
        p.give_stack(s);
        let s = p.take_stack(14);
        assert_eq!(s.min_size, 14);
        p.give_stack(s);
    }

    #[test]
    fn stack_pool_caches_exact() {
        let mut p = StackPool::new();
        let s = p.take_stack(10);
        p.give_stack(s);

        let s = p.take_stack(10);
        assert_eq!(s.min_size, 10);
    }
}
<commit_msg>Bugfix for Windows (std::mem usage)<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ptr;
use std::sync::atomic;
use std::env;
use std::fmt;
use std::mem;

use libc;

use memmap::{Mmap, MmapOptions, Protection};

/// A task's stack. The name "Stack" is a vestige of segmented stacks.
pub struct Stack {
    buf: Option<Mmap>,
    min_size: usize,
}

impl fmt::Debug for Stack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "Stack {} buf: ", "{"));
        match self.buf {
            Some(ref map) => try!(write!(f, "Some({:#x}), ", map.ptr() as libc::uintptr_t)),
            None => try!(write!(f, "None, ")),
        }
        write!(f, "min_size: {:?} {}", self.min_size, "}")
    }
}

impl Stack {
    /// Allocate a new stack of `size`. If size = 0, this will fail. Use
    /// `dummy_stack` if you want a zero-sized stack.
    pub fn new(size: usize) -> Stack {
        // Map in a stack. Eventually we might be able to handle stack
        // allocation failure, which would fail to spawn the task. But there's
        // not many sensible things to do on OOM.  Failure seems fine (and is
        // what the old stack allocation did).

        let options =
            if cfg!(all(not(windows), not(target_os = "freebsd"), not(target_os = "dragonfly"))) {
                MmapOptions { stack: true }
            } else if cfg!(any(target_os = "freebsd", target_os = "dragonfly")) {
                MmapOptions { stack: false }
            } else {
                MmapOptions { stack: false }
            };

        let stack = match Mmap::anonymous_with_options(size, Protection::ReadCopy, options) {
            Ok(map) => map,
            Err(e) => panic!("mmap for stack of size {} failed: {}", size, e)
        };

        // Change the last page to be inaccessible. This is to provide safety;
        // when an FFI function overflows it will (hopefully) hit this guard
        // page. It isn't guaranteed, but that's why FFI is unsafe. buf.data is
        // guaranteed to be aligned properly.
        if !protect_last_page(&stack) {
            panic!("Could not memory-protect guard page. stack={:?}",
                  stack.ptr());
        }

        Stack {
            buf: Some(stack),
            min_size: size,
        }
    }

    /// Create a 0-length stack which starts (and ends) at 0.
    #[allow(dead_code)]
    pub unsafe fn dummy_stack() -> Stack {
        Stack {
            buf: None,
            min_size: 0,
        }
    }

    #[allow(dead_code)]
    pub fn guard(&self) -> *const usize {
        (self.start() as usize + page_size()) as *const usize
    }

    /// Point to the low end of the allocated stack
    pub fn start(&self) -> *const usize {
        self.buf.as_ref()
            .map(|m| m.ptr() as *const usize)
            .unwrap_or(ptr::null())
    }

    /// Point one usize beyond the high end of the allocated stack
    pub fn end(&self) -> *const usize {
        self.buf
            .as_ref()
            .map(|buf| unsafe {
                buf.ptr().offset(buf.len() as isize) as *const usize
            })
            .unwrap_or(ptr::null())
    }
}

#[cfg(unix)]
fn protect_last_page(stack: &Mmap) -> bool {
    unsafe {
        // This may seem backwards: the start of the segment is the last page?
        // Yes! The stack grows from higher addresses (the end of the allocated
        // block) to lower addresses (the start of the allocated block).
        let last_page = stack.ptr() as *mut libc::c_void;
        libc::mprotect(last_page, page_size() as libc::size_t,
                       libc::PROT_NONE) != -1
    }
}

#[cfg(windows)]
fn protect_last_page(stack: &Mmap) -> bool {
    unsafe {
        // see above
        let last_page = stack.ptr() as *mut libc::c_void;
        let mut old_prot: libc::DWORD = 0;
        libc::VirtualProtect(last_page, page_size() as libc::SIZE_T,
                             libc::PAGE_NOACCESS,
                             &mut old_prot as libc::LPDWORD) != 0
    }
}

#[derive(Debug)]
pub struct StackPool {
    // Ideally this would be some data structure that preserved ordering on
    // Stack.min_size.
    stacks: Vec<Stack>,
}

impl StackPool {
    pub fn new() -> StackPool {
        StackPool {
            stacks: vec![],
        }
    }

    pub fn take_stack(&mut self, min_size: usize) -> Stack {
        // Ideally this would be a binary search
        match self.stacks.iter().position(|s| min_size <= s.min_size) {
            Some(idx) => self.stacks.swap_remove(idx),
            None => Stack::new(min_size)
        }
    }

    pub fn give_stack(&mut self, stack: Stack) {
        if self.stacks.len() <= max_cached_stacks() {
            self.stacks.push(stack)
        }
    }
}

fn max_cached_stacks() -> usize {
    static mut AMT: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT;
    match unsafe { AMT.load(atomic::Ordering::SeqCst) } {
        0 => {}
        n => return n - 1,
    }
    let amt = env::var("RUST_MAX_CACHED_STACKS").ok().and_then(|s| s.parse().ok());
    // This default corresponds to 20M of cache per scheduler (at the
    // default size).
    let amt = amt.unwrap_or(10);
    // 0 is our sentinel value, so ensure that we'll never see 0 after
    // initialization has run
    unsafe { AMT.store(amt + 1, atomic::Ordering::SeqCst); }
    return amt;
}

#[cfg(unix)]
fn page_size() -> usize {
    unsafe {
        libc::sysconf(libc::_SC_PAGESIZE) as usize
    }
}

#[cfg(windows)]
fn page_size() -> usize {
    unsafe {
        let mut info = mem::zeroed();
        libc::GetSystemInfo(&mut info);
        info.dwPageSize as usize
    }
}

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

    #[test]
    fn stack_pool_caches() {
        let mut p = StackPool::new();
        let s = p.take_stack(10);
        p.give_stack(s);
        let s = p.take_stack(4);
        assert_eq!(s.min_size, 10);
        p.give_stack(s);
        let s = p.take_stack(14);
        assert_eq!(s.min_size, 14);
        p.give_stack(s);
    }

    #[test]
    fn stack_pool_caches_exact() {
        let mut p = StackPool::new();
        let s = p.take_stack(10);
        p.give_stack(s);

        let s = p.take_stack(10);
        assert_eq!(s.min_size, 10);
    }
}
<|endoftext|>


<commit_before>extern crate stemmer;
extern crate unicode_normalization;
extern crate unicode_segmentation;

use self::stemmer::Stemmer;
use self::unicode_normalization::UnicodeNormalization;
use self::unicode_segmentation::UnicodeSegmentation;

pub struct Stems<'a> {
    words: unicode_segmentation::UWordBoundIndices<'a>,
    stemmer: Stemmer,
    word_position: usize,
}

#[derive(Debug, PartialEq)]
pub struct StemmedWord {
    // Where the stemmed word starts
    pub word_pos: u32,
    // The stemmed word
    pub stemmed: String,
}

impl<'a> Stems<'a> {
    pub fn new(text: &str) -> Stems {
        Stems{
            words: text.split_word_bound_indices(),
            stemmer: Stemmer::new("english").unwrap(),
            word_position: 0,
        }
    }
}

impl<'a> Iterator for Stems<'a> {
    type Item = StemmedWord;

    fn next(&mut self) -> Option<StemmedWord> {
        // we loop though until we find alphabetic chars. That becomes our stem word.
        let mut non_alpha = String::new(); // will contain any non-alphabetic chars
                                           // returned iff no other alphabetic chars
        while let Some((_pos, word)) = self.words.next() {
            let normalized = word.nfkc().collect::<String>();
            if normalized.chars().next().unwrap().is_alphabetic() {
                let pos = self.word_position;
                self.word_position += 1;
                return Some(StemmedWord {
                        word_pos: pos as u32,
                        stemmed: self.stemmer.stem(&normalized.to_lowercase()),
                    });
            } else {
                non_alpha.push_str(&normalized);
            }
        }
        if non_alpha.is_empty() {
            if self.word_position == 0 {
                self.word_position = 1;
                // in this case we were passed an empty string
                // so we don't just return None, but we return
                // an empty string Stemmed word.
                // otherwise searching fields for empty strings
                // wouldn't be possible.
                return Some(StemmedWord {
                    word_pos: 0,
                    stemmed: String::new(),
                });
            } else {
                return None;
            }
        } else {
            if self.word_position == 0 {
                self.word_position = 1;
                return Some(StemmedWord {
                                word_pos: 0,
                                stemmed: non_alpha,
                    });
            } else {
                return None;
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{StemmedWord, Stems};

    #[test]
    fn test_stems_mixedcase() {
        let input = "THEse Words deeplY test smOOthly that stemmING";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { word_pos: 0, stemmed: String::from("these")},
            StemmedWord { word_pos: 1, stemmed: String::from("word")},
            // "deeply" stems to "deepli"
            StemmedWord { word_pos: 2, stemmed: String::from("deepli")},
            StemmedWord { word_pos: 3, stemmed: String::from("test")},
            StemmedWord { word_pos: 4, stemmed: String::from("smooth")},
            StemmedWord { word_pos: 5, stemmed: String::from("that")},
            StemmedWord { word_pos: 6, stemmed: String::from("stem")},
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_nonchars() {
        let input = "  @#$!== \t+-";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        assert_eq!(result, vec![StemmedWord { word_pos: 0, stemmed: String::from("  @#$!== \t+-")}]);
    }

    #[test]
    fn test_stems_some_nonchars() {
        let input = "@!?   Let's seeing...";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { word_pos: 0, stemmed: String::from("let")},
            StemmedWord { word_pos: 1, stemmed: String::from("see")},
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode() {
        let input = "Ünicöde stemming";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { word_pos: 0, stemmed: String::from("ünicöd")},
            StemmedWord { word_pos: 1, stemmed: String::from("stem")},
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_trailing_needs_normalized() {
        let input = r#"Didgeridoos™"#;
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { word_pos: 0, stemmed: String::from("didgeridoo")},
            StemmedWord { word_pos: 1, stemmed: String::from("tm")},
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode_lowercase_has_more_bytes() {
        let input = "İ";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![StemmedWord { word_pos: 0, stemmed: String::from("i̇")}];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    // Some characters don't have a corresponding uppercase chararacter with the same byte size,
    // but when those uppercase letters are transformed to lowercase, they are transformed to the
    // same number of bytes combining several characters (although there is a single character for
    // it).
    // Here's an example in Python:
    //
    //     >>> u"Ρ̓".lower() == u"ῤ"
    //     False
    //     >>> u"Ρ̓".lower() == u"ῤ".lower()
    //     False
    //     >>> u"Ρ̓".lower() == u"ῤ".upper().lower()
    //     True
    // Or Rust:
    //
    //    let upper = "Ρ̓";
    //    let lower = "ῤ";
    //    println!("lower({}) == {}: {}", upper, lower, upper.to_lowercase() == lower);
    //    println!("lower({}) == lower(upper({})): {}", upper, lower, upper.to_lowercase() == lower.to_uppercase().to_lowercase());
    //    lower(Ρ̓) == ῤ: false
    //    lower(Ρ̓) == lower(upper(ῤ)): true
    #[test]
    fn test_stems_unicode_lowercase_has_less_bytes() {
        // The input is: Ρ̓ῤῤ (11 bytes), lowercases is ῤῤῤ (9 bytes)
        let input = "\u{03A1}\u{0313}\u{03C1}\u{0313}\u{1FE4}";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { word_pos: 0, stemmed: String::from("\u{03C1}\u{0313}\u{1FE4}\u{1FE4}")},
           ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }
}
<commit_msg>Small optimization and formatting changes<commit_after>extern crate stemmer;
extern crate unicode_normalization;
extern crate unicode_segmentation;

use self::stemmer::Stemmer;
use self::unicode_normalization::UnicodeNormalization;
use self::unicode_segmentation::UnicodeSegmentation;

pub struct Stems<'a> {
    words: unicode_segmentation::UWordBoundIndices<'a>,
    stemmer: Stemmer,
    word_position: usize,
}

#[derive(Debug, PartialEq)]
pub struct StemmedWord {
    // Where the stemmed word starts
    pub word_pos: u32,
    // The stemmed word
    pub stemmed: String,
}

impl<'a> Stems<'a> {
    pub fn new(text: &str) -> Stems {
        Stems{
            words: text.split_word_bound_indices(),
            stemmer: Stemmer::new("english").unwrap(),
            word_position: 0,
        }
    }
}

impl<'a> Iterator for Stems<'a> {
    type Item = StemmedWord;

    fn next(&mut self) -> Option<StemmedWord> {
        // we loop though until we find alphabetic chars. That becomes our stem word.
        let mut non_alpha = String::new(); // will contain any non-alphabetic chars
                                           // returned iff no other alphabetic chars
        while let Some((_pos, word)) = self.words.next() {
            let normalized = word.nfkc().collect::<String>();
            if normalized.chars().next().unwrap().is_alphabetic() {
                let pos = self.word_position;
                self.word_position += 1;
                return Some(StemmedWord {
                    word_pos: pos as u32,
                    stemmed: self.stemmer.stem(&normalized.to_lowercase()),
                });
            } else {
                if self.word_position == 0 {
                    non_alpha.push_str(&normalized);
                }
            }
        }
        if non_alpha.is_empty() {
            if self.word_position == 0 {
                self.word_position = 1;
                // in this case we were passed an empty string
                // so we don't just return None, but we return
                // an empty string Stemmed word.
                // otherwise searching fields for empty strings
                // wouldn't be possible.
                return Some(StemmedWord {
                    word_pos: 0,
                    stemmed: String::new(),
                });
            } else {
                return None;
            }
        } else {
            if self.word_position == 0 {
                self.word_position = 1;
                return Some(StemmedWord {
                    word_pos: 0,
                    stemmed: non_alpha,
                });
            } else {
                return None;
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{StemmedWord, Stems};

    #[test]
    fn test_stems_mixedcase() {
        let input = "THEse Words deeplY test smOOthly that stemmING";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { word_pos: 0, stemmed: String::from("these")},
            StemmedWord { word_pos: 1, stemmed: String::from("word")},
            // "deeply" stems to "deepli"
            StemmedWord { word_pos: 2, stemmed: String::from("deepli")},
            StemmedWord { word_pos: 3, stemmed: String::from("test")},
            StemmedWord { word_pos: 4, stemmed: String::from("smooth")},
            StemmedWord { word_pos: 5, stemmed: String::from("that")},
            StemmedWord { word_pos: 6, stemmed: String::from("stem")},
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_nonchars() {
        let input = "  @#$!== \t+-";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        assert_eq!(result, vec![StemmedWord { word_pos: 0, stemmed: String::from("  @#$!== \t+-")}]);
    }

    #[test]
    fn test_stems_some_nonchars() {
        let input = "@!?   Let's seeing...";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { word_pos: 0, stemmed: String::from("let")},
            StemmedWord { word_pos: 1, stemmed: String::from("see")},
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode() {
        let input = "Ünicöde stemming";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { word_pos: 0, stemmed: String::from("ünicöd")},
            StemmedWord { word_pos: 1, stemmed: String::from("stem")},
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_trailing_needs_normalized() {
        let input = r#"Didgeridoos™"#;
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { word_pos: 0, stemmed: String::from("didgeridoo")},
            StemmedWord { word_pos: 1, stemmed: String::from("tm")},
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode_lowercase_has_more_bytes() {
        let input = "İ";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![StemmedWord { word_pos: 0, stemmed: String::from("i̇")}];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    // Some characters don't have a corresponding uppercase chararacter with the same byte size,
    // but when those uppercase letters are transformed to lowercase, they are transformed to the
    // same number of bytes combining several characters (although there is a single character for
    // it).
    // Here's an example in Python:
    //
    //     >>> u"Ρ̓".lower() == u"ῤ"
    //     False
    //     >>> u"Ρ̓".lower() == u"ῤ".lower()
    //     False
    //     >>> u"Ρ̓".lower() == u"ῤ".upper().lower()
    //     True
    // Or Rust:
    //
    //    let upper = "Ρ̓";
    //    let lower = "ῤ";
    //    println!("lower({}) == {}: {}", upper, lower, upper.to_lowercase() == lower);
    //    println!("lower({}) == lower(upper({})): {}", upper, lower, upper.to_lowercase() == lower.to_uppercase().to_lowercase());
    //    lower(Ρ̓) == ῤ: false
    //    lower(Ρ̓) == lower(upper(ῤ)): true
    #[test]
    fn test_stems_unicode_lowercase_has_less_bytes() {
        // The input is: Ρ̓ῤῤ (11 bytes), lowercases is ῤῤῤ (9 bytes)
        let input = "\u{03A1}\u{0313}\u{03C1}\u{0313}\u{1FE4}";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { word_pos: 0, stemmed: String::from("\u{03C1}\u{0313}\u{1FE4}\u{1FE4}")},
           ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }
}
<|endoftext|>


<commit_before>use console::{measure_text_width, Style};

use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration};
use crate::state::ProgressState;
use crate::utils::{expand_template, pad_str};
use std::borrow::Cow;

#[cfg(feature = "improved_unicode")]
use unicode_segmentation::UnicodeSegmentation;

/// Behavior of a progress bar when it is finished
///
/// This is invoked when a [`ProgressBar`] or [`ProgressBarIter`] completes and
/// [`ProgressBar::is_finished`] is false.
///
/// [`ProgressBar`]: crate::ProgressBar
/// [`ProgressBarIter`]: crate::ProgressBarIter
/// [`ProgressBar::is_finished`]: crate::ProgressBar::is_finished
#[derive(Clone, Debug)]
pub enum ProgressFinish {
    /// Finishes the progress bar and leaves the current message
    ///
    /// Same behavior as calling [`ProgressBar::finish()`](crate::ProgressBar::finish).
    AndLeave,
    /// Finishes the progress bar at current position and leaves the current message
    ///
    /// Same behavior as calling [`ProgressBar::finish_at_current_pos()`](crate::ProgressBar::finish_at_current_pos).
    AtCurrentPos,
    /// Finishes the progress bar and sets a message
    ///
    /// Same behavior as calling [`ProgressBar::finish_with_message()`](crate::ProgressBar::finish_with_message).
    WithMessage(Cow<'static, str>),
    /// Finishes the progress bar and completely clears it (this is the default)
    ///
    /// Same behavior as calling [`ProgressBar::finish_and_clear()`](crate::ProgressBar::finish_and_clear).
    AndClear,
    /// Finishes the progress bar and leaves the current message and progress
    ///
    /// Same behavior as calling [`ProgressBar::abandon()`](crate::ProgressBar::abandon).
    Abandon,
    /// Finishes the progress bar and sets a message, and leaves the current progress
    ///
    /// Same behavior as calling [`ProgressBar::abandon_with_message()`](crate::ProgressBar::abandon_with_message).
    AbandonWithMessage(Cow<'static, str>),
}

impl Default for ProgressFinish {
    fn default() -> Self {
        Self::AndClear
    }
}

/// Controls the rendering style of progress bars
#[derive(Clone, Debug)]
pub struct ProgressStyle {
    tick_strings: Vec<Box<str>>,
    progress_chars: Vec<Box<str>>,
    template: Box<str>,
    on_finish: ProgressFinish,
    // how unicode-big each char in progress_chars is
    char_width: usize,
}

#[cfg(feature = "improved_unicode")]
fn segment(s: &str) -> Vec<Box<str>> {
    UnicodeSegmentation::graphemes(s, true)
        .map(|s| s.into())
        .collect()
}

#[cfg(not(feature = "improved_unicode"))]
fn segment(s: &str) -> Vec<Box<str>> {
    s.chars().map(|x| x.to_string().into()).collect()
}

#[cfg(feature = "improved_unicode")]
fn measure(s: &str) -> usize {
    unicode_width::UnicodeWidthStr::width(s)
}

#[cfg(not(feature = "improved_unicode"))]
fn measure(s: &str) -> usize {
    s.chars().count()
}

/// finds the unicode-aware width of the passed grapheme cluters
/// panics on an empty parameter, or if the characters are not equal-width
fn width(c: &[Box<str>]) -> usize {
    c.iter()
        .map(|s| measure(s.as_ref()))
        .fold(None, |acc, new| {
            match acc {
                None => return Some(new),
                Some(old) => assert_eq!(old, new, "got passed un-equal width progress characters"),
            }
            acc
        })
        .unwrap()
}

impl ProgressStyle {
    /// Returns the default progress bar style for bars
    pub fn default_bar() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: "{wide_bar} {pos}/{len}".into(),
            on_finish: ProgressFinish::default(),
        }
    }

    /// Returns the default progress bar style for spinners
    pub fn default_spinner() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: "{spinner} {msg}".into(),
            on_finish: ProgressFinish::default(),
        }
    }

    /// Sets the tick character sequence for spinners
    pub fn tick_chars(mut self, s: &str) -> ProgressStyle {
        self.tick_strings = s.chars().map(|c| c.to_string().into()).collect();
        // Format bar will panic with some potentially confusing message, better to panic here
        // with a message explicitly informing of the problem
        assert!(
            self.tick_strings.len() >= 2,
            "at least 2 tick chars required"
        );
        self
    }

    /// Sets the tick string sequence for spinners
    pub fn tick_strings(mut self, s: &[&str]) -> ProgressStyle {
        self.tick_strings = s.iter().map(|s| s.to_string().into()).collect();
        // Format bar will panic with some potentially confusing message, better to panic here
        // with a message explicitly informing of the problem
        assert!(
            self.progress_chars.len() >= 2,
            "at least 2 tick strings required"
        );
        self
    }

    /// Sets the progress characters `(filled, current, to do)`
    ///
    /// You can pass more then three for a more detailed display.
    /// All passed grapheme clusters need to be of equal width.
    pub fn progress_chars(mut self, s: &str) -> ProgressStyle {
        self.progress_chars = segment(s);
        // Format bar will panic with some potentially confusing message, better to panic here
        // with a message explicitly informing of the problem
        assert!(
            self.progress_chars.len() >= 2,
            "at least 2 progress chars required"
        );
        self.char_width = width(&self.progress_chars);
        self
    }

    /// Sets the template string for the progress bar
    ///
    /// Review the [list of template keys](./index.html#templates) for more information.
    pub fn template(mut self, s: &str) -> ProgressStyle {
        self.template = s.into();
        self
    }

    /// Sets the finish behavior for the progress bar
    ///
    /// This behavior is invoked when [`ProgressBar`] or
    /// [`ProgressBarIter`] completes and
    /// [`ProgressBar::is_finished()`] is false.
    /// If you don't want the progress bar to be automatically finished then
    /// call `on_finish(None)`.
    ///
    /// [`ProgressBar`]: crate::ProgressBar
    /// [`ProgressBarIter`]: crate::ProgressBarIter
    /// [`ProgressBar::is_finished()`]: crate::ProgressBar::is_finished
    pub fn on_finish(mut self, finish: ProgressFinish) -> ProgressStyle {
        self.on_finish = finish;
        self
    }

    /// Returns the tick char for a given number
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_tick_str")]
    pub fn get_tick_char(&self, idx: u64) -> char {
        self.get_tick_str(idx).chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for a given number
    pub fn get_tick_str(&self, idx: u64) -> &str {
        &self.tick_strings[(idx as usize) % (self.tick_strings.len() - 1)]
    }

    /// Returns the tick char for the finished state
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_final_tick_str")]
    pub fn get_final_tick_char(&self) -> char {
        self.get_final_tick_str().chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for the finished state
    pub fn get_final_tick_str(&self) -> &str {
        &self.tick_strings[self.tick_strings.len() - 1]
    }

    /// Returns the finish behavior
    pub fn get_on_finish(&self) -> &ProgressFinish {
        &self.on_finish
    }

    pub(crate) fn format_bar(&self, fract: f32, width: usize, alt_style: Option<&Style>) -> String {
        // The number of clusters from progress_chars to write (rounding down).
        let width = width / self.char_width;
        // The number of full clusters (including a fractional component for a partially-full one).
        let fill = fract * width as f32;
        // The number of entirely full clusters (by truncating `fill`).
        let entirely_filled = fill as usize;
        // 1 if the bar is not entirely empty or full (meaning we need to draw the "current"
        // character between the filled and "to do" segment), 0 otherwise.
        let head = if fill > 0.0 && entirely_filled < width {
            1
        } else {
            0
        };

        let pb = self.progress_chars[0].repeat(entirely_filled);

        let cur = if head == 1 {
            // Number of fine-grained progress entries in progress_chars.
            let n = self.progress_chars.len().saturating_sub(2);
            let cur_char = if n <= 1 {
                // No fine-grained entries. 1 is the single "current" entry if we have one, the "to
                // do" entry if not.
                1
            } else {
                // Pick a fine-grained entry, ranging from the last one (n) if the fractional part
                // of fill is 0 to the first one (1) if the fractional part of fill is almost 1.
                n.saturating_sub((fill.fract() * n as f32) as usize)
            };
            self.progress_chars[cur_char].to_string()
        } else {
            "".into()
        };

        // Number of entirely empty clusters needed to fill the bar up to `width`.
        let bg = width.saturating_sub(entirely_filled).saturating_sub(head);
        let rest = self.progress_chars[self.progress_chars.len() - 1].repeat(bg);
        format!(
            "{}{}{}",
            pb,
            cur,
            alt_style.unwrap_or(&Style::new()).apply_to(rest)
        )
    }

    pub(crate) fn format_state(&self, state: &ProgressState) -> Vec<String> {
        let (pos, len) = state.position();
        let mut rv = vec![];

        for line in self.template.lines() {
            let mut wide_element = None;

            let s = expand_template(line, |var| match var.key {
                "wide_bar" => {
                    wide_element = Some(var.duplicate_for_key("bar"));
                    "\x00".into()
                }
                "bar" => self.format_bar(
                    state.fraction(),
                    var.width.unwrap_or(20),
                    var.alt_style.as_ref(),
                ),
                "spinner" => state.current_tick_str().to_string(),
                "wide_msg" => {
                    wide_element = Some(var.duplicate_for_key("msg"));
                    "\x00".into()
                }
                "msg" => state.message().to_string(),
                "prefix" => state.prefix().to_string(),
                "pos" => pos.to_string(),
                "len" => len.to_string(),
                "percent" => format!("{:.*}", 0, state.fraction() * 100f32),
                "bytes" => format!("{}", HumanBytes(state.pos)),
                "total_bytes" => format!("{}", HumanBytes(state.len)),
                "decimal_bytes" => format!("{}", DecimalBytes(state.pos)),
                "decimal_total_bytes" => format!("{}", DecimalBytes(state.len)),
                "binary_bytes" => format!("{}", BinaryBytes(state.pos)),
                "binary_total_bytes" => format!("{}", BinaryBytes(state.len)),
                "elapsed_precise" => format!("{}", FormattedDuration(state.started.elapsed())),
                "elapsed" => format!("{:#}", HumanDuration(state.started.elapsed())),
                "per_sec" => format!("{}/s", state.per_sec()),
                "bytes_per_sec" => format!("{}/s", HumanBytes(state.per_sec())),
                "binary_bytes_per_sec" => format!("{}/s", BinaryBytes(state.per_sec())),
                "eta_precise" => format!("{}", FormattedDuration(state.eta())),
                "eta" => format!("{:#}", HumanDuration(state.eta())),
                "duration_precise" => format!("{}", FormattedDuration(state.duration())),
                "duration" => format!("{:#}", HumanDuration(state.duration())),
                _ => "".into(),
            });

            rv.push(if let Some(ref var) = wide_element {
                let total_width = state.width();
                if var.key == "bar" {
                    let bar_width = total_width.saturating_sub(measure_text_width(&s));
                    s.replace(
                        "\x00",
                        &self.format_bar(state.fraction(), bar_width, var.alt_style.as_ref()),
                    )
                } else if var.key == "msg" {
                    let msg_width = total_width.saturating_sub(measure_text_width(&s));
                    let msg = pad_str(state.message(), msg_width, var.align, true);
                    s.replace(
                        "\x00",
                        if var.last_element {
                            msg.trim_end()
                        } else {
                            &msg
                        },
                    )
                } else {
                    unreachable!()
                }
            } else {
                s.to_string()
            });
        }

        rv
    }
}
<commit_msg>Move ProgressFinish down in the style module<commit_after>use console::{measure_text_width, Style};

use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration};
use crate::state::ProgressState;
use crate::utils::{expand_template, pad_str};
use std::borrow::Cow;

#[cfg(feature = "improved_unicode")]
use unicode_segmentation::UnicodeSegmentation;

/// Controls the rendering style of progress bars
#[derive(Clone, Debug)]
pub struct ProgressStyle {
    tick_strings: Vec<Box<str>>,
    progress_chars: Vec<Box<str>>,
    template: Box<str>,
    on_finish: ProgressFinish,
    // how unicode-big each char in progress_chars is
    char_width: usize,
}

#[cfg(feature = "improved_unicode")]
fn segment(s: &str) -> Vec<Box<str>> {
    UnicodeSegmentation::graphemes(s, true)
        .map(|s| s.into())
        .collect()
}

#[cfg(not(feature = "improved_unicode"))]
fn segment(s: &str) -> Vec<Box<str>> {
    s.chars().map(|x| x.to_string().into()).collect()
}

#[cfg(feature = "improved_unicode")]
fn measure(s: &str) -> usize {
    unicode_width::UnicodeWidthStr::width(s)
}

#[cfg(not(feature = "improved_unicode"))]
fn measure(s: &str) -> usize {
    s.chars().count()
}

/// finds the unicode-aware width of the passed grapheme cluters
/// panics on an empty parameter, or if the characters are not equal-width
fn width(c: &[Box<str>]) -> usize {
    c.iter()
        .map(|s| measure(s.as_ref()))
        .fold(None, |acc, new| {
            match acc {
                None => return Some(new),
                Some(old) => assert_eq!(old, new, "got passed un-equal width progress characters"),
            }
            acc
        })
        .unwrap()
}

impl ProgressStyle {
    /// Returns the default progress bar style for bars
    pub fn default_bar() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: "{wide_bar} {pos}/{len}".into(),
            on_finish: ProgressFinish::default(),
        }
    }

    /// Returns the default progress bar style for spinners
    pub fn default_spinner() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: "{spinner} {msg}".into(),
            on_finish: ProgressFinish::default(),
        }
    }

    /// Sets the tick character sequence for spinners
    pub fn tick_chars(mut self, s: &str) -> ProgressStyle {
        self.tick_strings = s.chars().map(|c| c.to_string().into()).collect();
        // Format bar will panic with some potentially confusing message, better to panic here
        // with a message explicitly informing of the problem
        assert!(
            self.tick_strings.len() >= 2,
            "at least 2 tick chars required"
        );
        self
    }

    /// Sets the tick string sequence for spinners
    pub fn tick_strings(mut self, s: &[&str]) -> ProgressStyle {
        self.tick_strings = s.iter().map(|s| s.to_string().into()).collect();
        // Format bar will panic with some potentially confusing message, better to panic here
        // with a message explicitly informing of the problem
        assert!(
            self.progress_chars.len() >= 2,
            "at least 2 tick strings required"
        );
        self
    }

    /// Sets the progress characters `(filled, current, to do)`
    ///
    /// You can pass more then three for a more detailed display.
    /// All passed grapheme clusters need to be of equal width.
    pub fn progress_chars(mut self, s: &str) -> ProgressStyle {
        self.progress_chars = segment(s);
        // Format bar will panic with some potentially confusing message, better to panic here
        // with a message explicitly informing of the problem
        assert!(
            self.progress_chars.len() >= 2,
            "at least 2 progress chars required"
        );
        self.char_width = width(&self.progress_chars);
        self
    }

    /// Sets the template string for the progress bar
    ///
    /// Review the [list of template keys](./index.html#templates) for more information.
    pub fn template(mut self, s: &str) -> ProgressStyle {
        self.template = s.into();
        self
    }

    /// Sets the finish behavior for the progress bar
    ///
    /// This behavior is invoked when [`ProgressBar`] or
    /// [`ProgressBarIter`] completes and
    /// [`ProgressBar::is_finished()`] is false.
    /// If you don't want the progress bar to be automatically finished then
    /// call `on_finish(None)`.
    ///
    /// [`ProgressBar`]: crate::ProgressBar
    /// [`ProgressBarIter`]: crate::ProgressBarIter
    /// [`ProgressBar::is_finished()`]: crate::ProgressBar::is_finished
    pub fn on_finish(mut self, finish: ProgressFinish) -> ProgressStyle {
        self.on_finish = finish;
        self
    }

    /// Returns the tick char for a given number
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_tick_str")]
    pub fn get_tick_char(&self, idx: u64) -> char {
        self.get_tick_str(idx).chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for a given number
    pub fn get_tick_str(&self, idx: u64) -> &str {
        &self.tick_strings[(idx as usize) % (self.tick_strings.len() - 1)]
    }

    /// Returns the tick char for the finished state
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_final_tick_str")]
    pub fn get_final_tick_char(&self) -> char {
        self.get_final_tick_str().chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for the finished state
    pub fn get_final_tick_str(&self) -> &str {
        &self.tick_strings[self.tick_strings.len() - 1]
    }

    /// Returns the finish behavior
    pub fn get_on_finish(&self) -> &ProgressFinish {
        &self.on_finish
    }

    pub(crate) fn format_bar(&self, fract: f32, width: usize, alt_style: Option<&Style>) -> String {
        // The number of clusters from progress_chars to write (rounding down).
        let width = width / self.char_width;
        // The number of full clusters (including a fractional component for a partially-full one).
        let fill = fract * width as f32;
        // The number of entirely full clusters (by truncating `fill`).
        let entirely_filled = fill as usize;
        // 1 if the bar is not entirely empty or full (meaning we need to draw the "current"
        // character between the filled and "to do" segment), 0 otherwise.
        let head = if fill > 0.0 && entirely_filled < width {
            1
        } else {
            0
        };

        let pb = self.progress_chars[0].repeat(entirely_filled);

        let cur = if head == 1 {
            // Number of fine-grained progress entries in progress_chars.
            let n = self.progress_chars.len().saturating_sub(2);
            let cur_char = if n <= 1 {
                // No fine-grained entries. 1 is the single "current" entry if we have one, the "to
                // do" entry if not.
                1
            } else {
                // Pick a fine-grained entry, ranging from the last one (n) if the fractional part
                // of fill is 0 to the first one (1) if the fractional part of fill is almost 1.
                n.saturating_sub((fill.fract() * n as f32) as usize)
            };
            self.progress_chars[cur_char].to_string()
        } else {
            "".into()
        };

        // Number of entirely empty clusters needed to fill the bar up to `width`.
        let bg = width.saturating_sub(entirely_filled).saturating_sub(head);
        let rest = self.progress_chars[self.progress_chars.len() - 1].repeat(bg);
        format!(
            "{}{}{}",
            pb,
            cur,
            alt_style.unwrap_or(&Style::new()).apply_to(rest)
        )
    }

    pub(crate) fn format_state(&self, state: &ProgressState) -> Vec<String> {
        let (pos, len) = state.position();
        let mut rv = vec![];

        for line in self.template.lines() {
            let mut wide_element = None;

            let s = expand_template(line, |var| match var.key {
                "wide_bar" => {
                    wide_element = Some(var.duplicate_for_key("bar"));
                    "\x00".into()
                }
                "bar" => self.format_bar(
                    state.fraction(),
                    var.width.unwrap_or(20),
                    var.alt_style.as_ref(),
                ),
                "spinner" => state.current_tick_str().to_string(),
                "wide_msg" => {
                    wide_element = Some(var.duplicate_for_key("msg"));
                    "\x00".into()
                }
                "msg" => state.message().to_string(),
                "prefix" => state.prefix().to_string(),
                "pos" => pos.to_string(),
                "len" => len.to_string(),
                "percent" => format!("{:.*}", 0, state.fraction() * 100f32),
                "bytes" => format!("{}", HumanBytes(state.pos)),
                "total_bytes" => format!("{}", HumanBytes(state.len)),
                "decimal_bytes" => format!("{}", DecimalBytes(state.pos)),
                "decimal_total_bytes" => format!("{}", DecimalBytes(state.len)),
                "binary_bytes" => format!("{}", BinaryBytes(state.pos)),
                "binary_total_bytes" => format!("{}", BinaryBytes(state.len)),
                "elapsed_precise" => format!("{}", FormattedDuration(state.started.elapsed())),
                "elapsed" => format!("{:#}", HumanDuration(state.started.elapsed())),
                "per_sec" => format!("{}/s", state.per_sec()),
                "bytes_per_sec" => format!("{}/s", HumanBytes(state.per_sec())),
                "binary_bytes_per_sec" => format!("{}/s", BinaryBytes(state.per_sec())),
                "eta_precise" => format!("{}", FormattedDuration(state.eta())),
                "eta" => format!("{:#}", HumanDuration(state.eta())),
                "duration_precise" => format!("{}", FormattedDuration(state.duration())),
                "duration" => format!("{:#}", HumanDuration(state.duration())),
                _ => "".into(),
            });

            rv.push(if let Some(ref var) = wide_element {
                let total_width = state.width();
                if var.key == "bar" {
                    let bar_width = total_width.saturating_sub(measure_text_width(&s));
                    s.replace(
                        "\x00",
                        &self.format_bar(state.fraction(), bar_width, var.alt_style.as_ref()),
                    )
                } else if var.key == "msg" {
                    let msg_width = total_width.saturating_sub(measure_text_width(&s));
                    let msg = pad_str(state.message(), msg_width, var.align, true);
                    s.replace(
                        "\x00",
                        if var.last_element {
                            msg.trim_end()
                        } else {
                            &msg
                        },
                    )
                } else {
                    unreachable!()
                }
            } else {
                s.to_string()
            });
        }

        rv
    }
}

/// Behavior of a progress bar when it is finished
///
/// This is invoked when a [`ProgressBar`] or [`ProgressBarIter`] completes and
/// [`ProgressBar::is_finished`] is false.
///
/// [`ProgressBar`]: crate::ProgressBar
/// [`ProgressBarIter`]: crate::ProgressBarIter
/// [`ProgressBar::is_finished`]: crate::ProgressBar::is_finished
#[derive(Clone, Debug)]
pub enum ProgressFinish {
    /// Finishes the progress bar and leaves the current message
    ///
    /// Same behavior as calling [`ProgressBar::finish()`](crate::ProgressBar::finish).
    AndLeave,
    /// Finishes the progress bar at current position and leaves the current message
    ///
    /// Same behavior as calling [`ProgressBar::finish_at_current_pos()`](crate::ProgressBar::finish_at_current_pos).
    AtCurrentPos,
    /// Finishes the progress bar and sets a message
    ///
    /// Same behavior as calling [`ProgressBar::finish_with_message()`](crate::ProgressBar::finish_with_message).
    WithMessage(Cow<'static, str>),
    /// Finishes the progress bar and completely clears it (this is the default)
    ///
    /// Same behavior as calling [`ProgressBar::finish_and_clear()`](crate::ProgressBar::finish_and_clear).
    AndClear,
    /// Finishes the progress bar and leaves the current message and progress
    ///
    /// Same behavior as calling [`ProgressBar::abandon()`](crate::ProgressBar::abandon).
    Abandon,
    /// Finishes the progress bar and sets a message, and leaves the current progress
    ///
    /// Same behavior as calling [`ProgressBar::abandon_with_message()`](crate::ProgressBar::abandon_with_message).
    AbandonWithMessage(Cow<'static, str>),
}

impl Default for ProgressFinish {
    fn default() -> Self {
        Self::AndClear
    }
}
<|endoftext|>


<commit_before>use variables::*;
use variables::LpExpression::*;
use std::rc::Rc;
use operations::LpOperations;
use problem::LpFileFormat;

#[test]
fn expressions_creation() {
    let ref a = LpInteger::new("a")
        .lower_bound(10.0)
        .upper_bound(20.0);
    let ref b = LpInteger::new("b");

    assert_eq!(a + b, AddExpr(Rc::new(ConsInt(a.clone())), Rc::new(ConsInt(b.clone()))));
}

#[test]
fn distributivity() {

    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    let test = a * (2 + b) * 3;
    let test2 = test.clone();
    assert_eq!( (2 * (2 + a)).to_lp_file_format(), "4 + 2 a");
    assert_eq!( ((2+a) * (2+b)).to_lp_file_format(), "4 + 2 a + 2 b + b a" );
    assert_eq!( test.to_lp_file_format(), "6 a + 3 a b" );
    assert_eq!( (10 * test).to_lp_file_format(), "60 a + 30 a b" );
    assert_eq!( ((c + 10) * test2).to_lp_file_format(), "6 c a + 3 c a b + 60 a + 30 a b" );

}

#[test]
fn associativity() {

    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    assert_eq!( (a + (b + 2)).to_lp_file_format(), "a + b + 2" );
    assert_eq!( ((a + b) + 2).to_lp_file_format(), "a + b + 2" );

    assert_eq!( (a + (b - 2)).to_lp_file_format(), "a + b - 2" );
    assert_eq!( ((a + b) - 2).to_lp_file_format(), "a + b - 2" );

    assert_eq!( (a - (b + 2)).to_lp_file_format(), "a - b - 2" );
    assert_eq!( ((a - b) + 2).to_lp_file_format(), "a - b + 2" );

    assert_eq!( (a - (b - 2)).to_lp_file_format(), "a - b + 2" );
    assert_eq!( ((a - b) - 2).to_lp_file_format(), "a - b - 2" );

    assert_eq!( (a - (b - 2) + c).to_lp_file_format(), "a - b + 2 + c" );
    assert_eq!( ((a - b) - 2 + c).to_lp_file_format(), "a - b - 2 + c" );
}

#[test]
fn trivial_rules() {
    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");

    assert_eq!( ((a+b) * 0).to_lp_file_format(), "0");
    assert_eq!( ( 0 * (a+b)).to_lp_file_format(), "0");
    // failed:
//    assert_eq!( ((a+b) + 0).to_lp_file_format(), "a + b");
    assert_eq!( (0 + (a+b)).to_lp_file_format(), "a + b");
    // failed:
//    assert_eq!( (2 + (a+b) + 3).to_lp_file_format(), "5 + a + b");

}

#[test]
fn expressions_to_lp_file_format() {
    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    assert_eq!((a + 2*b + c).to_lp_file_format(), "a + 2 b + c");
    assert_eq!((a + b*2 + c).to_lp_file_format(), "a + 2 b + c");
    assert_eq!((a + b*2 + 3 * 2 * c).to_lp_file_format(), "a + 2 b + 6 c");
    assert_eq!((a + 2).to_lp_file_format(), "a + 2");
    assert_eq!((2*a + 2*b -4*c).to_lp_file_format(), "2 a + 2 b - 4 c");
    assert_eq!((-2*a).to_lp_file_format(), "-2 a");

}

#[test]
fn constraints_to_lp_file_format() {
    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    assert_eq!((a+b).equal(10).to_lp_file_format(), "a + b = 10");
    assert_eq!((2*a + b).ge(10).to_lp_file_format(), "2 a + b >= 10");
    assert_eq!((2*a + b + 20).ge(c).to_lp_file_format(), "2 a + b - c >= -20");
    assert_eq!((-a).ge(10).to_lp_file_format(), "-a >= 10");
    assert_eq!((2*a - 20 + b).ge(-c).to_lp_file_format(), "2 a + b + c >= 20");
}

#[test]
fn test_readme_example() {
    use problem::{LpObjective, LpProblem};
    use operations::{LpOperations};
    use variables::LpInteger;
    use solvers::{SolverTrait, CbcSolver};

    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    let mut problem = LpProblem::new("One Problem", LpObjective::Maximize);
    problem += 10.0 * a + 20.0 * b;

    problem += (500*a + 1200*b + 1500*c).le(10000);
    problem += (a).le(b);

    let solver = CbcSolver::new();

    match solver.run(&problem) {
        Ok((status, res)) => {
            println!("Status {:?}", status);
            for (name, value) in res.iter() {
                println!("value of {} = {}", name, value);
            }
        },
        Err(msg) => println!("{}", msg),
    }

    let output1 = "\\ One Problem

Maximize
  obj: 10 a + 20 b

Subject To
  c1: 500 a + 1200 b + 1500 c <= 10000
  c2: a - b <= 0

".to_string();
    let output2 = problem.to_lp_file_format();
    let output2 = output2.split("Generals").collect::<Vec<&str>>();
    let output2 = output2[0];
    assert_eq!(output1, output2);
}
<commit_msg>Add new rules for expressions<commit_after>use variables::*;
use variables::LpExpression::*;
use std::rc::Rc;
use operations::LpOperations;
use problem::LpFileFormat;

#[test]
fn expressions_creation() {
    let ref a = LpInteger::new("a")
        .lower_bound(10.0)
        .upper_bound(20.0);
    let ref b = LpInteger::new("b");

    assert_eq!(a + b, AddExpr(Rc::new(ConsInt(a.clone())), Rc::new(ConsInt(b.clone()))));
}

#[test]
fn distributivity() {

    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    let test = a * (2 + b) * 3;
    let test2 = test.clone();
    assert_eq!( (2 * (2 + a)).to_lp_file_format(), "4 + 2 a");
    assert_eq!( ((2+a) * (2+b)).to_lp_file_format(), "4 + 2 a + 2 b + b a" );
    assert_eq!( test.to_lp_file_format(), "6 a + 3 a b" );
    assert_eq!( (10 * test).to_lp_file_format(), "60 a + 30 a b" );
    assert_eq!( ((c + 10) * test2).to_lp_file_format(), "6 c a + 3 c a b + 60 a + 30 a b" );

}

#[test]
fn associativity() {

    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    assert_eq!( (a + (b + 2)).to_lp_file_format(), "2 + a + b" );
    assert_eq!( ((a + b) + 2).to_lp_file_format(), "2 + a + b" );

    assert_eq!( (a + (b - 2)).to_lp_file_format(), "-2 + a + b" );
    assert_eq!( ((a + b) - 2).to_lp_file_format(), "-2 + a + b" );

    assert_eq!( (a - (b + 2)).to_lp_file_format(), "-2 + a - b" );
    assert_eq!( ((a - b) + 2).to_lp_file_format(), "2 + a - b" );

    assert_eq!( (a - (b - 2)).to_lp_file_format(), "2 + a - b" );
    assert_eq!( ((a - b) - 2).to_lp_file_format(), "-2 + a - b" );

    assert_eq!( (a - (b - 2) + c).to_lp_file_format(), "2 + a - b + c" );
    assert_eq!( ((a - b) - 2 + c).to_lp_file_format(), "-2 + a - b + c" );
}

#[test]
fn literal_first_with_accumulation() {
    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    assert_eq!( (a + 1 + b + 2 + c + 3 + a + 4).to_lp_file_format(), "10 + a + b + c + a");
    assert_eq!( (a - 1 + b - 2 - c + 3 + a - 4).to_lp_file_format(), "-4 + a + b - c + a");
    assert_eq!( (a + b + 1 - c - a - 3).to_lp_file_format(), "-2 + a + b - c - a");
    assert_eq!( (a + b + (c - 1) * 2 - a - 3).to_lp_file_format(), "-5 + a + b + 2 c - a");
    assert_eq!( (a + b + (1 - c) * 2 - a - 3).to_lp_file_format(), "-1 + a + b - 2 c - a");
    assert_eq!( (2*(a + 5)).to_lp_file_format(), "10 + 2 a");
    assert_eq!( ((2+b)*(a + 5)).to_lp_file_format(), "10 + 2 a + a b + 5 b");
    assert_eq!( (2 + (a+b) + 3).to_lp_file_format(), "5 + a + b");

}

#[test]
fn trivial_rules() {
    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");

    assert_eq!( ((a+b) * 0).to_lp_file_format(), "0");
    assert_eq!( ( 0 * (a+b)).to_lp_file_format(), "0");
    assert_eq!( ((a+b) + 0).to_lp_file_format(), "a + b");
    assert_eq!( (0 + (a+b)).to_lp_file_format(), "a + b");

}

#[test]
fn expressions_to_lp_file_format() {
    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    // Expressions
    assert_eq!((a + 2*b + c).to_lp_file_format(), "a + 2 b + c");
    assert_eq!((a + b*2 + c).to_lp_file_format(), "a + 2 b + c");
    assert_eq!((a + b*2 + 3 * 2 * c).to_lp_file_format(), "a + 2 b + 6 c");
    assert_eq!((a + 2).to_lp_file_format(), "2 + a");
    assert_eq!((2*a + 2*b -4*c).to_lp_file_format(), "2 a + 2 b - 4 c");
    assert_eq!((-2*a).to_lp_file_format(), "-2 a");

    // Constraints
    assert_eq!((a+b).equal(10).to_lp_file_format(), "a + b = 10");
    assert_eq!((2*a + b).ge(10).to_lp_file_format(), "2 a + b >= 10");
    assert_eq!((2*a + b + 20).ge(c).to_lp_file_format(), "2 a + b - c >= -20");
    assert_eq!((-a).ge(10).to_lp_file_format(), "-a >= 10");
    assert_eq!((2*a - 20 + b).ge(-c).to_lp_file_format(), "2 a + b + c >= 20");
}

#[test]
fn test_readme_example() {
    use problem::{LpObjective, LpProblem};
    use operations::{LpOperations};
    use variables::LpInteger;
    use solvers::{SolverTrait, CbcSolver};

    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    let mut problem = LpProblem::new("One Problem", LpObjective::Maximize);
    problem += 10.0 * a + 20.0 * b;

    problem += (500*a + 1200*b + 1500*c).le(10000);
    problem += (a).le(b);

    let solver = CbcSolver::new();

    match solver.run(&problem) {
        Ok((status, res)) => {
            println!("Status {:?}", status);
            for (name, value) in res.iter() {
                println!("value of {} = {}", name, value);
            }
        },
        Err(msg) => println!("{}", msg),
    }

    let output1 = "\\ One Problem

Maximize
  obj: 10 a + 20 b

Subject To
  c1: 500 a + 1200 b + 1500 c <= 10000
  c2: a - b <= 0

".to_string();
    let output2 = problem.to_lp_file_format();
    let output2 = output2.split("Generals").collect::<Vec<&str>>();
    let output2 = output2[0];
    assert_eq!(output1, output2);
}
<|endoftext|>