<commit_before>// Copyright 2018 Developers of the Rand project.
// Copyright 2016-2017 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The binomial distribution.

use Rng;
use distributions::{Distribution, Cauchy};
use distributions::utils::log_gamma;

/// The binomial distribution `Binomial(n, p)`.
///
/// This distribution has density function:
/// `f(k) = n!/(k! (n-k)!) p^k (1-p)^(n-k)` for `k >= 0`.
///
/// # Example
///
/// ```
/// use rand::distributions::{Binomial, Distribution};
///
/// let bin = Binomial::new(20, 0.3);
/// let v = bin.sample(&mut rand::thread_rng());
/// println!("{} is from a binomial distribution", v);
/// ```
#[derive(Clone, Copy, Debug)]
pub struct Binomial {
    /// Number of trials.
    n: u64,
    /// Probability of success.
    p: f64,
}

impl Binomial {
    /// Construct a new `Binomial` with the given shape parameters `n` (number
    /// of trials) and `p` (probability of success).
    ///
    /// Panics if `p < 0` or `p > 1`.
    pub fn new(n: u64, p: f64) -> Binomial {
        assert!(p >= 0.0, "Binomial::new called with p < 0");
        assert!(p <= 1.0, "Binomial::new called with p > 1");
        Binomial { n, p }
    }
}

/// Raise a `base` to the power of `exp`, using exponentiation by squaring.
///
/// This implementation is based on the one in the `num_traits` crate. It is
/// slightly modified to accept `u64` exponents.
fn pow(mut base: f64, mut exp: u64) -> f64 {
    if exp == 0 {
        return 1.;
    }

    while exp & 1 == 0 {
        base *= base;
        exp >>= 1;
    }

    let mut acc = base;
    while exp > 1 {
        exp >>= 1;
        base *= base;
        if exp & 1 == 1 {
            acc *= base;
        }
    }
    acc
}

impl Distribution<u64> for Binomial {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 {
        // Handle these values directly.
        if self.p == 0.0 {
            return 0;
        } else if self.p == 1.0 {
            return self.n;
        }

        // binomial distribution is symmetrical with respect to p -> 1-p, k -> n-k
        // switch p so that it is less than 0.5 - this allows for lower expected values
        // we will just invert the result at the end
        let p = if self.p <= 0.5 {
            self.p
        } else {
            1.0 - self.p
        };

        let result;

        // For small n * min(p, 1 - p), the BINV algorithm based on the inverse
        // transformation of the binomial distribution is more efficient:
        //
        // Voratas Kachitvichyanukul and Bruce W. Schmeiser. 1988. Binomial
        // random variate generation. Commun. ACM 31, 2 (February 1988),
        // 216-222. http://dx.doi.org/10.1145/42372.42381
        if (self.n as f64) * p < 10. {
            let q = 1. - p;
            let s = p / q;
            let a = ((self.n + 1) as f64) * s;
            let mut r = pow(q, self.n);
            let mut u: f64 = rng.gen();
            let mut x = 0;
            while u > r as f64 {
                u -= r;
                x += 1;
                r *= a / (x as f64) - s;
            }
            result = x;
        } else {
            // FIXME: Using the BTPE algorithm is probably faster.

            // prepare some cached values
            let float_n = self.n as f64;
            let ln_fact_n = log_gamma(float_n + 1.0);
            let pc = 1.0 - p;
            let log_p = p.ln();
            let log_pc = pc.ln();
            let expected = self.n as f64 * p;
            let sq = (expected * (2.0 * pc)).sqrt();
            let mut lresult;

            // we use the Cauchy distribution as the comparison distribution
            // f(x) ~ 1/(1+x^2)
            let cauchy = Cauchy::new(0.0, 1.0);
            loop {
                let mut comp_dev: f64;
                loop {
                    // draw from the Cauchy distribution
                    comp_dev = rng.sample(cauchy);
                    // shift the peak of the comparison ditribution
                    lresult = expected + sq * comp_dev;
                    // repeat the drawing until we are in the range of possible values
                    if lresult >= 0.0 && lresult < float_n + 1.0 {
                        break;
                    }
                }

                // the result should be discrete
                lresult = lresult.floor();

                let log_binomial_dist = ln_fact_n - log_gamma(lresult+1.0) -
                    log_gamma(float_n - lresult + 1.0) + lresult*log_p + (float_n - lresult)*log_pc;
                // this is the binomial probability divided by the comparison probability
                // we will generate a uniform random value and if it is larger than this,
                // we interpret it as a value falling out of the distribution and repeat
                let comparison_coeff = (log_binomial_dist.exp() * sq) * (1.2 * (1.0 + comp_dev*comp_dev));

                if comparison_coeff >= rng.gen() {
                    break;
                }
            }
            result = lresult as u64;
        }

        // invert the result for p < 0.5
        if p != self.p {
            self.n - result
        } else {
            result
        }
    }
}

#[cfg(test)]
mod test {
    use Rng;
    use distributions::Distribution;
    use super::Binomial;

    fn test_binomial_mean_and_variance<R: Rng>(n: u64, p: f64, rng: &mut R) {
        let binomial = Binomial::new(n, p);

        let expected_mean = n as f64 * p;
        let expected_variance = n as f64 * p * (1.0 - p);

        let mut results = [0.0; 1000];
        for i in results.iter_mut() { *i = binomial.sample(rng) as f64; }

        let mean = results.iter().sum::<f64>() / results.len() as f64;
        assert!((mean as f64 - expected_mean).abs() < expected_mean / 50.0);

        let variance =
            results.iter().map(|x| (x - mean) * (x - mean)).sum::<f64>()
            / results.len() as f64;
        assert!((variance - expected_variance).abs() < expected_variance / 10.0);
    }

    #[test]
    fn test_binomial() {
        let mut rng = ::test::rng(351);
        test_binomial_mean_and_variance(150, 0.1, &mut rng);
        test_binomial_mean_and_variance(70, 0.6, &mut rng);
        test_binomial_mean_and_variance(40, 0.5, &mut rng);
        test_binomial_mean_and_variance(20, 0.7, &mut rng);
        test_binomial_mean_and_variance(20, 0.5, &mut rng);
    }

    #[test]
    fn test_binomial_end_points() {
        let mut rng = ::test::rng(352);
        assert_eq!(rng.sample(Binomial::new(20, 0.0)), 0);
        assert_eq!(rng.sample(Binomial::new(20, 1.0)), 20);
    }

    #[test]
    #[should_panic]
    fn test_binomial_invalid_lambda_neg() {
        Binomial::new(20, -10.0);
    }
}
<commit_msg>Binomial sampling: limit max n and use powi<commit_after>// Copyright 2018 Developers of the Rand project.
// Copyright 2016-2017 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The binomial distribution.

use Rng;
use distributions::{Distribution, Cauchy};
use distributions::utils::log_gamma;

/// The binomial distribution `Binomial(n, p)`.
///
/// This distribution has density function:
/// `f(k) = n!/(k! (n-k)!) p^k (1-p)^(n-k)` for `k >= 0`.
///
/// # Example
///
/// ```
/// use rand::distributions::{Binomial, Distribution};
///
/// let bin = Binomial::new(20, 0.3);
/// let v = bin.sample(&mut rand::thread_rng());
/// println!("{} is from a binomial distribution", v);
/// ```
#[derive(Clone, Copy, Debug)]
pub struct Binomial {
    /// Number of trials.
    n: u64,
    /// Probability of success.
    p: f64,
}

impl Binomial {
    /// Construct a new `Binomial` with the given shape parameters `n` (number
    /// of trials) and `p` (probability of success).
    ///
    /// Panics if `p < 0` or `p > 1`.
    pub fn new(n: u64, p: f64) -> Binomial {
        assert!(p >= 0.0, "Binomial::new called with p < 0");
        assert!(p <= 1.0, "Binomial::new called with p > 1");
        Binomial { n, p }
    }
}

impl Distribution<u64> for Binomial {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 {
        // Handle these values directly.
        if self.p == 0.0 {
            return 0;
        } else if self.p == 1.0 {
            return self.n;
        }

        // binomial distribution is symmetrical with respect to p -> 1-p, k -> n-k
        // switch p so that it is less than 0.5 - this allows for lower expected values
        // we will just invert the result at the end
        let p = if self.p <= 0.5 {
            self.p
        } else {
            1.0 - self.p
        };

        let result;

        // For small n * min(p, 1 - p), the BINV algorithm based on the inverse
        // transformation of the binomial distribution is more efficient:
        //
        // Voratas Kachitvichyanukul and Bruce W. Schmeiser. 1988. Binomial
        // random variate generation. Commun. ACM 31, 2 (February 1988),
        // 216-222. http://dx.doi.org/10.1145/42372.42381
        if (self.n as f64) * p < 10. && self.n <= (::std::i32::MAX as u64) {
            let q = 1. - p;
            let s = p / q;
            let a = ((self.n + 1) as f64) * s;
            let mut r = q.powi(self.n as i32);
            let mut u: f64 = rng.gen();
            let mut x = 0;
            while u > r as f64 {
                u -= r;
                x += 1;
                r *= a / (x as f64) - s;
            }
            result = x;
        } else {
            // FIXME: Using the BTPE algorithm is probably faster.

            // prepare some cached values
            let float_n = self.n as f64;
            let ln_fact_n = log_gamma(float_n + 1.0);
            let pc = 1.0 - p;
            let log_p = p.ln();
            let log_pc = pc.ln();
            let expected = self.n as f64 * p;
            let sq = (expected * (2.0 * pc)).sqrt();
            let mut lresult;

            // we use the Cauchy distribution as the comparison distribution
            // f(x) ~ 1/(1+x^2)
            let cauchy = Cauchy::new(0.0, 1.0);
            loop {
                let mut comp_dev: f64;
                loop {
                    // draw from the Cauchy distribution
                    comp_dev = rng.sample(cauchy);
                    // shift the peak of the comparison ditribution
                    lresult = expected + sq * comp_dev;
                    // repeat the drawing until we are in the range of possible values
                    if lresult >= 0.0 && lresult < float_n + 1.0 {
                        break;
                    }
                }

                // the result should be discrete
                lresult = lresult.floor();

                let log_binomial_dist = ln_fact_n - log_gamma(lresult+1.0) -
                    log_gamma(float_n - lresult + 1.0) + lresult*log_p + (float_n - lresult)*log_pc;
                // this is the binomial probability divided by the comparison probability
                // we will generate a uniform random value and if it is larger than this,
                // we interpret it as a value falling out of the distribution and repeat
                let comparison_coeff = (log_binomial_dist.exp() * sq) * (1.2 * (1.0 + comp_dev*comp_dev));

                if comparison_coeff >= rng.gen() {
                    break;
                }
            }
            result = lresult as u64;
        }

        // invert the result for p < 0.5
        if p != self.p {
            self.n - result
        } else {
            result
        }
    }
}

#[cfg(test)]
mod test {
    use Rng;
    use distributions::Distribution;
    use super::Binomial;

    fn test_binomial_mean_and_variance<R: Rng>(n: u64, p: f64, rng: &mut R) {
        let binomial = Binomial::new(n, p);

        let expected_mean = n as f64 * p;
        let expected_variance = n as f64 * p * (1.0 - p);

        let mut results = [0.0; 1000];
        for i in results.iter_mut() { *i = binomial.sample(rng) as f64; }

        let mean = results.iter().sum::<f64>() / results.len() as f64;
        assert!((mean as f64 - expected_mean).abs() < expected_mean / 50.0);

        let variance =
            results.iter().map(|x| (x - mean) * (x - mean)).sum::<f64>()
            / results.len() as f64;
        assert!((variance - expected_variance).abs() < expected_variance / 10.0);
    }

    #[test]
    fn test_binomial() {
        let mut rng = ::test::rng(351);
        test_binomial_mean_and_variance(150, 0.1, &mut rng);
        test_binomial_mean_and_variance(70, 0.6, &mut rng);
        test_binomial_mean_and_variance(40, 0.5, &mut rng);
        test_binomial_mean_and_variance(20, 0.7, &mut rng);
        test_binomial_mean_and_variance(20, 0.5, &mut rng);
    }

    #[test]
    fn test_binomial_end_points() {
        let mut rng = ::test::rng(352);
        assert_eq!(rng.sample(Binomial::new(20, 0.0)), 0);
        assert_eq!(rng.sample(Binomial::new(20, 1.0)), 20);
    }

    #[test]
    #[should_panic]
    fn test_binomial_invalid_lambda_neg() {
        Binomial::new(20, -10.0);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate eve;
extern crate test;
extern crate core;

use test::Bencher;
use eve::interpreter::*;
use std::num::Float;
use core::num::ToPrimitive;

fn main() {

	let c1 = Call{op: Op::StrSplit, args: exprvec!["The Quick Brown Fox Jumps Over The Lazy Dog"]};
	let result = calculate(&c1.to_expr());

	println!("{:?}",result);
}

#[test]
fn opstest() {

	// Test Some General Math Ops: (((1.3 + 2) * 3) + (7 - 4) / 10) ^ 2.5
	let c1 = Call{op: Op::Add, args: exprvec![1.3,2]};				// C1 = 1.3 + 2
	let c2 = Call{op: Op::Multiply, args: exprvec![c1,3]};			// C2 = C1 * 3
	let c3 = Call{op: Op::Subtract, args: exprvec![7,4]};			// C3 = 7 - 4
	let c4 = Call{op: Op::Divide, args: exprvec![c3,10]};			// C4 = C3 / 10
	let c5 = Call{op: Op::Add, args: exprvec![c2,c4]};				// C5 = C2 + C4
	let c6 = Call{op: Op::Exponentiate, args: exprvec![c5,2.5]};	// C6 = C5 ^ 2.5
	let result = calculate(&c6.to_expr());
	assert_eq!(result.to_f64().unwrap(),(((1.3f64 + 2f64) * 3f64) + (7f64 - 4f64) / 10f64).powf(2.5f64));

}

#[test]
fn stringtest() {

	// Test a text replacement
	let c1 = Call{op: Op::StrReplace, args: exprvec!["Hello World","l","q"] };
	let result = calculate(&c1.to_expr());
	assert_eq!(result.to_string(),"Heqqo Worqd".to_string());

}

#[test]
fn trigtest() {

	let pi = std::f64::consts::PI;

	// sin
	let c1 = Call{op: Op::Sin, args: exprvec![pi]};
	let result = calculate(&c1.to_expr());
	assert_eq!(result.to_f64().unwrap(),pi.sin());

	// cos
	let c1 = Call{op: Op::Cos, args: exprvec![pi]};
	let result = calculate(&c1.to_expr());
	assert_eq!(result.to_f64().unwrap(),pi.cos());

	// tan
	let c1 = Call{op: Op::Tan, args: exprvec![pi]};
	let result = calculate(&c1.to_expr());
	assert_eq!(result.to_f64().unwrap(),pi.tan());

	// atan2
	let c1 = Call{op: Op::ATan2, args: exprvec![1.2,2.3]};
	let result = calculate(&c1.to_expr());
	assert_eq!(result.to_f64().unwrap(),1.2f64.atan2(2.3f64));
}

#[test]
fn bigmathtest() {

	let wa = 1.5;
	let ma = 2.3;
	let va = 11.3;
	let ga = 0.34;
	let li = 3.45;
	let mu = 1.50;
	let gd = 3.14;
	let g = 9.8;
	let wx = 0.04;
	let ps = 0.013;
	let rh = 1.74;

	let c1 = Call{op: Op::Sin, args: exprvec![ga]}; 			// sin(ga)
	let c2 = Call{op: Op::Exponentiate, args: exprvec![c1,2]}; 	// sin(ga)^2
	let c3 = Call{op: Op::Multiply, args: exprvec![wx,c2]}; 	// wx*sin(ga)^2
	let c4 = Call{op: Op::Sin, args: exprvec![ps]}; 			// sin(ps)
	let c5 = Call{op: Op::Multiply, args: exprvec![c3,c4]}; 	// wx*sin(ga)^2*sin(ps)

	let c6 = Call{op: Op::Cos, args: exprvec![ga]}; 			// cos(ga)
	let c7 = Call{op: Op::Multiply, args: exprvec![g,c6]}; 		// g*cos(ga)
	let c8 = Call{op: Op::Divide, args: exprvec![c7,va]}; 		// g*cos(ga)/va
	let c9 = Call{op: Op::Add, args: exprvec![gd,c8]}; 			// gd+g*cos(ga)/va

	let c10 = Call{op: Op::Subtract, args: exprvec![c9,c5]}; 	// (gd+g*cos(ga)/va) - (wx*sin(ga)^2*sin(ps))

	let c11 = Call{op: Op::Multiply, args: exprvec![wa,rh]};	// wa*rh
	let c12 = Call{op: Op::Multiply, args: exprvec![c11,va]};	// wa*rh*va
	let c13 = Call{op: Op::Cos, args: exprvec![mu]}; 			// cos(mu)
	let c14 = Call{op: Op::Multiply, args: exprvec![c12,c13]};	// wa*rh*va*cos(mu)

	let c15 = Call{op: Op::Multiply, args: exprvec![2,ma]};		// 2*ma

	let c16 = Call{op: Op::Divide, args: exprvec![c15,c14]};	// 2*ma/wa*rh*va*cos(mu)

	let c17 = Call{op: Op::Multiply, args: exprvec![c16,c10]};	// (2*ma/wa*rh*va*cos(mu)) * (gd+g*cos(ga)/va) - (wx*sin(ga)^2*sin(ps))

	let result = calculate(&c17.to_expr());

	assert_eq!(result.to_f64().unwrap(),(2f64*ma/(wa*rh*va*mu.cos())*(gd+g*ga.cos()/va-wx*(ga.sin().powf(2f64))*ps.sin())));

}

#[bench]
fn opsbench(b: &mut Bencher) {

	// Test Some General Math Ops: (((1 + 2) * 3) + (7 - 4) / 10) ^ 2

	let c1 = Call{op: Op::Add, args: exprvec![1.3,2]};				// C1 = 1.3 + 2
	let c2 = Call{op: Op::Multiply, args: exprvec![c1,3]};			// C2 = C1 * 3
	let c3 = Call{op: Op::Subtract, args: exprvec![7,4]};			// C3 = 7 - 4
	let c4 = Call{op: Op::Divide, args: exprvec![c3,10]};			// C4 = C3 / 10
	let c5 = Call{op: Op::Add, args: exprvec![c2,c4]};				// C5 = C2 + C4
	let c6 = Call{op: Op::Exponentiate, args: exprvec![c5,2.5]};	// C6 = C5 ^ 2.5
	let e1 = c6.to_expr();
	b.iter(|| {
		calculate(&e1)
	});
}

#[bench]
fn bigmathbench(b: &mut Bencher) {

	let wa = 1.5;
	let ma = 2.3;
	let va = 11.3;
	let ga = 0.34;
	let mu = 1.50;
	let gd = 3.14;
	let g = 9.8;
	let wx = 0.04;
	let ps = 0.013;
	let rh = 1.74;

	let c1 = Call{op: Op::Sin, args: exprvec![ga]}; 			// sin(ga)
	let c2 = Call{op: Op::Exponentiate, args: exprvec![c1,2]}; 	// sin(ga)^2
	let c3 = Call{op: Op::Multiply, args: exprvec![wx,c2]}; 	// wx*sin(ga)^2
	let c4 = Call{op: Op::Sin, args: exprvec![ps]}; 			// sin(ps)
	let c5 = Call{op: Op::Multiply, args: exprvec![c3,c4]}; 	// wx*sin(ga)^2*sin(ps)

	let c6 = Call{op: Op::Cos, args: exprvec![ga]}; 			// cos(ga)
	let c7 = Call{op: Op::Multiply, args: exprvec![g,c6]}; 		// g*cos(ga)
	let c8 = Call{op: Op::Divide, args: exprvec![c7,va]}; 		// g*cos(ga)/va
	let c9 = Call{op: Op::Add, args: exprvec![gd,c8]}; 			// gd+g*cos(ga)/va

	let c10 = Call{op: Op::Subtract, args: exprvec![c9,c5]}; 	// (gd+g*cos(ga)/va) - (wx*sin(ga)^2*sin(ps))

	let c11 = Call{op: Op::Multiply, args: exprvec![wa,rh]};	// wa*rh
	let c12 = Call{op: Op::Multiply, args: exprvec![c11,va]};	// wa*rh*va
	let c13 = Call{op: Op::Cos, args: exprvec![mu]}; 			// cos(mu)
	let c14 = Call{op: Op::Multiply, args: exprvec![c12,c13]};	// wa*rh*va*cos(mu)

	let c15 = Call{op: Op::Multiply, args: exprvec![2,ma]};		// 2*ma

	let c16 = Call{op: Op::Divide, args: exprvec![c15,c14]};	// 2*ma/wa*rh*va*cos(mu)

	let c17 = Call{op: Op::Multiply, args: exprvec![c16,c10]};	// (2*ma/wa*rh*va*cos(mu)) * (gd+g*cos(ga)/va) - (wx*sin(ga)^2*sin(ps))

	let e1 = c17.to_expr();

	b.iter(|| {
		calculate(&e1)
	});

}<commit_msg>More rustup<commit_after>#![feature(core)]
#![feature(test)]

#[macro_use]
extern crate eve;
extern crate test;
extern crate core;

use test::Bencher;
use eve::interpreter::*;
use std::num::Float;
use core::num::ToPrimitive;

fn main() {

	let c1 = Call{op: Op::StrSplit, args: exprvec!["The Quick Brown Fox Jumps Over The Lazy Dog"]};
	let result = calculate(&c1.to_expr());

	println!("{:?}",result);
}

#[test]
fn opstest() {

	// Test Some General Math Ops: (((1.3 + 2) * 3) + (7 - 4) / 10) ^ 2.5
	let c1 = Call{op: Op::Add, args: exprvec![1.3,2]};				// C1 = 1.3 + 2
	let c2 = Call{op: Op::Multiply, args: exprvec![c1,3]};			// C2 = C1 * 3
	let c3 = Call{op: Op::Subtract, args: exprvec![7,4]};			// C3 = 7 - 4
	let c4 = Call{op: Op::Divide, args: exprvec![c3,10]};			// C4 = C3 / 10
	let c5 = Call{op: Op::Add, args: exprvec![c2,c4]};				// C5 = C2 + C4
	let c6 = Call{op: Op::Exponentiate, args: exprvec![c5,2.5]};	// C6 = C5 ^ 2.5
	let result = calculate(&c6.to_expr());
	assert_eq!(result.to_f64().unwrap(),(((1.3f64 + 2f64) * 3f64) + (7f64 - 4f64) / 10f64).powf(2.5f64));

}

#[test]
fn stringtest() {

	// Test a text replacement
	let c1 = Call{op: Op::StrReplace, args: exprvec!["Hello World","l","q"] };
	let result = calculate(&c1.to_expr());
	assert_eq!(result.to_string(),"Heqqo Worqd".to_string());

}

#[test]
fn trigtest() {

	let pi = std::f64::consts::PI;

	// sin
	let c1 = Call{op: Op::Sin, args: exprvec![pi]};
	let result = calculate(&c1.to_expr());
	assert_eq!(result.to_f64().unwrap(),pi.sin());

	// cos
	let c1 = Call{op: Op::Cos, args: exprvec![pi]};
	let result = calculate(&c1.to_expr());
	assert_eq!(result.to_f64().unwrap(),pi.cos());

	// tan
	let c1 = Call{op: Op::Tan, args: exprvec![pi]};
	let result = calculate(&c1.to_expr());
	assert_eq!(result.to_f64().unwrap(),pi.tan());

	// atan2
	let c1 = Call{op: Op::ATan2, args: exprvec![1.2,2.3]};
	let result = calculate(&c1.to_expr());
	assert_eq!(result.to_f64().unwrap(),1.2f64.atan2(2.3f64));
}

#[test]
fn bigmathtest() {

	let wa = 1.5;
	let ma = 2.3;
	let va = 11.3;
	let ga = 0.34;
	let li = 3.45;
	let mu = 1.50;
	let gd = 3.14;
	let g = 9.8;
	let wx = 0.04;
	let ps = 0.013;
	let rh = 1.74;

	let c1 = Call{op: Op::Sin, args: exprvec![ga]}; 			// sin(ga)
	let c2 = Call{op: Op::Exponentiate, args: exprvec![c1,2]}; 	// sin(ga)^2
	let c3 = Call{op: Op::Multiply, args: exprvec![wx,c2]}; 	// wx*sin(ga)^2
	let c4 = Call{op: Op::Sin, args: exprvec![ps]}; 			// sin(ps)
	let c5 = Call{op: Op::Multiply, args: exprvec![c3,c4]}; 	// wx*sin(ga)^2*sin(ps)

	let c6 = Call{op: Op::Cos, args: exprvec![ga]}; 			// cos(ga)
	let c7 = Call{op: Op::Multiply, args: exprvec![g,c6]}; 		// g*cos(ga)
	let c8 = Call{op: Op::Divide, args: exprvec![c7,va]}; 		// g*cos(ga)/va
	let c9 = Call{op: Op::Add, args: exprvec![gd,c8]}; 			// gd+g*cos(ga)/va

	let c10 = Call{op: Op::Subtract, args: exprvec![c9,c5]}; 	// (gd+g*cos(ga)/va) - (wx*sin(ga)^2*sin(ps))

	let c11 = Call{op: Op::Multiply, args: exprvec![wa,rh]};	// wa*rh
	let c12 = Call{op: Op::Multiply, args: exprvec![c11,va]};	// wa*rh*va
	let c13 = Call{op: Op::Cos, args: exprvec![mu]}; 			// cos(mu)
	let c14 = Call{op: Op::Multiply, args: exprvec![c12,c13]};	// wa*rh*va*cos(mu)

	let c15 = Call{op: Op::Multiply, args: exprvec![2,ma]};		// 2*ma

	let c16 = Call{op: Op::Divide, args: exprvec![c15,c14]};	// 2*ma/wa*rh*va*cos(mu)

	let c17 = Call{op: Op::Multiply, args: exprvec![c16,c10]};	// (2*ma/wa*rh*va*cos(mu)) * (gd+g*cos(ga)/va) - (wx*sin(ga)^2*sin(ps))

	let result = calculate(&c17.to_expr());

	assert_eq!(result.to_f64().unwrap(),(2f64*ma/(wa*rh*va*mu.cos())*(gd+g*ga.cos()/va-wx*(ga.sin().powf(2f64))*ps.sin())));

}

#[bench]
fn opsbench(b: &mut Bencher) {

	// Test Some General Math Ops: (((1 + 2) * 3) + (7 - 4) / 10) ^ 2

	let c1 = Call{op: Op::Add, args: exprvec![1.3,2]};				// C1 = 1.3 + 2
	let c2 = Call{op: Op::Multiply, args: exprvec![c1,3]};			// C2 = C1 * 3
	let c3 = Call{op: Op::Subtract, args: exprvec![7,4]};			// C3 = 7 - 4
	let c4 = Call{op: Op::Divide, args: exprvec![c3,10]};			// C4 = C3 / 10
	let c5 = Call{op: Op::Add, args: exprvec![c2,c4]};				// C5 = C2 + C4
	let c6 = Call{op: Op::Exponentiate, args: exprvec![c5,2.5]};	// C6 = C5 ^ 2.5
	let e1 = c6.to_expr();
	b.iter(|| {
		calculate(&e1)
	});
}

#[bench]
fn bigmathbench(b: &mut Bencher) {

	let wa = 1.5;
	let ma = 2.3;
	let va = 11.3;
	let ga = 0.34;
	let mu = 1.50;
	let gd = 3.14;
	let g = 9.8;
	let wx = 0.04;
	let ps = 0.013;
	let rh = 1.74;

	let c1 = Call{op: Op::Sin, args: exprvec![ga]}; 			// sin(ga)
	let c2 = Call{op: Op::Exponentiate, args: exprvec![c1,2]}; 	// sin(ga)^2
	let c3 = Call{op: Op::Multiply, args: exprvec![wx,c2]}; 	// wx*sin(ga)^2
	let c4 = Call{op: Op::Sin, args: exprvec![ps]}; 			// sin(ps)
	let c5 = Call{op: Op::Multiply, args: exprvec![c3,c4]}; 	// wx*sin(ga)^2*sin(ps)

	let c6 = Call{op: Op::Cos, args: exprvec![ga]}; 			// cos(ga)
	let c7 = Call{op: Op::Multiply, args: exprvec![g,c6]}; 		// g*cos(ga)
	let c8 = Call{op: Op::Divide, args: exprvec![c7,va]}; 		// g*cos(ga)/va
	let c9 = Call{op: Op::Add, args: exprvec![gd,c8]}; 			// gd+g*cos(ga)/va

	let c10 = Call{op: Op::Subtract, args: exprvec![c9,c5]}; 	// (gd+g*cos(ga)/va) - (wx*sin(ga)^2*sin(ps))

	let c11 = Call{op: Op::Multiply, args: exprvec![wa,rh]};	// wa*rh
	let c12 = Call{op: Op::Multiply, args: exprvec![c11,va]};	// wa*rh*va
	let c13 = Call{op: Op::Cos, args: exprvec![mu]}; 			// cos(mu)
	let c14 = Call{op: Op::Multiply, args: exprvec![c12,c13]};	// wa*rh*va*cos(mu)

	let c15 = Call{op: Op::Multiply, args: exprvec![2,ma]};		// 2*ma

	let c16 = Call{op: Op::Divide, args: exprvec![c15,c14]};	// 2*ma/wa*rh*va*cos(mu)

	let c17 = Call{op: Op::Multiply, args: exprvec![c16,c10]};	// (2*ma/wa*rh*va*cos(mu)) * (gd+g*cos(ga)/va) - (wx*sin(ga)^2*sin(ps))

	let e1 = c17.to_expr();

	b.iter(|| {
		calculate(&e1)
	});

}<|endoftext|>


<commit_before>use std::os::unix::prelude::*;

use nix::{Error, fcntl, unistd};
use nix::errno::Errno;
use nix::pty::openpty;
use nix::sys::termios::{self, ECHO, OPOST, OCRNL, Termios, tcgetattr};
use nix::unistd::{read, write, close};

/// Helper function analogous to std::io::Write::write_all, but for `RawFd`s
fn write_all(f: RawFd, buf: &[u8]) {
    let mut len = 0;
    while len < buf.len() {
        len += write(f, &buf[len..]).unwrap();
    }
}

#[test]
fn test_tcgetattr() {
    for fd in 0..5 {
        let termios = termios::tcgetattr(fd);
        match unistd::isatty(fd) {
            // If `fd` is a TTY, tcgetattr must succeed.
            Ok(true) => assert!(termios.is_ok()),
            // If it's an invalid file descriptor, tcgetattr should also return
            // the same error
            Err(Error::Sys(Errno::EBADF)) => {
                assert_eq!(termios.err(), Some(Error::Sys(Errno::EBADF)));
            },
            // Otherwise it should return any error
            _ => assert!(termios.is_err())
        }
    }
}

// Test modifying output flags
#[test]
fn test_output_flags() {
    // Open one pty to get attributes for the second one
    let mut termios = {
        let pty = openpty(None, None).unwrap();
        assert!(pty.master > 0);
        assert!(pty.slave > 0);
        let termios = tcgetattr(pty.master).unwrap();
        termios
    };

    // Make sure postprocessing '\r' isn't specified by default or this test is useless.
    assert!(!termios.output_flags.contains(OPOST | OCRNL));

    // Specify that '\r' characters should be transformed to '\n'
    // OPOST is specified to enable post-processing
    termios.output_flags.insert(OPOST | OCRNL);

    // Open a pty
    let pty = openpty(None, &termios).unwrap();
    assert!(pty.master > 0);
    assert!(pty.slave > 0);

    // Write into the master
    let string = "foofoofoo\r";
    write_all(pty.master, string.as_bytes());

    // Read from the slave verifying that the output has been properly transformed
    let mut buf = [0u8; 10];
    ::read_exact(pty.slave, &mut buf);
    let transformed_string = "foofoofoo\n";
    assert_eq!(&buf, transformed_string.as_bytes());
}

// Test modifying local flags
#[test]
fn test_local_flags() {
    // Open one pty to get attributes for the second one
    let mut termios = {
        let pty = openpty(None, None).unwrap();
        assert!(pty.master > 0);
        assert!(pty.slave > 0);
        let termios = tcgetattr(pty.master).unwrap();
        termios
    };

    // Make sure echo is specified by default or this test is useless.
    assert!(termios.local_flags.contains(ECHO));

    // Disable local echo
    termios.local_flags.remove(ECHO);

    // Open a new pty with our modified termios settings
    let pty = openpty(None, &termios).unwrap();
    assert!(pty.master > 0);
    assert!(pty.slave > 0);

    // Set the master is in nonblocking mode or reading will never return.
    let flags = fcntl::fcntl(pty.master, fcntl::F_GETFL).unwrap();
    let new_flags = fcntl::OFlag::from_bits(flags).unwrap() | fcntl::O_NONBLOCK;
    fcntl::fcntl(pty.master, fcntl::F_SETFL(new_flags)).unwrap();

    // Write into the master
    let string = "foofoofoo\r";
    write_all(pty.master, string.as_bytes());

    // Try to read from the master, which should not have anything as echoing was disabled.
    let mut buf = [0u8; 10];
    let read = read(pty.master, &mut buf).unwrap_err();
    assert_eq!(read, Error::Sys(Errno::EAGAIN));
}

#[test]
fn test_cfmakeraw() {
    let mut termios = unsafe { Termios::default_uninit() };
    termios::cfmakeraw(&mut termios);
}
<commit_msg>Fix some bugs in the termios tests<commit_after>use std::os::unix::prelude::*;
use tempfile::tempfile;

use nix::{Error, fcntl};
use nix::errno::Errno;
use nix::pty::openpty;
use nix::sys::termios::{self, ECHO, OPOST, OCRNL, Termios, tcgetattr};
use nix::unistd::{read, write, close};

/// Helper function analogous to std::io::Write::write_all, but for `RawFd`s
fn write_all(f: RawFd, buf: &[u8]) {
    let mut len = 0;
    while len < buf.len() {
        len += write(f, &buf[len..]).unwrap();
    }
}

// Test tcgetattr on a terminal
#[test]
fn test_tcgetattr_pty() {
    let pty = openpty(None, None).unwrap();
    assert!(termios::tcgetattr(pty.master).is_ok());
    close(pty.master).unwrap();
    close(pty.slave).unwrap();
}
// Test tcgetattr on something that isn't a terminal
#[test]
fn test_tcgetattr_enotty() {
    let file = tempfile().unwrap();
    assert_eq!(termios::tcgetattr(file.as_raw_fd()).err(),
               Some(Error::Sys(Errno::ENOTTY)));
}

// Test tcgetattr on an invalid file descriptor
#[test]
fn test_tcgetattr_ebadf() {
    assert_eq!(termios::tcgetattr(-1).err(),
               Some(Error::Sys(Errno::EBADF)));
}

// Test modifying output flags
#[test]
fn test_output_flags() {
    // Open one pty to get attributes for the second one
    let mut termios = {
        let pty = openpty(None, None).unwrap();
        assert!(pty.master > 0);
        assert!(pty.slave > 0);
        let termios = tcgetattr(pty.master).unwrap();
        close(pty.master).unwrap();
        close(pty.slave).unwrap();
        termios
    };

    // Make sure postprocessing '\r' isn't specified by default or this test is useless.
    assert!(!termios.output_flags.contains(OPOST | OCRNL));

    // Specify that '\r' characters should be transformed to '\n'
    // OPOST is specified to enable post-processing
    termios.output_flags.insert(OPOST | OCRNL);

    // Open a pty
    let pty = openpty(None, &termios).unwrap();
    assert!(pty.master > 0);
    assert!(pty.slave > 0);

    // Write into the master
    let string = "foofoofoo\r";
    write_all(pty.master, string.as_bytes());

    // Read from the slave verifying that the output has been properly transformed
    let mut buf = [0u8; 10];
    ::read_exact(pty.slave, &mut buf);
    let transformed_string = "foofoofoo\n";
    close(pty.master).unwrap();
    close(pty.slave).unwrap();
    assert_eq!(&buf, transformed_string.as_bytes());
}

// Test modifying local flags
#[test]
fn test_local_flags() {
    // Open one pty to get attributes for the second one
    let mut termios = {
        let pty = openpty(None, None).unwrap();
        assert!(pty.master > 0);
        assert!(pty.slave > 0);
        let termios = tcgetattr(pty.master).unwrap();
        close(pty.master).unwrap();
        close(pty.slave).unwrap();
        termios
    };

    // Make sure echo is specified by default or this test is useless.
    assert!(termios.local_flags.contains(ECHO));

    // Disable local echo
    termios.local_flags.remove(ECHO);

    // Open a new pty with our modified termios settings
    let pty = openpty(None, &termios).unwrap();
    assert!(pty.master > 0);
    assert!(pty.slave > 0);

    // Set the master is in nonblocking mode or reading will never return.
    let flags = fcntl::fcntl(pty.master, fcntl::F_GETFL).unwrap();
    let new_flags = fcntl::OFlag::from_bits(flags).unwrap() | fcntl::O_NONBLOCK;
    fcntl::fcntl(pty.master, fcntl::F_SETFL(new_flags)).unwrap();

    // Write into the master
    let string = "foofoofoo\r";
    write_all(pty.master, string.as_bytes());

    // Try to read from the master, which should not have anything as echoing was disabled.
    let mut buf = [0u8; 10];
    let read = read(pty.master, &mut buf).unwrap_err();
    close(pty.master).unwrap();
    close(pty.slave).unwrap();
    assert_eq!(read, Error::Sys(Errno::EAGAIN));
}

#[test]
fn test_cfmakeraw() {
    let mut termios = unsafe { Termios::default_uninit() };
    termios::cfmakeraw(&mut termios);
}
<|endoftext|>


<commit_before>extern crate crowbook;

use self::crowbook::{Parser, HtmlRenderer, Book, Token};

fn ast_to_html(v: &[Token]) -> String {
    let mut book = Book::new();
    book.numbering = false;
    let mut html = HtmlRenderer::new(&book);
    html.render_vec(v)
}

#[test]
fn html_combination() {
    let doc = "
Foo
===

```rust
fn min(x: &u32, y: u32) -> &u32 {
    if x < y { x } else { y }
}
```

Bar
---

Some paragraph

* a list
    * inside a list
* another item

3. three
4. four
5. five

[& some link](http://foo/bar?baz=42&coin=plop)
";
    let expected = "
Foo

fn min(x: &u32, y: u32) -> &u32 {
    if x < y { x } else { y }
}

Bar

Some paragraph

a list

inside a list

another item

three

four

five

& some link

";
    let actual = ast_to_html(&Parser::new().parse(doc).unwrap());
    assert_eq!(actual, expected);
}
<commit_msg>fixed tests/html.rs<commit_after>extern crate crowbook;

use self::crowbook::{Parser, HtmlRenderer, Book, Token};

fn ast_to_html(v: &[Token]) -> String {
    let mut book = Book::new();
    book.set_option("numbering", "false").unwrap();
    let mut html = HtmlRenderer::new(&book);
    html.render_vec(v)
}

#[test]
fn html_combination() {
    let doc = "
Foo
===

```rust
fn min(x: &u32, y: u32) -> &u32 {
    if x < y { x } else { y }
}
```

Bar
---

Some paragraph

* a list
    * inside a list
* another item

3. three
4. four
5. five

[& some link](http://foo/bar?baz=42&coin=plop)
";
    let expected = "
Foo

fn min(x: &u32, y: u32) -> &u32 {
    if x < y { x } else { y }
}

Bar

Some paragraph

a list

inside a list

another item

three

four

five

& some link

";
    let actual = ast_to_html(&Parser::new().parse(doc).unwrap());
    assert_eq!(actual, expected);
}
<|endoftext|>


<commit_before>#[macro_use] extern crate match_any;

use std::any::Any;

fn make_any<T: Any>(value: T) -> Box<Any> {
    Box::new(value)
}

#[test]
fn empty() {
    match_any!(make_any(10) =>
        x: i32 => {
            assert_eq!(*x, 10);
        }
    );
}

// TODO: Add.
// #[bench]
<commit_msg>Test function has been renamed<commit_after>#[macro_use] extern crate match_any;

use std::any::Any;

fn make_any<T: Any>(value: T) -> Box<Any> {
    Box::new(value)
}

#[test]
fn match_i32() {
    match_any!(make_any(10) =>
        x: i32 => {
            assert_eq!(*x, 10);
        }
    );
}

// TODO: Add.
// #[bench]
<|endoftext|>


<commit_before>#[macro_use] extern crate macrolisp;
use macrolisp::prelude::*;

macro_rules! ck {
    ($left:expr, $right:expr) => {{
        assert_eq!($left, $right);
        $left
    }}
}

#[test]
fn main() {
    let add4 = lisp!(
        (lambda (((a i32)
                  (b i32)
                  (c i32)
                  (d i32))
                 i32)
         (+ a b c d))
    );
    let factorial_proc = lisp!(
        (lambda (((a i32))
                 i32)
         (let ((mut x   a)
                (mut acc 1))
          (while (> x 1)
           (:= acc (* acc x))
           (:= x   (- x 1)))
          acc))
    );
    lisp!(
        (defn factorial (((a i32))
                         i32)
         (if (== a 1)
             1
             (* a (factorial (- a 1)))))
    );

    lisp!(
        (defn factorial_tail (((a i32))
                              i32)
         (defn factorial_tail_helper (((a i32)
                                       (acc i32))
                                      i32)
          (if (== a 1)
              acc
              (factorial_tail_helper (- a 1) (* acc a))))
         (factorial_tail_helper a 1))
    );

    struct IntegralFloat { i: i32 }
    impl std::ops::Add for IntegralFloat {
        type Output = f32;
        fn add(self, rhs: Self) -> Self::Output {
            self.i as Self::Output + rhs.i as Self::Output
        }
    }

    println!("1+2+3+4 = {}",  ck!(lisp!( (add4 1 2 3 4)     ),                10));
    println!("1+2+3.0 = {}",  ck!(lisp!( (+ (rust { IntegralFloat { i: 1 } })
                                            (rust { IntegralFloat { i: 2 } })
                                            3.0) ),                           6.0));
    println!("1-2-3-4 = {}",  ck!(lisp!( (- 1 2 3 4)        ),                -8));
    println!("5! = {}",       ck!(lisp!( (factorial_proc 5) ),                120));
    println!("6! = {}",       ck!(lisp!( (factorial 6)      ),                720));
    println!("7! = {}",       ck!(lisp!( (factorial_tail 7) ),                5040));
}

#[test]
fn lambdarec_tests() {
    let factorial_rec = lisp!(
        (lambda rec (((a i32))
                      i32)
         (if (== a 1)
             1
             (* a (rec (- a 1)))))
    );
    let fib = lisp!(
        (lambda rec (((a i32))
                      i32)
         (match a
          (0 1)
          (1 1)
          (n (+ (rec (- n 1))
                (rec (- n 2))))))
    );

    println!("-(8!) = {}", ck!(lisp!( (- (factorial_rec 8)) ), -40320));
    lisp!(
        (println! "fib = {} {} {} {} {} {} {} {} {} {} ..." (ck! (fib 0) 1)
                                                            (ck! (fib 1) 1)
                                                            (ck! (fib 2) 2)
                                                            (ck! (fib 3) 3)
                                                            (ck! (fib 4) 5)
                                                            (ck! (fib 5) 8)
                                                            (ck! (fib 6) 13)
                                                            (ck! (fib 7) 21)
                                                            (ck! (fib 8) 34)
                                                            (ck! (fib 9) 55))
    );
}

#[test]
fn lambda_tests() {
    let mut num = 5;
    lisp!(
        (let ((mut add_num (lambda (((x i32))
                                     ())
                             (:= num (+ num x)))))
         (add_num 5))
    );
    println!("num = {}", ck!(num, 10));
}

/* rust-lang/rust#12335

#[test] #[compile_fail]
fn lambdarec_cannot_capture() {
    let mut num = 5;
    lisp!(
        (let ((mut add_num (lambda rec (((x i32))
                                        ())
                            (if (> x 0)
                             (do
                              (:= num (+ num 1))
                              (rec (- x 1)))
                             ()))))
         (add_num 5))
    );
    println!("num = {}", ck!(num, 10));
}
*/

<commit_msg>fix whitespace again<commit_after>#[macro_use] extern crate macrolisp;
use macrolisp::prelude::*;

macro_rules! ck {
    ($left:expr, $right:expr) => {{
        assert_eq!($left, $right);
        $left
    }}
}

#[test]
fn main() {
    let add4 = lisp!(
        (lambda (((a i32)
                  (b i32)
                  (c i32)
                  (d i32))
                 i32)
         (+ a b c d))
    );
    let factorial_proc = lisp!(
        (lambda (((a i32))
                 i32)
         (let ((mut x   a)
               (mut acc 1))
          (while (> x 1)
           (:= acc (* acc x))
           (:= x   (- x 1)))
          acc))
    );
    lisp!(
        (defn factorial (((a i32))
                         i32)
         (if (== a 1)
             1
             (* a (factorial (- a 1)))))
    );

    lisp!(
        (defn factorial_tail (((a i32))
                              i32)
         (defn factorial_tail_helper (((a i32)
                                       (acc i32))
                                      i32)
          (if (== a 1)
              acc
              (factorial_tail_helper (- a 1) (* acc a))))
         (factorial_tail_helper a 1))
    );

    struct IntegralFloat { i: i32 }
    impl std::ops::Add for IntegralFloat {
        type Output = f32;
        fn add(self, rhs: Self) -> Self::Output {
            self.i as Self::Output + rhs.i as Self::Output
        }
    }

    println!("1+2+3+4 = {}",  ck!(lisp!( (add4 1 2 3 4)     ),                10));
    println!("1+2+3.0 = {}",  ck!(lisp!( (+ (rust { IntegralFloat { i: 1 } })
                                            (rust { IntegralFloat { i: 2 } })
                                            3.0) ),                           6.0));
    println!("1-2-3-4 = {}",  ck!(lisp!( (- 1 2 3 4)        ),                -8));
    println!("5! = {}",       ck!(lisp!( (factorial_proc 5) ),                120));
    println!("6! = {}",       ck!(lisp!( (factorial 6)      ),                720));
    println!("7! = {}",       ck!(lisp!( (factorial_tail 7) ),                5040));
}

#[test]
fn lambdarec_tests() {
    let factorial_rec = lisp!(
        (lambda rec (((a i32))
                      i32)
         (if (== a 1)
             1
             (* a (rec (- a 1)))))
    );
    let fib = lisp!(
        (lambda rec (((a i32))
                      i32)
         (match a
          (0 1)
          (1 1)
          (n (+ (rec (- n 1))
                (rec (- n 2))))))
    );

    println!("-(8!) = {}", ck!(lisp!( (- (factorial_rec 8)) ), -40320));
    lisp!(
        (println! "fib = {} {} {} {} {} {} {} {} {} {} ..." (ck! (fib 0) 1)
                                                            (ck! (fib 1) 1)
                                                            (ck! (fib 2) 2)
                                                            (ck! (fib 3) 3)
                                                            (ck! (fib 4) 5)
                                                            (ck! (fib 5) 8)
                                                            (ck! (fib 6) 13)
                                                            (ck! (fib 7) 21)
                                                            (ck! (fib 8) 34)
                                                            (ck! (fib 9) 55))
    );
}

#[test]
fn lambda_tests() {
    let mut num = 5;
    lisp!(
        (let ((mut add_num (lambda (((x i32))
                                     ())
                             (:= num (+ num x)))))
         (add_num 5))
    );
    println!("num = {}", ck!(num, 10));
}

/* rust-lang/rust#12335

#[test] #[compile_fail]
fn lambdarec_cannot_capture() {
    let mut num = 5;
    lisp!(
        (let ((mut add_num (lambda rec (((x i32))
                                        ())
                            (if (> x 0)
                             (do
                              (:= num (+ num 1))
                              (rec (- x 1)))
                             ()))))
         (add_num 5))
    );
    println!("num = {}", ck!(num, 10));
}
*/

<|endoftext|>


<commit_before>#![allow(clippy::assertions_on_constants, clippy::float_cmp, clippy::unit_cmp)]

use cxx_test_suite::module::ffi2;
use cxx_test_suite::{ffi, R};
use std::cell::Cell;
use std::ffi::CStr;

thread_local! {
    static CORRECT: Cell<bool> = Cell::new(false);
}

#[no_mangle]
extern "C" fn cxx_test_suite_set_correct() {
    CORRECT.with(|correct| correct.set(true));
}

macro_rules! check {
    ($run:expr) => {{
        CORRECT.with(|correct| correct.set(false));
        $run;
        assert!(CORRECT.with(|correct| correct.get()), stringify!($run));
    }};
}

#[test]
fn test_c_return() {
    let shared = ffi::Shared { z: 2020 };
    let ns_shared = ffi::AShared { z: 2020 };
    let nested_ns_shared = ffi::ABShared { z: 2020 };

    assert_eq!(2020, ffi::c_return_primitive());
    assert_eq!(2020, ffi::c_return_shared().z);
    assert_eq!(2020, ffi::c_return_box().0);
    ffi::c_return_unique_ptr();
    ffi2::c_return_ns_unique_ptr();
    assert_eq!(2020, *ffi::c_return_ref(&shared));
    assert_eq!(2020, *ffi::c_return_ns_ref(&ns_shared));
    assert_eq!(2020, *ffi::c_return_nested_ns_ref(&nested_ns_shared));
    assert_eq!("2020", ffi::c_return_str(&shared));
    assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared));
    assert_eq!("2020", ffi::c_return_rust_string());
    assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap());
    assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len());
    assert_eq!(
        200_u8,
        ffi::c_return_unique_ptr_vector_u8().into_iter().sum(),
    );
    assert_eq!(
        200.5_f64,
        ffi::c_return_unique_ptr_vector_f64().into_iter().sum(),
    );
    assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().len());
    assert_eq!(
        2021_usize,
        ffi::c_return_unique_ptr_vector_shared()
            .into_iter()
            .map(|o| o.z)
            .sum(),
    );
    assert_eq!(2020, ffi::c_return_identity(2020));
    assert_eq!(2021, ffi::c_return_sum(2020, 1));
    match ffi::c_return_enum(0) {
        enm @ ffi::Enum::AVal => assert_eq!(0, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(1) {
        enm @ ffi::Enum::BVal => assert_eq!(2020, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(2021) {
        enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_ns_enum(0) {
        enm @ ffi::AEnum::AAVal => assert_eq!(0, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_nested_ns_enum(0) {
        enm @ ffi::ABEnum::ABAVal => assert_eq!(0, enm.repr),
        _ => assert!(false),
    }
}

#[test]
fn test_c_try_return() {
    assert_eq!((), ffi::c_try_return_void().unwrap());
    assert_eq!(2020, ffi::c_try_return_primitive().unwrap());
    assert_eq!(
        "logic error",
        ffi::c_fail_return_primitive().unwrap_err().what(),
    );
    assert_eq!(2020, ffi::c_try_return_box().unwrap().0);
    assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap());
    assert_eq!("2020", ffi::c_try_return_str("2020").unwrap());
    assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap());
    assert_eq!("2020", ffi::c_try_return_rust_string().unwrap());
    assert_eq!("2020", &*ffi::c_try_return_unique_ptr_string().unwrap());
}

#[test]
fn test_c_take() {
    let unique_ptr = ffi::c_return_unique_ptr();
    let unique_ptr_ns = ffi2::c_return_ns_unique_ptr();

    check!(ffi::c_take_primitive(2020));
    check!(ffi::c_take_shared(ffi::Shared { z: 2020 }));
    check!(ffi::c_take_ns_shared(ffi::AShared { z: 2020 }));
    check!(ffi::ns_c_take_ns_shared(ffi::AShared { z: 2020 }));
    check!(ffi::c_take_nested_ns_shared(ffi::ABShared { z: 2020 }));
    check!(ffi::c_take_box(Box::new(R(2020))));
    check!(ffi::c_take_ref_c(&unique_ptr));
    check!(ffi2::c_take_ref_ns_c(&unique_ptr_ns));
    check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr));
    check!(ffi::c_take_str("2020"));
    check!(ffi::c_take_sliceu8(b"2020"));
    check!(ffi::c_take_rust_string("2020".to_owned()));
    check!(ffi::c_take_unique_ptr_string(
        ffi::c_return_unique_ptr_string()
    ));
    check!(ffi::c_take_unique_ptr_vector_u8(
        ffi::c_return_unique_ptr_vector_u8()
    ));
    check!(ffi::c_take_unique_ptr_vector_f64(
        ffi::c_return_unique_ptr_vector_f64()
    ));
    check!(ffi::c_take_unique_ptr_vector_shared(
        ffi::c_return_unique_ptr_vector_shared()
    ));
    check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8()));
    let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec();
    check!(ffi::c_take_rust_vec(test_vec.clone()));
    check!(ffi::c_take_rust_vec_index(test_vec.clone()));
    let shared_test_vec = vec![ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 }];
    check!(ffi::c_take_rust_vec_shared(shared_test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared_index(shared_test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared_push(shared_test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared_forward_iterator(
        shared_test_vec,
    ));
    check!(ffi::c_take_ref_rust_vec(&test_vec));
    check!(ffi::c_take_ref_rust_vec_index(&test_vec));
    check!(ffi::c_take_ref_rust_vec_copy(&test_vec));
    check!(ffi::c_take_ref_shared_string(&ffi::SharedString {
        msg: "2020".to_owned()
    }));
    let ns_shared_test_vec = vec![ffi::AShared { z: 1010 }, ffi::AShared { z: 1011 }];
    check!(ffi::c_take_rust_vec_ns_shared(ns_shared_test_vec));
    let nested_ns_shared_test_vec = vec![ffi::ABShared { z: 1010 }, ffi::ABShared { z: 1011 }];
    check!(ffi::c_take_rust_vec_nested_ns_shared(
        nested_ns_shared_test_vec
    ));

    check!(ffi::c_take_enum(ffi::Enum::AVal));
    check!(ffi::c_take_ns_enum(ffi::AEnum::AAVal));
    check!(ffi::c_take_nested_ns_enum(ffi::ABEnum::ABAVal));
}

#[test]
fn test_c_callback() {
    fn callback(s: String) -> usize {
        if s == "2020" {
            cxx_test_suite_set_correct();
        }
        0
    }

    check!(ffi::c_take_callback(callback));
}

#[test]
fn test_c_call_r() {
    fn cxx_run_test() {
        extern "C" {
            fn cxx_run_test() -> *const i8;
        }
        let failure = unsafe { cxx_run_test() };
        if !failure.is_null() {
            let msg = unsafe { CStr::from_ptr(failure as *mut std::os::raw::c_char) };
            eprintln!("{}", msg.to_string_lossy());
        }
    }
    check!(cxx_run_test());
}

#[test]
fn test_c_method_calls() {
    let mut unique_ptr = ffi::c_return_unique_ptr();

    let old_value = unique_ptr.get();
    assert_eq!(2020, old_value);
    assert_eq!(2021, unique_ptr.as_mut().unwrap().set(2021));
    assert_eq!(2021, unique_ptr.get());
    assert_eq!(2021, unique_ptr.get2());
    assert_eq!(
        2022,
        unique_ptr.as_mut().unwrap().set_succeed(2022).unwrap(),
    );
    assert!(unique_ptr.as_mut().unwrap().get_fail().is_err());
    assert_eq!(2021, ffi::Shared { z: 0 }.c_method_on_shared());
}

#[test]
fn test_c_ns_method_calls() {
    let unique_ptr = ffi2::ns_c_return_unique_ptr_ns();

    let old_value = unique_ptr.get();
    assert_eq!(1000, old_value);
}

#[test]
fn test_enum_representations() {
    assert_eq!(0, ffi::Enum::AVal.repr);
    assert_eq!(2020, ffi::Enum::BVal.repr);
    assert_eq!(2021, ffi::Enum::CVal.repr);
}

#[no_mangle]
extern "C" fn cxx_test_suite_get_box() -> *mut R {
    Box::into_raw(Box::new(R(2020usize)))
}

#[no_mangle]
unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const R) -> bool {
    (*r).0 == 2020
}

#[test]
fn test_rust_name_attribute() {
    assert_eq!("2020", ffi::i32_overloaded_function(2020));
    assert_eq!("2020", ffi::str_overloaded_function("2020"));
    let unique_ptr = ffi::c_return_unique_ptr();
    assert_eq!("2020", unique_ptr.i32_overloaded_method(2020));
    assert_eq!("2020", unique_ptr.str_overloaded_method("2020"));
}

#[test]
fn test_extern_trivial() {
    let d = ffi2::c_return_trivial();
    check!(ffi2::c_take_trivial_ref(&d));
    check!(ffi2::c_take_trivial(d));
    let d = ffi2::c_return_trivial_ptr();
    check!(ffi2::c_take_trivial_ptr(d));
    cxx::UniquePtr::new(ffi2::D { d: 42 });
    let d = ffi2::ns_c_return_trivial();
    check!(ffi2::ns_c_take_trivial(d));

    let g = ffi2::c_return_trivial_ns();
    check!(ffi2::c_take_trivial_ns_ref(&g));
    check!(ffi2::c_take_trivial_ns(g));
    let g = ffi2::c_return_trivial_ns_ptr();
    check!(ffi2::c_take_trivial_ns_ptr(g));
    cxx::UniquePtr::new(ffi2::G { g: 42 });
}

#[test]
fn test_extern_opaque() {
    let e = ffi2::c_return_opaque_ptr();
    check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap()));
    check!(ffi2::c_take_opaque_ptr(e));

    let f = ffi2::c_return_ns_opaque_ptr();
    check!(ffi2::c_take_opaque_ns_ref(f.as_ref().unwrap()));
    check!(ffi2::c_take_opaque_ns_ptr(f));
}
<commit_msg>Fix test suite panic message forward compatibility lint<commit_after>#![allow(clippy::assertions_on_constants, clippy::float_cmp, clippy::unit_cmp)]

use cxx_test_suite::module::ffi2;
use cxx_test_suite::{ffi, R};
use std::cell::Cell;
use std::ffi::CStr;

thread_local! {
    static CORRECT: Cell<bool> = Cell::new(false);
}

#[no_mangle]
extern "C" fn cxx_test_suite_set_correct() {
    CORRECT.with(|correct| correct.set(true));
}

macro_rules! check {
    ($run:expr) => {{
        CORRECT.with(|correct| correct.set(false));
        $run;
        assert!(CORRECT.with(|correct| correct.get()), "{}", stringify!($run));
    }};
}

#[test]
fn test_c_return() {
    let shared = ffi::Shared { z: 2020 };
    let ns_shared = ffi::AShared { z: 2020 };
    let nested_ns_shared = ffi::ABShared { z: 2020 };

    assert_eq!(2020, ffi::c_return_primitive());
    assert_eq!(2020, ffi::c_return_shared().z);
    assert_eq!(2020, ffi::c_return_box().0);
    ffi::c_return_unique_ptr();
    ffi2::c_return_ns_unique_ptr();
    assert_eq!(2020, *ffi::c_return_ref(&shared));
    assert_eq!(2020, *ffi::c_return_ns_ref(&ns_shared));
    assert_eq!(2020, *ffi::c_return_nested_ns_ref(&nested_ns_shared));
    assert_eq!("2020", ffi::c_return_str(&shared));
    assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared));
    assert_eq!("2020", ffi::c_return_rust_string());
    assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap());
    assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len());
    assert_eq!(
        200_u8,
        ffi::c_return_unique_ptr_vector_u8().into_iter().sum(),
    );
    assert_eq!(
        200.5_f64,
        ffi::c_return_unique_ptr_vector_f64().into_iter().sum(),
    );
    assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().len());
    assert_eq!(
        2021_usize,
        ffi::c_return_unique_ptr_vector_shared()
            .into_iter()
            .map(|o| o.z)
            .sum(),
    );
    assert_eq!(2020, ffi::c_return_identity(2020));
    assert_eq!(2021, ffi::c_return_sum(2020, 1));
    match ffi::c_return_enum(0) {
        enm @ ffi::Enum::AVal => assert_eq!(0, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(1) {
        enm @ ffi::Enum::BVal => assert_eq!(2020, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(2021) {
        enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_ns_enum(0) {
        enm @ ffi::AEnum::AAVal => assert_eq!(0, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_nested_ns_enum(0) {
        enm @ ffi::ABEnum::ABAVal => assert_eq!(0, enm.repr),
        _ => assert!(false),
    }
}

#[test]
fn test_c_try_return() {
    assert_eq!((), ffi::c_try_return_void().unwrap());
    assert_eq!(2020, ffi::c_try_return_primitive().unwrap());
    assert_eq!(
        "logic error",
        ffi::c_fail_return_primitive().unwrap_err().what(),
    );
    assert_eq!(2020, ffi::c_try_return_box().unwrap().0);
    assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap());
    assert_eq!("2020", ffi::c_try_return_str("2020").unwrap());
    assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap());
    assert_eq!("2020", ffi::c_try_return_rust_string().unwrap());
    assert_eq!("2020", &*ffi::c_try_return_unique_ptr_string().unwrap());
}

#[test]
fn test_c_take() {
    let unique_ptr = ffi::c_return_unique_ptr();
    let unique_ptr_ns = ffi2::c_return_ns_unique_ptr();

    check!(ffi::c_take_primitive(2020));
    check!(ffi::c_take_shared(ffi::Shared { z: 2020 }));
    check!(ffi::c_take_ns_shared(ffi::AShared { z: 2020 }));
    check!(ffi::ns_c_take_ns_shared(ffi::AShared { z: 2020 }));
    check!(ffi::c_take_nested_ns_shared(ffi::ABShared { z: 2020 }));
    check!(ffi::c_take_box(Box::new(R(2020))));
    check!(ffi::c_take_ref_c(&unique_ptr));
    check!(ffi2::c_take_ref_ns_c(&unique_ptr_ns));
    check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr));
    check!(ffi::c_take_str("2020"));
    check!(ffi::c_take_sliceu8(b"2020"));
    check!(ffi::c_take_rust_string("2020".to_owned()));
    check!(ffi::c_take_unique_ptr_string(
        ffi::c_return_unique_ptr_string()
    ));
    check!(ffi::c_take_unique_ptr_vector_u8(
        ffi::c_return_unique_ptr_vector_u8()
    ));
    check!(ffi::c_take_unique_ptr_vector_f64(
        ffi::c_return_unique_ptr_vector_f64()
    ));
    check!(ffi::c_take_unique_ptr_vector_shared(
        ffi::c_return_unique_ptr_vector_shared()
    ));
    check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8()));
    let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec();
    check!(ffi::c_take_rust_vec(test_vec.clone()));
    check!(ffi::c_take_rust_vec_index(test_vec.clone()));
    let shared_test_vec = vec![ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 }];
    check!(ffi::c_take_rust_vec_shared(shared_test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared_index(shared_test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared_push(shared_test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared_forward_iterator(
        shared_test_vec,
    ));
    check!(ffi::c_take_ref_rust_vec(&test_vec));
    check!(ffi::c_take_ref_rust_vec_index(&test_vec));
    check!(ffi::c_take_ref_rust_vec_copy(&test_vec));
    check!(ffi::c_take_ref_shared_string(&ffi::SharedString {
        msg: "2020".to_owned()
    }));
    let ns_shared_test_vec = vec![ffi::AShared { z: 1010 }, ffi::AShared { z: 1011 }];
    check!(ffi::c_take_rust_vec_ns_shared(ns_shared_test_vec));
    let nested_ns_shared_test_vec = vec![ffi::ABShared { z: 1010 }, ffi::ABShared { z: 1011 }];
    check!(ffi::c_take_rust_vec_nested_ns_shared(
        nested_ns_shared_test_vec
    ));

    check!(ffi::c_take_enum(ffi::Enum::AVal));
    check!(ffi::c_take_ns_enum(ffi::AEnum::AAVal));
    check!(ffi::c_take_nested_ns_enum(ffi::ABEnum::ABAVal));
}

#[test]
fn test_c_callback() {
    fn callback(s: String) -> usize {
        if s == "2020" {
            cxx_test_suite_set_correct();
        }
        0
    }

    check!(ffi::c_take_callback(callback));
}

#[test]
fn test_c_call_r() {
    fn cxx_run_test() {
        extern "C" {
            fn cxx_run_test() -> *const i8;
        }
        let failure = unsafe { cxx_run_test() };
        if !failure.is_null() {
            let msg = unsafe { CStr::from_ptr(failure as *mut std::os::raw::c_char) };
            eprintln!("{}", msg.to_string_lossy());
        }
    }
    check!(cxx_run_test());
}

#[test]
fn test_c_method_calls() {
    let mut unique_ptr = ffi::c_return_unique_ptr();

    let old_value = unique_ptr.get();
    assert_eq!(2020, old_value);
    assert_eq!(2021, unique_ptr.as_mut().unwrap().set(2021));
    assert_eq!(2021, unique_ptr.get());
    assert_eq!(2021, unique_ptr.get2());
    assert_eq!(
        2022,
        unique_ptr.as_mut().unwrap().set_succeed(2022).unwrap(),
    );
    assert!(unique_ptr.as_mut().unwrap().get_fail().is_err());
    assert_eq!(2021, ffi::Shared { z: 0 }.c_method_on_shared());
}

#[test]
fn test_c_ns_method_calls() {
    let unique_ptr = ffi2::ns_c_return_unique_ptr_ns();

    let old_value = unique_ptr.get();
    assert_eq!(1000, old_value);
}

#[test]
fn test_enum_representations() {
    assert_eq!(0, ffi::Enum::AVal.repr);
    assert_eq!(2020, ffi::Enum::BVal.repr);
    assert_eq!(2021, ffi::Enum::CVal.repr);
}

#[no_mangle]
extern "C" fn cxx_test_suite_get_box() -> *mut R {
    Box::into_raw(Box::new(R(2020usize)))
}

#[no_mangle]
unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const R) -> bool {
    (*r).0 == 2020
}

#[test]
fn test_rust_name_attribute() {
    assert_eq!("2020", ffi::i32_overloaded_function(2020));
    assert_eq!("2020", ffi::str_overloaded_function("2020"));
    let unique_ptr = ffi::c_return_unique_ptr();
    assert_eq!("2020", unique_ptr.i32_overloaded_method(2020));
    assert_eq!("2020", unique_ptr.str_overloaded_method("2020"));
}

#[test]
fn test_extern_trivial() {
    let d = ffi2::c_return_trivial();
    check!(ffi2::c_take_trivial_ref(&d));
    check!(ffi2::c_take_trivial(d));
    let d = ffi2::c_return_trivial_ptr();
    check!(ffi2::c_take_trivial_ptr(d));
    cxx::UniquePtr::new(ffi2::D { d: 42 });
    let d = ffi2::ns_c_return_trivial();
    check!(ffi2::ns_c_take_trivial(d));

    let g = ffi2::c_return_trivial_ns();
    check!(ffi2::c_take_trivial_ns_ref(&g));
    check!(ffi2::c_take_trivial_ns(g));
    let g = ffi2::c_return_trivial_ns_ptr();
    check!(ffi2::c_take_trivial_ns_ptr(g));
    cxx::UniquePtr::new(ffi2::G { g: 42 });
}

#[test]
fn test_extern_opaque() {
    let e = ffi2::c_return_opaque_ptr();
    check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap()));
    check!(ffi2::c_take_opaque_ptr(e));

    let f = ffi2::c_return_ns_opaque_ptr();
    check!(ffi2::c_take_opaque_ns_ref(f.as_ref().unwrap()));
    check!(ffi2::c_take_opaque_ns_ptr(f));
}
<|endoftext|>


<commit_before>use super::*;
use ast;

/// Compiles an unoptimized [`ast`](../ast/index.html) program to a run-length encoded program.
pub fn compile(program: &[ast::Statement]) -> Box<Program> {
    let mut compiler = Compiler::new();
    compiler.compile(program);
    compiler.into_program()
}

/// Represents the state of an RLE compiler from `ast::Instruction` to `Instruction`.
pub struct Compiler {
    instructions: Vec<Statement>,
    last_command: Command,
    last_repeat: Count,
}

impl Compiler {
    /// Creates a new RLE compiler.
    pub fn new() -> Self {
        Compiler {
            instructions: Vec::new(),
            last_command: Command::Right,
            last_repeat:  0,
        }
    }

    /// Compiles the given sequence of instructions.
    pub fn compile(&mut self, program: &[ast::Statement]) {
        for instruction in program {
            match *instruction {
                ast::Statement::Cmd(op_code) => self.issue_op(op_code),
                ast::Statement::Loop(ref body) => self.issue_loop(compile(body)),
            }
        }
    }

    /// Extracts the compiled program.
    pub fn into_program(mut self) -> Box<Program> {
        self.push_op();
        self.instructions.into_boxed_slice()
    }

    fn push_op(&mut self) {
        if self.last_repeat > 0 {
            self.instructions.push(Statement::Cmd(self.last_command, self.last_repeat));
            self.last_command = Command::Right;
            self.last_repeat = 0;
        }
    }

    fn issue_op(&mut self, cmd: Command) {
        if cmd == self.last_command {
            if let Some(last_repeat) = self.last_repeat.checked_add(1) {
                self.last_repeat = last_repeat;
            } else {
                self.push_op();
                self.last_repeat = 1;
            }
        } else {
            self.push_op();
            self.last_command = cmd;
            self.last_repeat = 1;
        }
    }

    fn issue_loop(&mut self, body: Box<Program>) {
        self.push_op();
        self.instructions.push(Statement::Loop(body));
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::Statement as Src;
    use super::Statement as Obj;
    use super::Command::*;

    #[test]
    fn right_compiles() {
        assert_compile(&[Src::Cmd(Right)], &[Obj::Cmd(Right, 1)]);
    }

    #[test]
    fn three_rights_compile() {
        assert_compile(&[Src::Cmd(Right), Src::Cmd(Right), Src::Cmd(Right)],
                       &[Obj::Cmd(Right, 3)]);
    }

    #[test]
    fn two_rights_two_ups_compile() {
        assert_compile(&[Src::Cmd(Right), Src::Cmd(Right), Src::Cmd(Up), Src::Cmd(Up)],
                       &[Obj::Cmd(Right, 2), Obj::Cmd(Up, 2)]);
    }

    #[test]
    fn loop_compiles() {
        assert_compile(&[Src::Cmd(In), src_mk_loop(vec![Src::Cmd(Right)]), Src::Cmd(In)],
                       &[Obj::Cmd(In, 1), mk_loop(vec![Obj::Cmd(Right, 1)]), Obj::Cmd(In, 1)]);

    }

    fn assert_compile(src: &[ast::Statement], expected: &[Statement]) {
        let actual = compile(src);
        assert_eq!(&*actual, expected);
    }

    fn src_mk_loop(body: Vec<Src>) -> Src {
        Src::Loop(body.into_boxed_slice())
    }

    fn mk_loop(body: Vec<Statement>) -> Statement {
        Obj::Loop(body.into_boxed_slice())
    }
}
<commit_msg>s/[Instruction]/Program/<commit_after>use super::*;
use ast;

/// Compiles an unoptimized [`ast`](../ast/index.html) program to a run-length encoded program.
pub fn compile(program: &ast::Program) -> Box<Program> {
    let mut compiler = Compiler::new();
    compiler.compile(program);
    compiler.into_program()
}

/// Represents the state of an RLE compiler from `ast::Instruction` to `Instruction`.
pub struct Compiler {
    instructions: Vec<Statement>,
    last_command: Command,
    last_repeat: Count,
}

impl Compiler {
    /// Creates a new RLE compiler.
    pub fn new() -> Self {
        Compiler {
            instructions: Vec::new(),
            last_command: Command::Right,
            last_repeat:  0,
        }
    }

    /// Compiles the given sequence of instructions.
    pub fn compile(&mut self, program: &[ast::Statement]) {
        for instruction in program {
            match *instruction {
                ast::Statement::Cmd(op_code) => self.issue_op(op_code),
                ast::Statement::Loop(ref body) => self.issue_loop(compile(body)),
            }
        }
    }

    /// Extracts the compiled program.
    pub fn into_program(mut self) -> Box<Program> {
        self.push_op();
        self.instructions.into_boxed_slice()
    }

    fn push_op(&mut self) {
        if self.last_repeat > 0 {
            self.instructions.push(Statement::Cmd(self.last_command, self.last_repeat));
            self.last_command = Command::Right;
            self.last_repeat = 0;
        }
    }

    fn issue_op(&mut self, cmd: Command) {
        if cmd == self.last_command {
            if let Some(last_repeat) = self.last_repeat.checked_add(1) {
                self.last_repeat = last_repeat;
            } else {
                self.push_op();
                self.last_repeat = 1;
            }
        } else {
            self.push_op();
            self.last_command = cmd;
            self.last_repeat = 1;
        }
    }

    fn issue_loop(&mut self, body: Box<Program>) {
        self.push_op();
        self.instructions.push(Statement::Loop(body));
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::Statement as Src;
    use super::Statement as Obj;
    use super::Command::*;

    #[test]
    fn right_compiles() {
        assert_compile(&[Src::Cmd(Right)], &[Obj::Cmd(Right, 1)]);
    }

    #[test]
    fn three_rights_compile() {
        assert_compile(&[Src::Cmd(Right), Src::Cmd(Right), Src::Cmd(Right)],
                       &[Obj::Cmd(Right, 3)]);
    }

    #[test]
    fn two_rights_two_ups_compile() {
        assert_compile(&[Src::Cmd(Right), Src::Cmd(Right), Src::Cmd(Up), Src::Cmd(Up)],
                       &[Obj::Cmd(Right, 2), Obj::Cmd(Up, 2)]);
    }

    #[test]
    fn loop_compiles() {
        assert_compile(&[Src::Cmd(In), src_mk_loop(vec![Src::Cmd(Right)]), Src::Cmd(In)],
                       &[Obj::Cmd(In, 1), mk_loop(vec![Obj::Cmd(Right, 1)]), Obj::Cmd(In, 1)]);

    }

    fn assert_compile(src: &[ast::Statement], expected: &[Statement]) {
        let actual = compile(src);
        assert_eq!(&*actual, expected);
    }

    fn src_mk_loop(body: Vec<Src>) -> Src {
        Src::Loop(body.into_boxed_slice())
    }

    fn mk_loop(body: Vec<Statement>) -> Statement {
        Obj::Loop(body.into_boxed_slice())
    }
}
<|endoftext|>


<commit_before>extern crate termion;

use termion::{TermRead, TermWrite, IntoRawMode, Color, Style, Key};
use termion::terminal_size;
use std::io::{Read, Write, Stdout, Stdin};
use std::io::{stdout, stdin};
use std;
use model::ListTopicItem;

pub struct Index {
    title: String,
    selected_topic_index: usize,
}

impl Index {
    pub fn new() -> Self {
        Index {
            title: String::from("高登"),
            selected_topic_index: 0,
        }
    }

    pub fn select_topic(&mut self, index: usize) {
        self.selected_topic_index = index;
    }

    pub fn get_selected_topic(&self) -> usize {
        self.selected_topic_index
    }

    pub fn print(&mut self, stdout: &mut termion::RawTerminal<std::io::StdoutLock>, collection: &Vec<ListTopicItem>) {

        if self.selected_topic_index > self.body_height() {
            self.selected_topic_index = self.body_height();
        }

        let width = terminal_size().unwrap().0 as usize;

        print_header(stdout, width as usize, &self.title);
    }

    pub fn body_height(&self) -> usize {

        let h = terminal_size().unwrap().1;

        if h >= 3 {
            h as usize - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {

        let w = terminal_size().unwrap().0;

        if w >= 2 {
            w as usize - 2
        } else {
            0
        }
    }

}

fn print_header(stdout: &mut termion::RawTerminal<std::io::StdoutLock>, width: usize, text: &str) {
    // print header
    let padding = ((width - text.len()) / 2) as u16;
    let header_bottom = (0..width).map(|_| "─").collect::<Vec<_>>().join("");

    stdout.goto(padding, 0).unwrap();
    stdout.color(Color::White).unwrap();
    stdout.bg_color(Color::Black).unwrap();
    stdout.style(Style::Bold).unwrap();
    stdout.write(text.as_bytes()).unwrap();

    stdout.goto(0, 1).unwrap();
    stdout.color(Color::Yellow).unwrap();
    stdout.bg_color(Color::Black).unwrap();
    stdout.style(Style::Bold).unwrap();
    stdout.write(header_bottom.as_bytes()).unwrap();

    stdout.hide_cursor().unwrap();
    stdout.reset().unwrap();
}
<commit_msg>index screen - print body<commit_after>extern crate termion;

use termion::{TermRead, TermWrite, IntoRawMode, Color, Style, Key};
use termion::terminal_size;
use std::io::{Read, Write, Stdout, Stdin};
use std::io::{stdout, stdin};
use std;

use utility::string::*;
use model::ListTopicItem;

pub struct Index {
    title: String,
    selected_topic_index: usize,
}

impl Index {
    pub fn new() -> Self {
        Index {
            title: String::from("高登"),
            selected_topic_index: 0,
        }
    }

    pub fn select_topic(&mut self, index: usize) {
        self.selected_topic_index = index;
    }

    pub fn get_selected_topic(&self) -> usize {
        self.selected_topic_index
    }

    pub fn print(&mut self, stdout: &mut termion::RawTerminal<std::io::StdoutLock>, collection: &Vec<ListTopicItem>) {

        if self.selected_topic_index > self.body_height() {
            self.selected_topic_index = self.body_height();
        }

        let width = terminal_size().unwrap().0 as usize;

        print_header(stdout, width as usize, &self.title);
        print_body(stdout,
                   self.body_width(),
                   2,
                   self.body_height(),
                   &collection,
                   self.selected_topic_index);
    }

    pub fn body_height(&self) -> usize {

        let h = terminal_size().unwrap().1;

        if h >= 3 {
            h as usize - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {

        let w = terminal_size().unwrap().0;

        if w >= 2 {
            w as usize - 2
        } else {
            0
        }
    }

}

fn print_header(stdout: &mut termion::RawTerminal<std::io::StdoutLock>, width: usize, text: &str) {
    // print header
    let padding = ((width - text.len()) / 2) as u16;
    let header_bottom = (0..width).map(|_| "─").collect::<Vec<_>>().join("");

    stdout.goto(padding, 0).unwrap();
    stdout.color(Color::White).unwrap();
    stdout.bg_color(Color::Black).unwrap();
    stdout.style(Style::Bold).unwrap();
    stdout.write(text.as_bytes()).unwrap();

    stdout.goto(0, 1).unwrap();
    stdout.color(Color::Yellow).unwrap();
    stdout.bg_color(Color::Black).unwrap();
    stdout.style(Style::Bold).unwrap();
    stdout.write(header_bottom.as_bytes()).unwrap();

    stdout.hide_cursor().unwrap();
    stdout.reset().unwrap();
}

fn print_body(stdout: &mut termion::RawTerminal<std::io::StdoutLock>,
    width: usize,
    offset_y: usize,
    rows: usize,
    collection: &Vec<ListTopicItem>,
    selected_topic_index: usize){

    let right_offset = 3;
    let author_max_width = 12;
    let no_max_width = 5;
    let title_max_width = width - no_max_width - author_max_width - right_offset;

    for (i, item) in collection.iter().take(rows).enumerate() {

        let original_title = item.title.text.clone();
        let title: String = substring(&original_title, title_max_width);
        let title_len = jks_len(&title);

        let title_spacin_minus = no_max_width + title_len + author_max_width + right_offset;
        let title_spacing_width = if width > title_spacin_minus {
            width - title_spacin_minus
        } else {
            0
        };
        let title_spacing = (0..title_spacing_width).map(|_| " ").collect::<Vec<_>>().join("");

        let author = item.author.name.clone();
        let author_spacing_width = author_max_width - jks_len(&author) + right_offset;
        let author_spacing = (0..author_spacing_width).map(|_| " ").collect::<Vec<_>>().join("");

        if selected_topic_index == i + 1 {
            stdout.goto(0, (i + offset_y) as u16).unwrap();
            stdout.color(Color::Black).unwrap();
            stdout.bg_color(Color::Yellow).unwrap();
            stdout.style(Style::Reset).unwrap();
            stdout.write(&format!("[{no:0>2}] {title}{title_spacing}| {author}{author_spacing}",
                     no = i + 1,
                     title = title,
                     title_spacing = title_spacing,
                     author = &author,
                     author_spacing = author_spacing).as_bytes()).unwrap();

            // rustbox.print(0,
            //               i + offset_y,
            //               rustbox::RB_NORMAL,
            //               Color::Black,
            //               Color::Yellow,
            //               &format!("[{no:0>2}] {title}{title_spacing}| {author}{author_spacing}",
            //                        no = i + 1,
            //                        title = title,
            //                        title_spacing = title_spacing,
            //                        author = &author,
            //                        author_spacing = author_spacing));
        } else {

            stdout.goto(0, (i + offset_y) as u16).unwrap();
            stdout.color(Color::White).unwrap();
            stdout.bg_color(Color::Black).unwrap();
            stdout.style(Style::Reset).unwrap();
            stdout.write(&format!("[{no:0>2}] {title}{title_spacing}| {author}{author_spacing}",
                     no = i + 1,
                     title = title,
                     title_spacing = title_spacing,
                     author = &author,
                     author_spacing = author_spacing).as_bytes()).unwrap();

            // rustbox.print(0,
            //               i + offset_y,
            //               rustbox::RB_NORMAL,
            //               Color::White,
            //               Color::Black,
            //               &format!("[{no:0>2}] {title}{title_spacing}| {author}{author_spacing}",
            //                        no = i + 1,
            //                        title = title,
            //                        title_spacing = title_spacing,
            //                        author = &author,
            //                        author_spacing = author_spacing));
            stdout.hide_cursor().unwrap();
            stdout.reset().unwrap();
        }

    }
}
<|endoftext|>


<commit_before>//! Matrix room version identifiers.

use std::{
    cmp::Ordering,
    convert::TryFrom,
    fmt::{self, Display, Formatter},
    str::FromStr,
};

#[cfg(feature = "serde")]
use serde1::{Deserialize, Deserializer, Serialize, Serializer};

use crate::Error;

/// A Matrix room version ID.
///
/// A `RoomVersionId` can be or converted or deserialized from a string slice, and can be converted
/// or serialized back into a string as needed.
///
/// ```
/// # use std::convert::TryFrom;
/// # use ruma_identifiers::RoomVersionId;
/// assert_eq!(RoomVersionId::try_from("1").unwrap().as_ref(), "1");
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum RoomVersionId {
    /// A version 1 room.
    Version1,

    /// A version 2 room.
    Version2,

    /// A version 3 room.
    Version3,

    /// A version 4 room.
    Version4,

    /// A version 5 room.
    Version5,

    /// A version 6 room.
    Version6,

    /// A custom room version.
    Custom(CustomRoomVersion),
}

impl RoomVersionId {
    /// Creates a string slice from this `RoomVersionId`.
    pub fn as_str(&self) -> &str {
        match &self {
            Self::Version1 => "1",
            Self::Version2 => "2",
            Self::Version3 => "3",
            Self::Version4 => "4",
            Self::Version5 => "5",
            Self::Version6 => "6",
            Self::Custom(version) => version.as_str(),
        }
    }

    /// Creates a byte slice from this `RoomVersionId`.
    pub fn as_bytes(&self) -> &[u8] {
        self.as_str().as_bytes()
    }

    /// Whether or not this room version is an official one specified by the Matrix protocol.
    pub fn is_official(&self) -> bool {
        !self.is_custom()
    }

    /// Whether or not this is a custom room version.
    pub fn is_custom(&self) -> bool {
        matches!(self, Self::Custom(_))
    }
}

impl From<RoomVersionId> for String {
    fn from(id: RoomVersionId) -> Self {
        match id {
            RoomVersionId::Version1 => "1".to_owned(),
            RoomVersionId::Version2 => "2".to_owned(),
            RoomVersionId::Version3 => "3".to_owned(),
            RoomVersionId::Version4 => "4".to_owned(),
            RoomVersionId::Version5 => "5".to_owned(),
            RoomVersionId::Version6 => "6".to_owned(),
            RoomVersionId::Custom(version) => version.into(),
        }
    }
}

impl AsRef<str> for RoomVersionId {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl Display for RoomVersionId {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_ref())
    }
}

impl PartialOrd for RoomVersionId {
    /// Compare the two given room version IDs by comparing their string representations.
    ///
    /// Please be aware that room version IDs don't have a defined ordering in the Matrix
    /// specification. This implementation only exists to be able to use `RoomVersionId`s or
    /// types containing `RoomVersionId`s as `BTreeMap` keys.
    fn partial_cmp(&self, other: &RoomVersionId) -> Option<Ordering> {
        self.as_ref().partial_cmp(other.as_ref())
    }
}

impl Ord for RoomVersionId {
    /// Compare the two given room version IDs by comparing their string representations.
    ///
    /// Please be aware that room version IDs don't have a defined ordering in the Matrix
    /// specification. This implementation only exists to be able to use `RoomVersionId`s or
    /// types containing `RoomVersionId`s as `BTreeMap` keys.
    fn cmp(&self, other: &Self) -> Ordering {
        self.as_ref().cmp(other.as_ref())
    }
}

#[cfg(feature = "serde")]
impl Serialize for RoomVersionId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.as_ref())
    }
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for RoomVersionId {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        crate::deserialize_id(deserializer, "a Matrix room version ID as a string")
    }
}

/// Attempts to create a new Matrix room version ID from a string representation.
fn try_from<S>(room_version_id: S) -> Result<RoomVersionId, Error>
where
    S: AsRef<str> + Into<Box<str>>,
{
    let version = match room_version_id.as_ref() {
        "1" => RoomVersionId::Version1,
        "2" => RoomVersionId::Version2,
        "3" => RoomVersionId::Version3,
        "4" => RoomVersionId::Version4,
        "5" => RoomVersionId::Version5,
        "6" => RoomVersionId::Version6,
        custom => {
            ruma_identifiers_validation::room_version_id::validate(custom)?;
            RoomVersionId::Custom(CustomRoomVersion(room_version_id.into()))
        }
    };

    Ok(version)
}

impl FromStr for RoomVersionId {
    type Err = crate::Error;

    fn from_str(s: &str) -> Result<Self, Error> {
        try_from(s)
    }
}

impl TryFrom<&str> for RoomVersionId {
    type Error = crate::Error;

    fn try_from(s: &str) -> Result<Self, Error> {
        try_from(s)
    }
}

impl TryFrom<String> for RoomVersionId {
    type Error = crate::Error;

    fn try_from(s: String) -> Result<Self, Error> {
        try_from(s)
    }
}

impl PartialEq<&str> for RoomVersionId {
    fn eq(&self, other: &&str) -> bool {
        self.as_str() == *other
    }
}

impl PartialEq<RoomVersionId> for &str {
    fn eq(&self, other: &RoomVersionId) -> bool {
        *self == other.as_str()
    }
}

impl PartialEq<String> for RoomVersionId {
    fn eq(&self, other: &String) -> bool {
        self.as_str() == other
    }
}

impl PartialEq<RoomVersionId> for String {
    fn eq(&self, other: &RoomVersionId) -> bool {
        self == other.as_str()
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CustomRoomVersion(Box<str>);

impl CustomRoomVersion {
    /// Creates a string slice from this `CustomRoomVersion`
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<CustomRoomVersion> for String {
    fn from(v: CustomRoomVersion) -> Self {
        v.0.into()
    }
}

impl AsRef<str> for CustomRoomVersion {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

#[cfg(test)]
mod tests {
    use std::convert::TryFrom;

    #[cfg(feature = "serde")]
    use serde_json::{from_str, to_string};

    use super::{CustomRoomVersion, RoomVersionId};
    use crate::Error;

    #[test]
    fn valid_version_1_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.").as_ref(),
            "1"
        );
    }

    #[test]
    fn valid_version_2_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("2").expect("Failed to create RoomVersionId.").as_ref(),
            "2"
        );
    }

    #[test]
    fn valid_version_3_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("3").expect("Failed to create RoomVersionId.").as_ref(),
            "3"
        );
    }

    #[test]
    fn valid_version_4_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("4").expect("Failed to create RoomVersionId.").as_ref(),
            "4"
        );
    }

    #[test]
    fn valid_version_5_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("5").expect("Failed to create RoomVersionId.").as_ref(),
            "5"
        );
    }

    #[test]
    fn valid_version_6_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("6").expect("Failed to create RoomVersionId.").as_ref(),
            "6"
        );
    }

    #[test]
    fn valid_custom_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.").as_ref(),
            "io.ruma.1"
        );
    }

    #[test]
    fn empty_room_version_id() {
        assert_eq!(RoomVersionId::try_from(""), Err(Error::EmptyRoomVersionId));
    }

    #[test]
    fn over_max_code_point_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("0123456789012345678901234567890123456789"),
            Err(Error::MaximumLengthExceeded)
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_official_room_id() {
        assert_eq!(
            to_string(&RoomVersionId::try_from("1").expect("Failed to create RoomVersionId."))
                .expect("Failed to convert RoomVersionId to JSON."),
            r#""1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_official_room_id() {
        let deserialized =
            from_str::<RoomVersionId>(r#""1""#).expect("Failed to convert RoomVersionId to JSON.");

        assert_eq!(deserialized, RoomVersionId::Version1);
        assert!(deserialized.is_official());

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.")
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_custom_room_id() {
        assert_eq!(
            to_string(
                &RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
            )
            .expect("Failed to convert RoomVersionId to JSON."),
            r#""io.ruma.1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_custom_room_id() {
        let deserialized = from_str::<RoomVersionId>(r#""io.ruma.1""#)
            .expect("Failed to convert RoomVersionId to JSON.");

        assert!(deserialized.is_custom());

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
        );
    }

    #[test]
    fn official_versions() {
        assert!(RoomVersionId::Version1.is_official());
        assert!(RoomVersionId::Version2.is_official());
        assert!(RoomVersionId::Version3.is_official());
        assert!(RoomVersionId::Version4.is_official());
        assert!(RoomVersionId::Version5.is_official());
        assert!(RoomVersionId::Version6.is_official());
        assert!(!RoomVersionId::Custom(CustomRoomVersion("io.ruma.1".into())).is_official());
    }

    #[test]
    fn custom_versions() {
        assert!(!RoomVersionId::Version1.is_custom());
        assert!(!RoomVersionId::Version2.is_custom());
        assert!(!RoomVersionId::Version3.is_custom());
        assert!(!RoomVersionId::Version4.is_custom());
        assert!(!RoomVersionId::Version5.is_custom());
        assert!(!RoomVersionId::Version6.is_custom());
        assert!(RoomVersionId::Custom(CustomRoomVersion("io.ruma.1".into())).is_custom());
    }
}
<commit_msg>identifiers: Remove RoomVersionId::{is_custom, is_official}<commit_after>//! Matrix room version identifiers.

use std::{
    cmp::Ordering,
    convert::TryFrom,
    fmt::{self, Display, Formatter},
    str::FromStr,
};

#[cfg(feature = "serde")]
use serde1::{Deserialize, Deserializer, Serialize, Serializer};

use crate::Error;

/// A Matrix room version ID.
///
/// A `RoomVersionId` can be or converted or deserialized from a string slice, and can be converted
/// or serialized back into a string as needed.
///
/// ```
/// # use std::convert::TryFrom;
/// # use ruma_identifiers::RoomVersionId;
/// assert_eq!(RoomVersionId::try_from("1").unwrap().as_ref(), "1");
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum RoomVersionId {
    /// A version 1 room.
    Version1,

    /// A version 2 room.
    Version2,

    /// A version 3 room.
    Version3,

    /// A version 4 room.
    Version4,

    /// A version 5 room.
    Version5,

    /// A version 6 room.
    Version6,

    /// A custom room version.
    Custom(CustomRoomVersion),
}

impl RoomVersionId {
    /// Creates a string slice from this `RoomVersionId`.
    pub fn as_str(&self) -> &str {
        match &self {
            Self::Version1 => "1",
            Self::Version2 => "2",
            Self::Version3 => "3",
            Self::Version4 => "4",
            Self::Version5 => "5",
            Self::Version6 => "6",
            Self::Custom(version) => version.as_str(),
        }
    }

    /// Creates a byte slice from this `RoomVersionId`.
    pub fn as_bytes(&self) -> &[u8] {
        self.as_str().as_bytes()
    }
}

impl From<RoomVersionId> for String {
    fn from(id: RoomVersionId) -> Self {
        match id {
            RoomVersionId::Version1 => "1".to_owned(),
            RoomVersionId::Version2 => "2".to_owned(),
            RoomVersionId::Version3 => "3".to_owned(),
            RoomVersionId::Version4 => "4".to_owned(),
            RoomVersionId::Version5 => "5".to_owned(),
            RoomVersionId::Version6 => "6".to_owned(),
            RoomVersionId::Custom(version) => version.into(),
        }
    }
}

impl AsRef<str> for RoomVersionId {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl Display for RoomVersionId {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_ref())
    }
}

impl PartialOrd for RoomVersionId {
    /// Compare the two given room version IDs by comparing their string representations.
    ///
    /// Please be aware that room version IDs don't have a defined ordering in the Matrix
    /// specification. This implementation only exists to be able to use `RoomVersionId`s or
    /// types containing `RoomVersionId`s as `BTreeMap` keys.
    fn partial_cmp(&self, other: &RoomVersionId) -> Option<Ordering> {
        self.as_ref().partial_cmp(other.as_ref())
    }
}

impl Ord for RoomVersionId {
    /// Compare the two given room version IDs by comparing their string representations.
    ///
    /// Please be aware that room version IDs don't have a defined ordering in the Matrix
    /// specification. This implementation only exists to be able to use `RoomVersionId`s or
    /// types containing `RoomVersionId`s as `BTreeMap` keys.
    fn cmp(&self, other: &Self) -> Ordering {
        self.as_ref().cmp(other.as_ref())
    }
}

#[cfg(feature = "serde")]
impl Serialize for RoomVersionId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.as_ref())
    }
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for RoomVersionId {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        crate::deserialize_id(deserializer, "a Matrix room version ID as a string")
    }
}

/// Attempts to create a new Matrix room version ID from a string representation.
fn try_from<S>(room_version_id: S) -> Result<RoomVersionId, Error>
where
    S: AsRef<str> + Into<Box<str>>,
{
    let version = match room_version_id.as_ref() {
        "1" => RoomVersionId::Version1,
        "2" => RoomVersionId::Version2,
        "3" => RoomVersionId::Version3,
        "4" => RoomVersionId::Version4,
        "5" => RoomVersionId::Version5,
        "6" => RoomVersionId::Version6,
        custom => {
            ruma_identifiers_validation::room_version_id::validate(custom)?;
            RoomVersionId::Custom(CustomRoomVersion(room_version_id.into()))
        }
    };

    Ok(version)
}

impl FromStr for RoomVersionId {
    type Err = crate::Error;

    fn from_str(s: &str) -> Result<Self, Error> {
        try_from(s)
    }
}

impl TryFrom<&str> for RoomVersionId {
    type Error = crate::Error;

    fn try_from(s: &str) -> Result<Self, Error> {
        try_from(s)
    }
}

impl TryFrom<String> for RoomVersionId {
    type Error = crate::Error;

    fn try_from(s: String) -> Result<Self, Error> {
        try_from(s)
    }
}

impl PartialEq<&str> for RoomVersionId {
    fn eq(&self, other: &&str) -> bool {
        self.as_str() == *other
    }
}

impl PartialEq<RoomVersionId> for &str {
    fn eq(&self, other: &RoomVersionId) -> bool {
        *self == other.as_str()
    }
}

impl PartialEq<String> for RoomVersionId {
    fn eq(&self, other: &String) -> bool {
        self.as_str() == other
    }
}

impl PartialEq<RoomVersionId> for String {
    fn eq(&self, other: &RoomVersionId) -> bool {
        self == other.as_str()
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CustomRoomVersion(Box<str>);

impl CustomRoomVersion {
    /// Creates a string slice from this `CustomRoomVersion`
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<CustomRoomVersion> for String {
    fn from(v: CustomRoomVersion) -> Self {
        v.0.into()
    }
}

impl AsRef<str> for CustomRoomVersion {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

#[cfg(test)]
mod tests {
    use std::convert::TryFrom;

    #[cfg(feature = "serde")]
    use serde_json::{from_str, to_string};

    use super::RoomVersionId;
    use crate::Error;

    #[test]
    fn valid_version_1_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.").as_ref(),
            "1"
        );
    }

    #[test]
    fn valid_version_2_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("2").expect("Failed to create RoomVersionId.").as_ref(),
            "2"
        );
    }

    #[test]
    fn valid_version_3_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("3").expect("Failed to create RoomVersionId.").as_ref(),
            "3"
        );
    }

    #[test]
    fn valid_version_4_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("4").expect("Failed to create RoomVersionId.").as_ref(),
            "4"
        );
    }

    #[test]
    fn valid_version_5_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("5").expect("Failed to create RoomVersionId.").as_ref(),
            "5"
        );
    }

    #[test]
    fn valid_version_6_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("6").expect("Failed to create RoomVersionId.").as_ref(),
            "6"
        );
    }

    #[test]
    fn valid_custom_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.").as_ref(),
            "io.ruma.1"
        );
    }

    #[test]
    fn empty_room_version_id() {
        assert_eq!(RoomVersionId::try_from(""), Err(Error::EmptyRoomVersionId));
    }

    #[test]
    fn over_max_code_point_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("0123456789012345678901234567890123456789"),
            Err(Error::MaximumLengthExceeded)
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_official_room_id() {
        assert_eq!(
            to_string(&RoomVersionId::try_from("1").expect("Failed to create RoomVersionId."))
                .expect("Failed to convert RoomVersionId to JSON."),
            r#""1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_official_room_id() {
        let deserialized =
            from_str::<RoomVersionId>(r#""1""#).expect("Failed to convert RoomVersionId to JSON.");

        assert_eq!(deserialized, RoomVersionId::Version1);

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.")
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_custom_room_id() {
        assert_eq!(
            to_string(
                &RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
            )
            .expect("Failed to convert RoomVersionId to JSON."),
            r#""io.ruma.1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_custom_room_id() {
        let deserialized = from_str::<RoomVersionId>(r#""io.ruma.1""#)
            .expect("Failed to convert RoomVersionId to JSON.");

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
        );
    }
}
<|endoftext|>